22# '
33# ' Calculates various cost components using prioritizr functions where possible
44# '
5- # ' @param prioritizr_problem A prioritizr problem object
6- # ' @param solution_data sf object with solution data
7- # ' @param unit_dict Named list containing cost and status for each planning unit (for compatibility)
8- # ' @param boundary_penalty Boundary length modifier (BLM) value
5+ # ' @param minpatch_data List containing all MinPatch data structures
96# '
107# ' @return List containing detailed cost breakdown
118# ' @keywords internal
12- calculate_cost_summary <- function (prioritizr_problem = NULL , solution_data = NULL ,
13- unit_dict = NULL , boundary_penalty = 0 ) {
14-
15- # If prioritizr objects are available, use prioritizr functions
16- if (! is.null(prioritizr_problem ) && ! is.null(solution_data )) {
17-
18- # Use prioritizr's eval_cost_summary for planning unit costs
19- cost_summary <- prioritizr :: eval_cost_summary(prioritizr_problem , solution_data )
20- total_unit_cost <- cost_summary $ cost
21-
22- # Use prioritizr's eval_n_summary for selected unit count
23- n_summary <- prioritizr :: eval_n_summary(prioritizr_problem , solution_data )
24- selected_unit_count <- n_summary $ n
25-
26- # Use prioritizr's eval_boundary_summary for boundary costs if boundary penalty > 0
27- if (boundary_penalty > 0 ) {
28- boundary_summary <- prioritizr :: eval_boundary_summary(prioritizr_problem , solution_data )
29- total_boundary_length <- boundary_summary $ boundary
30- total_boundary_cost <- total_boundary_length * boundary_penalty
31- } else {
32- total_boundary_length <- 0
33- total_boundary_cost <- 0
34- }
35-
36- total_cost <- total_unit_cost + total_boundary_cost
37-
38- return (list (
39- total_unit_cost = total_unit_cost ,
40- selected_unit_count = selected_unit_count ,
41- total_boundary_length = total_boundary_length ,
42- total_boundary_cost = total_boundary_cost ,
43- total_cost = total_cost ,
44- boundary_penalty = boundary_penalty
45- ))
46-
47- } else if (! is.null(unit_dict )) {
48- # Fallback to original implementation for backward compatibility
49-
50- # Calculate total planning unit cost
51- total_unit_cost <- 0
52- selected_unit_count <- 0
53-
54- for (unit_id in names(unit_dict )) {
55- if (unit_dict [[unit_id ]]$ status %in% c(1 , 2 )) { # Selected or conserved
56- total_unit_cost <- total_unit_cost + unit_dict [[unit_id ]]$ cost
57- selected_unit_count <- selected_unit_count + 1
58- }
59- }
60-
61- # For boundary costs, we'd need the boundary_matrix - this is a limitation
62- # of the fallback approach
63- total_boundary_length <- 0
64- total_boundary_cost <- 0
65- total_cost <- total_unit_cost
66-
67- return (list (
68- total_unit_cost = total_unit_cost ,
69- selected_unit_count = selected_unit_count ,
70- total_boundary_length = total_boundary_length ,
71- total_boundary_cost = total_boundary_cost ,
72- total_cost = total_cost ,
73- boundary_penalty = boundary_penalty
74- ))
75-
9+ calculate_cost_summary <- function (minpatch_data ) {
10+
11+ # Create solution data for prioritizr functions using the minpatch column
12+ solution_data <- minpatch_data $ prioritizr_solution %> %
13+ dplyr :: select(" minpatch" )
14+
15+ # Use prioritizr's eval_cost_summary for planning unit costs
16+ cost_summary <- prioritizr :: eval_cost_summary(minpatch_data $ prioritizr_problem , solution_data )
17+
18+ # Use prioritizr's eval_n_summary for selected unit count
19+ n_summary <- prioritizr :: eval_n_summary(minpatch_data $ prioritizr_problem , solution_data )
20+ cost_summary $ n <- n_summary $ n
21+
22+ # Use prioritizr's eval_boundary_summary for boundary costs if boundary penalty > 0
23+ if (minpatch_data $ boundary_penalty > 0 ) {
24+ boundary_summary <- prioritizr :: eval_boundary_summary(minpatch_data $ prioritizr_problem , solution_data )
25+ cost_summary $ boundary_length <- boundary_summary $ boundary
26+ cost_summary $ boundary_cost <- cost_summary $ boundary_length * minpatch_data $ boundary_penalty
7627 } else {
77- stop(" Either prioritizr_problem and solution_data, or unit_dict must be provided" )
28+ cost_summary $ boundary_length <- 0
29+ cost_summary $ boundary_cost <- 0
7830 }
31+
32+ cost_summary <- cost_summary %> %
33+ dplyr :: mutate(total_cost = .data $ cost + .data $ boundary_cost )
34+
35+ return (cost_summary )
36+
7937}
8038
81- # Note: calculate_boundary_costs() function removed - now using prioritizr::eval_boundary_summary()
82- # This reduces code duplication and ensures consistency with prioritizr calculations
8339
8440# ' Create solution vector from unit dictionary
8541# '
@@ -90,205 +46,18 @@ calculate_cost_summary <- function(prioritizr_problem = NULL, solution_data = NU
9046# ' @return Binary numeric vector indicating selected planning units
9147# ' @keywords internal
9248create_solution_vector <- function (unit_dict ) {
93-
49+
9450 n_units <- length(unit_dict )
9551 solution <- numeric (n_units )
96-
52+
9753 for (i in seq_len(n_units )) {
9854 unit_id <- as.character(i )
9955 if (unit_id %in% names(unit_dict )) {
10056 # Set to 1 if selected (status 1) or conserved (status 2)
10157 solution [i ] <- as.numeric(unit_dict [[unit_id ]]$ status %in% c(1 , 2 ))
10258 }
10359 }
104-
105- return (solution )
106- }
10760
108- # ' Calculate feature representation in solution
109- # '
110- # ' Calculates how much of each conservation feature is represented
111- # ' in the current solution using prioritizr functions where possible
112- # '
113- # ' @param minpatch_data List containing all MinPatch data structures including prioritizr objects
114- # '
115- # ' @return Data frame with feature representation statistics
116- # ' @export
117- calculate_feature_representation <- function (minpatch_data ) {
118-
119- # If prioritizr objects are available, use prioritizr functions
120- if (! is.null(minpatch_data $ prioritizr_problem ) && ! is.null(minpatch_data $ solution_data )) {
121-
122- # Use prioritizr's eval_feature_representation_summary
123- feature_rep <- prioritizr :: eval_feature_representation_summary(minpatch_data $ prioritizr_problem , minpatch_data $ solution_data )
124-
125- # Use prioritizr's eval_target_coverage_summary for target information
126- target_coverage <- prioritizr :: eval_target_coverage_summary(minpatch_data $ prioritizr_problem , minpatch_data $ solution_data )
127-
128- # Combine the results to match the expected output format
129- results <- data.frame (
130- feature_id = seq_len(nrow(feature_rep )),
131- target = target_coverage $ target ,
132- conserved = feature_rep $ absolute_held ,
133- proportion_met = feature_rep $ relative_held ,
134- target_met = target_coverage $ met ,
135- shortfall = pmax(0 , target_coverage $ target - feature_rep $ absolute_held ),
136- stringsAsFactors = FALSE
137- )
138-
139- return (results )
140-
141- } else {
142- # Fallback to original implementation using minpatch_data components
143-
144- # Calculate current conservation amounts
145- feature_amounts <- calculate_feature_conservation(minpatch_data )
146-
147- # Create results data frame
148- results <- data.frame (
149- feature_id = names(minpatch_data $ target_dict ),
150- target = sapply(minpatch_data $ target_dict , function (x ) x $ target ),
151- conserved = feature_amounts [names(minpatch_data $ target_dict )],
152- stringsAsFactors = FALSE
153- )
154-
155- # Calculate proportion of target met
156- results $ proportion_met <- ifelse(results $ target > 0 ,
157- results $ conserved / results $ target ,
158- NA )
159-
160- # Identify if target is met
161- results $ target_met <- results $ conserved > = results $ target
162-
163- # Calculate shortfall
164- results $ shortfall <- pmax(0 , results $ target - results $ conserved )
165-
166- return (results )
167- }
168- }
169-
170- # ' Generate comprehensive MinPatch report
171- # '
172- # ' Creates a detailed report of the MinPatch processing results
173- # '
174- # ' @param minpatch_result Result object from run_minpatch function
175- # ' @param prioritizr_problem A prioritizr problem object (optional, for enhanced reporting)
176- # ' @param solution_data sf object with solution data (optional, for enhanced reporting)
177- # '
178- # ' @return List containing formatted report components
179- # ' @export
180- generate_minpatch_report <- function (minpatch_result , prioritizr_problem = NULL , solution_data = NULL ) {
181-
182- # Extract components
183- initial_stats <- minpatch_result $ patch_stats $ initial
184- final_stats <- minpatch_result $ patch_stats $ final
185- cost_summary <- minpatch_result $ cost_summary
186-
187- # Calculate feature representation using prioritizr functions if available
188- if (! is.null(prioritizr_problem ) && ! is.null(solution_data )) {
189- # Create temporary minpatch_data with prioritizr objects
190- temp_minpatch_data <- minpatch_result $ minpatch_data
191- temp_minpatch_data $ prioritizr_problem <- prioritizr_problem
192- temp_minpatch_data $ solution_data <- solution_data
193- feature_rep <- calculate_feature_representation(temp_minpatch_data )
194- } else {
195- # Fallback to original method
196- feature_rep <- calculate_feature_representation(minpatch_result $ minpatch_data )
197- }
198-
199- # Summary statistics
200- summary_stats <- list (
201- initial_patches = initial_stats $ all_patch_count ,
202- final_patches = final_stats $ all_patch_count ,
203- initial_valid_patches = initial_stats $ valid_patch_count ,
204- final_valid_patches = final_stats $ valid_patch_count ,
205- initial_area = initial_stats $ all_patch_area ,
206- final_area = final_stats $ all_patch_area ,
207- area_change = final_stats $ all_patch_area - initial_stats $ all_patch_area ,
208- area_change_percent = ifelse(initial_stats $ all_patch_area > 0 ,
209- ((final_stats $ all_patch_area - initial_stats $ all_patch_area ) /
210- initial_stats $ all_patch_area ) * 100 , 0 ),
211- total_cost = cost_summary $ total_cost ,
212- unit_cost = cost_summary $ total_unit_cost ,
213- boundary_cost = cost_summary $ total_boundary_cost ,
214- selected_units = cost_summary $ selected_unit_count
215- )
216-
217- # Feature summary
218- feature_summary <- list (
219- total_features = nrow(feature_rep ),
220- targets_met = sum(feature_rep $ target_met , na.rm = TRUE ),
221- targets_unmet = sum(! feature_rep $ target_met , na.rm = TRUE ),
222- mean_proportion_met = mean(feature_rep $ proportion_met , na.rm = TRUE ),
223- total_shortfall = sum(feature_rep $ shortfall , na.rm = TRUE )
224- )
225-
226- return (list (
227- summary_stats = summary_stats ,
228- feature_summary = feature_summary ,
229- feature_representation = feature_rep ,
230- patch_stats = list (
231- initial = initial_stats ,
232- final = final_stats
233- ),
234- cost_breakdown = cost_summary
235- ))
61+ return (solution )
23662}
23763
238- # ' Print MinPatch results summary
239- # '
240- # ' Prints a formatted summary of MinPatch processing results
241- # '
242- # ' @param minpatch_result Result object from run_minpatch function
243- # '
244- # ' @export
245- print_minpatch_summary <- function (minpatch_result ) {
246-
247- report <- generate_minpatch_report(minpatch_result )
248-
249- cat(" === MinPatch Processing Summary ===\n\n " )
250-
251- # Patch statistics
252- cat(" Patch Statistics:\n " )
253- cat(sprintf(" Initial patches: %d (valid: %d)\n " ,
254- report $ summary_stats $ initial_patches ,
255- report $ summary_stats $ initial_valid_patches ))
256- cat(sprintf(" Final patches: %d (valid: %d)\n " ,
257- report $ summary_stats $ final_patches ,
258- report $ summary_stats $ final_valid_patches ))
259- cat(sprintf(" Area change: %.2f (%.1f%%)\n " ,
260- report $ summary_stats $ area_change ,
261- report $ summary_stats $ area_change_percent ))
262- cat(" \n " )
263-
264- # Cost breakdown
265- cat(" Cost Breakdown:\n " )
266- cat(sprintf(" Planning unit cost: %.2f\n " , report $ summary_stats $ unit_cost ))
267- cat(sprintf(" Boundary cost: %.2f\n " , report $ summary_stats $ boundary_cost ))
268- cat(sprintf(" Total cost: %.2f\n " , report $ summary_stats $ total_cost ))
269- cat(sprintf(" Selected units: %d\n " , report $ summary_stats $ selected_units ))
270- cat(" \n " )
271-
272- # Feature representation
273- cat(" Feature Representation:\n " )
274- cat(sprintf(" Total features: %d\n " , report $ feature_summary $ total_features ))
275- cat(sprintf(" Targets met: %d\n " , report $ feature_summary $ targets_met ))
276- cat(sprintf(" Targets unmet: %d\n " , report $ feature_summary $ targets_unmet ))
277- cat(sprintf(" Mean proportion met: %.3f\n " , report $ feature_summary $ mean_proportion_met ))
278- cat(sprintf(" Total shortfall: %.2f\n " , report $ feature_summary $ total_shortfall ))
279- cat(" \n " )
280-
281- if (report $ feature_summary $ targets_unmet > 0 ) {
282- cat(" Features with unmet targets:\n " )
283- unmet <- report $ feature_representation [! report $ feature_representation $ target_met , ]
284- for (i in seq_len(nrow(unmet ))) {
285- cat(sprintf(" Feature %s: %.2f/%.2f (%.1f%% met)\n " ,
286- unmet $ feature_id [i ],
287- unmet $ conserved [i ],
288- unmet $ target [i ],
289- unmet $ proportion_met [i ] * 100 ))
290- }
291- }
292-
293- cat(" \n === End Summary ===\n " )
294- }
0 commit comments