code rabbit review stuff
This commit is contained in:
parent
58d3e655ab
commit
ede84679d7
|
|
@ -807,7 +807,7 @@ main <- function() {
|
|||
total_acreage = sum(field_data$Acreage, na.rm = TRUE),
|
||||
mean_ci = round(mean(field_data$Mean_CI, na.rm = TRUE), 2),
|
||||
median_ci = round(median(field_data$Mean_CI, na.rm = TRUE), 2),
|
||||
mean_cv = round(mean(field_data$CI_CV, na.rm = TRUE), 4),
|
||||
mean_cv = round(mean(field_data$CV, na.rm = TRUE), 4),
|
||||
week = current_week,
|
||||
year = current_iso_year,
|
||||
date = as.character(end_date)
|
||||
|
|
|
|||
|
|
@ -610,45 +610,6 @@ if (exists("field_details_table") && !is.null(field_details_table)) {
|
|||
}
|
||||
```
|
||||
|
||||
```{r data, message=TRUE, warning=TRUE, include=FALSE}
|
||||
# Load CI index data with error handling
|
||||
tryCatch({
|
||||
CI_quadrant <- readRDS(here::here(cumulative_CI_vals_dir, "All_pivots_Cumulative_CI_quadrant_year_v2.rds"))
|
||||
safe_log("Successfully loaded CI quadrant data")
|
||||
}, error = function(e) {
|
||||
stop("Error loading CI quadrant data: ", e$message)
|
||||
})
|
||||
|
||||
# Get file paths for different weeks using the utility function
|
||||
tryCatch({
|
||||
path_to_week_current = get_week_path(weekly_CI_mosaic, today, 0)
|
||||
path_to_week_minus_1 = get_week_path(weekly_CI_mosaic, today, -1)
|
||||
path_to_week_minus_2 = get_week_path(weekly_CI_mosaic, today, -2)
|
||||
path_to_week_minus_3 = get_week_path(weekly_CI_mosaic, today, -3)
|
||||
|
||||
# Log the calculated paths
|
||||
safe_log("Required mosaic paths:")
|
||||
safe_log(paste("Path to current week:", path_to_week_current))
|
||||
safe_log(paste("Path to week minus 1:", path_to_week_minus_1))
|
||||
safe_log(paste("Path to week minus 2:", path_to_week_minus_2))
|
||||
safe_log(paste("Path to week minus 3:", path_to_week_minus_3))
|
||||
|
||||
# Validate that files exist
|
||||
if (!file.exists(path_to_week_current)) warning("Current week mosaic file does not exist: ", path_to_week_current)
|
||||
if (!file.exists(path_to_week_minus_1)) warning("Week minus 1 mosaic file does not exist: ", path_to_week_minus_1)
|
||||
if (!file.exists(path_to_week_minus_2)) warning("Week minus 2 mosaic file does not exist: ", path_to_week_minus_2)
|
||||
if (!file.exists(path_to_week_minus_3)) warning("Week minus 3 mosaic file does not exist: ", path_to_week_minus_3)
|
||||
|
||||
# Load raster data with terra functions
|
||||
CI <- terra::rast(path_to_week_current)$CI
|
||||
CI_m1 <- terra::rast(path_to_week_minus_1)$CI
|
||||
CI_m2 <- terra::rast(path_to_week_minus_2)$CI
|
||||
CI_m3 <- terra::rast(path_to_week_minus_3)$CI
|
||||
|
||||
}, error = function(e) {
|
||||
stop("Error loading raster data: ", e$message)
|
||||
})
|
||||
```
|
||||
|
||||
```{r calculate_difference_rasters, message=TRUE, warning=TRUE, include=FALSE}
|
||||
# Calculate difference rasters for comparisons
|
||||
|
|
|
|||
|
|
@ -120,8 +120,8 @@ date_suffix <- format(as.Date(report_date), "%Y%m%d")
|
|||
|
||||
# Calculate current week from report_date using ISO 8601 week numbering
|
||||
current_week <- as.numeric(format(as.Date(report_date), "%V"))
|
||||
week_suffix <- paste0("week", current_week)
|
||||
|
||||
current_iso_year <- as.numeric(format(as.Date(report_date), "%G"))
|
||||
week_suffix <- paste0("week", sprintf("%02d", current_week), "_", current_iso_year)
|
||||
# Candidate filenames we expect (exact and common variants)
|
||||
expected_summary_names <- c(
|
||||
paste0(project_dir, "_kpi_summary_tables_", week_suffix, ".rds"),
|
||||
|
|
|
|||
|
|
@ -80,7 +80,13 @@ load_weekly_ci_mosaic <- function(week_num, year, mosaic_dir) {
|
|||
|
||||
tryCatch({
|
||||
mosaic_raster <- terra::rast(week_path)
|
||||
ci_raster <- mosaic_raster[[5]] # CI is the 5th band
|
||||
# Extract CI band by name or position
|
||||
if ("CI" %in% names(mosaic_raster)) {
|
||||
ci_raster <- mosaic_raster[["CI"]]
|
||||
} else {
|
||||
# Fallback: assume last band is CI (after Red, Green, Blue, NIR)
|
||||
ci_raster <- mosaic_raster[[nlyr(mosaic_raster)]]
|
||||
}
|
||||
names(ci_raster) <- "CI"
|
||||
safe_log(paste("Loaded weekly mosaic:", week_file))
|
||||
return(ci_raster)
|
||||
|
|
@ -125,7 +131,7 @@ calculate_field_uniformity_kpi <- function(ci_raster, field_boundaries) {
|
|||
field_boundaries_vect <- field_boundaries
|
||||
}
|
||||
|
||||
field_results <- data.frame()
|
||||
field_results_list <- vector("list", nrow(field_boundaries))
|
||||
|
||||
for (i in seq_len(nrow(field_boundaries))) {
|
||||
field_name <- field_boundaries$field[i]
|
||||
|
|
@ -144,7 +150,7 @@ calculate_field_uniformity_kpi <- function(ci_raster, field_boundaries) {
|
|||
|
||||
# If all valid values are 0 (cloud), fill with NA row
|
||||
if (length(valid_values) == 0 || all(valid_values == 0)) {
|
||||
field_results <- rbind(field_results, data.frame(
|
||||
field_results_list[[i]] <- data.frame(
|
||||
field = field_name,
|
||||
sub_field = sub_field_name,
|
||||
field_id = field_id,
|
||||
|
|
@ -152,7 +158,7 @@ calculate_field_uniformity_kpi <- function(ci_raster, field_boundaries) {
|
|||
uniformity_level = NA_character_,
|
||||
mean_ci = NA_real_,
|
||||
std_ci = NA_real_
|
||||
))
|
||||
)
|
||||
} else if (length(valid_values) > 1) {
|
||||
# Calculate CV using existing function
|
||||
cv_value <- calculate_cv(valid_values)
|
||||
|
|
@ -165,7 +171,7 @@ calculate_field_uniformity_kpi <- function(ci_raster, field_boundaries) {
|
|||
TRUE ~ "Poor"
|
||||
)
|
||||
|
||||
field_results <- rbind(field_results, data.frame(
|
||||
field_results_list[[i]] <- data.frame(
|
||||
field = field_name,
|
||||
sub_field = sub_field_name,
|
||||
field_id = field_id,
|
||||
|
|
@ -173,10 +179,10 @@ calculate_field_uniformity_kpi <- function(ci_raster, field_boundaries) {
|
|||
uniformity_level = uniformity_level,
|
||||
mean_ci = mean(valid_values),
|
||||
std_ci = sd(valid_values)
|
||||
))
|
||||
)
|
||||
} else {
|
||||
# If only one valid value, fill with NA (not enough data for CV)
|
||||
field_results <- rbind(field_results, data.frame(
|
||||
field_results_list[[i]] <- data.frame(
|
||||
field = field_name,
|
||||
sub_field = sub_field_name,
|
||||
field_id = field_id,
|
||||
|
|
@ -184,10 +190,12 @@ calculate_field_uniformity_kpi <- function(ci_raster, field_boundaries) {
|
|||
uniformity_level = NA_character_,
|
||||
mean_ci = mean(valid_values),
|
||||
std_ci = NA_real_
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
field_results <- do.call(rbind, field_results_list)
|
||||
|
||||
# Create summary
|
||||
uniformity_summary <- field_results %>%
|
||||
dplyr::group_by(uniformity_level) %>%
|
||||
|
|
@ -671,8 +679,8 @@ calculate_weed_presence_kpi <- function(current_ci, previous_ci, field_boundarie
|
|||
safe_log("Previous week data not available for weed analysis", "WARNING")
|
||||
summary_result <- data.frame(
|
||||
weed_risk_level = c("Low", "Moderate", "High"),
|
||||
field_count = c(35, 8, 3),
|
||||
percent = c(76.1, 17.4, 6.5)
|
||||
field_count = c(0, 0, 0),
|
||||
percent = c(NA_real_, NA_real_, NA_real_)
|
||||
)
|
||||
field_results <- data.frame(
|
||||
field = character(0),
|
||||
|
|
@ -682,8 +690,6 @@ calculate_weed_presence_kpi <- function(current_ci, previous_ci, field_boundarie
|
|||
rapid_growth_pixels = numeric(0)
|
||||
)
|
||||
return(list(summary = summary_result, field_results = field_results))
|
||||
}
|
||||
|
||||
# Handle both sf and SpatVector inputs
|
||||
if (!inherits(field_boundaries, "SpatVector")) {
|
||||
field_boundaries_vect <- terra::vect(field_boundaries)
|
||||
|
|
@ -967,8 +973,8 @@ create_field_detail_table <- function(kpi_results, field_boundaries_sf = NULL) {
|
|||
dplyr::rename(`Gap Score` = gap_score)
|
||||
field_details <- dplyr::left_join(field_details, gap_data, by = c("Field" = "field"))
|
||||
} else {
|
||||
# Placeholder gap scores
|
||||
field_details$`Gap Score` <- round(runif(nrow(field_details), 5, 25), 1)
|
||||
# Gap score data unavailable - set to NA_real_ for deterministic handling
|
||||
field_details$`Gap Score` <- NA_real_
|
||||
}
|
||||
|
||||
# Add growth decline risk from growth decline field results (aggregate by field)
|
||||
|
|
@ -979,9 +985,8 @@ create_field_detail_table <- function(kpi_results, field_boundaries_sf = NULL) {
|
|||
dplyr::rename(`Decline Risk` = risk_level)
|
||||
field_details <- dplyr::left_join(field_details, decline_data, by = c("Field" = "field"))
|
||||
} else {
|
||||
# Placeholder risk levels
|
||||
field_details$`Decline Risk` <- sample(risk_levels, nrow(field_details),
|
||||
prob = c(0.6, 0.25, 0.1, 0.05), replace = TRUE)
|
||||
# Decline risk data unavailable - set to NA for deterministic handling
|
||||
field_details$`Decline Risk` <- NA
|
||||
}
|
||||
|
||||
# Add Moran's I spatial autocorrelation from growth decline field results (aggregate by field)
|
||||
|
|
@ -992,9 +997,8 @@ create_field_detail_table <- function(kpi_results, field_boundaries_sf = NULL) {
|
|||
dplyr::rename(`Moran's I` = morans_i)
|
||||
field_details <- dplyr::left_join(field_details, moran_data, by = c("Field" = "field"))
|
||||
} else {
|
||||
# Placeholder Moran's I values (typically range from -1 to 1)
|
||||
set.seed(123)
|
||||
field_details$`Moran's I` <- round(runif(nrow(field_details), -0.3, 0.8), 3)
|
||||
# Moran's I data unavailable - set to NA_real_ for deterministic handling
|
||||
field_details$`Moran's I` <- NA_real_
|
||||
}
|
||||
|
||||
# Add weed risk from weed presence field results (aggregate by field)
|
||||
|
|
@ -1005,17 +1009,12 @@ create_field_detail_table <- function(kpi_results, field_boundaries_sf = NULL) {
|
|||
dplyr::rename(`Weed Risk` = weed_risk_level)
|
||||
field_details <- dplyr::left_join(field_details, weed_data, by = c("Field" = "field"))
|
||||
} else {
|
||||
# Placeholder weed levels
|
||||
field_details$`Weed Risk` <- sample(weed_levels, nrow(field_details),
|
||||
prob = c(0.7, 0.2, 0.1), replace = TRUE)
|
||||
# Weed risk data unavailable - set to NA for deterministic handling
|
||||
field_details$`Weed Risk` <- NA
|
||||
}
|
||||
|
||||
# Fill any remaining NAs with defaults (but keep yield forecast as NA)
|
||||
field_details$`Gap Score`[is.na(field_details$`Gap Score`)] <- 0.0
|
||||
field_details$`Decline Risk`[is.na(field_details$`Decline Risk`)] <- sample(risk_levels, sum(is.na(field_details$`Decline Risk`)), replace = TRUE,
|
||||
prob = c(0.6, 0.25, 0.1, 0.05))
|
||||
field_details$`Weed Risk`[is.na(field_details$`Weed Risk`)] <- sample(weed_levels, sum(is.na(field_details$`Weed Risk`)), replace = TRUE,
|
||||
prob = c(0.7, 0.2, 0.1))
|
||||
# Keep any remaining NAs as-is for all fields (NA indicates data unavailable)
|
||||
# Do not fill with random values - let downstream code handle NA values deterministically
|
||||
|
||||
# Reorder columns for better presentation
|
||||
field_details <- field_details %>%
|
||||
|
|
@ -1204,19 +1203,16 @@ calculate_all_kpis <- function(report_date = Sys.Date(),
|
|||
|
||||
# Calculate week numbers
|
||||
weeks <- calculate_week_numbers(report_date)
|
||||
weeks <- calculate_week_numbers(report_date)
|
||||
weeks <- calculate_week_numbers(report_date)
|
||||
safe_log(paste("Current week:", weeks$current_week, "Previous week:", weeks$previous_week))
|
||||
|
||||
# Load weekly mosaics
|
||||
current_ci <- load_weekly_ci_mosaic(weeks$current_week, weeks$year, weekly_CI_mosaic)
|
||||
previous_ci <- load_weekly_ci_mosaic(weeks$previous_week, weeks$year, weekly_CI_mosaic)
|
||||
current_ci <- load_weekly_ci_mosaic(weeks$current_week, weeks$current_iso_year, weekly_CI_mosaic)
|
||||
previous_ci <- load_weekly_ci_mosaic(weeks$previous_week, weeks$previous_iso_year, weekly_CI_mosaic)
|
||||
|
||||
if (is.null(current_ci)) {
|
||||
stop("Current week CI mosaic is required but not found")
|
||||
}
|
||||
|
||||
# Check if field boundaries are loaded
|
||||
if (is.null(field_boundaries_sf)) {
|
||||
stop("Field boundaries not loaded. Check parameters_project.R initialization.")
|
||||
stop("Current week CI mosaic is required but not found") stop("Field boundaries not loaded. Check parameters_project.R initialization.")
|
||||
}
|
||||
|
||||
# Calculate all KPIs
|
||||
|
|
@ -1255,17 +1251,16 @@ calculate_all_kpis <- function(report_date = Sys.Date(),
|
|||
kpi_results$gap_filling_field_results <- gap_filling_result$field_results
|
||||
|
||||
# Add metadata and field boundaries for later use
|
||||
kpi_results$metadata <- list(
|
||||
report_date = report_date,
|
||||
kpi_results$metadata <- list(
|
||||
report_date = report_date,
|
||||
current_week = weeks$current_week,
|
||||
previous_week = weeks$previous_week,
|
||||
year = weeks$year,
|
||||
year = weeks$current_iso_year,
|
||||
calculation_time = Sys.time(),
|
||||
total_fields = nrow(field_boundaries_sf)
|
||||
)
|
||||
|
||||
# Store field_boundaries_sf for use in export_kpi_data
|
||||
kpi_results$field_boundaries_sf <- field_boundaries_sf
|
||||
) kpi_results$field_boundaries_sf <- field_boundaries_sf
|
||||
|
||||
# Save results if output directory specified
|
||||
if (!is.null(output_dir)) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SmartCane Data Validation Tool</title>
|
||||
<link rel="stylesheet" href="../theme.css">
|
||||
<link rel="icon" type="image/x-icon" href="../res/check.png">
|
||||
<link rel="icon" type="image/png" href="../res/check.png">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@
|
|||
<h1>📍 GeoJSON Viewer</h1>
|
||||
<div class="header-controls">
|
||||
<label class="map-toggle-wrapper" style="cursor: pointer;">
|
||||
<input type="checkbox" id="mapToggle">
|
||||
<input type="checkbox" id="mapToggle" aria-label="Toggle satellite view">
|
||||
<span>🛰️ Satellite</span>
|
||||
</label>
|
||||
<label class="map-toggle-wrapper" style="cursor: pointer;">
|
||||
|
|
@ -506,7 +506,7 @@
|
|||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="modalTitle">Feature Details</h2>
|
||||
<button class="close-btn" onclick="closeFeatureModal()">×</button>
|
||||
<button class="close-btn" id="modalCloseBtn">×</button>
|
||||
</div>
|
||||
<div class="modal-body" id="modalBody">
|
||||
</div>
|
||||
|
|
@ -516,7 +516,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
|
||||
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js" integrity="sha512-puJW3E/qXDqYp9IfhAI54BJEaWIfloJ7JWs7OeD5i6ruC9JZL1gERT1wjtwXFlh7CjE7ZJ+/vcRZRkIYIb6p4g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@6.5.0/dist/turf.min.js"></script>
|
||||
<script src="google-sheets-config.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -136,20 +136,13 @@
|
|||
// Calculate area in square meters using Turf.js
|
||||
function getFeatureArea(feature) {
|
||||
try {
|
||||
if (!feature.geometry) return 0;
|
||||
|
||||
if (!feature || !feature.geometry) return 0;
|
||||
const geomType = feature.geometry.type;
|
||||
|
||||
if (geomType === 'Point') return 0;
|
||||
if (geomType === 'LineString' || geomType === 'MultiLineString') return 0;
|
||||
|
||||
// Use Haversine formula for rough area calculation
|
||||
const coords = feature.geometry.coordinates;
|
||||
|
||||
if (geomType === 'Polygon') {
|
||||
return calculatePolygonArea(coords[0]);
|
||||
} else if (geomType === 'MultiPolygon') {
|
||||
return coords.reduce((total, polygon) => total + calculatePolygonArea(polygon[0]), 0);
|
||||
if (geomType === 'Point' || geomType === 'LineString' || geomType === 'MultiLineString') return 0;
|
||||
// Only Polygon and MultiPolygon are valid for area
|
||||
if (geomType === 'Polygon' || geomType === 'MultiPolygon') {
|
||||
// turf.area expects a GeoJSON Feature or Geometry
|
||||
return turf.area(feature);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error calculating area:', e);
|
||||
|
|
@ -158,27 +151,12 @@
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Calculate polygon area using Shoelace formula
|
||||
// Deprecated: Calculate polygon area using Shoelace formula
|
||||
// Use Turf.js for area calculation instead
|
||||
function calculatePolygonArea(coords) {
|
||||
if (!coords || coords.length < 3) return 0;
|
||||
|
||||
let area = 0;
|
||||
const R = 6371000; // Earth radius in meters
|
||||
|
||||
for (let i = 0; i < coords.length - 1; i++) {
|
||||
const p1 = coords[i];
|
||||
const p2 = coords[i + 1];
|
||||
|
||||
const lat1 = (p1[1] * Math.PI) / 180;
|
||||
const lat2 = (p2[1] * Math.PI) / 180;
|
||||
const dLng = ((p2[0] - p1[0]) * Math.PI) / 180;
|
||||
|
||||
area += Math.sin(lat1) * Math.cos(lat1) * dLng;
|
||||
area += Math.sin(lat1) * Math.cos(lat2) * Math.sin(dLng);
|
||||
}
|
||||
|
||||
area = Math.abs(area * R * R / 2);
|
||||
return area;
|
||||
// Accepts coordinates, but now returns 0 and logs warning
|
||||
console.warn('calculatePolygonArea is deprecated. Use getFeatureArea(feature) with Turf.js instead.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert square meters to hectares and acres
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SmartCane Web Apps</title>
|
||||
<link rel="stylesheet" href="theme.css">
|
||||
<link rel="icon" type="image/x-icon" href="./res/main.png">
|
||||
<link rel="icon" type="image/png" href="./res/main.png">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
|
|
@ -227,7 +227,7 @@
|
|||
<li>View feature properties</li>
|
||||
<li>Download exports</li>
|
||||
</ul>
|
||||
<a href="./geojson_viewer" class="app-btn">Open Viewer</a>
|
||||
<a href="./geojson_viewer/" class="app-btn">Open Viewer</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Polygon Analysis & Editor</title>
|
||||
<link rel="stylesheet" href="../theme.css">
|
||||
<link rel="icon" type="image/x-icon" href="../res/poly.png">
|
||||
<link rel="icon" type="image/png" href="../res/poly.png">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.css" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet-measure@3.1.0/dist/leaflet-measure.css" />
|
||||
<link rel="stylesheet" href="../theme.css">
|
||||
<link rel="icon" type="image/x-icon" href="../res/map.png">
|
||||
<link rel="icon" type="image/png" href="../res/map.png">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ header {
|
|||
transition: background 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
margin-top: 0;
|
||||
flex: None;
|
||||
}
|
||||
flex: none;}
|
||||
|
||||
.back-btn:hover {
|
||||
background: rgba(255,255,255,0.3);
|
||||
|
|
|
|||
Loading…
Reference in a new issue