Skip to content

Commit 6ccf01c

Browse files
committed
Update data output and structure
1 parent 03fe7cd commit 6ccf01c

44 files changed

Lines changed: 2193 additions & 1456 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DESCRIPTION

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: minpatch
22
Type: Package
33
Title: Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes
4-
Version: 1.0.0
4+
Version: 0.1.0
55
Authors@R: c(
66
person(
77
given = "Jason D.",
@@ -51,7 +51,9 @@ Suggests:
5151
knitr,
5252
patchwork,
5353
rmarkdown,
54+
stars,
5455
stringr
5556
VignetteBuilder: knitr
56-
URL: https://github.com/yourusername/minpatch
57-
BugReports: https://github.com/yourusername/minpatch/issues
57+
URL: https://github.com/SpatialPlanning/minpatch
58+
BugReports: https://github.com/SpatialPlanning/minpatch/issues
59+
Config/testthat/edition: 3

NAMESPACE

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Generated by roxygen2: do not edit by hand
22

33
export("%>%")
4-
export(calculate_feature_representation)
54
export(compare_solutions)
65
export(generate_minpatch_report)
6+
export(plot_minpatch)
7+
export(plot_prioritizr)
78
export(print_minpatch_summary)
89
export(run_minpatch)
9-
export(visualize_minpatch_results)
1010
importFrom(magrittr,"%>%")
1111
importFrom(rlang,.data)

R/cost_functions.R

Lines changed: 30 additions & 261 deletions
Original file line numberDiff line numberDiff line change
@@ -2,84 +2,40 @@
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
9248
create_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

Comments
 (0)