Merge branch 'code-improvements'

This commit is contained in:
Martin Folkerts 2025-06-24 09:58:56 +02:00
commit a316b30afc
42 changed files with 12611 additions and 1391 deletions

512
.Rhistory Normal file
View file

@ -0,0 +1,512 @@
})
# Verify farm_health_data exists and has content
if (!exists("farm_health_data") || nrow(farm_health_data) == 0) {
safe_log("farm_health_data not found or empty, generating default data", "WARNING")
# Create minimal fallback data
tryCatch({
# Get fields from boundaries
fields <- unique(AllPivots0$field)
# Create basic data frame with just field names
farm_health_data <- data.frame(
field = fields,
mean_ci = rep(NA, length(fields)),
ci_change = rep(NA, length(fields)),
ci_uniformity = rep(NA, length(fields)),
status = rep("Unknown", length(fields)),
anomaly_type = rep("Unknown", length(fields)),
priority_level = rep(5, length(fields)), # Low priority
age_weeks = rep(NA, length(fields)),
harvest_readiness = rep("Unknown", length(fields)),
stringsAsFactors = FALSE
)
safe_log("Created fallback farm_health_data with basic field information")
}, error = function(e) {
safe_log(paste("Error creating fallback farm_health_data:", e$message), "ERROR")
farm_health_data <<- data.frame(
field = character(),
mean_ci = numeric(),
ci_change = numeric(),
ci_uniformity = numeric(),
status = character(),
anomaly_type = character(),
priority_level = numeric(),
age_weeks = numeric(),
harvest_readiness = character(),
stringsAsFactors = FALSE
)
})
}
# Calculate farm health summary metrics
tryCatch({
# Generate farm health summary data
farm_health_data <- generate_farm_health_summary(
field_boundaries = AllPivots0,
ci_current = CI,
ci_previous = CI_m1,
harvesting_data = harvesting_data
)
# Log the summary data
safe_log(paste("Generated farm health summary with", nrow(farm_health_data), "fields"))
}, error = function(e) {
safe_log(paste("Error in farm health calculation:", e$message), "ERROR")
# Create empty dataframe if calculation failed
farm_health_data <- data.frame(
field = character(),
mean_ci = numeric(),
ci_change = numeric(),
ci_uniformity = numeric(),
status = character(),
anomaly_type = character(),
priority_level = numeric(),
age_weeks = numeric(),
harvest_readiness = character(),
stringsAsFactors = FALSE
)
})
# Render the velocity and acceleration indicators
tryCatch({
# Create and display the indicators using the imported utility function
velocity_plot <- create_velocity_acceleration_indicator(
health_data = farm_health_data,
ci_current = CI,
ci_prev1 = CI_m1,
ci_prev2 = CI_m2,
ci_prev3 = CI_m3,
field_boundaries = AllPivots0
)
# Print the visualization
print(velocity_plot)
# Create a table of fields with significant velocity changes
field_ci_metrics <- list()
# Process each field to get metrics
fields <- unique(AllPivots0$field)
for (field_name in fields) {
tryCatch({
# Get field boundary
field_shape <- AllPivots0 %>% dplyr::filter(field == field_name)
if (nrow(field_shape) == 0) next
# Extract CI values
ci_curr_values <- terra::extract(CI, field_shape)
ci_prev1_values <- terra::extract(CI_m1, field_shape)
# Calculate metrics
mean_ci_curr <- mean(ci_curr_values$CI, na.rm = TRUE)
mean_ci_prev1 <- mean(ci_prev1_values$CI, na.rm = TRUE)
velocity <- mean_ci_curr - mean_ci_prev1
# Store in list
field_ci_metrics[[field_name]] <- list(
field = field_name,
ci_current = mean_ci_curr,
ci_prev1 = mean_ci_prev1,
velocity = velocity
)
}, error = function(e) {
safe_log(paste("Error processing field", field_name, "for velocity table:", e$message), "WARNING")
})
}
# Convert list to data frame
velocity_df <- do.call(rbind, lapply(field_ci_metrics, function(x) {
data.frame(
field = x$field,
ci_current = round(x$ci_current, 2),
ci_prev1 = round(x$ci_prev1, 2),
velocity = round(x$velocity, 2),
direction = ifelse(x$velocity >= 0, "Improving", "Declining")
)
}))
# Select top 5 positive and top 5 negative velocity fields
top_positive <- velocity_df %>%
dplyr::filter(velocity > 0) %>%
dplyr::arrange(desc(velocity)) %>%
dplyr::slice_head(n = 5)
top_negative <- velocity_df %>%
dplyr::filter(velocity < 0) %>%
dplyr::arrange(velocity) %>%
dplyr::slice_head(n = 5)
# Display the tables if we have data
if (nrow(top_positive) > 0) {
cat("<h4>Fields with Fastest Improvement</h4>")
knitr::kable(top_positive %>%
dplyr::select(Field = field,
`Current CI` = ci_current,
`Previous CI` = ci_prev1,
`Weekly Change` = velocity))
}
if (nrow(top_negative) > 0) {
cat("<h4>Fields with Fastest Decline</h4>")
knitr::kable(top_negative %>%
dplyr::select(Field = field,
`Current CI` = ci_current,
`Previous CI` = ci_prev1,
`Weekly Change` = velocity))
}
}, error = function(e) {
safe_log(paste("Error rendering velocity visualization:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating velocity visualization.</div>")
})
# Generate anomaly timeline visualization
tryCatch({
# Use the imported function to create the anomaly timeline
anomaly_timeline <- create_anomaly_timeline(
field_boundaries = AllPivots0,
ci_data = CI_quadrant,
days_to_include = 90 # Show last 90 days of data
)
# Display the timeline
print(anomaly_timeline)
}, error = function(e) {
safe_log(paste("Error generating anomaly timeline:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating anomaly timeline visualization.</div>")
})
# Verify farm_health_data exists and has content
if (!exists("farm_health_data") || nrow(farm_health_data) == 0) {
safe_log("farm_health_data not found or empty, generating default data", "WARNING")
# Create minimal fallback data
tryCatch({
# Get fields from boundaries
fields <- unique(AllPivots0$field)
# Create basic data frame with just field names
farm_health_data <- data.frame(
field = fields,
mean_ci = rep(NA, length(fields)),
ci_change = rep(NA, length(fields)),
ci_uniformity = rep(NA, length(fields)),
status = rep("Unknown", length(fields)),
anomaly_type = rep("Unknown", length(fields)),
priority_level = rep(5, length(fields)), # Low priority
age_weeks = rep(NA, length(fields)),
harvest_readiness = rep("Unknown", length(fields)),
stringsAsFactors = FALSE
)
safe_log("Created fallback farm_health_data with basic field information")
}, error = function(e) {
safe_log(paste("Error creating fallback farm_health_data:", e$message), "ERROR")
farm_health_data <<- data.frame(
field = character(),
mean_ci = numeric(),
ci_change = numeric(),
ci_uniformity = numeric(),
status = character(),
anomaly_type = character(),
priority_level = numeric(),
age_weeks = numeric(),
harvest_readiness = character(),
stringsAsFactors = FALSE
)
})
}
# ADVANCED ANALYTICS FUNCTIONS
# Note: These functions are now imported from executive_report_utils.R
# The utility file contains functions for velocity/acceleration indicators,
# anomaly timeline creation, age cohort mapping, and cohort performance charts
safe_log("Using analytics functions from executive_report_utils.R")
# Chunk 1: setup_parameters
# Set up basic report parameters from input values
report_date <- params$report_date
mail_day <- params$mail_day
borders <- params$borders
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
# Environment setup notes (commented out)
# # Activeer de renv omgeving
# renv::activate()
# renv::deactivate()
# # Optioneel: Herstel de omgeving als dat nodig is
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
# renv::restore()
# Chunk 2: load_libraries
# Configure knitr options
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
# Path management
library(here)
# Spatial data libraries
library(sf)
library(terra)
library(exactextractr)
# library(raster) - Removed as it's no longer maintained
# Data manipulation and visualization
library(tidyverse) # Includes dplyr, ggplot2, etc.
library(tmap)
library(lubridate)
library(zoo)
# Machine learning
library(rsample)
library(caret)
library(randomForest)
library(CAST)
# Load custom utility functions
tryCatch({
source("report_utils.R")
}, error = function(e) {
message(paste("Error loading report_utils.R:", e$message))
# Try alternative path if the first one fails
tryCatch({
source(here::here("r_app", "report_utils.R"))
}, error = function(e) {
stop("Could not load report_utils.R from either location: ", e$message)
})
})
# Load executive report utilities
tryCatch({
source("executive_report_utils.R")
}, error = function(e) {
message(paste("Error loading executive_report_utils.R:", e$message))
# Try alternative path if the first one fails
tryCatch({
source(here::here("r_app", "executive_report_utils.R"))
}, error = function(e) {
stop("Could not load executive_report_utils.R from either location: ", e$message)
})
})
# Chunk 1: setup_parameters
# Set up basic report parameters from input values
report_date <- params$report_date
mail_day <- params$mail_day
borders <- params$borders
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
# Environment setup notes (commented out)
# # Activeer de renv omgeving
# renv::activate()
# renv::deactivate()
# # Optioneel: Herstel de omgeving als dat nodig is
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
# renv::restore()
# Chunk 2: load_libraries
# Configure knitr options
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
# Path management
library(here)
# Spatial data libraries
library(sf)
library(terra)
library(exactextractr)
# library(raster) - Removed as it's no longer maintained
# Data manipulation and visualization
library(tidyverse) # Includes dplyr, ggplot2, etc.
library(tmap)
library(lubridate)
library(zoo)
# Machine learning
library(rsample)
library(caret)
library(randomForest)
library(CAST)
# Load custom utility functions
tryCatch({
source("report_utils.R")
}, error = function(e) {
message(paste("Error loading report_utils.R:", e$message))
# Try alternative path if the first one fails
tryCatch({
source(here::here("r_app", "report_utils.R"))
}, error = function(e) {
stop("Could not load report_utils.R from either location: ", e$message)
})
})
# Load executive report utilities
tryCatch({
source("executive_report_utils.R")
}, error = function(e) {
message(paste("Error loading executive_report_utils.R:", e$message))
# Try alternative path if the first one fails
tryCatch({
source(here::here("r_app", "executive_report_utils.R"))
}, error = function(e) {
stop("Could not load executive_report_utils.R from either location: ", e$message)
})
})
# Chunk 1: setup_parameters
# Set up basic report parameters from input values
report_date <- params$report_date
mail_day <- params$mail_day
borders <- params$borders
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
# Environment setup notes (commented out)
# # Activeer de renv omgeving
# renv::activate()
# renv::deactivate()
# # Optioneel: Herstel de omgeving als dat nodig is
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
# renv::restore()
# Chunk 2: load_libraries
# Configure knitr options
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
# Path management
library(here)
# Spatial data libraries
library(sf)
library(terra)
library(exactextractr)
# library(raster) - Removed as it's no longer maintained
# Data manipulation and visualization
library(tidyverse) # Includes dplyr, ggplot2, etc.
library(tmap)
library(lubridate)
library(zoo)
# Machine learning
library(rsample)
library(caret)
library(randomForest)
library(CAST)
# Load custom utility functions
# tryCatch({
# source("report_utils.R")
# }, error = function(e) {
# message(paste("Error loading report_utils.R:", e$message))
# # Try alternative path if the first one fails
# tryCatch({
source(here::here("r_app", "report_utils.R"))
# }, error = function(e) {
# stop("Could not load report_utils.R from either location: ", e$message)
# })
# })
# Load executive report utilities
# tryCatch({
# source("executive_report_utils.R")
# }, error = function(e) {
# message(paste("Error loading executive_report_utils.R:", e$message))
# # Try alternative path if the first one fails
# tryCatch({
source(here::here("r_app", "executive_report_utils.R"))
# Chunk 1: setup_parameters
# Set up basic report parameters from input values
report_date <- params$report_date
mail_day <- params$mail_day
borders <- params$borders
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
# Environment setup notes (commented out)
# # Activeer de renv omgeving
# renv::activate()
# renv::deactivate()
# # Optioneel: Herstel de omgeving als dat nodig is
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
# renv::restore()
# Chunk 2: load_libraries
# Configure knitr options
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
# Path management
library(here)
# Spatial data libraries
library(sf)
library(terra)
library(exactextractr)
# library(raster) - Removed as it's no longer maintained
# Data manipulation and visualization
library(tidyverse) # Includes dplyr, ggplot2, etc.
library(tmap)
library(lubridate)
library(zoo)
# Machine learning
library(rsample)
library(caret)
library(randomForest)
library(CAST)
# Load custom utility functions
# tryCatch({
# source("report_utils.R")
# }, error = function(e) {
# message(paste("Error loading report_utils.R:", e$message))
# # Try alternative path if the first one fails
# tryCatch({
source(here::here("r_app", "report_utils.R"))
# }, error = function(e) {
# stop("Could not load report_utils.R from either location: ", e$message)
# })
# })
# Load executive report utilities
# tryCatch({
# source("executive_report_utils.R")
# }, error = function(e) {
# message(paste("Error loading executive_report_utils.R:", e$message))
# # Try alternative path if the first one fails
# tryCatch({
source(here::here("r_app", "exec_dashboard", "executive_report_utils.R"))
# }, error = function(e) {
# stop("Could not load executive_report_utils.R from either location: ", e$message)
# })
# })
# Chunk 3: initialize_project_config
# Set the project directory from parameters
project_dir <- params$data_dir
# Source project parameters with error handling
# tryCatch({
source(here::here("r_app", "parameters_project.R"))
# }, error = function(e) {
# stop("Error loading parameters_project.R: ", e$message)
# })
# Log initial configuration
safe_log("Starting the R Markdown script")
safe_log(paste("mail_day params:", params$mail_day))
safe_log(paste("report_date params:", params$report_date))
safe_log(paste("mail_day variable:", mail_day))
# Set locale for consistent date formatting
Sys.setlocale("LC_TIME", "C")
# Initialize date variables from parameters
today <- as.character(report_date)
mail_day_as_character <- as.character(mail_day)
# Calculate week days
report_date_as_week_day <- weekdays(lubridate::ymd(today))
days_of_week <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
# Calculate initial week number
week <- lubridate::week(today)
safe_log(paste("Initial week calculation:", week, "today:", today))
# Calculate previous dates for comparisons
today_minus_1 <- as.character(lubridate::ymd(today) - 7)
today_minus_2 <- as.character(lubridate::ymd(today) - 14)
today_minus_3 <- as.character(lubridate::ymd(today) - 21)
# Log the weekday calculations for debugging
safe_log(paste("Report date weekday:", report_date_as_week_day))
safe_log(paste("Weekday index:", which(days_of_week == report_date_as_week_day)))
safe_log(paste("Mail day:", mail_day_as_character))
safe_log(paste("Mail day index:", which(days_of_week == mail_day_as_character)))
# Adjust week calculation based on mail day
if (which(days_of_week == report_date_as_week_day) > which(days_of_week == mail_day_as_character)) {
safe_log("Adjusting weeks because of mail day")
week <- lubridate::week(today) + 1
today_minus_1 <- as.character(lubridate::ymd(today))
today_minus_2 <- as.character(lubridate::ymd(today) - 7)
today_minus_3 <- as.character(lubridate::ymd(today) - 14)
}
# Generate subtitle for report
subtitle_var <- paste("Report generated on", Sys.Date())
# Calculate week numbers for previous weeks
week_minus_1 <- week - 1
week_minus_2 <- week - 2
week_minus_3 <- week - 3
# Format current week with leading zeros
week <- sprintf("%02d", week)
# Get years for each date
year <- lubridate::year(today)
year_1 <- lubridate::year(today_minus_1)
year_2 <- lubridate::year(today_minus_2)
year_3 <- lubridate::year(today_minus_3)
# 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)
})

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"python.REPL.enableREPLSmartSend": false
}

Binary file not shown.

BIN
figure/sub_chunk_8433-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

File diff suppressed because one or more lines are too long

View file

@ -29,14 +29,32 @@
"from osgeo import gdal\n", "from osgeo import gdal\n",
"\n", "\n",
"from sentinelhub import MimeType, CRS, BBox, SentinelHubRequest, SentinelHubDownloadClient, \\\n", "from sentinelhub import MimeType, CRS, BBox, SentinelHubRequest, SentinelHubDownloadClient, \\\n",
" DataCollection, bbox_to_dimensions, DownloadRequest, SHConfig, BBoxSplitter, read_data\n", " DataCollection, bbox_to_dimensions, DownloadRequest, SHConfig, BBoxSplitter, read_data, Geometry, SentinelHubCatalog\n",
"\n", "\n",
"config = SHConfig()\n", "config = SHConfig()\n",
"catalog = SentinelHubCatalog(config=config)\n",
"\n", "\n",
"import time\n", "import time\n",
"import shutil" "import shutil\n",
"\n",
"import geopandas as gpd\n",
"from shapely.geometry import MultiLineString, MultiPolygon, Polygon, box, shape\n"
] ]
}, },
{
"cell_type": "markdown",
"id": "d19f7f18",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "80dc31ce",
"metadata": {},
"outputs": [],
"source": []
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 2, "execution_count": 2,
@ -103,29 +121,29 @@
"source": [ "source": [
"#project = 'chemba' #or xinavane or chemba_test_8b\n", "#project = 'chemba' #or xinavane or chemba_test_8b\n",
"#project = 'xinavane' #or xinavane or chemba_test_8b\n", "#project = 'xinavane' #or xinavane or chemba_test_8b\n",
"project = 'chemba_test_8b' #or xinavane or chemba_test_8b\n" "project = 'citrus_brazil_trial' #or xinavane or chemba_test_8b\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": 7,
"id": "c9f79e81-dff8-4109-8d26-6c423142dcf2", "id": "d5a99e68",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"# Adjust the number of days needed\n", "# Adjust the number of days needed\n",
"days = 7 #change back to 28 which is the default. 3 years is 1095 days." "days = 200 #change back to 28 which is the default. 3 years is 1095 days."
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": 8,
"id": "e18bdf8f-be4b-44ab-baaa-de5de60d92cb", "id": "f2b0e629",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"#delete all the satellite outputs -> then True\n", "#delete all the satellite outputs -> then True\n",
"empty_folder_question = False" "empty_folder_question = True"
] ]
}, },
{ {
@ -150,6 +168,7 @@
"BASE_PATH_SINGLE_IMAGES = Path(BASE_PATH / 'single_images')\n", "BASE_PATH_SINGLE_IMAGES = Path(BASE_PATH / 'single_images')\n",
"folder_for_merged_tifs = str(BASE_PATH / 'merged_tif')\n", "folder_for_merged_tifs = str(BASE_PATH / 'merged_tif')\n",
"folder_for_virtual_raster = str(BASE_PATH / 'merged_virtual')\n", "folder_for_virtual_raster = str(BASE_PATH / 'merged_virtual')\n",
"geojson_file = Path(BASE_PATH /'Data'/ 'pivot.geojson') #the geojsons should have the same name\n",
" \n", " \n",
"# Check if the folders exist, and if not, create them\n", "# Check if the folders exist, and if not, create them\n",
"if not os.path.exists(BASE_PATH_SINGLE_IMAGES):\n", "if not os.path.exists(BASE_PATH_SINGLE_IMAGES):\n",
@ -164,7 +183,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 15, "execution_count": 10,
"id": "244b5752-4f02-4347-9278-f6a0a46b88f4", "id": "244b5752-4f02-4347-9278-f6a0a46b88f4",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
@ -177,10 +196,10 @@
" input: [{\n", " input: [{\n",
" bands: \n", " bands: \n",
" [\"CoastalBlue\", \"Blue\", \"Green\", \"GreenI\", \"Yellow\", \"Red\", \n", " [\"CoastalBlue\", \"Blue\", \"Green\", \"GreenI\", \"Yellow\", \"Red\", \n",
" \"RedEdge\", \"NIR\", \"UDM2_Clear\"]\n", " \"RedEdge\", \"NIR\", \"udm1\" ]\n",
" }],\n", " }],\n",
" output: {\n", " output: {\n",
" bands: 9 \n", " bands: 8\n",
" //sampleType: \"FLOAT32\"\n", " //sampleType: \"FLOAT32\"\n",
" }\n", " }\n",
" };\n", " };\n",
@ -195,11 +214,10 @@
" var scaledYellow = [2.5 * sample.Yellow / 10000];\n", " var scaledYellow = [2.5 * sample.Yellow / 10000];\n",
" var scaledRedEdge = [2.5 * sample.RedEdge / 10000];\n", " var scaledRedEdge = [2.5 * sample.RedEdge / 10000];\n",
" var scaledNIR = [2.5 * sample.NIR / 10000];\n", " var scaledNIR = [2.5 * sample.NIR / 10000];\n",
" var UDM2_Clear = UDM2_Clear\n",
" \n", " \n",
" // Output the scaled bands\n", " // Output the scaled bands\n",
" \n", " \n",
" // if (sample.UDM2_Clear != 0) { \n", " // if (sample.udm1 == 0) { \n",
" return [\n", " return [\n",
" scaledCoastalBlue,\n", " scaledCoastalBlue,\n",
" scaledBlue,\n", " scaledBlue,\n",
@ -209,7 +227,6 @@
" scaledRed, \n", " scaledRed, \n",
" scaledRedEdge,\n", " scaledRedEdge,\n",
" scaledNIR,\n", " scaledNIR,\n",
" UDM2_Clear\n",
" ]\n", " ]\n",
" // } else {\n", " // } else {\n",
" // return [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]}\n", " // return [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]}\n",
@ -278,19 +295,20 @@
"text": [ "text": [
"Monthly time windows:\n", "Monthly time windows:\n",
"\n", "\n",
"2024-03-19\n", "2024-09-06\n",
"2024-03-20\n", "2024-09-07\n",
"2024-03-21\n", "2024-09-08\n",
"2024-03-22\n", "...\n",
"2024-03-23\n", "2025-03-22\n",
"2024-03-24\n", "2025-03-23\n",
"2024-03-25\n" "2025-03-24\n"
] ]
} }
], ],
"source": [ "source": [
"days_needed = int(os.environ.get(\"DAYS\", days))\n", "days_needed = int(os.environ.get(\"DAYS\", days))\n",
"date_str = os.environ.get(\"DATE\")\n", "date_str = os.environ.get(\"DATE\")\n",
"\n",
"if date_str:\n", "if date_str:\n",
" # Parse de datumstring naar een datetime.date object\n", " # Parse de datumstring naar een datetime.date object\n",
" end = datetime.datetime.strptime(date_str, \"%Y-%m-%d\").date()\n", " end = datetime.datetime.strptime(date_str, \"%Y-%m-%d\").date()\n",
@ -315,40 +333,6 @@
"\n" "\n"
] ]
}, },
{
"cell_type": "code",
"execution_count": 12,
"id": "93c715f7-4f7e-428e-bbb9-53a2d8f6e2c8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Monthly time windows:\n",
"\n",
"2024-03-19\n"
]
}
],
"source": [
"end = datetime.datetime(2024, 3, 19)\n",
"start = end \n",
"days_needed =1\n",
"slots = [(start + datetime.timedelta(days=i)).strftime('%Y-%m-%d') for i in range(days_needed)]\n",
"\n",
"print('Monthly time windows:\\n')\n",
"if len(slots) > 10:\n",
" for slot in slots[:3]:\n",
" print(slot)\n",
" print(\"...\")\n",
" for slot in slots[-3:]:\n",
" print(slot)\n",
"else:\n",
" for slot in slots:\n",
" print(slot)"
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "f8ea846f-783b-4460-a951-7b522273555f", "id": "f8ea846f-783b-4460-a951-7b522273555f",
@ -359,70 +343,415 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 13, "execution_count": 12,
"id": "1fb5dc6c-de83-4fb2-b3f7-ee9e7060a80d", "id": "f145bdd1",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"if project == 'chemba':\n", "geo_json = gpd.read_file(str(geojson_file))"
" chosen_area = [[34.946, -17.3516, 34.938, -17.2917], [34.883, -17.3516, 34.938, -17.2917]]\n",
"\n",
"if project == 'chemba_test_8b':\n",
" chosen_area = [[34.946, -17.3516, 34.938, -17.2917], [34.883, -17.3516, 34.938, -17.2917]]\n",
"\n",
"if project == 'xinavane':\n",
" chosen_area = [[32.6790, -25.0333, 32.7453, -25.0235], [32.6213, -25.0647, 32.6284, -25.0570]]"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 16, "execution_count": 13,
"id": "6c02d7de-cddf-4fc3-8d23-8431415d07b8", "id": "3e44fc64",
"metadata": {},
"outputs": [],
"source": [
"geometries = [Geometry(geometry, crs=CRS.WGS84) for geometry in geo_json.geometry]"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "50419beb",
"metadata": {},
"outputs": [],
"source": [
"shapely_geometries = [geometry.geometry for geometry in geometries]"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "308089ac",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
" Image downloaded for 2024-03-19\n", "Area bounding box: BBox(((-47.09879025717693, -22.67132809994226), (-47.09188307701802, -22.66813642658124)), crs=CRS('4326'))\n",
" Image downloaded for 2024-03-19\n" "\n"
]
}
],
"source": [
"bbox_splitter = BBoxSplitter(\n",
" shapely_geometries, CRS.WGS84, (1,1), reduce_bbox_sizes=True\n",
") # bounding box will be split into a grid of 5x4 bounding boxes\n",
"\n",
"# based on https://github.com/sentinel-hub/sentinelhub-py/blob/master/examples/large_area_utilities.ipynb \n",
"\n",
"print(\"Area bounding box: {}\\n\".format(bbox_splitter.get_area_bbox().__repr__()))\n",
"\n",
"bbox_list = bbox_splitter.get_bbox_list()\n",
"info_list = bbox_splitter.get_info_list()\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "8e330d6b",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"100.0\" height=\"100.0\" viewBox=\"-47.099066544383284 -22.67160438714862 0.007459754571620181 0.003744247773738607\" preserveAspectRatio=\"xMinYMin meet\"><g transform=\"matrix(1,0,0,-1,0,-45.3394645265235)\"><path fill-rule=\"evenodd\" fill=\"#66cc99\" stroke=\"#555555\" stroke-width=\"0.0001491950914324036\" opacity=\"0.6\" d=\"M -47.09805445286413,-22.66952337246588 L -47.09805851961499,-22.66952341396605 L -47.09831201628566,-22.6690573104498 L -47.09766248207074,-22.66894403661208 L -47.09743890798767,-22.66939352730967 L -47.09657817105054,-22.66896681839171 L -47.09661812458065,-22.66888004886227 L -47.09641381961848,-22.66877614597339 L -47.09612449194931,-22.66859711606797 L -47.09571930155018,-22.66905895027618 L -47.09517701850205,-22.66872806847029 L -47.0953727761646,-22.66834852803528 L -47.09494883808294,-22.66813642658124 L -47.09485879660592,-22.66814374727666 L -47.09188307701802,-22.66992047488275 L -47.09492613354303,-22.67132809994226 L -47.09500594723228,-22.67052749753276 L -47.09528028649954,-22.67029991687096 L -47.09556711980924,-22.67036970371914 L -47.09625642655305,-22.6700797885789 L -47.09785156654067,-22.67029446957762 L -47.09858155006654,-22.67039939171653 L -47.09879025717693,-22.66990727091475 L -47.09805445286413,-22.66952337246588 z\" /></g></svg>"
],
"text/plain": [
"<POLYGON Z ((-47.098 -22.67 0, -47.098 -22.67 0, -47.098 -22.669 0, -47.098 ...>"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"geometry_list = bbox_splitter.get_geometry_list()\n",
"\n",
"geometry_list[0]"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "62bc676c",
"metadata": {},
"outputs": [],
"source": [
"# Function to check if images are available for a given date\n",
"def is_image_available(date):\n",
" for bbox in bbox_list:\n",
" search_iterator = catalog.search(\n",
" collection=byoc,\n",
" bbox=bbox, # Define your bounding box\n",
" time=(date, date)\n",
" )\n",
" if len(list(search_iterator)) > 0:\n",
" return True\n",
" return False\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "8d686f8e",
"metadata": {},
"outputs": [],
"source": [
"# Filter slots to only include dates with available images\n",
"available_slots = [slot for slot in slots if is_image_available(slot)]\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "8536fb09",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['2024-09-06', '2024-09-07', '2024-09-08', '2024-09-09', '2024-09-10', '2024-09-11', '2024-09-12', '2024-09-13', '2024-09-14', '2024-09-17', '2024-09-18', '2024-09-19', '2024-09-20', '2024-09-22', '2024-09-23', '2024-09-25', '2024-09-26', '2024-09-27', '2024-09-28', '2024-09-29', '2024-09-30', '2024-10-01', '2024-10-03', '2024-10-08', '2024-10-13', '2024-10-15', '2024-10-16', '2024-10-18', '2024-10-22', '2024-10-23', '2024-10-29', '2024-10-30', '2024-10-31', '2024-11-01', '2024-11-05', '2024-11-06', '2024-11-09', '2024-11-10', '2024-11-11', '2024-11-13', '2024-11-14', '2024-11-15', '2024-11-17', '2024-11-19', '2024-11-20', '2024-11-24', '2024-11-25', '2024-11-26', '2024-11-27', '2024-12-01', '2024-12-02', '2024-12-04', '2024-12-05', '2024-12-06', '2024-12-07', '2024-12-08', '2024-12-09', '2024-12-10', '2024-12-11', '2024-12-15', '2024-12-17', '2024-12-18', '2024-12-19', '2024-12-21', '2024-12-24', '2024-12-25', '2024-12-28', '2024-12-29', '2024-12-30', '2025-01-01', '2025-01-02', '2025-01-04', '2025-01-05', '2025-01-06', '2025-01-07', '2025-01-08', '2025-01-09', '2025-01-10', '2025-01-11', '2025-01-12', '2025-01-13', '2025-01-14', '2025-01-15', '2025-01-16', '2025-01-19', '2025-01-20', '2025-01-21', '2025-01-22', '2025-01-23', '2025-01-24', '2025-01-25', '2025-01-27', '2025-01-28', '2025-01-30', '2025-02-05', '2025-02-06', '2025-02-08', '2025-02-10', '2025-02-12', '2025-02-13', '2025-02-14', '2025-02-15', '2025-02-16', '2025-02-17', '2025-02-18', '2025-02-20', '2025-02-21', '2025-02-22', '2025-02-23', '2025-02-25', '2025-02-27', '2025-03-01', '2025-03-02', '2025-03-03', '2025-03-04', '2025-03-05', '2025-03-06', '2025-03-07', '2025-03-08', '2025-03-09', '2025-03-11', '2025-03-12', '2025-03-14', '2025-03-15', '2025-03-16', '2025-03-17', '2025-03-18', '2025-03-19', '2025-03-20', '2025-03-21', '2025-03-22', '2025-03-23']\n",
"Total slots: 200\n",
"Available slots: 132\n",
"Excluded slots due to clouds: 68\n"
]
}
],
"source": [
"print(available_slots)\n",
"print(f\"Total slots: {len(slots)}\")\n",
"print(f\"Available slots: {len(available_slots)}\")\n",
"print(f\"Excluded slots due to clouds: {len(slots) - len(available_slots)}\")\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "5059aa6e",
"metadata": {},
"outputs": [],
"source": [
"def show_splitter(splitter, alpha=0.2, area_buffer=0.2, show_legend=False):\n",
" area_bbox = splitter.get_area_bbox()\n",
" minx, miny, maxx, maxy = area_bbox\n",
" lng, lat = area_bbox.middle\n",
" w, h = maxx - minx, maxy - miny\n",
" minx = minx - area_buffer * w\n",
" miny = miny - area_buffer * h\n",
" maxx = maxx + area_buffer * w\n",
" maxy = maxy + area_buffer * h\n",
"\n",
" fig = plt.figure(figsize=(10, 10))\n",
" ax = fig.add_subplot(111)\n",
"\n",
" base_map = Basemap(\n",
" projection=\"mill\",\n",
" lat_0=lat,\n",
" lon_0=lng,\n",
" llcrnrlon=minx,\n",
" llcrnrlat=miny,\n",
" urcrnrlon=maxx,\n",
" urcrnrlat=maxy,\n",
" resolution=\"l\",\n",
" epsg=4326,\n",
" )\n",
" base_map.drawcoastlines(color=(0, 0, 0, 0))\n",
"\n",
" area_shape = splitter.get_area_shape()\n",
"\n",
" if isinstance(area_shape, Polygon):\n",
" polygon_iter = [area_shape]\n",
" elif isinstance(area_shape, MultiPolygon):\n",
" polygon_iter = area_shape.geoms\n",
" else:\n",
" raise ValueError(f\"Geometry of type {type(area_shape)} is not supported\")\n",
"\n",
" for polygon in polygon_iter:\n",
" if isinstance(polygon.boundary, MultiLineString):\n",
" for linestring in polygon.boundary:\n",
" ax.add_patch(PltPolygon(np.array(linestring), closed=True, facecolor=(0, 0, 0, 0), edgecolor=\"red\"))\n",
" else:\n",
" ax.add_patch(\n",
" PltPolygon(np.array(polygon.boundary.coords), closed=True, facecolor=(0, 0, 0, 0), edgecolor=\"red\")\n",
" )\n",
"\n",
" bbox_list = splitter.get_bbox_list()\n",
" info_list = splitter.get_info_list()\n",
"\n",
" cm = plt.get_cmap(\"jet\", len(bbox_list))\n",
" legend_shapes = []\n",
" for i, bbox in enumerate(bbox_list):\n",
" wgs84_bbox = bbox.transform(CRS.WGS84).get_polygon()\n",
"\n",
" tile_color = tuple(list(cm(i))[:3] + [alpha])\n",
" ax.add_patch(PltPolygon(np.array(wgs84_bbox), closed=True, facecolor=tile_color, edgecolor=\"green\"))\n",
"\n",
" if show_legend:\n",
" legend_shapes.append(plt.Rectangle((0, 0), 1, 1, fc=cm(i)))\n",
"\n",
" if show_legend:\n",
" legend_names = []\n",
" for info in info_list:\n",
" legend_name = \"{},{}\".format(info[\"index_x\"], info[\"index_y\"])\n",
"\n",
" for prop in [\"grid_index\", \"tile\"]:\n",
" if prop in info:\n",
" legend_name = \"{},{}\".format(info[prop], legend_name)\n",
"\n",
" legend_names.append(legend_name)\n",
"\n",
" plt.legend(legend_shapes, legend_names)\n",
" plt.tight_layout()\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "39fda909",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"c:\\Users\\timon\\anaconda3\\Lib\\site-packages\\sentinelhub\\geometry.py:137: SHDeprecationWarning: Initializing `BBox` objects from `BBox` objects will no longer be possible in future versions.\n",
" return cls._tuple_from_bbox(bbox)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" Image downloaded for 2024-09-06\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"c:\\Users\\timon\\anaconda3\\Lib\\site-packages\\sentinelhub\\geometry.py:137: SHDeprecationWarning: Initializing `BBox` objects from `BBox` objects will no longer be possible in future versions.\n",
" return cls._tuple_from_bbox(bbox)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" Image downloaded for 2024-09-07\n",
" Image downloaded for 2024-09-08\n",
" Image downloaded for 2024-09-09\n",
" Image downloaded for 2024-09-10\n",
" Image downloaded for 2024-09-11\n",
" Image downloaded for 2024-09-12\n",
" Image downloaded for 2024-09-13\n",
" Image downloaded for 2024-09-14\n",
" Image downloaded for 2024-09-17\n",
" Image downloaded for 2024-09-18\n",
" Image downloaded for 2024-09-19\n",
" Image downloaded for 2024-09-20\n",
" Image downloaded for 2024-09-22\n",
" Image downloaded for 2024-09-23\n",
" Image downloaded for 2024-09-25\n",
" Image downloaded for 2024-09-26\n",
" Image downloaded for 2024-09-27\n",
" Image downloaded for 2024-09-28\n",
" Image downloaded for 2024-09-29\n",
" Image downloaded for 2024-09-30\n",
" Image downloaded for 2024-10-01\n",
" Image downloaded for 2024-10-03\n",
" Image downloaded for 2024-10-08\n",
" Image downloaded for 2024-10-13\n",
" Image downloaded for 2024-10-15\n",
" Image downloaded for 2024-10-16\n",
" Image downloaded for 2024-10-18\n",
" Image downloaded for 2024-10-22\n",
" Image downloaded for 2024-10-23\n",
" Image downloaded for 2024-10-29\n",
" Image downloaded for 2024-10-30\n",
" Image downloaded for 2024-10-31\n",
" Image downloaded for 2024-11-01\n",
" Image downloaded for 2024-11-05\n",
" Image downloaded for 2024-11-06\n",
" Image downloaded for 2024-11-09\n",
" Image downloaded for 2024-11-10\n",
" Image downloaded for 2024-11-11\n",
" Image downloaded for 2024-11-13\n",
" Image downloaded for 2024-11-14\n",
" Image downloaded for 2024-11-15\n",
" Image downloaded for 2024-11-17\n",
" Image downloaded for 2024-11-19\n",
" Image downloaded for 2024-11-20\n",
" Image downloaded for 2024-11-24\n",
" Image downloaded for 2024-11-25\n",
" Image downloaded for 2024-11-26\n",
" Image downloaded for 2024-11-27\n",
" Image downloaded for 2024-12-01\n",
" Image downloaded for 2024-12-02\n",
" Image downloaded for 2024-12-04\n",
" Image downloaded for 2024-12-05\n",
" Image downloaded for 2024-12-06\n",
" Image downloaded for 2024-12-07\n",
" Image downloaded for 2024-12-08\n",
" Image downloaded for 2024-12-09\n",
" Image downloaded for 2024-12-10\n",
" Image downloaded for 2024-12-11\n",
" Image downloaded for 2024-12-15\n",
" Image downloaded for 2024-12-17\n",
" Image downloaded for 2024-12-18\n",
" Image downloaded for 2024-12-19\n",
" Image downloaded for 2024-12-21\n",
" Image downloaded for 2024-12-24\n",
" Image downloaded for 2024-12-25\n",
" Image downloaded for 2024-12-28\n",
" Image downloaded for 2024-12-29\n",
" Image downloaded for 2024-12-30\n",
" Image downloaded for 2025-01-01\n",
" Image downloaded for 2025-01-02\n",
" Image downloaded for 2025-01-04\n",
" Image downloaded for 2025-01-05\n",
" Image downloaded for 2025-01-06\n",
" Image downloaded for 2025-01-07\n",
" Image downloaded for 2025-01-08\n",
" Image downloaded for 2025-01-09\n",
" Image downloaded for 2025-01-10\n",
" Image downloaded for 2025-01-11\n",
" Image downloaded for 2025-01-12\n",
" Image downloaded for 2025-01-13\n",
" Image downloaded for 2025-01-14\n",
" Image downloaded for 2025-01-15\n",
" Image downloaded for 2025-01-16\n",
" Image downloaded for 2025-01-19\n",
" Image downloaded for 2025-01-20\n",
" Image downloaded for 2025-01-21\n",
" Image downloaded for 2025-01-22\n",
" Image downloaded for 2025-01-23\n",
" Image downloaded for 2025-01-24\n",
" Image downloaded for 2025-01-25\n",
" Image downloaded for 2025-01-27\n",
" Image downloaded for 2025-01-28\n",
" Image downloaded for 2025-01-30\n",
" Image downloaded for 2025-02-05\n",
" Image downloaded for 2025-02-06\n",
" Image downloaded for 2025-02-08\n",
" Image downloaded for 2025-02-10\n",
" Image downloaded for 2025-02-12\n",
" Image downloaded for 2025-02-13\n",
" Image downloaded for 2025-02-14\n",
" Image downloaded for 2025-02-15\n",
" Image downloaded for 2025-02-16\n",
" Image downloaded for 2025-02-17\n",
" Image downloaded for 2025-02-18\n",
" Image downloaded for 2025-02-20\n",
" Image downloaded for 2025-02-21\n",
" Image downloaded for 2025-02-22\n",
" Image downloaded for 2025-02-23\n",
" Image downloaded for 2025-02-25\n",
" Image downloaded for 2025-02-27\n",
" Image downloaded for 2025-03-01\n",
" Image downloaded for 2025-03-02\n",
" Image downloaded for 2025-03-03\n",
" Image downloaded for 2025-03-04\n",
" Image downloaded for 2025-03-05\n",
" Image downloaded for 2025-03-06\n",
" Image downloaded for 2025-03-07\n",
" Image downloaded for 2025-03-08\n",
" Image downloaded for 2025-03-09\n",
" Image downloaded for 2025-03-11\n",
" Image downloaded for 2025-03-12\n",
" Image downloaded for 2025-03-14\n",
" Image downloaded for 2025-03-15\n",
" Image downloaded for 2025-03-16\n",
" Image downloaded for 2025-03-17\n",
" Image downloaded for 2025-03-18\n",
" Image downloaded for 2025-03-19\n",
" Image downloaded for 2025-03-20\n",
" Image downloaded for 2025-03-21\n",
" Image downloaded for 2025-03-22\n",
" Image downloaded for 2025-03-23\n"
] ]
} }
], ],
"source": [ "source": [
"# Load areas outside the loop if they remain constant\n", "# Load areas outside the loop if they remain constant\n",
"bbox_area = json.dumps(chosen_area)\n", "#bbox_area = json.dumps(chosen_area)\n",
"areas = json.loads(os.getenv('BBOX', bbox_area))\n", "#areas = json.loads(os.getenv('BBOX', bbox_area))\n",
"resolution = 3\n", "resolution = 3\n",
"\n", "\n",
"for slot in slots:\n", "for slot in available_slots:\n",
" for area in areas:\n", " for bbox in bbox_list:\n",
" bbox = BBox(bbox=area, crs=CRS.WGS84)\n", " bbox = BBox(bbox=bbox, crs=CRS.WGS84)\n",
" size = bbox_to_dimensions(bbox, resolution=resolution)\n", " size = bbox_to_dimensions(bbox, resolution=resolution)\n",
" download_function(slot, bbox, size)" " download_function(slot, bbox, size)"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 17, "execution_count": 22,
"id": "68db3c15-6f94-432e-b315-c329e4251b21", "id": "b7df8a5a",
"metadata": { "metadata": {},
"tags": [] "outputs": [],
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Merging complete\n"
]
}
],
"source": [ "source": [
"for slot in slots:\n", "for slot in available_slots:\n",
" merge_files(slot)\n", " merge_files(slot)"
"\n",
"print('Merging complete')"
] ]
}, },
{ {
@ -437,10 +766,109 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 23,
"id": "cb3fa856-a550-4899-844a-e69209bba3ad", "id": "cb3fa856-a550-4899-844a-e69209bba3ad",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Emptied folder: ..\\laravel_app\\storage\\app\\citrus_brazil_trial\\merged_virtual\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-06\\\\b043cc2d8d4c16ad7136d80bc773d9a6'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-07\\\\0c7fd3960f4f89065e4763012ee2d5bf'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-08\\\\863975cad69c994bee3cbcf8c4c66969'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-09\\\\71cb610837ce5a64d07e0ecedcf8a1f4'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-10\\\\4612a3bd3f125c866f0a5ac03be77d7d'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-11\\\\e1b17c3480bfbcc90893a4922e34ee75'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-12\\\\2ae0d2333c294e6c756ed52d156a0847'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-13\\\\3c30518183fb168f51b5642df92d386e'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-14\\\\14ca5b977611b62d8175b2eadf79b9a4'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-17\\\\5feb7fd93dc82d95616ca072e0092e48'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-18\\\\2c3c82d1217e30ba8b784d2006dc6eda'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-19\\\\0c82b031dacac3898a0d9b201832fe58'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-20\\\\8184249ce19d16a5eb46849fb71735f9'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-22\\\\11aed9763515aa396b91af85196cdae9'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-23\\\\88fe8d2633709c6e133667bd4b40899f'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-25\\\\cf8b1c1ffd3fef5752ceb69750b27547'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-26\\\\f7ca306f684f53ed384fac004491a613'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-27\\\\9d3276f2df5feae36237bf88882d5653'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-28\\\\6cbcccc4dfa89b7b1adbe765d676a970'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-29\\\\b452dd0597eac790f3556266165cf6eb'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-09-30\\\\bd77c0078ff31b690f18b7e47120cbe2'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-01\\\\674447e677f39c5fd90cf16d1532762b'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-03\\\\8a621381e180b7c6561d63463c98f6c6'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-08\\\\d774ecb97260c424f0ee978e9ad60ffc'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-13\\\\8b199b4d58832a4423030090e4fc0b73'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-15\\\\d40037ddaf70ecfdb7bc129935829f4a'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-16\\\\e16a77670903d84d205c6696e76463d7'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-18\\\\c39346f991a2ce0d049626064bcb81b7'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-22\\\\49104dad0d9cd58782679ab9f2fbe0f6'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-23\\\\b90dda221ad426daca9ebb7848558f4d'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-29\\\\e1b930c1d8a4ea4348d6283bb88b1605'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-30\\\\403e7bd13f7e7095ed52d444b99f77a2'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-10-31\\\\c2f7e70356620fd6c0a8acb428629b2b'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-01\\\\3a5fd53572d0739709008f079db0141d'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-05\\\\6d7966d56889b59ab95e6d8dd07e1629'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-06\\\\7f0d3dff7bcce30ba35b3fe948b94d06'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-09\\\\1bfaabaa18d20650876f3f0f837fc464'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-10\\\\4df7433c53fb91d39fbd8d6eb203a15f'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-11\\\\4a85241d10149b508127c5c42c58ce99'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-13\\\\4fee940f019c897926febd3ce122242d'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-14\\\\9870499724f5f8a700d5c9b71256c62f'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-15\\\\4d05dbbb8da72096ca77c283e804a022'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-17\\\\e6272d213276d0dde53704378704c813'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-19\\\\af8d29be427d674b55748ec8903848da'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-20\\\\793f4696d7ea76612f7f1cf673301505'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-24\\\\5f6e5af6b78179063b799391c6597d61'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-25\\\\208ea4f8505eab64fd572e25093a8f4b'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-26\\\\fac9b5276038bea8718b3f571336c3cf'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-11-27\\\\3d6409a209cda70e048113cf83da1c90'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-01\\\\7ac210da516cb232e9d76267f8ee39c7'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-02\\\\700b4040e3e2b720a1dbf7b25faf9059'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-04\\\\641f5a937bd11cfd6d24d85654f734e9'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-05\\\\eaeec4515a330c2343940e91620860a0'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-06\\\\f0bd756aa6772d9c3ff9f84af6434cb4'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-07\\\\94ad9369c6400b1acbdf8b894b1cb1c8'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-08\\\\284dd45d9d1813a2329460cc42cfdb4c'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-09\\\\aeeaa22f6830a9c85b45dd9c7f4c0d50'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-10\\\\0fe500fdba20435186533274c3209605'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-11\\\\9def53810a174b79a049ad9e1efec24e'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-15\\\\47195abd1c0e772244cf441056f5e991'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-17\\\\34982fe049a0fc1dbbe2f30f05705e6c'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-18\\\\eb4436f4ddfa0e59bf26c03183bd15eb'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-19\\\\f8c1b9e189ec749d62bee897ba91f10f'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-21\\\\ab2d84cebe8244281e0ff7d9006f0fb5'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-24\\\\b6e260c9430f266d1c8fd733c7a12c1e'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-25\\\\bd1b758761ac2ba6c943b00b5163bc2a'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-28\\\\dc365a6a3662767d6322833d515a1761'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-29\\\\67852f7ef1f65898221ce463ea2f51a2'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2024-12-30\\\\df1ffaa3e2ef4a0a56d4fddb87b02cd0'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-01\\\\914a1eedf54d698ded4481193249be40'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-02\\\\12e7c086459b3546676025cd992bc2ec'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-04\\\\9a345a95b91a71ca0d097d867aa0caf9'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-05\\\\d9662fb43eac4f6c78a99d8f18ad5615'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-06\\\\b5464b7cca316a27176c22bd43c77310'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-07\\\\ff8b06ca380223236930942f024e8c9d'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-08\\\\cd5476dfb101f204fee49eb4b3a6f009'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-09\\\\9fb60730334264b42a496aa1e050620d'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-10\\\\b26157b68926589158b98a1b0969f3f4'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-11\\\\af7aa729b49095a402cbdf11cf017297'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-12\\\\234bc64b3c483c4748f6246243614a88'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-13\\\\54748c54f681b300b70e2883fec8ea96'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-14\\\\df8c9ba68dfab8d405f80bc8dc26db88'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-15\\\\99b61d572a1be3143a067f74eff66079'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-16\\\\e1e1d6b5aaca9fba11e7bf62579a14ec'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-19\\\\f417a4934ae7a1e36f08c3d6489f17b8'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-20\\\\c8b490408b2264c6448192f12e792a75'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-21\\\\43dd07df050993853e78b63d6d956fe8'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-22\\\\7597b91f9fef0a84f0259802912723ac'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-23\\\\477ff86feb1c2850f1a24ee962f6f6ec'\n",
"Error: [WinError 5] Toegang geweigerd: '..\\\\laravel_app\\\\storage\\\\app\\\\citrus_brazil_trial\\\\single_images\\\\2025-01-24\\\\bee206b01468e0cc104bf60fdfe33874'\n",
"Emptied folder: ..\\laravel_app\\storage\\app\\citrus_brazil_trial\\single_images\n"
]
}
],
"source": [ "source": [
"# List of folder names\n", "# List of folder names\n",
"\n", "\n",
@ -475,16 +903,29 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 24,
"id": "0145b399-dfad-448a-9f0d-fa975fb01ad2", "id": "0145b399-dfad-448a-9f0d-fa975fb01ad2",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [
"source": [] {
"data": {
"text/plain": [
"True"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"empty_folder_question"
]
} }
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3 (ipykernel)", "display_name": "base",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
@ -498,7 +939,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.11.4" "version": "3.12.3"
} }
}, },
"nbformat": 4, "nbformat": 4,

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,319 @@
import os
import argparse
import numpy as np
from pathlib import Path
from osgeo import gdal
import rasterio as rio
from rasterio.enums import Resampling
from rasterio.warp import reproject
from osgeo import osr
# Attempt to import OmniCloudMask and set a flag
try:
from omnicloudmask import predict_from_array, load_multiband
HAS_OCM = True
except ImportError:
HAS_OCM = False
def calculate_utm_zone_and_hemisphere(longitude, latitude):
"""
Calculate the UTM zone and hemisphere based on longitude and latitude.
"""
utm_zone = int((longitude + 180) / 6) + 1
is_southern = latitude < 0
return utm_zone, is_southern
def reproject_to_projected_crs(input_path, output_path):
"""
Reprojects a raster to a projected coordinate system (e.g., UTM).
"""
input_ds = gdal.Open(str(input_path))
if not input_ds:
raise ValueError(f"Failed to open input raster: {input_path}")
# Get the source spatial reference
source_srs = osr.SpatialReference()
source_srs.ImportFromWkt(input_ds.GetProjection())
# Get the geographic coordinates of the image's center
geo_transform = input_ds.GetGeoTransform()
width = input_ds.RasterXSize
height = input_ds.RasterYSize
center_x = geo_transform[0] + (width / 2) * geo_transform[1]
center_y = geo_transform[3] + (height / 2) * geo_transform[5]
# Calculate the UTM zone and hemisphere dynamically
utm_zone, is_southern = calculate_utm_zone_and_hemisphere(center_x, center_y)
# Define the target spatial reference
target_srs = osr.SpatialReference()
target_srs.SetWellKnownGeogCS("WGS84")
target_srs.SetUTM(utm_zone, is_southern)
# Create the warp options
warp_options = gdal.WarpOptions(
dstSRS=target_srs.ExportToWkt(),
format="GTiff"
)
# Perform the reprojection
gdal.Warp(str(output_path), input_ds, options=warp_options)
input_ds = None # Close the dataset
print(f"Reprojected raster saved to: {output_path}")
return output_path
def resample_image(input_path, output_path, resolution=(10, 10), resample_alg="bilinear"):
"""
Resamples a raster to a specified resolution using gdal.Translate.
"""
print(f"Resampling {input_path} to {resolution}m resolution -> {output_path}")
# Reproject the input image to a projected CRS
reprojected_path = str(Path(output_path).with_name(f"{Path(output_path).stem}_reprojected.tif"))
reproject_to_projected_crs(input_path, reprojected_path)
# Open the reprojected dataset
input_ds = gdal.Open(reprojected_path)
if not input_ds:
raise ValueError(f"Failed to open reprojected raster: {reprojected_path}")
# Perform the resampling
result = gdal.Translate(
str(output_path),
input_ds,
xRes=resolution[0],
yRes=resolution[1],
resampleAlg=resample_alg
)
input_ds = None # Explicitly dereference the GDAL dataset
if result is None:
raise ValueError(f"Failed to resample image to {output_path}")
print(f"Successfully resampled image saved to: {output_path}")
return output_path
def run_ocm_on_image(image_path_10m, ocm_output_dir, save_mask=True):
"""
Processes a 10m resolution image with OmniCloudMask.
Adapted from process_with_ocm in the notebook.
"""
if not HAS_OCM:
print("OmniCloudMask not available. Please install with: pip install omnicloudmask")
return None, None
image_path_10m = Path(image_path_10m)
ocm_output_dir = Path(ocm_output_dir)
ocm_output_dir.mkdir(exist_ok=True, parents=True)
mask_10m_path = ocm_output_dir / f"{image_path_10m.stem}_ocm_mask_10m.tif"
try:
# Open the image to check dimensions
with rio.open(image_path_10m) as src:
width, height = src.width, src.height
# Check if the image is too small for OmniCloudMask
if width < 50 or height < 50:
print(f"Warning: Image {image_path_10m} is too small for OmniCloudMask (width: {width}, height: {height}). Skipping.")
return None, None
# PlanetScope 4-band images are typically [B,G,R,NIR]
# OCM expects [R,G,NIR] for its default model.
# Band numbers for load_multiband are 1-based.
# If original is B(1),G(2),R(3),NIR(4), then R=3, G=2, NIR=4
band_order = [3, 2, 4]
print(f"Loading 10m image for OCM: {image_path_10m}")
# load_multiband resamples if resample_res is different from source,
# but here image_path_10m is already 10m.
# We pass resample_res=None to use the image's own resolution.
rgn_data, profile = load_multiband(
input_path=str(image_path_10m),
resample_res=10, # Explicitly set target resolution for OCM
band_order=band_order
)
print("Applying OmniCloudMask...")
prediction = predict_from_array(rgn_data)
if save_mask:
profile.update(count=1, dtype='uint8')
with rio.open(mask_10m_path, 'w', **profile) as dst:
dst.write(prediction.astype('uint8'), 1)
print(f"Saved 10m OCM mask to: {mask_10m_path}")
# Summary (optional, can be removed for cleaner script output)
n_total = prediction.size
n_clear = np.sum(prediction == 0)
n_thick = np.sum(prediction == 1)
n_thin = np.sum(prediction == 2)
n_shadow = np.sum(prediction == 3)
print(f" OCM: Clear: {100*n_clear/n_total:.1f}%, Thick: {100*n_thick/n_total:.1f}%, Thin: {100*n_thin/n_total:.1f}%, Shadow: {100*n_shadow/n_total:.1f}%")
return str(mask_10m_path), profile
except Exception as e:
print(f"Error processing 10m image with OmniCloudMask: {str(e)}")
return None, None
def upsample_mask_to_3m(mask_10m_path, target_3m_image_path, output_3m_mask_path):
"""
Upsamples a 10m OCM mask to match the 3m target image.
Adapted from upsample_mask_to_highres in the notebook.
"""
print(f"Upsampling 10m mask {mask_10m_path} to 3m, referencing {target_3m_image_path}")
with rio.open(mask_10m_path) as src_mask, rio.open(target_3m_image_path) as src_img_3m:
mask_data_10m = src_mask.read(1)
img_shape_3m = (src_img_3m.height, src_img_3m.width)
img_transform_3m = src_img_3m.transform
img_crs_3m = src_img_3m.crs
upsampled_mask_3m_data = np.zeros(img_shape_3m, dtype=mask_data_10m.dtype)
reproject(
source=mask_data_10m,
destination=upsampled_mask_3m_data,
src_transform=src_mask.transform,
src_crs=src_mask.crs,
dst_transform=img_transform_3m,
dst_crs=img_crs_3m,
resampling=Resampling.nearest
)
profile_3m_mask = src_img_3m.profile.copy()
profile_3m_mask.update({
'count': 1,
'dtype': upsampled_mask_3m_data.dtype
})
with rio.open(output_3m_mask_path, 'w', **profile_3m_mask) as dst:
dst.write(upsampled_mask_3m_data, 1)
print(f"Upsampled 3m OCM mask saved to: {output_3m_mask_path}")
return str(output_3m_mask_path)
def apply_3m_mask_to_3m_image(image_3m_path, mask_3m_path, final_masked_output_path):
"""
Applies an upsampled 3m OCM mask to the original 3m image.
Adapted from apply_upsampled_mask_to_highres in the notebook.
"""
print(f"Applying 3m mask {mask_3m_path} to 3m image {image_3m_path}")
image_3m_path = Path(image_3m_path)
mask_3m_path = Path(mask_3m_path)
final_masked_output_path = Path(final_masked_output_path)
final_masked_output_path.parent.mkdir(parents=True, exist_ok=True)
try:
with rio.open(image_3m_path) as src_img_3m, rio.open(mask_3m_path) as src_mask_3m:
img_data_3m = src_img_3m.read()
img_profile_3m = src_img_3m.profile.copy()
mask_data_3m = src_mask_3m.read(1)
if img_data_3m.shape[1:] != mask_data_3m.shape:
print(f"Warning: 3m image shape {img_data_3m.shape[1:]} and 3m mask shape {mask_data_3m.shape} do not match.")
# This should ideally not happen if upsampling was correct.
# OCM: 0=clear, 1=thick cloud, 2=thin cloud, 3=shadow
# We want to mask out (set to nodata) pixels where OCM is > 0
binary_mask = np.ones_like(mask_data_3m, dtype=np.uint8)
binary_mask[mask_data_3m > 0] = 0 # 0 for cloud/shadow, 1 for clear
masked_img_data_3m = img_data_3m.copy()
nodata_val = img_profile_3m.get('nodata', 0) # Use existing nodata or 0
for i in range(img_profile_3m['count']):
masked_img_data_3m[i][binary_mask == 0] = nodata_val
# Ensure dtype of profile matches data to be written
# If original image was float, but nodata is int (0), rasterio might complain
# It's safer to use the original image's dtype for the output.
img_profile_3m.update(dtype=img_data_3m.dtype)
with rio.open(final_masked_output_path, 'w', **img_profile_3m) as dst:
dst.write(masked_img_data_3m)
print(f"Final masked 3m image saved to: {final_masked_output_path}")
return str(final_masked_output_path)
except Exception as e:
print(f"Error applying 3m mask to 3m image: {str(e)}")
return None
def main():
parser = argparse.ArgumentParser(description="Process PlanetScope 3m imagery with OmniCloudMask.")
parser.add_argument("input_3m_image", type=str, help="Path to the input merged 3m PlanetScope GeoTIFF image.")
parser.add_argument("output_dir", type=str, help="Directory to save processed files (10m image, masks, final 3m masked image).")
args = parser.parse_args()
try:
# Resolve paths to absolute paths immediately
input_3m_path = Path(args.input_3m_image).resolve(strict=True)
# output_base_dir is the directory where outputs will be saved.
# It should exist when the script is called (created by the notebook).
output_base_dir = Path(args.output_dir).resolve(strict=True)
except FileNotFoundError as e:
print(f"Error: Path resolution failed. Input image or output base directory may not exist or is not accessible: {e}")
return
except Exception as e:
print(f"Error resolving paths: {e}")
return
# The check for input_3m_path.exists() is now covered by resolve(strict=True)
# Define intermediate and final file paths using absolute base paths
intermediate_dir = output_base_dir / "intermediate_ocm_files"
intermediate_dir.mkdir(parents=True, exist_ok=True)
image_10m_path = intermediate_dir / f"{input_3m_path.stem}_10m.tif"
# OCM mask (10m) will be saved inside run_ocm_on_image, in a subdir of intermediate_dir
ocm_mask_output_dir = intermediate_dir / "ocm_10m_mask_output"
# Upsampled OCM mask (3m)
mask_3m_upsampled_path = intermediate_dir / f"{input_3m_path.stem}_ocm_mask_3m_upsampled.tif"
# Final masked image (3m)
final_masked_3m_path = output_base_dir / f"{input_3m_path.stem}_ocm_masked_3m.tif"
print(f"--- Starting OCM processing for {input_3m_path.name} ---")
print(f"Input 3m image (absolute): {input_3m_path}")
print(f"Output base directory (absolute): {output_base_dir}")
print(f"Intermediate 10m image path: {image_10m_path}")
# 1. Resample 3m input to 10m for OCM
try:
resample_image(input_3m_path, image_10m_path, resolution=(10, 10))
except Exception as e:
print(f"Failed to resample to 10m: {e}")
return
# 2. Run OCM on the 10m image
mask_10m_generated_path, _ = run_ocm_on_image(image_10m_path, ocm_mask_output_dir)
if not mask_10m_generated_path:
print("OCM processing failed. Exiting.")
return
# 3. Upsample the 10m OCM mask to 3m
try:
upsample_mask_to_3m(mask_10m_generated_path, input_3m_path, mask_3m_upsampled_path)
except Exception as e:
print(f"Failed to upsample 10m OCM mask to 3m: {e}")
return
# 4. Apply the 3m upsampled mask to the original 3m image
try:
apply_3m_mask_to_3m_image(input_3m_path, mask_3m_upsampled_path, final_masked_3m_path)
except Exception as e:
print(f"Failed to apply 3m mask to 3m image: {e}")
return
print(f"--- Successfully completed OCM processing for {input_3m_path.name} ---")
print(f"Final 3m masked output: {final_masked_3m_path}")
if __name__ == "__main__":
if not HAS_OCM:
print("OmniCloudMask library is not installed. Please install it to run this script.")
print("You can typically install it using: pip install omnicloudmask")
else:
main()

View file

@ -1,4 +1,5 @@
Version: 1.0 Version: 1.0
ProjectId: c69046c6-a50f-4ab9-8f53-a06b294d469f
RestoreWorkspace: Default RestoreWorkspace: Default
SaveWorkspace: Default SaveWorkspace: Default

View file

@ -2,8 +2,8 @@
params: params:
ref: "word-styles-reference-var1.docx" ref: "word-styles-reference-var1.docx"
output_file: CI_report.docx output_file: CI_report.docx
report_date: "2024-08-28" report_date: "2024-07-18"
data_dir: "Chemba" data_dir: "chemba"
mail_day: "Wednesday" mail_day: "Wednesday"
borders: TRUE borders: TRUE
output: output:
@ -12,266 +12,694 @@ output:
# df_print: paged # df_print: paged
word_document: word_document:
reference_docx: !expr file.path("word-styles-reference-var1.docx") reference_docx: !expr file.path("word-styles-reference-var1.docx")
toc: yes toc: no
editor_options: editor_options:
chunk_output_type: console chunk_output_type: console
--- ---
```{r setup, include=FALSE} ```{r setup_parameters, include=FALSE}
#set de filename van de output # Set up basic report parameters from input values
# knitr::opts_chunk$set(echo = TRUE)
# output_file <- params$output_file
report_date <- params$report_date report_date <- params$report_date
mail_day <- params$mail_day mail_day <- params$mail_day
borders <- params$borders borders <- params$borders
#
# # Environment setup notes (commented out)
# # Activeer de renv omgeving # # Activeer de renv omgeving
# renv::activate() # renv::activate()
# renv::deactivate() # renv::deactivate()
# Optioneel: Herstel de omgeving als dat nodig is # # Optioneel: Herstel de omgeving als dat nodig is
# Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren # # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
# renv::restore() # renv::restore()
``` ```
```{r libraries, message=FALSE, warning=FALSE, include=FALSE} ```{r load_libraries, message=FALSE, warning=FALSE, include=FALSE}
# Configure knitr options
knitr::opts_chunk$set(warning = FALSE, message = FALSE) knitr::opts_chunk$set(warning = FALSE, message = FALSE)
# Load all packages at once with suppressPackageStartupMessages
suppressPackageStartupMessages({
library(here) library(here)
library(sf) library(sf)
library(terra)
library(exactextractr)
library(tidyverse) library(tidyverse)
library(tmap) library(tmap)
library(lubridate) library(lubridate)
library(exactextractr)
library(zoo) library(zoo)
library(raster)
library(terra)
library(rsample) library(rsample)
library(caret) library(caret)
library(randomForest) library(randomForest)
library(CAST) library(CAST)
})
# Load custom utility functions
tryCatch({
source("report_utils.R") source("report_utils.R")
# source(here("r_app", "report_utils.R")) }, error = function(e) {
message(paste("Error loading report_utils.R:", e$message))
# Try alternative path if the first one fails
tryCatch({
source(here::here("r_app", "report_utils.R"))
}, error = function(e) {
stop("Could not load report_utils.R from either location: ", e$message)
})
})
``` ```
```{r directories, message=FALSE, warning=FALSE, include=FALSE} ```{r initialize_project_config, message=FALSE, warning=FALSE, include=FALSE}
# Set the project directory from parameters
project_dir <- params$data_dir project_dir <- params$data_dir
source(here("r_app", "parameters_project.R"))
log_message("Starting the R Markdown script") # Source project parameters with error handling
log_message(paste("mail_day params:", params$mail_day)) tryCatch({
log_message(paste("report_date params:", params$report_date)) source(here::here("r_app", "parameters_project.R"))
log_message(paste("mail_day variable:", mail_day)) }, error = function(e) {
stop("Error loading parameters_project.R: ", e$message)
})
# s2_dir <- "C:/Users/timon/Resilience BV/4002 CMD App - General/4002 CMD Team/4002 TechnicalData/04 WP2 technical/python/chemba_S2/" # Log initial configuration
safe_log("Starting the R Markdown script")
safe_log(paste("mail_day params:", params$mail_day))
safe_log(paste("report_date params:", params$report_date))
safe_log(paste("mail_day variable:", mail_day))
``` ```
```{r calculate_dates_and_weeks, message=FALSE, warning=FALSE, include=FALSE}
```{r week, message=FALSE, warning=FALSE, include=FALSE} # Set locale for consistent date formatting
Sys.setlocale("LC_TIME", "C") Sys.setlocale("LC_TIME", "C")
# Initialize date variables from parameters
today <- as.character(report_date) today <- as.character(report_date)
mail_day_as_character <- as.character(mail_day) mail_day_as_character <- as.character(mail_day)
report_date_as_week_day <- weekdays(ymd(today)) # Calculate week days
report_date_as_week_day <- weekdays(lubridate::ymd(today))
days_of_week <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") days_of_week <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
#als de index of report_date_as_week_day groter dan de index van de mail_day dan moet de week + 1
week <- week(today)
log_message(paste("week", week, "today", today))
today_minus_1 <- as.character(ymd(today) - 7)
today_minus_2 <- as.character(ymd(today) - 14)
today_minus_3 <- as.character(ymd(today) - 21)
log_message(paste("report_date_as_week_day", report_date_as_week_day)) # Calculate initial week number
log_message(paste("which(days_of_week == report_date_as_week_day)", which(days_of_week == report_date_as_week_day))) week <- lubridate::week(today)
log_message(paste("mail_day_as_character", mail_day_as_character)) safe_log(paste("Initial week calculation:", week, "today:", today))
log_message(paste(" which(days_of_week == mail_day_as_character)", which(days_of_week == mail_day_as_character)))
# Calculate previous dates for comparisons
today_minus_1 <- as.character(lubridate::ymd(today) - 7)
today_minus_2 <- as.character(lubridate::ymd(today) - 14)
today_minus_3 <- as.character(lubridate::ymd(today) - 21)
# Log the weekday calculations for debugging
safe_log(paste("Report date weekday:", report_date_as_week_day))
safe_log(paste("Weekday index:", which(days_of_week == report_date_as_week_day)))
safe_log(paste("Mail day:", mail_day_as_character))
safe_log(paste("Mail day index:", which(days_of_week == mail_day_as_character)))
# Adjust week calculation based on mail day
if (which(days_of_week == report_date_as_week_day) > which(days_of_week == mail_day_as_character)) { if (which(days_of_week == report_date_as_week_day) > which(days_of_week == mail_day_as_character)) {
log_message("adjusting weeks because of mail day") safe_log("Adjusting weeks because of mail day")
week <- week(today) + 1 week <- lubridate::week(today) + 1
today_minus_1 <- as.character(ymd(today)) today_minus_1 <- as.character(lubridate::ymd(today))
today_minus_2 <- as.character(ymd(today) - 7) today_minus_2 <- as.character(lubridate::ymd(today) - 7)
today_minus_3 <- as.character(ymd(today) - 14) today_minus_3 <- as.character(lubridate::ymd(today) - 14)
} }
# week <- week(today) # Calculate week numbers for previous weeks
week_minus_1 <- week - 1
week_minus_2 <- week - 2
week_minus_3 <- week - 3
# week <- 25 # Format current week with leading zeros
# today = "2024-06-21"
#today = as.character(Sys.Date())
#week = lubridate::week(Sys.time())
## week = 26
#title_var <- paste0("CI dashboard week ", week, " - all pivots dashboard using 3x3 meter resolution")
subtitle_var <- paste("Report generated on", Sys.Date())
week_minus_1 <- week -1 # sprintf("%02d", week(today_minus_1))
week_minus_2 <- week -2 # sprintf("%02d", week(today_minus_2))
week_minus_3 <- week -3 # sprintf("%02d", week(today_minus_3))
week <- sprintf("%02d", week) week <- sprintf("%02d", week)
# Get years for each date
year = year(today) year <- lubridate::year(today)
year_1 = year(today_minus_1) year_1 <- lubridate::year(today_minus_1)
year_2 = year(today_minus_2) year_2 <- lubridate::year(today_minus_2)
year_3 = year(today_minus_3) year_3 <- lubridate::year(today_minus_3)
``` ```
`r subtitle_var`
\pagebreak
# Explanation of the maps
This PDF-dashboard shows the status of your fields on a weekly basis. We will show this in different ways:
1) The first way is with a general overview of field heterogeneity using variation a higher number indicates more differences between plants in the same field.
2) The second map shows a normal image of the latest week in colour, of the demo fields.
3) Then come the maps per field, which show the status for three weeks ago, two weeks ago, last week, and this week, as well as a percentage difference map between last week and this week. The percentage difference maps shows the relative difference in growth over the last week, with positive numbers showing growth, and negative numbers showing decline.
4) Below the maps are graphs that show how each pivot quadrant is doing, measured through the chlorophyll index.
```{r data, message=TRUE, warning=TRUE, include=FALSE} ```{r data, message=TRUE, warning=TRUE, include=FALSE}
# get latest CI index # Load CI index data with error handling
tryCatch({
# remove_pivots <- c("1.1", "1.12", "1.8", "1.9", "1.11", "1.14") CI_quadrant <- readRDS(here::here(cumulative_CI_vals_dir, "All_pivots_Cumulative_CI_quadrant_year_v2.rds"))
CI_quadrant <- readRDS(here(cumulative_CI_vals_dir,"All_pivots_Cumulative_CI_quadrant_year_v2.rds"))# %>% safe_log("Successfully loaded CI quadrant data")
# rename(pivot_quadrant = field) }, error = function(e) {
stop("Error loading CI quadrant data: ", e$message)
})
# path_to_week_current = here(weekly_CI_mosaic, paste0("week_",week, "_", year, ".tif"))
# path_to_week_minus_1 = here(weekly_CI_mosaic, paste0("week_",week_minus_1, "_", year_1, ".tif"))
# path_to_week_minus_2 = here(weekly_CI_mosaic, paste0("week_",week_minus_2, "_", year_2, ".tif"))
# path_to_week_minus_3 = here(weekly_CI_mosaic, paste0("week_",week_minus_3, "_", year_3, ".tif"))
# 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_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_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_2 = get_week_path(weekly_CI_mosaic, today, -2)
path_to_week_minus_3 = get_week_path(weekly_CI_mosaic, today, -3) 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))
log_message("required mosaic paths") # Validate that files exist
log_message(paste("path to week current",path_to_week_current)) if (!file.exists(path_to_week_current)) warning("Current week mosaic file does not exist: ", path_to_week_current)
log_message(paste("path to week minus 1",path_to_week_minus_1)) if (!file.exists(path_to_week_minus_1)) warning("Week minus 1 mosaic file does not exist: ", path_to_week_minus_1)
log_message(paste("path to week minus 2",path_to_week_minus_2)) if (!file.exists(path_to_week_minus_2)) warning("Week minus 2 mosaic file does not exist: ", path_to_week_minus_2)
log_message(paste("path to week minus 3",path_to_week_minus_3)) if (!file.exists(path_to_week_minus_3)) warning("Week minus 3 mosaic file does not exist: ", path_to_week_minus_3)
CI <- brick(path_to_week_current) %>% subset("CI") # Load raster data with terra functions
CI_m1 <- brick(path_to_week_minus_1) %>% subset("CI") CI <- terra::rast(path_to_week_current)$CI
CI_m2 <- brick(path_to_week_minus_2) %>% subset("CI") CI_m1 <- terra::rast(path_to_week_minus_1)$CI
CI_m3 <- brick(path_to_week_minus_3) %>% subset("CI") CI_m2 <- terra::rast(path_to_week_minus_2)$CI
CI_m3 <- terra::rast(path_to_week_minus_3)$CI
}, error = function(e) {
# last_week_dif_raster <- ((CI - CI_m1) / CI_m1) * 100 stop("Error loading raster data: ", e$message)
last_week_dif_raster_abs <- (CI - CI_m1)
```
```{r data_129, message=TRUE, warning=TRUE, include=FALSE}
three_week_dif_raster_abs <- (CI - CI_m3)
```
```{r data_132, message=TRUE, warning=TRUE, include=FALSE}
# AllPivots0 <-st_read(here(data_dir_project, "pivot.geojson"))
# AllPivots0$pivot <- factor(AllPivots0$pivot, levels = c("1.1", "1.2", "1.3", "1.4", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "1.12", "1.13", "1.14" , "1.16" , "1.17" , "1.18" ,"2.1", "2.2", "2.3" , "2.4", "2.5", "3.1", "3.2", "3.3", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6", "5.1" ,"5.2", "5.3", "5.4", "6.1", "6.2", "DL1.1", "DL1.3"))
AllPivots0 <- field_boundaries_sf
# joined_spans <-st_read(here(data_dir_project, "span.geojson")) %>% st_transform(crs(AllPivots0))
# pivots_dates <- readRDS(here(harvest_dir, "harvest_data_new"))
# pivots_dates$pivot <- factor(pivots_dates$pivot, levels = c("1.1", "1.2", "1.3", "1.4", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "1.12", "1.13", "1.14" , "1.16" , "1.17" , "1.18" ,"2.1", "2.2", "2.3" , "2.4", "2.5", "3.1", "3.2", "3.3", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6", "5.1" ,"5.2", "5.3", "5.4", "6.1", "6.2", "DL1.1", "DL1.3"))
#AllPivots <- merge(AllPivots0, harvesting_data, by = c("field", "sub_field")) #%>%
#rename(field = pivot, sub_field = pivot_quadrant) #%>% select(-pivot.y)
#head(AllPivots)
#AllPivots_merged <- AllPivots %>% #dplyr::select(field, sub_field, sub_area) %>% unique() %>%
# group_by(field) %>% summarise(sub_area = first(sub_area))
#AllPivots_merged <- st_transform(AllPivots_merged, crs = proj4string(CI))
#pivot_names <- unique(CI_quadrant$field)
```
```{r eval=FALSE, fig.height=7.2, fig.width=10, message=FALSE, warning=FALSE, include=FALSE}
RGB_raster <- list.files(paste0(s2_dir,week),full.names = T, pattern = ".tiff", recursive = TRUE)[1] #use pattern = '.tif$' or something else if you have multiple files in this folder
RGB_raster <- brick(RGB_raster)
# RGB_raster <- brick(here("planet", paste0("week_",week, ".tif")))
tm_shape(RGB_raster, unit = "m") + tm_rgb(r=1, g=2, b=3, max.value = 255) +
tm_layout(main.title = paste0("RGB image of the fields - week ", week),
main.title.position = 'center') +
tm_scale_bar(position = c("right", "top"), text.color = "white") +
tm_compass(position = c("right", "top"), text.color = "white") +
tm_shape(AllPivots0)+ tm_borders( col = "white") +
tm_text("pivot_quadrant", size = .6, col = "white")
```
\newpage
```{r ci_overzicht_kaart, echo=FALSE, fig.height=7.3, fig.width=9, message=FALSE, warning=FALSE}
tm_shape(CI, unit = "m")+
tm_raster(breaks = c(0,0.5,1,2,3,4,5,6,7,Inf), palette = "RdYlGn", midpoint = NA,legend.is.portrait = F) +
tm_layout(legend.outside = TRUE,legend.outside.position = "bottom",legend.show = T, main.title = "Overview all fields (CI)")+
tm_scale_bar(position = c("right", "top"), text.color = "black") +
tm_compass(position = c("right", "top"), text.color = "black") +
tm_shape(AllPivots0)+ tm_borders( col = "black") +
tm_text("sub_field", size = .6, col = "black")
```
\newpage
```{r ci_diff_kaart, echo=FALSE, fig.height=7.3, fig.width=9, message=FALSE, warning=FALSE}
tm_shape(last_week_dif_raster_abs, unit = "m")+
tm_raster(breaks = c(-3,-2,-1,0,1,2, 3), palette = "RdYlGn", midpoint = NA,legend.is.portrait = F) +
tm_layout(legend.outside = TRUE,legend.outside.position = "bottom",legend.show = T, main.title = "Overview all fields - CI difference")+
tm_scale_bar(position = c("right", "top"), text.color = "black") +
tm_compass(position = c("right", "top"), text.color = "black") +
tm_shape(AllPivots0)+ tm_borders( col = "black") +
tm_text("sub_field", size = .6, col = "black")
```
\newpage
```{r plots_ci_estate, eval=TRUE, fig.height=3.8, fig.width=10, message=FALSE,echo=FALSE, warning=FALSE, include=TRUE, results='asis'}
AllPivots_merged <- AllPivots0 %>% dplyr::group_by(field) %>% summarise()
walk(AllPivots_merged$field, ~ {
cat("\n") # Add an empty line for better spacing
ci_plot(.x)
cat("\n")
cum_ci_plot(.x)
}) })
``` ```
```{r looping_over_sub_area, echo=FALSE, fig.height=3.8, fig.width=10, message=FALSE, warning=FALSE, results='asis', eval=FALSE} ```{r calculate_difference_rasters, message=TRUE, warning=TRUE, include=FALSE}
# Calculate difference rasters for comparisons
tryCatch({
# Calculate weekly difference
last_week_dif_raster_abs <- (CI - CI_m1)
safe_log("Calculated weekly difference raster")
# Calculate three-week difference
three_week_dif_raster_abs <- (CI - CI_m3)
safe_log("Calculated three-week difference raster")
}, error = function(e) {
safe_log(paste("Error calculating difference rasters:", e$message), "ERROR")
# Create placeholder rasters if calculations fail
if (!exists("last_week_dif_raster_abs")) {
last_week_dif_raster_abs <- CI * 0
}
if (!exists("three_week_dif_raster_abs")) {
three_week_dif_raster_abs <- CI * 0
}
})
```
```{r load_field_boundaries, message=TRUE, warning=TRUE, include=FALSE}
# Load field boundaries from parameters
tryCatch({
AllPivots0 <- field_boundaries_sf
safe_log("Successfully loaded field boundaries")
}, error = function(e) {
stop("Error loading field boundaries: ", e$message)
})
```
```{r create_front_page_variables, include=FALSE}
# Create variables for the front page
farm_name <- stringr::str_to_title(gsub("_", " ", project_dir))
# Format dates for display
report_date_formatted <- format(as.Date(report_date), "%B %d, %Y")
current_year <- format(Sys.Date(), "%Y")
# Get total field count and area if available
tryCatch({
total_fields <- length(unique(AllPivots0$field))
total_area_ha <- round(sum(sf::st_area(AllPivots0)) / 10000, 1) # Convert to hectares
}, error = function(e) {
total_fields <- "N/A"
total_area_ha <- "N/A"
})
```
---
title: ""
---
```{=openxml}
<w:p>
<w:pPr>
<w:jc w:val="center"/>
<w:spacing w:after="720"/>
</w:pPr>
<w:r>
<w:rPr>
<w:sz w:val="48"/>
<w:b/>
</w:rPr>
<w:t>SUGARCANE CROP MONITORING REPORT</w:t>
</w:r>
</w:p>
```
<div style="text-align: center; margin-top: 2cm; margin-bottom: 2cm;">
**`r farm_name`**
**Chlorophyll Index Analysis**
Report Date: **`r report_date_formatted`**
---
</div>
<div style="margin-top: 3cm; margin-bottom: 2cm;">
## Report Summary
**Farm Location:** `r farm_name`
**Report Period:** Week `r week` of `r current_year`
**Data Source:** Planet Labs Satellite Imagery
**Analysis Type:** Chlorophyll Index (CI) Monitoring
**Field Coverage:**
- Total Fields Monitored: `r total_fields`
- Total Area: `r total_area_ha` hectares
**Report Generated:** `r format(Sys.Date(), "%B %d, %Y at %H:%M")`
---
## About This Report
This automated report provides weekly analysis of sugarcane crop health using satellite-derived Chlorophyll Index (CI) measurements. The analysis helps identify:
- Field-level crop health variations
- Weekly changes in crop vigor
- Areas requiring agricultural attention
- Growth patterns across different field sections
**Key Features:**
- High-resolution satellite imagery analysis
- Week-over-week change detection
- Individual field performance metrics
- Actionable insights for crop management
</div>
\pagebreak
<!-- Original content starts here -->
\pagebreak
# Explanation of the Report
This report provides a detailed analysis of your sugarcane fields based on satellite imagery, helping you monitor crop health and development throughout the growing season. The data is processed weekly to give you timely insights for optimal farm management decisions.
## What is the Chlorophyll Index (CI)?
The **Chlorophyll Index (CI)** is a vegetation index that measures the relative amount of chlorophyll in plant leaves. Chlorophyll is the green pigment responsible for photosynthesis in plants. Higher CI values indicate:
* Greater photosynthetic activity
* Healthier plant tissue
* Better nitrogen uptake
* More vigorous crop growth
CI values typically range from 0 (bare soil or severely stressed vegetation) to 7+ (very healthy, dense vegetation). For sugarcane, values between 3-7 generally indicate good crop health, depending on the growth stage.
## What You'll Find in This Report:
1. **Chlorophyll Index Overview Map**: A comprehensive view of all your fields showing current CI values. This helps identify which fields are performing well and which might need attention.
2. **Weekly Difference Map**: Shows changes in CI values over the past week. Positive values (green) indicate improving crop health, while negative values (red) may signal stress or decline.
3. **Field-by-Field Analysis**: Detailed maps for each field showing:
* CI values for the current week and two previous weeks
* Week-to-week changes in CI values
* Three-week change in CI values to track longer-term trends
4. **Growth Trend Graphs**: Time-series visualizations showing how CI values have changed throughout the growing season for each section of your fields.
5. **Yield Prediction**: For mature crops (over 300 days), we provide estimated yield predictions based on historical data and current CI measurements.
Use these insights to identify areas that may need irrigation, fertilization, or other interventions, and to track the effectiveness of your management practices over time.
\pagebreak
# RGB Satellite Image - Current Week (if available)
```{r render_rgb_map, echo=FALSE, fig.height=6.9, fig.width=9, message=FALSE, warning=FALSE}
# Check if RGB bands are available and create RGB map
tryCatch({
# Load the full raster to check available bands
full_raster <- terra::rast(path_to_week_current)
available_bands <- names(full_raster)
# Check if RGB bands are available (look for red, green, blue or similar naming)
rgb_bands_available <- any(grepl("red|Red|RED", available_bands, ignore.case = TRUE)) &&
any(grepl("green|Green|GREEN", available_bands, ignore.case = TRUE)) &&
any(grepl("blue|Blue|BLUE", available_bands, ignore.case = TRUE))
# Alternative check for numbered bands that might be RGB (e.g., band_1, band_2, band_3)
if (!rgb_bands_available && length(available_bands) >= 3) {
# Check if we have at least 3 bands that could potentially be RGB
potential_rgb_bands <- grep("band_[1-3]|B[1-3]|[1-3]", available_bands, ignore.case = TRUE)
rgb_bands_available <- length(potential_rgb_bands) >= 3
}
if (rgb_bands_available) {
safe_log("RGB bands detected - creating RGB visualization")
# Try to extract RGB bands (prioritize named bands first)
red_band <- NULL
green_band <- NULL
blue_band <- NULL
# Look for named RGB bands first
red_candidates <- grep("red|Red|RED", available_bands, ignore.case = TRUE, value = TRUE)
green_candidates <- grep("green|Green|GREEN", available_bands, ignore.case = TRUE, value = TRUE)
blue_candidates <- grep("blue|Blue|BLUE", available_bands, ignore.case = TRUE, value = TRUE)
if (length(red_candidates) > 0) red_band <- red_candidates[1]
if (length(green_candidates) > 0) green_band <- green_candidates[1]
if (length(blue_candidates) > 0) blue_band <- blue_candidates[1]
# Fallback to numbered bands if named bands not found
if (is.null(red_band) || is.null(green_band) || is.null(blue_band)) {
if (length(available_bands) >= 3) {
# Assume first 3 bands are RGB (common convention)
red_band <- available_bands[1]
green_band <- available_bands[2]
blue_band <- available_bands[3]
}
}
if (!is.null(red_band) && !is.null(green_band) && !is.null(blue_band)) {
# Extract RGB bands
rgb_raster <- c(full_raster[[red_band]], full_raster[[green_band]], full_raster[[blue_band]])
names(rgb_raster) <- c("red", "green", "blue")
# Create RGB map
map <- tmap::tm_shape(rgb_raster, unit = "m") +
tmap::tm_rgb() +
tmap::tm_scalebar(position = c("right", "bottom"), text.color = "white") +
tmap::tm_compass(position = c("right", "bottom"), text.color = "white") +
tmap::tm_shape(AllPivots0) +
tmap::tm_borders(col = "white", lwd = 2) +
tmap::tm_text("sub_field", size = 0.6, col = "white") +
tmap::tm_layout(main.title = paste0("RGB Satellite Image - Week ", week),
main.title.size = 0.8,
main.title.color = "black")
# Print the map
print(map)
safe_log("RGB map created successfully")
} else {
safe_log("Could not identify RGB bands despite detection", "WARNING")
cat("RGB bands detected but could not be properly identified. Skipping RGB visualization.\n")
}
} else {
safe_log("No RGB bands available in the current week mosaic")
cat("**Note:** RGB satellite imagery is not available for this week. Only spectral index data is available.\n\n")
}
}, error = function(e) {
safe_log(paste("Error creating RGB map:", e$message), "ERROR")
cat("**Note:** Could not create RGB visualization for this week.\n\n")
})
```
\pagebreak
# Chlorophyll Index (CI) Overview Map - Current Week
```{r render_ci_overview_map, echo=FALSE, fig.height=6.9, fig.width=9, message=FALSE, warning=FALSE}
# Create overview chlorophyll index map
tryCatch({ # Base shape
map <- tmap::tm_shape(CI, unit = "m") # Add raster layer with continuous spectrum (fixed scale 1-8 for consistent comparison)
map <- map + tmap::tm_raster(col.scale = tm_scale_continuous(values = "brewer.rd_yl_gn",
limits = c(1, 8)),
col.legend = tm_legend(title = "Chlorophyll Index (CI)",
orientation = "landscape",
position = tm_pos_out("center", "bottom")))
# Complete the map with layout and other elements
map <- map +
tmap::tm_scalebar(position = c("right", "bottom"), text.color = "black") +
tmap::tm_compass(position = c("right", "bottom"), text.color = "black") +
tmap::tm_shape(AllPivots0) +
tmap::tm_borders(col = "black") +
tmap::tm_text("sub_field", size = 0.6, col = "black")
# Print the map
print(map)
}, error = function(e) {
safe_log(paste("Error creating CI overview map:", e$message), "ERROR")
plot(1, type="n", axes=FALSE, xlab="", ylab="")
text(1, 1, "Error creating CI overview map", cex=1.5)
})
```
\pagebreak
# Weekly Chlorophyll Index Difference Map
```{r render_ci_difference_map, echo=FALSE, fig.height=6.9, fig.width=9, message=FALSE, warning=FALSE}
# Create chlorophyll index difference map
tryCatch({ # Base shape
map <- tmap::tm_shape(last_week_dif_raster_abs, unit = "m") # Add raster layer with continuous spectrum (centered at 0 for difference maps, fixed scale)
map <- map + tmap::tm_raster(col.scale = tm_scale_continuous(values = "brewer.rd_yl_gn",
midpoint = 0,
limits = c(-3, 3)),
col.legend = tm_legend(title = "Chlorophyll Index (CI) Change",
orientation = "landscape",
position = tm_pos_out("center", "bottom")))
# Complete the map with layout and other elements
map <- map +
tmap::tm_scalebar(position = c("right", "bottom"), text.color = "black") +
tmap::tm_compass(position = c("right", "bottom"), text.color = "black") +
tmap::tm_shape(AllPivots0) +
tmap::tm_borders(col = "black") +
tmap::tm_text("sub_field", size = 0.6, col = "black")
# Print the map
print(map)
}, error = function(e) {
safe_log(paste("Error creating CI difference map:", e$message), "ERROR")
plot(1, type="n", axes=FALSE, xlab="", ylab="")
text(1, 1, "Error creating CI difference map", cex=1.5)
})
```
\pagebreak
```{r generate_field_visualizations, eval=TRUE, fig.height=3.8, fig.width=10, message=FALSE,echo=FALSE, warning=FALSE, include=TRUE, results='asis'}
# Generate detailed visualizations for each field
tryCatch({
# Merge field polygons for processing
AllPivots_merged <- AllPivots0 %>%
dplyr::group_by(field) %>%
dplyr::summarise(.groups = 'drop')
# Generate plots for each field
purrr::walk(AllPivots_merged$field[1:5], function(field_name) {
tryCatch({
cat("\n") # Add an empty line for better spacing
# Call ci_plot with explicit parameters
ci_plot(
pivotName = field_name,
field_boundaries = AllPivots0,
current_ci = CI,
ci_minus_1 = CI_m1,
ci_minus_2 = CI_m2,
last_week_diff = last_week_dif_raster_abs, three_week_diff = three_week_dif_raster_abs,
harvesting_data = harvesting_data,
week = week,
week_minus_1 = week_minus_1,
week_minus_2 = week_minus_2,
week_minus_3 = week_minus_3,
borders = borders
)
cat("\n")
# Call cum_ci_plot with explicit parameters
cum_ci_plot(
pivotName = field_name,
ci_quadrant_data = CI_quadrant,
plot_type = "value",
facet_on = FALSE
)
}, error = function(e) {
safe_log(paste("Error generating plots for field", field_name, ":", e$message), "ERROR")
cat(paste("## Error generating plots for field", field_name, "\n"))
cat(paste(e$message, "\n"))
})
})
}, error = function(e) {
safe_log(paste("Error in field visualization section:", e$message), "ERROR")
cat("Error generating field plots. See log for details.\n")
})
```
```{r generate_subarea_visualizations, echo=FALSE, fig.height=3.8, fig.width=10, message=FALSE, warning=FALSE, results='asis', eval=FALSE}
# Alternative visualization grouped by sub-area (disabled by default)
tryCatch({
# Group pivots by sub-area
pivots_grouped <- AllPivots0 pivots_grouped <- AllPivots0
# Iterate over each subgroup # Iterate over each subgroup
for (subgroup in unique(pivots_grouped$sub_area)) { for (subgroup in unique(pivots_grouped$sub_area)) {
cat("# HELLO!!!") # Add subgroup heading
print(" PRINT") cat("\n")
# cat("\n") cat("## Subgroup: ", subgroup, "\n")
# cat("# Subgroup: ", subgroup, "\n") # Add a title for the subgroup
subset_data <- filter(pivots_grouped, sub_area == subgroup) # Filter data for current subgroup
# cat("\\pagebreak") subset_data <- dplyr::filter(pivots_grouped, sub_area == subgroup)
walk(subset_data$field, ~ {
# cat("\n") # Add an empty line for better spacing # Generate visualizations for each field in the subgroup
ci_plot(.x) purrr::walk(subset_data$field, function(field_name) {
# cat("\n") cat("\n")
cum_ci_plot(.x) ci_plot(field_name)
# cat("\n") cat("\n")
cum_ci_plot(field_name)
cat("\n")
}) })
# Add page break after each subgroup
cat("\\pagebreak\n")
} }
}, error = function(e) {
safe_log(paste("Error in subarea visualization section:", e$message), "ERROR")
cat("Error generating subarea plots. See log for details.\n")
})
``` ```
# Yield prediction
The below table shows estimates of the biomass if you would harvest them now.
```{r yield_data_training, message=FALSE, warning=FALSE, include=FALSE}
# Load and prepare yield prediction data with error handling
tryCatch({
# Load CI quadrant data and fill missing values
CI_quadrant <- readRDS(here::here(cumulative_CI_vals_dir, "All_pivots_Cumulative_CI_quadrant_year_v2.rds")) %>%
dplyr::group_by(model) %>%
tidyr::fill(field, sub_field, .direction = "downup") %>%
dplyr::ungroup()
# Check if tonnage_ha is empty
if (all(is.na(harvesting_data$tonnage_ha))) {
safe_log("Lacking historic harvest data, please provide for yield prediction calculation", "WARNING")
knitr::knit_exit() # Exit the chunk if tonnage_ha is empty
}
# Rename year column to season for consistency
harvesting_data <- harvesting_data %>% dplyr::rename(season = year)
# Join CI and yield data
CI_and_yield <- dplyr::left_join(CI_quadrant, harvesting_data, by = c("field", "sub_field", "season")) %>%
dplyr::group_by(sub_field, season) %>%
dplyr::slice(which.max(DOY)) %>%
dplyr::select(field, sub_field, tonnage_ha, cumulative_CI, DOY, season, sub_area) %>%
dplyr::mutate(CI_per_day = cumulative_CI / DOY)
# Define predictors and response variables
predictors <- c("cumulative_CI", "DOY", "CI_per_day")
response <- "tonnage_ha"
# Prepare test and validation datasets
CI_and_yield_test <- CI_and_yield %>%
as.data.frame() %>%
dplyr::filter(!is.na(tonnage_ha))
CI_and_yield_validation <- CI_and_yield_test
# Prepare prediction dataset (fields without harvest data)
prediction_yields <- CI_and_yield %>%
as.data.frame() %>%
dplyr::filter(is.na(tonnage_ha))
# Configure model training parameters
ctrl <- caret::trainControl(
method = "cv",
savePredictions = TRUE,
allowParallel = TRUE,
number = 5,
verboseIter = TRUE
)
# Train the model with feature selection
set.seed(202) # For reproducibility
model_ffs_rf <- CAST::ffs(
CI_and_yield_test[, predictors],
CI_and_yield_test[, response],
method = "rf",
trControl = ctrl,
importance = TRUE,
withinSE = TRUE,
tuneLength = 5,
na.rm = TRUE
)
# Function to prepare predictions with consistent naming and formatting
prepare_predictions <- function(predictions, newdata) {
return(predictions %>%
as.data.frame() %>%
dplyr::rename(predicted_Tcha = ".") %>%
dplyr::mutate(
sub_field = newdata$sub_field,
field = newdata$field,
Age_days = newdata$DOY,
total_CI = round(newdata$cumulative_CI, 0),
predicted_Tcha = round(predicted_Tcha, 0),
season = newdata$season
) %>%
dplyr::select(field, sub_field, Age_days, total_CI, predicted_Tcha, season) %>%
dplyr::left_join(., newdata, by = c("field", "sub_field", "season"))
)
}
# Predict yields for the validation dataset
pred_ffs_rf <- prepare_predictions(stats::predict(model_ffs_rf, newdata = CI_and_yield_validation), CI_and_yield_validation)
# Predict yields for the current season (focus on mature fields over 300 days)
pred_rf_current_season <- prepare_predictions(stats::predict(model_ffs_rf, newdata = prediction_yields), prediction_yields) %>%
dplyr::filter(Age_days > 1) %>%
dplyr::mutate(CI_per_day = round(total_CI / Age_days, 1))
safe_log("Successfully completed yield prediction calculations")
}, error = function(e) {
safe_log(paste("Error in yield prediction:", e$message), "ERROR")
# Create empty dataframes to prevent errors in subsequent chunks
pred_ffs_rf <- data.frame()
pred_rf_current_season <- data.frame()
})
```
```{r plotting_yield_data, echo=FALSE, fig.height=5, fig.width=8, message=FALSE, warning=FALSE}
# Display yield prediction visualizations with error handling
tryCatch({
if (nrow(pred_ffs_rf) > 0) {
# Plot model performance (predicted vs actual)
ggplot2::ggplot(pred_ffs_rf, ggplot2::aes(y = predicted_Tcha, x = tonnage_ha)) +
ggplot2::geom_point(size = 2, alpha = 0.6) +
ggplot2::geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "red") +
ggplot2::scale_x_continuous(limits = c(0, 200)) +
ggplot2::scale_y_continuous(limits = c(0, 200)) +
ggplot2::labs(title = "Model Performance: \nPredicted vs Actual Tonnage/ha",
x = "Actual tonnage/ha (Tcha)",
y = "Predicted tonnage/ha (Tcha)") +
ggplot2::theme_minimal()
}
if (nrow(pred_rf_current_season) > 0) {
# Plot predicted yields by age
ggplot2::ggplot(pred_rf_current_season, ggplot2::aes(x = Age_days, y = predicted_Tcha)) +
ggplot2::geom_point(size = 2, alpha = 0.6) +
ggplot2::labs(title = "Predicted Yields for Fields Over 300 Days \nOld Yet to Be Harvested",
x = "Age (days)",
y = "Predicted tonnage/ha (Tcha)") +
ggplot2::scale_y_continuous(limits = c(0, 200)) +
ggplot2::theme_minimal()
# Display prediction table
knitr::kable(pred_rf_current_season,
digits = 0,
caption = "Predicted Tonnage/ha for Fields Over 300 Days Old")
} else {
cat("No fields over 300 days old without harvest data available for yield prediction.")
}
}, error = function(e) {
safe_log(paste("Error in yield prediction visualization:", e$message), "ERROR")
cat("Error generating yield prediction visualizations. See log for details.")
})
```
\pagebreak

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

View file

@ -0,0 +1,721 @@
---
params:
ref: "word-styles-reference-var1.docx"
output_file: CI_report.docx
report_date: "2025-06-16"
data_dir: "simba"
mail_day: "Wednesday"
borders: TRUE
use_breaks: FALSE
output:
# html_document:
# toc: yes
# df_print: paged
word_document:
reference_docx: !expr file.path("word-styles-reference-var1.docx")
toc: yes
editor_options:
chunk_output_type: console
---
```{r setup_parameters, include=FALSE}
# Set up basic report parameters from input values
report_date <- params$report_date
mail_day <- params$mail_day
borders <- params$borders
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
# Environment setup notes (commented out)
# # Activeer de renv omgeving
# renv::activate()
# renv::deactivate()
# # Optioneel: Herstel de omgeving als dat nodig is
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
# renv::restore()
```
```{r load_libraries, message=FALSE, warning=FALSE, include=FALSE}
# Configure knitr options
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
# Path management
library(here)
# Spatial data libraries
library(sf)
library(terra)
library(exactextractr)
# library(raster) - Removed as it's no longer maintained
# Data manipulation and visualization
library(tidyverse) # Includes dplyr, ggplot2, etc.
library(tmap)
library(lubridate)
library(zoo)
# Machine learning
library(rsample)
library(caret)
library(randomForest)
library(CAST)
# Load custom utility functions
# tryCatch({
# source("report_utils.R")
# }, error = function(e) {
# message(paste("Error loading report_utils.R:", e$message))
# # Try alternative path if the first one fails
# tryCatch({
source(here::here("r_app", "report_utils.R"))
# }, error = function(e) {
# stop("Could not load report_utils.R from either location: ", e$message)
# })
# })
# Load executive report utilities
# tryCatch({
# source("executive_report_utils.R")
# }, error = function(e) {
# message(paste("Error loading executive_report_utils.R:", e$message))
# # Try alternative path if the first one fails
# tryCatch({
source(here::here("r_app","exec_dashboard", "executive_report_utils.R"))
# }, error = function(e) {
# stop("Could not load executive_report_utils.R from either location: ", e$message)
# })
# })
safe_log("Successfully loaded utility functions")
```
```{r initialize_project_config, message=FALSE, warning=FALSE, include=FALSE}
# Set the project directory from parameters
project_dir <- params$data_dir
# Source project parameters with error handling
tryCatch({
source(here::here("r_app", "parameters_project.R"))
}, error = function(e) {
stop("Error loading parameters_project.R: ", e$message)
})
# Log initial configuration
safe_log("Starting the R Markdown script")
safe_log(paste("mail_day params:", params$mail_day))
safe_log(paste("report_date params:", params$report_date))
safe_log(paste("mail_day variable:", mail_day))
```
```{r calculate_dates_and_weeks, message=FALSE, warning=FALSE, include=FALSE}
# Set locale for consistent date formatting
Sys.setlocale("LC_TIME", "C")
# Initialize date variables from parameters
today <- as.character(report_date)
mail_day_as_character <- as.character(mail_day)
# Calculate week days
report_date_as_week_day <- weekdays(lubridate::ymd(today))
days_of_week <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
# Calculate initial week number
week <- lubridate::week(today)
safe_log(paste("Initial week calculation:", week, "today:", today))
# Calculate previous dates for comparisons
today_minus_1 <- as.character(lubridate::ymd(today) - 7)
today_minus_2 <- as.character(lubridate::ymd(today) - 14)
today_minus_3 <- as.character(lubridate::ymd(today) - 21)
# Log the weekday calculations for debugging
safe_log(paste("Report date weekday:", report_date_as_week_day))
safe_log(paste("Weekday index:", which(days_of_week == report_date_as_week_day)))
safe_log(paste("Mail day:", mail_day_as_character))
safe_log(paste("Mail day index:", which(days_of_week == mail_day_as_character)))
# Adjust week calculation based on mail day
if (which(days_of_week == report_date_as_week_day) > which(days_of_week == mail_day_as_character)) {
safe_log("Adjusting weeks because of mail day")
week <- lubridate::week(today) + 1
today_minus_1 <- as.character(lubridate::ymd(today))
today_minus_2 <- as.character(lubridate::ymd(today) - 7)
today_minus_3 <- as.character(lubridate::ymd(today) - 14)
}
# Generate subtitle for report
subtitle_var <- paste("Report generated on", Sys.Date())
# Calculate week numbers for previous weeks
week_minus_1 <- week - 1
week_minus_2 <- week - 2
week_minus_3 <- week - 3
# Format current week with leading zeros
week <- sprintf("%02d", week)
# Get years for each date
year <- lubridate::year(today)
year_1 <- lubridate::year(today_minus_1)
year_2 <- lubridate::year(today_minus_2)
year_3 <- lubridate::year(today_minus_3)
```
```{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
tryCatch({
# Calculate weekly difference
last_week_dif_raster_abs <- (CI - CI_m1)
safe_log("Calculated weekly difference raster")
# Calculate three-week difference
three_week_dif_raster_abs <- (CI - CI_m3)
safe_log("Calculated three-week difference raster")
}, error = function(e) {
safe_log(paste("Error calculating difference rasters:", e$message), "ERROR")
# Create placeholder rasters if calculations fail
if (!exists("last_week_dif_raster_abs")) {
last_week_dif_raster_abs <- CI * 0
}
if (!exists("three_week_dif_raster_abs")) {
three_week_dif_raster_abs <- CI * 0
}
})
```
```{r load_field_boundaries, message=TRUE, warning=TRUE, include=FALSE}
# Load field boundaries from parameters
tryCatch({
AllPivots0 <- field_boundaries_sf
safe_log("Successfully loaded field boundaries")
}, error = function(e) {
stop("Error loading field boundaries: ", e$message)
})
```
```{r create_farm_health_data, message=FALSE, warning=FALSE, include=FALSE}
# Create farm health summary data from scratch
tryCatch({
# Ensure we have the required data
if (!exists("AllPivots0") || !exists("CI") || !exists("CI_m1") || !exists("harvesting_data")) {
stop("Required input data (field boundaries, CI data, or harvesting data) not available")
}
safe_log("Starting to calculate farm health data")
# Get unique field names
fields <- unique(AllPivots0$field)
safe_log(paste("Found", length(fields), "unique fields"))
# Initialize result dataframe
farm_health_data <- data.frame(
field = character(),
mean_ci = numeric(),
ci_change = numeric(),
ci_uniformity = numeric(),
status = character(),
anomaly_type = character(),
priority_level = numeric(),
age_weeks = numeric(),
harvest_readiness = character(),
stringsAsFactors = FALSE
)
# Process each field with robust error handling
for (field_name in fields) {
tryCatch({
safe_log(paste("Processing field:", field_name))
# Get field boundary
field_shape <- AllPivots0 %>% dplyr::filter(field == field_name)
# Skip if field shape is empty
if (nrow(field_shape) == 0) {
safe_log(paste("Empty field shape for", field_name), "WARNING")
next
}
# Get field age from harvesting data - use direct filtering to avoid dplyr errors
field_age_data <- NULL
if (exists("harvesting_data") && !is.null(harvesting_data) && nrow(harvesting_data) > 0) {
field_age_data <- harvesting_data[harvesting_data$field == field_name, ]
if (nrow(field_age_data) > 0) {
field_age_data <- field_age_data[order(field_age_data$season_start, decreasing = TRUE), ][1, ]
}
}
# Default age if not available
field_age_weeks <- if (!is.null(field_age_data) && nrow(field_age_data) > 0 && !is.na(field_age_data$age)) {
field_age_data$age
} else {
10 # Default age
}
# Extract CI values using terra's extract function which is more robust
ci_values <- terra::extract(CI, field_shape)
ci_prev_values <- terra::extract(CI_m1, field_shape)
# Check if we got valid data
if (nrow(ci_values) == 0 || nrow(ci_prev_values) == 0) {
safe_log(paste("No CI data extracted for field", field_name), "WARNING")
# Add a placeholder row with Unknown status
farm_health_data <- rbind(farm_health_data, data.frame(
field = field_name,
mean_ci = NA,
ci_change = NA,
ci_uniformity = NA,
status = "Unknown",
anomaly_type = "Unknown",
priority_level = 5, # Low priority
age_weeks = field_age_weeks,
harvest_readiness = "Unknown",
stringsAsFactors = FALSE
))
next
}
# Calculate metrics - Handle NA values properly
ci_column <- if ("CI" %in% names(ci_values)) "CI" else colnames(ci_values)[1]
ci_prev_column <- if ("CI" %in% names(ci_prev_values)) "CI" else colnames(ci_prev_values)[1]
mean_ci <- mean(ci_values[[ci_column]], na.rm=TRUE)
mean_ci_prev <- mean(ci_prev_values[[ci_prev_column]], na.rm=TRUE)
ci_change <- mean_ci - mean_ci_prev
ci_sd <- sd(ci_values[[ci_column]], na.rm=TRUE)
ci_uniformity <- ci_sd / max(0.1, mean_ci) # Avoid division by zero
# Handle NaN or Inf results
if (is.na(mean_ci) || is.na(ci_change) || is.na(ci_uniformity) ||
is.nan(mean_ci) || is.nan(ci_change) || is.nan(ci_uniformity) ||
is.infinite(mean_ci) || is.infinite(ci_change) || is.infinite(ci_uniformity)) {
safe_log(paste("Invalid calculation results for field", field_name), "WARNING")
# Add a placeholder row with Unknown status
farm_health_data <- rbind(farm_health_data, data.frame(
field = field_name,
mean_ci = NA,
ci_change = NA,
ci_uniformity = NA,
status = "Unknown",
anomaly_type = "Unknown",
priority_level = 5, # Low priority
age_weeks = field_age_weeks,
harvest_readiness = "Unknown",
stringsAsFactors = FALSE
))
next
}
# Determine field status
status <- dplyr::case_when(
mean_ci >= 5 ~ "Excellent",
mean_ci >= 3.5 ~ "Good",
mean_ci >= 2 ~ "Fair",
mean_ci >= 1 ~ "Poor",
TRUE ~ "Critical"
)
# Determine anomaly type
anomaly_type <- dplyr::case_when(
ci_change > 2 ~ "Potential Weed Growth",
ci_change < -2 ~ "Potential Weeding/Harvesting",
ci_uniformity > 0.5 ~ "High Variability",
mean_ci < 1 ~ "Low Vigor",
TRUE ~ "None"
)
# Calculate priority level (1-5, with 1 being highest priority)
priority_score <- dplyr::case_when(
mean_ci < 1 ~ 1, # Critical - highest priority
anomaly_type == "Potential Weed Growth" ~ 2,
anomaly_type == "High Variability" ~ 3,
ci_change < -1 ~ 4,
TRUE ~ 5 # No urgent issues
)
# Determine harvest readiness
harvest_readiness <- dplyr::case_when(
field_age_weeks >= 52 & mean_ci >= 4 ~ "Ready for harvest",
field_age_weeks >= 48 & mean_ci >= 3.5 ~ "Approaching harvest",
field_age_weeks >= 40 & mean_ci >= 3 ~ "Mid-maturity",
field_age_weeks >= 12 ~ "Growing",
TRUE ~ "Early stage"
)
# Add to summary data
farm_health_data <- rbind(farm_health_data, data.frame(
field = field_name,
mean_ci = round(mean_ci, 2),
ci_change = round(ci_change, 2),
ci_uniformity = round(ci_uniformity, 2),
status = status,
anomaly_type = anomaly_type,
priority_level = priority_score,
age_weeks = field_age_weeks,
harvest_readiness = harvest_readiness,
stringsAsFactors = FALSE
))
}, error = function(e) {
safe_log(paste("Error processing field", field_name, ":", e$message), "ERROR")
# Add a placeholder row with Error status
farm_health_data <<- rbind(farm_health_data, data.frame(
field = field_name,
mean_ci = NA,
ci_change = NA,
ci_uniformity = NA,
status = "Unknown",
anomaly_type = "Unknown",
priority_level = 5, # Low priority since we don't know the status
age_weeks = NA,
harvest_readiness = "Unknown",
stringsAsFactors = FALSE
))
})
}
# Make sure we have data for all fields
if (nrow(farm_health_data) == 0) {
safe_log("No farm health data was created", "ERROR")
stop("Failed to create farm health data")
}
# Sort by priority level
farm_health_data <- farm_health_data %>% dplyr::arrange(priority_level, field)
safe_log(paste("Successfully created farm health data for", nrow(farm_health_data), "fields"))
}, error = function(e) {
safe_log(paste("Error creating farm health data:", e$message), "ERROR")
# Create an empty dataframe that can be filled by the verification chunk
})
```
```{r verify_farm_health_data, message=FALSE, warning=FALSE, include=FALSE}
# Verify farm_health_data exists and has content
if (!exists("farm_health_data") || nrow(farm_health_data) == 0) {
safe_log("farm_health_data not found or empty, generating default data", "WARNING")
# Create minimal fallback data
tryCatch({
# Get fields from boundaries
fields <- unique(AllPivots0$field)
# Create basic data frame with just field names
farm_health_data <- data.frame(
field = fields,
mean_ci = rep(NA, length(fields)),
ci_change = rep(NA, length(fields)),
ci_uniformity = rep(NA, length(fields)),
status = rep("Unknown", length(fields)),
anomaly_type = rep("Unknown", length(fields)),
priority_level = rep(5, length(fields)), # Low priority
age_weeks = rep(NA, length(fields)),
harvest_readiness = rep("Unknown", length(fields)),
stringsAsFactors = FALSE
)
safe_log("Created fallback farm_health_data with basic field information")
}, error = function(e) {
safe_log(paste("Error creating fallback farm_health_data:", e$message), "ERROR")
farm_health_data <<- data.frame(
field = character(),
mean_ci = numeric(),
ci_change = numeric(),
ci_uniformity = numeric(),
status = character(),
anomaly_type = character(),
priority_level = numeric(),
age_weeks = numeric(),
harvest_readiness = character(),
stringsAsFactors = FALSE
)
})
}
```
```{r calculate_farm_health, message=FALSE, warning=FALSE, include=FALSE}
# Calculate farm health summary metrics
tryCatch({
# Generate farm health summary data
farm_health_data <- generate_farm_health_summary(
field_boundaries = AllPivots0,
ci_current = CI,
ci_previous = CI_m1,
harvesting_data = harvesting_data
)
# Log the summary data
safe_log(paste("Generated farm health summary with", nrow(farm_health_data), "fields"))
}, error = function(e) {
safe_log(paste("Error in farm health calculation:", e$message), "ERROR")
# Create empty dataframe if calculation failed
farm_health_data <- data.frame(
field = character(),
mean_ci = numeric(),
ci_change = numeric(),
ci_uniformity = numeric(),
status = character(),
anomaly_type = character(),
priority_level = numeric(),
age_weeks = numeric(),
harvest_readiness = character(),
stringsAsFactors = FALSE
)
})
```
```{r advanced_analytics_functions, message=FALSE, warning=FALSE, include=FALSE}
# ADVANCED ANALYTICS FUNCTIONS
# Note: These functions are now imported from executive_report_utils.R
# The utility file contains functions for velocity/acceleration indicators,
# anomaly timeline creation, age cohort mapping, and cohort performance charts
safe_log("Using analytics functions from executive_report_utils.R")
```
\pagebreak
# Advanced Analytics
## Field Health Velocity and Acceleration
This visualization shows the rate of change in field health (velocity) and whether that change is speeding up or slowing down (acceleration). These metrics help identify if farm conditions are improving, stable, or deteriorating.
**How to interpret:**
- **Velocity gauge:** Shows the average weekly change in CI values across all fields
- Positive values (green/right side): Farm health improving week-to-week
- Negative values (red/left side): Farm health declining week-to-week
- **Acceleration gauge:** Shows whether the rate of change is increasing or decreasing
- Positive values (green/right side): Change is accelerating or improving faster
- Negative values (red/left side): Change is decelerating or slowing down
- **4-Week Trend:** Shows the overall CI value trajectory for the past month
```{r render_velocity_acceleration, echo=FALSE, fig.height=8, fig.width=10, message=FALSE, warning=FALSE}
# Render the velocity and acceleration indicators
tryCatch({
# Create and display the indicators using the imported utility function
velocity_plot <- create_velocity_acceleration_indicator(
health_data = farm_health_data,
ci_current = CI,
ci_prev1 = CI_m1,
ci_prev2 = CI_m2,
ci_prev3 = CI_m3,
field_boundaries = AllPivots0
)
# Print the visualization
print(velocity_plot)
# Create a table of fields with significant velocity changes
field_ci_metrics <- list()
# Process each field to get metrics
fields <- unique(AllPivots0$field)
for (field_name in fields) {
tryCatch({
# Get field boundary
field_shape <- AllPivots0 %>% dplyr::filter(field == field_name)
if (nrow(field_shape) == 0) next
# Extract CI values
ci_curr_values <- terra::extract(CI, field_shape)
ci_prev1_values <- terra::extract(CI_m1, field_shape)
# Calculate metrics
mean_ci_curr <- mean(ci_curr_values$CI, na.rm = TRUE)
mean_ci_prev1 <- mean(ci_prev1_values$CI, na.rm = TRUE)
velocity <- mean_ci_curr - mean_ci_prev1
# Store in list
field_ci_metrics[[field_name]] <- list(
field = field_name,
ci_current = mean_ci_curr,
ci_prev1 = mean_ci_prev1,
velocity = velocity
)
}, error = function(e) {
safe_log(paste("Error processing field", field_name, "for velocity table:", e$message), "WARNING")
})
}
# Convert list to data frame
velocity_df <- do.call(rbind, lapply(field_ci_metrics, function(x) {
data.frame(
field = x$field,
ci_current = round(x$ci_current, 2),
ci_prev1 = round(x$ci_prev1, 2),
velocity = round(x$velocity, 2),
direction = ifelse(x$velocity >= 0, "Improving", "Declining")
)
}))
# Select top 5 positive and top 5 negative velocity fields
top_positive <- velocity_df %>%
dplyr::filter(velocity > 0) %>%
dplyr::arrange(desc(velocity)) %>%
dplyr::slice_head(n = 5)
top_negative <- velocity_df %>%
dplyr::filter(velocity < 0) %>%
dplyr::arrange(velocity) %>%
dplyr::slice_head(n = 5)
# Display the tables if we have data
if (nrow(top_positive) > 0) {
cat("<h4>Fields with Fastest Improvement</h4>")
knitr::kable(top_positive %>%
dplyr::select(Field = field,
`Current CI` = ci_current,
`Previous CI` = ci_prev1,
`Weekly Change` = velocity))
}
if (nrow(top_negative) > 0) {
cat("<h4>Fields with Fastest Decline</h4>")
knitr::kable(top_negative %>%
dplyr::select(Field = field,
`Current CI` = ci_current,
`Previous CI` = ci_prev1,
`Weekly Change` = velocity))
}
}, error = function(e) {
safe_log(paste("Error rendering velocity visualization:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating velocity visualization.</div>")
})
```
\pagebreak
## Field Anomaly Timeline
This visualization shows the history of detected anomalies in fields across the monitoring period. It helps identify persistent issues or improvements over time.
**How to interpret:**
- **X-axis**: Dates of satellite observations
- **Y-axis**: Fields grouped by similar characteristics
- **Colors**: Red indicates negative anomalies, green indicates positive anomalies
- **Size**: Larger markers indicate stronger anomalies
```{r anomaly_timeline, echo=FALSE, fig.height=8, fig.width=10, message=FALSE, warning=FALSE}
# Generate anomaly timeline visualization
tryCatch({
# Use the imported function to create the anomaly timeline
anomaly_timeline <- create_anomaly_timeline(
field_boundaries = AllPivots0,
ci_data = CI_quadrant,
days_to_include = 90 # Show last 90 days of data
)
# Display the timeline
print(anomaly_timeline)
}, error = function(e) {
safe_log(paste("Error generating anomaly timeline:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating anomaly timeline visualization.</div>")
})
```
\pagebreak
## Field Age Cohorts Map
This map shows fields grouped by their crop age (weeks since planting). Understanding the distribution of crop ages helps interpret performance metrics and plan harvest scheduling.
**How to interpret:**
- **Colors**: Different colors represent different age groups (in weeks since planting)
- **Labels**: Each field is labeled with its name for easy reference
- **Legend**: Shows the age ranges in weeks and their corresponding colors
```{r age_cohort_map, echo=FALSE, fig.height=8, fig.width=10, message=FALSE, warning=FALSE}
# Generate age cohort map
tryCatch({
# Use the imported function to create the age cohort map
age_cohort_map <- create_age_cohort_map(
field_boundaries = AllPivots0,
harvesting_data = harvesting_data
)
# Display the map
print(age_cohort_map)
}, error = function(e) {
safe_log(paste("Error generating age cohort map:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating age cohort map visualization.</div>")
})
```
\pagebreak
## Cohort Performance Comparison
This visualization compares chlorophyll index (CI) performance across different age groups of fields. This helps identify if certain age groups are performing better or worse than expected.
**How to interpret:**
- **X-axis**: Field age groups in weeks since planting
- **Y-axis**: Average CI value for fields in that age group
- **Box plots**: Show the distribution of CI values within each age group
- **Line**: Shows the expected CI trajectory based on historical data
```{r cohort_performance_chart, echo=FALSE, fig.height=8, fig.width=10, message=FALSE, warning=FALSE}
# Generate cohort performance comparison chart
tryCatch({
# Use the imported function to create the cohort performance chart
cohort_chart <- create_cohort_performance_chart(
field_boundaries = AllPivots0,
ci_current = CI,
harvesting_data = harvesting_data
)
# Display the chart
print(cohort_chart)
}, error = function(e) {
safe_log(paste("Error generating cohort performance chart:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating cohort performance visualization.</div>")
})
```

Binary file not shown.

View file

@ -1,137 +1,117 @@
# nolint start: commented_code_linter, line_length_linter,object_usage_linter. # CI_EXTRACTION.R
# ==============
# This script processes satellite imagery to extract Canopy Index (CI) values for agricultural fields.
# It handles image processing, masking, and extraction of statistics by field/sub-field.
#
# Usage: Rscript ci_extraction.R [end_date] [offset] [project_dir]
# - end_date: End date for processing (YYYY-MM-DD format)
# - offset: Number of days to look back from end_date
# - project_dir: Project directory name (e.g., "chemba")
#
# 1. Load required packages
# -----------------------
suppressPackageStartupMessages({
library(sf) library(sf)
library(terra) library(terra)
library(tidyverse) library(tidyverse)
library(lubridate) library(lubridate)
library(exactextractr) library(exactextractr)
library(readxl) library(readxl)
library(here)
})
# Vang alle command line argumenten op # 2. Process command line arguments
# ------------------------------
main <- function() {
# Capture command line arguments
args <- commandArgs(trailingOnly = TRUE) args <- commandArgs(trailingOnly = TRUE)
# Controleer of er ten minste één argument is doorgegeven # Process end_date argument
if (length(args) == 0) { if (length(args) >= 1 && !is.na(args[1])) {
stop("Geen argumenten doorgegeven aan het script")
}
# Converteer het eerste argument naar een numerieke waarde
end_date <- as.Date(args[1]) end_date <- as.Date(args[1])
if (is.na(end_date)) { if (is.na(end_date)) {
end_date <- lubridate::dmy("28-08-2024") warning("Invalid end_date provided. Using default (current date).")
end_date <- Sys.Date()
#end_date <- "2023-10-01"
}
} else {
end_date <- Sys.Date()
#end_date <- "2023-10-01"
} }
# Process offset argument
if (length(args) >= 2 && !is.na(args[2])) {
offset <- as.numeric(args[2]) offset <- as.numeric(args[2])
# Controleer of weeks_ago een geldig getal is if (is.na(offset) || offset <= 0) {
if (is.na(offset)) { warning("Invalid offset provided. Using default (7 days).")
# stop("Het argument is geen geldig getal") offset <- 7
}
} else {
offset <- 7 offset <- 7
} }
week <- week(end_date) # Process project_dir argument
# Converteer het tweede argument naar een string waarde if (length(args) >= 3 && !is.na(args[3])) {
project_dir <- as.character(args[3]) project_dir <- as.character(args[3])
} else {
# Controleer of data_dir een geldige waarde is
if (!is.character(project_dir)) {
project_dir <- "chemba" project_dir <- "chemba"
} }
new_project_question = FALSE
# Make project_dir available globally so parameters_project.R can use it
assign("project_dir", project_dir, envir = .GlobalEnv)
# 3. Initialize project configuration
# --------------------------------
new_project_question <- FALSE
tryCatch({
source("parameters_project.R") source("parameters_project.R")
source("ci_extraction_utils.R") source("ci_extraction_utils.R")
}, error = function(e) {
warning("Default source files not found. Attempting to source from 'r_app' directory.")
tryCatch({
source(here::here("r_app", "parameters_project.R"))
source(here::here("r_app", "ci_extraction_utils.R"))
warning(paste("Successfully sourced files from 'r_app' directory."))
}, error = function(e) {
stop("Failed to source required files from both default and 'r_app' directories.")
})
})
# 4. Generate date list for processing
# ---------------------------------
dates <- date_list(end_date, offset) dates <- date_list(end_date, offset)
print(dates) log_message(paste("Processing data for week", dates$week, "of", dates$year))
raster_files <- list.files(planet_tif_folder,full.names = T, pattern = ".tif") # 5. Find and filter raster files by date
# -----------------------------------
log_message("Searching for raster files")
filtered_files <- map(dates$days_filter, ~ raster_files[grepl(pattern = .x, x = raster_files)]) %>% tryCatch({
compact() %>% # Use the new utility function to find satellite images
flatten_chr() existing_files <- find_satellite_images(planet_tif_folder, dates$days_filter)
log_message(paste("Found", length(existing_files), "raster files for processing"))
# Remove files that do not exist # 6. Process raster files and create VRT
existing_files <- filtered_files[file.exists(filtered_files)] # -----------------------------------
# Use the new utility function for batch processing
vrt_list <- process_satellite_images(existing_files, field_boundaries, merged_final, daily_vrt)
# Check if the list of existing files is empty # 7. Process and combine CI values
if (length(existing_files) == 0) { # ------------------------------
message("No files exist for the given date(s).") # Call the process_ci_values function from utils with all required parameters
stop("Terminating script.") process_ci_values(dates, field_boundaries, merged_final,
field_boundaries_sf, daily_CI_vals_dir, cumulative_CI_vals_dir)
}, error = function(e) {
log_message(paste("Error in main processing:", e$message), level = "ERROR")
stop(e$message)
})
} }
# Continue with the rest of the script if (sys.nframe() == 0) {
print(existing_files) main()
vrt_list <- list()
for (file in existing_files) {
v_crop <- create_mask_and_crop(file, field_boundaries, merged_final)
emtpy_or_full <- global(v_crop, "notNA")
vrt_file <- here(daily_vrt, paste0(tools::file_path_sans_ext(basename(file)), ".vrt"))
if(emtpy_or_full[1,] > 100){
vrt_list[vrt_file] <- vrt_file
}else{
file.remove(vrt_file)
}
message(file, " processed")
gc()
}
raster_files_NEW <- list.files(merged_final,full.names = T, pattern = ".tif")
# Define the path to the file
file_path <- here(cumulative_CI_vals_dir, "combined_CI_data.rds")
# Check if the file exists
if (!file.exists(file_path)) {
# File does not exist, create it with all available data
print("combined_CI_data.rds does not exist. Preparing combined_CI_data.rds file for all available images.")
# Extract data from all raster files
walk(raster_files_NEW, extract_rasters_daily, field_geojson = field_boundaries, quadrants = FALSE, daily_CI_vals_dir)
# Combine all extracted data
extracted_values <- list.files(here(daily_CI_vals_dir), full.names = TRUE)
pivot_stats <- extracted_values %>%
map(readRDS) %>% list_rbind() %>%
group_by(sub_field)
# Save the combined data to the file
saveRDS(pivot_stats, file_path)
print("All CI values extracted from all historic images and saved to combined_CI_data.rds.")
} else {
# File exists, add new data
print("combined_CI_data.rds exists, adding the latest image data to the table.")
# Filter and process the latest data
filtered_files <- map(dates$days_filter, ~ raster_files_NEW[grepl(pattern = .x, x = raster_files_NEW)]) %>%
compact() %>%
flatten_chr()
walk(filtered_files, extract_rasters_daily, field_geojson = field_boundaries, quadrants = TRUE, daily_CI_vals_dir)
# Extract new values
extracted_values <- list.files(daily_CI_vals_dir, full.names = TRUE)
extracted_values <- map(dates$days_filter, ~ extracted_values[grepl(pattern = .x, x = extracted_values)]) %>%
compact() %>%
flatten_chr()
pivot_stats <- extracted_values %>%
map(readRDS) %>% list_rbind() %>%
group_by(sub_field)
# Load existing data and append new data
combined_CI_data <- readRDS(file_path)
pivot_stats2 <- bind_rows(pivot_stats, combined_CI_data)
# Save the updated combined data
saveRDS(pivot_stats2, file_path)
print("All CI values extracted from the latest images and added to combined_CI_data.rds.")
} }

View file

@ -1,53 +1,426 @@
# Utils for ci extraction # CI_EXTRACTION_UTILS.R
# =====================
# Utility functions for the SmartCane CI (Chlorophill Index) extraction workflow.
# These functions support date handling, raster processing, and data extraction.
#' Safe logging function that works whether log_message exists or not
#'
#' @param message The message to log
#' @param level The log level (default: "INFO")
#' @return NULL (used for side effects)
#'
safe_log <- function(message, level = "INFO") {
if (exists("log_message")) {
log_message(message, level)
} else {
if (level %in% c("ERROR", "WARNING")) {
warning(message)
} else {
message(message)
}
}
}
#' Generate a sequence of dates for processing
#'
#' @param end_date The end date for the sequence (Date object)
#' @param offset Number of days to look back from end_date
#' @return A list containing week number, year, and a sequence of dates for filtering
#'
date_list <- function(end_date, offset) { date_list <- function(end_date, offset) {
offset <- as.numeric(offset) - 1 # Input validation
if (!lubridate::is.Date(end_date)) {
end_date <- as.Date(end_date) end_date <- as.Date(end_date)
if (is.na(end_date)) {
stop("Invalid end_date provided. Expected a Date object or a string convertible to Date.")
}
}
offset <- as.numeric(offset)
if (is.na(offset) || offset < 1) {
stop("Invalid offset provided. Expected a positive number.")
}
# Calculate date range
offset <- offset - 1 # Adjust offset to include end_date
start_date <- end_date - lubridate::days(offset) start_date <- end_date - lubridate::days(offset)
week <- week(start_date) # Extract week and year information
year <- year(start_date) week <- lubridate::week(start_date)
days_filter <- seq(from = start_date, to = end_date, by = "day") year <- lubridate::year(start_date)
return(list("week" = week, "year" = year, "days_filter" = days_filter)) # Generate sequence of dates
days_filter <- seq(from = start_date, to = end_date, by = "day")
days_filter <- format(days_filter, "%Y-%m-%d") # Format for consistent filtering
# Log the date range
safe_log(paste("Date range generated from", start_date, "to", end_date))
return(list(
"week" = week,
"year" = year,
"days_filter" = days_filter,
"start_date" = start_date,
"end_date" = end_date
))
} }
#' Create a Chlorophill Index (CI) mask from satellite imagery and crop to field boundaries
#'
#' @param file Path to the satellite image file
#' @param field_boundaries Field boundaries vector object
#' @param merged_final_dir Directory to save the processed raster
#' @return Processed raster object with CI band
#'
create_mask_and_crop <- function(file, field_boundaries, merged_final_dir) { create_mask_and_crop <- function(file, field_boundaries, merged_final_dir) {
message("starting ", file) # Validate inputs
loaded_raster <- rast(file) if (!file.exists(file)) {
names(loaded_raster) <- c("Red", "Green", "Blue", "NIR") stop(paste("File not found:", file))
message("raster loaded") }
if (is.null(field_boundaries)) {
stop("Field boundaries are required but were not provided")
}
# Establish file names for output
basename_no_ext <- tools::file_path_sans_ext(basename(file))
new_file <- here::here(merged_final_dir, paste0(basename_no_ext, ".tif"))
vrt_file <- here::here(daily_vrt, paste0(basename_no_ext, ".vrt"))
# Process with error handling
tryCatch({
# Log processing start
safe_log(paste("Processing", basename(file)))
# Load and prepare raster
loaded_raster <- terra::rast(file)
# Validate raster has necessary bands
if (terra::nlyr(loaded_raster) < 4) {
stop("Raster must have at least 4 bands (Red, Green, Blue, NIR)")
}
# Name bands for clarity
names(loaded_raster) <- c("Red", "Green", "Blue", "NIR")
# Calculate Canopy Index
CI <- loaded_raster$NIR / loaded_raster$Green - 1 CI <- loaded_raster$NIR / loaded_raster$Green - 1
# Add CI to raster and mask invalid values
loaded_raster$CI <- CI loaded_raster$CI <- CI
message("CI calculated") loaded_raster <- terra::crop(loaded_raster, field_boundaries, mask = TRUE)
loaded_raster <- terra::crop(loaded_raster, field_boundaries, mask = TRUE) #%>% CI_func()
# Replace zeros with NA for better visualization and analysis
loaded_raster[loaded_raster == 0] <- NA loaded_raster[loaded_raster == 0] <- NA
new_file <- here(merged_final_dir, paste0(tools::file_path_sans_ext(basename(file)), ".tif"))
writeRaster(loaded_raster, new_file, overwrite = TRUE)
vrt_file <- here(daily_vrt, paste0(tools::file_path_sans_ext(basename(file)), ".vrt")) # Write output files
terra::writeRaster(loaded_raster, new_file, overwrite = TRUE)
terra::vrt(new_file, vrt_file, overwrite = TRUE) terra::vrt(new_file, vrt_file, overwrite = TRUE)
# Check if the result has enough valid pixels
valid_pixels <- terra::global(loaded_raster$CI, "notNA", na.rm=TRUE)
# Log completion
safe_log(paste("Completed processing", basename(file),
"- Valid pixels:", valid_pixels[1,]))
return(loaded_raster) return(loaded_raster)
}, error = function(e) {
err_msg <- paste("Error processing", basename(file), "-", e$message)
safe_log(err_msg, "ERROR")
return(NULL)
}, finally = {
# Clean up memory
gc()
})
} }
#' Process a batch of satellite images and create VRT files
#'
#' @param files Vector of file paths to process
#' @param field_boundaries Field boundaries vector object for cropping
#' @param merged_final_dir Directory to save processed rasters
#' @param daily_vrt_dir Directory to save VRT files
#' @param min_valid_pixels Minimum number of valid pixels for a raster to be kept (default: 100)
#' @return List of valid VRT files created
#'
process_satellite_images <- function(files, field_boundaries, merged_final_dir, daily_vrt_dir, min_valid_pixels = 100) {
vrt_list <- list()
safe_log(paste("Starting batch processing of", length(files), "files"))
# Process each file
for (file in files) {
# Process each raster file
v_crop <- create_mask_and_crop(file, field_boundaries, merged_final_dir)
# Skip if processing failed
if (is.null(v_crop)) {
next
}
# Check if the raster has enough valid data
valid_data <- terra::global(v_crop, "notNA")
vrt_file <- here::here(daily_vrt_dir, paste0(tools::file_path_sans_ext(basename(file)), ".vrt"))
if (valid_data[1,] > min_valid_pixels) {
vrt_list[[vrt_file]] <- vrt_file
} else {
# Remove VRT files with insufficient data
if (file.exists(vrt_file)) {
file.remove(vrt_file)
}
safe_log(paste("Skipping", basename(file), "- insufficient valid data"), "WARNING")
}
# Clean up memory
rm(v_crop)
gc()
}
safe_log(paste("Completed processing", length(vrt_list), "raster files"))
return(vrt_list)
}
#' Find satellite image files filtered by date
#'
#' @param tif_folder Directory containing satellite imagery files
#' @param dates_filter Character vector of dates in YYYY-MM-DD format
#' @return Vector of file paths matching the date filter
#'
find_satellite_images <- function(tif_folder, dates_filter) {
# Find all raster files
raster_files <- list.files(tif_folder, full.names = TRUE, pattern = "\\.tif$")
if (length(raster_files) == 0) {
stop("No raster files found in directory: ", tif_folder)
}
# Filter files by dates
filtered_files <- purrr::map(dates_filter, ~ raster_files[grepl(pattern = .x, x = raster_files)]) %>%
purrr::compact() %>%
purrr::flatten_chr()
# Remove files that do not exist
existing_files <- filtered_files[file.exists(filtered_files)]
# Check if the list of existing files is empty
if (length(existing_files) == 0) {
stop("No files found matching the date filter: ", paste(dates_filter, collapse = ", "))
}
return(existing_files)
}
#' Extract date from file path
#'
#' @param file_path Path to the file
#' @return Extracted date in YYYY-MM-DD format
#'
date_extract <- function(file_path) { date_extract <- function(file_path) {
str_extract(file_path, "\\d{4}-\\d{2}-\\d{2}") date <- stringr::str_extract(file_path, "\\d{4}-\\d{2}-\\d{2}")
if (is.na(date)) {
warning(paste("Could not extract date from file path: ", file_path))
} }
return(date)
}
#' Extract CI values from a raster for each field or subfield
#'
#' @param file Path to the raster file
#' @param field_geojson Field boundaries as SF object
#' @param quadrants Boolean indicating whether to extract by quadrants
#' @param save_dir Directory to save the extracted values
#' @return Path to the saved RDS file
#'
extract_rasters_daily <- function(file, field_geojson, quadrants = TRUE, save_dir) { extract_rasters_daily <- function(file, field_geojson, quadrants = TRUE, save_dir) {
field_geojson <- sf::st_as_sf(field_geojson) # Validate inputs
x <- rast(file[1]) if (!file.exists(file)) {
date <- date_extract(file) stop(paste("File not found: ", file))
pivot_stats <- cbind(field_geojson, mean_CI = round(exactextractr::exact_extract(x$CI, field_geojson, fun = "mean"), 2)) %>%
st_drop_geometry() %>% rename("{date}" := mean_CI)
save_suffix <- if (quadrants){"quadrant"} else {"whole_field"}
save_path <- here(save_dir, paste0("extracted_", date, "_", save_suffix, ".rds"))
saveRDS(pivot_stats, save_path)
} }
if (!inherits(field_geojson, "sf") && !inherits(field_geojson, "sfc")) {
field_geojson <- sf::st_as_sf(field_geojson)
}
# Extract date from file path
date <- date_extract(file)
if (is.na(date)) {
stop(paste("Could not extract date from file path:", file))
}
# Log extraction start
safe_log(paste("Extracting CI values for", date, "- Using quadrants:", quadrants))
# Process with error handling
tryCatch({
# Load raster
x <- terra::rast(file)
# Check if CI band exists
if (!"CI" %in% names(x)) {
stop("CI band not found in raster")
}
# Extract statistics
pivot_stats <- cbind(
field_geojson,
mean_CI = round(exactextractr::exact_extract(x$CI, field_geojson, fun = "mean"), 2)
) %>%
sf::st_drop_geometry() %>%
dplyr::rename("{date}" := mean_CI)
# Determine save path
save_suffix <- if (quadrants) {"quadrant"} else {"whole_field"}
save_path <- here::here(save_dir, paste0("extracted_", date, "_", save_suffix, ".rds"))
# Save extracted data
saveRDS(pivot_stats, save_path)
# Log success
safe_log(paste("Successfully extracted and saved CI values for", date))
return(save_path)
}, error = function(e) {
err_msg <- paste("Error extracting CI values for", date, "-", e$message)
safe_log(err_msg, "ERROR")
return(NULL)
})
}
#' Combine daily CI values into a single dataset
#'
#' @param daily_CI_vals_dir Directory containing daily CI values
#' @param output_file Path to save the combined dataset
#' @return Combined dataset as a tibble
#'
combine_ci_values <- function(daily_CI_vals_dir, output_file = NULL) {
# List all RDS files in the daily CI values directory
files <- list.files(path = daily_CI_vals_dir, pattern = "^extracted_.*\\.rds$", full.names = TRUE)
if (length(files) == 0) {
stop("No extracted CI values found in directory:", daily_CI_vals_dir)
}
# Log process start
safe_log(paste("Combining", length(files), "CI value files"))
# Load and combine all files
combined_data <- files %>%
purrr::map(readRDS) %>%
purrr::list_rbind() %>%
dplyr::group_by(sub_field)
# Save if output file is specified
if (!is.null(output_file)) {
saveRDS(combined_data, output_file)
safe_log(paste("Combined CI values saved to", output_file))
}
return(combined_data)
}
#' Update existing CI data with new values
#'
#' @param new_data New CI data to be added
#' @param existing_data_file Path to the existing data file
#' @return Updated combined dataset
#'
update_ci_data <- function(new_data, existing_data_file) {
if (!file.exists(existing_data_file)) {
warning(paste("Existing data file not found:", existing_data_file))
return(new_data)
}
# Load existing data
existing_data <- readRDS(existing_data_file)
# Combine data, handling duplicates by keeping the newer values
combined_data <- dplyr::bind_rows(new_data, existing_data) %>%
dplyr::distinct() %>%
dplyr::group_by(sub_field)
# Save updated data
saveRDS(combined_data, existing_data_file)
safe_log(paste("Updated CI data saved to", existing_data_file))
return(combined_data)
}
#' Process and combine CI values from raster files
#'
#' @param dates List of dates from date_list()
#' @param field_boundaries Field boundaries as vector object
#' @param merged_final_dir Directory with processed raster files
#' @param field_boundaries_sf Field boundaries as SF object
#' @param daily_CI_vals_dir Directory to save daily CI values
#' @param cumulative_CI_vals_dir Directory to save cumulative CI values
#' @return NULL (used for side effects)
#'
process_ci_values <- function(dates, field_boundaries, merged_final_dir,
field_boundaries_sf, daily_CI_vals_dir,
cumulative_CI_vals_dir) {
# Find processed raster files
raster_files <- list.files(merged_final_dir, full.names = TRUE, pattern = "\\.tif$")
# Define path for combined CI data
combined_ci_path <- here::here(cumulative_CI_vals_dir, "combined_CI_data.rds")
# Check if the combined CI data file exists
if (!file.exists(combined_ci_path)) {
# Process all available data if file doesn't exist
safe_log("combined_CI_data.rds does not exist. Creating new file with all available data.")
# Extract data from all raster files
purrr::walk(
raster_files,
extract_rasters_daily,
field_geojson = field_boundaries_sf,
quadrants = FALSE,
save_dir = daily_CI_vals_dir
)
# Combine all extracted data
pivot_stats <- combine_ci_values(daily_CI_vals_dir, combined_ci_path)
safe_log("All CI values extracted from historic images and saved.")
} else {
# Process only the latest data and add to existing file
safe_log("combined_CI_data.rds exists, adding the latest image data.")
# Filter files by dates
filtered_files <- purrr::map(dates$days_filter, ~ raster_files[grepl(pattern = .x, x = raster_files)]) %>%
purrr::compact() %>%
purrr::flatten_chr()
# Extract data for the new files
purrr::walk(
filtered_files,
extract_rasters_daily,
field_geojson = field_boundaries_sf,
quadrants = TRUE,
save_dir = daily_CI_vals_dir
)
# Filter extracted values files by the current date range
extracted_values <- list.files(daily_CI_vals_dir, full.names = TRUE)
extracted_values <- purrr::map(dates$days_filter, ~ extracted_values[grepl(pattern = .x, x = extracted_values)]) %>%
purrr::compact() %>%
purrr::flatten_chr()
# Combine new values
new_pivot_stats <- extracted_values %>%
purrr::map(readRDS) %>%
purrr::list_rbind() %>%
dplyr::group_by(sub_field)
# Update the combined data file
update_ci_data(new_pivot_stats, combined_ci_path)
safe_log("CI values from latest images added to combined_CI_data.rds")
}
}

View file

@ -0,0 +1,572 @@
# CI_EXTRACTION_AND_YIELD_PREDICTION.R
# =====================================
#
# This standalone script demonstrates:
# 1. How Chlorophyll Index (CI) is extracted from satellite imagery
# 2. How yield prediction is performed based on CI values
#
# Created for sharing with colleagues to illustrate the core functionality
# of the SmartCane monitoring system.
#
# -----------------------------
# PART 1: LIBRARY DEPENDENCIES
# -----------------------------
suppressPackageStartupMessages({
# Spatial data processing
library(sf)
library(terra)
library(exactextractr)
# Data manipulation
library(tidyverse)
library(lubridate)
library(here)
# Machine learning for yield prediction
library(rsample)
library(caret)
library(randomForest)
library(CAST)
})
# ----------------------------------
# PART 2: LOGGING & UTILITY FUNCTIONS
# ----------------------------------
#' Safe logging function that works in any environment
#'
#' @param message The message to log
#' @param level The log level (default: "INFO")
#' @return NULL (used for side effects)
#'
safe_log <- function(message, level = "INFO") {
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%S")
formatted_msg <- paste0("[", timestamp, "][", level, "] ", message)
if (level %in% c("ERROR", "WARNING")) {
warning(formatted_msg)
} else {
message(formatted_msg)
}
}
#' Generate a sequence of dates for processing
#'
#' @param end_date The end date for the sequence (Date object)
#' @param offset Number of days to look back from end_date
#' @return A list containing week number, year, and a sequence of dates for filtering
#'
date_list <- function(end_date, offset) {
# Input validation
if (!lubridate::is.Date(end_date)) {
end_date <- as.Date(end_date)
if (is.na(end_date)) {
stop("Invalid end_date provided. Expected a Date object or a string convertible to Date.")
}
}
offset <- as.numeric(offset)
if (is.na(offset) || offset < 1) {
stop("Invalid offset provided. Expected a positive number.")
}
# Calculate date range
offset <- offset - 1 # Adjust offset to include end_date
start_date <- end_date - lubridate::days(offset)
# Extract week and year information
week <- lubridate::week(start_date)
year <- lubridate::year(start_date)
# Generate sequence of dates
days_filter <- seq(from = start_date, to = end_date, by = "day")
days_filter <- format(days_filter, "%Y-%m-%d") # Format for consistent filtering
# Log the date range
safe_log(paste("Date range generated from", start_date, "to", end_date))
return(list(
"week" = week,
"year" = year,
"days_filter" = days_filter,
"start_date" = start_date,
"end_date" = end_date
))
}
# -----------------------------
# PART 3: CI EXTRACTION PROCESS
# -----------------------------
#' Find satellite imagery files within a specific date range
#'
#' @param image_folder Path to the folder containing satellite images
#' @param date_filter Vector of dates to filter by (in YYYY-MM-DD format)
#' @return Vector of file paths matching the date filter
#'
find_satellite_images <- function(image_folder, date_filter) {
# Validate inputs
if (!dir.exists(image_folder)) {
stop(paste("Image folder not found:", image_folder))
}
# List all files in the directory
all_files <- list.files(image_folder, pattern = "\\.tif$", full.names = TRUE, recursive = TRUE)
if (length(all_files) == 0) {
safe_log("No TIF files found in the specified directory", "WARNING")
return(character(0))
}
# Filter files by date pattern in filename
filtered_files <- character(0)
for (date in date_filter) {
# Format date for matching (remove dashes)
date_pattern <- gsub("-", "", date)
# Find files with matching date pattern
matching_files <- all_files[grepl(date_pattern, all_files)]
if (length(matching_files) > 0) {
filtered_files <- c(filtered_files, matching_files)
safe_log(paste("Found", length(matching_files), "files for date", date))
}
}
return(filtered_files)
}
#' Create a Chlorophyll Index (CI) from satellite imagery
#'
#' @param raster_obj A SpatRaster object with Red, Green, Blue, and NIR bands
#' @return A SpatRaster object with a CI band
#'
calculate_ci <- function(raster_obj) {
# Validate input has required bands
if (terra::nlyr(raster_obj) < 4) {
stop("Raster must have at least 4 bands (Red, Green, Blue, NIR)")
}
# Extract bands (assuming standard order: B, G, R, NIR)
blue_band <- raster_obj[[1]]
green_band <- raster_obj[[2]]
red_band <- raster_obj[[3]]
nir_band <- raster_obj[[4]]
# CI formula: (NIR / Red) - 1
# This highlights chlorophyll content in vegetation
ci_raster <- (nir_band / red_band) - 1
# Filter extreme values that may result from division operations
ci_raster[ci_raster > 10] <- 10 # Cap max value
ci_raster[ci_raster < 0] <- 0 # Cap min value
# Name the layer
names(ci_raster) <- "CI"
return(ci_raster)
}
#' Create a mask for cloudy pixels and shadows using thresholds
#'
#' @param raster_obj A SpatRaster object with multiple bands
#' @return A binary mask where 1=clear pixel, 0=cloudy or shadow pixel
#'
create_cloud_mask <- function(raster_obj) {
# Extract bands
blue_band <- raster_obj[[1]]
green_band <- raster_obj[[2]]
red_band <- raster_obj[[3]]
nir_band <- raster_obj[[4]]
# Create initial mask (all pixels valid)
mask <- blue_band * 0 + 1
# Calculate indices used for detection
ndvi <- (nir_band - red_band) / (nir_band + red_band)
brightness <- (blue_band + green_band + red_band) / 3
# CLOUD DETECTION CRITERIA
# ------------------------
# Clouds are typically very bright in all bands
bright_pixels <- (blue_band > 0.3) & (green_band > 0.3) & (red_band > 0.3)
# Snow/high reflectance clouds have high blue values
blue_dominant <- blue_band > (red_band * 1.2)
# Low NDVI areas that are bright are likely clouds
low_ndvi <- ndvi < 0.1
# Combine cloud criteria
cloud_pixels <- bright_pixels & (blue_dominant | low_ndvi)
# SHADOW DETECTION CRITERIA
# ------------------------
# Shadows typically have:
# 1. Low overall brightness across all bands
# 2. Lower NIR reflectance
# 3. Can still have reasonable NDVI (if over vegetation)
# Dark pixels in visible spectrum
dark_pixels <- brightness < 0.1
# Low NIR reflectance
low_nir <- nir_band < 0.15
# Shadows often have higher blue proportion relative to NIR
blue_nir_ratio <- blue_band / (nir_band + 0.01) # Add small constant to avoid division by zero
blue_enhanced <- blue_nir_ratio > 0.8
# Combine shadow criteria
shadow_pixels <- dark_pixels & (low_nir | blue_enhanced)
# Update mask (0 for cloud or shadow pixels)
mask[cloud_pixels | shadow_pixels] <- 0
# Optional: create different values for clouds vs shadows for visualization
# mask[cloud_pixels] <- 0 # Clouds
# mask[shadow_pixels] <- 0 # Shadows
return(mask)
}
#' Process satellite image, calculate CI, and crop to field boundaries
#'
#' @param file Path to the satellite image file
#' @param field_boundaries Field boundaries vector object
#' @param output_dir Directory to save the processed raster
#' @return Path to the processed raster file
#'
process_satellite_image <- function(file, field_boundaries, output_dir) {
# Validate inputs
if (!file.exists(file)) {
stop(paste("File not found:", file))
}
if (is.null(field_boundaries)) {
stop("Field boundaries are required but were not provided")
}
# Create output filename
basename_no_ext <- tools::file_path_sans_ext(basename(file))
output_file <- here::here(output_dir, paste0(basename_no_ext, "_CI.tif"))
# Process with error handling
tryCatch({
# Load and prepare raster
loaded_raster <- terra::rast(file)
# Calculate CI
ci_raster <- calculate_ci(loaded_raster)
# Create cloud mask
cloud_mask <- create_cloud_mask(loaded_raster)
# Apply cloud mask to CI
ci_masked <- ci_raster * cloud_mask
# Crop to field boundaries extent (for efficiency)
field_extent <- terra::ext(field_boundaries)
ci_cropped <- terra::crop(ci_masked, field_extent)
# Write output
terra::writeRaster(ci_cropped, output_file, overwrite = TRUE)
safe_log(paste("Successfully processed", basename(file)))
return(output_file)
}, error = function(e) {
safe_log(paste("Error processing", basename(file), ":", e$message), "ERROR")
return(NULL)
})
}
#' Extract CI statistics for each field
#'
#' @param ci_raster A SpatRaster with CI values
#' @param field_boundaries An sf object with field polygons
#' @return A data frame with CI statistics by field
#'
extract_ci_by_field <- function(ci_raster, field_boundaries) {
# Validate inputs
if (is.null(ci_raster)) {
stop("CI raster is required but was NULL")
}
if (is.null(field_boundaries) || nrow(field_boundaries) == 0) {
stop("Field boundaries are required but were empty")
}
# Extract statistics using exact extraction (weighted by coverage)
ci_stats <- exactextractr::exact_extract(
ci_raster,
field_boundaries,
fun = c("mean", "median", "min", "max", "stdev", "count"),
progress = FALSE
)
# Add field identifiers
ci_stats$field <- field_boundaries$field
if ("sub_field" %in% names(field_boundaries)) {
ci_stats$sub_field <- field_boundaries$sub_field
} else {
ci_stats$sub_field <- field_boundaries$field
}
# Add date info
ci_stats$date <- Sys.Date()
# Clean up names
names(ci_stats) <- gsub("CI\\.", "", names(ci_stats))
return(ci_stats)
}
# -----------------------------------------
# PART 4: YIELD PREDICTION IMPLEMENTATION
# -----------------------------------------
#' Prepare data for yield prediction model
#'
#' @param ci_data Data frame with cumulative CI values
#' @param harvest_data Data frame with harvest information
#' @return Data frame ready for modeling
#'
prepare_yield_prediction_data <- function(ci_data, harvest_data) {
# Join CI and yield data
ci_and_yield <- dplyr::left_join(ci_data, harvest_data, by = c("field", "sub_field", "season")) %>%
dplyr::group_by(sub_field, season) %>%
dplyr::slice(which.max(DOY)) %>%
dplyr::select(field, sub_field, tonnage_ha, cumulative_CI, DOY, season, sub_area) %>%
dplyr::mutate(CI_per_day = cumulative_CI / DOY)
# Split into training and prediction sets
ci_and_yield_train <- ci_and_yield %>%
as.data.frame() %>%
dplyr::filter(!is.na(tonnage_ha))
prediction_yields <- ci_and_yield %>%
as.data.frame() %>%
dplyr::filter(is.na(tonnage_ha))
return(list(
train = ci_and_yield_train,
predict = prediction_yields
))
}
#' Train a random forest model for yield prediction
#'
#' @param training_data Data frame with training data
#' @param predictors Vector of predictor variable names
#' @param response Name of the response variable
#' @return Trained model
#'
train_yield_model <- function(training_data, predictors = c("cumulative_CI", "DOY", "CI_per_day"), response = "tonnage_ha") {
# Configure model training parameters
ctrl <- caret::trainControl(
method = "cv",
savePredictions = TRUE,
allowParallel = TRUE,
number = 5,
verboseIter = TRUE
)
# Train the model with feature selection
set.seed(202) # For reproducibility
model_ffs_rf <- CAST::ffs(
training_data[, predictors],
training_data[, response],
method = "rf",
trControl = ctrl,
importance = TRUE,
withinSE = TRUE,
tuneLength = 5,
na.rm = TRUE
)
return(model_ffs_rf)
}
#' Format predictions into a clean data frame
#'
#' @param predictions Raw prediction results
#' @param newdata Original data frame with field information
#' @return Formatted predictions data frame
#'
prepare_predictions <- function(predictions, newdata) {
return(predictions %>%
as.data.frame() %>%
dplyr::rename(predicted_Tcha = ".") %>%
dplyr::mutate(
sub_field = newdata$sub_field,
field = newdata$field,
Age_days = newdata$DOY,
total_CI = round(newdata$cumulative_CI, 0),
predicted_Tcha = round(predicted_Tcha, 0),
season = newdata$season
) %>%
dplyr::select(field, sub_field, Age_days, total_CI, predicted_Tcha, season) %>%
dplyr::left_join(., newdata, by = c("field", "sub_field", "season"))
)
}
#' Predict yields for mature fields
#'
#' @param model Trained model
#' @param prediction_data Data frame with fields to predict
#' @param min_age Minimum age in days to qualify as mature (default: 300)
#' @return Data frame with yield predictions
#'
predict_yields <- function(model, prediction_data, min_age = 300) {
# Make predictions
predictions <- stats::predict(model, newdata = prediction_data)
# Format predictions
pred_formatted <- prepare_predictions(predictions, prediction_data) %>%
dplyr::filter(Age_days > min_age) %>%
dplyr::mutate(CI_per_day = round(total_CI / Age_days, 1))
return(pred_formatted)
}
# ------------------------------
# PART 5: DEMONSTRATION WORKFLOW
# ------------------------------
#' Demonstration workflow showing how to use the functions
#'
#' @param end_date The end date for processing satellite images
#' @param offset Number of days to look back
#' @param image_folder Path to the folder containing satellite images
#' @param field_boundaries_path Path to field boundaries shapefile
#' @param output_dir Path to save processed outputs
#' @param harvest_data_path Path to historical harvest data
#'
demo_workflow <- function(end_date = Sys.Date(), offset = 7,
image_folder = "path/to/satellite/images",
field_boundaries_path = "path/to/field_boundaries.shp",
output_dir = "path/to/output",
harvest_data_path = "path/to/harvest_data.csv") {
# Step 1: Generate date list for processing
dates <- date_list(end_date, offset)
safe_log(paste("Processing data for week", dates$week, "of", dates$year))
# Step 2: Load field boundaries
field_boundaries <- sf::read_sf(field_boundaries_path)
safe_log(paste("Loaded", nrow(field_boundaries), "field boundaries"))
# Step 3: Find satellite images for the specified date range
image_files <- find_satellite_images(image_folder, dates$days_filter)
safe_log(paste("Found", length(image_files), "satellite images for processing"))
# Step 4: Process each satellite image and calculate CI
ci_files <- list()
for (file in image_files) {
ci_file <- process_satellite_image(file, field_boundaries, output_dir)
if (!is.null(ci_file)) {
ci_files <- c(ci_files, ci_file)
}
}
# Step 5: Extract CI statistics for each field
ci_stats_list <- list()
for (ci_file in ci_files) {
ci_raster <- terra::rast(ci_file)
ci_stats <- extract_ci_by_field(ci_raster, field_boundaries)
ci_stats_list[[basename(ci_file)]] <- ci_stats
}
# Combine all stats
all_ci_stats <- dplyr::bind_rows(ci_stats_list)
safe_log(paste("Extracted CI statistics for", nrow(all_ci_stats), "field-date combinations"))
# Step 6: Prepare for yield prediction
if (file.exists(harvest_data_path)) {
# Load harvest data
harvest_data <- read.csv(harvest_data_path)
safe_log("Loaded harvest data for yield prediction")
# Make up cumulative_CI data for demonstration purposes
# In a real scenario, this would come from accumulating CI values over time
ci_data <- all_ci_stats %>%
dplyr::group_by(field, sub_field) %>%
dplyr::summarise(
cumulative_CI = sum(mean, na.rm = TRUE),
DOY = n(), # Days of year as the count of observations
season = lubridate::year(max(date, na.rm = TRUE)),
.groups = "drop"
)
# Prepare data for modeling
modeling_data <- prepare_yield_prediction_data(ci_data, harvest_data)
if (nrow(modeling_data$train) > 0) {
# Train yield prediction model
yield_model <- train_yield_model(modeling_data$train)
safe_log("Trained yield prediction model")
# Predict yields for mature fields
yield_predictions <- predict_yields(yield_model, modeling_data$predict)
safe_log(paste("Generated yield predictions for", nrow(yield_predictions), "fields"))
# Return results
return(list(
ci_stats = all_ci_stats,
yield_predictions = yield_predictions,
model = yield_model
))
} else {
safe_log("No training data available for yield prediction", "WARNING")
return(list(ci_stats = all_ci_stats))
}
} else {
safe_log("Harvest data not found, skipping yield prediction", "WARNING")
return(list(ci_stats = all_ci_stats))
}
}
# ------------------------------
# PART 6: USAGE EXAMPLE
# ------------------------------
# Uncomment and modify paths to run the demo workflow
# results <- demo_workflow(
# end_date = "2023-10-01",
# offset = 7,
# image_folder = "data/satellite_images",
# field_boundaries_path = "data/field_boundaries.shp",
# output_dir = "output/processed",
# harvest_data_path = "data/harvest_history.csv"
# )
#
# # Access results
# ci_stats <- results$ci_stats
# yield_predictions <- results$yield_predictions
#
# # Example: Plot CI distribution by field
# if (require(ggplot2)) {
# ggplot(ci_stats, aes(x = field, y = mean, fill = field)) +
# geom_boxplot() +
# labs(title = "CI Distribution by Field",
# x = "Field",
# y = "Mean CI") +
# theme_minimal() +
# theme(axis.text.x = element_text(angle = 45, hjust = 1))
# }
#
# # Example: Plot predicted yield vs age
# if (exists("yield_predictions") && require(ggplot2)) {
# ggplot(yield_predictions, aes(x = Age_days, y = predicted_Tcha, color = field)) +
# geom_point(size = 3) +
# geom_text(aes(label = field), hjust = -0.2, vjust = -0.2) +
# labs(title = "Predicted Yield by Field Age",
# x = "Age (Days)",
# y = "Predicted Yield (Tonnes/ha)") +
# theme_minimal()
# }

View file

@ -0,0 +1,556 @@
# Cloud and Shadow Detection Analysis
# This script analyzes cloud and shadow detection parameters using the diagnostic GeoTIFF files
# and polygon-based classification to help optimize the detection algorithms
# Load required packages
library(terra)
library(sf)
library(dplyr)
library(ggplot2)
library(reshape2)
library(exactextractr) # For accurate polygon extraction
# Define diagnostic directory
diagnostic_dir <- "C:/Users/timon/Resilience BV/4020 SCane ESA DEMO - Documenten/General/4020 SCDEMO Team/4020 TechnicalData/WP3/smartcane/cloud_mask_diagnostics_20250515-164357"
# Simple logging function for this standalone script
safe_log <- function(message, level = "INFO") {
cat(paste0("[", level, "] ", message, "\n"))
}
safe_log("Starting cloud detection analysis on diagnostic rasters")
# Load all diagnostic rasters
safe_log("Loading diagnostic raster files...")
# Load original bands
red_band <- terra::rast(file.path(diagnostic_dir, "diagnostic_red_band.tif"))
green_band <- terra::rast(file.path(diagnostic_dir, "diagnostic_green_band.tif"))
blue_band <- terra::rast(file.path(diagnostic_dir, "diagnostic_blue_band.tif"))
nir_band <- terra::rast(file.path(diagnostic_dir, "diagnostic_nir_band.tif"))
# Load derived indices
brightness <- terra::rast(file.path(diagnostic_dir, "diagnostic_brightness.tif"))
ndvi <- terra::rast(file.path(diagnostic_dir, "diagnostic_ndvi.tif"))
blue_ratio <- terra::rast(file.path(diagnostic_dir, "diagnostic_blue_ratio.tif"))
green_nir_ratio <- terra::rast(file.path(diagnostic_dir, "diagnostic_green_nir_ratio.tif"))
ndwi <- terra::rast(file.path(diagnostic_dir, "diagnostic_ndwi.tif"))
# Load cloud detection parameters
bright_pixels <- terra::rast(file.path(diagnostic_dir, "param_bright_pixels.tif"))
very_bright_pixels <- terra::rast(file.path(diagnostic_dir, "param_very_bright_pixels.tif"))
blue_dominant <- terra::rast(file.path(diagnostic_dir, "param_blue_dominant.tif"))
low_ndvi <- terra::rast(file.path(diagnostic_dir, "param_low_ndvi.tif"))
green_dominant_nir <- terra::rast(file.path(diagnostic_dir, "param_green_dominant_nir.tif"))
high_ndwi <- terra::rast(file.path(diagnostic_dir, "param_high_ndwi.tif"))
# Load shadow detection parameters
dark_pixels <- terra::rast(file.path(diagnostic_dir, "param_dark_pixels.tif"))
very_dark_pixels <- terra::rast(file.path(diagnostic_dir, "param_very_dark_pixels.tif"))
low_nir <- terra::rast(file.path(diagnostic_dir, "param_low_nir.tif"))
shadow_ndvi <- terra::rast(file.path(diagnostic_dir, "param_shadow_ndvi.tif"))
low_red_to_blue <- terra::rast(file.path(diagnostic_dir, "param_low_red_to_blue.tif"))
high_blue_to_nir_ratio <- terra::rast(file.path(diagnostic_dir, "param_high_blue_to_nir_ratio.tif"))
blue_nir_ratio_raw <- terra::rast(file.path(diagnostic_dir, "param_blue_nir_ratio_raw.tif"))
red_blue_ratio_raw <- terra::rast(file.path(diagnostic_dir, "param_red_blue_ratio_raw.tif"))
# Load edge detection parameters
brightness_focal_sd <- terra::rast(file.path(diagnostic_dir, "param_brightness_focal_sd.tif"))
edge_pixels <- terra::rast(file.path(diagnostic_dir, "param_edge_pixels.tif"))
# Load final masks
cloud_mask <- terra::rast(file.path(diagnostic_dir, "mask_cloud.tif"))
shadow_mask <- terra::rast(file.path(diagnostic_dir, "mask_shadow.tif"))
combined_mask <- terra::rast(file.path(diagnostic_dir, "mask_combined.tif"))
dilated_mask <- terra::rast(file.path(diagnostic_dir, "mask_dilated.tif"))
safe_log("Raster data loaded successfully")
# Try to read the classification polygons if they exist
tryCatch({
# Check if the classes.geojson file exists in the diagnostic directory
classes_file <- file.path(diagnostic_dir, "classes.geojson")
# If no classes file in this directory, look for the most recent one
if (!file.exists(classes_file)) {
# Look in parent directory for most recent cloud_mask_diagnostics folder
potential_dirs <- list.dirs(path = dirname(diagnostic_dir),
full.names = TRUE,
recursive = FALSE)
# Filter for diagnostic directories and find the most recent one that has classes.geojson
diagnostic_dirs <- potential_dirs[grepl("cloud_mask_diagnostics_", potential_dirs)]
for (dir in rev(sort(diagnostic_dirs))) { # Reverse sort to get newest first
potential_file <- file.path(dir, "classes.geojson")
if (file.exists(potential_file)) {
classes_file <- potential_file
break
}
}
}
# Check if we found a classes file
if (file.exists(classes_file)) {
safe_log(paste("Using classification polygons from:", classes_file))
# Load the classification polygons
classifications <- sf::st_read(classes_file, quiet = TRUE) %>% rename(class = type)
# Remove empty polygons
classifications <- classifications[!sf::st_is_empty(classifications), ]
# Create a list to store all rasters we want to extract values from
extraction_rasters <- list(
# Original bands
red = red_band,
green = green_band,
blue = blue_band,
nir = nir_band,
# Derived indices
brightness = brightness,
ndvi = ndvi,
blue_ratio = blue_ratio,
green_nir_ratio = green_nir_ratio,
ndwi = ndwi,
# Cloud detection parameters
bright_pixels = terra::ifel(bright_pixels, 1, 0),
very_bright_pixels = terra::ifel(very_bright_pixels, 1, 0),
blue_dominant = terra::ifel(blue_dominant, 1, 0),
low_ndvi = terra::ifel(low_ndvi, 1, 0),
green_dominant_nir = terra::ifel(green_dominant_nir, 1, 0),
high_ndwi = terra::ifel(high_ndwi, 1, 0),
# Shadow detection parameters
dark_pixels = terra::ifel(dark_pixels, 1, 0),
very_dark_pixels = terra::ifel(very_dark_pixels, 1, 0),
low_nir = terra::ifel(low_nir, 1, 0),
shadow_ndvi = terra::ifel(shadow_ndvi, 1, 0),
low_red_to_blue = terra::ifel(low_red_to_blue, 1, 0),
high_blue_to_nir_ratio = terra::ifel(high_blue_to_nir_ratio, 1, 0),
blue_nir_ratio_raw = (blue_band / (nir_band + 0.01)),
red_blue_ratio_raw = (red_band / (blue_band + 0.01)),
# Edge detection parameters
brightness_focal_sd = brightness_focal_sd,
edge_pixels = terra::ifel(edge_pixels, 1, 0),
# Final masks
cloud_mask = terra::ifel(cloud_mask, 1, 0),
shadow_mask = terra::ifel(shadow_mask, 1, 0),
combined_mask = terra::ifel(combined_mask, 1, 0),
dilated_mask = terra::ifel(dilated_mask, 1, 0)
)
# Create a stack of all rasters
extraction_stack <- terra::rast(extraction_rasters)
# User-provided simplified extraction for mean statistics per polygon
pivot_stats_sf <- cbind(
classifications,
round(exactextractr::exact_extract(extraction_stack, classifications, fun = "mean", progress = FALSE), 2)
) %>%
sf::st_drop_geometry()
# Convert to a regular data frame for easier downstream processing
all_stats <- sf::st_drop_geometry(pivot_stats_sf)
# Ensure 'class_name' column exists, if not, use 'class' as 'class_name'
if (!("class_name" %in% colnames(all_stats)) && ("class" %in% colnames(all_stats))) {
all_stats$class_name <- all_stats$class
if (length(valid_class_ids) == 0) {
safe_log("No valid (non-NA) class IDs found for exactextractr processing.", "WARNING")
}
for (class_id in valid_class_ids) {
# Subset polygons for this class
class_polygons_sf <- classifications[which(classifications$class == class_id), ] # Use which for NA-safe subsetting
if (nrow(class_polygons_sf) == 0) {
safe_log(paste("Skipping empty class (no polygons after filtering):", class_id), "WARNING")
next
}
tryCatch({
safe_log(paste("Processing class:", class_id))
# Check if the polygon overlaps with the raster extent (check based on the combined extent of class polygons)
rast_extent <- terra::ext(extraction_stack)
poly_extent <- sf::st_bbox(class_polygons_sf)
if (poly_extent["xmin"] > rast_extent["xmax"] ||
poly_extent["xmax"] < rast_extent["xmin"] ||
poly_extent["ymin"] > rast_extent["ymax"] ||
poly_extent["ymax"] < rast_extent["ymin"]) {
safe_log(paste("Skipping class that doesn't overlap with raster:", class_id), "WARNING")
next
}
# exact_extract will process each feature in class_polygons_sf
# and return a list of data frames (one per feature)
per_polygon_stats_list <- exactextractr::exact_extract(
extraction_stack,
class_polygons_sf,
function(values, coverage_fraction) {
# Filter pixels by coverage (e.g., >50% of the pixel is covered by the polygon)
valid_pixels_idx <- coverage_fraction > 0.5
df_filtered <- values[valid_pixels_idx, , drop = FALSE]
if (nrow(df_filtered) == 0) {
# If no pixels meet coverage, return a data frame with NAs
# to maintain structure, matching expected column names.
# Column names are derived from the extraction_stack
stat_cols <- paste0(names(extraction_stack), "_mean")
na_df <- as.data.frame(matrix(NA_real_, nrow = 1, ncol = length(stat_cols)))
names(na_df) <- stat_cols
return(na_df)
}
# Calculate mean for each band (column in df_filtered)
stats_per_band <- lapply(names(df_filtered), function(band_name) {
col_data <- df_filtered[[band_name]]
if (length(col_data) > 0 && sum(!is.na(col_data)) > 0) {
mean_val <- mean(col_data, na.rm = TRUE)
return(setNames(mean_val, paste0(band_name, "_mean")))
} else {
return(setNames(NA_real_, paste0(band_name, "_mean")))
}
})
# Combine all stats (named values) into a single named vector then data frame
return(as.data.frame(t(do.call(c, stats_per_band))))
},
summarize_df = FALSE, # Important: get a list of DFs, one per polygon
force_df = TRUE # Ensure the output of the summary function is treated as a DF
)
# Combine all stats for this class if we have any
if (length(per_polygon_stats_list) > 0) {
# per_polygon_stats_list is now a list of single-row data.frames
class_stats_df <- do.call(rbind, per_polygon_stats_list)
# Remove rows that are all NA (from polygons with no valid pixels)
class_stats_df <- class_stats_df[rowSums(is.na(class_stats_df)) < ncol(class_stats_df), ]
if (nrow(class_stats_df) > 0) {
# Add class information
class_stats_df$class <- class_id
# Get class_name from the first polygon (assuming it's consistent for the class_id)
# Ensure class_polygons_sf is not empty before accessing class_name
if ("class_name" %in% names(class_polygons_sf) && nrow(class_polygons_sf) > 0) {
class_stats_df$class_name <- as.character(class_polygons_sf$class_name[1])
} else {
class_stats_df$class_name <- as.character(class_id) # Fallback
}
# Add to overall results
all_stats <- rbind(all_stats, class_stats_df)
safe_log(paste("Successfully extracted data for", nrow(class_stats_df), "polygons in class", class_id))
} else {
safe_log(paste("No valid data extracted for class (after NA removal):", class_id), "WARNING")
}
} else {
safe_log(paste("No data frames returned by exact_extract for class:", class_id), "WARNING")
}
}, error = function(e) {
safe_log(paste("Error processing class", class_id, "with exact_extract:", e$message), "ERROR")
})
}
# Save the extracted statistics to a CSV file
if (nrow(all_stats) > 0) {
stats_file <- file.path(diagnostic_dir, "class_spectral_stats_mean.csv") # New filename
write.csv(all_stats, stats_file, row.names = FALSE)
safe_log(paste("Saved MEAN spectral statistics by class to:", stats_file))
} else {
safe_log("No statistics were generated to save.", "WARNING")
}
# Calculate optimized thresholds for cloud/shadow detection (using only _mean columns)
if (nrow(all_stats) > 0 && ncol(all_stats) > 2) { # Check if all_stats has data and parameter columns
threshold_results <- data.frame(
parameter = character(),
best_threshold = numeric(),
direction = character(),
target_class = character(),
vs_class = character(),
accuracy = numeric(),
stringsAsFactors = FALSE
)
# Define class pairs to analyze
class_pairs <- list(
# Cloud vs various surfaces
c("cloud", "crop"),
c("cloud", "bare_soil_dry"),
c("cloud", "bare_soil_wet"),
# Shadow vs various surfaces
c("shadow_over_crop", "crop"),
c("shadow_over_bare_soil", "bare_soil_dry"),
c("shadow_over_bare_soil", "bare_soil_wet")
)
# For now, let's assume all _mean parameters derived from extraction_rasters are relevant for clouds/shadows
# This part might need more specific logic if you want to distinguish cloud/shadow params cloud_params <- grep("_mean$", names(extraction_rasters), value = TRUE)
params logic
# Parameters to analyze for shadows (now only _mean versions)tatistics by class to:", stats_file))
shadow_params <- cloud_params # Simplified: using the same set for now, adjust if specific shadow params are needed
lds for cloud/shadow detection
# Find optimal thresholdsframe(
if (length(class_pairs) > 0 && (length(cloud_params) > 0 || length(shadow_params) > 0)) {
for (pair in class_pairs) {c(),
target_class <- pair[1](),
vs_class <- pair[2](),
vs_class = character(),
# Select appropriate parameters based on whether we're analyzing clouds or shadows accuracy = numeric(),
if (grepl("cloud", target_class)) {
params_to_check <- cloud_params
} else {
params_to_check <- shadow_paramsto analyze
}
# For each parameter, find the best threshold to separate the classesc("cloud", "crop"),
for (param in params_to_check) {
if (param %in% colnames(all_stats)) {
# Get values for both classes
target_values <- all_stats[all_stats$class_name == target_class, param]
vs_values <- all_stats[all_stats$class_name == vs_class, param] c("shadow_over_crop", "crop"),
c("shadow_over_bare_soil", "bare_soil_dry"),
if (length(target_values) > 0 && length(vs_values) > 0) {_soil_wet")
# Calculate mean and sd for both classes
target_mean <- mean(target_values, na.rm = TRUE)
target_sd <- sd(target_values, na.rm = TRUE)# Parameters to analyze for clouds
vs_mean <- mean(vs_values, na.rm = TRUE), "blue_ratio_mean", "ndvi_mean",
vs_sd <- sd(vs_values, na.rm = TRUE)
# Determine if higher or lower values indicate the target classshadow_params <- c("brightness_mean", "dark_pixels_mean", "very_dark_pixels_mean",
if (target_mean > vs_mean) {r_mean", "shadow_ndvi_mean", "blue_nir_ratio_raw_mean",
direction <- ">"_ratio_raw_mean", "low_red_to_blue_mean")
# Try different thresholds
potential_thresholds <- seq(olds
min(min(target_values, na.rm = TRUE), vs_mean + 0.5 * vs_sd),r (pair in class_pairs) {
max(max(vs_values, na.rm = TRUE), target_mean - 0.5 * target_sd),
length.out = 20
)
} else { appropriate parameters based on whether we're analyzing clouds or shadows
direction <- "<"{
# Try different thresholds params_to_check <- cloud_params
potential_thresholds <- seq(} else {
min(min(vs_values, na.rm = TRUE), target_mean + 0.5 * target_sd),
max(max(target_values, na.rm = TRUE), vs_mean - 0.5 * vs_sd),
length.out = 20
)st threshold to separate the classes
}
# Calculate accuracy for each threshold# Get values for both classes
best_accuracy <- 0_class, param]
best_threshold <- ifelse(direction == ">", min(potential_thresholds), max(potential_thresholds))e == vs_class, param]
for (threshold in potential_thresholds) {ues) > 0) {
if (direction == ">") {
correct_target <- sum(target_values > threshold, na.rm = TRUE)a.rm = TRUE)
correct_vs <- sum(vs_values <= threshold, na.rm = TRUE)target_sd <- sd(target_values, na.rm = TRUE)
} else { vs_mean <- mean(vs_values, na.rm = TRUE)
correct_target <- sum(target_values < threshold, na.rm = TRUE)
correct_vs <- sum(vs_values >= threshold, na.rm = TRUE)
}
er values indicate the target class
total_target <- length(target_values)
total_vs <- length(vs_values)
accuracy <- (correct_target + correct_vs) / (total_target + total_vs)lds <- seq(
min(min(target_values, na.rm = TRUE), vs_mean + 0.5 * vs_sd),
if (accuracy > best_accuracy) {max(vs_values, na.rm = TRUE), target_mean - 0.5 * target_sd),
best_accuracy <- accuracy0
best_threshold <- threshold
}
}
# Add to resultslds <- seq(
threshold_results <- rbind(threshold_results, data.frame( min(min(vs_values, na.rm = TRUE), target_mean + 0.5 * target_sd),
parameter = gsub("_mean", "", param), max(max(target_values, na.rm = TRUE), vs_mean - 0.5 * vs_sd),
best_threshold = best_threshold, length.out = 20
direction = direction,
target_class = target_class,
vs_class = vs_class,
accuracy = best_accuracy,# Calculate accuracy for each threshold
stringsAsFactors = FALSE
))direction == ">", min(potential_thresholds), max(potential_thresholds))
}
}
}ction == ">") {
}
}
else {
# Save threshold results correct_target <- sum(target_values < threshold, na.rm = TRUE)
thresholds_file <- file.path(diagnostic_dir, "optimal_thresholds.csv")shold, na.rm = TRUE)
write.csv(threshold_results, thresholds_file, row.names = FALSE)
safe_log(paste("Saved optimal threshold recommendations to:", thresholds_file))
# Generate box plots for key parameters to visualize class differencestotal_vs <- length(vs_values)
if (requireNamespace("ggplot2", quietly = TRUE) && nrow(all_stats) > 0) {
# Reshape data for plotting (only _mean columns) + correct_vs) / (total_target + total_vs)
mean_cols <- grep("_mean$", colnames(all_stats), value = TRUE)
if (length(mean_cols) > 0) {f (accuracy > best_accuracy) {
plot_data <- reshape2::melt(all_stats, best_accuracy <- accuracy
id.vars = c("class", "class_name"), best_threshold <- threshold
measure.vars = mean_cols, # Use only _mean columns
variable.name = "parameter",
value.name = "value")
# Create directory for plotsnd(threshold_results, data.frame(
plots_dir <- file.path(diagnostic_dir, "class_plots"), param),
dir.create(plots_dir, showWarnings = FALSE, recursive = TRUE)t_threshold,
# Create plots for selected key parameters (ensure they are _mean versions)ass,
# Adjust key_params to reflect the new column names (e.g., "brightness_mean")vs_class = vs_class,
key_params_plot <- intersect(c( accuracy = best_accuracy,
"brightness_mean", "ndvi_mean", "blue_ratio_mean", "ndwi_mean", stringsAsFactors = FALSE
"blue_nir_ratio_raw_mean", "red_blue_ratio_raw_mean" ))
), mean_cols) # Ensure these params exist }
}
for (param in key_params_plot) {
# param_data <- plot_data[plot_data$parameter == param,] # Exact match for parameter
# No, grepl was fine if plot_data only contains _mean parameters now.
# Let's ensure plot_data only has the _mean parameters for simplicity here.
param_data <- plot_data[plot_data$parameter == param, ]thresholds_file <- file.path(diagnostic_dir, "optimal_thresholds.csv")
if (nrow(param_data) > 0) {file))
param_name <- gsub("_mean", "", param)
o visualize class differences
p <- ggplot2::ggplot(param_data, ggplot2::aes(x = class_name, y = value, fill = class_name)) +s) > 0) {
ggplot2::geom_boxplot() +
ggplot2::theme_minimal() +
ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) + id.vars = c("class", "class_name"),
ggplot2::labs(ariable.name = "parameter",
title = paste("Distribution of", param_name, "by Land Cover Class"),
x = "Class",
y = param_name,# Create directory for plots
fill = "Class"ass_plots")
)_dir, showWarnings = FALSE, recursive = TRUE)
# Save the plot
plot_file <- file.path(plots_dir, paste0("boxplot_", param_name, ".png"))ey_params <- c(
ggplot2::ggsave(plot_file, p, width = 10, height = 6, dpi = 150) "brightness_mean", "ndvi_mean", "blue_ratio_mean", "ndwi_mean",
}, "red_blue_ratio_raw_mean"
}
# Create a summary plot showing multiple parameters
summary_data <- plot_data[plot_data$parameter %in% ram_data <- plot_data[grepl(param, plot_data$parameter),]
c("brightness_mean", "ndvi_mean",
"blue_nir_ratio_raw_mean", "red_blue_ratio_raw_mean"),] "", param)
if (nrow(summary_data) > 0) {= class_name)) +
# Clean up parameter names for displayboxplot() +
summary_data$parameter <- gsub("_mean$", "", summary_data$parameter) # Remove _mean suffix for display
summary_data$parameter <- gsub("_raw$", "", summary_data$parameter) # Keep this if _raw_mean was a thing(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) +
# Create faceted plot"Distribution of", param_name, "by Land Cover Class"),
p <- ggplot2::ggplot(summary_data, x = "Class",
ggplot2::aes(x = class_name, y = value, fill = class_name)) + y = param_name,
ggplot2::geom_boxplot() +ss"
ggplot2::facet_wrap(~parameter, scales = "free_y") +
ggplot2::theme_minimal() +
ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) + # Save the plot
ggplot2::labs( plot_file <- file.path(plots_dir, paste0("boxplot_", param_name, ".png"))
title = "Key Spectral Parameters by Land Cover Class", ggplot2::ggsave(plot_file, p, width = 10, height = 6, dpi = 150)
x = "Class",
y = "Value",
fill = "Class"
)
summary_data <- plot_data[plot_data$parameter %in%
# Save the summary plot"brightness_mean", "ndvi_mean",
summary_file <- file.path(plots_dir, "spectral_parameters_summary.png")atio_raw_mean", "red_blue_ratio_raw_mean"),]
ggplot2::ggsave(summary_file, p, width = 12, height = 8, dpi = 150)
}
# Clean up parameter names for display
safe_log(paste("Generated spectral parameter plots in:", plots_dir))r <- gsub("_mean", "", summary_data$parameter)
}w", "", summary_data$parameter)
} else {
safe_log("Package 'exactextractr' not available. Install it for more accurate polygon extraction.", "WARNING")
# Fall back to simple extraction using terra (calculating only mean)::aes(x = class_name, y = value, fill = class_name)) +
class_stats <- data.frame()
_wrap(~parameter, scales = "free_y") +
valid_class_names_fallback <- unique(classifications$class_name)
valid_class_names_fallback <- valid_class_names_fallback[!is.na(valid_class_names_fallback)](axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) +
if (length(valid_class_names_fallback) == 0) {pectral Parameters by Land Cover Class",
safe_log("No valid (non-NA) class names found for fallback terra::extract processing.", "WARNING") x = "Class",
} y = "Value",
for (class_name_fb in valid_class_names_fallback) {
class_polygons_fb <- classifications[which(classifications$class_name == class_name_fb), ]
# Save the summary plot
if(nrow(class_polygons_fb) == 0) next summary_file <- file.path(plots_dir, "spectral_parameters_summary.png")
)
class_vect_fb <- terra::vect(class_polygons_fb) }
# Extract values for each raster
for (i in seq_along(extraction_rasters)) {}
raster_name <- names(extraction_rasters)[i]
# terra::extract returns a data.frame with ID and layer valuesractr' not available. Install it for more accurate polygon extraction.", "WARNING")
# For multiple polygons, it will have multiple rows per polygon if ID is not unique
# We need to aggregate per polygon, then per class if not already handled by exact_extract style
# However, for simplicity here, let's assume terra::extract gives one value per polygon for the mean
# This part of fallback might need more robust aggregation if polygons are complex
r (class_name in unique(classifications$class_name)) {
# A more robust terra::extract approach for means per polygon:s[classifications$class_name == class_name, ]
extracted_values_list <- terra::extract(extraction_rasters[[i]], class_vect_fb, fun = mean, na.rm = TRUE, ID = FALSE)
# extracted_values_list will be a data.frame with one column (the layer) and rows corresponding to polygons
if (nrow(extracted_values_list) > 0 && ncol(extracted_values_list) > 0) {r (i in seq_along(extraction_rasters)) {
# Average over all polygons in this class for this rastertraction_rasters)[i]
mean_val_for_class <- mean(extracted_values_list[[1]], na.rm = TRUE)ct(extraction_rasters[[i]], class_vect)
if (!is.na(mean_val_for_class)) {
stats_row <- data.frame(
class_name = class_name_fb, # Using class_name as the identifier here
parameter = paste0(raster_name, "_mean"),
value = mean_val_for_class),
) TRUE),
class_stats <- rbind(class_stats, stats_row) sd = sd(values[,2], na.rm = TRUE),
} min = min(values[,2], na.rm = TRUE),
}
} )
}
class_stats <- rbind(class_stats, stats)
# Save the statistics (if any were generated) }
if(nrow(class_stats) > 0) {
# Reshape class_stats from long to wide for consistency if needed, or save as is.
# For now, save as long format.
stats_file <- file.path(diagnostic_dir, "class_spectral_stats_simple_mean_long.csv")
write.csv(class_stats, stats_file, row.names = FALSE) stats_file <- file.path(diagnostic_dir, "class_spectral_stats_simple.csv")
safe_log(paste("Saved simple MEAN (long format) spectral statistics by class to:", stats_file)) write.csv(class_stats, stats_file, row.names = FALSE)
} else {e spectral statistics by class to:", stats_file))
safe_log("No statistics generated by fallback method.", "WARNING")
}
}ve RMarkdown generation
# Remove RMarkdown generation
# safe_log("RMarkdown report generation has been removed as per user request.")
NING")
} else {}
safe_log("No classification polygons file (classes.geojson) found. Skipping spectral analysis.", "WARNING")}, error = function(e) {
}cessing or spectral analysis:", e$message), "ERROR")
}, error = function(e) {})
safe_log(paste("Error in classification polygon processing or spectral analysis:", e$message), "ERROR")
}) detection analysis script finished.")
safe_log("Cloud detection analysis script finished.")# Clean up workspace
rm(list = ls())
# Clean up workspace
rm(list = ls())

View file

@ -0,0 +1,191 @@
```r
# Cloud detection analysis script
# Load necessary libraries
library(terra)
library(exactextractr)
library(sf)
library(dplyr)
library(ggplot2)
library(tidyr)
library(reshape2)
# Define file paths (these should be set to your actual file locations)
classes_file <- "path/to/classes.geojson"
rasters_dir <- "path/to/rasters"
diagnostic_dir <- "path/to/diagnostics"
# Helper function for logging
safe_log <- function(message, level = "INFO") {
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%S")
cat(paste0("[", timestamp, "] [", level, "] ", message, "\n"))
}
# Main processing block
# Load classification polygons
safe_log(paste("Loading classification polygons from:", classes_file))
classifications <- sf::st_read(classes_file, quiet = TRUE)
# Ensure the CRS is set (assuming WGS84 here, adjust if necessary)
safe_log("No CRS found for the classifications. Setting to WGS84 (EPSG:4326).", "WARNING")
sf::st_crs(classifications) <- 4326
# List all raster files in the directory
raster_files <- list.files(rasters_dir, pattern = "\\.tif$", full.names = TRUE)
# Create a named vector for extraction_rasters based on base names
extraction_rasters <- setNames(raster_files, tools::file_path_sans_ext(basename(raster_files)))
# Create a stack of all rasters
extraction_stack <- terra::rast(extraction_rasters)
# User-provided simplified extraction for mean statistics per polygon
safe_log("Extracting mean statistics per polygon using exactextractr...")
all_stats <- cbind(
classifications,
round(exactextractr::exact_extract(extraction_stack, classifications, fun = "mean", progress = FALSE), 2)
) %>%
sf::st_drop_geometry() # Ensures all_stats is a data frame
# Ensure 'class_name' column exists, if not, use 'class' as 'class_name'
all_stats$class_name <- all_stats$class
# Save the extracted statistics to a CSV file
stats_file <- file.path(diagnostic_dir, "polygon_mean_spectral_stats.csv")
write.csv(all_stats, stats_file, row.names = FALSE)
safe_log(paste("Saved mean spectral statistics per polygon to:", stats_file))
# Calculate optimized thresholds for cloud/shadow detection
threshold_results <- data.frame(
parameter = character(),
best_threshold = numeric(),
direction = character(),
target_class = character(),
vs_class = character(),
accuracy = numeric(),
stringsAsFactors = FALSE
)
class_pairs <- list(
c("cloud", "crop"),
c("cloud", "bare_soil_dry"),
c("cloud", "bare_soil_wet"),
c("shadow_over_crop", "crop"),
c("shadow_over_bare_soil", "bare_soil_dry"),
c("shadow_over_bare_soil", "bare_soil_wet")
)
cloud_detection_params_for_threshold <- intersect(
c("mean.brightness", "mean.very_bright_pixels", "mean.blue_dominant", "mean.low_ndvi", "mean.green_dominant_nir", "mean.high_ndwi", "mean.blue_ratio", "mean.ndvi"),
colnames(all_stats)
)
shadow_detection_params_for_threshold <- intersect(
c("mean.brightness", "mean.dark_pixels", "mean.very_dark_pixels", "mean.low_nir", "mean.shadow_ndvi", "mean.low_red_to_blue", "mean.high_blue_to_nir_ratio", "mean.blue_nir_ratio_raw", "mean.red_blue_ratio_raw"),
colnames(all_stats)
)
for (pair in class_pairs) {
target_class <- pair[1]
vs_class <- pair[2]
params_to_check <- c(cloud_detection_params_for_threshold, shadow_detection_params_for_threshold)
for (param in params_to_check) {
target_values <- all_stats[all_stats$class_name == target_class, param]
vs_values <- all_stats[all_stats$class_name == vs_class, param]
target_values <- target_values[!is.na(target_values)]
vs_values <- vs_values[!is.na(vs_values)]
# Only proceed if both groups have at least one value
if (length(target_values) > 0 && length(vs_values) > 0) {
target_mean <- mean(target_values)
target_sd <- sd(target_values)
vs_mean <- mean(vs_values)
vs_sd <- sd(vs_values)
target_sd[is.na(target_sd)] <- 0
vs_sd[is.na(vs_sd)] <- 0
direction <- ifelse(target_mean > vs_mean, ">", "<")
all_values <- c(target_values, vs_values)
min_val <- min(all_values)
max_val <- max(all_values)
# Only proceed if min and max are finite and not equal
if (is.finite(min_val) && is.finite(max_val) && min_val != max_val) {
potential_thresholds <- seq(min_val, max_val, length.out = 20)
best_accuracy <- -1
best_threshold <- ifelse(direction == ">", min(potential_thresholds), max(potential_thresholds))
for (threshold in potential_thresholds) {
if (direction == ">") {
correct_target <- sum(target_values > threshold)
correct_vs <- sum(vs_values <= threshold)
} else {
correct_target <- sum(target_values < threshold)
correct_vs <- sum(vs_values >= threshold)
}
accuracy <- (correct_target + correct_vs) / (length(target_values) + length(vs_values))
if (accuracy > best_accuracy) {
best_accuracy <- accuracy
best_threshold <- threshold
}
}
threshold_results <- rbind(threshold_results, data.frame(
parameter = param,
best_threshold = best_threshold,
direction = direction,
target_class = target_class,
vs_class = vs_class,
accuracy = best_accuracy,
stringsAsFactors = FALSE
))
}
}
}
}
thresholds_file <- file.path(diagnostic_dir, "optimal_thresholds.csv")
write.csv(threshold_results, thresholds_file, row.names = FALSE)
safe_log(paste("Saved optimal threshold recommendations to:", thresholds_file))
# Fix: get plot_measure_cols by matching raster base names to all_stats columns with 'mean.' prefix
plot_measure_cols <- intersect(names(extraction_rasters), gsub('^mean\\.', '', colnames(all_stats)))
plot_data <- reshape2::melt(
all_stats,
id.vars = c("class", "class_name"),
measure.vars = paste0("mean.", plot_measure_cols),
variable.name = "parameter",
value.name = "value"
)
# Remove 'mean.' prefix from parameter column for clarity
plot_data$parameter <- sub("^mean\\.", "", plot_data$parameter)
plots_dir <- file.path(diagnostic_dir, "class_plots")
dir.create(plots_dir, showWarnings = FALSE, recursive = TRUE)
key_params_for_plot_list <- c("brightness", "ndvi", "blue_ratio", "ndwi",
"blue_nir_ratio_raw", "red_blue_ratio_raw")
key_params_to_plot <- intersect(key_params_for_plot_list, plot_measure_cols)
for (param_to_plot in key_params_to_plot) {
param_data_subset <- plot_data[plot_data$parameter == param_to_plot, ]
p <- ggplot2::ggplot(param_data_subset, ggplot2::aes(x = class_name, y = value, fill = class_name)) +
ggplot2::geom_boxplot() +
ggplot2::theme_minimal() +
ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) +
ggplot2::labs(
title = paste("Distribution of", param_to_plot, "by Land Cover Class"),
x = "Class",
y = param_to_plot,
fill = "Class"
)
plot_file <- file.path(plots_dir, paste0("boxplot_", param_to_plot, ".png"))
ggplot2::ggsave(plot_file, p, width = 10, height = 6, dpi = 150)
}
summary_params_for_plot_list <- c("brightness", "ndvi",
"blue_nir_ratio_raw", "red_blue_ratio_raw")
summary_params_to_plot <- intersect(summary_params_for_plot_list, plot_measure_cols)
summary_data_subset <- plot_data[plot_data$parameter %in% summary_params_to_plot,]
p_summary <- ggplot2::ggplot(summary_data_subset, ggplot2::aes(x = class_name, y = value, fill = class_name)) +
ggplot2::geom_boxplot() +
ggplot2::facet_wrap(~parameter, scales = "free_y") +
ggplot2::theme_minimal() +
ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1),
strip.text = ggplot2::element_text(size = 8)) +
ggplot2::labs(
title = "Summary of Key Spectral Parameters by Land Cover Class",
x = "Class",
y = "Value",
fill = "Class"
)
summary_file <- file.path(plots_dir, "spectral_parameters_summary.png")
ggplot2::ggsave(summary_file, p_summary, width = 12, height = 8, dpi = 150)
safe_log(paste("Generated spectral parameter plots in:", plots_dir))
safe_log("Cloud detection analysis script finished.")
```

View file

@ -0,0 +1,718 @@
---
params:
ref: "word-styles-reference-var1.docx"
output_file: CI_report.docx
report_date: "2024-08-28"
data_dir: "Chemba"
mail_day: "Wednesday"
borders: TRUE
use_breaks: FALSE
output:
# html_document:
# toc: yes
# df_print: paged
word_document:
reference_docx: !expr file.path("word-styles-reference-var1.docx")
toc: yes
editor_options:
chunk_output_type: console
---
```{r setup_parameters, include=FALSE}
# Set up basic report parameters from input values
report_date <- params$report_date
mail_day <- params$mail_day
borders <- params$borders
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
# Environment setup notes (commented out)
# # Activeer de renv omgeving
# renv::activate()
# renv::deactivate()
# # Optioneel: Herstel de omgeving als dat nodig is
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
# renv::restore()
```
```{r load_libraries, message=FALSE, warning=FALSE, include=FALSE}
# Configure knitr options
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
# Path management
library(here)
# Spatial data libraries
library(sf)
library(terra)
library(exactextractr)
# library(raster) - Removed as it's no longer maintained
# Data manipulation and visualization
library(tidyverse) # Includes dplyr, ggplot2, etc.
library(tmap)
library(lubridate)
library(zoo)
# Machine learning
library(rsample)
library(caret)
library(randomForest)
library(CAST)
# Load custom utility functions
tryCatch({
source("report_utils.R")
}, error = function(e) {
message(paste("Error loading report_utils.R:", e$message))
# Try alternative path if the first one fails
tryCatch({
source(here::here("r_app", "report_utils.R"))
}, error = function(e) {
stop("Could not load report_utils.R from either location: ", e$message)
})
})
# Load executive report utilities
tryCatch({
source("executive_report_utils.R")
}, error = function(e) {
message(paste("Error loading executive_report_utils.R:", e$message))
# Try alternative path if the first one fails
tryCatch({
source(here::here("r_app", "executive_report_utils.R"))
}, error = function(e) {
stop("Could not load executive_report_utils.R from either location: ", e$message)
})
})
safe_log("Successfully loaded utility functions")
```
```{r initialize_project_config, message=FALSE, warning=FALSE, include=FALSE}
# Set the project directory from parameters
project_dir <- params$data_dir
# Source project parameters with error handling
tryCatch({
source(here::here("r_app", "parameters_project.R"))
}, error = function(e) {
stop("Error loading parameters_project.R: ", e$message)
})
# Log initial configuration
safe_log("Starting the R Markdown script")
safe_log(paste("mail_day params:", params$mail_day))
safe_log(paste("report_date params:", params$report_date))
safe_log(paste("mail_day variable:", mail_day))
```
```{r calculate_dates_and_weeks, message=FALSE, warning=FALSE, include=FALSE}
# Set locale for consistent date formatting
Sys.setlocale("LC_TIME", "C")
# Initialize date variables from parameters
today <- as.character(report_date)
mail_day_as_character <- as.character(mail_day)
# Calculate week days
report_date_as_week_day <- weekdays(lubridate::ymd(today))
days_of_week <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
# Calculate initial week number
week <- lubridate::week(today)
safe_log(paste("Initial week calculation:", week, "today:", today))
# Calculate previous dates for comparisons
today_minus_1 <- as.character(lubridate::ymd(today) - 7)
today_minus_2 <- as.character(lubridate::ymd(today) - 14)
today_minus_3 <- as.character(lubridate::ymd(today) - 21)
# Log the weekday calculations for debugging
safe_log(paste("Report date weekday:", report_date_as_week_day))
safe_log(paste("Weekday index:", which(days_of_week == report_date_as_week_day)))
safe_log(paste("Mail day:", mail_day_as_character))
safe_log(paste("Mail day index:", which(days_of_week == mail_day_as_character)))
# Adjust week calculation based on mail day
if (which(days_of_week == report_date_as_week_day) > which(days_of_week == mail_day_as_character)) {
safe_log("Adjusting weeks because of mail day")
week <- lubridate::week(today) + 1
today_minus_1 <- as.character(lubridate::ymd(today))
today_minus_2 <- as.character(lubridate::ymd(today) - 7)
today_minus_3 <- as.character(lubridate::ymd(today) - 14)
}
# Generate subtitle for report
subtitle_var <- paste("Report generated on", Sys.Date())
# Calculate week numbers for previous weeks
week_minus_1 <- week - 1
week_minus_2 <- week - 2
week_minus_3 <- week - 3
# Format current week with leading zeros
week <- sprintf("%02d", week)
# Get years for each date
year <- lubridate::year(today)
year_1 <- lubridate::year(today_minus_1)
year_2 <- lubridate::year(today_minus_2)
year_3 <- lubridate::year(today_minus_3)
```
```{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
tryCatch({
# Calculate weekly difference
last_week_dif_raster_abs <- (CI - CI_m1)
safe_log("Calculated weekly difference raster")
# Calculate three-week difference
three_week_dif_raster_abs <- (CI - CI_m3)
safe_log("Calculated three-week difference raster")
}, error = function(e) {
safe_log(paste("Error calculating difference rasters:", e$message), "ERROR")
# Create placeholder rasters if calculations fail
if (!exists("last_week_dif_raster_abs")) {
last_week_dif_raster_abs <- CI * 0
}
if (!exists("three_week_dif_raster_abs")) {
three_week_dif_raster_abs <- CI * 0
}
})
```
```{r load_field_boundaries, message=TRUE, warning=TRUE, include=FALSE}
# Load field boundaries from parameters
tryCatch({
AllPivots0 <- field_boundaries_sf
safe_log("Successfully loaded field boundaries")
}, error = function(e) {
stop("Error loading field boundaries: ", e$message)
})
```
```{r create_farm_health_data, message=FALSE, warning=FALSE, include=FALSE}
# Create farm health summary data from scratch
tryCatch({
# Ensure we have the required data
if (!exists("AllPivots0") || !exists("CI") || !exists("CI_m1") || !exists("harvesting_data")) {
stop("Required input data (field boundaries, CI data, or harvesting data) not available")
}
safe_log("Starting to calculate farm health data")
# Get unique field names
fields <- unique(AllPivots0$field)
safe_log(paste("Found", length(fields), "unique fields"))
# Initialize result dataframe
farm_health_data <- data.frame(
field = character(),
mean_ci = numeric(),
ci_change = numeric(),
ci_uniformity = numeric(),
status = character(),
anomaly_type = character(),
priority_level = numeric(),
age_weeks = numeric(),
harvest_readiness = character(),
stringsAsFactors = FALSE
)
# Process each field with robust error handling
for (field_name in fields) {
tryCatch({
safe_log(paste("Processing field:", field_name))
# Get field boundary
field_shape <- AllPivots0 %>% dplyr::filter(field == field_name)
# Skip if field shape is empty
if (nrow(field_shape) == 0) {
safe_log(paste("Empty field shape for", field_name), "WARNING")
next
}
# Get field age from harvesting data - use direct filtering to avoid dplyr errors
field_age_data <- NULL
if (exists("harvesting_data") && !is.null(harvesting_data) && nrow(harvesting_data) > 0) {
field_age_data <- harvesting_data[harvesting_data$field == field_name, ]
if (nrow(field_age_data) > 0) {
field_age_data <- field_age_data[order(field_age_data$season_start, decreasing = TRUE), ][1, ]
}
}
# Default age if not available
field_age_weeks <- if (!is.null(field_age_data) && nrow(field_age_data) > 0 && !is.na(field_age_data$age)) {
field_age_data$age
} else {
10 # Default age
}
# Extract CI values using terra's extract function which is more robust
ci_values <- terra::extract(CI, field_shape)
ci_prev_values <- terra::extract(CI_m1, field_shape)
# Check if we got valid data
if (nrow(ci_values) == 0 || nrow(ci_prev_values) == 0) {
safe_log(paste("No CI data extracted for field", field_name), "WARNING")
# Add a placeholder row with Unknown status
farm_health_data <- rbind(farm_health_data, data.frame(
field = field_name,
mean_ci = NA,
ci_change = NA,
ci_uniformity = NA,
status = "Unknown",
anomaly_type = "Unknown",
priority_level = 5, # Low priority
age_weeks = field_age_weeks,
harvest_readiness = "Unknown",
stringsAsFactors = FALSE
))
next
}
# Calculate metrics - Handle NA values properly
ci_column <- if ("CI" %in% names(ci_values)) "CI" else colnames(ci_values)[1]
ci_prev_column <- if ("CI" %in% names(ci_prev_values)) "CI" else colnames(ci_prev_values)[1]
mean_ci <- mean(ci_values[[ci_column]], na.rm=TRUE)
mean_ci_prev <- mean(ci_prev_values[[ci_prev_column]], na.rm=TRUE)
ci_change <- mean_ci - mean_ci_prev
ci_sd <- sd(ci_values[[ci_column]], na.rm=TRUE)
ci_uniformity <- ci_sd / max(0.1, mean_ci) # Avoid division by zero
# Handle NaN or Inf results
if (is.na(mean_ci) || is.na(ci_change) || is.na(ci_uniformity) ||
is.nan(mean_ci) || is.nan(ci_change) || is.nan(ci_uniformity) ||
is.infinite(mean_ci) || is.infinite(ci_change) || is.infinite(ci_uniformity)) {
safe_log(paste("Invalid calculation results for field", field_name), "WARNING")
# Add a placeholder row with Unknown status
farm_health_data <- rbind(farm_health_data, data.frame(
field = field_name,
mean_ci = NA,
ci_change = NA,
ci_uniformity = NA,
status = "Unknown",
anomaly_type = "Unknown",
priority_level = 5, # Low priority
age_weeks = field_age_weeks,
harvest_readiness = "Unknown",
stringsAsFactors = FALSE
))
next
}
# Determine field status
status <- dplyr::case_when(
mean_ci >= 5 ~ "Excellent",
mean_ci >= 3.5 ~ "Good",
mean_ci >= 2 ~ "Fair",
mean_ci >= 1 ~ "Poor",
TRUE ~ "Critical"
)
# Determine anomaly type
anomaly_type <- dplyr::case_when(
ci_change > 2 ~ "Potential Weed Growth",
ci_change < -2 ~ "Potential Weeding/Harvesting",
ci_uniformity > 0.5 ~ "High Variability",
mean_ci < 1 ~ "Low Vigor",
TRUE ~ "None"
)
# Calculate priority level (1-5, with 1 being highest priority)
priority_score <- dplyr::case_when(
mean_ci < 1 ~ 1, # Critical - highest priority
anomaly_type == "Potential Weed Growth" ~ 2,
anomaly_type == "High Variability" ~ 3,
ci_change < -1 ~ 4,
TRUE ~ 5 # No urgent issues
)
# Determine harvest readiness
harvest_readiness <- dplyr::case_when(
field_age_weeks >= 52 & mean_ci >= 4 ~ "Ready for harvest",
field_age_weeks >= 48 & mean_ci >= 3.5 ~ "Approaching harvest",
field_age_weeks >= 40 & mean_ci >= 3 ~ "Mid-maturity",
field_age_weeks >= 12 ~ "Growing",
TRUE ~ "Early stage"
)
# Add to summary data
farm_health_data <- rbind(farm_health_data, data.frame(
field = field_name,
mean_ci = round(mean_ci, 2),
ci_change = round(ci_change, 2),
ci_uniformity = round(ci_uniformity, 2),
status = status,
anomaly_type = anomaly_type,
priority_level = priority_score,
age_weeks = field_age_weeks,
harvest_readiness = harvest_readiness,
stringsAsFactors = FALSE
))
}, error = function(e) {
safe_log(paste("Error processing field", field_name, ":", e$message), "ERROR")
# Add a placeholder row with Error status
farm_health_data <<- rbind(farm_health_data, data.frame(
field = field_name,
mean_ci = NA,
ci_change = NA,
ci_uniformity = NA,
status = "Unknown",
anomaly_type = "Unknown",
priority_level = 5, # Low priority since we don't know the status
age_weeks = NA,
harvest_readiness = "Unknown",
stringsAsFactors = FALSE
))
})
}
# Make sure we have data for all fields
if (nrow(farm_health_data) == 0) {
safe_log("No farm health data was created", "ERROR")
stop("Failed to create farm health data")
}
# Sort by priority level
farm_health_data <- farm_health_data %>% dplyr::arrange(priority_level, field)
safe_log(paste("Successfully created farm health data for", nrow(farm_health_data), "fields"))
}, error = function(e) {
safe_log(paste("Error creating farm health data:", e$message), "ERROR")
# Create an empty dataframe that can be filled by the verification chunk
})
```
```{r verify_farm_health_data, message=FALSE, warning=FALSE, include=FALSE}
# Verify farm_health_data exists and has content
if (!exists("farm_health_data") || nrow(farm_health_data) == 0) {
safe_log("farm_health_data not found or empty, generating default data", "WARNING")
# Create minimal fallback data
tryCatch({
# Get fields from boundaries
fields <- unique(AllPivots0$field)
# Create basic data frame with just field names
farm_health_data <- data.frame(
field = fields,
mean_ci = rep(NA, length(fields)),
ci_change = rep(NA, length(fields)),
ci_uniformity = rep(NA, length(fields)),
status = rep("Unknown", length(fields)),
anomaly_type = rep("Unknown", length(fields)),
priority_level = rep(5, length(fields)), # Low priority
age_weeks = rep(NA, length(fields)),
harvest_readiness = rep("Unknown", length(fields)),
stringsAsFactors = FALSE
)
safe_log("Created fallback farm_health_data with basic field information")
}, error = function(e) {
safe_log(paste("Error creating fallback farm_health_data:", e$message), "ERROR")
farm_health_data <<- data.frame(
field = character(),
mean_ci = numeric(),
ci_change = numeric(),
ci_uniformity = numeric(),
status = character(),
anomaly_type = character(),
priority_level = numeric(),
age_weeks = numeric(),
harvest_readiness = character(),
stringsAsFactors = FALSE
)
})
}
```
```{r calculate_farm_health, message=FALSE, warning=FALSE, include=FALSE}
# Calculate farm health summary metrics
tryCatch({
# Generate farm health summary data
farm_health_data <- generate_farm_health_summary(
field_boundaries = AllPivots0,
ci_current = CI,
ci_previous = CI_m1,
harvesting_data = harvesting_data
)
# Log the summary data
safe_log(paste("Generated farm health summary with", nrow(farm_health_data), "fields"))
}, error = function(e) {
safe_log(paste("Error in farm health calculation:", e$message), "ERROR")
# Create empty dataframe if calculation failed
farm_health_data <- data.frame(
field = character(),
mean_ci = numeric(),
ci_change = numeric(),
ci_uniformity = numeric(),
status = character(),
anomaly_type = character(),
priority_level = numeric(),
age_weeks = numeric(),
harvest_readiness = character(),
stringsAsFactors = FALSE
)
})
```
```{r advanced_analytics_functions, message=FALSE, warning=FALSE, include=FALSE}
# ADVANCED ANALYTICS FUNCTIONS
# Note: These functions are now imported from executive_report_utils.R
# The utility file contains functions for velocity/acceleration indicators,
# anomaly timeline creation, age cohort mapping, and cohort performance charts
safe_log("Using analytics functions from executive_report_utils.R")
```
\pagebreak
# Advanced Analytics
## Field Health Velocity and Acceleration
This visualization shows the rate of change in field health (velocity) and whether that change is speeding up or slowing down (acceleration). These metrics help identify if farm conditions are improving, stable, or deteriorating.
**How to interpret:**
- **Velocity gauge:** Shows the average weekly change in CI values across all fields
- Positive values (green/right side): Farm health improving week-to-week
- Negative values (red/left side): Farm health declining week-to-week
- **Acceleration gauge:** Shows whether the rate of change is increasing or decreasing
- Positive values (green/right side): Change is accelerating or improving faster
- Negative values (red/left side): Change is decelerating or slowing down
- **4-Week Trend:** Shows the overall CI value trajectory for the past month
```{r render_velocity_acceleration, echo=FALSE, fig.height=8, fig.width=10, message=FALSE, warning=FALSE}
# Render the velocity and acceleration indicators
tryCatch({
# Create and display the indicators using the imported utility function
velocity_plot <- create_velocity_acceleration_indicator(
health_data = farm_health_data,
ci_current = CI,
ci_prev1 = CI_m1,
ci_prev2 = CI_m2,
ci_prev3 = CI_m3,
field_boundaries = AllPivots0
)
# Print the visualization
print(velocity_plot)
# Create a table of fields with significant velocity changes
field_ci_metrics <- list()
# Process each field to get metrics
fields <- unique(AllPivots0$field)
for (field_name in fields) {
tryCatch({
# Get field boundary
field_shape <- AllPivots0 %>% dplyr::filter(field == field_name)
if (nrow(field_shape) == 0) next
# Extract CI values
ci_curr_values <- terra::extract(CI, field_shape)
ci_prev1_values <- terra::extract(CI_m1, field_shape)
# Calculate metrics
mean_ci_curr <- mean(ci_curr_values$CI, na.rm = TRUE)
mean_ci_prev1 <- mean(ci_prev1_values$CI, na.rm = TRUE)
velocity <- mean_ci_curr - mean_ci_prev1
# Store in list
field_ci_metrics[[field_name]] <- list(
field = field_name,
ci_current = mean_ci_curr,
ci_prev1 = mean_ci_prev1,
velocity = velocity
)
}, error = function(e) {
safe_log(paste("Error processing field", field_name, "for velocity table:", e$message), "WARNING")
})
}
# Convert list to data frame
velocity_df <- do.call(rbind, lapply(field_ci_metrics, function(x) {
data.frame(
field = x$field,
ci_current = round(x$ci_current, 2),
ci_prev1 = round(x$ci_prev1, 2),
velocity = round(x$velocity, 2),
direction = ifelse(x$velocity >= 0, "Improving", "Declining")
)
}))
# Select top 5 positive and top 5 negative velocity fields
top_positive <- velocity_df %>%
dplyr::filter(velocity > 0) %>%
dplyr::arrange(desc(velocity)) %>%
dplyr::slice_head(n = 5)
top_negative <- velocity_df %>%
dplyr::filter(velocity < 0) %>%
dplyr::arrange(velocity) %>%
dplyr::slice_head(n = 5)
# Display the tables if we have data
if (nrow(top_positive) > 0) {
cat("<h4>Fields with Fastest Improvement</h4>")
knitr::kable(top_positive %>%
dplyr::select(Field = field,
`Current CI` = ci_current,
`Previous CI` = ci_prev1,
`Weekly Change` = velocity))
}
if (nrow(top_negative) > 0) {
cat("<h4>Fields with Fastest Decline</h4>")
knitr::kable(top_negative %>%
dplyr::select(Field = field,
`Current CI` = ci_current,
`Previous CI` = ci_prev1,
`Weekly Change` = velocity))
}
}, error = function(e) {
safe_log(paste("Error rendering velocity visualization:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating velocity visualization.</div>")
})
```
\pagebreak
## Field Anomaly Timeline
This visualization shows the history of detected anomalies in fields across the monitoring period. It helps identify persistent issues or improvements over time.
**How to interpret:**
- **X-axis**: Dates of satellite observations
- **Y-axis**: Fields grouped by similar characteristics
- **Colors**: Red indicates negative anomalies, green indicates positive anomalies
- **Size**: Larger markers indicate stronger anomalies
```{r anomaly_timeline, echo=FALSE, fig.height=8, fig.width=10, message=FALSE, warning=FALSE}
# Generate anomaly timeline visualization
tryCatch({
# Use the imported function to create the anomaly timeline
anomaly_timeline <- create_anomaly_timeline(
field_boundaries = AllPivots0,
ci_data = CI_quadrant,
days_to_include = 90 # Show last 90 days of data
)
# Display the timeline
print(anomaly_timeline)
}, error = function(e) {
safe_log(paste("Error generating anomaly timeline:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating anomaly timeline visualization.</div>")
})
```
\pagebreak
## Field Age Cohorts Map
This map shows fields grouped by their crop age (weeks since planting). Understanding the distribution of crop ages helps interpret performance metrics and plan harvest scheduling.
**How to interpret:**
- **Colors**: Different colors represent different age groups (in weeks since planting)
- **Labels**: Each field is labeled with its name for easy reference
- **Legend**: Shows the age ranges in weeks and their corresponding colors
```{r age_cohort_map, echo=FALSE, fig.height=8, fig.width=10, message=FALSE, warning=FALSE}
# Generate age cohort map
tryCatch({
# Use the imported function to create the age cohort map
age_cohort_map <- create_age_cohort_map(
field_boundaries = AllPivots0,
harvesting_data = harvesting_data
)
# Display the map
print(age_cohort_map)
}, error = function(e) {
safe_log(paste("Error generating age cohort map:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating age cohort map visualization.</div>")
})
```
\pagebreak
## Cohort Performance Comparison
This visualization compares chlorophyll index (CI) performance across different age groups of fields. This helps identify if certain age groups are performing better or worse than expected.
**How to interpret:**
- **X-axis**: Field age groups in weeks since planting
- **Y-axis**: Average CI value for fields in that age group
- **Box plots**: Show the distribution of CI values within each age group
- **Line**: Shows the expected CI trajectory based on historical data
```{r cohort_performance_chart, echo=FALSE, fig.height=8, fig.width=10, message=FALSE, warning=FALSE}
# Generate cohort performance comparison chart
tryCatch({
# Use the imported function to create the cohort performance chart
cohort_chart <- create_cohort_performance_chart(
field_boundaries = AllPivots0,
ci_current = CI,
harvesting_data = harvesting_data
)
# Display the chart
print(cohort_chart)
}, error = function(e) {
safe_log(paste("Error generating cohort performance chart:", e$message), "ERROR")
cat("<div class='alert alert-danger'>Error generating cohort performance visualization.</div>")
})
```

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,437 @@
# EXECUTIVE REPORT UTILITIES
# This file contains functions for creating advanced visualizations for the executive summary report
#' Create a velocity and acceleration indicator for CI change
#'
#' @param health_data Current farm health data
#' @param ci_current Current CI raster
#' @param ci_prev1 CI raster from 1 week ago
#' @param ci_prev2 CI raster from 2 weeks ago
#' @param ci_prev3 CI raster from 3 weeks ago
#' @param field_boundaries Field boundaries spatial data (sf object)
#' @return A ggplot2 object with velocity and acceleration gauges
#'
create_velocity_acceleration_indicator <- function(health_data, ci_current, ci_prev1, ci_prev2, ci_prev3, field_boundaries) {
tryCatch({
# Calculate farm-wide metrics for multiple weeks
mean_ci_current <- mean(health_data$mean_ci, na.rm = TRUE)
# Calculate previous week metrics
# Extract CI values for previous weeks
field_ci_metrics <- data.frame(field = character(),
week_current = numeric(),
week_minus_1 = numeric(),
week_minus_2 = numeric(),
week_minus_3 = numeric(),
stringsAsFactors = FALSE)
# Process each field
fields <- unique(field_boundaries$field)
for (field_name in fields) {
tryCatch({
# Get field boundary
field_shape <- field_boundaries %>% dplyr::filter(field == field_name)
if (nrow(field_shape) == 0) next
# Extract CI values for all weeks
ci_curr_values <- terra::extract(ci_current, field_shape)
ci_prev1_values <- terra::extract(ci_prev1, field_shape)
ci_prev2_values <- terra::extract(ci_prev2, field_shape)
ci_prev3_values <- terra::extract(ci_prev3, field_shape)
# Calculate mean CI for each week
mean_ci_curr <- mean(ci_curr_values$CI, na.rm = TRUE)
mean_ci_prev1 <- mean(ci_prev1_values$CI, na.rm = TRUE)
mean_ci_prev2 <- mean(ci_prev2_values$CI, na.rm = TRUE)
mean_ci_prev3 <- mean(ci_prev3_values$CI, na.rm = TRUE)
# Add to metrics table
field_ci_metrics <- rbind(field_ci_metrics, data.frame(
field = field_name,
week_current = mean_ci_curr,
week_minus_1 = mean_ci_prev1,
week_minus_2 = mean_ci_prev2,
week_minus_3 = mean_ci_prev3,
stringsAsFactors = FALSE
))
}, error = function(e) {
message(paste("Error processing field", field_name, "for velocity indicator:", e$message))
})
}
# Calculate farm-wide averages
farm_avg <- colMeans(field_ci_metrics[, c("week_current", "week_minus_1", "week_minus_2", "week_minus_3")], na.rm = TRUE)
# Calculate velocity (rate of change) - current week compared to last week
velocity <- farm_avg["week_current"] - farm_avg["week_minus_1"]
# Calculate previous velocity (last week compared to two weeks ago)
prev_velocity <- farm_avg["week_minus_1"] - farm_avg["week_minus_2"]
# Calculate acceleration (change in velocity)
acceleration <- velocity - prev_velocity
# Prepare data for velocity gauge
velocity_data <- data.frame(
label = "Weekly CI Change",
value = velocity
)
# Prepare data for acceleration gauge
acceleration_data <- data.frame(
label = "Change Acceleration",
value = acceleration
)
# Create velocity trend data
trend_data <- data.frame(
week = c(-3, -2, -1, 0),
ci_value = c(farm_avg["week_minus_3"], farm_avg["week_minus_2"],
farm_avg["week_minus_1"], farm_avg["week_current"])
)
# Create layout grid for the visualizations
layout_matrix <- matrix(c(1, 1, 2, 2, 3, 3), nrow = 2, byrow = TRUE)
# Create velocity gauge
velocity_gauge <- ggplot2::ggplot(velocity_data, ggplot2::aes(x = 0, y = 0)) +
ggplot2::geom_arc_bar(ggplot2::aes(
x0 = 0, y0 = 0,
r0 = 0.5, r = 1,
start = -pi/2, end = pi/2,
fill = "background"
), fill = "#f0f0f0") +
ggplot2::geom_arc_bar(ggplot2::aes(
x0 = 0, y0 = 0,
r0 = 0.5, r = 1,
start = -pi/2,
end = -pi/2 + (pi * (0.5 + (velocity / 2))), # Scale to range -1 to +1
fill = "velocity"
), fill = ifelse(velocity >= 0, "#1a9850", "#d73027")) +
ggplot2::geom_text(ggplot2::aes(label = sprintf("%.2f", velocity)),
size = 8, fontface = "bold") +
ggplot2::geom_text(ggplot2::aes(label = "Velocity"), y = -0.3, size = 4) +
ggplot2::coord_fixed() +
ggplot2::theme_void() +
ggplot2::scale_fill_manual(values = c("background" = "#f0f0f0", "velocity" = "steelblue"),
guide = "none") +
ggplot2::annotate("text", x = -0.85, y = 0, label = "Declining",
angle = 90, size = 3.5) +
ggplot2::annotate("text", x = 0.85, y = 0, label = "Improving",
angle = -90, size = 3.5) +
ggplot2::labs(title = "Farm Health Velocity",
subtitle = "CI change per week") +
ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5, size = 14, face = "bold"),
plot.subtitle = ggplot2::element_text(hjust = 0.5, size = 12))
# Create acceleration gauge
acceleration_gauge <- ggplot2::ggplot(acceleration_data, ggplot2::aes(x = 0, y = 0)) +
ggplot2::geom_arc_bar(ggplot2::aes(
x0 = 0, y0 = 0,
r0 = 0.5, r = 1,
start = -pi/2, end = pi/2,
fill = "background"
), fill = "#f0f0f0") +
ggplot2::geom_arc_bar(ggplot2::aes(
x0 = 0, y0 = 0,
r0 = 0.5, r = 1,
start = -pi/2,
end = -pi/2 + (pi * (0.5 + (acceleration / 1))), # Scale to range -0.5 to +0.5
fill = "acceleration"
), fill = ifelse(acceleration >= 0, "#1a9850", "#d73027")) +
ggplot2::geom_text(ggplot2::aes(label = sprintf("%.2f", acceleration)),
size = 8, fontface = "bold") +
ggplot2::geom_text(ggplot2::aes(label = "Acceleration"), y = -0.3, size = 4) +
ggplot2::coord_fixed() +
ggplot2::theme_void() +
ggplot2::scale_fill_manual(values = c("background" = "#f0f0f0", "acceleration" = "steelblue"),
guide = "none") +
ggplot2::annotate("text", x = -0.85, y = 0, label = "Slowing",
angle = 90, size = 3.5) +
ggplot2::annotate("text", x = 0.85, y = 0, label = "Accelerating",
angle = -90, size = 3.5) +
ggplot2::labs(title = "Change Acceleration",
subtitle = "Increasing or decreasing trend") +
ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5, size = 14, face = "bold"),
plot.subtitle = ggplot2::element_text(hjust = 0.5, size = 12))
# Create trend chart
trend_chart <- ggplot2::ggplot(trend_data, ggplot2::aes(x = week, y = ci_value)) +
ggplot2::geom_line(size = 1.5, color = "steelblue") +
ggplot2::geom_point(size = 3, color = "steelblue") +
ggplot2::geom_hline(yintercept = trend_data$ci_value[1], linetype = "dashed", color = "gray50") +
ggplot2::labs(
title = "4-Week CI Trend",
x = "Weeks from current",
y = "Average CI Value"
) +
ggplot2::theme_minimal() +
ggplot2::scale_x_continuous(breaks = c(-3, -2, -1, 0))
# Create table of top velocity changes
field_ci_metrics$velocity <- field_ci_metrics$week_current - field_ci_metrics$week_minus_1
top_velocity_fields <- field_ci_metrics %>%
dplyr::arrange(desc(abs(velocity))) %>%
dplyr::slice_head(n = 5) %>%
dplyr::select(field, velocity) %>%
dplyr::mutate(direction = ifelse(velocity >= 0, "Improving", "Declining"))
# Combine into multi-panel figure
main_plot <- gridExtra::grid.arrange(
gridExtra::grid.arrange(velocity_gauge, acceleration_gauge, ncol = 2),
trend_chart,
heights = c(1.5, 1),
nrow = 2
)
return(main_plot)
}, error = function(e) {
message(paste("Error in create_velocity_acceleration_indicator:", e$message))
return(ggplot2::ggplot() +
ggplot2::annotate("text", x = 0, y = 0, label = paste("Error creating velocity indicator:", e$message)) +
ggplot2::theme_void())
})
}
#' Generate a field health score based on CI values and trends
#'
#' @param ci_current Current CI raster
#' @param ci_change CI change raster
#' @param field_age_weeks Field age in weeks
#' @return List containing score, status, and component scores
#'
generate_field_health_score <- function(ci_current, ci_change, field_age_weeks) {
# Get mean CI value for the field
mean_ci <- terra::global(ci_current, "mean", na.rm=TRUE)[[1]]
# Get mean CI change
mean_change <- terra::global(ci_change, "mean", na.rm=TRUE)[[1]]
# Get CI uniformity (coefficient of variation)
ci_sd <- terra::global(ci_current, "sd", na.rm=TRUE)[[1]]
ci_uniformity <- ifelse(mean_ci > 0, ci_sd / mean_ci, 1)
# Calculate base score from current CI (scale 0-5)
# Adjusted for crop age - expectations increase with age
expected_ci <- min(5, field_age_weeks / 10) # Simple linear model
ci_score <- max(0, min(5, 5 - 2 * abs(mean_ci - expected_ci)))
# Add points for positive change (scale 0-3)
change_score <- max(0, min(3, 1 + mean_change))
# Add points for uniformity (scale 0-2)
uniformity_score <- max(0, min(2, 2 * (1 - ci_uniformity)))
# Calculate total score (0-10)
total_score <- ci_score + change_score + uniformity_score
# Create status label
status <- dplyr::case_when(
total_score >= 8 ~ "Excellent",
total_score >= 6 ~ "Good",
total_score >= 4 ~ "Fair",
total_score >= 2 ~ "Needs Attention",
TRUE ~ "Critical"
)
# Return results
return(list(
score = round(total_score, 1),
status = status,
components = list(
ci = round(ci_score, 1),
change = round(change_score, 1),
uniformity = round(uniformity_score, 1)
)
))
}
#' Create an irrigation recommendation map
#'
#' @param ci_current Current CI raster
#' @param ci_change CI change raster
#' @param field_shape Field boundary shape
#' @param title Map title
#' @return A tmap object with irrigation recommendations
#'
create_irrigation_map <- function(ci_current, ci_change, field_shape, title = "Irrigation Priority Zones") {
# Create a new raster for irrigation recommendations
irrigation_priority <- ci_current * 0
# Extract values for processing
ci_values <- terra::values(ci_current)
change_values <- terra::values(ci_change)
# Create priority zones:
# 3 = High priority (low CI, negative trend)
# 2 = Medium priority (low CI but stable, or good CI with negative trend)
# 1 = Low priority (watch, good CI with slight decline)
# 0 = No action needed (good CI, stable/positive trend)
priority_values <- rep(NA, length(ci_values))
# High priority: Low CI (< 2) and negative change (< 0)
high_priority <- which(ci_values < 2 & change_values < 0 & !is.na(ci_values) & !is.na(change_values))
priority_values[high_priority] <- 3
# Medium priority: Low CI (< 2) with stable/positive change, or moderate CI (2-4) with significant negative change (< -1)
medium_priority <- which(
(ci_values < 2 & change_values >= 0 & !is.na(ci_values) & !is.na(change_values)) |
(ci_values >= 2 & ci_values < 4 & change_values < -1 & !is.na(ci_values) & !is.na(change_values))
)
priority_values[medium_priority] <- 2
# Low priority (watch): Moderate/good CI (>= 2) with mild negative change (-1 to 0)
low_priority <- which(
ci_values >= 2 & change_values < 0 & change_values >= -1 & !is.na(ci_values) & !is.na(change_values)
)
priority_values[low_priority] <- 1
# No action needed: Good CI (>= 2) with stable/positive change (>= 0)
no_action <- which(ci_values >= 2 & change_values >= 0 & !is.na(ci_values) & !is.na(change_values))
priority_values[no_action] <- 0
# Set values in the irrigation priority raster
terra::values(irrigation_priority) <- priority_values
# Create the map
tm_shape(irrigation_priority) +
tm_raster(
style = "cat",
palette = c("#1a9850", "#91cf60", "#fc8d59", "#d73027"),
labels = c("No Action", "Watch", "Medium Priority", "High Priority"),
title = "Irrigation Need"
) +
tm_shape(field_shape) +
tm_borders(lwd = 2) +
tm_layout(
main.title = title,
legend.outside = FALSE,
legend.position = c("left", "bottom")
)
}
#' Simple mock function to get weather data for a field
#' In a real implementation, this would fetch data from a weather API
#'
#' @param start_date Start date for weather data
#' @param end_date End date for weather data
#' @param lat Latitude of the field center
#' @param lon Longitude of the field center
#' @return A data frame of weather data
#'
get_weather_data <- function(start_date, end_date, lat = -16.1, lon = 34.7) {
# This is a mock implementation - in production, you'd replace with actual API call
# to a service like OpenWeatherMap, NOAA, or other weather data provider
# Create date sequence
dates <- seq.Date(from = as.Date(start_date), to = as.Date(end_date), by = "day")
n_days <- length(dates)
# Generate some random but realistic weather data with seasonal patterns
# More rain in summer, less in winter (Southern hemisphere)
month_nums <- as.numeric(format(dates, "%m"))
# Simplified seasonal patterns - adjust for your local climate
is_rainy_season <- month_nums %in% c(11, 12, 1, 2, 3, 4)
# Generate rainfall - more in rainy season, occasional heavy rainfall
rainfall <- numeric(n_days)
rainfall[is_rainy_season] <- pmax(0, rnorm(sum(is_rainy_season), mean = 4, sd = 8))
rainfall[!is_rainy_season] <- pmax(0, rnorm(sum(!is_rainy_season), mean = 0.5, sd = 2))
# Add some rare heavy rainfall events
heavy_rain_days <- sample(which(is_rainy_season), size = max(1, round(sum(is_rainy_season) * 0.1)))
rainfall[heavy_rain_days] <- rainfall[heavy_rain_days] + runif(length(heavy_rain_days), 20, 50)
# Generate temperatures - seasonal variation
temp_mean <- 18 + 8 * sin((month_nums - 1) * pi/6) # Peak in January (month 1)
temp_max <- temp_mean + rnorm(n_days, mean = 5, sd = 1)
temp_min <- temp_mean - rnorm(n_days, mean = 5, sd = 1)
# Create weather data frame
weather_data <- data.frame(
date = dates,
rainfall_mm = round(rainfall, 1),
temp_max_c = round(temp_max, 1),
temp_min_c = round(temp_min, 1),
temp_mean_c = round((temp_max + temp_min) / 2, 1)
)
return(weather_data)
}
#' Creates a weather summary visualization integrated with CI data
#'
#' @param pivotName Name of the pivot field
#' @param ci_data CI quadrant data
#' @param days_to_show Number of days of weather to show
#' @return ggplot object
#'
create_weather_ci_plot <- function(pivotName, ci_data = CI_quadrant, days_to_show = 30) {
# Get field data
field_data <- ci_data %>%
dplyr::filter(field == pivotName) %>%
dplyr::arrange(Date) %>%
dplyr::filter(!is.na(value))
if (nrow(field_data) == 0) {
return(ggplot() +
annotate("text", x = 0, y = 0, label = "No data available") +
theme_void())
}
# Get the latest date and 30 days before
latest_date <- max(field_data$Date, na.rm = TRUE)
start_date <- latest_date - days_to_show
# Filter for recent data only
recent_field_data <- field_data %>%
dplyr::filter(Date >= start_date)
# Get center point coordinates for the field (would be calculated from geometry in production)
# This is mocked for simplicity
lat <- -16.1 # Mock latitude
lon <- 34.7 # Mock longitude
# Get weather data
weather_data <- get_weather_data(start_date, latest_date, lat, lon)
# Aggregate CI data to daily mean across subfields if needed
daily_ci <- recent_field_data %>%
dplyr::group_by(Date) %>%
dplyr::summarize(mean_ci = mean(value, na.rm = TRUE))
# Create combined plot with dual y-axis
g <- ggplot() +
# Rainfall as bars
geom_col(data = weather_data, aes(x = date, y = rainfall_mm),
fill = "#1565C0", alpha = 0.7, width = 0.7) +
# CI as a line
geom_line(data = daily_ci, aes(x = Date, y = mean_ci * 10),
color = "#2E7D32", size = 1) +
geom_point(data = daily_ci, aes(x = Date, y = mean_ci * 10),
color = "#2E7D32", size = 2) +
# Temperature range as ribbon
geom_ribbon(data = weather_data,
aes(x = date, ymin = temp_min_c, ymax = temp_max_c),
fill = "#FF9800", alpha = 0.2) +
# Primary y-axis (rainfall)
scale_y_continuous(
name = "Rainfall (mm)",
sec.axis = sec_axis(~./10, name = "Chlorophyll Index & Temperature (°C)")
) +
labs(
title = paste("Field", pivotName, "- Weather and CI Relationship"),
subtitle = paste("Last", days_to_show, "days"),
x = "Date"
) +
theme_minimal() +
theme(
axis.title.y.left = element_text(color = "#1565C0"),
axis.title.y.right = element_text(color = "#2E7D32"),
legend.position = "bottom"
)
return(g)
}

View file

@ -0,0 +1,54 @@
library(sf)
library(terra)
library(tidyverse)
library(lubridate)
library(exactextractr)
library(readxl)
# Vang alle command line argumenten op
args <- commandArgs(trailingOnly = TRUE)
# Controleer of er ten minste één argument is doorgegeven
if (length(args) == 0) {
stop("Geen argumenten doorgegeven aan het script")
}
# Converteer het eerste argument naar een numerieke waarde
end_date <- as.Date(args[1])
if (is.na(end_date)) {
end_date <- lubridate::dmy("28-08-2024")
}
offset <- as.numeric(args[2])
# Controleer of weeks_ago een geldig getal is
if (is.na(offset)) {
# stop("Het argument is geen geldig getal")
offset <- 7
}
week <- week(end_date)
# Converteer het tweede argument naar een string waarde
project_dir <- as.character(args[3])
# Controleer of data_dir een geldige waarde is
if (!is.character(project_dir)) {
project_dir <- "chemba"
}
new_project_question = FALSE
source("parameters_project.R")
source("ci_extraction_utils.R")
raster_files <- list.files(merged_final,full.names = T, pattern = ".tif")
# Load all raster files
rasters <- lapply(raster_files, function(file) {
r <- rast(file)
r[[which(names(r) == "CI")]] # Select the CI band
})
# Calculate the maximum CI per pixel
max_ci_raster <- do.call(terra::app, c(rasters, fun = mean, na.rm = TRUE))
plot(max_ci_raster)

View file

@ -0,0 +1,421 @@
# Optimal CI Analysis - Day 30 CI values with quadratic fitting
# Author: SmartCane Analysis Team
# Date: 2025-06-12
# Load required libraries
library(ggplot2)
library(dplyr)
library(readr)
# Set file path
rds_file_path <- "C:/Users/timon/Resilience BV/4020 SCane ESA DEMO - Documenten/General/4020 SCDEMO Team/4020 TechnicalData/WP3/smartcane/laravel_app/storage/app/chemba/Data/extracted_ci/cumulative_vals/All_pivots_Cumulative_CI_quadrant_year_v2.rds"
# Check if file exists
if (!file.exists(rds_file_path)) {
stop("RDS file not found at specified path: ", rds_file_path)
}
# Load the data
cat("Loading RDS file...\n")
ci_data <- readRDS(rds_file_path)
# Display structure of the data to understand it better
cat("Data structure:\n")
str(ci_data)
cat("\nFirst few rows:\n")
head(ci_data)
cat("\nColumn names:\n")
print(colnames(ci_data))
# Filter data based on requirements
cat("\nApplying data filters...\n")
# 1. Filter out models that don't reach at least DOY 300
model_doy_max <- ci_data %>%
group_by(model) %>%
summarise(max_doy = max(DOY, na.rm = TRUE), .groups = 'drop')
valid_models <- model_doy_max %>%
filter(max_doy >= 300) %>%
pull(model)
cat(paste("Models before filtering:", n_distinct(ci_data$model), "\n"))
cat(paste("Models reaching at least DOY 300:", length(valid_models), "\n"))
ci_data <- ci_data %>%
filter(model %in% valid_models)
# 2. Apply IQR filtering per DOY (remove outliers outside IQR)
cat("Applying IQR filtering per DOY...\n")
original_rows <- nrow(ci_data)
ci_data <- ci_data %>%
group_by(DOY) %>%
mutate(
Q1 = quantile(FitData, 0.25, na.rm = TRUE),
Q3 = quantile(FitData, 0.75, na.rm = TRUE),
IQR = Q3 - Q1,
lower_bound = Q1 - 1.5 * IQR,
upper_bound = Q3 + 1.5 * IQR
) %>%
filter(FitData >= lower_bound & FitData <= upper_bound) %>%
select(-Q1, -Q3, -IQR, -lower_bound, -upper_bound) %>%
ungroup()
filtered_rows <- nrow(ci_data)
cat(paste("Rows before IQR filtering:", original_rows, "\n"))
cat(paste("Rows after IQR filtering:", filtered_rows, "\n"))
cat(paste("Removed", original_rows - filtered_rows, "outliers (",
round(100 * (original_rows - filtered_rows) / original_rows, 1), "%)\n"))
# Check what day values are available after filtering
if ("DOY" %in% colnames(ci_data)) {
cat("\nUnique DOY values after filtering:\n")
print(sort(unique(ci_data$DOY)))
} else {
cat("\nNo 'DOY' column found. Available columns:\n")
print(colnames(ci_data))
}
# Extract CI values at day 30 for each field
cat("\nExtracting day 30 CI values...\n")
# Set column names based on known structure
day_col <- "DOY"
ci_col <- "FitData"
field_col <- "model"
# Try different possible field column name combinations
if ("field" %in% colnames(ci_data)) {
field_col <- "field"
} else if ("Field" %in% colnames(ci_data)) {
field_col <- "Field"
} else if ("pivot" %in% colnames(ci_data)) {
field_col <- "pivot"
} else if ("Pivot" %in% colnames(ci_data)) {
field_col <- "Pivot"
} else if ("field_id" %in% colnames(ci_data)) {
field_col <- "field_id"
} else if ("Field_ID" %in% colnames(ci_data)) {
field_col <- "Field_ID"
}
# Check if we found the required columns
if (!("DOY" %in% colnames(ci_data))) {
stop("DOY column not found in data")
}
if (!("FitData" %in% colnames(ci_data))) {
stop("FitData column not found in data")
}
if (is.null(field_col)) {
cat("Could not automatically identify field column. Please check column names.\n")
cat("Available columns: ", paste(colnames(ci_data), collapse = ", "), "\n")
stop("Manual field column identification required")
}
cat(paste("Using columns - DOY:", day_col, "Field:", field_col, "FitData:", ci_col, "\n"))
# Extract day 30 data
day_30_data <- ci_data %>%
filter(DOY %% 30 == 0) %>%
select(field = !!sym(field_col), ci = !!sym(ci_col), DOY) %>%
na.omit() %>%
arrange(field)
cat(paste("Found", nrow(day_30_data), "fields with day 30 CI values\n"))
if (nrow(day_30_data) == 0) {
stop("No data found for day 30. Check if day 30 exists in the dataset.")
}
# Display summary of day 30 data
cat("\nSummary of day 30 CI values:\n")
print(summary(day_30_data))
# Add field index for plotting (assuming fields represent some spatial or sequential order)
#day_30_data$field_index <- 1:nrow(day_30_data)
# Create scatter plot
p1 <- ggplot(day_30_data, aes(x = DOY, y = ci)) +
geom_point(color = "blue", size = 3, alpha = 0.7) +
labs(
title = "CI Values at Day 30 for All Fields",
x = "Day of Year (DOY)",
y = "CI Value",
subtitle = paste("Total fields:", nrow(day_30_data))
) +
theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold"),
axis.title = element_text(size = 12),
axis.text = element_text(size = 10)
)
print(p1)
# Try multiple curve fitting approaches
cat("\nTrying different curve fitting approaches...\n")
# Aggregate data by DOY (take mean CI for each DOY to reduce noise)
aggregated_data <- day_30_data %>%
group_by(DOY) %>%
summarise(
mean_ci = mean(ci, na.rm = TRUE),
median_ci = median(ci, na.rm = TRUE),
count = n(),
.groups = 'drop'
) %>%
filter(count >= 5) # Only keep DOY values with sufficient data points
cat(paste("Aggregated to", nrow(aggregated_data), "DOY points with sufficient data\n"))
# Create prediction data for smooth curves
x_smooth <- seq(min(aggregated_data$DOY), max(aggregated_data$DOY), length.out = 100)
# 1. Quadratic model (as requested)
cat("\n1. Fitting quadratic model...\n")
quad_model <- lm(mean_ci ~ poly(DOY, 2, raw = TRUE), data = aggregated_data)
quad_pred <- predict(quad_model, newdata = data.frame(DOY = x_smooth))
quad_r2 <- summary(quad_model)$r.squared
cat(paste("Quadratic R² =", round(quad_r2, 3), "\n"))
# 2. Logistic growth model: y = K / (1 + exp(-r*(x-x0))) - biologically realistic
cat("\n2. Fitting logistic growth model...\n")
tryCatch({
# Estimate starting values
K_start <- max(aggregated_data$mean_ci) * 1.1 # Carrying capacity
r_start <- 0.05 # Growth rate
x0_start <- mean(aggregated_data$DOY) # Inflection point
logistic_model <- nls(mean_ci ~ K / (1 + exp(-r * (DOY - x0))),
data = aggregated_data,
start = list(K = K_start, r = r_start, x0 = x0_start),
control = nls.control(maxiter = 1000))
logistic_pred <- predict(logistic_model, newdata = data.frame(DOY = x_smooth))
logistic_r2 <- 1 - sum(residuals(logistic_model)^2) / sum((aggregated_data$mean_ci - mean(aggregated_data$mean_ci))^2)
cat(paste("Logistic growth R² =", round(logistic_r2, 3), "\n"))
}, error = function(e) {
cat("Logistic growth model failed to converge\n")
logistic_model <- NULL
logistic_pred <- NULL
logistic_r2 <- NA
})
# 3. Beta function model: good for crop growth curves with clear peak
cat("\n3. Fitting Beta function model...\n")
tryCatch({
# Normalize DOY to 0-1 range for Beta function
doy_min <- min(aggregated_data$DOY)
doy_max <- max(aggregated_data$DOY)
aggregated_data$doy_norm <- (aggregated_data$DOY - doy_min) / (doy_max - doy_min)
# Beta function: y = a * (x^(p-1)) * ((1-x)^(q-1)) + c
beta_model <- nls(mean_ci ~ a * (doy_norm^(p-1)) * ((1-doy_norm)^(q-1)) + c,
data = aggregated_data,
start = list(a = max(aggregated_data$mean_ci) * 20, p = 2, q = 3, c = min(aggregated_data$mean_ci)),
control = nls.control(maxiter = 1000))
# Predict on normalized scale then convert back
x_smooth_norm <- (x_smooth - doy_min) / (doy_max - doy_min)
beta_pred <- predict(beta_model, newdata = data.frame(doy_norm = x_smooth_norm))
beta_r2 <- 1 - sum(residuals(beta_model)^2) / sum((aggregated_data$mean_ci - mean(aggregated_data$mean_ci))^2)
cat(paste("Beta function R² =", round(beta_r2, 3), "\n"))
}, error = function(e) {
cat("Beta function model failed to converge\n")
beta_model <- NULL
beta_pred <- NULL
beta_r2 <- NA
})
# 4. Gaussian (normal) curve: good for symmetric growth patterns
cat("\n4. Fitting Gaussian curve...\n")
tryCatch({
# Gaussian: y = a * exp(-((x-mu)^2)/(2*sigma^2)) + c
gaussian_model <- nls(mean_ci ~ a * exp(-((DOY - mu)^2)/(2 * sigma^2)) + c,
data = aggregated_data,
start = list(a = max(aggregated_data$mean_ci),
mu = aggregated_data$DOY[which.max(aggregated_data$mean_ci)],
sigma = 50,
c = min(aggregated_data$mean_ci)),
control = nls.control(maxiter = 1000))
gaussian_pred <- predict(gaussian_model, newdata = data.frame(DOY = x_smooth))
gaussian_r2 <- 1 - sum(residuals(gaussian_model)^2) / sum((aggregated_data$mean_ci - mean(aggregated_data$mean_ci))^2)
cat(paste("Gaussian R² =", round(gaussian_r2, 3), "\n"))
}, error = function(e) {
cat("Gaussian model failed to converge\n")
gaussian_model <- NULL
gaussian_pred <- NULL
gaussian_r2 <- NA
})
# 5. LOESS (local regression) - for comparison
cat("\n5. Fitting LOESS smoothing...\n")
loess_model <- loess(mean_ci ~ DOY, data = aggregated_data, span = 0.5)
loess_pred <- predict(loess_model, newdata = data.frame(DOY = x_smooth))
loess_r2 <- 1 - sum(residuals(loess_model)^2) / sum((aggregated_data$mean_ci - mean(aggregated_data$mean_ci))^2)
cat(paste("LOESS R² =", round(loess_r2, 3), "\n"))
# Calculate confidence intervals for both models
cat("\nCalculating confidence intervals...\n")
# Function to calculate confidence intervals using residual-based method
calculate_ci <- function(model, data, newdata, alpha = 0.5) {
# Get model predictions
pred_vals <- predict(model, newdata = newdata)
# For parametric models (lm), use built-in prediction intervals
if(class(model)[1] == "lm") {
pred_intervals <- predict(model, newdata = newdata, interval = "confidence", level = 1 - alpha)
return(list(
lower = pred_intervals[, "lwr"],
upper = pred_intervals[, "upr"]
))
}
# For LOESS, calculate confidence intervals using residual bootstrap
if(class(model)[1] == "loess") {
# Calculate residuals from the original model
fitted_vals <- fitted(model)
residuals_vals <- residuals(model)
residual_sd <- sd(residuals_vals, na.rm = TRUE)
# Use normal approximation for confidence intervals
# For 50% CI, use 67% quantile (approximately 0.67 standard deviations)
margin <- qnorm(1 - alpha/2) * residual_sd
return(list(
lower = pred_vals - margin,
upper = pred_vals + margin
))
}
# Fallback method
residual_sd <- sd(residuals(model), na.rm = TRUE)
margin <- qnorm(1 - alpha/2) * residual_sd
return(list(
lower = pred_vals - margin,
upper = pred_vals + margin
))
}# Calculate CIs for quadratic model using aggregated data (same as model fitting)
quad_ci <- calculate_ci(quad_model, aggregated_data, data.frame(DOY = x_smooth))
# Calculate CIs for LOESS model using aggregated data (same as model fitting)
loess_ci <- calculate_ci(loess_model, aggregated_data, data.frame(DOY = x_smooth))
# Create separate plots for LOESS and Quadratic models
cat("\nCreating LOESS plot with confidence intervals...\n")
# LOESS plot
p_loess <- ggplot(day_30_data, aes(x = DOY, y = ci)) +
geom_point(color = "lightblue", size = 1.5, alpha = 0.4) +
geom_point(data = aggregated_data, aes(x = DOY, y = mean_ci),
color = "darkblue", size = 3, alpha = 0.8) +
geom_ribbon(data = data.frame(DOY = x_smooth,
lower = loess_ci$lower,
upper = loess_ci$upper),
aes(x = DOY, ymin = lower, ymax = upper),
alpha = 0.3, fill = "purple", inherit.aes = FALSE) +
geom_line(data = data.frame(DOY = x_smooth, loess = loess_pred),
aes(x = DOY, y = loess),
color = "purple", size = 1.5) +
labs(
title = "LOESS Model - CI Values Over Growing Season",
x = "Day of Year (DOY)",
y = "CI Value",
subtitle = paste("LOESS R² =", round(loess_r2, 3), "| 50% Confidence Interval")
) +
theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold"),
axis.title = element_text(size = 12),
axis.text = element_text(size = 10)
)
# Find optimal point for LOESS
loess_max_idx <- which.max(loess_pred)
loess_optimal_doy <- x_smooth[loess_max_idx]
loess_optimal_ci <- loess_pred[loess_max_idx]
p_loess <- p_loess +
geom_point(aes(x = loess_optimal_doy, y = loess_optimal_ci),
color = "red", size = 5, shape = 8) +
annotate("text",
x = loess_optimal_doy + 30,
y = loess_optimal_ci,
label = paste("Optimal: DOY", round(loess_optimal_doy, 1), "\nCI =", round(loess_optimal_ci, 3)),
color = "red", size = 4, fontface = "bold")
print(p_loess)
cat("\nCreating Quadratic plot with confidence intervals...\n")
# Quadratic plot
p_quadratic <- ggplot(day_30_data, aes(x = DOY, y = ci)) +
geom_point(color = "lightcoral", size = 1.5, alpha = 0.4) +
geom_point(data = aggregated_data, aes(x = DOY, y = mean_ci),
color = "darkred", size = 3, alpha = 0.8) +
geom_ribbon(data = data.frame(DOY = x_smooth,
lower = quad_ci$lower,
upper = quad_ci$upper),
aes(x = DOY, ymin = lower, ymax = upper),
alpha = 0.3, fill = "red", inherit.aes = FALSE) +
geom_line(data = data.frame(DOY = x_smooth, quadratic = quad_pred),
aes(x = DOY, y = quadratic),
color = "red", size = 1.5) +
labs(
title = "Quadratic Model - CI Values Over Growing Season",
x = "Day of Year (DOY)",
y = "CI Value",
subtitle = paste("Quadratic R² =", round(quad_r2, 3), "| 50% Confidence Interval")
) +
theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold"),
axis.title = element_text(size = 12),
axis.text = element_text(size = 10)
)
# Find optimal point for Quadratic
quad_coeffs <- coef(quad_model)
a <- quad_coeffs[3]
b <- quad_coeffs[2]
if(a != 0) {
quad_optimal_doy <- -b / (2*a)
doy_range <- range(aggregated_data$DOY)
if(quad_optimal_doy >= doy_range[1] && quad_optimal_doy <= doy_range[2]) {
quad_optimal_ci <- predict(quad_model, newdata = data.frame(DOY = quad_optimal_doy))
} else {
quad_optimal_doy <- x_smooth[which.max(quad_pred)]
quad_optimal_ci <- max(quad_pred)
}
} else {
quad_optimal_doy <- x_smooth[which.max(quad_pred)]
quad_optimal_ci <- max(quad_pred)
}
p_quadratic <- p_quadratic +
geom_point(aes(x = quad_optimal_doy, y = quad_optimal_ci),
color = "darkred", size = 5, shape = 8) +
annotate("text",
x = quad_optimal_doy + 30,
y = quad_optimal_ci,
label = paste("Optimal: DOY", round(quad_optimal_doy, 1), "\nCI =", round(quad_optimal_ci, 3)),
color = "darkred", size = 4, fontface = "bold")
print(p_quadratic)
print(p_loess)
# Print results summary
cat("\n=== RESULTS SUMMARY ===\n")
cat(paste("LOESS Model - R² =", round(loess_r2, 3), "\n"))
cat(paste(" Optimal DOY:", round(loess_optimal_doy, 1), "\n"))
cat(paste(" Optimal CI:", round(loess_optimal_ci, 4), "\n\n"))
cat(paste("Quadratic Model - R² =", round(quad_r2, 3), "\n"))
cat(paste(" Optimal DOY:", round(quad_optimal_doy, 1), "\n"))
cat(paste(" Optimal CI:", round(quad_optimal_ci, 4), "\n"))

View file

@ -0,0 +1,159 @@
{
"type": "FeatureCollection",
"name": "pivot",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "field": "6.2", "sub_field": "6.2B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.933686532028823, -17.34741850738671 ], [ 34.936458268544008, -17.344213063126752 ], [ 34.936487787462688, -17.34423678523423 ], [ 34.936654047751475, -17.344385275553996 ], [ 34.936811988565154, -17.344541918958814 ], [ 34.936961177001955, -17.344706286119994 ], [ 34.93710120414535, -17.344877926538839 ], [ 34.937231686184816, -17.345056369781194 ], [ 34.937352265468007, -17.345241126766645 ], [ 34.937462611481052, -17.345431691108853 ], [ 34.937562421754727, -17.345627540503298 ], [ 34.937651422693591, -17.345828138158684 ], [ 34.937729370326288, -17.346032934268045 ], [ 34.937796050974356, -17.346241367515468 ], [ 34.937851281838412, -17.34645286661458 ], [ 34.937894911499427, -17.346666851874129 ], [ 34.93792682033417, -17.346882736786782 ], [ 34.937946920843501, -17.347099929636563 ], [ 34.937955157892617, -17.347317835120631 ], [ 34.937951508862682, -17.347535855980816 ], [ 34.937935983713146, -17.347753394640662 ], [ 34.937908624955057, -17.347969854843281 ], [ 34.937869507534906, -17.348184643285634 ], [ 34.937818738629701, -17.348397171244734 ], [ 34.937756457353686, -17.348606856191328 ], [ 34.937682834377377, -17.348813123386648 ], [ 34.937598071460293, -17.349015407457767 ], [ 34.937502400898275, -17.349213153947368 ], [ 34.937396084887148, -17.349405820833582 ], [ 34.937279414804458, -17.349592880015777 ], [ 34.93715271041102, -17.349773818762202 ], [ 34.937016318974827, -17.349948141115476 ], [ 34.936891437505039, -17.35009147006711 ], [ 34.933686532028823, -17.34741850738671 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.2", "sub_field": "5.2B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.892982579947684, -17.30260313717654 ], [ 34.8969126991338, -17.302747852699646 ], [ 34.896848733623173, -17.306522026027363 ], [ 34.896761992373065, -17.306520666974571 ], [ 34.89655566821915, -17.306507035570725 ], [ 34.896350369530914, -17.306483049995649 ], [ 34.896146659052505, -17.306448775992763 ], [ 34.895945095174199, -17.306404307507115 ], [ 34.895746230401478, -17.306349766427822 ], [ 34.895550609840363, -17.306285302254011 ], [ 34.895358769702952, -17.306211091684883 ], [ 34.895171235837296, -17.306127338135401 ], [ 34.894988522285892, -17.306034271178618 ], [ 34.894811129876366, -17.305932145916291 ], [ 34.894639544848609, -17.305821242279585 ], [ 34.894474237521642, -17.3057018642616 ], [ 34.894315661004462, -17.305574339084064 ], [ 34.894164249953917, -17.305439016300223 ], [ 34.89402041938321, -17.305296266836596 ], [ 34.89388456352436, -17.305146481976095 ], [ 34.893757054747589, -17.304990072285385 ], [ 34.893638242540632, -17.304827466489353 ], [ 34.893528452551024, -17.304659110295812 ], [ 34.89342798569345, -17.304485465173695 ], [ 34.893337117325196, -17.304307007087981 ], [ 34.893256096491513, -17.304124225194958 ], [ 34.893185145243294, -17.30393762050133 ], [ 34.893124458028609, -17.303747704490792 ], [ 34.893074201160012, -17.303554997721992 ], [ 34.893034512359016, -17.303360028401542 ], [ 34.893005500378912, -17.303163330936176 ], [ 34.892987244707008, -17.302965444467794 ], [ 34.892979795347131, -17.302766911395732 ], [ 34.892982579947684, -17.30260313717654 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.3", "sub_field": "5.3B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.887065228066767, -17.304559537671949 ], [ 34.887585923201613, -17.308341731698718 ], [ 34.887518594228453, -17.308350893542517 ], [ 34.887312586440835, -17.308368425006687 ], [ 34.887105905758595, -17.30837557560691 ], [ 34.886899118713622, -17.308372325739303 ], [ 34.886692792129786, -17.308358684308679 ], [ 34.886487491568985, -17.308334688704125 ], [ 34.88628377978047, -17.308300404696595 ], [ 34.886082215158112, -17.308255926258568 ], [ 34.885883350209426, -17.308201375306449 ], [ 34.885687730040928, -17.308136901366442 ], [ 34.885495890863602, -17.308062681164554 ], [ 34.885308358522892, -17.307978918142162 ], [ 34.885125647057059, -17.30788584189828 ], [ 34.884948257288038, -17.307783707560155 ], [ 34.884776675448421, -17.307672795083811 ], [ 34.884611371848493, -17.307553408486619 ], [ 34.884452799587045, -17.307425875013813 ], [ 34.88430139330918, -17.307290544241372 ], [ 34.884157568015056, -17.307147787117731 ], [ 34.884021717922174, -17.306997994946784 ], [ 34.883894215384913, -17.306841578315254 ], [ 34.883775409873941, -17.30667896596702 ], [ 34.88366562701836, -17.306510603627871 ], [ 34.8835651677133, -17.306336952783568 ], [ 34.883474307295344, -17.306158489414777 ], [ 34.883393294787901, -17.305975702692255 ], [ 34.883322352219061, -17.305789093635923 ], [ 34.883261674013042, -17.305599173741417 ], [ 34.883211426457713, -17.305406463578016 ], [ 34.883171747249051, -17.305211491361643 ], [ 34.883142745114029, -17.305014791506924 ], [ 34.883138407553353, -17.304967747142655 ], [ 34.887065228066767, -17.304559537671949 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.1", "sub_field": "5.1D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.893567231271753, -17.309696371939275 ], [ 34.897496240357995, -17.309278976679899 ], [ 34.897513960068423, -17.309470988039859 ], [ 34.897521415315232, -17.309669520314646 ], [ 34.897518043544963, -17.309868155299348 ], [ 34.897503853964437, -17.310066348552152 ], [ 34.897478885431291, -17.310263556840273 ], [ 34.897443206347809, -17.310459239628845 ], [ 34.897396914473845, -17.310652860562591 ], [ 34.897340136659309, -17.310843888935889 ], [ 34.897273028496706, -17.311031801147418 ], [ 34.897195773895142, -17.311216082135513 ], [ 34.897108584576472, -17.311396226789807 ], [ 34.89701169949538, -17.311571741335957 ], [ 34.896905384184613, -17.311742144689092 ], [ 34.896789930027452, -17.311906969772526 ], [ 34.896665653459273, -17.312065764798231 ], [ 34.896532895100407, -17.312218094505198 ], [ 34.896392018822652, -17.312363541352671 ], [ 34.896243410752028, -17.312501706664793 ], [ 34.896087478210511, -17.312632211723482 ], [ 34.895924648599603, -17.312754698806646 ], [ 34.895755368228862, -17.312868832168913 ], [ 34.895580101092513, -17.312974298962004 ], [ 34.895399327597637, -17.313070810092483 ], [ 34.89521354324723, -17.313158101014249 ], [ 34.895023257281863, -17.313235932453882 ], [ 34.894828991283774, -17.313304091066559 ], [ 34.894631277746932, -17.313362390021041 ], [ 34.89443065861731, -17.313410669511875 ], [ 34.894227683807067, -17.313448797197559 ], [ 34.894135431768206, -17.313461353424515 ], [ 34.893567231271753, -17.309696371939275 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.5", "sub_field": "4.5D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.893769553711472, -17.316586264213559 ], [ 34.892115685045027, -17.314291416092694 ], [ 34.892195700790658, -17.314237469887129 ], [ 34.892325067135602, -17.314159627332327 ], [ 34.892458497355165, -17.314088394803694 ], [ 34.892595625734742, -17.314023967536052 ], [ 34.892736076425152, -17.313966522111834 ], [ 34.892879464472585, -17.313916215977194 ], [ 34.893025396873647, -17.313873187010579 ], [ 34.893173473652432, -17.313837553144793 ], [ 34.893323288956616, -17.313809412043945 ], [ 34.893474432169747, -17.313788840835716 ], [ 34.893626489036507, -17.3137758959001 ], [ 34.893779042797945, -17.313770612714848 ], [ 34.89393167533364, -17.313773005758303 ], [ 34.894083968307477, -17.313783068469746 ], [ 34.894235504314096, -17.313800773267371 ], [ 34.894385868022724, -17.313826071623883 ], [ 34.894534647315439, -17.313858894199562 ], [ 34.894681434416484, -17.313899151032253 ], [ 34.894825827009804, -17.313946731783918 ], [ 34.89496742934147, -17.314001506043073 ], [ 34.895105853304429, -17.314063323682145 ], [ 34.895240719501928, -17.31413201526895 ], [ 34.895371658287381, -17.314207392530999 ], [ 34.895498310777313, -17.314289248871496 ], [ 34.895620329835019, -17.314377359935495 ], [ 34.895737381021917, -17.3144714842248 ], [ 34.895849143514049, -17.314571363759772 ], [ 34.895955310981499, -17.31467672478637 ], [ 34.896055592427977, -17.314787278526357 ], [ 34.896149712988276, -17.314902721968778 ], [ 34.896237414681828, -17.315022738700307 ], [ 34.896250865251233, -17.315043362242964 ], [ 34.893769553711472, -17.316586264213559 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.6", "sub_field": "4.6C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.90130835758503, -17.31122562620051 ], [ 34.899440097999843, -17.313485312578923 ], [ 34.8972189524941, -17.311647539775638 ], [ 34.897298142992156, -17.311556674113714 ], [ 34.897402122142452, -17.311449320113248 ], [ 34.897511807803241, -17.311347340442076 ], [ 34.897626899332451, -17.311251014610363 ], [ 34.897747081272875, -17.311160606631386 ], [ 34.897872024216817, -17.311076364297964 ], [ 34.898001385709044, -17.310998518503435 ], [ 34.898134811185194, -17.310927282608827 ], [ 34.898271934943708, -17.310862851858154 ], [ 34.898412381148042, -17.310805402843425 ], [ 34.898555764856667, -17.310755093020635 ], [ 34.898701693078088, -17.310712060278291 ], [ 34.898849765847828, -17.310676422559613 ], [ 34.898999577324581, -17.310648277539208 ], [ 34.899150716902355, -17.310627702355518 ], [ 34.89930277033578, -17.31061475339942 ], [ 34.899455320875333, -17.310609466159704 ], [ 34.899607950409418, -17.310611855125785 ], [ 34.899760240610121, -17.310621913748125 ], [ 34.899911774079676, -17.310639614456139 ], [ 34.900062135494302, -17.31066490873372 ], [ 34.900210912742331, -17.310697727252315 ], [ 34.900357698053639, -17.310737980060885 ], [ 34.900502089117012, -17.310785556832428 ], [ 34.90064369018279, -17.310840327166343 ], [ 34.900782113147358, -17.310902140945849 ], [ 34.900916978616706, -17.310970828749291 ], [ 34.901047916946233, -17.311046202314571 ], [ 34.901174569253754, -17.311128055055008 ], [ 34.901296588403042, -17.31121616262552 ], [ 34.90130835758503, -17.31122562620051 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.4", "sub_field": "4.4C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.900201998898332, -17.316577029381111 ], [ 34.901510659033576, -17.318761975282452 ], [ 34.89921991385566, -17.319939028684551 ], [ 34.89919319521254, -17.31989285674155 ], [ 34.899133472197327, -17.319775587027554 ], [ 34.899080220785628, -17.319655475739637 ], [ 34.899033586926492, -17.319532852100672 ], [ 34.898993698429557, -17.31940805221921 ], [ 34.898960664614833, -17.319281418168284 ], [ 34.898934576013126, -17.319153297047684 ], [ 34.898915504118094, -17.319024040032534 ], [ 34.898903501190432, -17.318894001410737 ], [ 34.898898600114762, -17.318763537611808 ], [ 34.89890081430967, -17.318633006229952 ], [ 34.898910137691097, -17.318502765043846 ], [ 34.898926544689147, -17.318373171036008 ], [ 34.898949990318393, -17.318244579414355 ], [ 34.898980410301313, -17.318117342638544 ], [ 34.899017721244583, -17.317991809453979 ], [ 34.899061820867914, -17.317868323935873 ], [ 34.89911258828446, -17.317747224546256 ], [ 34.899169884332323, -17.317628843206275 ], [ 34.899233551956158, -17.317513504386454 ], [ 34.899303416637665, -17.317401524217455 ], [ 34.899379286874172, -17.317293209623546 ], [ 34.899460954703535, -17.317188857481462 ], [ 34.899548196274232, -17.317088753806768 ], [ 34.899640772459016, -17.316993172969916 ], [ 34.899738429510414, -17.31690237694437 ], [ 34.899840899756157, -17.316816614588575 ], [ 34.899947902332954, -17.316736120963917 ], [ 34.900059143956319, -17.316661116690586 ], [ 34.900174319724371, -17.316591807342899 ], [ 34.900201998898332, -17.316577029381111 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.3", "sub_field": "4.3C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.895357321246472, -17.319337751577674 ], [ 34.896432140987109, -17.321371469783127 ], [ 34.894337657341168, -17.322447678138754 ], [ 34.894309541855741, -17.3223990902598 ], [ 34.89425566308077, -17.32229329109223 ], [ 34.894207622969382, -17.322184928461439 ], [ 34.894165553188593, -17.322074299386902 ], [ 34.894129569040125, -17.321961707099845 ], [ 34.894099769144624, -17.321847460212147 ], [ 34.894076235171276, -17.321731871870391 ], [ 34.894059031614177, -17.321615258897467 ], [ 34.89404820561564, -17.321497940924189 ], [ 34.894043786837109, -17.321380239513154 ], [ 34.894045787378019, -17.321262477277379 ], [ 34.894054201742712, -17.321144976995981 ], [ 34.894069006855688, -17.321028060729436 ], [ 34.894090162124932, -17.32091204893689 ], [ 34.894117609553369, -17.320797259597772 ], [ 34.894151273897926, -17.320684007340216 ], [ 34.89419106287589, -17.320572602578771 ], [ 34.894236867418009, -17.32046335066353 ], [ 34.894288561967485, -17.32035655104325 ], [ 34.894346004824278, -17.320252496444606 ], [ 34.894409038533638, -17.320151472069909 ], [ 34.894477490317634, -17.320053754815419 ], [ 34.894551172548908, -17.319959612512406 ], [ 34.894629883264997, -17.319869303193162 ], [ 34.894713406721877, -17.319783074383764 ], [ 34.894801513985477, -17.319701162425709 ], [ 34.894893963559014, -17.319623791828143 ], [ 34.894990502045104, -17.319551174652602 ], [ 34.895090864840206, -17.319483509931821 ], [ 34.895194776859846, -17.319420983124225 ], [ 34.895301953292638, -17.319363765605722 ], [ 34.895357321246472, -17.319337751577674 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.1", "sub_field": "4.1C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.885846577829994, -17.324065205990067 ], [ 34.886782334569538, -17.326739205117487 ], [ 34.883964047061163, -17.327599439232444 ], [ 34.883926891813566, -17.3274831654618 ], [ 34.883889797650568, -17.327340927904032 ], [ 34.883860504808354, -17.3271970206277 ], [ 34.883839093559693, -17.327051838077736 ], [ 34.883825622573575, -17.326905778193673 ], [ 34.88382012875465, -17.326759241318918 ], [ 34.883822627142258, -17.326612629103337 ], [ 34.883833110869425, -17.326466343402387 ], [ 34.883851551181877, -17.326320785175589 ], [ 34.883877897517046, -17.326176353387591 ], [ 34.88391207764294, -17.326033443914554 ], [ 34.883953997856267, -17.325892448459154 ], [ 34.884003543239437, -17.325753753476985 ], [ 34.884060577975809, -17.32561773911727 ], [ 34.884124945722114, -17.325484778181053 ], [ 34.884196470037082, -17.325355235099337 ], [ 34.884274954865283, -17.325229464934313 ], [ 34.884360185074598, -17.325107812406213 ], [ 34.884451927046008, -17.324990610948547 ], [ 34.884549929313991, -17.324878181794265 ], [ 34.884653923255925, -17.324770833095336 ], [ 34.884763623828356, -17.324668859078276 ], [ 34.884878730348319, -17.324572539237757 ], [ 34.884998927317575, -17.324482137570598 ], [ 34.885123885287243, -17.324397901852347 ], [ 34.885253261760887, -17.324320062958176 ], [ 34.885386702133168, -17.324248834230222 ], [ 34.885523840661676, -17.324184410892851 ], [ 34.885664301469411, -17.324126969517753 ], [ 34.885807699574862, -17.324076667539991 ], [ 34.885846577829994, -17.324065205990067 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.2", "sub_field": "4.2D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.892592473294087, -17.324940604483569 ], [ 34.890807409243124, -17.32342733014784 ], [ 34.890881858516011, -17.32335047371787 ], [ 34.890969969192497, -17.323268563141767 ], [ 34.891062422186693, -17.323191194008754 ], [ 34.891158964091794, -17.32311857837632 ], [ 34.891259330294872, -17.32305091527299 ], [ 34.891363245702173, -17.322988390152755 ], [ 34.891470425492983, -17.322931174386909 ], [ 34.891580575900392, -17.322879424794326 ], [ 34.891693395016318, -17.322833283211736 ], [ 34.891808573619024, -17.322792876105019 ], [ 34.891925796020537, -17.322758314222579 ], [ 34.892044740931865, -17.322729692291919 ], [ 34.892165082343496, -17.322707088759937 ], [ 34.892286490418904, -17.32269056557811 ], [ 34.892408632398393, -17.32268016803253 ], [ 34.89253117351118, -17.322675924619965 ], [ 34.892653777892725, -17.322677846969675 ], [ 34.892776109505235, -17.322685929811669 ], [ 34.892897833058612, -17.322700150991011 ], [ 34.893018614929247, -17.322720471528676 ], [ 34.893138124074405, -17.322746835728307 ], [ 34.893256032939391, -17.322779171328932 ], [ 34.893372018355308, -17.322817389702962 ], [ 34.893485762424611, -17.322861386099067 ], [ 34.893596953392439, -17.322911039929352 ], [ 34.89370528650096, -17.322966215099775 ], [ 34.893810464824533, -17.323026760383154 ], [ 34.893912200083612, -17.3230925098336 ], [ 34.894010213434655, -17.323163283241353 ], [ 34.894104236234512, -17.323238886626648 ], [ 34.894194010776616, -17.323319112771372 ], [ 34.89423852225147, -17.323363284353789 ], [ 34.892592473294087, -17.324940604483569 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.3", "sub_field": "3.3C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.900331858463275, -17.322163720229909 ], [ 34.900551100160158, -17.32491517010353 ], [ 34.897565743794004, -17.325142207305205 ], [ 34.89756504241015, -17.325134608564934 ], [ 34.89755953787251, -17.324988073109029 ], [ 34.897562025399424, -17.324841461768788 ], [ 34.897572498153679, -17.324695176397274 ], [ 34.897590927411045, -17.324549617953146 ], [ 34.897617262639329, -17.324405185401652 ], [ 34.897651431636909, -17.324262274621056 ], [ 34.897693340730996, -17.324121277317641 ], [ 34.897742875034517, -17.323982579952059 ], [ 34.897799898761171, -17.323846562680117 ], [ 34.897864255597824, -17.32371359831081 ], [ 34.897935769133142, -17.323584051284573 ], [ 34.898014243341194, -17.323458276674447 ], [ 34.898099463118982, -17.323336619212824 ], [ 34.898191194876041, -17.323219412346742 ], [ 34.898289187174854, -17.323106977324002 ], [ 34.898393171420096, -17.322999622312651 ], [ 34.898502862594867, -17.322897641556505 ], [ 34.89861796004196, -17.322801314568704 ], [ 34.898738148287975, -17.322710905365678 ], [ 34.898863097907935, -17.322626661743598 ], [ 34.898992466428268, -17.322548814599326 ], [ 34.899125899265393, -17.322477577297583 ], [ 34.89926303069759, -17.322413145086237 ], [ 34.899403484867264, -17.322355694561299 ], [ 34.899546876811073, -17.322305383182883 ], [ 34.89969281351496, -17.322262348843783 ], [ 34.899840894991208, -17.322226709491549 ], [ 34.899990715374656, -17.322198562805294 ], [ 34.900141864034907, -17.322177985928029 ], [ 34.900293926701728, -17.322165035255239 ], [ 34.900331858463275, -17.322163720229909 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.2", "sub_field": "3.2C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.907099870341185, -17.322607987848503 ], [ 34.906021160934294, -17.32453810278799 ], [ 34.903896908377526, -17.323502846894009 ], [ 34.903917242324283, -17.323466009089106 ], [ 34.903980271340309, -17.323364982123337 ], [ 34.904048718674474, -17.323267261990232 ], [ 34.904122396711607, -17.323173116528974 ], [ 34.904201103500704, -17.323082803780501 ], [ 34.904284623308548, -17.322996571280285 ], [ 34.904372727211005, -17.322914655379957 ], [ 34.90446517372056, -17.32283728059944 ], [ 34.90456170944816, -17.32276465901176 ], [ 34.904662069797851, -17.322696989661715 ], [ 34.904765979691838, -17.322634458020435 ], [ 34.904873154324569, -17.322577235477077 ], [ 34.90498329994324, -17.322525478869093 ], [ 34.905096114652899, -17.322479330052449 ], [ 34.905211289243852, -17.322438915512812 ], [ 34.905328508039148, -17.322404346018985 ], [ 34.905447449759642, -17.322375716319311 ], [ 34.905567788404575, -17.322353104881994 ], [ 34.905689194144998, -17.322336573680126 ], [ 34.905811334227622, -17.322326168021803 ], [ 34.905933873886852, -17.322321916426024 ], [ 34.906056477262126, -17.322323830544466 ], [ 34.90617880831843, -17.32233190512968 ], [ 34.906300531767172, -17.322346118049349 ], [ 34.906421313985021, -17.322366430347106 ], [ 34.906540823928317, -17.322392786349138 ], [ 34.906658734040178, -17.322425113816962 ], [ 34.906774721148288, -17.322463324145243 ], [ 34.90688846735047, -17.32250731260471 ], [ 34.906999660886036, -17.322556958629232 ], [ 34.907099870341185, -17.322607987848503 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.1", "sub_field": "3.1D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.911721763444021, -17.327270799151147 ], [ 34.909685584378018, -17.325148690849765 ], [ 34.909715691499358, -17.325120697051958 ], [ 34.90983078373403, -17.325024364110629 ], [ 34.909950967164427, -17.324933948628292 ], [ 34.910075912378765, -17.32484969841833 ], [ 34.910205276915569, -17.324771844395649 ], [ 34.910338706202126, -17.3247005999438 ], [ 34.910475834526437, -17.324636160330265 ], [ 34.910616286039428, -17.324578702171344 ], [ 34.910759675785044, -17.324528382948049 ], [ 34.910905610755215, -17.32448534057465 ], [ 34.911053690967016, -17.324449693020743 ], [ 34.911203510558707, -17.324421537987895 ], [ 34.911354658902077, -17.324400952641973 ], [ 34.911506721727747, -17.324387993401679 ], [ 34.911659282260466, -17.324382695783971 ], [ 34.911811922361267, -17.324385074306729 ], [ 34.911964223673273, -17.324395122449015 ], [ 34.912115768768309, -17.324412812668925 ], [ 34.912266142290733, -17.32443809647912 ], [ 34.912414932095714, -17.324470904579723 ], [ 34.912561730378727, -17.324511147048252 ], [ 34.912706134793041, -17.324558713586011 ], [ 34.912847749552412, -17.32461347382046 ], [ 34.91298618651571, -17.324675277662489 ], [ 34.913121066250604, -17.32474395571769 ], [ 34.913252019073362, -17.32481931975067 ], [ 34.913378686062146, -17.324901163200888 ], [ 34.91350072004051, -17.32498926174873 ], [ 34.913617786528953, -17.325083373930308 ], [ 34.913729564661651, -17.325183241799166 ], [ 34.913835748065821, -17.325288591633232 ], [ 34.913936045701512, -17.325399134684982 ], [ 34.914030182659175, -17.325514567972732 ], [ 34.914031362474837, -17.325516182075784 ], [ 34.911721763444021, -17.327270799151147 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.4", "sub_field": "2.4D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.896270521422032, -17.345778057340958 ], [ 34.894633955948727, -17.344108401435214 ], [ 34.894659373611468, -17.344084773603896 ], [ 34.894751834385303, -17.344007402050345 ], [ 34.894848384675079, -17.343934783858305 ], [ 34.894948759844887, -17.343867118063262 ], [ 34.895052684776374, -17.343804590126624 ], [ 34.895159874622749, -17.343747371427341 ], [ 34.895270035589526, -17.343695618792371 ], [ 34.895382865739712, -17.343649474066748 ], [ 34.895498055821321, -17.343609063724955 ], [ 34.895615290114932, -17.343574498524291 ], [ 34.895734247298968, -17.343545873201311 ], [ 34.895854601330292, -17.343523266212244 ], [ 34.895976022337791, -17.343506739517984 ], [ 34.896098177526369, -17.343496338414269 ], [ 34.896220732089027, -17.343492091407629 ], [ 34.896343350124383, -17.343494010137192 ], [ 34.896465695557261, -17.343502089342831 ], [ 34.896587433059651, -17.343516306879611 ], [ 34.896708228969764, -17.343536623778476 ], [ 34.896827752206391, -17.343562984353067 ], [ 34.896945675176234, -17.343595316352321 ], [ 34.897061674671747, -17.343633531158535 ], [ 34.897175432756811, -17.343677524030166 ], [ 34.897286637638175, -17.34372717438897 ], [ 34.897394984519835, -17.34378234615043 ], [ 34.897500176438477, -17.343842888096699 ], [ 34.897601925077247, -17.343908634291068 ], [ 34.897699951556, -17.343979404532686 ], [ 34.89779398719557, -17.344055004850485 ], [ 34.89788377425419, -17.344135228034745 ], [ 34.897969066633934, -17.344219854204994 ], [ 34.898018534309401, -17.34427437726421 ], [ 34.896270521422032, -17.345778057340958 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.3", "sub_field": "2.3C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.8993698935897, -17.3404132919784 ], [ 34.899977306418968, -17.342598676400389 ], [ 34.897632654475345, -17.343156624592137 ], [ 34.897616064985897, -17.343093039404593 ], [ 34.897592524699924, -17.342977452198006 ], [ 34.897575315514821, -17.342860840179966 ], [ 34.897564484588315, -17.342743522978669 ], [ 34.897560061595343, -17.342625822154599 ], [ 34.897562058646876, -17.342508060319165 ], [ 34.897570470256781, -17.342390560250386 ], [ 34.897585273357109, -17.34227364400817 ], [ 34.89760642736136, -17.342157632051599 ], [ 34.897633874275868, -17.34204284236052 ], [ 34.897667538859004, -17.341929589564067 ], [ 34.897707328827337, -17.341818184078249 ], [ 34.8977531351089, -17.341708931255134 ], [ 34.897804832142121, -17.341602130545965 ], [ 34.89786227822016, -17.341498074680427 ], [ 34.89792531587937, -17.341397048864266 ], [ 34.89799377233102, -17.341299329997696 ], [ 34.898067459934943, -17.341205185916401 ], [ 34.898146176713922, -17.341114874657549 ], [ 34.898229706907351, -17.341028643752509 ], [ 34.898317821562657, -17.34094672954852 ], [ 34.898410279162817, -17.340869356560894 ], [ 34.898506826288433, -17.340796736857698 ], [ 34.89860719831227, -17.340729069478559 ], [ 34.898711120124595, -17.340666539889227 ], [ 34.898818306887151, -17.340609319473213 ], [ 34.898928464813913, -17.340557565062127 ], [ 34.899041291976218, -17.34051141850593 ], [ 34.899156479130262, -17.34047100628408 ], [ 34.899273710564628, -17.340436439159028 ], [ 34.8993698935897, -17.3404132919784 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.2", "sub_field": "2.2C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.906533452371185, -17.338050305737823 ], [ 34.909113153334729, -17.34030905206204 ], [ 34.906860112803329, -17.342790160574449 ], [ 34.906785146846069, -17.342729896748654 ], [ 34.90665424850053, -17.342612951146734 ], [ 34.906529902534977, -17.342489586312865 ], [ 34.906412449774884, -17.342360140393918 ], [ 34.906302212149164, -17.34222496820469 ], [ 34.906199491807826, -17.342084440255348 ], [ 34.906104570293813, -17.341938941735673 ], [ 34.906017707771433, -17.341788871459226 ], [ 34.905939142313287, -17.341634640770003 ], [ 34.905869089247915, -17.341476672414917 ], [ 34.905807740569742, -17.341315399384907 ], [ 34.905755264412988, -17.341151263728026 ], [ 34.905711804591036, -17.340984715337761 ], [ 34.905677480202478, -17.340816210719719 ], [ 34.90565238530489, -17.340646211740392 ], [ 34.905636588657302, -17.340475184361022 ], [ 34.905630133532, -17.340303597360474 ], [ 34.905633037596203, -17.340131921050247 ], [ 34.905645292863881, -17.339960625985302 ], [ 34.905666865717976, -17.339790181674402 ], [ 34.905697697002793, -17.339621055293083 ], [ 34.905737702186485, -17.339453710403209 ], [ 34.905786771592958, -17.339288605682437 ], [ 34.905844770702799, -17.339126193667006 ], [ 34.905911540522254, -17.338966919511417 ], [ 34.905986898019179, -17.338811219768395 ], [ 34.906070636625088, -17.338659521192326 ], [ 34.906162526801396, -17.338512239569724 ], [ 34.906262316668872, -17.338369778579619 ], [ 34.90636973269811, -17.338232528687239 ], [ 34.906484480459412, -17.338100866073905 ], [ 34.906533452371185, -17.338050305737823 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.1", "sub_field": "2.1D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.913379761071603, -17.335625170111705 ], [ 34.911437392245574, -17.333407717834426 ], [ 34.911460462998669, -17.333388407832093 ], [ 34.911580650515376, -17.333297990941542 ], [ 34.911705600115177, -17.333213739250976 ], [ 34.911834969324552, -17.33313588367934 ], [ 34.911968403558056, -17.333064637614456 ], [ 34.912105537090085, -17.333000196328197 ], [ 34.912245994057315, -17.332942736441435 ], [ 34.91238938948873, -17.33289241543994 ], [ 34.912535330360718, -17.332849371242844 ], [ 34.912683416674128, -17.332813721824717 ], [ 34.912833242550533, -17.332785564892241 ], [ 34.912984397344523, -17.332764977616499 ], [ 34.913136466769025, -17.332752016421484 ], [ 34.913289034030726, -17.33274671682949 ], [ 34.913441680972198, -17.332749093363816 ], [ 34.913593989217865, -17.332759139508962 ], [ 34.91374554132053, -17.332776827728509 ], [ 34.913895921905365, -17.332802109540605 ], [ 34.914044718808199, -17.332834915650842 ], [ 34.914191524205023, -17.332875156142183 ], [ 34.914335935729667, -17.332922720721363 ], [ 34.914477557576397, -17.332977479021213 ], [ 34.914616001584655, -17.333039280957927 ], [ 34.914750888302791, -17.333107957142325 ], [ 34.914881848028003, -17.333183319344126 ], [ 34.915008521819459, -17.333265161007834 ], [ 34.915130562482133, -17.333353257818729 ], [ 34.915247635518178, -17.333447368317675 ], [ 34.915359420043842, -17.33354723456284 ], [ 34.915465609668836, -17.333652582836624 ], [ 34.915518445366438, -17.33371081141938 ], [ 34.913379761071603, -17.335625170111705 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.5", "sub_field": "2.5B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.920240082361659, -17.336946735747041 ], [ 34.922942052048946, -17.33414882953203 ], [ 34.923015700072284, -17.334214619847671 ], [ 34.92315957577275, -17.334357343654762 ], [ 34.923295477304066, -17.334507103771536 ], [ 34.923423032168401, -17.33466348973268 ], [ 34.923541890741852, -17.334826072912001 ], [ 34.923651727232865, -17.334994407696968 ], [ 34.923752240575283, -17.335168032709991 ], [ 34.923843155253664, -17.335346472072818 ], [ 34.923924222058574, -17.335529236710677 ], [ 34.92399521876996, -17.335715825692713 ], [ 34.924055950766373, -17.335905727604761 ], [ 34.924106251558761, -17.336098421951011 ], [ 34.924145983247044, -17.336293380580464 ], [ 34.924175036898419, -17.336490069134513 ], [ 34.924193332846301, -17.336687948511432 ], [ 34.924200820909014, -17.336886476343878 ], [ 34.924197480527702, -17.337085108485553 ], [ 34.924183320823069, -17.337283300502477 ], [ 34.924158380570788, -17.337480509165285 ], [ 34.924122728095575, -17.337676193938162 ], [ 34.924076461084262, -17.337869818460412 ], [ 34.924019706318504, -17.338060852016596 ], [ 34.923952619327601, -17.338248770991257 ], [ 34.923875383962496, -17.338433060304176 ], [ 34.923788211892266, -17.338613214822228 ], [ 34.923691342024206, -17.338788740744029 ], [ 34.923585039849272, -17.338959156953514 ], [ 34.923469596714675, -17.339123996338795 ], [ 34.923345329025501, -17.339282807072621 ], [ 34.923212577377683, -17.339435153850914 ], [ 34.923199307657669, -17.339448856263918 ], [ 34.920240082361659, -17.336946735747041 ] ] ] } },
{ "type": "Feature", "properties": { "field": "6.1", "sub_field": "6.1B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.926975301324241, -17.341756277068452 ], [ 34.929874621707043, -17.3387198647094 ], [ 34.929976827889384, -17.338811156788797 ], [ 34.930134758291622, -17.338967806549938 ], [ 34.930283936250547, -17.339132179782727 ], [ 34.930423952878314, -17.339303825971783 ], [ 34.930554424393229, -17.339482274667148 ], [ 34.930674993171756, -17.339667036773445 ], [ 34.930785328728838, -17.339857605890288 ], [ 34.930885128623856, -17.340053459700087 ], [ 34.930974119289864, -17.340254061399367 ], [ 34.931052056783614, -17.340458861170085 ], [ 34.931118727454496, -17.340667297686302 ], [ 34.931173948530414, -17.340878799652643 ], [ 34.931217568619211, -17.341092787369995 ], [ 34.93124946812388, -17.341308674324267 ], [ 34.931269559570865, -17.34152586879387 ], [ 34.931277787850235, -17.341743775471489 ], [ 34.931274130367186, -17.341961797095689 ], [ 34.931258597104346, -17.342179336087895 ], [ 34.931231230595053, -17.342395796190306 ], [ 34.931192105807014, -17.342610584100129 ], [ 34.931141329937518, -17.342823111095818 ], [ 34.93107904211984, -17.343032794650775 ], [ 34.93100541304252, -17.343239060030005 ], [ 34.930920644481787, -17.343441341865585 ], [ 34.930824968748979, -17.34363908570629 ], [ 34.930718648054125, -17.343831749537522 ], [ 34.930601973787617, -17.344018805267019 ], [ 34.930475265721768, -17.344199740172474 ], [ 34.930338871134673, -17.344374058307057 ], [ 34.930206135800262, -17.344526394378821 ], [ 34.926975301324241, -17.341756277068452 ] ] ] } },
{ "type": "Feature", "properties": { "field": "6.2", "sub_field": "6.2C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.930918907739319, -17.350619195960071 ], [ 34.933686532028823, -17.34741850738671 ], [ 34.936891437505039, -17.35009147006711 ], [ 34.936870614319417, -17.350115369252237 ], [ 34.936715995799332, -17.350275044792941 ], [ 34.936552887205806, -17.350426730058501 ], [ 34.936381735605124, -17.350570009270172 ], [ 34.936203010113339, -17.350704489689271 ], [ 34.936017200610422, -17.35082980269403 ], [ 34.93582481639752, -17.350945604790095 ], [ 34.935626384800806, -17.351051578552209 ], [ 34.935422449725991, -17.351147433494578 ], [ 34.935213570167356, -17.351232906867171 ], [ 34.935000318675286, -17.351307764376163 ], [ 34.934783279786686, -17.351371800826296 ], [ 34.934563048422454, -17.351424840683471 ], [ 34.93434022825658, -17.351466738556034 ], [ 34.934115430061013, -17.351497379593461 ], [ 34.933889270031244, -17.351516679801193 ], [ 34.933662368096904, -17.351524586271111 ], [ 34.933435346222147, -17.351521077326474 ], [ 34.933208826700387, -17.351506162581522 ], [ 34.932983430448253, -17.351479882915079 ], [ 34.932759775303161, -17.351442310358582 ], [ 34.932538474329434, -17.351393547898589 ], [ 34.932320134137491, -17.351333729194508 ], [ 34.932105353220734, -17.351263018212201 ], [ 34.931894720314659, -17.351181608774432 ], [ 34.931688812782838, -17.351089724029563 ], [ 34.931488195034035, -17.350987615839795 ], [ 34.931293416974796, -17.350875564090668 ], [ 34.931105012501995, -17.350753875923818 ], [ 34.930923498039114, -17.350622884894868 ], [ 34.930918907739319, -17.350619195960071 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.8", "sub_field": "1.8B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.961161225337996, -17.342695153682683 ], [ 34.963463180156758, -17.345951999810669 ], [ 34.963432746238198, -17.345972531590462 ], [ 34.963250401228407, -17.34608232777347 ], [ 34.963062322565051, -17.346182808203306 ], [ 34.962869025775234, -17.346273697452876 ], [ 34.962671040692122, -17.346354746384645 ], [ 34.962468910002343, -17.346425732833538 ], [ 34.962263187758481, -17.346486462216212 ], [ 34.962054437859955, -17.346536768064457 ], [ 34.961843232507221, -17.346576512481615 ], [ 34.961630150633034, -17.34660558652071 ], [ 34.961415776315228, -17.346623910483206 ], [ 34.961200697175478, -17.346631434137443 ], [ 34.960985502768224, -17.346628136856513 ], [ 34.960770782964332, -17.346614027674772 ], [ 34.960557126333896, -17.346589145263163 ], [ 34.960345118532622, -17.346553557823199 ], [ 34.960135340696091, -17.346507362900038 ], [ 34.959928367846587, -17.346450687115105 ], [ 34.959724767316587, -17.346383685818981 ], [ 34.959525097193278, -17.346306542665452 ], [ 34.959329904788703, -17.346219469108206 ], [ 34.959139725139103, -17.34612270382096 ], [ 34.958955079538242, -17.346016512043306 ], [ 34.958776474108255, -17.345901184853453 ], [ 34.958604398412149, -17.345777038370297 ], [ 34.958439324111843, -17.345644412886852 ], [ 34.958281703675084, -17.345503671937216 ], [ 34.958131969135259, -17.345355201300144 ], [ 34.957990530907111, -17.345199407941248 ], [ 34.95785777666174, -17.345036718897553 ], [ 34.957734070264131, -17.344867580106648 ], [ 34.957665162047512, -17.344762020273741 ], [ 34.961161225337996, -17.342695153682683 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.6", "sub_field": "1.6C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.978354668011164, -17.346604682049772 ], [ 34.977997053286906, -17.349855632180109 ], [ 34.977869135434879, -17.349840746730006 ], [ 34.977691568440811, -17.349810957379173 ], [ 34.977515868251459, -17.349772283910664 ], [ 34.977342516473946, -17.349724832328775 ], [ 34.977171988277505, -17.349668732699339 ], [ 34.977004751090774, -17.349604138793072 ], [ 34.976841263320381, -17.349531227664105 ], [ 34.976681973094188, -17.349450199164586 ], [ 34.976527317032911, -17.349361275396888 ], [ 34.97637771905309, -17.34926470010463 ], [ 34.976233589205009, -17.349160738004564 ], [ 34.976095322548673, -17.349049674060865 ], [ 34.975963298070845, -17.348931812703938 ], [ 34.975837877646171, -17.348807476995859 ], [ 34.97571940504529, -17.348677007744797 ], [ 34.97560820499254, -17.348540762570682 ], [ 34.975504582275924, -17.34839911492486 ], [ 34.975408820911731, -17.348252453066394 ], [ 34.975321183366191, -17.348101178997709 ], [ 34.9752419098361, -17.347945707362602 ], [ 34.975171217590642, -17.347786464309539 ], [ 34.975109300375976, -17.347623886323589 ], [ 34.975056327884417, -17.347458419029859 ], [ 34.975012445289508, -17.347290515971977 ], [ 34.974977772848305, -17.34712063736885 ], [ 34.974952405572012, -17.346949248853143 ], [ 34.974936412965867, -17.346776820194972 ], [ 34.974929838838861, -17.346603824014135 ], [ 34.974932701183953, -17.346430734484802 ], [ 34.974944992128989, -17.346258026035596 ], [ 34.974946239091686, -17.346248144213522 ], [ 34.978354668011164, -17.346604682049772 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.5", "sub_field": "1.5C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.981679444479695, -17.339111001077271 ], [ 34.981414279389675, -17.341507128753143 ], [ 34.979069772171925, -17.341221754105334 ], [ 34.97907218780167, -17.341187800312291 ], [ 34.979087294041641, -17.341068065924567 ], [ 34.979108904300226, -17.340949254926347 ], [ 34.979136959332656, -17.340831692969534 ], [ 34.979171382229808, -17.340715702281784 ], [ 34.979212078629267, -17.340601600783437 ], [ 34.979258936974006, -17.340489701216075 ], [ 34.979311828818311, -17.340380310285358 ], [ 34.979370609179981, -17.340273727820453 ], [ 34.979435116937729, -17.340170245952184 ], [ 34.979505175273005, -17.340070148312417 ], [ 34.979580592154655, -17.339973709256714 ], [ 34.979661160865334, -17.339881193112319 ], [ 34.979746660568168, -17.339792853453819 ], [ 34.979836856912151, -17.339708932408108 ], [ 34.979931502674376, -17.33962965999083 ], [ 34.9800303384378, -17.339555253475954 ], [ 34.980133093302214, -17.339485916800367 ], [ 34.980239485626747, -17.339421840004896 ], [ 34.980349223801831, -17.339363198713549 ], [ 34.980462007048374, -17.339310153652161 ], [ 34.980577526242143, -17.339262850207973 ], [ 34.980695464761006, -17.339221418031112 ], [ 34.98081549935263, -17.339185970679388 ], [ 34.980937301020425, -17.339156605307025 ], [ 34.981060535925209, -17.339133402398442 ], [ 34.981184866300069, -17.339116425547651 ], [ 34.981309951376097, -17.339105721284096 ], [ 34.981435448316205, -17.339101318945044 ], [ 34.98156101315476, -17.339103230595249 ], [ 34.981679444479695, -17.339111001077271 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.4", "sub_field": "1.4B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.975949718590073, -17.343237636498074 ], [ 34.976297118077937, -17.340867839790402 ], [ 34.978926265695563, -17.341206981511462 ], [ 34.978913049912912, -17.341279639946706 ], [ 34.978882794231374, -17.341406422427387 ], [ 34.978845671151007, -17.341531510388236 ], [ 34.978801782409981, -17.341654560969456 ], [ 34.978751248291495, -17.341775236894811 ], [ 34.978694207294154, -17.341893207396165 ], [ 34.978630815752453, -17.342008149120165 ], [ 34.978561247408479, -17.342119747014557 ], [ 34.978485692935763, -17.342227695191749 ], [ 34.978404359416693, -17.342331697767307 ], [ 34.978317469775135, -17.342431469671059 ], [ 34.978225262165303, -17.342526737428432 ], [ 34.978127989319184, -17.342617239910208 ], [ 34.97802591785377, -17.342702729048238 ], [ 34.97791932754032, -17.34278297051554 ], [ 34.977808510537486, -17.342857744368597 ], [ 34.977693770590584, -17.34292684565029 ], [ 34.97757542219891, -17.342990084951776 ], [ 34.977453789753753, -17.343047288931722 ], [ 34.977329206649109, -17.343098300791471 ], [ 34.977202014367826, -17.343142980704894 ], [ 34.977072561545548, -17.343181206201727 ], [ 34.976941203014952, -17.343212872503312 ], [ 34.97680829883312, -17.343237892809821 ], [ 34.97667421329443, -17.343256198538253 ], [ 34.976539313931994, -17.343267739510448 ], [ 34.976403970510042, -17.34327248409063 ], [ 34.976268554010332, -17.343270419272212 ], [ 34.976133435615104, -17.343261550713425 ], [ 34.975998985689543, -17.343245902721812 ], [ 34.975949718590073, -17.343237636498074 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.2", "sub_field": "1.2D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.976816384818314, -17.338238223714697 ], [ 34.976762738159529, -17.338203588832894 ], [ 34.976658388456634, -17.338128315241242 ], [ 34.976558283695852, -17.3380478996406 ], [ 34.976462698261315, -17.337962562450763 ], [ 34.976371894148514, -17.33787253758145 ], [ 34.976286120246264, -17.337778071791206 ], [ 34.976205611654429, -17.337679424010897 ], [ 34.97613058903957, -17.337576864633927 ], [ 34.976061258030121, -17.337470674775066 ], [ 34.975997808652856, -17.337361145499887 ], [ 34.975940414812008, -17.337248577026841 ], [ 34.975889233812737, -17.337133277904332 ], [ 34.975844405930061, -17.337015564164975 ], [ 34.97580605402436, -17.336895758459242 ], [ 34.97577428320492, -17.336774189171134 ], [ 34.975749180541747, -17.336651189517951 ], [ 34.975730814827195, -17.336527096636967 ], [ 34.975719236387441, -17.336402250661358 ], [ 34.975714476944766, -17.336276993787809 ], [ 34.975716549530709, -17.336151669338609 ], [ 34.975725448450497, -17.336026620820551 ], [ 34.975741149298841, -17.33590219098344 ], [ 34.975763609026941, -17.335778720880647 ], [ 34.975792766060671, -17.335656548934217 ], [ 34.975828540469436, -17.335536010007385 ], [ 34.975870834185486, -17.335417434486722 ], [ 34.975919531272815, -17.335301147376583 ], [ 34.975974498245002, -17.335187467408328 ], [ 34.976035584431322, -17.335076706166753 ], [ 34.976102622389732, -17.334969167236075 ], [ 34.976145471591806, -17.334907946244652 ], [ 34.97820349148671, -17.336307953800048 ], [ 34.976973009965533, -17.338311269909727 ], [ 34.976816384818314, -17.338238223714697 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.7", "sub_field": "1.7C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.965141978743468, -17.34115131321493 ], [ 34.96697037363257, -17.340380748223968 ], [ 34.967862993493888, -17.342116599326115 ], [ 34.967767121182092, -17.342155850020827 ], [ 34.967665828053995, -17.342191426810356 ], [ 34.967562734991439, -17.342221863436112 ], [ 34.967458124572644, -17.342247076470123 ], [ 34.967352283535391, -17.342266996802234 ], [ 34.96724550199103, -17.342281569829609 ], [ 34.967138072629211, -17.342290755606424 ], [ 34.967030289915506, -17.342294528953374 ], [ 34.966922449284276, -17.342292879526671 ], [ 34.966814846328695, -17.342285811846477 ], [ 34.966707775990557, -17.342273345284447 ], [ 34.966601531751685, -17.342255514010727 ], [ 34.966496404829407, -17.342232366900181 ], [ 34.966392683378324, -17.342203967398536 ], [ 34.966290651700341, -17.342170393348415 ], [ 34.966190589465299, -17.342131736775904 ], [ 34.966092770944378, -17.342088103638435 ], [ 34.965997464258237, -17.342039613534183 ], [ 34.965904930642061, -17.341986399374331 ], [ 34.965815423729424, -17.341928607018694 ], [ 34.965729188857061, -17.34186639487589 ], [ 34.965646462392378, -17.341799933469115 ], [ 34.965567471085528, -17.341729404968714 ], [ 34.965492431447899, -17.341655002692814 ], [ 34.965421549158599, -17.341576930577368 ], [ 34.965355018500752, -17.34149540261722 ], [ 34.965293021828984, -17.34141064227941 ], [ 34.965235729069583, -17.341322881890694 ], [ 34.965183297254761, -17.341232362000632 ], [ 34.965141978743468, -17.34115131321493 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.3", "sub_field": "1.3B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.970773107481506, -17.335663616039604 ], [ 34.970354223381719, -17.338412629071641 ], [ 34.970344564689924, -17.338411504611443 ], [ 34.970194661483319, -17.338386347845049 ], [ 34.970046334609464, -17.338353690604716 ], [ 34.96989999063991, -17.338313622403852 ], [ 34.96975603071045, -17.338266253069662 ], [ 34.969614849421397, -17.338211712442192 ], [ 34.969476833755884, -17.338150150018279 ], [ 34.969342362018992, -17.338081734541827 ], [ 34.969211802800672, -17.338006653541218 ], [ 34.969085513965389, -17.337925112815231 ], [ 34.968963841671005, -17.337837335868841 ], [ 34.968847119420062, -17.337743563300634 ], [ 34.96873566714541, -17.337644052143105 ], [ 34.96862979033336, -17.337539075158222 ], [ 34.968529779186312, -17.337428920089575 ], [ 34.968435907827235, -17.337313888873631 ], [ 34.968348433548449, -17.3371942968121 ], [ 34.968267596106315, -17.337070471707587 ], [ 34.968193617064209, -17.336942752964909 ], [ 34.968126699185284, -17.336811490660896 ], [ 34.968067025876756, -17.336677044584633 ], [ 34.96801476068736, -17.336539783251222 ], [ 34.967970046859186, -17.33640008289165 ], [ 34.967933006935205, -17.336258326421515 ], [ 34.967903742423537, -17.336114902391344 ], [ 34.967882333519427, -17.335970203921576 ], [ 34.967868838885579, -17.335824627624998 ], [ 34.967863295491597, -17.335678572519573 ], [ 34.967865718512797, -17.335532438934763 ], [ 34.967876101288894, -17.335386627414191 ], [ 34.967894415342442, -17.335241537617812 ], [ 34.967900765229288, -17.335206638144747 ], [ 34.970773107481506, -17.335663616039604 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.1", "sub_field": "1.1D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.975967002903133, -17.329006613978088 ], [ 34.977225538507916, -17.327402417183986 ], [ 34.977276534416632, -17.327439206013974 ], [ 34.977359257505945, -17.327505662931092 ], [ 34.977438246093733, -17.327576187031788 ], [ 34.977513283679436, -17.327650585018809 ], [ 34.977584164590994, -17.327728652976958 ], [ 34.97765069454843, -17.327810176931923 ], [ 34.97771269119648, -17.327894933436831 ], [ 34.977769984604379, -17.327982690184498 ], [ 34.97782241773163, -17.328073206644273 ], [ 34.977869846858539, -17.328166234721127 ], [ 34.977912141980134, -17.328261519435703 ], [ 34.977949187162594, -17.328358799623132 ], [ 34.977980880861054, -17.328457808648796 ], [ 34.978007136197959, -17.328558275139187 ], [ 34.978027881201406, -17.328659923725571 ], [ 34.978043059002367, -17.328762475798857 ], [ 34.978052627990678, -17.328865650273102 ], [ 34.978056561929264, -17.328969164356007 ], [ 34.978054850026062, -17.329072734323933 ], [ 34.978047496963782, -17.329176076299639 ], [ 34.978034522887093, -17.329278907030307 ], [ 34.978015963347566, -17.329380944663903 ], [ 34.977991869206356, -17.329481909521785 ], [ 34.977962306494838, -17.329581524865198 ], [ 34.977927356233742, -17.329679517653869 ], [ 34.977887114211171, -17.329775619294388 ], [ 34.97784169072019, -17.329869566376452 ], [ 34.977791210256534, -17.32996110139484 ], [ 34.977735811177453, -17.330049973455242 ], [ 34.97768378767087, -17.330124305102657 ], [ 34.975967002903133, -17.329006613978088 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.9", "sub_field": "1.9D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.964728870983024, -17.336051561916904 ], [ 34.967428748672695, -17.335013069641263 ], [ 34.967469972171351, -17.335121335030792 ], [ 34.967514685216699, -17.335261035588786 ], [ 34.967551724543249, -17.335402792202579 ], [ 34.967580988613044, -17.335546216333061 ], [ 34.967602397198554, -17.335690914869808 ], [ 34.967615891602726, -17.335836491208521 ], [ 34.967621434820103, -17.335982546338016 ], [ 34.967619011638341, -17.336128679933918 ], [ 34.967608628680289, -17.336274491455832 ], [ 34.967590314385895, -17.336419581245202 ], [ 34.967564118934519, -17.33656355162071 ], [ 34.96753011410761, -17.336706007968349 ], [ 34.96748839309214, -17.336846559823023 ], [ 34.967439070225389, -17.336984821938778 ], [ 34.967382280681733, -17.337120415344799 ], [ 34.967318180102346, -17.337252968384213 ], [ 34.967246944168728, -17.337382117732766 ], [ 34.96716876812134, -17.337507509394726 ], [ 34.967083866224598, -17.33762879967334 ], [ 34.966992471179729, -17.337745656112833 ], [ 34.966894833487004, -17.337857758409786 ], [ 34.966791220759234, -17.337964799291147 ], [ 34.96668191698835, -17.338066485356531 ], [ 34.966567221766972, -17.338162537882528 ], [ 34.966447449467367, -17.338252693586735 ], [ 34.966322928379626, -17.338336705349501 ], [ 34.966193999811964, -17.338414342891376 ], [ 34.966061017155035, -17.33848539340439 ], [ 34.966003469280459, -17.338512454706869 ], [ 34.964728870983024, -17.336051561916904 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.10", "sub_field": "1.10D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.957010326871092, -17.336809457524954 ], [ 34.955242480798987, -17.334332157368866 ], [ 34.955350420930586, -17.334267165729322 ], [ 34.955492013053238, -17.334191523989539 ], [ 34.955637532828916, -17.334123103067494 ], [ 34.95578658140937, -17.3340620904909 ], [ 34.955938750276061, -17.334008653482151 ], [ 34.956093622359603, -17.333962938500228 ], [ 34.956250773182859, -17.333925070839225 ], [ 34.95640977202418, -17.333895154285123 ], [ 34.956570183097803, -17.33387327083128 ], [ 34.956731566748068, -17.333859480453818 ], [ 34.95689348065433, -17.333853820947308 ], [ 34.957055481043014, -17.333856307821161 ], [ 34.957217123903831, -17.333866934257205 ], [ 34.957377966206458, -17.333885671128321 ], [ 34.957537567114713, -17.333912467078409 ], [ 34.957695489194478, -17.333947248662994 ], [ 34.957851299612628, -17.333989920550646 ], [ 34.958004571322995, -17.334040365784176 ], [ 34.958154884236826, -17.334098446101205 ], [ 34.958301826373862, -17.33416400231307 ], [ 34.958444994991495, -17.334236854741064 ], [ 34.958583997688393, -17.334316803708923 ], [ 34.958718453479904, -17.334403630089952 ], [ 34.958847993842262, -17.334497095907654 ], [ 34.958972263722472, -17.334596944987808 ], [ 34.9590909225114, -17.33470290366062 ], [ 34.959203644977393, -17.33481468151064 ], [ 34.959310122157575, -17.33493197217275 ], [ 34.959410062204682, -17.335054454171708 ], [ 34.959503191187082, -17.335181791803198 ], [ 34.959540353269119, -17.335238722501 ], [ 34.957010326871092, -17.336809457524954 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.11", "sub_field": "1.11D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.960619385935466, -17.330692249633707 ], [ 34.961187884958441, -17.334400587778653 ], [ 34.961072028950504, -17.334416395801075 ], [ 34.960867969605161, -17.334433837419532 ], [ 34.960663239453304, -17.33444099772812 ], [ 34.960458399679979, -17.334437857096304 ], [ 34.96025401177112, -17.334424424129391 ], [ 34.960050635974234, -17.334400735644973 ], [ 34.959848829762414, -17.334366856572057 ], [ 34.959649146305892, -17.33432287977303 ], [ 34.959452132955583, -17.334268925789214 ], [ 34.959258329742404, -17.334205142510285 ], [ 34.959068267896775, -17.334131704768978 ], [ 34.958882468392225, -17.334048813861745 ], [ 34.958701440517132, -17.333956696996925 ], [ 34.958525680478566, -17.333855606671875 ], [ 34.958355670041925, -17.333745819980795 ], [ 34.958191875210261, -17.333627637855024 ], [ 34.958034744946829, -17.333501384238147 ], [ 34.957884709944366, -17.333367405197862 ], [ 34.957742181444438, -17.333226067977272 ], [ 34.957607550110296, -17.333077759988161 ], [ 34.957481184955945, -17.332922887748911 ], [ 34.957363432334795, -17.332761875770107 ], [ 34.957254614990347, -17.332595165390771 ], [ 34.957155031171695, -17.332423213568532 ], [ 34.957064953816122, -17.332246491626936 ], [ 34.956984629801269, -17.332065483963447 ], [ 34.956914279268503, -17.331880686721558 ], [ 34.956854095019878, -17.331692606430721 ], [ 34.956804241989921, -17.331501758617907 ], [ 34.956779145904193, -17.33137872104356 ], [ 34.960619385935466, -17.330692249633707 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.12", "sub_field": "1.12A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.955236330182871, -17.33431716598372 ], [ 34.953639390624843, -17.331963065750791 ], [ 34.956244881516085, -17.330425794331145 ], [ 34.956261331297341, -17.330450996469615 ], [ 34.956336256917439, -17.330580374246498 ], [ 34.956404030135602, -17.330713340954052 ], [ 34.956464465179891, -17.330849532148207 ], [ 34.956517396390062, -17.330988574546339 ], [ 34.956562678671851, -17.33113008705028 ], [ 34.95660018789475, -17.331273681790872 ], [ 34.956629821232468, -17.331418965190952 ], [ 34.956651497444895, -17.331565539044075 ], [ 34.95666515710095, -17.33171300160593 ], [ 34.956670762741744, -17.331860948695443 ], [ 34.956668298983416, -17.33200897480253 ], [ 34.956657772559488, -17.332156674199577 ], [ 34.956639212302683, -17.332303642053507 ], [ 34.956612669066011, -17.332449475535363 ], [ 34.956578215583725, -17.33259377492443 ], [ 34.956535946272076, -17.332736144703894 ], [ 34.95648597697074, -17.3328761946449 ], [ 34.95642844462553, -17.333013540876248 ], [ 34.956363506913185, -17.33314780693653 ], [ 34.956291341809404, -17.333278624806081 ], [ 34.956212147101148, -17.333405635915714 ], [ 34.956126139844635, -17.333528492129666 ], [ 34.956033555770553, -17.333646856699861 ], [ 34.955934648638085, -17.33376040518899 ], [ 34.955829689539371, -17.333868826359854 ], [ 34.955718966156532, -17.333971823028588 ], [ 34.95560278197329, -17.33406911287927 ], [ 34.955481455443014, -17.334160429237812 ], [ 34.955355319115959, -17.334245521803091 ], [ 34.955236330182871, -17.33431716598372 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.13", "sub_field": "1.13B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.954988844373986, -17.325074100205931 ], [ 34.955577723835631, -17.328974101137693 ], [ 34.955456933590519, -17.32899057899429 ], [ 34.955237672884792, -17.329009313455341 ], [ 34.955017691779858, -17.329017000371412 ], [ 34.95479759326777, -17.329013618667915 ], [ 34.954577980662954, -17.328999177610459 ], [ 34.954359455948037, -17.328973716779579 ], [ 34.95414261812347, -17.328937305962217 ], [ 34.9539280615653, -17.328890044960499 ], [ 34.953716374395555, -17.328832063318103 ], [ 34.953508136869821, -17.328763519965111 ], [ 34.953303919786507, -17.328684602782413 ], [ 34.953104282921842, -17.328595528086584 ], [ 34.952909773495264, -17.328496540036845 ], [ 34.952720924669258, -17.328387909965802 ], [ 34.952538254087635, -17.328269935635504 ], [ 34.95236226245656, -17.328142940421166 ], [ 34.952193432171818, -17.328007272424674 ], [ 34.952032225996575, -17.327863303520239 ], [ 34.95187908579274, -17.327711428334975 ], [ 34.951734431309859, -17.32755206316698 ], [ 34.951598659034502, -17.327385644844153 ], [ 34.951472141103615, -17.32721262952661 ], [ 34.951355224284541, -17.327033491456227 ], [ 34.951248229024607, -17.326848721656539 ], [ 34.951151448573079, -17.326658826586701 ], [ 34.951065148177435, -17.326464326753069 ], [ 34.950989564356654, -17.326265755282392 ], [ 34.950924904253185, -17.326063656460345 ], [ 34.95087134506548, -17.325858584239537 ], [ 34.950829033562627, -17.325651100720965 ], [ 34.950800585931511, -17.325458685865403 ], [ 34.954988844373986, -17.325074100205931 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.14", "sub_field": "1.14D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.959429692942393, -17.324632437480492 ], [ 34.96199524794266, -17.324405539234302 ], [ 34.962917097922038, -17.326827271055144 ], [ 34.9628057726948, -17.326860136943811 ], [ 34.962670123327506, -17.326892829017506 ], [ 34.962532878449238, -17.326918657395606 ], [ 34.962394414252785, -17.326937551279997 ], [ 34.962255110273766, -17.32694945888019 ], [ 34.96211534835011, -17.326954347555368 ], [ 34.961975511575361, -17.326952203903854 ], [ 34.961835983248399, -17.326943033799861 ], [ 34.961697145822697, -17.326926862377444 ], [ 34.961559379857924, -17.326903733961579 ], [ 34.961423062976593, -17.326873711946686 ], [ 34.961288568828891, -17.326836878622842 ], [ 34.961156266068393, -17.326793334950221 ], [ 34.961026517341416, -17.326743200282376 ], [ 34.96089967829289, -17.32668661203898 ], [ 34.960776096591395, -17.326623725329195 ], [ 34.960656110976146, -17.326554712526441 ], [ 34.960540050328412, -17.326479762795923 ], [ 34.960428232769921, -17.326399081575982 ], [ 34.960320964790888, -17.326312890015025 ], [ 34.960218540409869, -17.326221424365276 ], [ 34.960121240367819, -17.326124935335116 ], [ 34.960029331358555, -17.32602368740184 ], [ 34.959943065297743, -17.325917958086681 ], [ 34.959862678632533, -17.325808037194083 ], [ 34.959788391693294, -17.325694226017156 ], [ 34.959720408089943, -17.325576836511928 ], [ 34.959658914153827, -17.32545619044215 ], [ 34.959604078427063, -17.325332618497225 ], [ 34.959556051200735, -17.325206459385821 ], [ 34.959514964103008, -17.325078058907394 ], [ 34.959480929738518, -17.324947769004307 ], [ 34.9594540413798, -17.324815946797088 ], [ 34.959434372711804, -17.324682953605588 ], [ 34.959429692942393, -17.324632437480492 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.16", "sub_field": "1.16C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.955074672069358, -17.318897254404234 ], [ 34.953055168419084, -17.319242829332627 ], [ 34.953045135325517, -17.3191749687957 ], [ 34.953035406841693, -17.319069907036841 ], [ 34.953031416182405, -17.318964500208651 ], [ 34.953033174276065, -17.318859037225231 ], [ 34.953040676293959, -17.31875380715416 ], [ 34.953053901663694, -17.318649098424061 ], [ 34.953072814125669, -17.318545198034169 ], [ 34.953097361832498, -17.318442390767569 ], [ 34.953127477491357, -17.318340958410715 ], [ 34.9531630785484, -17.31824117898103 ], [ 34.953204067415207, -17.318143325964947 ], [ 34.953250331736328, -17.318047667568258 ], [ 34.953301744697342, -17.317954465981085 ], [ 34.953358165372507, -17.317863976659215 ], [ 34.953419439111137, -17.31777644762397 ], [ 34.953485397961508, -17.317692118782404 ], [ 34.953555861131257, -17.317611221269846 ], [ 34.953630635482966, -17.317533976816364 ], [ 34.953709516063618, -17.317460597139068 ], [ 34.953792286666292, -17.317391283361872 ], [ 34.953878720422843, -17.317326225464313 ], [ 34.953968580425695, -17.317265601760816 ], [ 34.954061620377189, -17.317209578412005 ], [ 34.954157585264667, -17.3171583089694 ], [ 34.954256212059335, -17.317111933954514 ], [ 34.954357230437225, -17.317070580473761 ], [ 34.954460363520035, -17.317034361870103 ], [ 34.954565328634033, -17.317003377412487 ], [ 34.954661651915764, -17.316980166566335 ], [ 34.955074672069358, -17.318897254404234 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.17", "sub_field": "1.17A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.954026875506962, -17.314612148368443 ], [ 34.95629602563065, -17.314112227198336 ], [ 34.956322154378064, -17.31421226865082 ], [ 34.956345251698423, -17.314325525156761 ], [ 34.95636214657646, -17.314439787577303 ], [ 34.956372792693529, -17.314554742729765 ], [ 34.956377160857997, -17.314670075532256 ], [ 34.956375239085375, -17.314785469867143 ], [ 34.956367032631277, -17.314900609447584 ], [ 34.956352563977227, -17.315015178684423 ], [ 34.956331872769042, -17.31512886355118 ], [ 34.956305015708395, -17.315241352444758 ], [ 34.956272066397489, -17.31535233703957 ], [ 34.956233115137429, -17.315461513132622 ], [ 34.956188268680854, -17.31556858147739 ], [ 34.956137649939436, -17.315673248603972 ], [ 34.956081397647068, -17.315775227623565 ], [ 34.95601966597976, -17.315874239014846 ], [ 34.955952624133047, -17.315970011390132 ], [ 34.955880455858406, -17.316062282239301 ], [ 34.955803358959564, -17.31615079864936 ], [ 34.955721544750475, -17.31623531799772 ], [ 34.955635237476123, -17.316315608617266 ], [ 34.955544673697872, -17.316391450431393 ], [ 34.955450101645134, -17.316462635557304 ], [ 34.955351780534926, -17.316528968875815 ], [ 34.955249979861421, -17.316590268566287 ], [ 34.955144978657209, -17.316646366605035 ], [ 34.955037064728437, -17.316697109225839 ], [ 34.954926533865958, -17.3167423573416 ], [ 34.954813689034438, -17.316781986925569 ], [ 34.954698839541891, -17.316815889351282 ], [ 34.95460479566993, -17.316838550983693 ], [ 34.954026875506962, -17.314612148368443 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.18", "sub_field": "1.18D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.959294104555354, -17.315720468707916 ], [ 34.961301964672387, -17.313838203379714 ], [ 34.963115666188799, -17.315822640664031 ], [ 34.963041894062059, -17.315884425490665 ], [ 34.962926783983661, -17.315971076915993 ], [ 34.962807110273779, -17.316051822842091 ], [ 34.962683200953926, -17.316126441941666 ], [ 34.96255539565685, -17.316194729680749 ], [ 34.962424044695666, -17.316256498879472 ], [ 34.962289508103467, -17.316311580225161 ], [ 34.962152154646418, -17.316359822736526 ], [ 34.962012360812984, -17.316401094177511 ], [ 34.961870509781747, -17.316435281419889 ], [ 34.961726990371027, -17.316462290753368 ], [ 34.961582195973008, -17.316482048142475 ], [ 34.961436523475342, -17.316494499429613 ], [ 34.961290372173067, -17.316499610483458 ], [ 34.961144142673987, -17.316497367292616 ], [ 34.96099823580051, -17.316487776003999 ], [ 34.960853051490787, -17.316470862906066 ], [ 34.960708987702283, -17.316446674356676 ], [ 34.960566439320871, -17.31641527665608 ], [ 34.960425797078265, -17.316376755865221 ], [ 34.960287446480869, -17.316331217569729 ], [ 34.960151766752965, -17.316278786590583 ], [ 34.960019129797111, -17.316219606641869 ], [ 34.959889899174662, -17.316153839936874 ], [ 34.959764429109072, -17.316081666743379 ], [ 34.959643063514939, -17.316003284889536 ], [ 34.959526135055206, -17.315918909221505 ], [ 34.959413964229249, -17.31582877101453 ], [ 34.959306858494422, -17.315733117339001 ], [ 34.959294104555354, -17.315720468707916 ] ] ] } },
{ "type": "Feature", "properties": { "field": "DL1.3", "sub_field": "DL1.3" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.947298927757849, -17.321663500672134 ], [ 34.947451385514611, -17.321753599029073 ], [ 34.947340653363916, -17.32175579751058 ], [ 34.947298927757849, -17.321663500672134 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.6", "sub_field": "1.6D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.978720327296884, -17.343280601630497 ], [ 34.978354668011164, -17.346604682049772 ], [ 34.974946239091686, -17.346248144213522 ], [ 34.974966677958683, -17.346086172049372 ], [ 34.974997699207229, -17.345915643565576 ], [ 34.975037970821617, -17.345746907989223 ], [ 34.975087382395003, -17.345580427809782 ], [ 34.97514579846969, -17.345416659333534 ], [ 34.975213058908558, -17.345256051432944 ], [ 34.97528897933433, -17.345099044316385 ], [ 34.97537335163512, -17.344946068321619 ], [ 34.975465944535074, -17.344797542736355 ], [ 34.975566504228468, -17.344653874649165 ], [ 34.975674755075552, -17.34451545783368 ], [ 34.975790400358186, -17.344382671669511 ], [ 34.975913123093221, -17.344255880102434 ], [ 34.976042586901428, -17.344135430647047 ], [ 34.976178436929551, -17.344021653434318 ], [ 34.976320300822913, -17.343914860306906 ], [ 34.976467789746074, -17.343815343964536 ], [ 34.97662049944848, -17.343723377161893 ], [ 34.976778011372502, -17.343639211961133 ], [ 34.976939893800484, -17.343563079041154 ], [ 34.977105703038013, -17.343495187065461 ], [ 34.977274984629851, -17.343435722110325 ], [ 34.977447274605353, -17.343384847154883 ], [ 34.977622100750068, -17.343342701634597 ], [ 34.97779898389976, -17.34330940105912 ], [ 34.977977439253515, -17.343285036695779 ], [ 34.978156977702334, -17.343269675319497 ], [ 34.978337107169416, -17.343263359029866 ], [ 34.978517333958685, -17.343266105135793 ], [ 34.978697164107672, -17.343277906108096 ], [ 34.978720327296884, -17.343280601630497 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.6", "sub_field": "1.6B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.977997053286906, -17.349855632180109 ], [ 34.978354668011164, -17.346604682049772 ], [ 34.981789841437561, -17.346964017488332 ], [ 34.981778585407078, -17.347053244850819 ], [ 34.981747570472095, -17.34722377374904 ], [ 34.981707304975053, -17.34739251006086 ], [ 34.981657899255794, -17.347558991288736 ], [ 34.981599488707879, -17.347722761114898 ], [ 34.981532233407833, -17.347883370652081 ], [ 34.981456317676674, -17.348040379673982 ], [ 34.981371949574829, -17.348193357821991 ], [ 34.981279360332159, -17.348341885784777 ], [ 34.981178803714307, -17.348485556447741 ], [ 34.981070555327335, -17.348623976009009 ], [ 34.980954911862447, -17.348756765058912 ], [ 34.980832190282847, -17.348883559620049 ], [ 34.980702726955109, -17.349004012144992 ], [ 34.980566876727238, -17.349117792469176 ], [ 34.980425011956086, -17.349224588715831 ], [ 34.98027752148672, -17.349324108151073 ], [ 34.980124809586627, -17.349416077986319 ], [ 34.979967294837536, -17.349500246126194 ], [ 34.979805408988021, -17.349576381859599 ], [ 34.979639595770003, -17.349644276492203 ], [ 34.979470309682334, -17.349703743918624 ], [ 34.979298014744906, -17.34975462113265 ], [ 34.979123183226534, -17.349796768674064 ], [ 34.978946294350337, -17.349830071011084 ], [ 34.978767832979948, -17.349854436857079 ], [ 34.978588288290318, -17.349869799420887 ], [ 34.978408152426617, -17.34987611658989 ], [ 34.978227919154975, -17.349873371045543 ], [ 34.97804808250892, -17.349861570310917 ], [ 34.977997053286906, -17.349855632180109 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.6", "sub_field": "1.6A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.978354668011164, -17.346604682049772 ], [ 34.978720327296884, -17.343280601630497 ], [ 34.978876104741104, -17.343298729600111 ], [ 34.979053665421596, -17.343328518536502 ], [ 34.979229359493594, -17.343367191269554 ], [ 34.979402705416973, -17.343414641803008 ], [ 34.979573228086664, -17.343470740082537 ], [ 34.979740460134565, -17.3435353323522 ], [ 34.979903943210367, -17.343608241575833 ], [ 34.980063229237601, -17.343689267922098 ], [ 34.980217881641593, -17.343778189312268 ], [ 34.980367476545865, -17.343874762028761 ], [ 34.98051160393382, -17.343978721383035 ], [ 34.980649868772417, -17.34408978244101 ], [ 34.980781892094825, -17.344207640803891 ], [ 34.980907312039015, -17.344331973442401 ], [ 34.981025784839666, -17.344462439582017 ], [ 34.981136985770199, -17.344598681636882 ], [ 34.981240610033026, -17.344740326189832 ], [ 34.9813363735949, -17.344886985015684 ], [ 34.981424013965515, -17.345038256145251 ], [ 34.981503290917125, -17.345193724966947 ], [ 34.981573987143072, -17.345352965363091 ], [ 34.981635908853576, -17.345515540877745 ], [ 34.981688886307055, -17.345681005912901 ], [ 34.981732774275621, -17.345848906949652 ], [ 34.981767452443343, -17.346018783791244 ], [ 34.981792825736257, -17.34619017082429 ], [ 34.981808824583204, -17.346362598294956 ], [ 34.981815405106829, -17.346535593596407 ], [ 34.981812549244125, -17.346708682564159 ], [ 34.981800264796163, -17.3468813907757 ], [ 34.981789841437561, -17.346964017488332 ], [ 34.978354668011164, -17.346604682049772 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.5", "sub_field": "1.5B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.981414279389675, -17.341507128753143 ], [ 34.981172574054533, -17.343691265597464 ], [ 34.981109475823907, -17.343683923580347 ], [ 34.980985765165556, -17.343663170309824 ], [ 34.980863355007244, -17.343636227310451 ], [ 34.980742580878861, -17.343603168432548 ], [ 34.980623773825592, -17.343564084290307 ], [ 34.980507259500335, -17.343519082013337 ], [ 34.980393357270984, -17.343468284952998 ], [ 34.980282379345013, -17.343411832344319 ], [ 34.980174629913549, -17.343349878924265 ], [ 34.980070404317615, -17.343282594507567 ], [ 34.979969988238452, -17.343210163521317 ], [ 34.979873656914428, -17.343132784499232 ], [ 34.979781674386686, -17.343050669537625 ], [ 34.9796942927752, -17.34296404371387 ], [ 34.979611751587868, -17.342873144469429 ], [ 34.979534277063955, -17.34277822095903 ], [ 34.979462081553976, -17.342679533367615 ], [ 34.979395362937744, -17.3425773521972 ], [ 34.979334304081959, -17.342471957525312 ], [ 34.979279072339089, -17.34236363823727 ], [ 34.979229819088637, -17.342252691234346 ], [ 34.979186679322453, -17.342139420619834 ], [ 34.979149771274642, -17.342024136865543 ], [ 34.979119196097734, -17.341907155960765 ], [ 34.979095037585431, -17.341788798546016 ], [ 34.979077361943133, -17.341669389034291 ], [ 34.979066217606537, -17.341549254721702 ], [ 34.97906163510909, -17.341428724890427 ], [ 34.979063626998311, -17.341308129906089 ], [ 34.979069772171925, -17.341221754105334 ], [ 34.981414279389675, -17.341507128753143 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.5", "sub_field": "1.5D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.981414279389675, -17.341507128753143 ], [ 34.981679444479695, -17.339111001077271 ], [ 34.981686301740211, -17.339111450993876 ], [ 34.981810970678175, -17.339125957608928 ], [ 34.981934678272594, -17.339146710678925 ], [ 34.982057085462138, -17.339173653321986 ], [ 34.982177856749416, -17.339206711691663 ], [ 34.98229666112038, -17.339245795179348 ], [ 34.982413172951517, -17.339290796662603 ], [ 34.982527072902251, -17.33934159279875 ], [ 34.982638048790086, -17.339398044362888 ], [ 34.982745796446203, -17.339459996629525 ], [ 34.98285002054908, -17.339527279796517 ], [ 34.982950435433793, -17.339599709450528 ], [ 34.983046765875045, -17.339677087072367 ], [ 34.983138747841409, -17.33975920058111 ], [ 34.983226129219041, -17.339845824915287 ], [ 34.983308670502595, -17.339936722649764 ], [ 34.983386145451767, -17.340031644646341 ], [ 34.983458341711369, -17.340130330736663 ], [ 34.983525061393408, -17.340232510435182 ], [ 34.98358612161946, -17.340337903680478 ], [ 34.983641355022087, -17.340446221602868 ], [ 34.983690610203553, -17.340557167316049 ], [ 34.983733752150904, -17.340670436730836 ], [ 34.983770662606105, -17.340785719388574 ], [ 34.98380124039025, -17.340902699312007 ], [ 34.983825401681088, -17.341021055871334 ], [ 34.983843080242778, -17.341140464662963 ], [ 34.983854227607615, -17.341260598398659 ], [ 34.983858813209004, -17.341381127802574 ], [ 34.983856824465377, -17.341501722513755 ], [ 34.983848266814846, -17.341622051991578 ], [ 34.983833163700361, -17.341741786421814 ], [ 34.983822529204822, -17.341800262183327 ], [ 34.981414279389675, -17.341507128753143 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.5", "sub_field": "1.5A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.981172574054533, -17.343691265597464 ], [ 34.981414279389675, -17.341507128753143 ], [ 34.983822529204822, -17.341800262183327 ], [ 34.983811556505685, -17.341860597620538 ], [ 34.98378350444203, -17.341978159933678 ], [ 34.983749084385991, -17.342094151129654 ], [ 34.983708390668845, -17.342208253282561 ], [ 34.983661534818218, -17.342320153643641 ], [ 34.983608645252517, -17.342429545498533 ], [ 34.983549866928996, -17.342536129007982 ], [ 34.983485360946574, -17.342639612029675 ], [ 34.983415304104362, -17.34273971091913 ], [ 34.983339888417177, -17.342836151307104 ], [ 34.983259320589269, -17.342928668851723 ], [ 34.983173821447792, -17.343017009963088 ], [ 34.983083625337699, -17.343100932498373 ], [ 34.982988979479238, -17.343180206425632 ], [ 34.982890143290554, -17.343254614454327 ], [ 34.982787387676503, -17.343323952631003 ], [ 34.982680994286177, -17.343388030898371 ], [ 34.982571254740826, -17.343446673616306 ], [ 34.982458469834533, -17.343499720043344 ], [ 34.982342948709736, -17.343547024777287 ], [ 34.982225008009713, -17.343588458153864 ], [ 34.982104971010692, -17.343623906602136 ], [ 34.981983166735645, -17.343653272955859 ], [ 34.981859929052334, -17.343676476719857 ], [ 34.981735595758096, -17.343693454290744 ], [ 34.981610507653819, -17.343704159131189 ], [ 34.981485007609791, -17.343708561897607 ], [ 34.981359439625614, -17.343706650520566 ], [ 34.981234147887363, -17.3436984302379 ], [ 34.981172574054533, -17.343691265597464 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.4", "sub_field": "1.4A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.977605005473734, -17.338606017387828 ], [ 34.977665672603734, -17.33863431107558 ], [ 34.977763772866389, -17.338690719975755 ], [ 34.977876169269216, -17.338763283345308 ], [ 34.977984457548132, -17.338841396753352 ], [ 34.978088340898189, -17.338924846102827 ], [ 34.978187534586567, -17.339013402671572 ], [ 34.978281766732948, -17.339106823739105 ], [ 34.978370779054657, -17.339204853251911 ], [ 34.978454327574674, -17.339307222525107 ], [ 34.97853218329027, -17.339413650978859 ], [ 34.978604132800747, -17.339523846907355 ], [ 34.978669978892427, -17.33963750827823 ], [ 34.978729541079169, -17.339754323560388 ], [ 34.978782656097266, -17.339873972577784 ], [ 34.97882917835291, -17.339996127386925 ], [ 34.978868980321394, -17.34012045317569 ], [ 34.978901952896805, -17.340246609181008 ], [ 34.978928005691152, -17.340374249622688 ], [ 34.978947067282256, -17.340503024651259 ], [ 34.978959085409684, -17.340632581306743 ], [ 34.978964027118096, -17.340762564486102 ], [ 34.978961878847812, -17.340892617916531 ], [ 34.978952646472038, -17.341022385131897 ], [ 34.97893635528105, -17.34115151044988 ], [ 34.978926265695563, -17.341206981511462 ], [ 34.976297118077937, -17.340867839790402 ], [ 34.976670537115623, -17.338320549892174 ], [ 34.976755638684374, -17.338330454697513 ], [ 34.976889048044576, -17.338352838997491 ], [ 34.977004841715654, -17.33837832923189 ], [ 34.977605005473734, -17.338606017387828 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.4", "sub_field": "1.4C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.976297118077937, -17.340867839790402 ], [ 34.975949718590073, -17.343237636498074 ], [ 34.97586557276648, -17.343223518187639 ], [ 34.975733562536121, -17.343194458466325 ], [ 34.975603316843497, -17.343158803210262 ], [ 34.975475192696571, -17.343116650150446 ], [ 34.975349541287549, -17.343068114828622 ], [ 34.97522670703011, -17.343013330280517 ], [ 34.975107026615298, -17.342952446671219 ], [ 34.974990828088544, -17.34288563088344 ], [ 34.974878429950408, -17.342813066060131 ], [ 34.974770140283518, -17.342734951102397 ], [ 34.974666255908012, -17.342651500124298 ], [ 34.974567061567939, -17.342562941865822 ], [ 34.974472829150777, -17.342469519065983 ], [ 34.974383816942158, -17.342371487797241 ], [ 34.974300268917865, -17.342269116763756 ], [ 34.974222414075207, -17.34216268656462 ], [ 34.974150465805295, -17.342052488924793 ], [ 34.974084621308229, -17.341938825895362 ], [ 34.974025061052657, -17.341822009025627 ], [ 34.973971948281175, -17.341702358509004 ], [ 34.973925428562943, -17.341580202305412 ], [ 34.973885629394871, -17.34145587524225 ], [ 34.973852659852184, -17.341329718096571 ], [ 34.973826610289663, -17.34120207666103 ], [ 34.973807552094073, -17.341073300796005 ], [ 34.973795537488641, -17.340943743470646 ], [ 34.973790599390107, -17.340813759795306 ], [ 34.973792751318612, -17.340683706048242 ], [ 34.973801987360879, -17.340553938699045 ], [ 34.973802974953465, -17.340546112708161 ], [ 34.976297118077937, -17.340867839790402 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.4", "sub_field": "1.4D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.976670537115623, -17.338320549892174 ], [ 34.976297118077937, -17.340867839790402 ], [ 34.973802974953465, -17.340546112708161 ], [ 34.973818282186457, -17.340424813431532 ], [ 34.973841591117456, -17.34029668416888 ], [ 34.973871850251086, -17.340169902103536 ], [ 34.973908976634981, -17.340044814734618 ], [ 34.973952868494727, -17.33992176491547 ], [ 34.974003405512995, -17.339801089913962 ], [ 34.974060449159396, -17.339683120488104 ], [ 34.974123843070416, -17.339568179979455 ], [ 34.974193413478005, -17.339456583426976 ], [ 34.97426896968603, -17.339348636703559 ], [ 34.974350304593074, -17.339244635677673 ], [ 34.974437195260094, -17.339144865402556 ], [ 34.974529403521622, -17.339049599334913 ], [ 34.974626676638515, -17.338959098585459 ], [ 34.974728747990838, -17.338873611203347 ], [ 34.974835337808571, -17.338793371496333 ], [ 34.974946153938468, -17.338718599388653 ], [ 34.975060892644805, -17.338649499818292 ], [ 34.975179239441857, -17.338586262175358 ], [ 34.975300869955831, -17.338529059782996 ], [ 34.975425450813887, -17.338478049422477 ], [ 34.975552640557751, -17.338433370903498 ], [ 34.975682090579618, -17.338395146681059 ], [ 34.975813446077481, -17.338363481519838 ], [ 34.975946347027502, -17.33833846220719 ], [ 34.976080429170672, -17.338320157315209 ], [ 34.976215325011133, -17.338308617012903 ], [ 34.976350664823187, -17.338303872928698 ], [ 34.976486077664653, -17.338305938063733 ], [ 34.976621192393402, -17.338314806756351 ], [ 34.976670537115623, -17.338320549892174 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.2", "sub_field": "1.2A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.979495810456342, -17.334203961691134 ], [ 34.97820349148671, -17.336307953800048 ], [ 34.976145471591806, -17.334907946244652 ], [ 34.976175428365991, -17.334865145367932 ], [ 34.976253802797373, -17.334764925673479 ], [ 34.976337530859709, -17.334668782842019 ], [ 34.976426383056278, -17.334576980388171 ], [ 34.976520115846917, -17.33448976992964 ], [ 34.976618472315508, -17.334407390497606 ], [ 34.976721182874279, -17.334330067881691 ], [ 34.976827966002574, -17.334258014011112 ], [ 34.976938529018604, -17.334191426373845 ], [ 34.977052568881554, -17.334130487475448 ], [ 34.977169773022176, -17.334075364338887 ], [ 34.977289820199417, -17.334026208046808 ], [ 34.977412381380873, -17.333983153327463 ], [ 34.977537120644556, -17.333946318185511 ], [ 34.977663696099462, -17.333915803578684 ], [ 34.977791760822647, -17.333891693141041 ], [ 34.977920963809922, -17.333874052953824 ], [ 34.978050950937828, -17.333862931364354 ], [ 34.978181365934098, -17.333858358853604 ], [ 34.978311851354057, -17.333860347952598 ], [ 34.978442049560201, -17.333868893208162 ], [ 34.978571603702264, -17.333883971197807 ], [ 34.978700158695197, -17.333905540594017 ], [ 34.978827362192298, -17.33393354227746 ], [ 34.978952865550774, -17.333967899499068 ], [ 34.97907632478725, -17.334008518090307 ], [ 34.979197401520416, -17.334055286721373 ], [ 34.979315763898441, -17.334108077206196 ], [ 34.979431087508353, -17.334166744853853 ], [ 34.979495810456342, -17.334203961691134 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.2", "sub_field": "1.2C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.976973009965533, -17.338311269909727 ], [ 34.97820349148671, -17.336307953800048 ], [ 34.980211685376219, -17.337674066258426 ], [ 34.980160358782953, -17.337739701106912 ], [ 34.98007663167305, -17.33783584545019 ], [ 34.979987780081963, -17.337927649472633 ], [ 34.979894047543212, -17.338014861539353 ], [ 34.979795690970128, -17.338097242601521 ], [ 34.979692979951686, -17.338174566851677 ], [ 34.979586196013528, -17.338246622342712 ], [ 34.979475631846341, -17.33831321156887 ], [ 34.979361590503522, -17.338374152007198 ], [ 34.979244384570571, -17.338429276617834 ], [ 34.979124335308164, -17.338478434302029 ], [ 34.979001771771522, -17.338521490316253 ], [ 34.978877029908503, -17.33855832664165 ], [ 34.978750451638518, -17.338588842307534 ], [ 34.978622383915415, -17.338612953668228 ], [ 34.978493177776194, -17.338630594632406 ], [ 34.978363187378896, -17.338641716844212 ], [ 34.978232769031571, -17.338646289815852 ], [ 34.978102280215623, -17.338644301011275 ], [ 34.977972078605781, -17.338635755880404 ], [ 34.97784252108957, -17.338620677844375 ], [ 34.977713962789039, -17.338599108231218 ], [ 34.977586756087099, -17.338571106162615 ], [ 34.97746124966168, -17.338536748391885 ], [ 34.977445054809721, -17.33853142025189 ], [ 34.976973009965533, -17.338311269909727 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.2", "sub_field": "1.2B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.97820349148671, -17.336307953800048 ], [ 34.979495810456342, -17.334203961691134 ], [ 34.979543056265214, -17.334231128865 ], [ 34.979651363278293, -17.334301052772663 ], [ 34.979755711692235, -17.334376324925834 ], [ 34.979855815500571, -17.334456739014655 ], [ 34.979951400329632, -17.334542074635905 ], [ 34.980042204190553, -17.334632097897025 ], [ 34.980127978197274, -17.334726562057128 ], [ 34.980208487248802, -17.334825208203203 ], [ 34.980283510673523, -17.334927765959726 ], [ 34.980352842834122, -17.33503395422969 ], [ 34.980416293691249, -17.335143481964927 ], [ 34.980473689324427, -17.335256048963895 ], [ 34.980524872408871, -17.335371346694291 ], [ 34.980569702646726, -17.335489059138776 ], [ 34.98060805715177, -17.335608863661058 ], [ 34.980639830786288, -17.33573043189012 ], [ 34.980664936449386, -17.335853430620244 ], [ 34.980683305315878, -17.335977522724271 ], [ 34.980694887025031, -17.336102368077537 ], [ 34.980699649818746, -17.336227624490192 ], [ 34.980697580628785, -17.336352948644983 ], [ 34.980688685112696, -17.336477997038344 ], [ 34.980672987638499, -17.336602426921839 ], [ 34.980650531218004, -17.336725897241585 ], [ 34.980621377389127, -17.336848069573168 ], [ 34.980585606047292, -17.336968609049123 ], [ 34.980543315226697, -17.337087185276864 ], [ 34.980494620831621, -17.337203473244294 ], [ 34.980439656318971, -17.337317154210655 ], [ 34.98037857233254, -17.337427916580229 ], [ 34.980311536290245, -17.337535456756427 ], [ 34.980238731925354, -17.337639479973969 ], [ 34.980211685376219, -17.337674066258426 ], [ 34.97820349148671, -17.336307953800048 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.1", "sub_field": "1.1A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.974747055587912, -17.330561624074441 ], [ 34.975967002903133, -17.329006613978088 ], [ 34.97768378767087, -17.330124305102657 ], [ 34.977675645322599, -17.330135938962055 ], [ 34.977610877597876, -17.330218762285984 ], [ 34.977541685523548, -17.330298216410046 ], [ 34.97746825874755, -17.330374083551767 ], [ 34.97739079852591, -17.330446155760175 ], [ 34.97730951717098, -17.330514235485847 ], [ 34.977224637469618, -17.330578136122426 ], [ 34.977136392072467, -17.330637682518109 ], [ 34.977045022856281, -17.330692711455839 ], [ 34.976950780260992, -17.33074307210066 ], [ 34.976853922603134, -17.330788626413213 ], [ 34.976754715367839, -17.330829249528179 ], [ 34.9766534304811, -17.330864830096498 ], [ 34.976550345564355, -17.330895270590695 ], [ 34.976445743173485, -17.330920487572168 ], [ 34.976339910024251, -17.330940411919936 ], [ 34.976233136206396, -17.330954989020189 ], [ 34.976125714388374, -17.330964178915902 ], [ 34.97601793901508, -17.33096795641648 ], [ 34.975910105500709, -17.330966311166762 ], [ 34.975802509418969, -17.330959247675441 ], [ 34.975695445692772, -17.330946785302704 ], [ 34.975589207785838, -17.330928958207167 ], [ 34.975484086898128, -17.330905815252294 ], [ 34.975380371167688, -17.330877419872376 ], [ 34.975278344880749, -17.330843849898734 ], [ 34.975178287692358, -17.330805197346297 ], [ 34.975080473859883, -17.33076156816146 ], [ 34.97498517149112, -17.330713081931542 ], [ 34.974892641809419, -17.330659871557128 ], [ 34.974803138437572, -17.330602082887651 ], [ 34.974747055587912, -17.330561624074441 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.1", "sub_field": "1.1C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.977225538507916, -17.327402417183986 ], [ 34.975967002903133, -17.329006613978088 ], [ 34.974274653035941, -17.327904830888109 ], [ 34.974317836853025, -17.327843131256433 ], [ 34.974382605457386, -17.327760308914549 ], [ 34.974451798181676, -17.327680855822567 ], [ 34.974525225370748, -17.327604989751688 ], [ 34.974602685764104, -17.327532918641111 ], [ 34.974683967047483, -17.327464840028238 ], [ 34.97476884643482, -17.327400940507246 ], [ 34.974857091278928, -17.327341395217715 ], [ 34.974948459709061, -17.327286367364568 ], [ 34.975042701293965, -17.327236007770892 ], [ 34.975139557728149, -17.327190454464535 ], [ 34.975238763539863, -17.327149832299764 ], [ 34.975340046818744, -17.32711425261525 ], [ 34.975443129960965, -17.327083812928787 ], [ 34.975547730430073, -17.327058596670124 ], [ 34.97565356153136, -17.32703867295232 ], [ 34.975760333197549, -17.327024096382285 ], [ 34.975867752783742, -17.32701490691122 ], [ 34.975975525869487, -17.327011129725086 ], [ 34.976083357065676, -17.327012775175607 ], [ 34.976190950823955, -17.327019838751866 ], [ 34.976298012246922, -17.327032301092764 ], [ 34.97640424789607, -17.327050128040007 ], [ 34.976509366596161, -17.327073270731798 ], [ 34.976613080233122, -17.327101665736684 ], [ 34.976715104543686, -17.327135235227491 ], [ 34.976815159894414, -17.327173887194547 ], [ 34.976912972048119, -17.327217515697914 ], [ 34.977008272915363, -17.327266001157675 ], [ 34.977100801289254, -17.327319210681736 ], [ 34.977190303561343, -17.32737699842998 ], [ 34.977225538507916, -17.327402417183986 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.1", "sub_field": "1.1B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.975967002903133, -17.329006613978088 ], [ 34.974747055587912, -17.330561624074441 ], [ 34.974716906702625, -17.330539874321609 ], [ 34.974634182963371, -17.330473416372392 ], [ 34.974555193962502, -17.330402891200858 ], [ 34.974480156205075, -17.330328492116003 ], [ 34.974409275365083, -17.330250423045062 ], [ 34.974342745721671, -17.330168897974517 ], [ 34.974280749626672, -17.330084140363468 ], [ 34.974223457004811, -17.329996382531196 ], [ 34.974171024887951, -17.329905865020287 ], [ 34.974123596984739, -17.329812835937251 ], [ 34.974081303286702, -17.329717550272466 ], [ 34.974044259712088, -17.329620269201229 ], [ 34.974012567788179, -17.329521259367823 ], [ 34.973986314373022, -17.329420792154632 ], [ 34.973965571417502, -17.329319142938278 ], [ 34.973950395768235, -17.329216590334781 ], [ 34.973940829011752, -17.329113415435877 ], [ 34.973936897360709, -17.329009901038535 ], [ 34.973938611582099, -17.328906330869806 ], [ 34.973945966967818, -17.328802988809159 ], [ 34.973958943347753, -17.328700158110344 ], [ 34.973977505145072, -17.328598120625017 ], [ 34.974001601473894, -17.328497156030238 ], [ 34.974031166278891, -17.328397541061847 ], [ 34.974066118516369, -17.328299548756014 ], [ 34.974106362376588, -17.328203447700862 ], [ 34.974151787546347, -17.328109501300297 ], [ 34.97420226951153, -17.328017967052048 ], [ 34.974257669898414, -17.327929095841995 ], [ 34.974274653035941, -17.327904830888109 ], [ 34.975967002903133, -17.329006613978088 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.3", "sub_field": "1.3C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.971195967655134, -17.332888509205493 ], [ 34.970773107481506, -17.335663616039604 ], [ 34.967900765229288, -17.335206638144747 ], [ 34.967920610457007, -17.335097567226367 ], [ 34.967954614815163, -17.334955110851492 ], [ 34.967996335195423, -17.334814558954029 ], [ 34.968045657228011, -17.33467629677391 ], [ 34.968102445708503, -17.334540703274193 ], [ 34.968166544968589, -17.334408150102437 ], [ 34.968237779302939, -17.334279000572078 ], [ 34.968315953450919, -17.334153608666739 ], [ 34.968400853131925, -17.334032318069912 ], [ 34.968492245632852, -17.333915461223164 ], [ 34.968589880446025, -17.333803358414983 ], [ 34.968693489955889, -17.333696316902909 ], [ 34.968802790172646, -17.333594630071527 ], [ 34.968917481510559, -17.333498576628386 ], [ 34.969037249609258, -17.333408419840225 ], [ 34.969161766195299, -17.333324406811389 ], [ 34.969290689981875, -17.333246767806713 ], [ 34.969423667604325, -17.333175715620431 ], [ 34.969560334588486, -17.333111444993076 ], [ 34.969700316349666, -17.333054132077738 ], [ 34.969843229219229, -17.333003933957372 ], [ 34.969988681496098, -17.332960988214293 ], [ 34.970136274520222, -17.332925412553223 ], [ 34.970285603765085, -17.33289730447866 ], [ 34.970436259946382, -17.332876741027736 ], [ 34.970587830143572, -17.332863778559105 ], [ 34.970739898931548, -17.332858452598554 ], [ 34.970892049519023, -17.332860777741558 ], [ 34.97104386489081, -17.332870747613462 ], [ 34.971194928950496, -17.332888334886835 ], [ 34.971195967655134, -17.332888509205493 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.3", "sub_field": "1.3A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.970354223381719, -17.338412629071641 ], [ 34.970773107481506, -17.335663616039604 ], [ 34.973634084154476, -17.336118785717325 ], [ 34.973618904232666, -17.336202230245593 ], [ 34.973584904230833, -17.33634468714585 ], [ 34.973543188019221, -17.336485239791532 ], [ 34.97349386992196, -17.336623502934522 ], [ 34.973437085100322, -17.336759097601192 ], [ 34.973372989182529, -17.336891652131232 ], [ 34.973301757837334, -17.337020803196278 ], [ 34.973223586292583, -17.337146196795945 ], [ 34.973138688800368, -17.33726748922814 ], [ 34.973047298049813, -17.337384348031179 ], [ 34.972949664529423, -17.337496452895188 ], [ 34.97284605584057, -17.337603496540073 ], [ 34.972736755964092, -17.337705185557869 ], [ 34.972622064481968, -17.337801241217083 ], [ 34.972502295756165, -17.337891400226717 ], [ 34.972377778066971, -17.337975415458054 ], [ 34.9722488527133, -17.338053056622162 ], [ 34.972115873076987, -17.338124110901102 ], [ 34.97197920365425, -17.338188383531453 ], [ 34.97183921905652, -17.338245698338167 ], [ 34.97169630298346, -17.338295898217574 ], [ 34.971550847171287, -17.338338845568071 ], [ 34.971403250318779, -17.338374422667343 ], [ 34.971253916994385, -17.338402531995111 ], [ 34.971103256527137, -17.338423096500538 ], [ 34.970951681884493, -17.3384360598134 ], [ 34.970799608540283, -17.338441386398674 ], [ 34.970647453335715, -17.338439061654018 ], [ 34.970495633336562, -17.338429091949749 ], [ 34.970354223381719, -17.338412629071641 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.3", "sub_field": "1.3D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.970773107481506, -17.335663616039604 ], [ 34.971195967655134, -17.332888509205493 ], [ 34.971344827660843, -17.33291349135645 ], [ 34.971493150178375, -17.332946148071397 ], [ 34.971639489979275, -17.332986215524052 ], [ 34.971783445973415, -17.333033583895354 ], [ 34.971924623603627, -17.333088123355868 ], [ 34.972062635926825, -17.333149684421517 ], [ 34.972197104674571, -17.333218098363261 ], [ 34.972327661289661, -17.333293177669553 ], [ 34.97245394793616, -17.333374716560179 ], [ 34.972575618480157, -17.333462491550247 ], [ 34.972692339438325, -17.333556262062647 ], [ 34.972803790891966, -17.333655771087365 ], [ 34.972909667363737, -17.333760745885836 ], [ 34.973009678655011, -17.333870898738422 ], [ 34.973103550641191, -17.33398592773289 ], [ 34.973191026023137, -17.334105517591908 ], [ 34.973271865032345, -17.334229340537018 ], [ 34.9733458460883, -17.33435705718701 ], [ 34.973412766405815, -17.334488317488017 ], [ 34.973472442550914, -17.334622761672907 ], [ 34.973524710943792, -17.334760021247295 ], [ 34.973569428307272, -17.334899719999441 ], [ 34.973606472059643, -17.335041475031371 ], [ 34.973635740650828, -17.335184897808336 ], [ 34.973657153840904, -17.335329595223612 ], [ 34.973670652920205, -17.335475170675963 ], [ 34.973676200870415, -17.335621225156661 ], [ 34.973673782466292, -17.335767358343112 ], [ 34.973663404317563, -17.335913169696045 ], [ 34.97364509485098, -17.336058259557369 ], [ 34.973634084154476, -17.336118785717325 ], [ 34.970773107481506, -17.335663616039604 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.7", "sub_field": "1.7B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.96697037363257, -17.340380748223968 ], [ 34.968913119447087, -17.339561990788184 ], [ 34.968924732557923, -17.339588154703865 ], [ 34.968961776131572, -17.339685436701803 ], [ 34.96899346779216, -17.339784447337809 ], [ 34.969019720667752, -17.339884915233949 ], [ 34.969040462793117, -17.33998656501765 ], [ 34.969055637307129, -17.340089118076534 ], [ 34.969065202608647, -17.340192293321937 ], [ 34.969069132470679, -17.34029580795945 ], [ 34.969067416112331, -17.340399378263918 ], [ 34.969060058228507, -17.340502720357161 ], [ 34.969047078977077, -17.340605550986041 ], [ 34.969028513923831, -17.340707588298791 ], [ 34.969004413945001, -17.340808552617649 ], [ 34.968974845087928, -17.340908167205317 ], [ 34.968939888390231, -17.341006159023614 ], [ 34.968899639657678, -17.341102259481772 ], [ 34.968854209201659, -17.341196205172718 ], [ 34.968803721537007, -17.341287738595042 ], [ 34.968748315040735, -17.341376608858798 ], [ 34.968688141572834, -17.341462572373302 ], [ 34.968623366060072, -17.341545393514739 ], [ 34.968554166044022, -17.341624845272079 ], [ 34.96848073119444, -17.341700709869322 ], [ 34.968403262789458, -17.341772779362497 ], [ 34.968321973163881, -17.3418408562096 ], [ 34.968237085127249, -17.341904753812166 ], [ 34.968148831353027, -17.341964297026713 ], [ 34.968057453740961, -17.342019322644887 ], [ 34.967963202754007, -17.34206967984084 ], [ 34.967866336731689, -17.342115230584671 ], [ 34.967862993493888, -17.342116599326115 ], [ 34.96697037363257, -17.340380748223968 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.7", "sub_field": "1.7D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.96697037363257, -17.340380748223968 ], [ 34.965141978743468, -17.34115131321493 ], [ 34.965135870092325, -17.341139330722289 ], [ 34.965093577571821, -17.341044043052051 ], [ 34.965056535608213, -17.340946760170706 ], [ 34.965024845724365, -17.340847748727509 ], [ 34.964998594772723, -17.340747280109223 ], [ 34.964977854697416, -17.340645629696343 ], [ 34.964962682337145, -17.340543076108169 ], [ 34.964953119269445, -17.340439900439126 ], [ 34.96494919169681, -17.340336385488293 ], [ 34.964950910375102, -17.340232814984212 ], [ 34.964958270583949, -17.340129472807298 ], [ 34.964971252140032, -17.340026642211601 ], [ 34.964989819452306, -17.339924605048481 ], [ 34.965013921619786, -17.339823640994112 ], [ 34.965043492571134, -17.339724026782864 ], [ 34.965078451245816, -17.339626035448813 ], [ 34.965118701816422, -17.339529935577406 ], [ 34.965164133951369, -17.339435990569296 ], [ 34.965214623117454, -17.339344457918411 ], [ 34.965270030921225, -17.339255588506234 ], [ 34.965330205488343, -17.339169625914124 ], [ 34.965394981880017, -17.339086805755809 ], [ 34.965464182545041, -17.33900735503153 ], [ 34.965537617806547, -17.338931491505967 ], [ 34.965615086381874, -17.338859423111348 ], [ 34.965696375934364, -17.338791347377654 ], [ 34.965781263655309, -17.338727450891149 ], [ 34.965869516874641, -17.338667908783069 ], [ 34.965960893698714, -17.338612884249663 ], [ 34.966039653327748, -17.338570804330793 ], [ 34.96697037363257, -17.340380748223968 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.7", "sub_field": "1.7A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.968913119447087, -17.339561990788184 ], [ 34.96697037363257, -17.340380748223968 ], [ 34.966039653327748, -17.338570804330793 ], [ 34.966055143673252, -17.338562528104891 ], [ 34.966152008469777, -17.338516978367149 ], [ 34.96625122259362, -17.338476359880929 ], [ 34.966352514111676, -17.338440783974821 ], [ 34.966455605397492, -17.338410348156252 ], [ 34.966560213892357, -17.33838513584438 ], [ 34.966666052879539, -17.338365216141444 ], [ 34.966772832270152, -17.338350643643359 ], [ 34.96688025939816, -17.338341458290188 ], [ 34.966988039822454, -17.338337685256583 ], [ 34.967095878133762, -17.338339334882878 ], [ 34.967203478764311, -17.338346402646742 ], [ 34.967310546797805, -17.338358869175604 ], [ 34.967416788777662, -17.33837670029968 ], [ 34.967521913511277, -17.338399847145745 ], [ 34.967625632868057, -17.338428246270951 ], [ 34.967727662569054, -17.338461819836844 ], [ 34.967827722966078, -17.338500475822592 ], [ 34.967925539808057, -17.338544108277233 ], [ 34.968020844992765, -17.338592597610052 ], [ 34.968113377301442, -17.338645810918319 ], [ 34.968202883114863, -17.338703602351565 ], [ 34.968289117108299, -17.338765813511245 ], [ 34.968371842924007, -17.338832273884954 ], [ 34.968450833818949, -17.338902801313676 ], [ 34.968525873286232, -17.33897720249103 ], [ 34.968596755648612, -17.339055273493063 ], [ 34.968663286622174, -17.339136800337148 ], [ 34.968725283848819, -17.339221559568486 ], [ 34.968782577396205, -17.339309318872438 ], [ 34.968835010223451, -17.339399837711309 ], [ 34.968882438611672, -17.339492867983591 ], [ 34.968913119447087, -17.339561990788184 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.8", "sub_field": "1.8C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.958846576270247, -17.339420347508153 ], [ 34.961161225337996, -17.342695153682683 ], [ 34.957665162047512, -17.344762020273741 ], [ 34.957619750775827, -17.344692455184319 ], [ 34.957515131525668, -17.344511824153546 ], [ 34.95742049925115, -17.344326182128594 ], [ 34.957336113312707, -17.344136037957821 ], [ 34.957262204982982, -17.343941912828704 ], [ 34.957198976813203, -17.343744338839166 ], [ 34.957146602078303, -17.343543857538954 ], [ 34.957105224302296, -17.34334101844518 ], [ 34.957074956865263, -17.343136377535984 ], [ 34.957055882692877, -17.342930495726488 ], [ 34.957048054029585, -17.342723937331343 ], [ 34.957051492295761, -17.342517268517859 ], [ 34.957066188029401, -17.342311055754124 ], [ 34.957092100912497, -17.342105864256347 ], [ 34.957129159881944, -17.341902256439582 ], [ 34.957177263324716, -17.341700790376251 ], [ 34.95723627935687, -17.341502018266478 ], [ 34.957306046185302, -17.341306484924623 ], [ 34.957386372551667, -17.341114726286037 ], [ 34.957477038256947, -17.34092726793822 ], [ 34.957577794765285, -17.340744623680305 ], [ 34.957688365885573, -17.340567294114859 ], [ 34.957808448528688, -17.340395765275964 ], [ 34.957937713538506, -17.340230507297168 ], [ 34.958075806594252, -17.340071973122999 ], [ 34.958222349181902, -17.339920597267728 ], [ 34.958376939631663, -17.339776794624512 ], [ 34.95853915421911, -17.339640959328449 ], [ 34.958708548326541, -17.339513463676425 ], [ 34.958846576270247, -17.339420347508153 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.8", "sub_field": "1.8A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.963463180156758, -17.345951999810669 ], [ 34.961161225337996, -17.342695153682683 ], [ 34.964664424622271, -17.340624068304397 ], [ 34.964697486842347, -17.340674713360091 ], [ 34.964802110127501, -17.34085534020641 ], [ 34.964896747286502, -17.341040978226815 ], [ 34.964981138905401, -17.341231118616804 ], [ 34.965055053649337, -17.34142524023035 ], [ 34.965118288896797, -17.34162281100804 ], [ 34.965170671295283, -17.3418232894353 ], [ 34.965212057236833, -17.342026126026514 ], [ 34.965242333251901, -17.342230764830955 ], [ 34.96526141632085, -17.342436644956557 ], [ 34.96526925410172, -17.342643202107119 ], [ 34.965265825074276, -17.342849870128966 ], [ 34.965251138599292, -17.34305608256269 ], [ 34.965225234893339, -17.343261274195687 ], [ 34.965188184918972, -17.343464882611432 ], [ 34.965140090190644, -17.343666349730935 ], [ 34.965081082496845, -17.343865123342518 ], [ 34.965011323539322, -17.344060658615334 ], [ 34.964931004490182, -17.344252419592877 ], [ 34.964840345468289, -17.344439880662051 ], [ 34.964739594936226, -17.344622527993895 ], [ 34.96462902901969, -17.344799860952165 ], [ 34.964508950750719, -17.344971393465599 ], [ 34.964379689237582, -17.345136655360388 ], [ 34.964241598762683, -17.345295193649122 ], [ 34.964095057811839, -17.345446573772414 ], [ 34.963940468036853, -17.34559038079038 ], [ 34.963778253154771, -17.345726220519964 ], [ 34.963608857786525, -17.345853720615708 ], [ 34.963463180156758, -17.345951999810669 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.8", "sub_field": "1.8D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.961161225337996, -17.342695153682683 ], [ 34.958846576270247, -17.339420347508153 ], [ 34.958884657661677, -17.339394657106936 ], [ 34.959066999530179, -17.339284865242441 ], [ 34.959255074158534, -17.339184388997051 ], [ 34.95944836606386, -17.339093503752036 ], [ 34.959646345466524, -17.339012458601051 ], [ 34.95984846974207, -17.33894147566772 ], [ 34.96005418490828, -17.338880749496898 ], [ 34.960262927143212, -17.338830446521602 ], [ 34.960474124330389, -17.338790704607014 ], [ 34.960687197626548, -17.338761632672661 ], [ 34.960901563047791, -17.338743310394069 ], [ 34.961116633069963, -17.338735787984415 ], [ 34.961331818238484, -17.338739086056979 ], [ 34.961546528783735, -17.338753195568739 ], [ 34.961760176237021, -17.338778077845117 ], [ 34.961972175043229, -17.3388136646861 ], [ 34.962181944165295, -17.338859858553089 ], [ 34.962388908676445, -17.338916532836219 ], [ 34.96259250133555, -17.338983532201397 ], [ 34.962792164141611, -17.33906067301595 ], [ 34.962987349862807, -17.339147743851825 ], [ 34.963177523536125, -17.33924450606515 ], [ 34.963362163933262, -17.33935069444999 ], [ 34.963540764989084, -17.339466017965307 ], [ 34.96371283718851, -17.339590160532456 ], [ 34.963877908907953, -17.339722781901354 ], [ 34.964035527707921, -17.339863518582955 ], [ 34.964185261572979, -17.340011984845287 ], [ 34.964326700095761, -17.340167773770666 ], [ 34.964459455601954, -17.340330458370634 ], [ 34.964583164212755, -17.340499592756277 ], [ 34.964664424622271, -17.340624068304397 ], [ 34.961161225337996, -17.342695153682683 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.9", "sub_field": "1.9A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.962034302490196, -17.337088012040809 ], [ 34.964728870983024, -17.336051561916904 ], [ 34.966003469280459, -17.338512454706869 ], [ 34.965924344913347, -17.338549662135424 ], [ 34.965784357706042, -17.338606972920125 ], [ 34.965641439239995, -17.338657168665854 ], [ 34.965495981257945, -17.338700111782341 ], [ 34.965348382464626, -17.338735684558884 ], [ 34.965199047433821, -17.338763789487064 ], [ 34.965048385499237, -17.33878434952808 ], [ 34.96489680963235, -17.338797308323986 ], [ 34.964744735310369, -17.338802630352134 ], [ 34.964592579377161, -17.338800301022673 ], [ 34.964440758900508, -17.338790326718492 ], [ 34.964289690028806, -17.338772734777791 ], [ 34.96413978685019, -17.338747573419131 ], [ 34.963991460257319, -17.338714911609266 ], [ 34.963845116820963, -17.338674838874116 ], [ 34.963701157675445, -17.338627465053349 ], [ 34.96355997741891, -17.338572919999283 ], [ 34.963421963031685, -17.338511353220905 ], [ 34.963287492815319, -17.338442933474067 ], [ 34.96315693535562, -17.338367848298851 ], [ 34.963030648512166, -17.338286303505473 ], [ 34.96290897843744, -17.338198522610085 ], [ 34.962792258627822, -17.338104746222072 ], [ 34.962680809009477, -17.338005231384418 ], [ 34.962574935061454, -17.337900250869161 ], [ 34.962474926978267, -17.337790092429572 ], [ 34.962381058874492, -17.337675058011381 ], [ 34.962293588033532, -17.337555462925053 ], [ 34.962212754202319, -17.337431634981467 ], [ 34.962138778934303, -17.337303913593271 ], [ 34.962071864982285, -17.337172648844497 ], [ 34.962034302490196, -17.337088012040809 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.9", "sub_field": "1.9C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.967428748672695, -17.335013069641263 ], [ 34.964728870983024, -17.336051561916904 ], [ 34.963415169170496, -17.333515171177073 ], [ 34.963505628599926, -17.333472634116486 ], [ 34.963645612969273, -17.33341532522212 ], [ 34.963788528230573, -17.33336513123426 ], [ 34.96393398267621, -17.33332218972393 ], [ 34.964081577640179, -17.333286618384236 ], [ 34.964230908590665, -17.333258514707836 ], [ 34.964381566238657, -17.333237955719817 ], [ 34.964533137659622, -17.333224997766614 ], [ 34.964685207425113, -17.333219676361633 ], [ 34.964837358741143, -17.333222006087869 ], [ 34.964989174590535, -17.333231980558111 ], [ 34.96514023887557, -17.333249572432333 ], [ 34.965290137558391, -17.333274733492722 ], [ 34.965438459795585, -17.333307394775765 ], [ 34.965584799064111, -17.333347466761317 ], [ 34.965728754275318, -17.333394839617938 ], [ 34.965869930874121, -17.333449383503872 ], [ 34.966007941920303, -17.333510948922914 ], [ 34.966142409148922, -17.333579367134121 ], [ 34.96627296400689, -17.333654450614219 ], [ 34.966399248663151, -17.333735993571572 ], [ 34.966520916989182, -17.333823772510151 ], [ 34.966637635507752, -17.333917546842027 ], [ 34.966749084306826, -17.334017059546696 ], [ 34.966854957916375, -17.334122037875517 ], [ 34.966954966145565, -17.334232194099176 ], [ 34.967048834878206, -17.334347226296227 ], [ 34.967136306824045, -17.334466819180523 ], [ 34.967217142224001, -17.334590644965317 ], [ 34.967291119507479, -17.334718364261622 ], [ 34.967358035899565, -17.334849627008314 ], [ 34.967417707977077, -17.334984073431553 ], [ 34.967428748672695, -17.335013069641263 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.9", "sub_field": "1.9B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.964728870983024, -17.336051561916904 ], [ 34.962034302490196, -17.337088012040809 ], [ 34.962012195742716, -17.337038200530916 ], [ 34.961959934753182, -17.336900937173795 ], [ 34.961915225244269, -17.33676123500965 ], [ 34.961878189747068, -17.336619476959022 ], [ 34.961848929757601, -17.33647605157676 ], [ 34.961827525458673, -17.336331351987006 ], [ 34.96181403550041, -17.336185774805603 ], [ 34.961808496839552, -17.336039719052945 ], [ 34.961810924638471, -17.335893585060266 ], [ 34.961821312223798, -17.33574777337228 ], [ 34.961839631104837, -17.335602683649384 ], [ 34.961865831052002, -17.335458713572155 ], [ 34.961899840234594, -17.335316257751341 ], [ 34.961941565417888, -17.335175706646258 ], [ 34.961990892218978, -17.335037445494642 ], [ 34.962047685420352, -17.33490185325676 ], [ 34.962111789340781, -17.334769301576685 ], [ 34.962183028262146, -17.334640153763782 ], [ 34.962261206911265, -17.334514763796925 ], [ 34.962346110995185, -17.334393475354325 ], [ 34.962437507788763, -17.334276620871645 ], [ 34.962535146772538, -17.334164520630836 ], [ 34.962638760319571, -17.334057481882457 ], [ 34.962748064428943, -17.333955798003494 ], [ 34.962862759504311, -17.333859747693428 ], [ 34.962982531175044, -17.333769594210374 ], [ 34.96310705115792, -17.333685584649643 ], [ 34.963235978156838, -17.33360794926655 ], [ 34.963368958798306, -17.333536900845434 ], [ 34.963415169170496, -17.333515171177073 ], [ 34.964728870983024, -17.336051561916904 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.10", "sub_field": "1.10A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.958739701646998, -17.339232847473824 ], [ 34.957010326871092, -17.336809457524954 ], [ 34.959540353269119, -17.335238722501 ], [ 34.959589253839567, -17.335313636053893 ], [ 34.959668014263052, -17.335449625557878 ], [ 34.959739256571318, -17.335589387587159 ], [ 34.959802785482793, -17.335732539073017 ], [ 34.959858426855881, -17.335878687655981 ], [ 34.959906028166579, -17.336027432761135 ], [ 34.959945458926505, -17.336178366695954 ], [ 34.959976611040844, -17.336331075767689 ], [ 34.959999399104831, -17.336485141417217 ], [ 34.960013760638006, -17.336640141366214 ], [ 34.960019656255689, -17.336795650774501 ], [ 34.960017069777173, -17.336951243404492 ], [ 34.960006008270348, -17.337106492789449 ], [ 34.959986502032443, -17.337260973402362 ], [ 34.959958604507399, -17.337414261822307 ], [ 34.959922392139397, -17.337565937894968 ], [ 34.959877964163766, -17.337715585884322 ], [ 34.959825442335102, -17.337862795612132 ], [ 34.959764970593746, -17.338007163582269 ], [ 34.959696714671516, -17.338148294086736 ], [ 34.959620861637603, -17.338285800290265 ], [ 34.959537619385998, -17.33841930529077 ], [ 34.959447216065776, -17.338548443152437 ], [ 34.959349899456001, -17.33867285990879 ], [ 34.959245936286599, -17.338792214533026 ], [ 34.959135611507378, -17.338906179872861 ], [ 34.959019227507071, -17.339014443547278 ], [ 34.958897103284585, -17.339116708802869 ], [ 34.9587695735746, -17.339212695327376 ], [ 34.958739701646998, -17.339232847473824 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.10", "sub_field": "1.10C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.955242480798987, -17.334332157368866 ], [ 34.957010326871092, -17.336809457524954 ], [ 34.954343541002665, -17.33846509794364 ], [ 34.954260784434837, -17.338338313495246 ], [ 34.95418202629854, -17.338202321620905 ], [ 34.954110786758697, -17.338062557323308 ], [ 34.954047261066549, -17.337919403696002 ], [ 34.953991623328406, -17.337773253121934 ], [ 34.953944026028459, -17.33762450619783 ], [ 34.95390459961115, -17.337473570636146 ], [ 34.953873452123695, -17.337320860147432 ], [ 34.95385066892019, -17.337166793306359 ], [ 34.953836312427846, -17.337011792404329 ], [ 34.953830421976093, -17.336856282291919 ], [ 34.953833013689021, -17.336700689214442 ], [ 34.953844080441442, -17.336545439643551 ], [ 34.953863591878616, -17.336390959108272 ], [ 34.953891494499679, -17.336237671028709 ], [ 34.953927711804546, -17.336085995555422 ], [ 34.953972144503872, -17.335936348417885 ], [ 34.954024670791277, -17.335789139784975 ], [ 34.954085146677556, -17.33564477314086 ], [ 34.954153406385494, -17.335503644178985 ], [ 34.954229262804425, -17.335366139717731 ], [ 34.954312508003291, -17.33523263664009 ], [ 34.954402913800664, -17.335103500860804 ], [ 34.954500232390394, -17.334979086323543 ], [ 34.954604197020849, -17.334859734030776 ], [ 34.954714522726206, -17.334745771109269 ], [ 34.954830907107592, -17.334637509913541 ], [ 34.954953031161914, -17.334535247169818 ], [ 34.95508056015634, -17.334439263162871 ], [ 34.955213144545709, -17.334349820967844 ], [ 34.955242480798987, -17.334332157368866 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.10", "sub_field": "1.10B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.957010326871092, -17.336809457524954 ], [ 34.958739701646998, -17.339232847473824 ], [ 34.958636987930127, -17.339302140018049 ], [ 34.95849970976434, -17.339384797702966 ], [ 34.9583581153545, -17.339460441813092 ], [ 34.958212592810412, -17.339528865003448 ], [ 34.958063541010617, -17.33958987972149 ], [ 34.957911368508981, -17.339643318721347 ], [ 34.957756492414681, -17.339689035522227 ], [ 34.957599337248816, -17.339726904810092 ], [ 34.957440333780596, -17.339756822781162 ], [ 34.957279917846499, -17.339778707426536 ], [ 34.957118529155416, -17.339792498757056 ], [ 34.956956610083246, -17.339798158967696 ], [ 34.956794604460114, -17.339795672541321 ], [ 34.956632956353708, -17.339785046291222 ], [ 34.956472108851798, -17.339766309342444 ], [ 34.95631250284756, -17.339739513051995 ], [ 34.956154575830887, -17.339704730868029 ], [ 34.95599876068907, -17.339662058128571 ], [ 34.955845484519955, -17.339611611800109 ], [ 34.955695167461201, -17.33955353015708 ], [ 34.955548221538436, -17.339487972402619 ], [ 34.955405049535798, -17.339415118232349 ], [ 34.955266043891676, -17.339335167341641 ], [ 34.955131585622986, -17.339248338878221 ], [ 34.955002043280672, -17.339154870841433 ], [ 34.954877771939422, -17.339055019429797 ], [ 34.954759112224359, -17.338949058338667 ], [ 34.954646389377288, -17.338837278009954 ], [ 34.95453991236527, -17.338719984835976 ], [ 34.954439973033701, -17.338597500319473 ], [ 34.954346845306404, -17.338470160192326 ], [ 34.954343541002665, -17.33846509794364 ], [ 34.957010326871092, -17.336809457524954 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.11", "sub_field": "1.11A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.959791281109077, -17.327010987988146 ], [ 34.960630932270384, -17.330690185640638 ], [ 34.956779145904193, -17.33137872104356 ], [ 34.956764856793804, -17.331308666394371 ], [ 34.956736047353225, -17.331113859021773 ], [ 34.95671789260097, -17.330917870461413 ], [ 34.956710442264828, -17.330721237910581 ], [ 34.956713716731777, -17.330524500330025 ], [ 34.956727706992346, -17.330328196966747 ], [ 34.956752374665776, -17.330132865875832 ], [ 34.956787652105561, -17.329939042445719 ], [ 34.956833442585214, -17.329747257930709 ], [ 34.956889620563857, -17.329558037994907 ], [ 34.956956032030497, -17.329371901271418 ], [ 34.957032494926693, -17.329189357940876 ], [ 34.957118799645741, -17.329010908333149 ], [ 34.957214709607541, -17.328837041556099 ], [ 34.957319961907359, -17.328668234155014 ], [ 34.957434268036607, -17.328504948806675 ], [ 34.957557314673871, -17.328347633051155 ], [ 34.957688764543875, -17.328196718065481 ], [ 34.957828257342058, -17.328052617481823 ], [ 34.957975410722298, -17.327915726254009 ], [ 34.958129821344905, -17.327786419575087 ], [ 34.958291065982159, -17.327665051849202 ], [ 34.958458702678442, -17.327551955720331 ], [ 34.958632271961456, -17.327447441160722 ], [ 34.958811298101537, -17.327351794621475 ], [ 34.958995290415487, -17.32726527824753 ], [ 34.959183744611337, -17.327188129159367 ], [ 34.959376144170342, -17.327120558803227 ], [ 34.959571961762535, -17.327062752371688 ], [ 34.959770660691824, -17.327014868296196 ], [ 34.959791281109077, -17.327010987988146 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.11", "sub_field": "1.11B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.960630932270384, -17.330690185640638 ], [ 34.959791281109077, -17.327010987988146 ], [ 34.959971696366765, -17.326977037813023 ], [ 34.960174517792879, -17.326949364603571 ], [ 34.960378569082671, -17.326931924510365 ], [ 34.960583290978782, -17.32692476532921 ], [ 34.960788122386646, -17.326927906678304 ], [ 34.960992501911946, -17.326941339944465 ], [ 34.961195869399035, -17.326965028306834 ], [ 34.96139766746591, -17.326998906837758 ], [ 34.961597343031521, -17.327042882680757 ], [ 34.96179434883144, -17.327096835305003 ], [ 34.96198814491747, -17.327160616835705 ], [ 34.962178200137267, -17.327234052459289 ], [ 34.962363993589896, -17.327316940902499 ], [ 34.962545016053291, -17.327409054984003 ], [ 34.962720771379686, -17.327510142236925 ], [ 34.962890777855286, -17.327619925600736 ], [ 34.963054569520445, -17.327738104180536 ], [ 34.963211697446603, -17.327864354071615 ], [ 34.963361730966632, -17.327998329247073 ], [ 34.96350425885521, -17.328139662506167 ], [ 34.963638890455776, -17.328287966480499 ], [ 34.963765256751422, -17.328442834695693 ], [ 34.963883011376232, -17.328603842685226 ], [ 34.963991831564719, -17.328770549153813 ], [ 34.96409141903667, -17.328942497186652 ], [ 34.964181500814739, -17.329119215501724 ], [ 34.964261829972877, -17.329300219741285 ], [ 34.964332186313328, -17.329485013799381 ], [ 34.964392376970423, -17.329673091181412 ], [ 34.964442236939426, -17.329863936392307 ], [ 34.964470767983819, -17.3300037865056 ], [ 34.960630932270384, -17.330690185640638 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.11", "sub_field": "1.11C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.961187884958441, -17.334400587778653 ], [ 34.960619385935466, -17.330692249633707 ], [ 34.964470767983819, -17.3300037865056 ], [ 34.964481629529139, -17.330057026349291 ], [ 34.964510446736824, -17.330251831815545 ], [ 34.964528609544658, -17.330447818850658 ], [ 34.964536068136539, -17.330644450274068 ], [ 34.964532802035059, -17.330841187137342 ], [ 34.964518820158055, -17.331037490201368 ], [ 34.964494160794402, -17.331232821414325 ], [ 34.964458891499589, -17.331426645386436 ], [ 34.964413108910819, -17.331618430857471 ], [ 34.964356938482574, -17.331807652152843 ], [ 34.964290534143089, -17.331993790624551 ], [ 34.964214077872739, -17.332176336072834 ], [ 34.964127779205612, -17.332354788144567 ], [ 34.964031874655497, -17.332528657704952 ], [ 34.963926627067856, -17.332697468178146 ], [ 34.963812324899642, -17.332860756853766 ], [ 34.963689281428891, -17.333018076155238 ], [ 34.963557833896161, -17.33316899486671 ], [ 34.963418342580404, -17.333313099315198 ], [ 34.963271189811508, -17.333449994504512 ], [ 34.96311677892249, -17.333579305198217 ], [ 34.962955533143941, -17.333700676948169 ], [ 34.962787894444006, -17.333813777066307 ], [ 34.962614322316931, -17.333918295536666 ], [ 34.962435292523566, -17.334013945865316 ], [ 34.962251295787105, -17.334100465865792 ], [ 34.962062836447977, -17.334177618377865 ], [ 34.961870431081294, -17.334245191917802 ], [ 34.961674607080617, -17.334303001258153 ], [ 34.961475901212289, -17.334350887935564 ], [ 34.961274858143774, -17.334388720685283 ], [ 34.961187884958441, -17.334400587778653 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.14", "sub_field": "1.14C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.96199524794266, -17.324405539234302 ], [ 34.964747977596112, -17.324162087220788 ], [ 34.964754107256297, -17.324228233923105 ], [ 34.964759198639079, -17.324362472761511 ], [ 34.964756968599289, -17.324496783565166 ], [ 34.964747423233277, -17.324630798198889 ], [ 34.964730588688205, -17.324764149338428 ], [ 34.964706511090462, -17.324896471477366 ], [ 34.964675256419525, -17.325027401928896 ], [ 34.964636910327144, -17.325156581819918 ], [ 34.96459157790283, -17.325283657074714 ], [ 34.964539383385976, -17.325408279385538 ], [ 34.964480469825446, -17.325530107167204 ], [ 34.964414998687616, -17.325648806493543 ], [ 34.964343149413992, -17.32576405201263 ], [ 34.964265118929369, -17.325875527838612 ], [ 34.964181121102321, -17.325982928417645 ], [ 34.964091386158998, -17.326085959365418 ], [ 34.963996160052147, -17.326184338274128 ], [ 34.963895703787074, -17.326277795486632 ], [ 34.963790292706257, -17.326366074835622 ], [ 34.96368021573462, -17.326448934345862 ], [ 34.963565774587678, -17.326526146897482 ], [ 34.9634472829445, -17.32659750084861 ], [ 34.963325065587895, -17.326662800615537 ], [ 34.963199457514115, -17.326721867208889 ], [ 34.963070803014624, -17.32677453872428 ], [ 34.962939454732314, -17.326820670786223 ], [ 34.962917097922038, -17.326827271055144 ], [ 34.96199524794266, -17.324405539234302 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.14", "sub_field": "1.14A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.96199524794266, -17.324405539234302 ], [ 34.959429692942393, -17.324632437480492 ], [ 34.959421977630143, -17.324549153958536 ], [ 34.959416890093451, -17.32441491459441 ], [ 34.959419124030532, -17.324280603456128 ], [ 34.959428673302277, -17.324146588682531 ], [ 34.959445511718769, -17.324013237599395 ], [ 34.959469593111187, -17.323880915712522 ], [ 34.9595008514585, -17.323749985705984 ], [ 34.959539201068687, -17.323620806447984 ], [ 34.959584536813644, -17.323493732007297 ], [ 34.959636734417636, -17.323369110682769 ], [ 34.959695650797968, -17.323247284048762 ], [ 34.95976112445738, -17.323128586018875 ], [ 34.95983297592673, -17.323013341930839 ], [ 34.959911008257158, -17.322901867654821 ], [ 34.959995007559861, -17.322794468727679 ], [ 34.960084743592546, -17.322691439515644 ], [ 34.96017997039052, -17.322593062407474 ], [ 34.960280426940905, -17.322499607040605 ], [ 34.960385837898102, -17.322411329562119 ], [ 34.960495914338523, -17.322328471926756 ], [ 34.960610354552486, -17.322251261233856 ], [ 34.960728844871134, -17.322179909104896 ], [ 34.960851060526181, -17.322114611103654 ], [ 34.960976666540034, -17.322055546200151 ], [ 34.96108398180769, -17.322011611539249 ], [ 34.96199524794266, -17.324405539234302 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.14", "sub_field": "1.14B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.964747977596112, -17.324162087220788 ], [ 34.96199524794266, -17.324405539234302 ], [ 34.96108398180769, -17.322011611539249 ], [ 34.961105318643774, -17.322002876280248 ], [ 34.96123666422077, -17.321956745701957 ], [ 34.961370343273025, -17.321917280899935 ], [ 34.961505989407819, -17.321884590038856 ], [ 34.961643230841815, -17.321858762717138 ], [ 34.961781691419915, -17.321839869721348 ], [ 34.961920991646146, -17.321827962832202 ], [ 34.962060749723705, -17.32182307468274 ], [ 34.962200582601191, -17.321825218668891 ], [ 34.96234010702242, -17.321834388912762 ], [ 34.962478940576688, -17.321850560278769 ], [ 34.962616702746779, -17.321873688442555 ], [ 34.962753015951741, -17.321903710012467 ], [ 34.962887506581652, -17.321940542703295 ], [ 34.963019806021485, -17.321984085561798 ], [ 34.963149551661324, -17.32203421924336 ], [ 34.963276387890048, -17.322090806339091 ], [ 34.963399967069982, -17.322153691752426 ], [ 34.963519950489498, -17.322222703124122 ], [ 34.963636009291434, -17.32229765130468 ], [ 34.963747825374263, -17.322378330872727 ], [ 34.963855092263934, -17.322464520697949 ], [ 34.963957515953815, -17.322555984547151 ], [ 34.964054815710554, -17.322652471731679 ], [ 34.964146724843467, -17.322753717794438 ], [ 34.96423299143548, -17.322859445234723 ], [ 34.964313379033676, -17.322969364268623 ], [ 34.96438766729743, -17.323083173623363 ], [ 34.964455652602332, -17.323200561362906 ], [ 34.964517148598397, -17.323321205742843 ], [ 34.964571986720919, -17.323444776092288 ], [ 34.964620016652553, -17.323570933720035 ], [ 34.964661106735534, -17.323699332842892 ], [ 34.964695144332502, -17.323829621533367 ], [ 34.96472203613552, -17.323961442684205 ], [ 34.964741708421862, -17.324094434987114 ], [ 34.964747977596112, -17.324162087220788 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.13", "sub_field": "1.13C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.954371207002062, -17.320983642849392 ], [ 34.954988844373986, -17.325074100205931 ], [ 34.950800585931511, -17.325458685865403 ], [ 34.950798085682457, -17.325441774613314 ], [ 34.95077858621412, -17.32523117967385 ], [ 34.950770588566108, -17.325019893135909 ], [ 34.950774114620295, -17.324808494126536 ], [ 34.9507891546723, -17.324597562079067 ], [ 34.950815667458698, -17.324387675145019 ], [ 34.950853580270376, -17.324179408609258 ], [ 34.950902789152352, -17.323973333313319 ], [ 34.950963159189094, -17.323770014090702 ], [ 34.951034524874757, -17.323570008218802 ], [ 34.951116690567204, -17.323373863891501 ], [ 34.951209431024573, -17.323182118716737 ], [ 34.95131249202305, -17.322995298242994 ], [ 34.951425591053983, -17.322813914518999 ], [ 34.951548418098469, -17.322638464690389 ], [ 34.951680636477363, -17.322469429637149 ], [ 34.951821883774279, -17.322307272655788 ], [ 34.951971772829168, -17.322152438189679 ], [ 34.952129892799526, -17.322005350611001 ], [ 34.952295810286643, -17.321866413057819 ], [ 34.952469070523492, -17.321736006329285 ], [ 34.952649198621224, -17.321614487842119 ], [ 34.952835700870786, -17.321502190651106 ], [ 34.95302806609596, -17.321399422536551 ], [ 34.953225767054377, -17.32130646516076 ], [ 34.953428261882415, -17.321223573296251 ], [ 34.953634995580217, -17.321150974127637 ], [ 34.953845401532618, -17.321088866629136 ], [ 34.95405890306192, -17.321037421019309 ], [ 34.954274915008149, -17.320996778294667 ], [ 34.954371207002062, -17.320983642849392 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.13", "sub_field": "1.13A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.955577723835631, -17.328974101137693 ], [ 34.954988844373986, -17.325074100205931 ], [ 34.959167806457437, -17.324690368182022 ], [ 34.959171141296942, -17.324726367099057 ], [ 34.959179148473709, -17.324937652338512 ], [ 34.959175632074597, -17.325149050522835 ], [ 34.959160601698166, -17.325359982227685 ], [ 34.95913409850187, -17.32556986930533 ], [ 34.959096195089728, -17.325778136469321 ], [ 34.95904699531377, -17.325984212871251 ], [ 34.958986633989731, -17.3261875336655 ], [ 34.958915276527989, -17.326387541557416 ], [ 34.958833118480612, -17.326583688331024 ], [ 34.958740385005683, -17.326775436351578 ], [ 34.958637330250511, -17.326962260039437 ], [ 34.958524236655357, -17.327143647310606 ], [ 34.958401414179555, -17.327319100980638 ], [ 34.958269199452211, -17.327488140127418 ], [ 34.958127954849679, -17.327650301409609 ], [ 34.957978067502509, -17.327805140336711 ], [ 34.957819948234501, -17.327952232487675 ], [ 34.957654030436665, -17.328091174674338 ], [ 34.957480768879407, -17.328221586046784 ], [ 34.957300638466052, -17.328343109137428 ], [ 34.957114132931054, -17.328455410841006 ], [ 34.956921763486584, -17.328558183327818 ], [ 34.956724057421276, -17.328651144887623 ], [ 34.956521556654678, -17.328734040702091 ], [ 34.956314816251719, -17.328806643543295 ], [ 34.95610440290104, -17.328868754396808 ], [ 34.955890893361392, -17.328920203007311 ], [ 34.955674872880472, -17.3289608483454 ], [ 34.955577723835631, -17.328974101137693 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.13", "sub_field": "1.13D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.954694649227697, -17.320949807844581 ], [ 34.954492845332638, -17.320967049843368 ], [ 34.954371207002062, -17.320983642849392 ], [ 34.954988844373986, -17.325074100205931 ], [ 34.959167806457437, -17.324690368182022 ], [ 34.959151632530556, -17.324515773917259 ], [ 34.9591206756849, -17.324306450007203 ], [ 34.959078355647378, -17.324098969102089 ], [ 34.959024788449319, -17.323893899881941 ], [ 34.958960120947573, -17.323691804414914 ], [ 34.958884530421642, -17.323493236616837 ], [ 34.958798224087445, -17.323298740733154 ], [ 34.958701438529012, -17.323108849847333 ], [ 34.958594439049804, -17.322924084419899 ], [ 34.958477518945266, -17.322744950862091 ], [ 34.958350998698776, -17.322571940148009 ], [ 34.958215225103011, -17.322405526469097 ], [ 34.958070570309403, -17.32224616593458 ], [ 34.957917430807903, -17.322094295321598 ], [ 34.957756226340365, -17.321950330878167 ], [ 34.957587398750036, -17.321814667182498 ], [ 34.957411410770504, -17.32168767606171 ], [ 34.957228744757572, -17.321569705572823 ], [ 34.957039901367345, -17.321461079048959 ], [ 34.956845398184079, -17.321362094213331 ], [ 34.956645768301847, -17.321273022363293 ], [ 34.956441558863588, -17.321194107626912 ], [ 34.956233329561812, -17.321125566293954 ], [ 34.956021651104756, -17.321067586223158 ], [ 34.955807103652589, -17.321020326327432 ], [ 34.955590275227564, -17.320983916138314 ], [ 34.955489834358993, -17.320972213087796 ], [ 34.955350371836573, -17.320966445369216 ], [ 34.954694649227697, -17.320949807844581 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.12", "sub_field": "1.12D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.953639390624843, -17.331963065750791 ], [ 34.952025040374643, -17.329583299853645 ], [ 34.952098160034545, -17.329533973616417 ], [ 34.952228758810662, -17.32945534029977 ], [ 34.952363463216741, -17.329383379493198 ], [ 34.952501904045853, -17.329318288427501 ], [ 34.952643701851599, -17.329260245504308 ], [ 34.952788467988015, -17.32920940980728 ], [ 34.952935805674699, -17.329165920666156 ], [ 34.953085311084202, -17.329129897274889 ], [ 34.953236574448724, -17.329101438365029 ], [ 34.953389181183084, -17.329080621935237 ], [ 34.953542713020894, -17.329067505037447 ], [ 34.953696749160791, -17.329062123620638 ], [ 34.953850867419632, -17.329064492432295 ], [ 34.954004645389389, -17.329074604977993 ], [ 34.954157661594884, -17.32909243353928 ], [ 34.954309496648627, -17.32911792924957 ], [ 34.954459734400203, -17.329151022228181 ], [ 34.954607963076747, -17.329191621771777 ], [ 34.954753776411302, -17.329239616603008 ], [ 34.954896774756236, -17.329294875175442 ], [ 34.955036566178386, -17.329357246034085 ], [ 34.955172767533284, -17.329426558230505 ], [ 34.955305005515051, -17.329502621791256 ], [ 34.955432917679516, -17.329585228238543 ], [ 34.955556153437534, -17.329674151161573 ], [ 34.955674375015825, -17.329769146837066 ], [ 34.955787258382678, -17.329869954897095 ], [ 34.955894494136068, -17.329976299042801 ], [ 34.955995788351686, -17.330087887801479 ], [ 34.956090863388482, -17.330204415325397 ], [ 34.956179458649743, -17.330325562230108 ], [ 34.956244881516085, -17.330425794331145 ], [ 34.953639390624843, -17.331963065750791 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.12", "sub_field": "1.12B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.953639390624843, -17.331963065750791 ], [ 34.955236330182871, -17.33431716598372 ], [ 34.955224718727678, -17.334324157333015 ], [ 34.955090012251418, -17.334396120283976 ], [ 34.954951568916741, -17.334461213401692 ], [ 34.954809768197514, -17.334519258262056 ], [ 34.954664998771598, -17.334570095760149 ], [ 34.954517657455462, -17.334613586546542 ], [ 34.954368148116309, -17.33464961140923 ], [ 34.954216880565006, -17.334678071600539 ], [ 34.954064269432592, -17.334698889107798 ], [ 34.953910733033638, -17.334712006867239 ], [ 34.953756692219486, -17.334717388920446 ], [ 34.9536025692245, -17.334715020512974 ], [ 34.953448786508559, -17.334704908134796 ], [ 34.953295765598902, -17.334687079502569 ], [ 34.953143925934526, -17.334661583483619 ], [ 34.952993683716358, -17.334628489962032 ], [ 34.952845450766205, -17.334587889647075 ], [ 34.952699633397849, -17.334539893824552 ], [ 34.952556631303125, -17.334484634051776 ], [ 34.952416836456244, -17.334422261796878 ], [ 34.952280632039226, -17.334352948023589 ], [ 34.952148391391503, -17.334276882722619 ], [ 34.952020476986448, -17.334194274390857 ], [ 34.951897239437749, -17.334105349459705 ], [ 34.95177901653831, -17.33401035167449 ], [ 34.951666132334324, -17.333909541426202 ], [ 34.951558896236968, -17.333803195037721 ], [ 34.951457602174337, -17.333691604006312 ], [ 34.951362527785797, -17.333575074204578 ], [ 34.951273933660957, -17.333453925041969 ], [ 34.951229068073914, -17.333385185468085 ], [ 34.953639390624843, -17.331963065750791 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.12", "sub_field": "1.12C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.952025040374643, -17.329583299853645 ], [ 34.953639390624843, -17.331963065750791 ], [ 34.951229068073914, -17.333385185468085 ], [ 34.951192062625495, -17.333328488589178 ], [ 34.951117139075599, -17.333199108667912 ], [ 34.951049368362995, -17.333066139908343 ], [ 34.9509889362322, -17.332929946777021 ], [ 34.950936008311515, -17.33279090257782 ], [ 34.950890729659179, -17.332649388428607 ], [ 34.950853224365929, -17.332505792216605 ], [ 34.950823595214999, -17.332360507535061 ], [ 34.95080192340064, -17.332213932604489 ], [ 34.950788268305715, -17.332066469180983 ], [ 34.950782667339162, -17.331918521455069 ], [ 34.950785135833648, -17.331770494943793 ], [ 34.950795667003739, -17.331622795379207 ], [ 34.950814231964728, -17.33147582759624 ], [ 34.950840779812026, -17.331329994423033 ], [ 34.950875237760897, -17.331185695576949 ], [ 34.950917511346177, -17.331043326568849 ], [ 34.950967484681314, -17.330903277619115 ], [ 34.951025020776335, -17.330765932588115 ], [ 34.951089961913382, -17.330631667924091 ], [ 34.951162130079283, -17.330500851631427 ], [ 34.951241327453488, -17.330373842262006 ], [ 34.951327336950605, -17.33025098793253 ], [ 34.951419922815354, -17.330132625370453 ], [ 34.951518831268977, -17.330019078991043 ], [ 34.951623791204831, -17.329910660008423 ], [ 34.95173451493163, -17.329807665582468 ], [ 34.951850698961884, -17.329710378004542 ], [ 34.951972024843911, -17.32961906392379 ], [ 34.952025040374643, -17.329583299853645 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.17", "sub_field": "1.17B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.951827023263888, -17.31509680237874 ], [ 34.954026875506962, -17.314612148368443 ], [ 34.95460479566993, -17.316838550983693 ], [ 34.954582300191881, -17.316843971690474 ], [ 34.954464390420419, -17.316866156967695 ], [ 34.954345433420492, -17.316882384371478 ], [ 34.954225755255891, -17.316892609420933 ], [ 34.954105683967548, -17.316896804087772 ], [ 34.953985548674133, -17.316894956873099 ], [ 34.953865678669999, -17.316887072839005 ], [ 34.953746402522292, -17.316873173594651 ], [ 34.953628047170405, -17.316853297237095 ], [ 34.953510937029606, -17.316827498246788 ], [ 34.953395393101793, -17.316795847338348 ], [ 34.953281732095512, -17.316758431266628 ], [ 34.953170265557723, -17.316715352588957 ], [ 34.953061299019829, -17.316666729384 ], [ 34.95295513116006, -17.316612694928025 ], [ 34.952852052984774, -17.316553397329692 ], [ 34.95275234703076, -17.316488999123926 ], [ 34.952656286590667, -17.316419676826428 ], [ 34.952564134963964, -17.316345620449805 ], [ 34.95247614473513, -17.316267032982701 ], [ 34.952392557081382, -17.316184129833367 ], [ 34.952313601111499, -17.316097138239147 ], [ 34.952239493237947, -17.31600629664365 ], [ 34.952170436583643, -17.315911854043023 ], [ 34.952106620425212, -17.315814069303528 ], [ 34.952048219674296, -17.31571321045185 ], [ 34.95199539439816, -17.315609553940433 ], [ 34.951948289380944, -17.315503383889688 ], [ 34.951907033726947, -17.315394991309187 ], [ 34.951871740506874, -17.315284673299935 ], [ 34.951842506447925, -17.315172732240043 ], [ 34.951827023263888, -17.31509680237874 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.17", "sub_field": "1.17D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.95629602563065, -17.314112227198336 ], [ 34.954026875506962, -17.314612148368443 ], [ 34.953494750340077, -17.312562168216999 ], [ 34.953582389133082, -17.312541050248115 ], [ 34.953700296363273, -17.312518865749148 ], [ 34.953819250693684, -17.312502638999813 ], [ 34.953938926089734, -17.312492414473798 ], [ 34.954058994540823, -17.312488220193675 ], [ 34.95417912695936, -17.312490067654078 ], [ 34.954298994082514, -17.312497951790249 ], [ 34.954418267374685, -17.312511850991893 ], [ 34.954536619927801, -17.312531727162444 ], [ 34.954653727357204, -17.312557525823461 ], [ 34.954769268690725, -17.312589176263966 ], [ 34.954882927248256, -17.312626591734215 ], [ 34.954994391509658, -17.312669669683476 ], [ 34.955103355968461, -17.312718292041072 ], [ 34.955209521969209, -17.312772325539996 ], [ 34.955312598525929, -17.312831622082093 ], [ 34.955412303119552, -17.312896019143999 ], [ 34.955508362472351, -17.312965340222579 ], [ 34.955600513296758, -17.313039395318516 ], [ 34.955688503017065, -17.313117981457257 ], [ 34.955772090461657, -17.313200883245095 ], [ 34.955851046524032, -17.313287873459569 ], [ 34.955925154790734, -17.313378713672218 ], [ 34.955994212134506, -17.313473154902031 ], [ 34.956058029271169, -17.313570938297769 ], [ 34.956116431278367, -17.313671795847476 ], [ 34.956169258075107, -17.313775451112978 ], [ 34.956216364860559, -17.31388161998748 ], [ 34.956257622511032, -17.313990011474338 ], [ 34.956292917933979, -17.3141003284845 ], [ 34.95629602563065, -17.314112227198336 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.17", "sub_field": "1.17C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.954026875506962, -17.314612148368443 ], [ 34.951827023263888, -17.31509680237874 ], [ 34.951819411668815, -17.315059474955834 ], [ 34.951802519460308, -17.314945211880861 ], [ 34.951791876111791, -17.314830256204942 ], [ 34.951787510784577, -17.314714923015753 ], [ 34.951789435432069, -17.314599528435117 ], [ 34.951797644767126, -17.314484388752604 ], [ 34.951812116276713, -17.314369819558468 ], [ 34.95183281028369, -17.314256134878732 ], [ 34.951859670055732, -17.314143646314449 ], [ 34.951892621960923, -17.314032662187568 ], [ 34.95193157566974, -17.31392348669598 ], [ 34.951976424402709, -17.313816419079632 ], [ 34.952027045223218, -17.313711752800415 ], [ 34.952083299374593, -17.313609774737873 ], [ 34.952145032660468, -17.313510764402817 ], [ 34.952212075867564, -17.313414993171374 ], [ 34.952284245229521, -17.313322723541155 ], [ 34.952361342930651, -17.31323420841176 ], [ 34.952443157648254, -17.313149690391761 ], [ 34.952529465131775, -17.313069401133724 ], [ 34.95262002881752, -17.312993560699343 ], [ 34.952714600477137, -17.312922376956276 ], [ 34.952812920897877, -17.312856045008573 ], [ 34.952914720593142, -17.312794746661851 ], [ 34.95301972054105, -17.312738649925091 ], [ 34.953127632949226, -17.31268790855022 ], [ 34.953238162043483, -17.312642661610678 ], [ 34.953351004878527, -17.312603033120357 ], [ 34.953465852168193, -17.312569131693717 ], [ 34.953494750340077, -17.312562168216999 ], [ 34.954026875506962, -17.314612148368443 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.16", "sub_field": "1.16D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.957182302821295, -17.318536599276882 ], [ 34.955074672069358, -17.318897254404234 ], [ 34.954661651915764, -17.316980166566335 ], [ 34.954671838084693, -17.316977712023718 ], [ 34.954779599945176, -17.316957436047787 ], [ 34.954888318856462, -17.316942605057037 ], [ 34.954997696836713, -17.316933259699976 ], [ 34.955107434097982, -17.316929425589755 ], [ 34.955217229867827, -17.316931113234073 ], [ 34.955326783213508, -17.316938318006393 ], [ 34.955435793866819, -17.316951020158577 ], [ 34.955543963046949, -17.316969184875028 ], [ 34.955650994279274, -17.316992762368198 ], [ 34.955756594207948, -17.317021688014929 ], [ 34.955860473399802, -17.31705588253363 ], [ 34.955962347137593, -17.3170952522016 ], [ 34.9560619362003, -17.317139689111766 ], [ 34.956158967628355, -17.317189071468594 ], [ 34.956253175471744, -17.317243263921736 ], [ 34.956344301518882, -17.317302117937068 ], [ 34.956432096004264, -17.317365472203772 ], [ 34.956516318293041, -17.317433153076365 ], [ 34.956596737540529, -17.317504975050671 ], [ 34.956673133324919, -17.317580741272227 ], [ 34.956745296251377, -17.317660244075764 ], [ 34.956813028526042, -17.317743265554331 ], [ 34.956876144498082, -17.31782957815658 ], [ 34.956934471168672, -17.317918945310385 ], [ 34.956987848665094, -17.318011122071191 ], [ 34.957036130679015, -17.318105855793377 ], [ 34.957079184867595, -17.318202886822661 ], [ 34.957116893216238, -17.31830194920779 ], [ 34.957149152362099, -17.318402771429408 ], [ 34.957175873877567, -17.318505077144206 ], [ 34.957182302821295, -17.318536599276882 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.16", "sub_field": "1.16B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.954859932451754, -17.320941430306988 ], [ 34.954821771081448, -17.320936983748155 ], [ 34.954713599562865, -17.320918818875519 ], [ 34.954606566065088, -17.320895241107298 ], [ 34.954500963968805, -17.320866315069665 ], [ 34.954397082730935, -17.320832120048479 ], [ 34.954295207091114, -17.320792749771989 ], [ 34.95419561629123, -17.320748312153899 ], [ 34.954098583309857, -17.320698928997505 ], [ 34.954004374114035, -17.320644735661915 ], [ 34.95391324693022, -17.320585880690935 ], [ 34.953825451536296, -17.320522525405838 ], [ 34.953741228577087, -17.320454843463274 ], [ 34.95366080890463, -17.320383020379118 ], [ 34.953584412945403, -17.320307253020022 ], [ 34.953512250096132, -17.320227749063722 ], [ 34.953444518149837, -17.320144726429767 ], [ 34.95338140275377, -17.320058412682187 ], [ 34.953323076900439, -17.319969044405653 ], [ 34.95326970045361, -17.319876866557024 ], [ 34.953221419710069, -17.319782131793826 ], [ 34.953178366998799, -17.319685099781751 ], [ 34.953140660318205, -17.319586036482843 ], [ 34.953108403012827, -17.31948521342645 ], [ 34.953081683490133, -17.319382906965 ], [ 34.953060574978281, -17.319279397516461 ], [ 34.953055168419084, -17.319242829332627 ], [ 34.955074672069358, -17.318897254404234 ], [ 34.955511377817565, -17.320924282071903 ], [ 34.955477979112175, -17.320930566296397 ], [ 34.955369257970489, -17.320945397833995 ], [ 34.95529630929034, -17.320951630797449 ], [ 34.954859932451754, -17.320941430306988 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.16", "sub_field": "1.16A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.955074672069358, -17.318897254404234 ], [ 34.957182302821295, -17.318536599276882 ], [ 34.95719698451262, -17.318608585942435 ], [ 34.957212426395785, -17.318713014116351 ], [ 34.95722215719276, -17.318818075437807 ], [ 34.957226150222589, -17.318923481942836 ], [ 34.957224394530947, -17.319028944720859 ], [ 34.957216894920172, -17.319134174706594 ], [ 34.957203671936284, -17.319238883472298 ], [ 34.957184761812783, -17.319342784018371 ], [ 34.957160216371385, -17.319445591560026 ], [ 34.957130102880129, -17.319547024307777 ], [ 34.957094503869122, -17.319646804239927 ], [ 34.957053516904331, -17.31974465786454 ], [ 34.957007254320388, -17.319840316969159 ], [ 34.956955842912699, -17.319933519355892 ], [ 34.956899423589938, -17.320024009560218 ], [ 34.956838150988013, -17.320111539551114 ], [ 34.956772193046199, -17.320195869411073 ], [ 34.956701730546861, -17.320276767993608 ], [ 34.956626956620106, -17.320354013556926 ], [ 34.956548076214318, -17.320427394371769 ], [ 34.956465305534444, -17.320496709301732 ], [ 34.95637887144953, -17.320561768354683 ], [ 34.956289010870705, -17.320622393203507 ], [ 34.956195970101952, -17.320678417675055 ], [ 34.956100004164895, -17.320729688205471 ], [ 34.956001376099834, -17.32077606426137 ], [ 34.955900356244626, -17.320817418724921 ], [ 34.955797221493775, -17.320853638242355 ], [ 34.955692254539301, -17.320884623534763 ], [ 34.955585743095874, -17.320910289670167 ], [ 34.955511377817565, -17.320924282071903 ], [ 34.955074672069358, -17.318897254404234 ] ] ] } },
{ "type": "Feature", "properties": { "field": "6.2", "sub_field": "6.2D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.933686532028823, -17.34741850738671 ], [ 34.930918907739319, -17.350619195960071 ], [ 34.930749371120577, -17.350482950059067 ], [ 34.9305831090279, -17.350334454986911 ], [ 34.930425167481324, -17.35017780671259 ], [ 34.930275979390657, -17.350013434618159 ], [ 34.930135953668632, -17.349841789256359 ], [ 34.930005474110168, -17.349663341115505 ], [ 34.929884898340468, -17.349478579329688 ], [ 34.929774556834893, -17.349288010337819 ], [ 34.929674752013298, -17.349092156495377 ], [ 34.929585757411353, -17.348891554642378 ], [ 34.92950781693105, -17.348686754631803 ], [ 34.929441144172401, -17.348478317822277 ], [ 34.9293859218484, -17.348266815539219 ], [ 34.929342301284514, -17.348052827508759 ], [ 34.929310402004319, -17.347836940268596 ], [ 34.92929031140222, -17.347619745560188 ], [ 34.929282084504514, -17.347401838706777 ], [ 34.929285743818809, -17.347183816981492 ], [ 34.92930127927297, -17.346966277970296 ], [ 34.929328648243064, -17.346749817933908 ], [ 34.929367775670698, -17.346535030173548 ], [ 34.929418554269198, -17.34632250340475 ], [ 34.929480844818194, -17.346112820143716 ], [ 34.929554476545505, -17.345906555110741 ], [ 34.929639247595759, -17.345704273655056 ], [ 34.929734925584, -17.345506530205324 ], [ 34.929841248233032, -17.345313866750129 ], [ 34.929957924092612, -17.34512681135249 ], [ 34.930084633338602, -17.344945876702717 ], [ 34.930221028649861, -17.34477155871328 ], [ 34.930343832257769, -17.344630621189719 ], [ 34.933686532028823, -17.34741850738671 ] ] ] } },
{ "type": "Feature", "properties": { "field": "6.2", "sub_field": "6.2A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.936458268544008, -17.344213063126752 ], [ 34.933686532028823, -17.34741850738671 ], [ 34.930343832257769, -17.344630621189719 ], [ 34.930366736160408, -17.344604335159747 ], [ 34.93052135648437, -17.344444664371434 ], [ 34.930684465810785, -17.344292983975372 ], [ 34.93085561706534, -17.344149709696961 ], [ 34.931034341135764, -17.344015234220802 ], [ 34.931220148157657, -17.343889926114542 ], [ 34.931412528857074, -17.343774128818861 ], [ 34.931610955946326, -17.343668159706397 ], [ 34.931814885569068, -17.343572309212028 ], [ 34.93202375879077, -17.343486840037041 ], [ 34.932237003130467, -17.34341198642927 ], [ 34.932454034129648, -17.343347953541233 ], [ 34.932674256953796, -17.343294916868011 ], [ 34.932897068022541, -17.343253021766351 ], [ 34.933121856663583, -17.343222383056439 ], [ 34.933348006786112, -17.343203084707323 ], [ 34.933574898569034, -17.343195179606763 ], [ 34.933801910159438, -17.343198689416489 ], [ 34.934028419376581, -17.343213604512808 ], [ 34.934253805416724, -17.343239884013023 ], [ 34.934477450554319, -17.343277455887517 ], [ 34.93469874183473, -17.343326217157209 ], [ 34.934917072753706, -17.343386034175726 ], [ 34.935131844919461, -17.343456742995702 ], [ 34.935342469692365, -17.343538149818031 ], [ 34.935548369797949, -17.343630031523016 ], [ 34.935748980908848, -17.343732136281769 ], [ 34.935943753191204, -17.343844184246329 ], [ 34.936132152811439, -17.343965868316541 ], [ 34.936313663399275, -17.34409685498165 ], [ 34.936458268544008, -17.344213063126752 ] ] ] } },
{ "type": "Feature", "properties": { "field": "6.1", "sub_field": "6.1C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.924014427740026, -17.344857153121342 ], [ 34.926975301324241, -17.341756277068452 ], [ 34.930206135800262, -17.344526394378821 ], [ 34.93019316385849, -17.344541281858941 ], [ 34.93003854325503, -17.344700952461203 ], [ 34.929875433121211, -17.344852632448269 ], [ 34.929704280527545, -17.344995906055839 ], [ 34.929525554592857, -17.345130380560647 ], [ 34.929339745198348, -17.345255687357142 ], [ 34.929147361644837, -17.345371482967963 ], [ 34.92894893125672, -17.34547744998568 ], [ 34.928744997936427, -17.345573297942956 ], [ 34.928536120673414, -17.345658764108965 ], [ 34.928322872011776, -17.345733614209617 ], [ 34.928105836480654, -17.345797643070011 ], [ 34.92788560899178, -17.345850675176816 ], [ 34.927662793208455, -17.345892565159698 ], [ 34.927437999890635, -17.345923198189734 ], [ 34.927211845220448, -17.345942490294355 ], [ 34.926984949112807, -17.345950388587646 ], [ 34.926757933515866, -17.345946871415304 ], [ 34.926531420705857, -17.345931948414133 ], [ 34.926306031580957, -17.345905660485595 ], [ 34.926082383959056, -17.345868079683793 ], [ 34.925861090883807, -17.345819309017926 ], [ 34.925642758943994, -17.345759482169868 ], [ 34.925427986610323, -17.345688763127814 ], [ 34.9252173625948, -17.345607345736642 ], [ 34.925011464236583, -17.345515453166517 ], [ 34.92481085591924, -17.345413337301096 ], [ 34.924616087523482, -17.345301278046954 ], [ 34.92442769291965, -17.345179582566296 ], [ 34.924246188504156, -17.34504858443481 ], [ 34.924072071783883, -17.344908642727177 ], [ 34.924014427740026, -17.344857153121342 ] ] ] } },
{ "type": "Feature", "properties": { "field": "6.1", "sub_field": "6.1A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.929874621707043, -17.3387198647094 ], [ 34.926975301324241, -17.341756277068452 ], [ 34.923737722930689, -17.338980377523537 ], [ 34.923844300080475, -17.338870324057513 ], [ 34.924007410942906, -17.33871864893819 ], [ 34.924178563186587, -17.338575380261723 ], [ 34.92435728769653, -17.338440910697358 ], [ 34.924543094607159, -17.338315608796531 ], [ 34.924735474644862, -17.338199817982947 ], [ 34.924933900523754, -17.338093855611472 ], [ 34.925137828390824, -17.337998012098549 ], [ 34.92534669931635, -17.33791255012633 ], [ 34.925559940825678, -17.337837703922908 ], [ 34.925776968468028, -17.33777367862054 ], [ 34.925997187418091, -17.337720649693534 ], [ 34.92621999410612, -17.337678762477399 ], [ 34.926444777871808, -17.337648131770756 ], [ 34.926670922637705, -17.337628841520697 ], [ 34.926897808597396, -17.337620944592842 ], [ 34.927124813913892, -17.337624462626497 ], [ 34.927351316423618, -17.337639385975457 ], [ 34.927576695341251, -17.337665673734445 ], [ 34.927800332960729, -17.337703253851203 ], [ 34.928021616347976, -17.33775202332404 ], [ 34.928239939020372, -17.337811848484161 ], [ 34.928454702608725, -17.337882565361848 ], [ 34.928665318496876, -17.337963980135974 ], [ 34.928871209434661, -17.338055869664995 ], [ 34.929071811119861, -17.338157982098618 ], [ 34.929266573744414, -17.338270037567831 ], [ 34.929454963501193, -17.338391728951922 ], [ 34.929636464046908, -17.338522722720082 ], [ 34.929810577917074, -17.338662659845419 ], [ 34.929874621707043, -17.3387198647094 ] ] ] } },
{ "type": "Feature", "properties": { "field": "6.1", "sub_field": "6.1D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.926975301324241, -17.341756277068452 ], [ 34.924014427740026, -17.344857153121342 ], [ 34.923905820012344, -17.344760141032754 ], [ 34.923747888881501, -17.344603486403877 ], [ 34.923598711272561, -17.344439108239992 ], [ 34.923458696069524, -17.344267457110487 ], [ 34.923328227038496, -17.344089003519457 ], [ 34.92320766177577, -17.343904236615927 ], [ 34.923097330727906, -17.343713662852821 ], [ 34.922997536286125, -17.343517804598687 ], [ 34.922908551957605, -17.343317198705623 ], [ 34.922830621616164, -17.343112395037664 ], [ 34.922763958834061, -17.342903954963422 ], [ 34.922708746296955, -17.342692449817278 ], [ 34.922665135303461, -17.342478459333183 ], [ 34.922633245350909, -17.342262570055521 ], [ 34.922613163808208, -17.342045373731359 ], [ 34.922604945676802, -17.341827465688315 ], [ 34.922608613440303, -17.341609443202778 ], [ 34.922624157003398, -17.341391903862778 ], [ 34.922651533719979, -17.341175443929924 ], [ 34.922690668510434, -17.34096065670515 ], [ 34.922741454067939, -17.340748130902483 ], [ 34.922803751153019, -17.340538449035467 ], [ 34.922877388975664, -17.340332185820596 ], [ 34.922962165663776, -17.34012990660208 ], [ 34.92305784881701, -17.339932165802466 ], [ 34.923164176144006, -17.339739505403038 ], [ 34.923280856181734, -17.33955245345847 ], [ 34.923407569094621, -17.339371522649586 ], [ 34.923543967551495, -17.339197208878335 ], [ 34.923689677677785, -17.339029989908727 ], [ 34.923737722930689, -17.338980377523537 ], [ 34.926975301324241, -17.341756277068452 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.5", "sub_field": "2.5C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.917570487117061, -17.339711118027701 ], [ 34.920240082361659, -17.336946735747041 ], [ 34.923199307657669, -17.339448856263918 ], [ 34.923071705624515, -17.33958061908611 ], [ 34.922923099879576, -17.339718804051945 ], [ 34.922767167458396, -17.339849329976413 ], [ 34.922604335762102, -17.339971839080217 ], [ 34.922435051105928, -17.340085995557583 ], [ 34.922259777495817, -17.340191486496835 ], [ 34.922078995356557, -17.340288022738303 ], [ 34.921893200214804, -17.340375339667009 ], [ 34.921702901340709, -17.340453197938189 ], [ 34.921508620351901, -17.340521384133421 ], [ 34.921310889783442, -17.340579711345818 ], [ 34.921110251627987, -17.340628019692403 ], [ 34.920907255849897, -17.340666176752553 ], [ 34.920702458877493, -17.34069407793098 ], [ 34.920496422077605, -17.340711646744563 ], [ 34.920289710216586, -17.340718835032121 ], [ 34.920082889911853, -17.3407156230864 ], [ 34.919876528078532, -17.340702019708203 ], [ 34.919671190375219, -17.34067806218227 ], [ 34.91946743965304, -17.340643816175103 ], [ 34.919265834412677, -17.340599375555005 ], [ 34.919066927273107, -17.340544862134671 ], [ 34.918871263456573, -17.340480425337375 ], [ 34.918679379293778, -17.340406241787306 ], [ 34.918491800753571, -17.34032251482536 ], [ 34.918309042000999, -17.340229473951673 ], [ 34.918131603987675, -17.340127374196545 ], [ 34.917959973078531, -17.340016495421217 ], [ 34.917794619718457, -17.33989714155069 ], [ 34.917635997142689, -17.339769639740499 ], [ 34.917570487117061, -17.339711118027701 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.5", "sub_field": "2.5A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.922942052048946, -17.33414882953203 ], [ 34.920240082361659, -17.336946735747041 ], [ 34.917269399022985, -17.334434927085208 ], [ 34.917287823506996, -17.334413783914261 ], [ 34.91742869674966, -17.334268322621668 ], [ 34.917577303095733, -17.334130141694978 ], [ 34.917733235223096, -17.33399961986192 ], [ 34.917896065733828, -17.333877114856964 ], [ 34.918065348325705, -17.333762962440996 ], [ 34.918240619015357, -17.333657475481164 ], [ 34.91842139741005, -17.333560943093559 ], [ 34.918607188024126, -17.333473629850932 ], [ 34.918797481637021, -17.33339577505766 ], [ 34.91899175668879, -17.333327592094061 ], [ 34.919189480709392, -17.333269267831611 ], [ 34.919390111777922, -17.333220962120961 ], [ 34.91959310000766, -17.333182807353882 ], [ 34.919797889052994, -17.333154908100525 ], [ 34.920003917633906, -17.333137340822887 ], [ 34.920210621074162, -17.333130153665365 ], [ 34.920417432848552, -17.333133366322823 ], [ 34.920623786135408, -17.33314696998664 ], [ 34.920829115369827, -17.333170927368968 ], [ 34.921032857793406, -17.333205172804849 ], [ 34.921234454996402, -17.333249612432297 ], [ 34.921433354447913, -17.333304124449441 ], [ 34.921629011009919, -17.333368559448381 ], [ 34.921820888431192, -17.333442740824655 ], [ 34.922008460816762, -17.333526465261187 ], [ 34.922191214068988, -17.333619503285469 ], [ 34.922368647296558, -17.333721599898446 ], [ 34.922540274186922, -17.333832475273329 ], [ 34.922705624339265, -17.333951825522362 ], [ 34.922864244553487, -17.334079323529647 ], [ 34.922942052048946, -17.33414882953203 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.5", "sub_field": "2.5D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.920240082361659, -17.336946735747041 ], [ 34.917570487117061, -17.339711118027701 ], [ 34.917484540134382, -17.339634339479865 ], [ 34.917340663832782, -17.339491611633623 ], [ 34.917204762595283, -17.339341847425441 ], [ 34.917077208916524, -17.339185457365442 ], [ 34.916958352407384, -17.339022870124747 ], [ 34.916848518836794, -17.338854531360347 ], [ 34.916748009238944, -17.338680902493451 ], [ 34.916657099088269, -17.338502459444559 ], [ 34.916576037544537, -17.338319691328788 ], [ 34.916505046770205, -17.338133099115122 ], [ 34.916444321321642, -17.337943194253128 ], [ 34.916394027616185, -17.337750497270942 ], [ 34.916354303476247, -17.337555536348415 ], [ 34.916325257751936, -17.337358845869311 ], [ 34.916306970023015, -17.337160964956428 ], [ 34.916299490381085, -17.336962435993936 ], [ 34.91630283929274, -17.336763803140538 ], [ 34.91631700754381, -17.33656561083799 ], [ 34.916341956265015, -17.336368402318797 ], [ 34.916377617038826, -17.33617271811719 ], [ 34.916423892087501, -17.335979094587614 ], [ 34.91648065454131, -17.335788062434609 ], [ 34.916547748786755, -17.335600145258304 ], [ 34.916624990893368, -17.33541585811918 ], [ 34.916712169118249, -17.335235706126554 ], [ 34.916809044486669, -17.335060183054143 ], [ 34.916915351447358, -17.334889769986802 ], [ 34.917030798600706, -17.334724934001962 ], [ 34.917155069497618, -17.334566126889655 ], [ 34.917269399022985, -17.334434927085208 ], [ 34.920240082361659, -17.336946735747041 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.1", "sub_field": "2.1A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.915191282440283, -17.337693243965902 ], [ 34.913379761071603, -17.335625170111705 ], [ 34.915518445366438, -17.33371081141938 ], [ 34.91556591333611, -17.333763124395745 ], [ 34.915660056119648, -17.333878556262661 ], [ 34.915747779977927, -17.333998562055793 ], [ 34.915828844461352, -17.334122812856673 ], [ 34.915903027371279, -17.33425096811143 ], [ 34.9159701253691, -17.334382676564005 ], [ 34.916029954533762, -17.33451757721895 ], [ 34.916082350865892, -17.334655300330706 ], [ 34.916127170737504, -17.334795468417038 ], [ 34.916164291285796, -17.334937697293565 ], [ 34.916193610750071, -17.335081597126699 ], [ 34.916215048750807, -17.335226773502143 ], [ 34.916228546510219, -17.33537282850585 ], [ 34.916234067013484, -17.335519361814619 ], [ 34.916231595110446, -17.335665971793379 ], [ 34.916221137557322, -17.335812256595954 ], [ 34.91620272299842, -17.335957815266493 ], [ 34.916176401887839, -17.336102248838476 ], [ 34.916142246351313, -17.336245161428248 ], [ 34.916100349988831, -17.336386161320107 ], [ 34.916050827618207, -17.33652486203999 ], [ 34.915993814960572, -17.336660883414858 ], [ 34.915929468268565, -17.336793852614697 ], [ 34.915857963898262, -17.336923405174531 ], [ 34.915779497825845, -17.337049185993408 ], [ 34.915694285110668, -17.337170850307807 ], [ 34.915602559305853, -17.337288064636702 ], [ 34.915504571818289, -17.337400507695637 ], [ 34.915400591219608, -17.337507871277509 ], [ 34.91529090251003, -17.337609861097384 ], [ 34.915191282440283, -17.337693243965902 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.1", "sub_field": "2.1C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.911437392245574, -17.333407717834426 ], [ 34.913379761071603, -17.335625170111705 ], [ 34.911255677582851, -17.337526459469718 ], [ 34.911170570838692, -17.337442025530336 ], [ 34.911070267333443, -17.337331481742687 ], [ 34.910976125197692, -17.337216047643373 ], [ 34.910888402465794, -17.337096039638435 ], [ 34.910807339575037, -17.336971786670553 ], [ 34.910733158706634, -17.336843629317364 ], [ 34.910666063176791, -17.336711918857905 ], [ 34.910606236879538, -17.336577016309619 ], [ 34.910553843782843, -17.336439291438793 ], [ 34.910509027479215, -17.336299121746951 ], [ 34.910471910792388, -17.336156891436062 ], [ 34.910442595440756, -17.336012990355417 ], [ 34.910421161758748, -17.335867812933003 ], [ 34.910407668476871, -17.335721757094326 ], [ 34.910402152560884, -17.335575223171723 ], [ 34.910404629110673, -17.335428612807021 ], [ 34.910415091319095, -17.335282327850589 ], [ 34.910433510490847, -17.335136769259989 ], [ 34.910459836121319, -17.334992336000848 ], [ 34.910493996035243, -17.334849423953397 ], [ 34.910535896584648, -17.334708424827415 ], [ 34.910585422905875, -17.334569725088556 ], [ 34.910642439234486, -17.334433704899109 ], [ 34.910706789277619, -17.334300737076106 ], [ 34.910778296642519, -17.334171186069447 ], [ 34.910856765320155, -17.334045406963057 ], [ 34.910941980222667, -17.333923744501693 ], [ 34.911033707772944, -17.33380653214607 ], [ 34.911131696544956, -17.333694091159053 ], [ 34.911235677953037, -17.333586729725003 ], [ 34.911345366988073, -17.33348474210538 ], [ 34.911437392245574, -17.333407717834426 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.1", "sub_field": "2.1B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.913379761071603, -17.335625170111705 ], [ 34.915191282440283, -17.337693243965902 ], [ 34.915175806337395, -17.337706197599196 ], [ 34.915055618172943, -17.337796616722148 ], [ 34.914930667446747, -17.337880870624524 ], [ 34.914801296644683, -17.337958728363152 ], [ 34.914667860369669, -17.338029976526471 ], [ 34.914530724369655, -17.338094419819619 ], [ 34.914390264535022, -17.338151881599728 ], [ 34.914246865868172, -17.338202204360293 ], [ 34.914100921428201, -17.338245250162895 ], [ 34.913952831253319, -17.338280901015388 ], [ 34.913803001264291, -17.33830905919536 ], [ 34.913651842151559, -17.338329647518076 ], [ 34.913499768249508, -17.338342609548064 ], [ 34.913347196400544, -17.338347909753889 ], [ 34.913194544812328, -17.338345533605516 ], [ 34.913042231911355, -17.33833548761422 ], [ 34.912890675195818, -17.338317799314741 ], [ 34.912740290091094, -17.338292517189796 ], [ 34.912591488810882, -17.33825971053723 ], [ 34.912444679227121, -17.33821946928002 ], [ 34.912300263751881, -17.338171903719815 ], [ 34.912158638234182, -17.338117144234534 ], [ 34.912020190874792, -17.338055340920988 ], [ 34.911885301162073, -17.337986663183429 ], [ 34.911754338831614, -17.33791129926913 ], [ 34.91162766285283, -17.337829455752399 ], [ 34.911505620444707, -17.337741356968213 ], [ 34.911388546124208, -17.337647244397367 ], [ 34.911276760789143, -17.337547376004352 ], [ 34.911255677582851, -17.337526459469718 ], [ 34.913379761071603, -17.335625170111705 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.2", "sub_field": "2.2B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.909113153334729, -17.34030905206204 ], [ 34.911578911920543, -17.342468032199982 ], [ 34.911483266003707, -17.342566783644735 ], [ 34.911354818022311, -17.342686208422425 ], [ 34.911220037986965, -17.342799013216435 ], [ 34.911079295321287, -17.342904888824119 ], [ 34.910932975794793, -17.343003545035192 ], [ 34.910781480465488, -17.343094711427394 ], [ 34.910625224580507, -17.343178138107756 ], [ 34.910464636437894, -17.343253596397744 ], [ 34.9103001562125, -17.343320879460098 ], [ 34.910132234749348, -17.343379802865964 ], [ 34.909961332327725, -17.343430205100489 ], [ 34.90978791739942, -17.343471948005583 ], [ 34.909612465304448, -17.343504917158779 ], [ 34.909435456967969, -17.343529022186893 ], [ 34.909257377581909, -17.343544197013831 ], [ 34.90907871527471, -17.343550400041732 ], [ 34.90889995977318, -17.343547614265091 ], [ 34.908721601059909, -17.343535847317359 ], [ 34.908544128029973, -17.343515131450051 ], [ 34.908368027150622, -17.343485523444404 ], [ 34.908193781127558, -17.343447104455645 ], [ 34.908021867581709, -17.343399979790647 ], [ 34.907852757739754, -17.343344278619131 ], [ 34.90768691514225, -17.343280153619709 ], [ 34.907524794372911, -17.343207780561201 ], [ 34.90736683981239, -17.343127357820926 ], [ 34.907213484420012, -17.343039105840777 ], [ 34.907065148546927, -17.342943266522941 ], [ 34.906922238783757, -17.342840102566772 ], [ 34.906860112803329, -17.342790160574449 ], [ 34.909113153334729, -17.34030905206204 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.2", "sub_field": "2.2D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.909113153334729, -17.34030905206204 ], [ 34.906533452371185, -17.338050305737823 ], [ 34.90660624542992, -17.337975151605978 ], [ 34.906734693855789, -17.337855729846009 ], [ 34.906869473666958, -17.337742928108351 ], [ 34.907010215442298, -17.337637055562148 ], [ 34.907156533422004, -17.337538402384133 ], [ 34.907308026565047, -17.33744723896336 ], [ 34.907464279648224, -17.337363815160227 ], [ 34.907624864404212, -17.337288359621752 ], [ 34.907789340695246, -17.337221079155054 ], [ 34.907957257719431, -17.337162158160531 ], [ 34.908128155246089, -17.33711175812665 ], [ 34.908301564877085, -17.337070017187351 ], [ 34.908477011330419, -17.337037049743579 ], [ 34.908654013742748, -17.337012946149805 ], [ 34.908832086987054, -17.336997772466383 ], [ 34.909010743002206, -17.336991570278656 ], [ 34.909189492130309, -17.33699435658291 ], [ 34.9093678444586, -17.337006123739954 ], [ 34.909545311161978, -17.337026839495923 ], [ 34.90972140584249, -17.337056447070864 ], [ 34.909895645862328, -17.337094865314214 ], [ 34.910067553666394, -17.3371419889273 ], [ 34.910236658090895, -17.337197688751889 ], [ 34.910402495654601, -17.337261812124172 ], [ 34.910564611828974, -17.33733418329313 ], [ 34.910722562283695, -17.337414603902154 ], [ 34.910875914104444, -17.337502853532694 ], [ 34.911024246979174, -17.337598690308319 ], [ 34.911167154350139, -17.337701851557512 ], [ 34.911304244527969, -17.337812054533547 ], [ 34.91134641261079, -17.337849727197245 ], [ 34.909113153334729, -17.34030905206204 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.2", "sub_field": "2.2A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.911578911920543, -17.342468032199982 ], [ 34.909113153334729, -17.34030905206204 ], [ 34.91134641261079, -17.337849727197245 ], [ 34.91143514176526, -17.337928997189451 ], [ 34.911559487286333, -17.338052359005587 ], [ 34.91167694027061, -17.338181801868199 ], [ 34.911787178786682, -17.338316970995947 ], [ 34.911889900674801, -17.33845749591223 ], [ 34.911984824375082, -17.338602991460462 ], [ 34.91207168969926, -17.33875305885968 ], [ 34.912150258544031, -17.338907286797372 ], [ 34.912220315543756, -17.339065252556818 ], [ 34.912281668660903, -17.339226523175476 ], [ 34.912334149712621, -17.339390656631728 ], [ 34.91237761483189, -17.339557203056199 ], [ 34.912411944862086, -17.339725705964806 ], [ 34.912437045683824, -17.339895703509814 ], [ 34.912452848473158, -17.340066729745672 ], [ 34.912459309890508, -17.340238315906035 ], [ 34.912456412199738, -17.340409991688645 ], [ 34.912444163317041, -17.340581286544278 ], [ 34.912422596789526, -17.340751730966492 ], [ 34.912391771703525, -17.340920857778553 ], [ 34.912351772523039, -17.341088203413818 ], [ 34.912302708858348, -17.34125330918652 ], [ 34.912244715165983, -17.341415722548859 ], [ 34.912177950380325, -17.341574998331588 ], [ 34.912102597478352, -17.341730699964163 ], [ 34.912018862978236, -17.341882400671473 ], [ 34.911926976373564, -17.342029684643691 ], [ 34.91182718950445, -17.342172148176019 ], [ 34.911719775867432, -17.34230940077537 ], [ 34.911605029865996, -17.34244106623078 ], [ 34.911578911920543, -17.342468032199982 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.3", "sub_field": "2.3B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.899977306418968, -17.342598676400389 ], [ 34.900577206073855, -17.344757029496293 ], [ 34.90053099248378, -17.344770656279508 ], [ 34.900412035589035, -17.344799284489611 ], [ 34.900291681601324, -17.344821894278759 ], [ 34.900170260413489, -17.344838423671792 ], [ 34.900048104844025, -17.344848827360014 ], [ 34.899925549724713, -17.344853076825434 ], [ 34.899802930982794, -17.344851160418948 ], [ 34.899680584720031, -17.344843083392274 ], [ 34.899558846291384, -17.344828867883589 ], [ 34.899438049385651, -17.34480855285684 ], [ 34.899318525110722, -17.344782193994938 ], [ 34.899200601085958, -17.344749863547143 ], [ 34.899084600543979, -17.344711650130986 ], [ 34.898970841444694, -17.344667658489421 ], [ 34.898859635603621, -17.344618009203618 ], [ 34.898751287837094, -17.344562838362481 ], [ 34.898646095126765, -17.344502297189571 ], [ 34.898544345805426, -17.344436551628583 ], [ 34.898446318766688, -17.344365781888438 ], [ 34.898352282700444, -17.34429018194934 ], [ 34.898262495356455, -17.344209959030934 ], [ 34.898177202837687, -17.344125333024376 ], [ 34.898096638925864, -17.344036535889487 ], [ 34.898021024440638, -17.343943811018971 ], [ 34.897950566634307, -17.343847412571144 ], [ 34.89788545862384, -17.343747604773341 ], [ 34.897825878861511, -17.343644661197509 ], [ 34.897771990645907, -17.343538864010416 ], [ 34.897723941674307, -17.343430503200111 ], [ 34.897681863638006, -17.343319875781045 ], [ 34.897645871861357, -17.343207284979929 ], [ 34.897632654475345, -17.343156624592137 ], [ 34.899977306418968, -17.342598676400389 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.3", "sub_field": "2.3D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.899977306418968, -17.342598676400389 ], [ 34.8993698935897, -17.3404132919784 ], [ 34.899392664965589, -17.340407811872627 ], [ 34.899513016297661, -17.340385202886463 ], [ 34.899634434697063, -17.340368674166911 ], [ 34.89975658737589, -17.340358271015273 ], [ 34.899879139534015, -17.340354021943661 ], [ 34.90000175527662, -17.340355938596865 ], [ 34.900124098534818, -17.340364015720475 ], [ 34.900245833986595, -17.340378231175233 ], [ 34.900366627975728, -17.340398545997807 ], [ 34.900486149426307, -17.340424904507497 ], [ 34.900604070749914, -17.340457234458924 ], [ 34.90072006874351, -17.340495447239984 ], [ 34.90083382547509, -17.340539438114668 ], [ 34.900945029155146, -17.340589086510239 ], [ 34.901053374990944, -17.340644256347534 ], [ 34.901158566022026, -17.340704796413981 ], [ 34.901260313933982, -17.34077054077801 ], [ 34.901358339848642, -17.340841309243807 ], [ 34.901452375088432, -17.340916907845159 ], [ 34.901542161912666, -17.340997129377051 ], [ 34.901627454224055, -17.34108175396354 ], [ 34.901708018243141, -17.34117054966033 ], [ 34.901783633149101, -17.341263273090505 ], [ 34.901854091685024, -17.341359670111498 ], [ 34.901919200725978, -17.341459476511659 ], [ 34.901978781808353, -17.341562418734288 ], [ 34.902032671619118, -17.341668214627489 ], [ 34.902080722443529, -17.341776574217395 ], [ 34.902122802569984, -17.341887200502885 ], [ 34.902158796651207, -17.341999790269696 ], [ 34.902178392629686, -17.3420748920176 ], [ 34.899977306418968, -17.342598676400389 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.3", "sub_field": "2.3A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.900577206073855, -17.344757029496293 ], [ 34.899977306418968, -17.342598676400389 ], [ 34.902178392629686, -17.3420748920176 ], [ 34.902188606020459, -17.342114034921313 ], [ 34.902212148962079, -17.342229621324904 ], [ 34.9022293609356, -17.342346232669474 ], [ 34.902240194752764, -17.342463549334198 ], [ 34.902244620706924, -17.34258124976445 ], [ 34.902242626654697, -17.342699011353165 ], [ 34.902234218049344, -17.342816511325012 ], [ 34.902219417925892, -17.342933427621141 ], [ 34.902198266838255, -17.343049439781904 ], [ 34.902170822748069, -17.343164229825184 ], [ 34.902137160866111, -17.343277483118008 ], [ 34.902097373446153, -17.343388889238934 ], [ 34.902051569532283, -17.343498142828913 ], [ 34.901999874660113, -17.343604944428311 ], [ 34.901942430512797, -17.343709001297707 ], [ 34.901879394532841, -17.343810028220332 ], [ 34.901810939490574, -17.343907748283865 ], [ 34.901737253010715, -17.344001893639497 ], [ 34.901658537058189, -17.344092206236112 ], [ 34.901575007384508, -17.344178438527649 ], [ 34.901486892936596, -17.344260354151714 ], [ 34.901394435229165, -17.344337728577433 ], [ 34.901297887682823, -17.344410349720999 ], [ 34.901197514929372, -17.344478018526939 ], [ 34.901093592086589, -17.344540549513926 ], [ 34.90098640400398, -17.344597771283127 ], [ 34.900876244482049, -17.344649526988103 ], [ 34.900763415466933, -17.344695674764722 ], [ 34.900648226222749, -17.344736088120147 ], [ 34.900577206073855, -17.344757029496293 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.4", "sub_field": "2.4A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.89784433134804, -17.347383688817565 ], [ 34.896270521422032, -17.345778057340958 ], [ 34.898018534309401, -17.34427437726421 ], [ 34.898049630555178, -17.344308651412607 ], [ 34.898125245197328, -17.344401376276529 ], [ 34.898195703304218, -17.344497774650247 ], [ 34.898260811752031, -17.344597582318404 ], [ 34.898320392078851, -17.344700525720842 ], [ 34.898374280973691, -17.344806322702414 ], [ 34.898422330724294, -17.344914683286273 ], [ 34.898464409622015, -17.345025310468575 ], [ 34.898500402322924, -17.345137901032597 ], [ 34.898530210164083, -17.345252146379647 ], [ 34.898553751434022, -17.345367733374978 ], [ 34.898570961596832, -17.345484345205968 ], [ 34.898581793469205, -17.345601662250449 ], [ 34.898586217349859, -17.345719362952764 ], [ 34.898584221101068, -17.345837124705092 ], [ 34.898575810182095, -17.345954624731661 ], [ 34.898561007634342, -17.346071540973472 ], [ 34.898539854018331, -17.34618755297101 ], [ 34.898512407302668, -17.346302342742597 ], [ 34.898478742705286, -17.346415595656016 ], [ 34.898438952487425, -17.346527001290848 ], [ 34.898393145700823, -17.346636254289407 ], [ 34.898341447888967, -17.346743055193635 ], [ 34.898284000743089, -17.34684711126604 ], [ 34.898220961713911, -17.346948137292031 ], [ 34.898152503580086, -17.347045856361728 ], [ 34.898078813974848, -17.347140000629064 ], [ 34.898000094871662, -17.347230312045884 ], [ 34.897916562030716, -17.347316543069386 ], [ 34.89784433134804, -17.347383688817565 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.4", "sub_field": "2.4C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.894633955948727, -17.344108401435214 ], [ 34.896270521422032, -17.345778057340958 ], [ 34.894530375270897, -17.347274970273929 ], [ 34.89451869393659, -17.347263380014812 ], [ 34.894438130123419, -17.347174581368925 ], [ 34.894362515902834, -17.347081855064534 ], [ 34.894292058526425, -17.346985455263962 ], [ 34.894226951109985, -17.346885646198167 ], [ 34.894167372104157, -17.346782701442645 ], [ 34.894113484805423, -17.346676903167346 ], [ 34.894065436908569, -17.346568541363308 ], [ 34.894023360101947, -17.346457913047725 ], [ 34.893987369706529, -17.346345321449771 ], [ 34.893957564360043, -17.346231075179425 ], [ 34.893934025746624, -17.346115487381585 ], [ 34.893916818373043, -17.345998874877647 ], [ 34.893905989392053, -17.345881557297147 ], [ 34.893901568473275, -17.345763856201621 ], [ 34.893903567721992, -17.3456460942032 ], [ 34.893911981646049, -17.34552859408036 ], [ 34.893926787171132, -17.345411677893143 ], [ 34.893947943704113, -17.345295666100494 ], [ 34.893975393244382, -17.345180876681777 ], [ 34.894009060543098, -17.345067624265408 ], [ 34.894048853309421, -17.344956219266344 ], [ 34.894094662463672, -17.344846967035298 ], [ 34.894146362436459, -17.344740167021918 ], [ 34.894203811512831, -17.34463611195395 ], [ 34.894266852220959, -17.344535087034966 ], [ 34.894335311763747, -17.344437369162709 ], [ 34.894409002492552, -17.344343226170164 ], [ 34.894487722421609, -17.344252916091463 ], [ 34.894571255781663, -17.344166686454802 ], [ 34.894633955948727, -17.344108401435214 ] ] ] } },
{ "type": "Feature", "properties": { "field": "2.4", "sub_field": "2.4B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.896270521422032, -17.345778057340958 ], [ 34.89784433134804, -17.347383688817565 ], [ 34.897828444407587, -17.347398457340631 ], [ 34.897735983525699, -17.347475830332453 ], [ 34.897639432814358, -17.347548449964961 ], [ 34.89753905691407, -17.347616117186867 ], [ 34.897435130951216, -17.347678646521146 ], [ 34.897327939783857, -17.347735866573512 ], [ 34.897217777220966, -17.347787620502217 ], [ 34.897104945217023, -17.347833766448016 ], [ 34.896989753044323, -17.347874177923067 ], [ 34.8968725164452, -17.347908744157682 ], [ 34.896753556766491, -17.347937370403951 ], [ 34.896633200078661, -17.347959978195529 ], [ 34.896511776281947, -17.347976505562741 ], [ 34.896389618201965, -17.347986907202465 ], [ 34.896267060677431, -17.347991154602312 ], [ 34.89614443964215, -17.347989236118849 ], [ 34.896022091204195, -17.347981157009478 ], [ 34.895900350724474, -17.347966939418107 ], [ 34.895779551897398, -17.347946622314364 ], [ 34.895660025836158, -17.347920261386893 ], [ 34.895542100164981, -17.347887928890579 ], [ 34.895426098121014, -17.347849713448607 ], [ 34.89531233766828, -17.347805719809461 ], [ 34.895201130625999, -17.347756068559807 ], [ 34.89509278181383, -17.347700895793917 ], [ 34.894987588216253, -17.347640352740644 ], [ 34.894885838168513, -17.347574605348829 ], [ 34.894787810566186, -17.347503833832434 ], [ 34.894693774100759, -17.347428232176501 ], [ 34.894603986523009, -17.347348007605405 ], [ 34.894530375270897, -17.347274970273929 ], [ 34.896270521422032, -17.345778057340958 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.1", "sub_field": "3.1A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.913636518942788, -17.329266359662952 ], [ 34.911721763444021, -17.327270799151147 ], [ 34.914031362474837, -17.325516182075784 ], [ 34.914117900913318, -17.325634575111025 ], [ 34.914198960029665, -17.325758827177712 ], [ 34.914273137824281, -17.325886983615426 ], [ 34.914340230972627, -17.326018693164876 ], [ 34.914400055566908, -17.326153594827613 ], [ 34.914452447620299, -17.326291318855283 ], [ 34.914497263516544, -17.326431487763152 ], [ 34.914534380403744, -17.326573717364585 ], [ 34.914563696531239, -17.326717617824006 ], [ 34.914585131528675, -17.326862794725379 ], [ 34.914598626626493, -17.327008850153227 ], [ 34.914604144817197, -17.327155383783179 ], [ 34.914601670956962, -17.327301993979287 ], [ 34.914591211807391, -17.327448278894774 ], [ 34.91457279601719, -17.327593837573485 ], [ 34.914546474043796, -17.327738271048865 ], [ 34.914512318015369, -17.327881183437523 ], [ 34.914470421533231, -17.328022183024327 ], [ 34.914420899415532, -17.328160883336039 ], [ 34.914363887382741, -17.328296904200737 ], [ 34.914299541685793, -17.328429872789819 ], [ 34.914228038678019, -17.328559424639966 ], [ 34.914149574331894, -17.328685204652192 ], [ 34.914064363702025, -17.328806868065175 ], [ 34.913972640335864, -17.328924081400377 ], [ 34.913874655633592, -17.329036523376065 ], [ 34.913770678159203, -17.32914388578811 ], [ 34.913660992904397, -17.329245874354772 ], [ 34.913636518942788, -17.329266359662952 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.1", "sub_field": "3.1C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.909685584378018, -17.325148690849765 ], [ 34.911721763444021, -17.327270799151147 ], [ 34.909460106454006, -17.328988994353146 ], [ 34.909440549923985, -17.328967439651969 ], [ 34.909346413613996, -17.328852004133012 ], [ 34.909258696485878, -17.328731994784103 ], [ 34.909177638961573, -17.328607740551625 ], [ 34.909103463207565, -17.328479582016655 ], [ 34.909036372526053, -17.328347870461474 ], [ 34.908976550797789, -17.32821296690652 ], [ 34.908924161978199, -17.32807524112085 ], [ 34.908879349648117, -17.327935070608461 ], [ 34.908842236620366, -17.327792839573583 ], [ 34.908812924603325, -17.327648937867501 ], [ 34.908791493922287, -17.327503759919889 ], [ 34.908778003299552, -17.327357703657697 ], [ 34.908772489693582, -17.327211169414433 ], [ 34.90877496819796, -17.327064558832792 ], [ 34.908785432000194, -17.326918273763734 ], [ 34.908803852400631, -17.326772715165127 ], [ 34.908830178891286, -17.32662828200262 ], [ 34.908864339294539, -17.326485370156192 ], [ 34.908906239961134, -17.326344371335054 ], [ 34.908955766027034, -17.326205672004026 ], [ 34.90901278172857, -17.3260696523243 ], [ 34.909077130774563, -17.325936685111486 ], [ 34.909148636775029, -17.325807134813829 ], [ 34.909227103724675, -17.325681356513325 ], [ 34.909312316540387, -17.325559694952492 ], [ 34.90940404165076, -17.325442483589615 ], [ 34.909502027636492, -17.325330043684811 ], [ 34.909606005919557, -17.325222683419522 ], [ 34.909685584378018, -17.325148690849765 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.1", "sub_field": "3.1B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.911721763444021, -17.327270799151147 ], [ 34.913636518942788, -17.329266359662952 ], [ 34.913545900507494, -17.329342209523453 ], [ 34.91342571642943, -17.329432627237008 ], [ 34.913300770089045, -17.329516879657564 ], [ 34.913171403960227, -17.329594735846065 ], [ 34.913037972633042, -17.329665982395142 ], [ 34.912900841841903, -17.3297304240144 ], [ 34.912760387462882, -17.329787884065563 ], [ 34.912616994483479, -17.329838205046872 ], [ 34.912471055947115, -17.329881249024769 ], [ 34.912322971875888, -17.329916898012115 ], [ 34.912173148173757, -17.329945054291645 ], [ 34.912021995513982, -17.329965640683806 ], [ 34.911869928213214, -17.329978600758434 ], [ 34.911717363095775, -17.329983898989447 ], [ 34.911564718350839, -17.329981520852233 ], [ 34.911412412386156, -17.329971472863519 ], [ 34.911260862680884, -17.329953782563518 ], [ 34.911110484641192, -17.329928498440431 ], [ 34.910961690461392, -17.329895689797588 ], [ 34.910814887993944, -17.32985544656341 ], [ 34.910670479631406, -17.329807879044964 ], [ 34.910528861203304, -17.329753117625536 ], [ 34.910390420890941, -17.329691312407235 ], [ 34.910255538163376, -17.329622632799548 ], [ 34.910124582737076, -17.329547267054856 ], [ 34.909997913562464, -17.329465421752481 ], [ 34.90987587783988, -17.3293773212323 ], [ 34.909758810067963, -17.329283206979852 ], [ 34.909647031126617, -17.329183336964235 ], [ 34.90954084739743, -17.329077984931061 ], [ 34.909460106454006, -17.328988994353146 ], [ 34.911721763444021, -17.327270799151147 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.2", "sub_field": "3.2B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.906021160934294, -17.32453810278799 ], [ 34.904885066790669, -17.326570895183487 ], [ 34.904806230599256, -17.326530749763023 ], [ 34.904701047182016, -17.326470210855359 ], [ 34.904599306688482, -17.326404467496388 ], [ 34.904501287988097, -17.326333699888984 ], [ 34.904407259747913, -17.32625810200749 ], [ 34.904317479696211, -17.32617788106592 ], [ 34.904232193915959, -17.326093256949981 ], [ 34.904151636170468, -17.326004461614346 ], [ 34.904076027262448, -17.325911738446724 ], [ 34.90400557442895, -17.325815341600823 ], [ 34.903940470773314, -17.32571553529953 ], [ 34.903880894735885, -17.325612593110719 ], [ 34.903827009605024, -17.325506797197328 ], [ 34.90377896306962, -17.325398437543917 ], [ 34.903736886814265, -17.32528781116179 ], [ 34.903700896158483, -17.325175221274797 ], [ 34.903671089740698, -17.325060976488256 ], [ 34.903647549248021, -17.324945389942979 ], [ 34.903630339192389, -17.324828778456951 ], [ 34.903619506733925, -17.324711461656918 ], [ 34.90361508155182, -17.324593761102271 ], [ 34.903617075763009, -17.324475999403674 ], [ 34.903625483889194, -17.324358499338782 ], [ 34.903640282871983, -17.324241582967488 ], [ 34.903661432136161, -17.324125570749231 ], [ 34.903688873701093, -17.324010780664615 ], [ 34.903722532339778, -17.323897527343828 ], [ 34.903762315785151, -17.323786121204328 ], [ 34.903808114983079, -17.323676867599978 ], [ 34.903859804391431, -17.323570065984185 ], [ 34.903896908377526, -17.323502846894009 ], [ 34.906021160934294, -17.32453810278799 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.2", "sub_field": "3.2D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.906021160934294, -17.32453810278799 ], [ 34.907099870341185, -17.322607987848503 ], [ 34.907107996990085, -17.322612126146115 ], [ 34.907213178728831, -17.322672663949191 ], [ 34.907314917813316, -17.322738406113043 ], [ 34.907412935389594, -17.322809172447901 ], [ 34.907506962802877, -17.322884768993358 ], [ 34.907596742334007, -17.322964988550062 ], [ 34.907682027905636, -17.323049611247448 ], [ 34.907762585756814, -17.323138405146423 ], [ 34.907838195083627, -17.323231126874965 ], [ 34.907908648644458, -17.32332752229518 ], [ 34.907973753328015, -17.323427327199781 ], [ 34.908033330682649, -17.323530268036201 ], [ 34.908087217405573, -17.323636062656309 ], [ 34.908135265790484, -17.323744421089724 ], [ 34.908177344132525, -17.323855046338515 ], [ 34.908213337089279, -17.323967635191241 ], [ 34.908243145997119, -17.324081879053907 ], [ 34.908266689141676, -17.324197464795841 ], [ 34.908283901981953, -17.324314075607841 ], [ 34.908294737327296, -17.324431391870569 ], [ 34.908299165466907, -17.324549092030502 ], [ 34.908297174251452, -17.324666853481297 ], [ 34.908288769126365, -17.324784353448031 ], [ 34.908273973117211, -17.32490126987188 ], [ 34.908252826766599, -17.325017282292816 ], [ 34.908225388023233, -17.325132072728039 ], [ 34.908191732083218, -17.325245326543502 ], [ 34.908151951184053, -17.32535673331634 ], [ 34.908106154351955, -17.325465987685721 ], [ 34.908071600970572, -17.325537386101097 ], [ 34.906021160934294, -17.32453810278799 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.2", "sub_field": "3.2A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.904885066790669, -17.326570895183487 ], [ 34.906021160934294, -17.32453810278799 ], [ 34.908071600970572, -17.325537386101097 ], [ 34.908054467103149, -17.325572790189899 ], [ 34.90799703109991, -17.325676848086957 ], [ 34.907934003762392, -17.325777876157318 ], [ 34.907865557837262, -17.325875597485545 ], [ 34.907791880924258, -17.325969744219353 ], [ 34.907713174962034, -17.326060058303852 ], [ 34.907629655674789, -17.326146292188934 ], [ 34.907541551980941, -17.326228209507818 ], [ 34.907449105365721, -17.326305585725002 ], [ 34.907352569219306, -17.326378208751755 ], [ 34.907252208142296, -17.32644587952749 ], [ 34.907148297220381, -17.326508412565456 ], [ 34.90704112127046, -17.326565636461215 ], [ 34.906930974059705, -17.326617394362458 ], [ 34.906818157500545, -17.326663544399111 ], [ 34.906702980822907, -17.326703960072088 ], [ 34.906585759726646, -17.326738530600174 ], [ 34.906466815516076, -17.326767161223724 ], [ 34.906346474219269, -17.326789773464377 ], [ 34.906225065694215, -17.326806305340277 ], [ 34.906102922724692, -17.326816711535912 ], [ 34.905980380107977, -17.326820963526419 ], [ 34.905857773736962, -17.326819049655761 ], [ 34.905735439679553, -17.326810975168716 ], [ 34.905613713257182, -17.326796762196473 ], [ 34.905492928125767, -17.326776449696034 ], [ 34.905373415360891, -17.326750093343406 ], [ 34.905255502550368, -17.326717765380913 ], [ 34.905139512896064, -17.326679554419297 ], [ 34.90502576432803, -17.326635565194774 ], [ 34.904914568632925, -17.326585918281896 ], [ 34.904885066790669, -17.326570895183487 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.3", "sub_field": "3.3B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.900551100160158, -17.32491517010353 ], [ 34.900776626466843, -17.327745490872061 ], [ 34.900656786239431, -17.327755697621463 ], [ 34.900504221756528, -17.327760987206652 ], [ 34.900351578120073, -17.327758600371943 ], [ 34.900199273734728, -17.327748543657901 ], [ 34.900047726075364, -17.327730844628654 ], [ 34.899897350542489, -17.327705551796367 ], [ 34.899748559323548, -17.327672734488232 ], [ 34.899601760262883, -17.327632482656444 ], [ 34.899457355743735, -17.327584906631635 ], [ 34.899315741585028, -17.327530136820421 ], [ 34.899177305956393, -17.327468323347929 ], [ 34.899042428314019, -17.327399635646241 ], [ 34.898911478360418, -17.327324261989983 ], [ 34.898784815031014, -17.327242408980108 ], [ 34.898662785510126, -17.327154300977668 ], [ 34.898545724279373, -17.32706017948869 ], [ 34.898433952200669, -17.326960302502119 ], [ 34.898327775636837, -17.326854943782664 ], [ 34.898227485611763, -17.326744392120304 ], [ 34.898133357012782, -17.326628950538552 ], [ 34.898045647837129, -17.326508935463988 ], [ 34.897964598484933, -17.32638467585863 ], [ 34.897890431100279, -17.32625651231832 ], [ 34.897823348962419, -17.326124796139013 ], [ 34.897763535928661, -17.325989888353835 ], [ 34.897711155930615, -17.325852158743412 ], [ 34.897666352524929, -17.325711984822245 ], [ 34.897629248499918, -17.325569750803872 ], [ 34.897599945539312, -17.325425846547741 ], [ 34.897578523943601, -17.325280666490528 ], [ 34.897565743794004, -17.325142207305205 ], [ 34.900551100160158, -17.32491517010353 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.3", "sub_field": "3.3D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.900551100160158, -17.32491517010353 ], [ 34.900331858463275, -17.322163720229909 ], [ 34.900446486600295, -17.322159746280438 ], [ 34.900599125593352, -17.322162133497788 ], [ 34.900751425327122, -17.322172190362547 ], [ 34.900902968377679, -17.322189889308945 ], [ 34.901053339395013, -17.322215181825705 ], [ 34.901202126241159, -17.322247998589095 ], [ 34.901348921119656, -17.322288249652903 ], [ 34.901493321693145, -17.3223358246949 ], [ 34.901634932185885, -17.322390593319241 ], [ 34.901773364468404, -17.322452405413809 ], [ 34.901908239121205, -17.322521091561601 ], [ 34.902039186474497, -17.322596463505029 ], [ 34.902165847621376, -17.322678314661896 ], [ 34.902287875401406, -17.322766420691451 ], [ 34.902404935352081, -17.322860540109318 ], [ 34.90251670662547, -17.322960414949193 ], [ 34.902622882867597, -17.323065771469913 ], [ 34.902723173058085, -17.323176320905603 ], [ 34.90281730230781, -17.323291760257099 ], [ 34.902905012612401, -17.323411773122313 ], [ 34.902986063559332, -17.323536030563393 ], [ 34.903060232987073, -17.323664192008231 ], [ 34.903127317593992, -17.323795906183808 ], [ 34.903187133495692, -17.323930812078967 ], [ 34.903239516729194, -17.324068539933798 ], [ 34.90328432370238, -17.324208712253018 ], [ 34.903321431587827, -17.324350944840624 ], [ 34.903350738659526, -17.32449484785289 ], [ 34.903372164571941, -17.324640026866742 ], [ 34.90337772102832, -17.324700204781678 ], [ 34.900551100160158, -17.32491517010353 ] ] ] } },
{ "type": "Feature", "properties": { "field": "3.3", "sub_field": "3.3A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.900776626466843, -17.327745490872061 ], [ 34.900551100160158, -17.32491517010353 ], [ 34.90337772102832, -17.324700204781678 ], [ 34.903385650580439, -17.324786083960905 ], [ 34.903391159702402, -17.324932618806443 ], [ 34.903388676818878, -17.325079229764025 ], [ 34.903378208716227, -17.325225514984815 ], [ 34.903359784067653, -17.325371073511793 ], [ 34.903333453354918, -17.325515506378824 ], [ 34.903299288730132, -17.325658417704162 ], [ 34.903257383818179, -17.325799415775553 ], [ 34.903207853460337, -17.325938114123943 ], [ 34.903150833399678, -17.326074132582772 ], [ 34.903086479909113, -17.326207098330002 ], [ 34.903014969363383, -17.326336646910139 ], [ 34.902936497755626, -17.326462423233099 ], [ 34.902851280160384, -17.326584082547722 ], [ 34.902759550144168, -17.326701291386676 ], [ 34.902661559125448, -17.326813728480538 ], [ 34.902557575685506, -17.326921085638585 ], [ 34.902447884832448, -17.327023068593451 ], [ 34.902332787219926, -17.327119397807941 ], [ 34.902212598323182, -17.327209809241211 ], [ 34.902087647574284, -17.327294055072645 ], [ 34.901958277459215, -17.327371904381195 ], [ 34.901824842578996, -17.327443143778424 ], [ 34.901687708677741, -17.327507577993487 ], [ 34.901547251640118, -17.327565030408447 ], [ 34.90140385646086, -17.327615343542455 ], [ 34.901257916189486, -17.327658379483513 ], [ 34.901109830852782, -17.327694020266488 ], [ 34.900960006358247, -17.327722168196619 ], [ 34.900808853381278, -17.327742746117259 ], [ 34.900776626466843, -17.327745490872061 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.1", "sub_field": "4.1B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.886782334569538, -17.326739205117487 ], [ 34.887703979851366, -17.329372879634846 ], [ 34.8876643191578, -17.329386792202165 ], [ 34.887518373215272, -17.329429818514686 ], [ 34.887370282716603, -17.329465449470245 ], [ 34.88722045358341, -17.329493587401011 ], [ 34.887069296503846, -17.329514155177758 ], [ 34.886917225806691, -17.329527096421376 ], [ 34.886764658325532, -17.329532375657408 ], [ 34.886612012256101, -17.329529978413341 ], [ 34.886459706009731, -17.329519911258302 ], [ 34.886308157066374, -17.329502201785065 ], [ 34.886157780830047, -17.329476898534416 ], [ 34.886008989490165, -17.32944407086211 ], [ 34.885862190891373, -17.32940380874879 ], [ 34.885717787415615, -17.329356222553258 ], [ 34.88557617487897, -17.329301442710033 ], [ 34.885437741446637, -17.32923961937173 ], [ 34.885302866568736, -17.329170921997534 ], [ 34.885171919940205, -17.32909553888852 ], [ 34.885045260487331, -17.329013676671618 ], [ 34.884923235383837, -17.328925559733097 ], [ 34.884806179099137, -17.328831429603483 ], [ 34.884694412481636, -17.328731544295415 ], [ 34.88458824187915, -17.328626177596401 ], [ 34.884487958299232, -17.328515618318296 ], [ 34.884393836611494, -17.328400169505525 ], [ 34.884306134794222, -17.328280147604413 ], [ 34.884225093227393, -17.328155881595727 ], [ 34.884150934033698, -17.328027712092847 ], [ 34.884083860469936, -17.327895990408074 ], [ 34.884024056369917, -17.327761077589614 ], [ 34.883971685640724, -17.327623343431874 ], [ 34.883964047061163, -17.327599439232444 ], [ 34.886782334569538, -17.326739205117487 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.1", "sub_field": "4.1D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.886782334569538, -17.326739205117487 ], [ 34.885846577829994, -17.324065205990067 ], [ 34.885953641947083, -17.324033642826574 ], [ 34.886101728582837, -17.323998013298716 ], [ 34.886251553602811, -17.323969876608579 ], [ 34.886402706363931, -17.323949309871807 ], [ 34.886554772584716, -17.32393636945611 ], [ 34.886707335480622, -17.323931090826889 ], [ 34.88685997690618, -17.323933488449978 ], [ 34.887012278500904, -17.323943555752123 ], [ 34.887163822835845, -17.323961265138948 ], [ 34.887314194557419, -17.323986568070588 ], [ 34.887462981525729, -17.324019395194775 ], [ 34.887609775944007, -17.324059656536921 ], [ 34.887754175476097, -17.324107241746617 ], [ 34.887895784349141, -17.324162020400173 ], [ 34.888034214438093, -17.324223842358005 ], [ 34.888169086329441, -17.324292538176074 ], [ 34.88830003036098, -17.324367919570307 ], [ 34.888426687634862, -17.324449779932586 ], [ 34.888548711001285, -17.324537894896967 ], [ 34.888665766009801, -17.324632022954553 ], [ 34.888777531826008, -17.324731906115399 ], [ 34.888883702110896, -17.324837270615532 ], [ 34.888983985860385, -17.324947827667181 ], [ 34.88907810820298, -17.325063274250319 ], [ 34.889165811153212, -17.325183293943041 ], [ 34.889246854318749, -17.325307557788776 ], [ 34.889321015559332, -17.325435725197845 ], [ 34.889388091595755, -17.325567444880889 ], [ 34.889447898567113, -17.325702355811647 ], [ 34.88950027253486, -17.325840088216371 ], [ 34.889520512970037, -17.325903422939145 ], [ 34.886782334569538, -17.326739205117487 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.1", "sub_field": "4.1A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.887703979851366, -17.329372879634846 ], [ 34.886782334569538, -17.326739205117487 ], [ 34.889520512970037, -17.325903422939145 ], [ 34.889545069932325, -17.325980264587329 ], [ 34.889582167958245, -17.326122500717403 ], [ 34.889611464913656, -17.326266406753103 ], [ 34.889632880480761, -17.326411588263088 ], [ 34.889646355943256, -17.326557647319234 ], [ 34.889651854347427, -17.326704183587182 ], [ 34.889649360603691, -17.326850795423734 ], [ 34.889638881528178, -17.326997080977577 ], [ 34.889620445824171, -17.327142639290791 ], [ 34.889594104003734, -17.327287071397805 ], [ 34.889559928249412, -17.327429981418941 ], [ 34.889518012216612, -17.327570977645536 ], [ 34.889468470777103, -17.327709673613544 ], [ 34.889411439704311, -17.32784568916291 ], [ 34.889347075301409, -17.327978651479597 ], [ 34.889275553972986, -17.328108196117469 ], [ 34.889197071741769, -17.328233967997264 ], [ 34.88911184371144, -17.328355622379977 ], [ 34.889020103477137, -17.32847282581178 ], [ 34.888922102485324, -17.328585257038089 ], [ 34.888818109344669, -17.328692607884239 ], [ 34.888708409089844, -17.328794584100173 ], [ 34.888593302400324, -17.328890906167157 ], [ 34.888473104776217, -17.328981310063959 ], [ 34.888348145673561, -17.329065547990599 ], [ 34.888218767601209, -17.329143389047712 ], [ 34.888085325182047, -17.329214619869479 ], [ 34.887948184180871, -17.329279045208576 ], [ 34.887807720501797, -17.329336488471384 ], [ 34.887703979851366, -17.329372879634846 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.2", "sub_field": "4.2A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.894318941931957, -17.326404205007993 ], [ 34.892592473294087, -17.324940604483569 ], [ 34.89423852225147, -17.323363284353789 ], [ 34.894279290997311, -17.323403741786901 ], [ 34.894359843150291, -17.323492541716824 ], [ 34.894435446447254, -17.323585269172604 ], [ 34.894505893663094, -17.323681670000639 ], [ 34.894570991703901, -17.323781479978805 ], [ 34.894630562136221, -17.323884425540623 ], [ 34.894684441676247, -17.32399022452503 ], [ 34.894732482637338, -17.32409858694966 ], [ 34.894774553334969, -17.324209215805645 ], [ 34.8948105384477, -17.324321807871623 ], [ 34.894840339333385, -17.324436054544822 ], [ 34.894863874299624, -17.324551642686835 ], [ 34.894881078827837, -17.324668255481892 ], [ 34.894891905750164, -17.324785573305192 ], [ 34.89489632537888, -17.32490327459891 ], [ 34.894894325587956, -17.325021036753622 ], [ 34.894885911846373, -17.325138536992469 ], [ 34.894871107203294, -17.325255453255821 ], [ 34.894849952224995, -17.325371465084146 ], [ 34.894822504883827, -17.325486254496205 ], [ 34.894788840399457, -17.32559950686079 ], [ 34.894749051032846, -17.325710911758964 ], [ 34.894703245833377, -17.325820163835068 ], [ 34.894651550340264, -17.325926963633588 ], [ 34.894594106238337, -17.326031018420061 ], [ 34.89453107096999, -17.326132042983406 ], [ 34.894462617303638, -17.326229760417807 ], [ 34.894388932860224, -17.326323902881626 ], [ 34.894318941931957, -17.326404205007993 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.2", "sub_field": "4.2C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.890807409243124, -17.32342733014784 ], [ 34.892592473294087, -17.324940604483569 ], [ 34.890923514244896, -17.32653987812137 ], [ 34.890914484047336, -17.32653180828056 ], [ 34.890829203620619, -17.3264471778465 ], [ 34.890748651575883, -17.326358376480158 ], [ 34.890673048700258, -17.326265647585799 ], [ 34.890602602214209, -17.326169245332796 ], [ 34.890537505203675, -17.326069433958875 ], [ 34.890477936090804, -17.325966487045811 ], [ 34.890424058144973, -17.325860686769481 ], [ 34.890376019035322, -17.325752323126384 ], [ 34.890333950426125, -17.325641693138767 ], [ 34.890297967615908, -17.325529100040367 ], [ 34.890268169221613, -17.32541485244527 ], [ 34.890244636908328, -17.325299263501982 ], [ 34.890227435165592, -17.325182650035078 ], [ 34.890216611130739, -17.325065331676697 ], [ 34.890212194459821, -17.324947629990511 ], [ 34.890214197246515, -17.324829867590271 ], [ 34.890222613988975, -17.324712367255533 ], [ 34.890237421605185, -17.324595451046935 ], [ 34.890258579496297, -17.324479439423452 ], [ 34.890286029658014, -17.324364650364053 ], [ 34.890319696839796, -17.324251398496109 ], [ 34.890359488751137, -17.324139994233082 ], [ 34.890405296314732, -17.324030742923696 ], [ 34.890456993965522, -17.323923944015053 ], [ 34.890514439994988, -17.323819890231881 ], [ 34.890577476939619, -17.32371886677425 ], [ 34.890645932012674, -17.323621150535875 ], [ 34.89071961757778, -17.323527009345277 ], [ 34.890798331663291, -17.323436701231689 ], [ 34.890807409243124, -17.32342733014784 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.2", "sub_field": "4.2B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.892592473294087, -17.324940604483569 ], [ 34.894318941931957, -17.326404205007993 ], [ 34.894310219599099, -17.326414212331692 ], [ 34.894226693264422, -17.326500441230607 ], [ 34.89413858279395, -17.326582353225231 ], [ 34.89404612969151, -17.32665972379467 ], [ 34.893949587365086, -17.326732340865671 ], [ 34.893849220432202, -17.326800005393974 ], [ 34.893745303994642, -17.32686253190996 ], [ 34.893638122884369, -17.326919749027052 ], [ 34.893527970882786, -17.326971499911568 ], [ 34.893415149915398, -17.327017642712615 ], [ 34.893299969224287, -17.327058050950988 ], [ 34.893182744520246, -17.327092613865844 ], [ 34.893063797117513, -17.327121236718394 ], [ 34.892943453052915, -17.327143841051626 ], [ 34.892822042192059, -17.327160364905325 ], [ 34.892699897325102, -17.327170762986 ], [ 34.892577353254502, -17.327175006791023 ], [ 34.892454745877195, -17.327173084686827 ], [ 34.892332411263794, -17.327165001940749 ], [ 34.892210684737286, -17.327150780706638 ], [ 34.892089899953852, -17.327130459964124 ], [ 34.891970387988138, -17.32710409541183 ], [ 34.89185247642574, -17.327071759314567 ], [ 34.891736488465099, -17.327033540305397 ], [ 34.89162274203165, -17.32698954314257 ], [ 34.891511548906152, -17.326939888422402 ], [ 34.891403213870092, -17.326884712248685 ], [ 34.891298033870221, -17.326824165859591 ], [ 34.891196297204459, -17.326758415213135 ], [ 34.891098282731733, -17.326687640532139 ], [ 34.891004259107483, -17.326612035810371 ], [ 34.890923514244896, -17.32653987812137 ], [ 34.892592473294087, -17.324940604483569 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.4", "sub_field": "4.4B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.901510659033576, -17.318761975282452 ], [ 34.90279052743562, -17.320898850353373 ], [ 34.902695969314003, -17.320949335742025 ], [ 34.902573880267774, -17.32100670306102 ], [ 34.90244883266864, -17.321057854335081 ], [ 34.902321169272682, -17.321102649355964 ], [ 34.902191240006921, -17.321140965337896 ], [ 34.902059401010106, -17.321172697254248 ], [ 34.901926013656272, -17.321197758125358 ], [ 34.901791443564306, -17.321216079257098 ], [ 34.90165605959551, -17.32122761042908 ], [ 34.901520232842493, -17.321232320032479 ], [ 34.901384335611887, -17.321230195156613 ], [ 34.901248740403666, -17.321221241624368 ], [ 34.901113818890003, -17.321205483976261 ], [ 34.900979940896406, -17.321182965403178 ], [ 34.900847473387834, -17.321153747627999 ], [ 34.900716779462755, -17.321117910736373 ], [ 34.900588217357772, -17.321075552957247 ], [ 34.900462139465517, -17.321026790393535 ], [ 34.900338891368634, -17.320971756703898 ], [ 34.900218810892511, -17.320910602736351 ], [ 34.900102227179111, -17.32084349611473 ], [ 34.899989459784749, -17.320770620779172 ], [ 34.899880817804124, -17.320692176481966 ], [ 34.899776599023014, -17.320608378239882 ], [ 34.899677089102028, -17.320519455744826 ], [ 34.899582560793547, -17.320425652734166 ], [ 34.899493273194146, -17.320327226322597 ], [ 34.89940947103441, -17.32022444629732 ], [ 34.899331384008093, -17.320117594378495 ], [ 34.899259226142632, -17.320006963446986 ], [ 34.89921991385566, -17.319939028684551 ], [ 34.901510659033576, -17.318761975282452 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.3", "sub_field": "4.3B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.896432140987109, -17.321371469783127 ], [ 34.89747695456991, -17.323348411891939 ], [ 34.897469679008267, -17.323352296088142 ], [ 34.897359530326881, -17.323404048785928 ], [ 34.897246712631812, -17.32345019345582 ], [ 34.897131535156085, -17.323490603613518 ], [ 34.897014313601751, -17.323525168492896 ], [ 34.896895369274397, -17.323553793349777 ], [ 34.896775028202391, -17.323576399721649 ], [ 34.896653620243129, -17.323592925642721 ], [ 34.896531478178844, -17.323603325813824 ], [ 34.896408936804313, -17.323607571726608 ], [ 34.896286332009041, -17.323605651741715 ], [ 34.896163999856569, -17.323597571120658 ], [ 34.89604227566317, -17.323583352011493 ], [ 34.895921493078589, -17.323563033388012 ], [ 34.895801983171467, -17.32353667094301 ], [ 34.895684073521728, -17.323504336935521 ], [ 34.895568087322616, -17.32346611999289 ], [ 34.895454342494695, -17.323422124867694 ], [ 34.895343150814341, -17.323372472150655 ], [ 34.895234817059112, -17.32331729794009 ], [ 34.895129638172165, -17.323256753468765 ], [ 34.895027902448454, -17.323191004689452 ], [ 34.894929888744272, -17.323120231819857 ], [ 34.894835865713006, -17.323044628848749 ], [ 34.894746091068598, -17.3229644030041 ], [ 34.894660810879273, -17.322879774185061 ], [ 34.894580258892958, -17.322790974359165 ], [ 34.894504655896569, -17.322698246926471 ], [ 34.894434209110955, -17.322601846052333 ], [ 34.894369111622865, -17.3225020359707 ], [ 34.894337657341168, -17.322447678138754 ], [ 34.896432140987109, -17.321371469783127 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.4", "sub_field": "4.4D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.901510659033576, -17.318761975282452 ], [ 34.900201998898332, -17.316577029381111 ], [ 34.900293113953509, -17.316528382885895 ], [ 34.900415201043664, -17.316471017154804 ], [ 34.900540246370653, -17.316419867378574 ], [ 34.900667907203236, -17.316375073749036 ], [ 34.900797833642493, -17.316336759036712 ], [ 34.900929669580677, -17.316305028254352 ], [ 34.90106305367722, -17.316279968369155 ], [ 34.90119762034896, -17.316261648064486 ], [ 34.901333000772034, -17.316250117551629 ], [ 34.901468823892699, -17.316245408432209 ], [ 34.901604717444123, -17.316247533611602 ], [ 34.90174030896663, -17.316256487263608 ], [ 34.901875226828437, -17.316272244846392 ], [ 34.90200910124404, -17.316294763169765 ], [ 34.902141565287693, -17.316323980513619 ], [ 34.902272255898858, -17.316359816796982 ], [ 34.902400814877282, -17.31640217379762 ], [ 34.902526889864582, -17.316450935421123 ], [ 34.902650135309905, -17.31650596801914 ], [ 34.902770213416957, -17.316567120755611 ], [ 34.902886795069712, -17.316634226020188 ], [ 34.902999560734415, -17.316707099887594 ], [ 34.903108201335357, -17.316785542621652 ], [ 34.903212419101848, -17.316869339222688 ], [ 34.903311928384397, -17.316958260016797 ], [ 34.903406456437629, -17.317052061285217 ], [ 34.903495744167735, -17.317150485932377 ], [ 34.903579546842757, -17.317253264190409 ], [ 34.903657634763242, -17.317360114358475 ], [ 34.903729793891969, -17.317470743574912 ], [ 34.903795826440607, -17.317584848619777 ], [ 34.903797012928912, -17.317587178252715 ], [ 34.901510659033576, -17.318761975282452 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.4", "sub_field": "4.4A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.90279052743562, -17.320898850353373 ], [ 34.901510659033576, -17.318761975282452 ], [ 34.903797012928912, -17.317587178252715 ], [ 34.903855551411873, -17.317702116745874 ], [ 34.903908805095746, -17.317822226535956 ], [ 34.903955441518242, -17.317944848783579 ], [ 34.90399533284166, -17.318069647395419 ], [ 34.904028369715036, -17.318196280312364 ], [ 34.904054461574034, -17.318324400447054 ], [ 34.904073536889292, -17.318453656635143 ], [ 34.904085543362676, -17.31858369459782 ], [ 34.904090448070683, -17.318714157912751 ], [ 34.90408823755498, -17.318844688991089 ], [ 34.904078917859259, -17.318974930057436 ], [ 34.904062514513072, -17.319104524130601 ], [ 34.904039072461806, -17.319233116001957 ], [ 34.904008655943819, -17.319360353209095 ], [ 34.90397134831445, -17.31948588700191 ], [ 34.903927251817677, -17.319609373298508 ], [ 34.903876487306093, -17.319730473628336 ], [ 34.903819193909769, -17.319848856059931 ], [ 34.903755528655026, -17.319964196110821 ], [ 34.903685666034164, -17.320076177636885 ], [ 34.903609797527295, -17.320184493698932 ], [ 34.903528131077636, -17.320288847404175 ], [ 34.903440890521551, -17.320388952719917 ], [ 34.903348314975204, -17.320484535257712 ], [ 34.903250658179061, -17.320575333025491 ], [ 34.903148187802621, -17.320661097145702 ], [ 34.903041184710538, -17.320741592537622 ], [ 34.902929942192998, -17.320816598561695 ], [ 34.902814765161672, -17.320885909624447 ], [ 34.90279052743562, -17.320898850353373 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.3", "sub_field": "4.3D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.896432140987109, -17.321371469783127 ], [ 34.895357321246472, -17.319337751577674 ], [ 34.895412100380852, -17.319312014200012 ], [ 34.895524916225504, -17.319265870748787 ], [ 34.895640091613821, -17.319225461723033 ], [ 34.895757310866635, -17.319190897876435 ], [ 34.89587625270358, -17.319162273941881 ], [ 34.895996591123556, -17.319139668371751 ], [ 34.896117996298237, -17.319123143123075 ], [ 34.8962401354759, -17.319112743487636 ], [ 34.8963626738934, -17.319108497967935 ], [ 34.896485275693607, -17.31911041819901 ], [ 34.896607604845812, -17.319118498916652 ], [ 34.896729326066612, -17.319132717971751 ], [ 34.896850105738828, -17.319153036391111 ], [ 34.896969612825721, -17.31917939848416 ], [ 34.897087519778275, -17.319211731995729 ], [ 34.897203503432799, -17.31924994830392 ], [ 34.897317245896588, -17.319293942663073 ], [ 34.897428435419187, -17.319343594490885 ], [ 34.897536767246699, -17.319398767698779 ], [ 34.897641944457035, -17.319459311064964 ], [ 34.897743678773672, -17.319525058648797 ], [ 34.897841691355659, -17.319595830245646 ], [ 34.897935713561985, -17.3196714318807 ], [ 34.898025487687661, -17.319751656340614 ], [ 34.898110767670225, -17.319836283741424 ], [ 34.898191319763967, -17.319925082131114 ], [ 34.89826692318082, -17.320017808125392 ], [ 34.898337370695316, -17.320114207574626 ], [ 34.898402469212712, -17.320214016260511 ], [ 34.898462040298284, -17.32031696062009 ], [ 34.898466676679575, -17.320326064479389 ], [ 34.896432140987109, -17.321371469783127 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.3", "sub_field": "4.3A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.89747695456991, -17.323348411891939 ], [ 34.896432140987109, -17.321371469783127 ], [ 34.898466676679575, -17.320326064479389 ], [ 34.898515920666433, -17.320422758495592 ], [ 34.898563962628245, -17.320531119907688 ], [ 34.898606034496453, -17.320641747850299 ], [ 34.898642020946426, -17.320754339104575 ], [ 34.898671823332343, -17.320868585069945 ], [ 34.898695359957713, -17.320984172609954 ], [ 34.898712566299402, -17.321100784910492 ], [ 34.898723395184547, -17.321218102348105 ], [ 34.898727816920108, -17.321335803366054 ], [ 34.89872581937427, -17.32145356535565 ], [ 34.898717408009844, -17.321571065540464 ], [ 34.898702605869474, -17.321687981861071 ], [ 34.898681453512594, -17.321803993857738 ], [ 34.898654008904387, -17.321918783548817 ], [ 34.898620347257008, -17.322032036302279 ], [ 34.898580560823632, -17.322143441698167 ], [ 34.898534758645638, -17.322252694379447 ], [ 34.898483066253917, -17.322359494888957 ], [ 34.89842562532484, -17.322463550490273 ], [ 34.898362593292084, -17.322564575970105 ], [ 34.898294142915205, -17.322662294420091 ], [ 34.89822046180609, -17.322756437995842 ], [ 34.898141751914984, -17.322846748651152 ], [ 34.89805822897678, -17.322932978845273 ], [ 34.897970121919982, -17.323014892221561 ], [ 34.897877672239005, -17.323092264255322 ], [ 34.897781133332444, -17.323164882869285 ], [ 34.897680769808474, -17.323232549014975 ], [ 34.897576856759528, -17.323295077218333 ], [ 34.89747695456991, -17.323348411891939 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.5", "sub_field": "4.5A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.895433867295488, -17.318895605322869 ], [ 34.893769553711472, -17.316586264213559 ], [ 34.896250865251233, -17.315043362242964 ], [ 34.896318457119754, -17.315146999772502 ], [ 34.896392618163802, -17.315275164603293 ], [ 34.896459694535324, -17.315406881910381 ], [ 34.896519502372527, -17.315541790673997 ], [ 34.896571877734488, -17.31567952112637 ], [ 34.896616677050702, -17.315819695765096 ], [ 34.896653777514736, -17.315961930387815 ], [ 34.896683077420903, -17.316105835145184 ], [ 34.896704496443341, -17.316251015609364 ], [ 34.896717975856284, -17.316397073855079 ], [ 34.896723478695229, -17.316543609550223 ], [ 34.896720989858451, -17.316690221053168 ], [ 34.896710516148602, -17.316836506513539 ], [ 34.896692086254312, -17.316982064973665 ], [ 34.896665750671708, -17.317126497467576 ], [ 34.896631581566275, -17.317269408114562 ], [ 34.896589672575168, -17.317410405204214 ], [ 34.89654013855089, -17.317549102270181 ], [ 34.896483115246532, -17.317685119149388 ], [ 34.896418758943973, -17.317818083024147 ], [ 34.89634724602562, -17.317947629444067 ], [ 34.896268772491148, -17.318073403325023 ], [ 34.896183553420386, -17.318195059922527 ], [ 34.896091822383909, -17.31831226577669 ], [ 34.895993830802993, -17.318424699626295 ], [ 34.89588984726047, -17.318532053289481 ], [ 34.895780156764722, -17.318634032508506 ], [ 34.895665059968458, -17.31873035775638 ], [ 34.895544872344658, -17.31882076500316 ], [ 34.895433867295488, -17.318895605322869 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.5", "sub_field": "4.5C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.892115685045027, -17.314291416092694 ], [ 34.893769553711472, -17.316586264213559 ], [ 34.891356570129695, -17.318086679241528 ], [ 34.891297083615598, -17.317995466139099 ], [ 34.891222924615022, -17.317867299214686 ], [ 34.891155850712593, -17.317735579905825 ], [ 34.891096045743055, -17.317600669254201 ], [ 34.891043673615911, -17.31746293704829 ], [ 34.890998877866231, -17.317322760809684 ], [ 34.890961781261367, -17.317180524758289 ], [ 34.890932485464695, -17.317036618759101 ], [ 34.890911070757085, -17.31689143725356 ], [ 34.890897595817023, -17.316745378178336 ], [ 34.890892097560027, -17.316598841874647 ], [ 34.890894591037593, -17.316452229990769 ], [ 34.890905069396219, -17.316305944381273 ], [ 34.890923503896353, -17.316160386005446 ], [ 34.8909498439914, -17.316015953828344 ], [ 34.890984017466458, -17.315873043727201 ], [ 34.891025930636467, -17.315732047406435 ], [ 34.891075468603169, -17.315593351324001 ], [ 34.89113249557024, -17.315457335632171 ], [ 34.891196855215718, -17.315324373135638 ], [ 34.891268371120539, -17.315194828269686 ], [ 34.891346847252301, -17.315069056101418 ], [ 34.891432068502752, -17.314947401356562 ], [ 34.891523801277408, -17.314830197474695 ], [ 34.891621794135965, -17.314717765695455 ], [ 34.891725778481565, -17.314610414177974 ], [ 34.891835469297064, -17.314508437156523 ], [ 34.891950565926237, -17.314412114133994 ], [ 34.892070752897929, -17.314321709115958 ], [ 34.892115685045027, -17.314291416092694 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.5", "sub_field": "4.5B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.893769553711472, -17.316586264213559 ], [ 34.895433867295488, -17.318895605322869 ], [ 34.895419923321938, -17.318905006439724 ], [ 34.895290555381528, -17.318982851157038 ], [ 34.895157123118501, -17.319054085779275 ], [ 34.895019992269809, -17.319118515048697 ], [ 34.894879538711734, -17.319175962360919 ], [ 34.894736147429491, -17.319226270249096 ], [ 34.894590211461896, -17.319269300815598 ], [ 34.894442130823954, -17.319304936110047 ], [ 34.894292311410268, -17.319333078452726 ], [ 34.894141163882288, -17.319353650702329 ], [ 34.893989102542648, -17.319366596467447 ], [ 34.89383654419926, -17.31937188026124 ], [ 34.893683907022783, -17.319369487598706 ], [ 34.893531609400171, -17.319359425036396 ], [ 34.893380068787728, -17.319341720154483 ], [ 34.893229700566629, -17.319316421481151 ], [ 34.893080916904303, -17.31928359835964 ], [ 34.892934125624336, -17.319243340758049 ], [ 34.892789729088619, -17.319195759022865 ], [ 34.892648123094212, -17.319140983576336 ], [ 34.892509695788348, -17.319079164559028 ], [ 34.892374826604382, -17.319010471418256 ], [ 34.892243885221639, -17.318935092443517 ], [ 34.892117230552039, -17.318853234250383 ], [ 34.891995209756132, -17.318765121214074 ], [ 34.891878157291615, -17.318670994854429 ], [ 34.891766393996384, -17.318571113173732 ], [ 34.891660226209176, -17.31846574994956 ], [ 34.89155994492981, -17.318355193984225 ], [ 34.891465825021619, -17.318239748313058 ], [ 34.891378124458043, -17.318119729373805 ], [ 34.891356570129695, -17.318086679241528 ], [ 34.893769553711472, -17.316586264213559 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.6", "sub_field": "4.6B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.899440097999843, -17.313485312578923 ], [ 34.897682148973075, -17.31561157677756 ], [ 34.89767166812657, -17.315604008707908 ], [ 34.897554615295256, -17.315509885726271 ], [ 34.897442851360346, -17.315410007298045 ], [ 34.897336682662356, -17.315304647191873 ], [ 34.897236400203575, -17.315194094201519 ], [ 34.897142278850595, -17.31507865135416 ], [ 34.897054576580814, -17.314958635079801 ], [ 34.896973533775437, -17.314834374343764 ], [ 34.89689937256059, -17.314706209744962 ], [ 34.896832296198639, -17.314574492582278 ], [ 34.89677248853107, -17.314439583891517 ], [ 34.896720113474764, -17.314301853455778 ], [ 34.89667531457286, -17.314161678791795 ], [ 34.896638214601346, -17.314019444115193 ], [ 34.896608915232846, -17.313875539287164 ], [ 34.896587496758009, -17.313730358745971 ], [ 34.89657401786566, -17.313584300425656 ], [ 34.896568515482123, -17.313437764665327 ], [ 34.896571004670271, -17.313291153111834 ], [ 34.896581478588317, -17.313144867618806 ], [ 34.896599908508904, -17.312999309145241 ], [ 34.896626243897977, -17.312854876656449 ], [ 34.896660412553551, -17.312711966030534 ], [ 34.896702320803762, -17.312570968973372 ], [ 34.896751853763831, -17.312432271944935 ], [ 34.896808875651196, -17.312296255100076 ], [ 34.896873230157759, -17.312163291246673 ], [ 34.896944740878574, -17.312033744823729 ], [ 34.897023211795457, -17.31190797090262 ], [ 34.897108427814409, -17.311786314213879 ], [ 34.897200155355293, -17.311669108202434 ], [ 34.8972189524941, -17.311647539775638 ], [ 34.899440097999843, -17.313485312578923 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.6", "sub_field": "4.6D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.899440097999843, -17.313485312578923 ], [ 34.90130835758503, -17.31122562620051 ], [ 34.901413639955251, -17.311310283537498 ], [ 34.901525403085479, -17.31141015982055 ], [ 34.901631571462033, -17.311515517729532 ], [ 34.901731854086115, -17.311626068494771 ], [ 34.901825976089349, -17.311741509113453 ], [ 34.901913679487194, -17.311861523179989 ], [ 34.901994723886119, -17.311985781753258 ], [ 34.902068887142498, -17.312113944258016 ], [ 34.90213596597161, -17.312245659418359 ], [ 34.902195776504954, -17.312380566220416 ], [ 34.902248154794236, -17.312518294901775 ], [ 34.902292957260912, -17.312658467964877 ], [ 34.902330061089906, -17.312800701211714 ], [ 34.902359364566351, -17.312944604796709 ], [ 34.902380787354609, -17.313089784295226 ], [ 34.902394270718574, -17.313235841784657 ], [ 34.902399777682902, -17.313382376934985 ], [ 34.902397293134591, -17.313528988106064 ], [ 34.902386823864568, -17.313675273448435 ], [ 34.902368398549257, -17.313820832004755 ], [ 34.902342067672237, -17.313965264808807 ], [ 34.902307903386109, -17.314108175979023 ], [ 34.902265999314849, -17.314249173803603 ], [ 34.902216470297404, -17.314387871814134 ], [ 34.902159452073207, -17.314523889844988 ], [ 34.902095100910188, -17.314656855075331 ], [ 34.902023593176651, -17.314786403051055 ], [ 34.901945124858074, -17.31491217868377 ], [ 34.901859911019962, -17.315033837224195 ], [ 34.901768185218579, -17.315151045207116 ], [ 34.90167019886082, -17.315263481365488 ], [ 34.901634167766389, -17.315300682952017 ], [ 34.899440097999843, -17.313485312578923 ] ] ] } },
{ "type": "Feature", "properties": { "field": "4.6", "sub_field": "4.6A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.897682148973075, -17.31561157677756 ], [ 34.899440097999843, -17.313485312578923 ], [ 34.901634167766389, -17.315300682952017 ], [ 34.901566220515207, -17.315370837511132 ], [ 34.901456535175853, -17.31547281937948 ], [ 34.901341443481343, -17.315569147436317 ], [ 34.901221260890679, -17.315659557643983 ], [ 34.901096316818645, -17.31574380218521 ], [ 34.900966953732905, -17.315821650142521 ], [ 34.900833526215209, -17.315892888131156 ], [ 34.900696399989521, -17.315957320884166 ], [ 34.900555950919419, -17.316014771787611 ], [ 34.900412563977817, -17.316065083364801 ], [ 34.900266632191638, -17.316108117707994 ], [ 34.900118555564397, -17.316143756856455 ], [ 34.899968739979705, -17.316171903119908 ], [ 34.899817596088525, -17.316192479346252 ], [ 34.899665538183491, -17.316205429133237 ], [ 34.899512983063111, -17.316210716982958 ], [ 34.899360348889211, -17.316208328399284 ], [ 34.8992080540405, -17.316198269927593 ], [ 34.899056515965661, -17.316180569136844 ], [ 34.898906150038954, -17.316155274543995 ], [ 34.898757368421464, -17.316122455481082 ], [ 34.898610578931248, -17.316082201905107 ], [ 34.898466183925223, -17.316034624151484 ], [ 34.898324579196277, -17.31597985263155 ], [ 34.898186152888186, -17.315918037475122 ], [ 34.898051284431546, -17.315849348118899 ], [ 34.897920343503721, -17.315773972842017 ], [ 34.897793689015366, -17.315692118249903 ], [ 34.897682148973075, -17.31561157677756 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.1", "sub_field": "5.1A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.889644280205616, -17.310113123629545 ], [ 34.893567231271753, -17.309696371939275 ], [ 34.894135431768206, -17.313461353424515 ], [ 34.894022909687017, -17.31347666856341 ], [ 34.893816897561351, -17.313494207208084 ], [ 34.893610212128692, -17.313501365053114 ], [ 34.893403419933982, -17.313498122474773 ], [ 34.893197087815246, -17.313484488357865 ], [ 34.892991781349544, -17.313460500071429 ], [ 34.892788063302312, -17.313426223366324 ], [ 34.892586492084611, -17.313381752195014 ], [ 34.892387620222046, -17.313327208454009 ], [ 34.892191992840104, -17.313262741649709 ], [ 34.892000146169543, -17.313188528488556 ], [ 34.891812606076378, -17.313104772392641 ], [ 34.891629886620137, -17.313011702941967 ], [ 34.891452488644688, -17.312909575245161 ], [ 34.891280898405093, -17.31279866924006 ], [ 34.89111558623474, -17.312679288926272 ], [ 34.890957005255849, -17.312551761531832 ], [ 34.890805590137525, -17.312416436616065 ], [ 34.890661755904112, -17.312273685111339 ], [ 34.890525896797627, -17.312123898306147 ], [ 34.890398385197194, -17.311967486772534 ], [ 34.890279570598331, -17.311804879240409 ], [ 34.890169778655036, -17.311636521422365 ], [ 34.89006931028738, -17.311462874791772 ], [ 34.889978440856758, -17.311284415317768 ], [ 34.889897419411362, -17.311101632160437 ], [ 34.889826468003683, -17.310915026329962 ], [ 34.889765781082247, -17.310725109313132 ], [ 34.889715524958767, -17.310532401671345 ], [ 34.889675837352712, -17.310337431613661 ], [ 34.889646827014069, -17.310140733548774 ], [ 34.889644280205616, -17.310113123629545 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.1", "sub_field": "5.1C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.897496240357995, -17.309278976679899 ], [ 34.893567231271753, -17.309696371939275 ], [ 34.893002323669023, -17.305953209660426 ], [ 34.893119668539669, -17.305937238579631 ], [ 34.893325672458758, -17.305919701457171 ], [ 34.893532349484737, -17.30591254472894 ], [ 34.893739133164758, -17.305915788006445 ], [ 34.893945456754139, -17.305929422397227 ], [ 34.894150754769228, -17.30595341052922 ], [ 34.894354464537166, -17.30598768665326 ], [ 34.894556027737657, -17.306032156823267 ], [ 34.894754891932848, -17.306086699153688 ], [ 34.89495051208133, -17.30615116415359 ], [ 34.895142352031577, -17.306225375136297 ], [ 34.895329885991188, -17.306309128703578 ], [ 34.895512599967851, -17.306402195303132 ], [ 34.895689993177726, -17.306504319857581 ], [ 34.895861579417975, -17.306615222463513 ], [ 34.896026888399007, -17.306734599158542 ], [ 34.896185467033547, -17.306862122754314 ], [ 34.896336880678206, -17.306997443733113 ], [ 34.896480714324767, -17.307140191205693 ], [ 34.896616573737617, -17.30728997392773 ], [ 34.896744086534312, -17.307446381371953 ], [ 34.89686290320622, -17.307608984853228 ], [ 34.89697269807661, -17.30777733870335 ], [ 34.897073170193366, -17.307950981492414 ], [ 34.897164044153996, -17.308129437293385 ], [ 34.897245070860698, -17.308312216986412 ], [ 34.897316028203313, -17.308498819599276 ], [ 34.897376721668323, -17.308688733680437 ], [ 34.897426984872283, -17.308881438700638 ], [ 34.897466680018177, -17.309076406479587 ], [ 34.897495698273403, -17.309273102633544 ], [ 34.897496240357995, -17.309278976679899 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.1", "sub_field": "5.1B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.893567231271753, -17.309696371939275 ], [ 34.889644280205616, -17.310113123629545 ], [ 34.889628573425647, -17.309942846620235 ], [ 34.889621126585517, -17.309744313228641 ], [ 34.889624506870462, -17.309545677544772 ], [ 34.889638704980335, -17.309347484018105 ], [ 34.889663681964102, -17.309150275884431 ], [ 34.88969936932687, -17.308954593676912 ], [ 34.889745669218065, -17.308760973744466 ], [ 34.889802454699968, -17.308569946781756 ], [ 34.889869570096032, -17.308382036374624 ], [ 34.889946831417923, -17.308197757564983 ], [ 34.890034026870126, -17.308017615439326 ], [ 34.890130917430803, -17.307842103744292 ], [ 34.890237237507151, -17.307671703533526 ], [ 34.890352695663665, -17.307506881849253 ], [ 34.890476975421151, -17.307348090442296 ], [ 34.890609736124361, -17.307195764533997 ], [ 34.890750613875788, -17.3070503216235 ], [ 34.890899222533278, -17.306912160343522 ], [ 34.891055154768431, -17.306781659367985 ], [ 34.891217983183068, -17.306659176374215 ], [ 34.891387261480766, -17.306545047062802 ], [ 34.891562525690006, -17.306439584237634 ], [ 34.891743295435781, -17.306343076948679 ], [ 34.891929075256186, -17.306255789699943 ], [ 34.892119355960247, -17.306177961724611 ], [ 34.892313616023394, -17.306109806329552 ], [ 34.892511323016663, -17.306051510310741 ], [ 34.892711935065812, -17.30600323344148 ], [ 34.892914902336223, -17.305965108034528 ], [ 34.893002323669023, -17.305953209660426 ], [ 34.893567231271753, -17.309696371939275 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.3", "sub_field": "5.3C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.88655044353051, -17.30082027669599 ], [ 34.887065228066767, -17.304559537671949 ], [ 34.883138407553353, -17.304967747142655 ], [ 34.883124499512981, -17.304816903162319 ], [ 34.883117060422094, -17.30461836873225 ], [ 34.883120448196905, -17.304419732390357 ], [ 34.883134653516784, -17.304221538587896 ], [ 34.883159637410927, -17.304024330561418 ], [ 34.883195331365513, -17.303828648843769 ], [ 34.883241637511922, -17.303635029782509 ], [ 34.883298428895301, -17.303444004069913 ], [ 34.883365549822955, -17.303256095288376 ], [ 34.883442816291399, -17.30307181847537 ], [ 34.883530016491029, -17.302891678711884 ], [ 34.883626911387026, -17.302716169738098 ], [ 34.883733235374699, -17.30254577260019 ], [ 34.883848697007878, -17.302380954331959 ], [ 34.883972979797818, -17.302222166674863 ], [ 34.884105743080994, -17.302069844839973 ], [ 34.884246622952844, -17.301924406315258 ], [ 34.88439523326543, -17.301786249721427 ], [ 34.88455116668581, -17.301655753719565 ], [ 34.884713995812575, -17.301533275973377 ], [ 34.884883274347381, -17.301419152169071 ], [ 34.885058538318042, -17.301313695095438 ], [ 34.885239307350204, -17.301217193786709 ], [ 34.885425085983954, -17.301129912730506 ], [ 34.885615365031562, -17.301052091143053 ], [ 34.885809622973014, -17.30098394231371 ], [ 34.886007327385144, -17.300925653020485 ], [ 34.88620793640078, -17.30087738301825 ], [ 34.886410900193646, -17.30083926460097 ], [ 34.88655044353051, -17.30082027669599 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.3", "sub_field": "5.3A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.887585923201613, -17.308341731698718 ], [ 34.887065228066767, -17.304559537671949 ], [ 34.890991820206963, -17.304151351941599 ], [ 34.891009712544758, -17.30434532014915 ], [ 34.891017160039745, -17.304543853465603 ], [ 34.891013780776646, -17.304742489111405 ], [ 34.890999583982811, -17.304940682642922 ], [ 34.890974608535664, -17.305137890826575 ], [ 34.890938922856463, -17.305333573127797 ], [ 34.890892624723172, -17.305527193192621 ], [ 34.89083584100284, -17.305718220317804 ], [ 34.890768727304206, -17.305906130905438 ], [ 34.890691467551548, -17.306090409898285 ], [ 34.890604273480889, -17.306270552191478 ], [ 34.890507384059916, -17.306446064017113 ], [ 34.890401064833291, -17.306616464297758 ], [ 34.890285607195082, -17.306781285965204 ], [ 34.890161327590214, -17.306940077240721 ], [ 34.890028566647352, -17.307092402873621 ], [ 34.889887688245388, -17.307237845334313 ], [ 34.889739078516179, -17.307376005958961 ], [ 34.889583144786272, -17.307506506042298 ], [ 34.889420314460438, -17.307628987875948 ], [ 34.889251033850186, -17.307743115728897 ], [ 34.889075766950455, -17.307848576767977 ], [ 34.888894994167643, -17.307945081915541 ], [ 34.888709211002805, -17.3080323666419 ], [ 34.888518926693266, -17.308110191690613 ], [ 34.888324662816736, -17.308178343734379 ], [ 34.888126951861395, -17.308236635960007 ], [ 34.887926335766082, -17.308284908580482 ], [ 34.887723364434663, -17.308323029273154 ], [ 34.887585923201613, -17.308341731698718 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.3", "sub_field": "5.3D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.887065228066767, -17.304559537671949 ], [ 34.88655044353051, -17.30082027669599 ], [ 34.886615662485013, -17.300811402239216 ], [ 34.886821662068201, -17.300793872293969 ], [ 34.88702833434634, -17.300786722807359 ], [ 34.887235112879623, -17.30078997337106 ], [ 34.887441430937386, -17.300803615072628 ], [ 34.887646723051198, -17.300827610519995 ], [ 34.88785042656432, -17.30086189394396 ], [ 34.888051983173575, -17.300906371378414 ], [ 34.888250840459207, -17.300960920917966 ], [ 34.888446453398686, -17.301025393051869 ], [ 34.888638285860232, -17.301099611073905 ], [ 34.888825812071993, -17.301183371566594 ], [ 34.889008518062795, -17.301276444958631 ], [ 34.889185903070754, -17.301378576154015 ], [ 34.889357480915407, -17.301489485231148 ], [ 34.889522781330243, -17.301608868209907 ], [ 34.889681351251404, -17.301736397884714 ], [ 34.889832756059356, -17.301871724721188 ], [ 34.88997658077011, -17.302014477814012 ], [ 34.89011243117254, -17.302164265903457 ], [ 34.890239934908863, -17.302320678447551 ], [ 34.890358742495273, -17.302483286747144 ], [ 34.890468528279946, -17.302651645120829 ], [ 34.890568991335634, -17.3028252921263 ], [ 34.890659856284707, -17.303003751824949 ], [ 34.890740874054046, -17.303186535086216 ], [ 34.890811822557978, -17.303373140928109 ], [ 34.890872507307208, -17.303563057890219 ], [ 34.890922761942207, -17.303755765435376 ], [ 34.890962448689429, -17.303950735376358 ], [ 34.890991458739279, -17.304147433323465 ], [ 34.890991820206963, -17.304151351941599 ], [ 34.887065228066767, -17.304559537671949 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.2", "sub_field": "5.2A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.8969126991338, -17.302747852699646 ], [ 34.900876176680299, -17.302893796550027 ], [ 34.900862215681485, -17.303088843323266 ], [ 34.900837250927268, -17.303286051715396 ], [ 34.900801576024612, -17.303481734749649 ], [ 34.900755288721975, -17.303675356070045 ], [ 34.900698515856696, -17.303866384969915 ], [ 34.900631413007766, -17.30405429784653 ], [ 34.900554164069703, -17.304238579636362 ], [ 34.900466980748888, -17.30441872522692 ], [ 34.900370101983533, -17.304594240841258 ], [ 34.900263793289071, -17.304764645391586 ], [ 34.900148346030669, -17.30492947179798 ], [ 34.900024076624817, -17.305088268268765 ], [ 34.899891325672165, -17.305240599538983 ], [ 34.899750457024226, -17.305386048063617 ], [ 34.899601856786099, -17.305524215162226 ], [ 34.899445932258295, -17.305654722111793 ], [ 34.899283110820335, -17.305777211185095 ], [ 34.899113838759355, -17.305891346631288 ], [ 34.898938580046824, -17.305996815596405 ], [ 34.898757815066695, -17.306093328981046 ], [ 34.898572039298578, -17.30618062223294 ], [ 34.898381761959563, -17.306258456072264 ], [ 34.898187504608195, -17.30632661714764 ], [ 34.89798979971475, -17.306384918621077 ], [ 34.89778918920144, -17.306433200680189 ], [ 34.897586222956839, -17.306471330976475 ], [ 34.897381457328272, -17.306499204988068 ], [ 34.89717545359661, -17.306516746306354 ], [ 34.896968776437447, -17.306523906845566 ], [ 34.896848733623173, -17.306522026027363 ], [ 34.8969126991338, -17.302747852699646 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.2", "sub_field": "5.2C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.8969126991338, -17.302747852699646 ], [ 34.892982579947684, -17.30260313717654 ], [ 34.892983172682968, -17.302568275889897 ], [ 34.892997367422488, -17.302370082399289 ], [ 34.893022340623894, -17.302172874159623 ], [ 34.893058023802681, -17.301977191704324 ], [ 34.89310431911975, -17.301783571383027 ], [ 34.893161099649916, -17.301592543891452 ], [ 34.893228209730253, -17.301404632816908 ], [ 34.89330546538698, -17.301220353203167 ], [ 34.893392654840142, -17.301040210138897 ], [ 34.8934895390843, -17.300864697373338 ], [ 34.893595852544017, -17.300694295963083 ], [ 34.893711303801894, -17.300529472953652 ], [ 34.893835576397656, -17.300370680099508 ], [ 34.893968329695682, -17.300218352625976 ], [ 34.894109199818764, -17.30007290803648 ], [ 34.894257800645626, -17.299934744968354 ], [ 34.894413724869359, -17.299804242100432 ], [ 34.894576545113729, -17.299681757115238 ], [ 34.894745815104677, -17.299567625718822 ], [ 34.894921070893432, -17.299462160720779 ], [ 34.89510183212807, -17.299365651177041 ], [ 34.895287603369994, -17.299278361597814 ], [ 34.895477875451739, -17.29920053122266 ], [ 34.895672126872348, -17.299132373365019 ], [ 34.895869825226519, -17.299074074827637 ], [ 34.896070428663677, -17.299025795390687 ], [ 34.896273387372787, -17.298987667373979 ], [ 34.896478145089084, -17.298959795274413 ], [ 34.89668414061839, -17.29894225547962 ], [ 34.896890809374973, -17.298935096058699 ], [ 34.8969772955911, -17.298936451464556 ], [ 34.8969126991338, -17.302747852699646 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.2", "sub_field": "5.2D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.900876176680299, -17.302893796550027 ], [ 34.8969126991338, -17.302747852699646 ], [ 34.8969772955911, -17.298936451464556 ], [ 34.897097584928609, -17.298938336630535 ], [ 34.897303900556857, -17.298951968310067 ], [ 34.897509190797912, -17.298975953732672 ], [ 34.89771289300019, -17.299010227156604 ], [ 34.897914448864078, -17.299054694643154 ], [ 34.898113305971897, -17.299109234314148 ], [ 34.898308919301599, -17.299173696685894 ], [ 34.898500752720359, -17.299247905078936 ], [ 34.898688280453683, -17.299331656102137 ], [ 34.898870988526284, -17.299424720210141 ], [ 34.899048376170541, -17.299526842332384 ], [ 34.899219957198824, -17.299637742572131 ], [ 34.899385261335887, -17.299757116973481 ], [ 34.899543835507686, -17.299884638354321 ], [ 34.899695245083088, -17.300019957203023 ], [ 34.899839075065046, -17.300162702636175 ], [ 34.899974931227973, -17.300312483415023 ], [ 34.900102441198349, -17.30046888901763 ], [ 34.900221255475309, -17.300631490763884 ], [ 34.900331048388715, -17.300799842990351 ], [ 34.900431518991795, -17.300973484271562 ], [ 34.900522391886227, -17.301151938684619 ], [ 34.900603417977173, -17.301334717113466 ], [ 34.90067437515615, -17.301521318589351 ], [ 34.900735068910123, -17.301711231663859 ], [ 34.900785332854859, -17.301903935810486 ], [ 34.900825029191338, -17.302098902851345 ], [ 34.900854049083712, -17.302295598404701 ], [ 34.900872312957986, -17.302493483349586 ], [ 34.900879770720429, -17.302692015303364 ], [ 34.900876401895346, -17.302890650108349 ], [ 34.900876176680299, -17.302893796550027 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.4", "sub_field": "5.4B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.894017157017068, -17.294410311809727 ], [ 34.89693731592709, -17.294573580994058 ], [ 34.896864188941137, -17.297331505303269 ], [ 34.896805931020118, -17.297330592199042 ], [ 34.896653651768069, -17.297320530228568 ], [ 34.896502129402165, -17.297302825932473 ], [ 34.896351779253607, -17.297277527837316 ], [ 34.896203013440193, -17.297244705284744 ], [ 34.896056239736616, -17.297204448241324 ], [ 34.89591186045655, -17.297156867051939 ], [ 34.895770271349726, -17.297102092137393 ], [ 34.895631860517078, -17.29704027363676 ], [ 34.895497007346798, -17.296971580995908 ], [ 34.895366081474315, -17.296896202502978 ], [ 34.895239441768993, -17.296814344772198 ], [ 34.8951174353504, -17.296726232177512 ], [ 34.895000396636796, -17.296632106237524 ], [ 34.894888646428399, -17.296532224953381 ], [ 34.894782491028053, -17.296426862101541 ], [ 34.894682221401673, -17.296316306483291 ], [ 34.894588112380617, -17.296200861132984 ], [ 34.894500421908518, -17.296080842487484 ], [ 34.894419390334207, -17.295956579518617 ], [ 34.894345239753093, -17.295828412831394 ], [ 34.894278173398355, -17.295696693730406 ], [ 34.894218375084108, -17.295561783256755 ], [ 34.894166008701575, -17.295424051198424 ], [ 34.89412121777012, -17.295283875076617 ], [ 34.89408412504396, -17.295141639110881 ], [ 34.894054832175833, -17.294997733166007 ], [ 34.894033419438607, -17.294852551683256 ], [ 34.894019945505391, -17.294706492599275 ], [ 34.894014447288981, -17.294559956255252 ], [ 34.894016939840789, -17.294413344299628 ], [ 34.894017157017068, -17.294410311809727 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.4", "sub_field": "5.4A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.89693731592709, -17.294573580994058 ], [ 34.89983659573803, -17.294735682805793 ], [ 34.899832163531997, -17.294797598243601 ], [ 34.899822899654588, -17.294870775272294 ], [ 34.899813736479395, -17.294943156836158 ], [ 34.899787404689512, -17.295087589492788 ], [ 34.899753240317445, -17.295230500332377 ], [ 34.899711336987437, -17.29537149764397 ], [ 34.89966180953644, -17.29551019496061 ], [ 34.899604793699602, -17.295646212118569 ], [ 34.89954044573831, -17.295779176299387 ], [ 34.89946894201217, -17.295908723051863 ], [ 34.899390478495619, -17.296034497290957 ], [ 34.899305270241072, -17.296156154271213 ], [ 34.899213550789469, -17.29627336053171 ], [ 34.899115571530331, -17.296385794810135 ], [ 34.899011601012702, -17.296493148923457 ], [ 34.898901924209291, -17.296595128612708 ], [ 34.898786841735244, -17.296691454349638 ], [ 34.898666669024301, -17.296781862102957 ], [ 34.898541735464178, -17.296866104062158 ], [ 34.898412383493714, -17.296943949316809 ], [ 34.898278967664204, -17.297015184489627 ], [ 34.898141853667532, -17.297079614321344 ], [ 34.898001417333766, -17.297137062206048 ], [ 34.89785804360087, -17.297187370675346 ], [ 34.897712125459591, -17.297230401829999 ], [ 34.897564062875993, -17.297266037718053 ], [ 34.897414261695189, -17.297294180658142 ], [ 34.89726313252865, -17.297314753507322 ], [ 34.897111089628602, -17.29732769987255 ], [ 34.896958549752426, -17.297332984265342 ], [ 34.896864188941137, -17.297331505303269 ], [ 34.89693731592709, -17.294573580994058 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.4", "sub_field": "5.4C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.89693731592709, -17.294573580994058 ], [ 34.894017157017068, -17.294410311809727 ], [ 34.894027416309868, -17.294267058587142 ], [ 34.894045847961912, -17.294121500077392 ], [ 34.894072184258114, -17.293977067735781 ], [ 34.894089624499351, -17.293904124152515 ], [ 34.894106352994037, -17.293834157440013 ], [ 34.894148260497595, -17.293693160895057 ], [ 34.894197791886107, -17.293554464559477 ], [ 34.89425481138133, -17.293418448586269 ], [ 34.894319162681754, -17.293285485780885 ], [ 34.894390669391221, -17.293155940579478 ], [ 34.894469135502568, -17.293030168050077 ], [ 34.894554345934992, -17.292908512919421 ], [ 34.894646067123652, -17.292791308628154 ], [ 34.894744047660041, -17.292678876417035 ], [ 34.894848018981065, -17.292571524446419 ], [ 34.894957696105273, -17.292469546951811 ], [ 34.895072778413969, -17.292373223437412 ], [ 34.89519295047522, -17.292282817910166 ], [ 34.895317882908451, -17.292198578156203 ], [ 34.89544723328715, -17.292120735061754 ], [ 34.89558064707748, -17.292049501980475 ], [ 34.895717758609869, -17.291985074148698 ], [ 34.89585819208127, -17.291927628150439 ], [ 34.896001562585077, -17.291877321433407 ], [ 34.89614747716594, -17.291834291877638 ], [ 34.896295535896762, -17.291798657417591 ], [ 34.896445332974679, -17.29177051571898 ], [ 34.896596457833176, -17.291749943911135 ], [ 34.896748496267172, -17.291736998375693 ], [ 34.896901031568262, -17.291731714592061 ], [ 34.897012622272264, -17.291733463938773 ], [ 34.896942313597627, -17.29438509794787 ], [ 34.89693731592709, -17.294573580994058 ] ] ] } },
{ "type": "Feature", "properties": { "field": "5.4", "sub_field": "5.4D" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.89983659573803, -17.294735682805793 ], [ 34.89693731592709, -17.294573580994058 ], [ 34.896942313597627, -17.29438509794787 ], [ 34.897012622272264, -17.291733463938773 ], [ 34.897053645666574, -17.291734107040188 ], [ 34.897205920276505, -17.291744169161014 ], [ 34.897357438042967, -17.29176187337432 ], [ 34.897507783685214, -17.291787171154436 ], [ 34.89765654513478, -17.291819993163191 ], [ 34.897803314664799, -17.291860249439978 ], [ 34.897947690007349, -17.291907829648292 ], [ 34.898089275455796, -17.291962603378103 ], [ 34.89822768294929, -17.292024420503296 ], [ 34.898362533136257, -17.292093111593083 ], [ 34.898493456413945, -17.292168488376308 ], [ 34.898620093941389, -17.2922503442575 ], [ 34.898742098622918, -17.292338454882941 ], [ 34.898859136059286, -17.292432578755633 ], [ 34.898970885464273, -17.292532457897078 ], [ 34.899077040543823, -17.292637818554272 ], [ 34.899177310335574, -17.29274837194999 ], [ 34.899271420006308, -17.29286381507422 ], [ 34.8993591116053, -17.292983831514487 ], [ 34.899440144771376, -17.293108092323138 ], [ 34.899514297391676, -17.293236256918828 ], [ 34.899581366210683, -17.293367974019876 ], [ 34.899641167387308, -17.29350288260709 ], [ 34.899693536998917, -17.293640612913151 ], [ 34.899738331490795, -17.293780787436059 ], [ 34.899775428069709, -17.293923021973789 ], [ 34.899804725040731, -17.294066926677193 ], [ 34.899826142086027, -17.294212107118611 ], [ 34.899839620485281, -17.294358165372817 ], [ 34.899845123276762, -17.29450470110768 ], [ 34.899842635358958, -17.294651312681466 ], [ 34.89983659573803, -17.294735682805793 ] ] ] } },
{ "type": "Feature", "properties": { "field": "DL1.3", "sub_field": "DL1.3" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.947451385514611, -17.321753599029073 ], [ 34.947298927757849, -17.321663500672134 ], [ 34.947001852567226, -17.32100637178991 ], [ 34.947092750341952, -17.318710743453458 ], [ 34.94828268484742, -17.317606307231124 ], [ 34.948861125232021, -17.317077753261884 ], [ 34.949299087237506, -17.316123196719229 ], [ 34.950715487823011, -17.316226521400374 ], [ 34.951786380891292, -17.316304641732966 ], [ 34.953480384874766, -17.317416974640771 ], [ 34.953265535589061, -17.317724640001025 ], [ 34.953133320644007, -17.318048082516636 ], [ 34.953034159435212, -17.318442523838343 ], [ 34.952968051962692, -17.318884297113428 ], [ 34.952968051962692, -17.319373402000465 ], [ 34.953092003473678, -17.319909838121443 ], [ 34.952794519847309, -17.321448138900415 ], [ 34.952108654819853, -17.321661133377713 ], [ 34.950066988602522, -17.321701668718116 ], [ 34.947451385514611, -17.321753599029073 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.18", "sub_field": "1.18C" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.961301964672387, -17.313838203379714 ], [ 34.963291039141012, -17.311973548628245 ], [ 34.963318548862773, -17.312000830743248 ], [ 34.963414658522524, -17.312106712348005 ], [ 34.963504867652226, -17.312217280413027 ], [ 34.963588928992536, -17.312332231887332 ], [ 34.963666612132982, -17.31245125170528 ], [ 34.963737704143533, -17.312574013649993 ], [ 34.963802010158297, -17.312700181247447 ], [ 34.963859353909704, -17.312829408688614 ], [ 34.963909578211776, -17.312961341777211 ], [ 34.963952545391066, -17.313095618900494 ], [ 34.963988137664089, -17.313231872020292 ], [ 34.964016257460422, -17.3133697276817 ], [ 34.964036827690187, -17.313508808036687 ], [ 34.964049791955574, -17.313648731879656 ], [ 34.964055114705573, -17.313789115692281 ], [ 34.964052781333649, -17.313929574694654 ], [ 34.964042798217903, -17.314069723899909 ], [ 34.964025192703858, -17.314209179169463 ], [ 34.964000013029583, -17.314347558265844 ], [ 34.963967328193803, -17.314484481900458 ], [ 34.963927227766852, -17.314619574773129 ], [ 34.963879821645449, -17.314752466600854 ], [ 34.96382523975155, -17.314882793132711 ], [ 34.963763631676478, -17.315010197148315 ], [ 34.963695166271002, -17.315134329436955 ], [ 34.963620031182742, -17.315254849754851 ], [ 34.963538432341863, -17.315371427757743 ], [ 34.963450593396857, -17.315483743906491 ], [ 34.963356755101515, -17.3155914903429 ], [ 34.96325717465519, -17.315694371733709 ], [ 34.963152124997862, -17.315792106080078 ], [ 34.963115666188799, -17.315822640664031 ], [ 34.961301964672387, -17.313838203379714 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.18", "sub_field": "1.18A" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.961301964672387, -17.313838203379714 ], [ 34.959294104555354, -17.315720468707916 ], [ 34.959205111423216, -17.315632210383061 ], [ 34.959109001898497, -17.315526326733945 ], [ 34.959018793349316, -17.315415756619789 ], [ 34.95893473302857, -17.315300803113992 ], [ 34.958857051335606, -17.315181781304474 ], [ 34.958785961184603, -17.315059017429945 ], [ 34.958721657421044, -17.314932847985528 ], [ 34.958664316287852, -17.314803618800539 ], [ 34.958614094942313, -17.314671684090325 ], [ 34.958571131025515, -17.314537405485428 ], [ 34.958535542285148, -17.314401151040233 ], [ 34.958507426252964, -17.314263294224176 ], [ 34.958486859977597, -17.31412421289793 ], [ 34.958473899813526, -17.313984288277727 ], [ 34.958468581266821, -17.313843903890437 ], [ 34.958470918897959, -17.313703444522226 ], [ 34.958480906282176, -17.313563295164005 ], [ 34.958498516027234, -17.31342383995602 ], [ 34.95852369984862, -17.313285461135049 ], [ 34.958556388702249, -17.313148537986695 ], [ 34.958596492973705, -17.31301344580578 ], [ 34.958643902724212, -17.312880554867768 ], [ 34.958698487991995, -17.31275022941379 ], [ 34.958760099148805, -17.312622826652447 ], [ 34.958828567310086, -17.312498695780704 ], [ 34.958903704798047, -17.312378177026815 ], [ 34.958985305656192, -17.3122616007179 ], [ 34.959073146213981, -17.312149286374552 ], [ 34.959166985699902, -17.31204154183515 ], [ 34.95926656690159, -17.311938662412228 ], [ 34.959371616870783, -17.311840930083029 ], [ 34.959431043918748, -17.311791160431401 ], [ 34.961301964672387, -17.313838203379714 ] ] ] } },
{ "type": "Feature", "properties": { "field": "1.18", "sub_field": "1.18B" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.963291039141012, -17.311973548628245 ], [ 34.961301964672387, -17.313838203379714 ], [ 34.959431043918748, -17.311791160431401 ], [ 34.959481847671512, -17.311748612716794 ], [ 34.95959695716941, -17.311661963340608 ], [ 34.959716629859727, -17.311581219445991 ], [ 34.959840537732092, -17.311506602337992 ], [ 34.95996834116962, -17.311438316528729 ], [ 34.960099689879613, -17.311376549176956 ], [ 34.960234223853597, -17.311321469575088 ], [ 34.960371574354085, -17.311273228685337 ], [ 34.960511364925047, -17.311231958725969 ], [ 34.960653212423622, -17.311197772808992 ], [ 34.9607967280702, -17.31117076463017 ], [ 34.960941518513806, -17.311151008212327 ], [ 34.961087186910177, -17.311138557702407 ], [ 34.961233334009165, -17.311133447223234 ], [ 34.961379559249025, -17.31113569077991 ], [ 34.961525461854002, -17.311145282221496 ], [ 34.961670641932749, -17.311162195257861 ], [ 34.96181470157412, -17.311186383531847 ], [ 34.96195724593769, -17.311217780746176 ], [ 34.962097884335776, -17.311256300845265 ], [ 34.962236231304082, -17.311301838251062 ], [ 34.962371907658095, -17.311354268152318 ], [ 34.962504541532205, -17.311413446846771 ], [ 34.962633769398863, -17.311479212134888 ], [ 34.962759237064759, -17.311551383764364 ], [ 34.962880600641611, -17.31162976392423 ], [ 34.962997527488582, -17.311714137786886 ], [ 34.96310969712394, -17.311804274096819 ], [ 34.96321680210341, -17.311899925804482 ], [ 34.963291039141012, -17.311973548628245 ] ] ] } },
{ "type": "Feature", "properties": { "field": "DL1.1", "sub_field": "DL1.1" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.973316494728564, -17.333251794475164 ], [ 34.973346256561562, -17.333419673334269 ], [ 34.973237698645903, -17.333332450175927 ], [ 34.973316494728564, -17.333251794475164 ] ] ] } },
{ "type": "Feature", "properties": { "field": "DL1.1", "sub_field": "DL1.1" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.973346256561562, -17.333419673334269 ], [ 34.973316494728564, -17.333251794475164 ], [ 34.974847423373653, -17.331684735320394 ], [ 34.975486037377962, -17.331031049777948 ], [ 34.976506367806572, -17.330987952117045 ], [ 34.97768922874593, -17.331936098320032 ], [ 34.978068466604348, -17.332608417206025 ], [ 34.977245857001407, -17.333467640564528 ], [ 34.975576332106158, -17.335211474799472 ], [ 34.973346256561562, -17.333419673334269 ] ] ] } }
]
}

View file

@ -0,0 +1,117 @@
library(here)
library(sf)
library(tidyverse)
library(tmap)
library(lubridate)
library(exactextractr)
library(zoo)
library(raster)
library(terra)
library(rsample)
library(caret)
library(randomForest)
library(CAST)
project_dir <- "chemba"
source(here("r_app", "parameters_project.R"))
cumulative_CI_vals_dir <- "C:/Users/timon/Resilience BV/4020 SCane ESA DEMO - Documenten/General/4020 SCDEMO Team/4020 TechnicalData/WP3/smartcane/laravel_app/storage/app/chemba/Data/extracted_ci/cumulative_vals"
CI_quadrant <- readRDS(here(cumulative_CI_vals_dir,"All_pivots_Cumulative_CI_quadrant_year_v2.rds"))# %>%
cum_ci_plot <- function(pivotName, plot_type = "value", facet_on = TRUE, x_axis = "days") {
# pivotName = "3a13"
data_ci <- CI_quadrant %>% filter(field == pivotName)
if (nrow(data_ci) == 0) {
return(cum_ci_plot2(pivotName)) # Return an empty data frame if no data is found
}
data_ci2 <- data_ci %>%
mutate(CI_rate = cumulative_CI / DOY,
week = week(Date),
week_from_doy = DOY / 7) %>%
group_by(field) %>%
mutate(mean_CIrate_rolling_10_days = rollapplyr(CI_rate, width = 10, FUN = mean, partial = TRUE),
mean_rolling_10_days = rollapplyr(value, width = 10, FUN = mean, partial = TRUE))
data_ci2 <- data_ci2 %>% mutate(season = as.factor(season))
date_preperation_perfect_pivot <- data_ci2 %>%
group_by(season) %>%
summarise(min_date = min(Date),
max_date = max(Date),
days = max_date - min_date)
unique_seasons <- sort(unique(date_preperation_perfect_pivot$season), decreasing = TRUE)[1:3]
# Determine the y aesthetic based on the plot type
y_aesthetic <- switch(plot_type,
"CI_rate" = "mean_CIrate_rolling_10_days",
"cumulative_CI" = "cumulative_CI",
"value" = "mean_rolling_10_days")
y_label <- switch(plot_type,
"CI_rate" = "10-Day Rolling Mean CI Rate (cumulative CI / age)",
"cumulative_CI" = "Cumulative CI",
"value" = "10-Day Rolling Mean CI")
if (facet_on) {
g <- ggplot(data = data_ci2 %>% filter(season %in% unique_seasons)) +
facet_wrap(~season, scales = "free_x") +
geom_line(aes_string(x = "Date", y = y_aesthetic, col = "sub_field", group = "sub_field")) +
labs(title = paste("Plot of", y_label, "for Pivot", pivotName),
color = "Field Name",
y = y_label) +
scale_x_date(date_breaks = "1 month", date_labels = "%m-%Y") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 60, hjust = 1),
legend.justification = c(1, 0), legend.position = c(1, 0),
legend.title = element_text(size = 8),
legend.text = element_text(size = 8)) +
guides(color = guide_legend(nrow = 2, byrow = TRUE))
} else if (x_axis == "weeks") {
g <- ggplot(data = data_ci2 %>% filter(season %in% unique_seasons)) +
facet_wrap(~sub_field, nrow=1) +
geom_line(aes_string(x = "week_from_doy", y = y_aesthetic, col = "season", group = "season")) +
labs(title = paste("Plot of", y_label, "for Pivot", pivotName),
color = "Season",
y = y_label,
x = "Week of Year") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 60, hjust = 1),
legend.justification = c(1, 0), legend.position = c(1, 0),
legend.title = element_text(size = 8),
legend.text = element_text(size = 8)) +
guides(color = guide_legend(nrow = 2, byrow = TRUE))
} else {
g <- ggplot(data = data_ci2 %>% filter(season %in% unique_seasons)) +
facet_wrap(~sub_field, nrow=1) +
geom_line(aes_string(x = "DOY", y = y_aesthetic, col = "season", group = "season")) +
labs(title = paste("Plot of", y_label, "for Pivot", pivotName),
color = "Season",
y = y_label,
x = "Age of Crop (Days)") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 60, hjust = 1),
legend.justification = c(1, 0), legend.position = c(1, 0),
legend.title = element_text(size = 8),
legend.text = element_text(size = 8)) +
guides(color = guide_legend(nrow = 2, byrow = TRUE))
}
return(g)
}
# facet_on FALSE
g1 <- cum_ci_plot("1.16", "value", FALSE, "days")
g2 <- cum_ci_plot("3a13", "cumulative_CI", FALSE, "days")
g3 <- cum_ci_plot("3a13", "CI_rate", FALSE, "days")
g7 <- cum_ci_plot("3a13", "value", FALSE, "weeks")
g8 <- cum_ci_plot("3a13", "cumulative_CI", FALSE, "weeks")
g9 <- cum_ci_plot("3a13", "CI_rate", FALSE, "weeks")
# facet_on TRUE
g4 <- cum_ci_plot("3a13", "value", TRUE, "days")
g5 <- cum_ci_plot("3a13", "cumulative_CI", TRUE, "days")
g6 <- cum_ci_plot("3a13", "CI_rate", TRUE, "days")

View file

@ -0,0 +1,83 @@
library(ggplot2)
library(dplyr)
library(sf)
library(terra)
# Define the file paths
raster_dir <- "C:/Users/timon/Resilience BV/4020 SCane ESA DEMO - Documenten/General/4020 SCDEMO Team/4020 TechnicalData/WP3/smartcane/laravel_app/storage/app/chemba/merged_final_tif"
polygon_path <- "C:/Users/timon/Resilience BV/4020 SCane ESA DEMO - Documenten/General/4020 SCDEMO Team/4020 TechnicalData/WP3/smartcane/r_app/experiments/pivot.geojson"
# Load the polygon
polygon <- st_read(polygon_path)
# Filter the polygon for the specific field
polygon_filtered <- subset(polygon, field == "1.1")
# List all raster files in the directory
raster_files <- list.files(raster_dir, pattern = "\\.tif$", full.names = FALSE)
raster_file <- raster_files[1]
# Initialize an empty list to store CI values
ci_values <- list()
# Define a function to process each raster file
process_raster_file <- function(raster_file) {
# Extract the date from the file name (assuming the date is in YYYYMMDD format)
date <- as.Date(substr(raster_file, 1, 10), format = "%Y-%m-%d")
# Load the raster
raster_data <- terra::rast(file.path(raster_dir, raster_file))
# Crop the raster using the filtered polygon
cropped_raster <- terra::crop(raster_data, polygon_filtered)
# Extract the CI band (assuming the CI band is the first band)
ci_band <- cropped_raster$CI
# Extract the CI values from the CI band
return(data.frame(Date = date, CI = terra::values(ci_band)) %>%
filter(!is.na(CI)) %>%
mutate(Polygon = unique(polygon_filtered$field)))
}
# Use walk to apply the function to each raster file
ci_values <- purrr::map(raster_files, process_raster_file)
# Combine all CI values into a single data frame
ci_values_df <- do.call(rbind, ci_values)
head(ci_values_df)
# Plot the first raster
plot(terra::rast(file.path(raster_dir, raster_files[1]))[[1]])
# Calculate the mean CI value
ci_stats <- ci_values_df %>%
group_by(Date, Polygon) %>%
summarize(
mean_ci = mean(CI, na.rm = TRUE),
q5 = quantile(CI, 0.05, na.rm = TRUE),
q25 = quantile(CI, 0.25, na.rm = TRUE),
q50 = quantile(CI, 0.50, na.rm = TRUE),
q75 = quantile(CI, 0.75, na.rm = TRUE),
q95 = quantile(CI, 0.95, na.rm = TRUE),
cv = sd(CI, na.rm = TRUE) / mean(CI, na.rm = TRUE) * 100,
.groups = "drop"
)
# Plot the mean CI value over time
ggplot(ci_stats, aes(x = Date, y = mean_ci)) +
geom_line() +
geom_ribbon(aes(ymin = q25, ymax = q75), alpha = 0.2) +
labs(title = "Mean CI value over time",
x = "Date",
y = "Mean CI") +
theme_minimal()
# Plot the coefficient of variation over time
ggplot(ci_stats, aes(x = Date, y = cv)) +
geom_line() +
labs(title = "Coefficient of variation over time",
x = "Date",
y = "CV (%)") +
theme_minimal()

View file

@ -0,0 +1,38 @@
# run_tests.R
#
# TEST RUNNER FOR SMARTCANE
# =======================
# This script runs all tests for the SmartCane project.
# Usage: Rscript run_tests.R [test_pattern]
# - test_pattern: Optional regex pattern to match test files (default: all test_*.R files)
#
# Process command line arguments
args <- commandArgs(trailingOnly = TRUE)
test_pattern <- if (length(args) > 0) args[1] else "^test_.+\\.R$"
# Set working directory to script location
script_dir <- dirname(normalizePath(sys.frame(1)$ofile))
setwd(script_dir)
# Source test framework
source("tests/test_framework.R")
# Set up test environment
env <- setup_test_env()
# Run tests
cat("Running tests with pattern:", test_pattern, "\n\n")
success <- run_tests(test_pattern)
# Clean up
teardown_test_env()
# Exit with appropriate status code
if (success) {
cat("\n✓ All tests passed successfully!\n")
quit(save = "no", status = 0)
} else {
cat("\n✗ Some tests failed.\n")
quit(save = "no", status = 1)
}

233
r_app/growth_model_utils.R Normal file
View file

@ -0,0 +1,233 @@
# filepath: c:\Users\timon\Resilience BV\4020 SCane ESA DEMO - Documenten\General\4020 SCDEMO Team\4020 TechnicalData\WP3\smartcane\r_app\growth_model_utils.R
#
# GROWTH_MODEL_UTILS.R
# ===================
# Utility functions for growth model interpolation and manipulation.
# These functions support the creation of continuous growth models from point measurements.
#' Safe logging function that works whether log_message exists or not
#'
#' @param message The message to log
#' @param level The log level (default: "INFO")
#' @return NULL (used for side effects)
#'
safe_log <- function(message, level = "INFO") {
if (exists("log_message")) {
log_message(message, level)
} else {
if (level %in% c("ERROR", "WARNING")) {
warning(message)
} else {
message(message)
}
}
}
#' Load and prepare the combined CI data
#'
#' @param data_dir Directory containing the combined CI data
#' @return Long-format dataframe with CI values by date
#'
load_combined_ci_data <- function(data_dir) {
file_path <- here::here(data_dir, "combined_CI_data.rds")
if (!file.exists(file_path)) {
stop(paste("Combined CI data file not found:", file_path))
}
safe_log(paste("Loading CI data from:", file_path))
# Load and transform the data to long format
pivot_stats <- readRDS(file_path) %>%
dplyr::ungroup() %>%
dplyr::group_by(field, sub_field) %>%
dplyr::summarise(dplyr::across(everything(), ~ first(stats::na.omit(.))), .groups = "drop")
pivot_stats_long <- pivot_stats %>%
tidyr::gather("Date", value, -field, -sub_field) %>%
dplyr::mutate(Date = lubridate::ymd(Date)) %>%
tidyr::drop_na(c("value", "Date")) %>%
dplyr::mutate(value = as.numeric(value)) %>%
dplyr::filter_all(dplyr::all_vars(!is.infinite(.))) %>%
dplyr::distinct()
safe_log(paste("Loaded", nrow(pivot_stats_long), "CI data points"))
return(pivot_stats_long)
}
#' Extract and interpolate CI data for a specific field and season
#'
#' @param field_name Name of the field or sub-field
#' @param harvesting_data Dataframe with harvesting information
#' @param field_CI_data Dataframe with CI measurements
#' @param season Year of the growing season
#' @return Dataframe with interpolated daily CI values
#'
extract_CI_data <- function(field_name, harvesting_data, field_CI_data, season) {
# Filter harvesting data for the given season and field name
filtered_harvesting_data <- harvesting_data %>%
dplyr::filter(year == season, sub_field == field_name)
if (nrow(filtered_harvesting_data) == 0) {
safe_log(paste("No harvesting data found for field:", field_name, "in season:", season), "WARNING")
return(data.frame())
}
# Filter field CI data for the given field name
filtered_field_CI_data <- field_CI_data %>%
dplyr::filter(sub_field == field_name)
# Return an empty data frame if no CI data is found
if (nrow(filtered_field_CI_data) == 0) {
safe_log(paste("No CI data found for field:", field_name, "in season:", season), "WARNING")
return(data.frame())
}
# Log season dates
season_start <- filtered_harvesting_data$season_start[1]
season_end <- filtered_harvesting_data$season_end[1]
ci_date_range <- paste(format(min(filtered_field_CI_data$Date), "%Y-%m-%d"),
"to",
format(max(filtered_field_CI_data$Date), "%Y-%m-%d"))
# Create a linear interpolation function for the CI data
tryCatch({
ApproxFun <- stats::approxfun(x = filtered_field_CI_data$Date, y = filtered_field_CI_data$value)
Dates <- seq.Date(min(filtered_field_CI_data$Date), max(filtered_field_CI_data$Date), by = 1)
LinearFit <- ApproxFun(Dates)
# Combine interpolated data with the original CI data
CI <- data.frame(Date = Dates, FitData = LinearFit) %>%
dplyr::left_join(filtered_field_CI_data, by = "Date") %>%
dplyr::filter(Date > filtered_harvesting_data$season_start & Date < filtered_harvesting_data$season_end)
# If CI is empty after filtering, return an empty dataframe
if (nrow(CI) == 0) {
safe_log(paste0("No CI data within season dates for field: ", field_name,
" (Season: ", season, ", dates: ",
format(season_start, "%Y-%m-%d"), " to ",
format(season_end, "%Y-%m-%d"),
"). Available CI data range: ", ci_date_range),
"WARNING")
return(data.frame())
}
# Add additional columns
CI <- CI %>%
dplyr::mutate(
DOY = seq(1, n(), 1),
model = paste0("Data", season, " : ", field_name),
season = season,
subField = field_name
)
# Log successful interpolation
safe_log(paste0("Successfully interpolated CI data for field: ", field_name,
" (Season: ", season, ", dates: ",
format(season_start, "%Y-%m-%d"), " to ",
format(season_end, "%Y-%m-%d"),
"). ", nrow(CI), " data points created."))
return(CI)
}, error = function(e) {
safe_log(paste0("Error interpolating CI data for field ", field_name,
" in season ", season,
" (", format(season_start, "%Y-%m-%d"), " to ",
format(season_end, "%Y-%m-%d"),
"): ", e$message), "ERROR")
return(data.frame())
})
}
#' Generate interpolated CI data for all fields and seasons
#'
#' @param years Vector of years to process
#' @param harvesting_data Dataframe with harvesting information
#' @param ci_data Long-format dataframe with CI measurements
#' @return Dataframe with interpolated daily CI values for all fields/seasons
#'
generate_interpolated_ci_data <- function(years, harvesting_data, ci_data) {
safe_log("Starting CI data interpolation for all fields")
# Process each year
result <- purrr::map_df(years, function(yr) {
safe_log(paste("Processing year:", yr))
# Get the fields harvested in this year with valid season start dates
sub_fields <- harvesting_data %>%
dplyr::filter(year == yr, !is.na(season_start)) %>%
dplyr::pull(sub_field)
if (length(sub_fields) == 0) {
safe_log(paste("No fields with valid season data for year:", yr), "WARNING")
return(data.frame())
}
# Filter sub_fields to only include those with value data in ci_data
valid_sub_fields <- sub_fields %>%
purrr::keep(~ any(ci_data$sub_field == .x))
if (length(valid_sub_fields) == 0) {
safe_log(paste("No fields with CI data for year:", yr), "WARNING")
return(data.frame())
}
# Extract and interpolate data for each valid field
safe_log(paste("Processing", length(valid_sub_fields), "fields for year:", yr))
result <- purrr::map(valid_sub_fields, ~ extract_CI_data(.x,
harvesting_data = harvesting_data,
field_CI_data = ci_data,
season = yr)) %>%
purrr::list_rbind()
safe_log(paste("Generated", nrow(result), "interpolated data points for year:", yr))
return(result)
})
safe_log(paste("Total interpolated data points:", nrow(result)))
return(result)
}
#' Calculate growth metrics for interpolated CI data
#'
#' @param interpolated_data Dataframe with interpolated CI values
#' @return Dataframe with added growth metrics (CI_per_day and cumulative_CI)
#'
calculate_growth_metrics <- function(interpolated_data) {
if (nrow(interpolated_data) == 0) {
safe_log("No data provided to calculate growth metrics", "WARNING")
return(interpolated_data)
}
result <- interpolated_data %>%
dplyr::group_by(model) %>%
dplyr::mutate(
CI_per_day = FitData - dplyr::lag(FitData),
cumulative_CI = cumsum(FitData)
)
return(result)
}
#' Save interpolated growth model data
#'
#' @param data Dataframe with interpolated growth data
#' @param output_dir Directory to save the output
#' @param file_name Filename for the output (default: "All_pivots_Cumulative_CI_quadrant_year_v2.rds")
#' @return Path to the saved file
#'
save_growth_model <- function(data, output_dir, file_name = "All_pivots_Cumulative_CI_quadrant_year_v2.rds") {
# Create output directory if it doesn't exist
dir.create(output_dir, recursive = TRUE, showWarnings = FALSE)
# Create full file path
file_path <- here::here(output_dir, file_name)
# Save the data
saveRDS(data, file_path)
safe_log(paste("Interpolated CI data saved to:", file_path))
return(file_path)
}

View file

@ -1,15 +1,40 @@
install.packages('CAST') # Install required packages for SmartCane project
install.packages("packages/CIprep_0.1.4.tar.gz",repos=NULL, type="source") # This script installs all packages needed to run the CI report dashboard
install.packages('caret')
install.packages('exactextractr') # List of required packages
install.packages('googledrive') required_packages <- c(
install.packages('here') # Core packages
install.packages('lubridate') "here", "tidyverse", "sf", "terra", "tmap", "lubridate",
install.packages('raster')
install.packages('readxl') # Additional data manipulation
install.packages('rsample') "zoo", "readxl", "knitr", "rmarkdown", "dplyr", "purrr", "stringr",
install.packages('sf')
install.packages('terra') # Spatial analysis
install.packages('tidyverse') "exactextractr",
install.packages('tmap')
install.packages('zoo') # Machine learning and statistics
"rsample", "caret", "randomForest", "CAST"
)
# Function to install missing packages
install_if_missing <- function(pkg) {
if (!requireNamespace(pkg, quietly = TRUE)) {
message(paste("Installing package:", pkg))
install.packages(pkg, repos = "https://cloud.r-project.org")
} else {
message(paste("Package already installed:", pkg))
}
}
# Install missing packages
for (pkg in required_packages) {
install_if_missing(pkg)
}
# Load core packages to verify installation
library(here)
library(tidyverse)
library(sf)
library(terra)
message("All required packages have been installed!")

View file

@ -1,124 +1,102 @@
library(sf) # filepath: c:\Users\timon\Resilience BV\4020 SCane ESA DEMO - Documenten\General\4020 SCDEMO Team\4020 TechnicalData\WP3\smartcane\r_app\interpolate_growth_model.R
library(terra) #
# INTERPOLATE_GROWTH_MODEL.R
# =========================
# This script interpolates CI (Chlorophyll Index) values between measurement dates
# to create a continuous growth model. It generates daily values and cumulative
# CI statistics for each field.
#
# Usage: Rscript interpolate_growth_model.R [project_dir]
# - project_dir: Project directory name (e.g., "chemba")
#
# 1. Load required packages
# -----------------------
suppressPackageStartupMessages({
library(tidyverse) library(tidyverse)
library(lubridate) library(lubridate)
library(exactextractr) library(here)
})
# Vang alle command line argumenten op # 2. Main function to handle interpolation
# -------------------------------------
main <- function() {
# Process command line arguments
args <- commandArgs(trailingOnly = TRUE) args <- commandArgs(trailingOnly = TRUE)
# Converteer het tweede argument naar een string waarde # Get project directory from arguments or use default
if (length(args) >= 1 && !is.na(args[1])) {
project_dir <- as.character(args[1]) project_dir <- as.character(args[1])
} else {
# Controleer of data_dir een geldige waarde is
if (!is.character(project_dir)) {
project_dir <- "chemba" project_dir <- "chemba"
message("No project_dir provided. Using default:", project_dir)
} }
# Make project_dir available globally so parameters_project.R can use it
assign("project_dir", project_dir, envir = .GlobalEnv)
# Initialize project configuration and load utility functions
tryCatch({
source("parameters_project.R") source("parameters_project.R")
source("ci_extraction_utils.R") source("growth_model_utils.R")
# Check if the file exists }, error = function(e) {
file_path <- here(cumulative_CI_vals_dir, "combined_CI_data.rds") warning("Default source files not found. Attempting to source from 'r_app' directory.")
pivot_stats2 <- data.frame() tryCatch({
if (file.exists(file_path)) { source(here::here("r_app", "parameters_project.R"))
pivot_stats2 <- readRDS(file_path) %>% source(here::here("r_app", "growth_model_utils.R"))
ungroup() %>% warning(paste("Successfully sourced files from 'r_app' directory."))
group_by(field, sub_field) %>%
summarise(across(everything(), ~ first(na.omit(.))), .groups = "drop") }, error = function(e) {
stop("Failed to source required files from both default and 'r_app' directories.")
})
})
log_message("Starting CI growth model interpolation")
# Load and process the data
tryCatch({
# Load the combined CI data
CI_data <- load_combined_ci_data(cumulative_CI_vals_dir)
# Validate harvesting data
if (is.null(harvesting_data) || nrow(harvesting_data) == 0) {
stop("No harvesting data available")
} }
head(pivot_stats2)
#%>% drop_na(pivot_quadrant)
# gather data into long format for easier calculation and visualisation
pivot_stats_long <- pivot_stats2 %>%
tidyr::gather("Date", value, -field, -sub_field ) %>%
mutate(#Date = right(Date, 8),
Date = lubridate::ymd(Date)
) %>%
drop_na(c("value","Date")) %>%
mutate(value = as.numeric(value))%>%
filter_all(all_vars(!is.infinite(.))) %>%
# rename(field = pivot_quadrant,
# sub_field = field) %>%
unique()
# Get the years from harvesting data
years <- harvesting_data %>% years <- harvesting_data %>%
filter(!is.na(season_start)) %>% filter(!is.na(season_start)) %>%
distinct(year) %>% distinct(year) %>%
pull(year) pull(year)
extract_CI_data <- function(field_names, harvesting_data, field_CI_data, season) { log_message(paste("Processing data for years:", paste(years, collapse = ", ")))
# Filter harvesting data for the given season and field names
filtered_harvesting_data <- harvesting_data %>%
filter(year == season, sub_field %in% field_names)
# Filter field CI data for the given field names # Generate interpolated CI data for each year and field
filtered_field_CI_data <- field_CI_data %>% CI_all <- generate_interpolated_ci_data(years, harvesting_data, CI_data)
filter(sub_field %in% field_names)
# Return an empty data frame if no CI data is found # Calculate growth metrics and save the results
if (nrow(filtered_field_CI_data) == 0) { if (nrow(CI_all) > 0) {
return(data.frame()) # Add daily and cumulative metrics
CI_all_with_metrics <- calculate_growth_metrics(CI_all)
# Save the processed data
save_growth_model(
CI_all_with_metrics,
cumulative_CI_vals_dir,
"All_pivots_Cumulative_CI_quadrant_year_v2.rds"
)
} else {
log_message("No CI data was generated after interpolation", level = "WARNING")
} }
log_message("Growth model interpolation completed successfully")
# Create a linear interpolation function for the CI data }, error = function(e) {
ApproxFun <- approxfun(x = filtered_field_CI_data$Date, y = filtered_field_CI_data$value) log_message(paste("Error in growth model interpolation:", e$message), level = "ERROR")
Dates <- seq.Date(ymd(min(filtered_field_CI_data$Date)), ymd(max(filtered_field_CI_data$Date)), by = 1) stop(e$message)
LinearFit <- ApproxFun(Dates)
# Combine interpolated data with the original CI data
CI <- data.frame(Date = Dates, FitData = LinearFit) %>%
left_join(., filtered_field_CI_data, by = "Date") %>%
filter(Date > filtered_harvesting_data$season_start & Date < filtered_harvesting_data$season_end)
# If CI is empty after filtering, return an empty dataframe
if (nrow(CI) == 0) {
message ('CI empty after filtering')
return(data.frame())
}
# Add additional columns if data exists
CI <- CI %>%
mutate(DOY = seq(1, n(), 1),
model = paste0("Data", season, " : ", field_names),
season = season,
subField = field_names)
return(CI)
}
message(harvesting_data)
CI_all <- map_df(years, function(yr) {
# yr = 2021
message(yr)
# Get the fields harvested in this year
sub_fields <- harvesting_data %>%
filter(year == yr) %>%
filter(!is.na(season_start)) %>%
pull(sub_field)
# Filter sub_fields to only include those with value data in pivot_stats_long
valid_sub_fields <- sub_fields %>%
keep(~ any(pivot_stats_long$sub_field == .x))
# Extract data for each valid field
map(valid_sub_fields, ~ extract_CI_data(.x, harvesting_data = harvesting_data, field_CI_data = pivot_stats_long, season = yr)) %>%
list_rbind()
}) })
}
# it will crash here if CI_all is empty and will not overwrite the rds rendering growth_model.R useless if (sys.nframe() == 0) {
# if(nrow(CI_all) > 0){ main()
CI_all <- CI_all %>% }
group_by(model) %>%
mutate(CI_per_day = FitData - lag(FitData), cumulative_CI = cumsum(FitData))
# }
saveRDS(CI_all, here(cumulative_CI_vals_dir,"All_pivots_Cumulative_CI_quadrant_year_v2.rds"))
message('rds saved')

View file

@ -1,137 +1,119 @@
# filepath: c:\Users\timon\Resilience BV\4020 SCane ESA DEMO - Documenten\General\4020 SCDEMO Team\4020 TechnicalData\WP3\smartcane\r_app\mosaic_creation.R
#
# MOSAIC_CREATION.R
# ===============
# This script creates weekly mosaics from daily satellite imagery.
# It handles command-line arguments and initiates the mosaic creation process.
#
# Usage: Rscript mosaic_creation.R [end_date] [offset] [project_dir] [file_name]
# - end_date: End date for processing (YYYY-MM-DD format)
# - offset: Number of days to look back from end_date
# - project_dir: Project directory name (e.g., "chemba")
# - file_name: Optional custom output file name
#
# 1. Load required packages
# -----------------------
suppressPackageStartupMessages({
library(sf) library(sf)
library(terra) library(terra)
library(tidyverse) library(tidyverse)
library(lubridate) library(lubridate)
library(here)
})
# Vang alle command line argumenten op # 2. Process command line arguments and run mosaic creation
# ------------------------------------------------------
main <- function() {
# Capture command line arguments
args <- commandArgs(trailingOnly = TRUE) args <- commandArgs(trailingOnly = TRUE)
# Controleer of er ten minste één argument is doorgegeven # Process project_dir argument with default
if (length(args) == 0) { if (length(args) >= 3 && !is.na(args[3])) {
stop("Geen argumenten doorgegeven aan het script") project_dir <- as.character(args[3])
} else {
# Default project directory
project_dir <- "chemba"
message("No project_dir provided. Using default:", project_dir)
} }
# Converteer het eerste argument naar een numerieke waarde # Make project_dir available globally so parameters_project.R can use it
assign("project_dir", project_dir, envir = .GlobalEnv)
# Process end_date argument with default
if (length(args) >= 1 && !is.na(args[1])) {
end_date <- as.Date(args[1]) end_date <- as.Date(args[1])
if (is.na(end_date)) {
message("Invalid end_date provided. Using current date.")
end_date <- Sys.Date()
#end_date <- "2024-08-25" # Default date for testing
}
} else {
# Default to current date if no argument is provided
end_date <- Sys.Date()
#end_date <- "2024-08-25" # Default date for testing
message("No end_date provided. Using current date: ", format(end_date))
}
# Process offset argument with default
if (length(args) >= 2 && !is.na(args[2])) {
offset <- as.numeric(args[2]) offset <- as.numeric(args[2])
# Controleer of weeks_ago een geldig getal is if (is.na(offset) || offset <= 0) {
if (is.na(offset)) { message("Invalid offset provided. Using default (7 days).")
# stop("Het argument is geen geldig getal")
offset <- 7 offset <- 7
} }
} else {
# Converteer het tweede argument naar een string waarde # Default to 7 days if no argument is provided
project_dir <- as.character(args[3]) offset <- 7
message("No offset provided. Using default:", offset, "days")
# Controleer of data_dir een geldige waarde is
if (!is.character(project_dir)) {
project_dir <- "chemba"
} }
# 3. Initialize project configuration
# --------------------------------
tryCatch({
source("parameters_project.R") source("parameters_project.R")
source("mosaic_creation_utils.R") source("mosaic_creation_utils.R")
safe_log(paste("Successfully sourced files from default directory."))
}, error = function(e) {
warning("Default source files not found. Attempting to source from 'r_app' directory.")
tryCatch({
source(here::here("r_app", "parameters_project.R"))
source(here::here("r_app", "mosaic_creation_utils.R"))
warning(paste("Successfully sourced files from 'r_app' directory."))
}, error = function(e) {
stop("Failed to source required files from both default and 'r_app' directories.")
})
})
week <- week(end_date) # 4. Generate date range for processing
# ---------------------------------
dates <- date_list(end_date, offset) dates <- date_list(end_date, offset)
safe_log(paste("Processing data for week", dates$week, "of", dates$year))
file_name_tif <- as.character(args[4]) # Create output filename
if (is.na(file_name_tif)) { file_name_tif <- if (length(args) >= 4 && !is.na(args[4])) {
file_name_tif <- paste0("week_", sprintf("%02d", dates$week), "_", dates$year, ".tif") as.character(args[4])
}
print(dates)
print(file_name_tif)
#load pivot geojson
# pivot_sf_q <- st_read(here(data_dir, "pivot.geojson")) %>% dplyr::select(pivot, pivot_quadrant) %>% vect()
vrt_files <- list.files(here(daily_vrt),full.names = T)
vrt_list <- map(dates$days_filter, ~ vrt_files[grepl(pattern = .x, x = vrt_files)]) %>%
compact() %>%
flatten_chr()
raster_files_final <- list.files(merged_final,full.names = T, pattern = ".tif")
if (length(vrt_list) > 0) {
print("vrt list made, preparing mosaic creation by counting cloud cover")
total_pix_area <-
rast(vrt_list[1]) %>% terra::subset(1) %>% setValues(1) %>%
crop(field_boundaries, mask = TRUE) %>%
global(., fun = "notNA") #%>%
layer_5_list <- purrr::map(vrt_list, function(vrt_list) {
rast(vrt_list[1]) %>% terra::subset(1)
}) %>% rast()
missing_pixels_count <-
layer_5_list %>% global(., fun = "notNA") %>%
mutate(
total_pixels = total_pix_area$notNA,
missing_pixels_percentage = round(100 - ((
notNA / total_pix_area$notNA
) * 100)),
thres_5perc = as.integer(missing_pixels_percentage < 5),
thres_40perc = as.integer(missing_pixels_percentage < 45)
)
index_5perc <- which(missing_pixels_count$thres_5perc == max(missing_pixels_count$thres_5perc))
index_40perc <- which(missing_pixels_count$thres_40perc == max(missing_pixels_count$thres_40perc))
## Create mosaic
if (sum(missing_pixels_count$thres_5perc) > 1) {
message("More than 1 raster without clouds (<5%), max composite made")
cloudy_rasters_list <- vrt_list[index_5perc]
rsrc <- sprc(cloudy_rasters_list)
x <- terra::mosaic(rsrc, fun = "max")
names(x) <- c("Red", "Green", "Blue", "NIR", "CI")
} else if (sum(missing_pixels_count$thres_5perc) == 1) {
message("Only 1 raster without clouds (<5%)")
x <- rast(vrt_list[index_5perc[1]])
names(x) <- c("Red", "Green", "Blue", "NIR", "CI")
} else if (sum(missing_pixels_count$thres_40perc) > 1) {
message("More than 1 image contains clouds, composite made of <40% cloud cover images")
cloudy_rasters_list <- vrt_list[index_40perc]
rsrc <- sprc(cloudy_rasters_list)
x <- mosaic(rsrc, fun = "max")
names(x) <- c("Red", "Green", "Blue", "NIR", "CI")
} else if (sum(missing_pixels_count$thres_40perc) == 1) {
message("Only 1 image available but contains clouds")
x <- rast(vrt_list[index_40perc[1]])
names(x) <- c("Red", "Green", "Blue", "NIR", "CI")
} else if (sum(missing_pixels_count$thres_40perc) == 0) {
message("No cloud free images available, all images combined")
rsrc <- sprc(vrt_list)
x <- mosaic(rsrc, fun = "max")
names(x) <- c("Red", "Green", "Blue", "NIR", "CI")
}
} else { } else {
message("No images available this week, empty mosaic created") paste0("week_", sprintf("%02d", dates$week), "_", dates$year, ".tif")
x <- rast(raster_files_final[1]) %>% setValues(0) %>%
crop(field_boundaries, mask = TRUE)
names(x) <- c("Red", "Green", "Blue", "NIR", "CI")
} }
plot(x$CI, main = paste("CI map ", dates$week))
plotRGB(x, main = paste("RGB map ", dates$week))
file_path_tif <- here(weekly_CI_mosaic ,file_name_tif) safe_log(paste("Output will be saved as:", file_name_tif))
writeRaster(x, file_path_tif, overwrite=TRUE)
message("Raster written/made at: ", file_path_tif) # 5. Create weekly mosaic using the function from utils
# -------------------------------------------------
create_weekly_mosaic(
dates = dates,
field_boundaries = field_boundaries,
daily_vrt_dir = daily_vrt,
merged_final_dir = merged_final,
output_dir = weekly_CI_mosaic,
file_name_tif = file_name_tif,
create_plots = TRUE
)
}
if (sys.nframe() == 0) {
main()
}

View file

@ -1,13 +1,424 @@
# utils for mosaic creation # MOSAIC_CREATION_UTILS.R
# ======================
# Utility functions for creating weekly mosaics from daily satellite imagery.
# These functions support cloud cover assessment, date handling, and mosaic creation.
#' Safe logging function
#' @param message The message to log
#' @param level The log level (default: "INFO")
#' @return NULL (used for side effects)
#'
safe_log <- function(message, level = "INFO") {
if (exists("log_message")) {
log_message(message, level)
} else {
if (level %in% c("ERROR", "WARNING")) {
warning(message)
} else {
message(message)
}
}
}
#' Generate a sequence of dates for processing
#'
#' @param end_date The end date for the sequence (Date object)
#' @param offset Number of days to look back from end_date
#' @return A list containing week number, year, and a sequence of dates for filtering
#'
date_list <- function(end_date, offset) { date_list <- function(end_date, offset) {
offset <- as.numeric(offset) - 1 # Input validation
if (!lubridate::is.Date(end_date)) {
end_date <- as.Date(end_date) end_date <- as.Date(end_date)
if (is.na(end_date)) {
stop("Invalid end_date provided. Expected a Date object or a string convertible to Date.")
}
}
offset <- as.numeric(offset)
if (is.na(offset) || offset < 1) {
stop("Invalid offset provided. Expected a positive number.")
}
# Calculate date range
offset <- offset - 1 # Adjust offset to include end_date
start_date <- end_date - lubridate::days(offset) start_date <- end_date - lubridate::days(offset)
week <- week(start_date) # Extract week and year information
year <- year(start_date) week <- lubridate::week(start_date)
days_filter <- seq(from = start_date, to = end_date, by = "day") year <- lubridate::year(start_date)
return(list("week" = week, "year" = year, "days_filter" = days_filter)) # Generate sequence of dates
days_filter <- seq(from = start_date, to = end_date, by = "day")
days_filter <- format(days_filter, "%Y-%m-%d") # Format for consistent filtering
# Log the date range
safe_log(paste("Date range generated from", start_date, "to", end_date))
return(list(
"week" = week,
"year" = year,
"days_filter" = days_filter,
"start_date" = start_date,
"end_date" = end_date
))
}
#' Create a weekly mosaic from available VRT files
#'
#' @param dates List from date_list() with date range info
#' @param field_boundaries Field boundaries for image cropping
#' @param daily_vrt_dir Directory containing VRT files
#' @param merged_final_dir Directory with merged final rasters
#' @param output_dir Output directory for weekly mosaics
#' @param file_name_tif Output filename for the mosaic
#' @param create_plots Whether to create visualization plots (default: TRUE)
#' @return The file path of the saved mosaic
#'
create_weekly_mosaic <- function(dates, field_boundaries, daily_vrt_dir,
merged_final_dir, output_dir, file_name_tif,
create_plots = TRUE) {
# Find VRT files for the specified date range
vrt_list <- find_vrt_files(daily_vrt_dir, dates)
# Find final raster files for fallback
raster_files_final <- list.files(merged_final_dir, full.names = TRUE, pattern = "\\.tif$")
# Process the mosaic if VRT files are available
if (length(vrt_list) > 0) {
safe_log("VRT list created, assessing cloud cover for mosaic creation")
# Calculate cloud cover statistics
missing_pixels_count <- count_cloud_coverage(vrt_list, field_boundaries)
# Create mosaic based on cloud cover assessment
mosaic <- create_mosaic(vrt_list, missing_pixels_count, field_boundaries, raster_files_final)
} else {
safe_log("No VRT files available for the date range, creating empty mosaic", "WARNING")
# Create empty mosaic if no files are available
if (length(raster_files_final) == 0) {
stop("No VRT files or final raster files available to create mosaic")
}
mosaic <- terra::rast(raster_files_final[1]) %>%
terra::setValues(0) %>%
terra::crop(field_boundaries, mask = TRUE)
names(mosaic) <- c("Red", "Green", "Blue", "NIR", "CI")
}
# Save the mosaic
file_path <- save_mosaic(mosaic, output_dir, file_name_tif, create_plots)
safe_log(paste("Weekly mosaic processing completed for week", dates$week))
return(file_path)
}
#' Find VRT files within a date range
#'
#' @param vrt_directory Directory containing VRT files
#' @param dates List from date_list() function containing days_filter
#' @return Character vector of VRT file paths
#'
find_vrt_files <- function(vrt_directory, dates) {
# Get all VRT files in directory
vrt_files <- list.files(here::here(vrt_directory), full.names = TRUE)
if (length(vrt_files) == 0) {
warning("No VRT files found in directory: ", vrt_directory)
return(character(0))
}
# Filter files by dates
vrt_list <- purrr::map(dates$days_filter, ~ vrt_files[grepl(pattern = .x, x = vrt_files)]) %>%
purrr::compact() %>%
purrr::flatten_chr()
# Log results
safe_log(paste("Found", length(vrt_list), "VRT files for the date range"))
return(vrt_list)
}
#' Count missing pixels (clouds) in rasters
#'
#' @param vrt_list List of VRT files to analyze
#' @param field_boundaries Field boundaries vector for masking
#' @return Data frame with cloud coverage statistics
#'
count_cloud_coverage <- function(vrt_list, field_boundaries) {
if (length(vrt_list) == 0) {
warning("No VRT files provided for cloud coverage calculation")
return(NULL)
}
tryCatch({
# Calculate total pixel area using the first VRT file
total_pix_area <- terra::rast(vrt_list[1]) |>
terra::subset(1) |>
terra::setValues(1) |>
terra::crop(field_boundaries, mask = TRUE) |>
terra::global(fun = "notNA")
# Process each raster to detect clouds and shadows
processed_rasters <- list()
cloud_masks <- list()
# Create data frame for missing pixels count
missing_pixels_df <- data.frame(
filename = vrt_list,
notNA = numeric(length(vrt_list)),
total_pixels = numeric(length(vrt_list)),
missing_pixels_percentage = numeric(length(vrt_list)),
thres_5perc = numeric(length(vrt_list)),
thres_40perc = numeric(length(vrt_list))
)
# Fill in the data frame with missing pixel statistics
for (i in seq_along(processed_rasters)) {
notna_count <- terra::global(processed_rasters[[i]][[1]], fun = "notNA")$notNA
missing_pixels_df$notNA[i] <- notna_count
missing_pixels_df$total_pixels[i] <- total_pix_area$notNA
missing_pixels_df$missing_pixels_percentage[i] <- round(100 - ((notna_count / total_pix_area$notNA) * 100))
missing_pixels_df$thres_5perc[i] <- as.integer(missing_pixels_df$missing_pixels_percentage[i] < 5)
missing_pixels_df$thres_40perc[i] <- as.integer(missing_pixels_df$missing_pixels_percentage[i] < 45)
}
# Store processed rasters and cloud masks as attributes
attr(missing_pixels_df, "cloud_masks") <- cloud_masks
attr(missing_pixels_df, "processed_rasters") <- processed_rasters
# Log results
safe_log(paste(
"Cloud cover assessment completed for", length(vrt_list), "files.",
sum(missing_pixels_df$thres_5perc), "files with <5% cloud cover,",
sum(missing_pixels_df$thres_40perc), "files with <45% cloud cover"
))
return(missing_pixels_df)
}, error = function(e) {
warning("Error in cloud coverage calculation: ", e$message)
return(NULL)
})
}
#' Create a mosaic from VRT files based on cloud coverage
#'
#' @param vrt_list List of VRT files to create mosaic from
#' @param missing_pixels_count Cloud coverage statistics from count_cloud_coverage()
#' @param field_boundaries Field boundaries vector for masking (optional)
#' @param raster_files_final List of processed raster files to use as fallback
#' @return A SpatRaster object with the mosaic
#'
create_mosaic <- function(vrt_list, missing_pixels_count, field_boundaries = NULL, raster_files_final = NULL) {
# If no VRT files, create an empty mosaic
if (length(vrt_list) == 0) {
if (length(raster_files_final) == 0 || is.null(field_boundaries)) {
stop("No VRT files available and no fallback raster files or field boundaries provided")
}
safe_log("No images available for this period, creating empty mosaic", "WARNING")
x <- terra::rast(raster_files_final[1]) |>
terra::setValues(0) |>
terra::crop(field_boundaries, mask = TRUE)
names(x) <- c("Red", "Green", "Blue", "NIR", "CI")
return(x)
}
# If missing pixel count was not calculated, use all files
if (is.null(missing_pixels_count)) {
safe_log("No cloud coverage data available, using all images", "WARNING")
rsrc <- terra::sprc(vrt_list)
x <- terra::mosaic(rsrc, fun = "max")
names(x) <- c("Red", "Green", "Blue", "NIR", "CI")
return(x)
}
# Check if we have processed rasters from cloud detection
processed_rasters <- attr(missing_pixels_count, "processed_rasters")
cloud_masks <- attr(missing_pixels_count, "cloud_masks")
if (!is.null(processed_rasters) && length(processed_rasters) > 0) {
safe_log("Using cloud-masked rasters for mosaic creation")
# Determine best rasters to use based on cloud coverage
index_5perc <- which(missing_pixels_count$thres_5perc == max(missing_pixels_count$thres_5perc))
index_40perc <- which(missing_pixels_count$thres_40perc == max(missing_pixels_count$thres_40perc))
# Create mosaic based on available cloud-free images
if (sum(missing_pixels_count$thres_5perc) > 1) {
safe_log("Creating max composite from multiple cloud-free images (<5% clouds)")
# Use the cloud-masked rasters instead of original files
cloudy_rasters_list <- processed_rasters[index_5perc]
rsrc <- terra::sprc(cloudy_rasters_list)
x <- terra::mosaic(rsrc, fun = "max")
# Also create a composite mask showing where data is valid
mask_list <- cloud_masks[index_5perc]
mask_rsrc <- terra::sprc(mask_list)
mask_composite <- terra::mosaic(mask_rsrc, fun = "max")
attr(x, "cloud_mask") <- mask_composite
} else if (sum(missing_pixels_count$thres_5perc) == 1) {
safe_log("Using single cloud-free image (<5% clouds)")
# Use the cloud-masked raster
x <- processed_rasters[[index_5perc[1]]]
attr(x, "cloud_mask") <- cloud_masks[[index_5perc[1]]]
} else if (sum(missing_pixels_count$thres_40perc) > 1) {
safe_log("Creating max composite from partially cloudy images (<40% clouds)", "WARNING")
# Use the cloud-masked rasters
cloudy_rasters_list <- processed_rasters[index_40perc]
rsrc <- terra::sprc(cloudy_rasters_list)
x <- terra::mosaic(rsrc, fun = "max")
# Also create a composite mask
mask_list <- cloud_masks[index_40perc]
mask_rsrc <- terra::sprc(mask_list)
mask_composite <- terra::mosaic(mask_rsrc, fun = "max")
attr(x, "cloud_mask") <- mask_composite
} else if (sum(missing_pixels_count$thres_40perc) == 1) {
safe_log("Using single partially cloudy image (<40% clouds)", "WARNING")
# Use the cloud-masked raster
x <- processed_rasters[[index_40perc[1]]]
attr(x, "cloud_mask") <- cloud_masks[[index_40perc[1]]]
} else {
safe_log("No cloud-free images available, using all cloud-masked images", "WARNING")
# Use all cloud-masked rasters
rsrc <- terra::sprc(processed_rasters)
x <- terra::mosaic(rsrc, fun = "max")
# Also create a composite mask
mask_rsrc <- terra::sprc(cloud_masks)
mask_composite <- terra::mosaic(mask_rsrc, fun = "max")
attr(x, "cloud_mask") <- mask_composite
}
} else {
# Fall back to original behavior if no cloud-masked rasters available
safe_log("No cloud-masked rasters available, using original images", "WARNING")
# Determine best rasters to use based on cloud coverage
index_5perc <- which(missing_pixels_count$thres_5perc == max(missing_pixels_count$thres_5perc))
index_40perc <- which(missing_pixels_count$thres_40perc == max(missing_pixels_count$thres_40perc))
# Create mosaic based on available cloud-free images
if (sum(missing_pixels_count$thres_5perc) > 1) {
safe_log("Creating max composite from multiple cloud-free images (<5% clouds)")
cloudy_rasters_list <- vrt_list[index_5perc]
rsrc <- terra::sprc(cloudy_rasters_list)
x <- terra::mosaic(rsrc, fun = "max")
} else if (sum(missing_pixels_count$thres_5perc) == 1) {
safe_log("Using single cloud-free image (<5% clouds)")
x <- terra::rast(vrt_list[index_5perc[1]])
} else if (sum(missing_pixels_count$thres_40perc) > 1) {
safe_log("Creating max composite from partially cloudy images (<40% clouds)", "WARNING")
cloudy_rasters_list <- vrt_list[index_40perc]
rsrc <- terra::sprc(cloudy_rasters_list)
x <- terra::mosaic(rsrc, fun = "max")
} else if (sum(missing_pixels_count$thres_40perc) == 1) {
safe_log("Using single partially cloudy image (<40% clouds)", "WARNING")
x <- terra::rast(vrt_list[index_40perc[1]])
} else {
safe_log("No cloud-free images available, using all images", "WARNING")
rsrc <- terra::sprc(vrt_list)
x <- terra::mosaic(rsrc, fun = "max")
}
}
# Set consistent layer names
names(x) <- c("Red", "Green", "Blue", "NIR", "CI")
return(x)
}
#' Save a mosaic raster to disk
#'
#' @param mosaic_raster A SpatRaster object to save
#' @param output_dir Directory to save the output
#' @param file_name Filename for the output raster
#' @param plot_result Whether to create visualizations (default: FALSE)
#' @return The file path of the saved raster
#'
save_mosaic <- function(mosaic_raster, output_dir, file_name, plot_result = FALSE) {
# Validate input
if (is.null(mosaic_raster)) {
stop("No mosaic raster provided to save")
}
# Create output directory if it doesn't exist
dir.create(output_dir, recursive = TRUE, showWarnings = FALSE)
# Create full file path
file_path <- here::here(output_dir, file_name)
# Get cloud mask if it exists
cloud_mask <- attr(mosaic_raster, "cloud_mask")
# Save raster
terra::writeRaster(mosaic_raster, file_path, overwrite = TRUE)
# Save cloud mask if available
if (!is.null(cloud_mask)) {
# Create mask filename by adding _mask before extension
mask_file_name <- gsub("\\.(tif|TIF)$", "_mask.\\1", file_name)
mask_file_path <- here::here(output_dir, mask_file_name)
# Save the mask
terra::writeRaster(cloud_mask, mask_file_path, overwrite = TRUE)
safe_log(paste("Cloud/shadow mask saved to:", mask_file_path))
}
# Create plots if requested
if (plot_result) {
# Plot the CI band
if ("CI" %in% names(mosaic_raster)) {
terra::plot(mosaic_raster$CI, main = paste("CI map", file_name))
}
# Plot RGB image
if (all(c("Red", "Green", "Blue") %in% names(mosaic_raster))) {
terra::plotRGB(mosaic_raster, main = paste("RGB map", file_name))
}
# Plot cloud mask if available
if (!is.null(cloud_mask)) {
terra::plot(cloud_mask, main = paste("Cloud/shadow mask", file_name),
col = c("red", "green"))
}
# If we have both RGB and cloud mask, create a side-by-side comparison
if (all(c("Red", "Green", "Blue") %in% names(mosaic_raster)) && !is.null(cloud_mask)) {
old_par <- par(mfrow = c(1, 2))
terra::plotRGB(mosaic_raster, main = "RGB Image")
# Create a colored mask for visualization (red = cloud/shadow, green = clear)
mask_plot <- cloud_mask
terra::plot(mask_plot, main = "Cloud/Shadow Mask", col = c("red", "green"))
par(old_par)
}
}
# Log save completion
safe_log(paste("Mosaic saved to:", file_path))
return(file_path)
} }

117
r_app/packages.R Normal file
View file

@ -0,0 +1,117 @@
# packages.R
#
# PACKAGE MANAGEMENT FOR SMARTCANE
# ===============================
# This script centralizes all package dependencies for the SmartCane project.
# It installs missing packages and loads all required libraries.
#
#' Check and install packages if needed
#'
#' @param pkg_list List of packages to check and install
#' @param install_missing Whether to install missing packages
#' @return Vector of packages that couldn't be installed (if any)
#'
check_and_install_packages <- function(pkg_list, install_missing = TRUE) {
# Check which packages are already installed
is_installed <- pkg_list %in% rownames(installed.packages())
missing_pkgs <- pkg_list[!is_installed]
# Install missing packages if requested
failed_pkgs <- character(0)
if (length(missing_pkgs) > 0) {
if (install_missing) {
message("Installing ", length(missing_pkgs), " missing packages...")
for (pkg in missing_pkgs) {
tryCatch({
install.packages(pkg, repos = "https://cran.rstudio.com/", dependencies = TRUE)
message(" Installed: ", pkg)
}, error = function(e) {
warning("Failed to install package: ", pkg)
warning("Error: ", e$message)
failed_pkgs <<- c(failed_pkgs, pkg)
})
}
} else {
message("The following packages are required but not installed:")
message(paste(missing_pkgs, collapse = ", "))
failed_pkgs <- missing_pkgs
}
} else {
message("All required packages are already installed.")
}
return(failed_pkgs)
}
#' Load all required packages for SmartCane project
#'
#' @param verbose Whether to show messages during loading
#' @return Logical indicating success (TRUE if all packages loaded)
#'
load_smartcane_packages <- function(verbose = FALSE) {
# Define all required packages
required_packages <- c(
# Geospatial packages
"sf", # Simple Features for spatial vector data
"terra", # Raster data processing
"exactextractr", # Fast extraction from rasters
"tmap", # Thematic mapping for spatial visualization
# Data manipulation
"tidyverse", # Collection of data manipulation packages
"lubridate", # Date manipulation
"readxl", # Excel file reading
"stringr", # String manipulation
"purrr", # Functional programming tools
"zoo", # Time series processing with rolling functions
# Visualization
"ggplot2", # Advanced plotting
"leaflet", # Interactive maps
"plotly", # Interactive plots
# Machine learning and statistics
"caret", # Classification and regression training
"rsample", # Data sampling for modeling
"randomForest", # Random forest implementation
"CAST", # Feature selection for spatial data
# Project management
"here", # Path handling
# Document generation
"knitr", # Dynamic report generation
"rmarkdown" # R Markdown processing
)
# Check and install missing packages
failed_pkgs <- check_and_install_packages(required_packages)
# Load all installed packages
success <- TRUE
for (pkg in setdiff(required_packages, failed_pkgs)) {
tryCatch({
if (verbose) message("Loading package: ", pkg)
suppressPackageStartupMessages(library(pkg, character.only = TRUE))
}, error = function(e) {
warning("Failed to load package: ", pkg)
warning("Error: ", e$message)
success <- FALSE
})
}
# Report any issues
if (length(failed_pkgs) > 0) {
warning("The following packages could not be installed: ",
paste(failed_pkgs, collapse = ", "))
success <- FALSE
}
return(success)
}
# Run the loading function if the script is sourced directly
if (!exists("skip_package_loading") || !skip_package_loading) {
load_smartcane_packages()
}

View file

@ -1,41 +1,102 @@
# filepath: c:\Users\timon\Resilience BV\4020 SCane ESA DEMO - Documenten\General\4020 SCDEMO Team\4020 TechnicalData\WP3\smartcane\r_app\parameters_project.R
#
# PARAMETERS_PROJECT.R
# ====================
# This script defines project parameters, directory structures, and loads field boundaries.
# It establishes all the necessary paths and creates required directories for the SmartCane project.
# 1. Load required libraries
# -------------------------
suppressPackageStartupMessages({
library(here) library(here)
library('readxl') library(readxl)
#chemba library(sf)
library(dplyr)
library(tidyr)
})
# 2. Define project directory structure
# -----------------------------------
setup_project_directories <- function(project_dir) {
# Base directories
laravel_storage_dir <- here("laravel_app/storage/app", project_dir) laravel_storage_dir <- here("laravel_app/storage/app", project_dir)
reports_dir <- here(laravel_storage_dir, "reports")
log_dir <- here(laravel_storage_dir, "logs")
planet_tif_folder <- here(laravel_storage_dir, "merged_tif")
merged_final <- here(laravel_storage_dir, "merged_final_tif")
planet_tif_folder <- here(laravel_storage_dir, "merged_tif")
merged_final <- here(laravel_storage_dir, "merged_final_tif")
data_dir <- here(laravel_storage_dir, "Data")
extracted_CI_dir <- here(data_dir, "extracted_ci")
daily_CI_vals_dir <- here(extracted_CI_dir, "daily_vals")
cumulative_CI_vals_dir <- here(extracted_CI_dir, "cumulative_vals")
weekly_CI_mosaic <- here(laravel_storage_dir, "weekly_mosaic")
daily_vrt <- here(data_dir, "vrt")
harvest_dir <- here(data_dir, "HarvestData")
dir.create(here(laravel_storage_dir), showWarnings = FALSE) # Main subdirectories
dir.create(here(reports_dir), showWarnings = FALSE) dirs <- list(
dir.create(here(data_dir), showWarnings = FALSE) reports = here(laravel_storage_dir, "reports"),
dir.create(here(log_dir), showWarnings = FALSE) logs = here(laravel_storage_dir, "logs"),
dir.create(here(extracted_CI_dir), showWarnings = FALSE) data = here(laravel_storage_dir, "Data"),
dir.create(here(daily_CI_vals_dir), showWarnings = FALSE) tif = list(
dir.create(here(cumulative_CI_vals_dir), showWarnings = FALSE) merged = here(laravel_storage_dir, "merged_tif"),
dir.create(here(weekly_CI_mosaic),showWarnings = FALSE) final = here(laravel_storage_dir, "merged_final_tif")
dir.create(here(daily_vrt), showWarnings = FALSE) ),
dir.create(merged_final,showWarnings = FALSE) weekly_mosaic = here(laravel_storage_dir, "weekly_mosaic"),
dir.create(harvest_dir,showWarnings = FALSE) extracted_ci = list(
base = here(laravel_storage_dir, "Data/extracted_ci"),
daily = here(laravel_storage_dir, "Data/extracted_ci/daily_vals"),
cumulative = here(laravel_storage_dir, "Data/extracted_ci/cumulative_vals")
),
vrt = here(laravel_storage_dir, "Data/vrt"),
harvest = here(laravel_storage_dir, "Data/HarvestData")
)
field_boundaries_sf <- st_read(here(data_dir, "pivot.geojson"), crs = 4326) # Create all directories
for (dir_path in unlist(dirs)) {
dir.create(dir_path, showWarnings = FALSE, recursive = TRUE)
}
# Return directory structure for use in other functions
return(list(
laravel_storage_dir = laravel_storage_dir,
reports_dir = dirs$reports,
log_dir = dirs$logs,
data_dir = dirs$data,
planet_tif_folder = dirs$tif$merged,
merged_final = dirs$tif$final,
daily_CI_vals_dir = dirs$extracted_ci$daily,
cumulative_CI_vals_dir = dirs$extracted_ci$cumulative,
weekly_CI_mosaic = dirs$weekly_mosaic,
daily_vrt = dirs$vrt,
harvest_dir = dirs$harvest,
extracted_CI_dir = dirs$extracted_ci$base
))
}
# 3. Load field boundaries
# ----------------------
load_field_boundaries <- function(data_dir) {
field_boundaries_path <- here(data_dir, "pivot.geojson")
if (!file.exists(field_boundaries_path)) {
stop(paste("Field boundaries file not found at path:", field_boundaries_path))
}
tryCatch({
field_boundaries_sf <- st_read(field_boundaries_path, crs = 4326, quiet = TRUE)
names(field_boundaries_sf) <- c("field", "sub_field", "geometry") names(field_boundaries_sf) <- c("field", "sub_field", "geometry")
field_boundaries <- terra::vect(field_boundaries_sf)
field_boundaries <- field_boundaries_sf %>% terra::vect() return(list(
field_boundaries_sf = field_boundaries_sf,
field_boundaries = field_boundaries
))
}, error = function(e) {
stop(paste("Error loading field boundaries:", e$message))
})
}
harvesting_data <- read_excel(here(data_dir, "harvest.xlsx")) %>% # 4. Load harvesting data
# ---------------------
load_harvesting_data <- function(data_dir) {
harvest_file <- here(data_dir, "harvest.xlsx")
if (!file.exists(harvest_file)) {
warning(paste("Harvest data file not found at path:", harvest_file))
return(NULL)
}
tryCatch({
harvesting_data <- read_excel(harvest_file) %>%
dplyr::select( dplyr::select(
c( c(
"field", "field",
@ -59,24 +120,106 @@ harvesting_data <- read_excel(here(data_dir, "harvest.xlsx")) %>%
tonnage_ha = as.numeric(tonnage_ha) tonnage_ha = as.numeric(tonnage_ha)
) %>% ) %>%
mutate( mutate(
season_end = case_when(season_end > Sys.Date() ~ Sys.Date(), season_end = case_when(
season_end > Sys.Date() ~ Sys.Date(),
is.na(season_end) ~ Sys.Date(), is.na(season_end) ~ Sys.Date(),
TRUE ~ season_end), TRUE ~ season_end
),
age = round(as.numeric(season_end - season_start) / 7, 0) age = round(as.numeric(season_end - season_start) / 7, 0)
) )
return(harvesting_data)
}, error = function(e) {
warning(paste("Error loading harvesting data:", e$message))
return(NULL)
})
}
# 5. Define logging functions globally first
# ---------------------------------------
# Create a simple default log function in case setup_logging hasn't been called yet
log_message <- function(message, level = "INFO") {
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%S")
formatted_message <- paste0("[", level, "] ", timestamp, " - ", message)
cat(formatted_message, "\n")
}
log_head <- function(list, level = "INFO") {
log_message(paste(capture.output(str(head(list))), collapse = "\n"), level)
}
# 6. Set up full logging system with file output
# -------------------------------------------
setup_logging <- function(log_dir) {
log_file <- here(log_dir, paste0(format(Sys.Date(), "%Y%m%d"), ".log")) log_file <- here(log_dir, paste0(format(Sys.Date(), "%Y%m%d"), ".log"))
# Create enhanced log functions
log_message <- function(message, level = "INFO") {
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%S")
formatted_message <- paste0("[", level, "] ", timestamp, " - ", message)
cat(formatted_message, "\n", file = log_file, append = TRUE)
# Also print to console for debugging
if (level %in% c("ERROR", "WARNING")) {
cat(formatted_message, "\n")
# Create a logging function }
log_message <- function(message) {
cat(message, "\n", file = log_file, append = TRUE)
} }
log_head <- function(list, level = "INFO") {
log_head <- function(list) { log_message(paste(capture.output(str(head(list))), collapse = "\n"), level)
log_message(paste(capture.output(str(head(list))), collapse = "\n")) }
# Update the global functions with the enhanced versions
assign("log_message", log_message, envir = .GlobalEnv)
assign("log_head", log_head, envir = .GlobalEnv)
return(list(
log_file = log_file,
log_message = log_message,
log_head = log_head
))
}
# 7. Initialize the project
# ----------------------
# Export project directories and settings
initialize_project <- function(project_dir) {
# Set up directory structure
dirs <- setup_project_directories(project_dir)
# Set up logging
logging <- setup_logging(dirs$log_dir)
# Load field boundaries
boundaries <- load_field_boundaries(dirs$data_dir)
# Load harvesting data
harvesting_data <- load_harvesting_data(dirs$data_dir)
# Return all initialized components
return(c(
dirs,
list(
logging = logging,
field_boundaries = boundaries$field_boundaries,
field_boundaries_sf = boundaries$field_boundaries_sf,
harvesting_data = harvesting_data
)
))
}
# When script is sourced, initialize with the global project_dir variable if it exists
if (exists("project_dir")) {
# Now we can safely log before initialization
log_message(paste("Initializing project with directory:", project_dir))
project_config <- initialize_project(project_dir)
# Expose all variables to the global environment
list2env(project_config, envir = .GlobalEnv)
# Log project initialization completion
log_message(paste("Project initialized with directory:", project_dir))
} else {
warning("project_dir variable not found. Please set project_dir before sourcing parameters_project.R")
} }

View file

@ -1,3 +1,34 @@
# REPORT_UTILS.R
# =============
# Utility functions for generating SmartCane reports with visualizations.
# These functions support the creation of maps, charts and report elements
# for the CI_report_dashboard_planet.Rmd document.
#' Safe logging function that works whether log_message exists or not
#'
#' @param message The message to log
#' @param level The log level (default: "INFO")
#' @return NULL (used for side effects)
#'
safe_log <- function(message, level = "INFO") {
if (exists("log_message")) {
log_message(message, level)
} else {
if (level %in% c("ERROR", "WARNING")) {
warning(message)
} else {
message(message)
}
}
}
#' Creates a sub-chunk for use within RMarkdown documents
#'
#' @param g A ggplot object to render in the sub-chunk
#' @param fig_height Height of the figure in inches
#' @param fig_width Width of the figure in inches
#' @return NULL (writes chunk directly to output)
#'
subchunkify <- function(g, fig_height=7, fig_width=5) { subchunkify <- function(g, fig_height=7, fig_width=5) {
g_deparsed <- paste0(deparse( g_deparsed <- paste0(deparse(
function() {g} function() {g}
@ -14,12 +45,48 @@ subchunkify <- function(g, fig_height=7, fig_width=5) {
cat(knitr::knit(text = knitr::knit_expand(text = sub_chunk), quiet = TRUE)) cat(knitr::knit(text = knitr::knit_expand(text = sub_chunk), quiet = TRUE))
} }
#' Creates a Chlorophyll Index map for a pivot
#'
#' @param pivot_raster The raster data containing CI values
#' @param pivot_shape The shape of the pivot field
#' @param pivot_spans Additional boundary data for the field
#' @param show_legend Whether to show the legend (default: FALSE)
#' @param legend_is_portrait Whether to show the legend in portrait orientation (default: FALSE)
#' @param week Week number to display in the title
#' @param age Age of the crop in weeks
#' @param borders Whether to display field borders (default: FALSE)
#' @return A tmap object with the CI map
#'
create_CI_map <- function(pivot_raster, pivot_shape, pivot_spans, show_legend = F, legend_is_portrait = F, week, age, borders = FALSE){ create_CI_map <- function(pivot_raster, pivot_shape, pivot_spans, show_legend = F, legend_is_portrait = F, week, age, borders = FALSE){
map <- tm_shape(pivot_raster, unit = "m") + # Input validation
tm_raster(breaks = c(0,0.5,1,2,3,4,5,6,7,Inf), palette = "RdYlGn",legend.is.portrait = legend_is_portrait ,midpoint = NA) + if (missing(pivot_raster) || is.null(pivot_raster)) {
tm_layout(main.title = paste0("\nMax CI week ", week,"\n", age, " weeks old"), stop("pivot_raster is required")
main.title.size = 0.7, legend.show = show_legend) }
if (missing(pivot_shape) || is.null(pivot_shape)) {
stop("pivot_shape is required")
}
if (missing(pivot_spans) || is.null(pivot_spans)) {
stop("pivot_spans is required")
}
if (missing(week) || is.null(week)) {
stop("week parameter is required")
}
if (missing(age) || is.null(age)) {
stop("age parameter is required")
}
# Create the base map
map <- tm_shape(pivot_raster, unit = "m") # Add raster with continuous spectrum (fixed scale 1-8 for consistent comparison)
map <- map + tm_raster(col.scale = tm_scale_continuous(values = "brewer.rd_yl_gn",
limits = c(1, 8)),
col.legend = tm_legend(title = "CI",
orientation = if(legend_is_portrait) "portrait" else "landscape",
show = show_legend,
position = c("left", "bottom")))
# Add layout elements
map <- map + tm_layout(main.title = paste0("Max CI week ", week,"\n", age, " weeks old"),
main.title.size = 0.7)
# Add borders if requested
if (borders) { if (borders) {
map <- map + map <- map +
tm_shape(pivot_shape) + tm_shape(pivot_shape) +
@ -32,12 +99,50 @@ create_CI_map <- function(pivot_raster, pivot_shape, pivot_spans, show_legend =
return(map) return(map)
} }
#' Creates a Chlorophyll Index difference map between two weeks
#'
#' @param pivot_raster The raster data containing CI difference values
#' @param pivot_shape The shape of the pivot field
#' @param pivot_spans Additional boundary data for the field
#' @param show_legend Whether to show the legend (default: FALSE)
#' @param legend_is_portrait Whether to show the legend in portrait orientation (default: FALSE)
#' @param week_1 First week number for comparison
#' @param week_2 Second week number for comparison
#' @param age Age of the crop in weeks
#' @param borders Whether to display field borders (default: TRUE)
#' @return A tmap object with the CI difference map
#'
create_CI_diff_map <- function(pivot_raster, pivot_shape, pivot_spans, show_legend = F, legend_is_portrait = F, week_1, week_2, age, borders = TRUE){ create_CI_diff_map <- function(pivot_raster, pivot_shape, pivot_spans, show_legend = F, legend_is_portrait = F, week_1, week_2, age, borders = TRUE){
map <- tm_shape(pivot_raster, unit = "m") + # Input validation
tm_raster(breaks = c(-3,-2,-1,0,1,2,3), palette = "RdYlGn",legend.is.portrait = legend_is_portrait, midpoint = 0, title = "CI difference") + if (missing(pivot_raster) || is.null(pivot_raster)) {
tm_layout(main.title = paste0("CI change week ", week_1, " - week ", week_2, "\n", age, " weeks old"), stop("pivot_raster is required")
main.title.size = 0.7, legend.show = show_legend) }
if (missing(pivot_shape) || is.null(pivot_shape)) {
stop("pivot_shape is required")
}
if (missing(pivot_spans) || is.null(pivot_spans)) {
stop("pivot_spans is required")
}
if (missing(week_1) || is.null(week_1) || missing(week_2) || is.null(week_2)) {
stop("week_1 and week_2 parameters are required")
}
if (missing(age) || is.null(age)) {
stop("age parameter is required")
}
# Create the base map
map <- tm_shape(pivot_raster, unit = "m") # Add raster with continuous spectrum (centered at 0 for difference maps, fixed scale)
map <- map + tm_raster(col.scale = tm_scale_continuous(values = "brewer.rd_yl_gn",
midpoint = 0,
limits = c(-3, 3)),
col.legend = tm_legend(title = "CI difference",
orientation = if(legend_is_portrait) "portrait" else "landscape",
show = show_legend,
position = c("left", "bottom")))
# Add layout elements
map <- map + tm_layout(main.title = paste0("CI change week ", week_1, " - week ", week_2, "\n", age, " weeks old"),
main.title.size = 0.7)
# Add borders if requested
if (borders) { if (borders) {
map <- map + map <- map +
tm_shape(pivot_shape) + tm_shape(pivot_shape) +
@ -50,106 +155,184 @@ create_CI_diff_map <- function(pivot_raster, pivot_shape, pivot_spans, show_lege
return(map) return(map)
} }
ci_plot <- function(pivotName){ #' Creates a visualization of CI data for a specific pivot field
# pivotName = "1.1" #'
pivotShape <- AllPivots0 %>% terra::subset(field %in% pivotName) %>% st_transform(crs(CI)) #' @param pivotName The name or ID of the pivot field to visualize
age <- harvesting_data %>% dplyr::filter(field %in% pivotName) %>% sort("year") %>% tail(., 1) %>% dplyr::select(age) %>% unique() %>% pull() %>% round() #' @param field_boundaries Field boundaries spatial data (sf object)
#' @param current_ci Current week's Chlorophyll Index raster
#' @param ci_minus_1 Previous week's Chlorophyll Index raster
#' @param ci_minus_2 Two weeks ago Chlorophyll Index raster
#' @param last_week_diff Difference raster between current and last week
#' @param three_week_diff Difference raster between current and three weeks ago
#' @param harvesting_data Data frame containing field harvesting/planting information
#' @param week Current week number
#' @param week_minus_1 Previous week number
#' @param week_minus_2 Two weeks ago week number
#' @param week_minus_3 Three weeks ago week number
#' @param borders Whether to display field borders (default: TRUE)
#' @return NULL (adds output directly to R Markdown document)
#'
ci_plot <- function(pivotName,
field_boundaries = AllPivots0,
current_ci = CI,
ci_minus_1 = CI_m1,
ci_minus_2 = CI_m2,
last_week_diff = last_week_dif_raster_abs,
three_week_diff = three_week_dif_raster_abs,
harvesting_data = harvesting_data,
week = week,
week_minus_1 = week_minus_1,
week_minus_2 = week_minus_2,
week_minus_3 = week_minus_3,
borders = TRUE){
# Input validation
if (missing(pivotName) || is.null(pivotName) || pivotName == "") {
stop("pivotName is required")
}
if (missing(field_boundaries) || is.null(field_boundaries)) {
stop("field_boundaries is required")
}
if (missing(current_ci) || is.null(current_ci)) {
stop("current_ci is required")
}
if (missing(ci_minus_1) || is.null(ci_minus_1)) {
stop("ci_minus_1 is required")
}
if (missing(ci_minus_2) || is.null(ci_minus_2)) {
stop("ci_minus_2 is required")
}
if (missing(last_week_diff) || is.null(last_week_diff)) {
stop("last_week_diff is required")
}
if (missing(three_week_diff) || is.null(three_week_diff)) {
stop("three_week_diff is required")
}
if (missing(harvesting_data) || is.null(harvesting_data)) {
stop("harvesting_data is required")
}
AllPivots2 <- AllPivots0 %>% dplyr::filter(field %in% pivotName) # Extract pivot shape and age data
tryCatch({
pivotShape <- field_boundaries %>% terra::subset(field %in% pivotName) %>% sf::st_transform(terra::crs(current_ci))
age <- harvesting_data %>%
dplyr::filter(field %in% pivotName) %>%
sort("year") %>%
tail(., 1) %>%
dplyr::select(age) %>%
unique() %>%
pull() %>%
round()
singlePivot <- CI %>% crop(., pivotShape) %>% mask(., pivotShape) # Filter for the specific pivot
AllPivots2 <- field_boundaries %>% dplyr::filter(field %in% pivotName)
singlePivot_m1 <- CI_m1 %>% crop(., pivotShape) %>% mask(., pivotShape) # Create crop masks for different timepoints using terra functions
singlePivot_m2 <- CI_m2 %>% crop(., pivotShape) %>% mask(., pivotShape) singlePivot <- terra::crop(current_ci, pivotShape) %>% terra::mask(., pivotShape)
# singlePivot_m3 <- CI_m3 %>% crop(., pivotShape) %>% mask(., pivotShape) singlePivot_m1 <- terra::crop(ci_minus_1, pivotShape) %>% terra::mask(., pivotShape)
singlePivot_m2 <- terra::crop(ci_minus_2, pivotShape) %>% terra::mask(., pivotShape)
abs_CI_last_week <- last_week_dif_raster_abs %>% crop(., pivotShape) %>% mask(., pivotShape) # Create difference maps
abs_CI_three_week <- three_week_dif_raster_abs %>% crop(., pivotShape) %>% mask(., pivotShape) abs_CI_last_week <- terra::crop(last_week_diff, pivotShape) %>% terra::mask(., pivotShape)
abs_CI_three_week <- terra::crop(three_week_diff, pivotShape) %>% terra::mask(., pivotShape)
planting_date <- harvesting_data %>% dplyr::filter(field %in% pivotName) %>% ungroup() %>% dplyr::select(season_start) %>% unique() # Get planting date
planting_date <- harvesting_data %>%
dplyr::filter(field %in% pivotName) %>%
ungroup() %>%
dplyr::select(season_start) %>%
unique()
joined_spans2 <- AllPivots0 %>% st_transform(crs(pivotShape)) %>% dplyr::filter(field %in% pivotName) #%>% unique() %>% st_crop(., pivotShape) # Create spans for borders
joined_spans2 <- field_boundaries %>%
sf::st_transform(sf::st_crs(pivotShape)) %>% dplyr::filter(field %in% pivotName)
CImap_m2 <- create_CI_map(singlePivot_m2, AllPivots2, joined_spans2, show_legend= T, legend_is_portrait = T, week = week_minus_2, age = age -2, borders = borders) # Create the maps for different timepoints
CImap_m1 <- create_CI_map(singlePivot_m1, AllPivots2, joined_spans2, show_legend= F, legend_is_portrait = F, week = week_minus_1, age = age -1, borders = borders) CImap_m2 <- create_CI_map(singlePivot_m2, AllPivots2, joined_spans2,
CImap <- create_CI_map(singlePivot, AllPivots2, joined_spans2, show_legend= F, legend_is_portrait = F, week = week, age = age, borders = borders) show_legend = TRUE, legend_is_portrait = TRUE,
week = week_minus_2, age = age - 2, borders = borders)
CImap_m1 <- create_CI_map(singlePivot_m1, AllPivots2, joined_spans2,
show_legend = FALSE, legend_is_portrait = FALSE,
week = week_minus_1, age = age - 1, borders = borders)
CI_max_abs_last_week <- create_CI_diff_map(abs_CI_last_week,AllPivots2, joined_spans2, show_legend = T, legend_is_portrait = T, week_1 = week, week_2 = week_minus_1, age = age, borders = borders) CImap <- create_CI_map(singlePivot, AllPivots2, joined_spans2,
CI_max_abs_three_week <- create_CI_diff_map(abs_CI_three_week, AllPivots2, joined_spans2, show_legend = T, legend_is_portrait = T, week_1 = week, week_2 = week_minus_3, age = age, borders = borders) show_legend = FALSE, legend_is_portrait = FALSE,
week = week, age = age, borders = borders)
# Create difference maps - only show legend on the second one to avoid redundancy
CI_max_abs_last_week <- create_CI_diff_map(abs_CI_last_week, AllPivots2, joined_spans2,
show_legend = FALSE, legend_is_portrait = FALSE,
week_1 = week, week_2 = week_minus_1, age = age, borders = borders)
CI_max_abs_three_week <- create_CI_diff_map(abs_CI_three_week, AllPivots2, joined_spans2,
show_legend = TRUE, legend_is_portrait = TRUE,
week_1 = week, week_2 = week_minus_3, age = age, borders = borders)
# Arrange the maps
tst <- tmap_arrange(CImap_m2, CImap_m1, CImap, CI_max_abs_last_week, CI_max_abs_three_week, nrow = 1) tst <- tmap_arrange(CImap_m2, CImap_m1, CImap, CI_max_abs_last_week, CI_max_abs_three_week, nrow = 1)
# Output heading and map to R Markdown
cat(paste("## Field", pivotName, "-", age, "weeks after planting/harvest", "\n")) cat(paste("## Field", pivotName, "-", age, "weeks after planting/harvest", "\n"))
# cat("\n")
# cat('<h2> Pivot', pivotName, '- week', week, '-', age$Age, 'weeks after planting/harvest <h2>')
# cat(paste("# Pivot",pivots$pivot[i],"\n"))
print(tst) print(tst)
}, error = function(e) {
safe_log(paste("Error creating CI plot for pivot", pivotName, ":", e$message), "ERROR")
cat(paste("## Field", pivotName, "- Error creating visualization", "\n"))
cat(paste("Error:", e$message, "\n"))
})
} }
cum_ci_plot <- function(pivotName){ #' Creates a plot showing Chlorophyll Index data over time for a pivot field
#'
#' @param pivotName The name or ID of the pivot field to visualize
#' @param ci_quadrant_data Data frame containing CI quadrant data with field, sub_field, Date, DOY, cumulative_CI, value and season columns
#' @param plot_type Type of plot to generate: "value", "CI_rate", or "cumulative_CI"
#' @param facet_on Whether to facet the plot by season (TRUE) or overlay all seasons (FALSE)
#' @param x_unit Unit for x-axis: "days" for DOY or "weeks" for week number (default: "days")
#' @return NULL (adds output directly to R Markdown document)
#'
cum_ci_plot <- function(pivotName, ci_quadrant_data = CI_quadrant, plot_type = "value", facet_on = FALSE, x_unit = "days") {
# Input validation
if (missing(pivotName) || is.null(pivotName) || pivotName == "") {
stop("pivotName is required")
}
if (missing(ci_quadrant_data) || is.null(ci_quadrant_data)) {
stop("ci_quadrant_data is required")
}
if (!plot_type %in% c("value", "CI_rate", "cumulative_CI")) {
stop("plot_type must be one of: 'value', 'CI_rate', or 'cumulative_CI'")
}
if (!x_unit %in% c("days", "weeks")) {
stop("x_unit must be either 'days' or 'weeks'")
}
# pivotName = "3a13" # Filter data for the specified pivot
data_ci <- CI_quadrant %>% filter(field == pivotName) tryCatch({
data_ci <- ci_quadrant_data %>% dplyr::filter(field == pivotName)
if (nrow(data_ci) == 0) { if (nrow(data_ci) == 0) {
return(cum_ci_plot2(pivotName)) # Return an empty data frame if no data is found safe_log(paste("No CI data found for pivot", pivotName), "WARNING")
} return(cum_ci_plot2(pivotName)) # Use fallback function when no data is available
data_ci2 <- data_ci %>% mutate(CI_rate = cumulative_CI/DOY,
week = week(Date))%>% group_by(field) %>%
mutate(mean_rolling10 = rollapplyr(CI_rate , width = 10, FUN = mean, partial = TRUE))
date_preperation_perfect_pivot <- data_ci2 %>% group_by(season) %>% summarise(min_date = min(Date),
max_date = max(Date),
days = max_date - min_date)
unique_seasons <- unique(date_preperation_perfect_pivot$season)
g <- ggplot(data= data_ci2 %>% filter(season %in% unique_seasons)) +
facet_wrap(~season, scales = "free_x") +
geom_line( aes(Date, mean_rolling10, col = sub_field, group = sub_field)) +
labs(title = paste("14 day rolling MEAN CI rate - Pivot ", pivotName),
color = "Field name")+
scale_x_date(date_breaks = "1 month", date_labels = "%m-%Y") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 60, hjust = 1),
legend.justification=c(1,0), legend.position = c(1, 0),
legend.title = element_text(size = 8),
legend.text = element_text(size = 8)) +
guides(color = guide_legend(nrow = 2, byrow = TRUE))
subchunkify(g, 3.2, 10)
}
cum_ci_plot <- function(pivotName, plot_type = "value", facet_on = FALSE) {
# pivotName = "3a13"
data_ci <- CI_quadrant %>% filter(field == pivotName)
if (nrow(data_ci) == 0) {
return(cum_ci_plot2(pivotName)) # Return an empty data frame if no data is found
} }
# Process data
data_ci2 <- data_ci %>% data_ci2 <- data_ci %>%
mutate(CI_rate = cumulative_CI / DOY, dplyr::mutate(CI_rate = cumulative_CI / DOY,
week = week(Date)) %>% week = lubridate::week(Date)) %>%
group_by(field) %>% dplyr::group_by(field) %>%
mutate(mean_CIrate_rolling_10_days = rollapplyr(CI_rate, width = 10, FUN = mean, partial = TRUE), dplyr::mutate(mean_CIrate_rolling_10_days = zoo::rollapplyr(CI_rate, width = 10, FUN = mean, partial = TRUE),
mean_rolling_10_days = rollapplyr(value, width = 10, FUN = mean, partial = TRUE)) mean_rolling_10_days = zoo::rollapplyr(value, width = 10, FUN = mean, partial = TRUE))
data_ci2 <- data_ci2 %>% mutate(season = as.factor(season)) data_ci2 <- data_ci2 %>% dplyr::mutate(season = as.factor(season))
date_preperation_perfect_pivot <- data_ci2 %>% # Prepare date information by season
group_by(season) %>% date_preparation_perfect_pivot <- data_ci2 %>%
summarise(min_date = min(Date), dplyr::group_by(season) %>%
dplyr::summarise(min_date = min(Date),
max_date = max(Date), max_date = max(Date),
days = max_date - min_date) days = max_date - min_date)
unique_seasons <- sort(unique(date_preperation_perfect_pivot$season), decreasing = TRUE)[1:3] # Get the 3 most recent seasons
unique_seasons <- sort(unique(date_preparation_perfect_pivot$season), decreasing = TRUE)[1:3]
# Determine the y aesthetic based on the plot type # Determine the y aesthetic based on the plot type
y_aesthetic <- switch(plot_type, y_aesthetic <- switch(plot_type,
@ -162,48 +345,81 @@ cum_ci_plot <- function(pivotName, plot_type = "value", facet_on = FALSE) {
"cumulative_CI" = "Cumulative CI", "cumulative_CI" = "Cumulative CI",
"value" = "10-Day Rolling Mean CI") "value" = "10-Day Rolling Mean CI")
if (facet_on) { # Determine x-axis variable based on x_unit parameter
g <- ggplot(data = data_ci2 %>% filter(season %in% unique_seasons)) + x_var <- if (x_unit == "days") {
facet_wrap(~season, scales = "free_x") + if (facet_on) "Date" else "DOY"
geom_line(aes_string(x = "Date", y = y_aesthetic, col = "sub_field", group = "sub_field")) +
labs(title = paste("Plot of", y_label, "for Pivot", pivotName),
color = "Field Name",
y = y_label) +
scale_x_date(date_breaks = "1 month", date_labels = "%m-%Y") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 60, hjust = 1),
legend.justification = c(1, 0), legend.position = c(1, 0),
legend.title = element_text(size = 8),
legend.text = element_text(size = 8)) +
guides(color = guide_legend(nrow = 2, byrow = TRUE))
} else { } else {
g <- ggplot(data = data_ci2 %>% filter(season %in% unique_seasons)) + "week"
geom_line(aes_string(x = "DOY", y = y_aesthetic, col = "season", group = "season")) + }
labs(title = paste("Plot of", y_label, "for Pivot", pivotName),
x_label <- switch(x_unit,
"days" = if (facet_on) "Date" else "Age of Crop (Days)",
"weeks" = "Week Number")
# Create plot with either facets by season or overlay by DOY/week
if (facet_on) {
g <- ggplot2::ggplot(data = data_ci2 %>% dplyr::filter(season %in% unique_seasons)) +
ggplot2::facet_wrap(~season, scales = "free_x") +
ggplot2::geom_line(ggplot2::aes_string(x = x_var, y = y_aesthetic, col = "sub_field", group = "sub_field")) +
ggplot2::labs(title = paste("Plot of", y_label, "for Pivot", pivotName),
color = "Field Name",
y = y_label,
x = x_label) +
ggplot2::scale_x_date(date_breaks = "1 month", date_labels = "%m-%Y") +
ggplot2::theme_minimal() +
ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 60, hjust = 1),
legend.justification = c(1, 0), legend.position = c(1, 0),
legend.title = ggplot2::element_text(size = 8),
legend.text = ggplot2::element_text(size = 8)) +
ggplot2::guides(color = ggplot2::guide_legend(nrow = 2, byrow = TRUE))
} else {
g <- ggplot2::ggplot(data = data_ci2 %>% dplyr::filter(season %in% unique_seasons)) +
ggplot2::geom_line(ggplot2::aes_string(x = x_var, y = y_aesthetic, col = "season", group = "season")) +
ggplot2::labs(title = paste("Plot of", y_label, "for Pivot", pivotName),
color = "Season", color = "Season",
y = y_label, y = y_label,
x = "Age of Crop (Days)") + x = x_label) +
theme_minimal() + ggplot2::theme_minimal() +
theme(axis.text.x = element_text(angle = 60, hjust = 1), ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 60, hjust = 1),
legend.justification = c(1, 0), legend.position = c(1, 0), legend.justification = c(1, 0), legend.position = c(1, 0),
legend.title = element_text(size = 8), legend.title = ggplot2::element_text(size = 8),
legend.text = element_text(size = 8)) + legend.text = ggplot2::element_text(size = 8)) +
guides(color = guide_legend(nrow = 2, byrow = TRUE)) ggplot2::guides(color = ggplot2::guide_legend(nrow = 2, byrow = TRUE))
} }
subchunkify(g, 3.2, 10) # Output plot to R Markdown with reduced height
subchunkify(g, 3.2, 10) # Reduced from 3.2 to 2.8
}, error = function(e) {
safe_log(paste("Error creating CI trend plot for pivot", pivotName, ":", e$message), "ERROR")
cum_ci_plot2(pivotName) # Use fallback function in case of error
})
} }
#' Fallback function for creating CI visualization when data is missing
#'
#' @param pivotName The name or ID of the pivot field to visualize
#' @return NULL (adds output directly to R Markdown document)
#'
cum_ci_plot2 <- function(pivotName){ cum_ci_plot2 <- function(pivotName){
# Input validation
if (missing(pivotName) || is.null(pivotName) || pivotName == "") {
stop("pivotName is required")
}
# Create a simple plot showing "No data available"
tryCatch({
end_date <- Sys.Date() end_date <- Sys.Date()
start_date <- end_date %m-% months(11) # 11 months ago from end_date start_date <- end_date %m-% months(11) # 11 months ago from end_date
date_seq <- seq.Date(from = start_date, to = end_date, by = "month") date_seq <- seq.Date(from = start_date, to = end_date, by = "month")
midpoint_date <- start_date + (end_date - start_date) / 2 midpoint_date <- start_date + (end_date - start_date) / 2
g <- ggplot() + g <- ggplot() +
scale_x_date(limits = c(start_date, end_date), date_breaks = "1 month", date_labels = "%m-%Y") + scale_x_date(limits = c(start_date, end_date), date_breaks = "1 month", date_labels = "%m-%Y") +
scale_y_continuous(limits = c(0, 4)) + scale_y_continuous(limits = c(0, 4)) +
labs(title = paste("14 day rolling MEAN CI rate - Field ", pivotName), labs(title = paste("14 day rolling MEAN CI rate - Field ", pivotName),
x = "Date", y = "CI Rate") + x = "Date", y = "CI Rate") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 60, hjust = 1), theme(axis.text.x = element_text(angle = 60, hjust = 1),
legend.justification = c(1, 0), legend.position = c(1, 0), legend.justification = c(1, 0), legend.position = c(1, 0),
legend.title = element_text(size = 8), legend.title = element_text(size = 8),
@ -211,25 +427,64 @@ cum_ci_plot2 <- function(pivotName){
annotate("text", x = midpoint_date, y = 2, label = "No data available", size = 6, hjust = 0.5) annotate("text", x = midpoint_date, y = 2, label = "No data available", size = 6, hjust = 0.5)
subchunkify(g, 3.2, 10) subchunkify(g, 3.2, 10)
}, error = function(e) {
safe_log(paste("Error creating fallback CI plot for pivot", pivotName, ":", e$message), "ERROR")
cat(paste("No data available for field", pivotName, "\n"))
})
} }
#' Gets the file path for a specific week's mosaic
#'
#' @param mosaic_path Base directory containing mosaic files
#' @param input_date Reference date to calculate from
#' @param week_offset Number of weeks to offset from input date (positive or negative)
#' @return File path to the requested week's mosaic TIF file
#'
get_week_path <- function(mosaic_path, input_date, week_offset) { get_week_path <- function(mosaic_path, input_date, week_offset) {
# Input validation
if (missing(mosaic_path) || is.null(mosaic_path) || mosaic_path == "") {
stop("mosaic_path is required")
}
if (missing(input_date)) {
stop("input_date is required")
}
tryCatch({
# Convert input_date to Date object (in case it's a string) # Convert input_date to Date object (in case it's a string)
input_date <- as.Date(input_date) input_date <- as.Date(input_date)
if (is.na(input_date)) {
stop("Invalid input_date. Expected a Date object or a string convertible to Date.")
}
# Validate week_offset
week_offset <- as.integer(week_offset)
if (is.na(week_offset)) {
stop("Invalid week_offset. Expected an integer value.")
}
# Get the start of the week for the input date (adjust to Monday as the start of the week) # Get the start of the week for the input date (adjust to Monday as the start of the week)
start_of_week <- floor_date(input_date, unit = "week", week_start = 1) start_of_week <- lubridate::floor_date(input_date, unit = "week", week_start = 1)
# Calculate the new date after applying the week offset # Calculate the new date after applying the week offset
target_date <- start_of_week + weeks(week_offset) target_date <- start_of_week + lubridate::weeks(week_offset)
# Get the week number and year of the target date # Get the week number and year of the target date
target_week <- sprintf("%02d", isoweek(target_date)) # Left-pad week number with a zero if needed target_week <- sprintf("%02d", lubridate::isoweek(target_date)) # Left-pad week number with a zero if needed
target_year <- isoyear(target_date) target_year <- lubridate::isoyear(target_date)
# Generate the file path for the target week # Generate the file path for the target week
path_to_week <- here(mosaic_path, paste0("week_", target_week, "_", target_year, ".tif")) path_to_week <- here::here(mosaic_path, paste0("week_", target_week, "_", target_year, ".tif"))
# Log the path calculation
safe_log(paste("Calculated path for week", target_week, "of year", target_year, ":", path_to_week), "INFO")
# Return the path # Return the path
return(path_to_week) return(path_to_week)
}, error = function(e) {
safe_log(paste("Error calculating week path:", e$message), "ERROR")
stop(e$message)
})
} }

View file

@ -0,0 +1,409 @@
<!-- filepath: c:\Users\timon\Resilience BV\4020 SCane ESA DEMO - Documenten\General\4020 SCDEMO Team\4020 TechnicalData\WP3\smartcane\r_app\system_architecture.md -->
# SmartCane System Architecture
## Overview
The SmartCane system is a comprehensive agricultural intelligence platform that processes satellite imagery and farm data to provide agronomic insights for sugarcane farmers. The system architecture follows a modular, layered approach with clear separation of concerns between data acquisition, processing, and presentation.
## Architectural Layers
The SmartCane system follows a layered architecture pattern, which is a standard approach in software engineering for organizing complex systems. This architecture divides the system into distinct functional layers, each with specific responsibilities. While these layers aren't explicitly shown as separate visual elements in the diagrams, they help conceptualize how components are organized by their function:
### 1. Data Acquisition Layer
- **Role**: Responsible for fetching raw data from external sources and user inputs
- **Components**: Manual Sentinel Hub Requests, Python API Downloader, User Input Interface
- **Functions**: Manual request setup on Sentinel Hub Requests Builder for specific client fields, connects to satellite data providers, downloads imagery, manages API credentials, performs preliminary data validation
### 2. Processing Layer (SmartCane Engine)
- **Role**: Core analytical engine that transforms raw data into actionable insights
- **Components**: Python API Downloader (pre-processing), R Processing Engine (analytics)
- **Functions**: Image processing, cloud masking, crop index calculation, field boundary processing, statistical analysis, report generation
### 3. Presentation Layer
- **Role**: Delivers insights to end users in accessible formats
- **Components**: Laravel Web App, Email Delivery System
- **Functions**: Interactive dashboards, visualization, report delivery, user management, project scheduling
### 4. Data Storage Layer
- **Role**: Persists system data across processing cycles
- **Components**: File System, Database
- **Functions**: Stores raw imagery, processed rasters, analytical results, user data, configuration
## Key Subsystems
### 1. Python API Downloader
- **Role**: Acquires and pre-processes satellite imagery
- **Inputs**: API credentials, field boundaries, date parameters, evaluation scripts
- **Outputs**: Raw satellite images, merged GeoTIFFs, virtual rasters
- **Interfaces**: External satellite APIs (Planet via Sentinel Hub), file system
- **Orchestration**: Triggered by shell scripts from the Laravel application
### 2. R Processing Engine
- **Role**: Performs advanced analytics and generates insights
- **Inputs**: Processed satellite imagery, field boundaries, harvest data, project parameters
- **Outputs**: Crop indices, mosaics, RDS data files, agronomic reports
- **Interfaces**: File system, report templates
- **Orchestration**: Triggered by shell scripts from the Laravel application
### 3. Laravel Web Application
- **Role**: Provides operator interface and orchestrates the overall system
- **Inputs**: User data, configuration settings
- **Outputs**: Web interface, scheduling, report delivery
- **Interfaces**: Users, database, file system
- **Orchestration**: Controls execution of the SmartCane Engine via shell scripts
### 4. Shell Script Orchestration
- **Role**: Bridges between web application and processing components
- **Functions**: Triggers processing workflows, manages execution environment, handles errors
- **Examples**: runcane.sh, runpython.sh, build_mosaic.sh, build_report.sh
## Data Flow
1. **Input Stage**:
- Operators (internal team) manually prepare and submit requests on Sentinel Hub Requests Builder for the specific fields of a client.
- Operators (internal team) provide farm data (field boundaries, harvest data) via the Laravel Web App.
- System schedules data acquisition for specific dates/regions
2. **Acquisition Stage**:
- Laravel triggers Python API Downloader via shell scripts
- Python connects to satellite data providers and downloads raw imagery
- Downloaded data is stored in the file system
3. **Processing Stage**:
- Laravel triggers R Processing Engine via shell scripts
- R scripts read satellite imagery and farm data
- Processing produces crop indices, analytics, and reports
- Results are stored in the file system
4. **Output Stage**:
- Laravel Web App accesses processed results
- Reports are delivered to users via email
## System Integration Points
- **Python-R Integration**: Data handover via file system (GeoTIFF, virtual rasters)
- **Engine-Laravel Integration**: Orchestration via shell scripts, data exchange via file system and database
- **User-System Integration**: Web interface, file uploads, email notifications
## Developed/Customized Elements
- **Custom Cloud Masking Algorithm**: Specialized for agricultural applications in tropical regions
- **Crop Index Extraction Pipeline**: Tailored to sugarcane spectral characteristics
- **Reporting Templates**: Designed for agronomic decision support
- **Shell Script Orchestration**: Custom workflow management for the system's components
## Strategic Role of Satellite Data
Satellite data is central to the SmartCane system, providing:
- Regular, non-invasive field monitoring
- Detection of spatial patterns not visible from ground level
- Historical analysis of crop performance
- Early warning of crop stress or disease
- Quantification of field variability for precision agriculture
## Pilot Utilization Sites
The SmartCane system is currently operational in Mozambique, Kenya, and Tanzania. Future pilot deployments and expansions are planned for Uganda, Colombia, Mexico, Guatemala, South Africa, and Zambia.
---
## System Architecture Diagrams
Below are diagrams illustrating the system architecture from different perspectives.
### Overall System Architecture
This diagram provides a high-level overview of the complete SmartCane system, showing how major components interact. It focuses on the system boundaries and main data flows between the Python API Downloader, R Processing Engine, Laravel Web App, and data storage components. This view helps understand how the system works as a whole.
```mermaid
graph TD
A["fa:fa-satellite External Satellite Data Providers API"] --> PyDL["fa:fa-download Python API Downloader"];
C["fa:fa-users Users: Farm Data Input e.g., GeoJSON, Excel"] --> D{"fa:fa-laptop-code Laravel Web App"};
subgraph SmartCane System
PyDL --> G["fa:fa-folder-open File System: Raw Satellite Imagery, Rasters, RDS, Reports, Boundaries"];
E["fa:fa-cogs R Processing Engine"] -- Reads --> G;
E -- Writes --> G;
D -- Manages/Triggers --> F["fa:fa-terminal Shell Script Orchestration"];
F -- Executes --> PyDL;
F -- Executes --> E;
D -- Manages/Accesses --> G;
D -- Reads/Writes --> H["fa:fa-database Database: Project Metadata, Users, Schedules"];
E -- Generates --> I["fa:fa-file-alt Agronomic Reports: DOCX, HTML"];
D -- Accesses/Delivers --> I;
end
D --> J["fa:fa-desktop Users: Web Interface (future)"];
I -- Via Email (SMTP) --> K["fa:fa-envelope Users: Email Reports"];
style E fill:#f9f,stroke:#333,stroke-width:2px
style D fill:#bbf,stroke:#333,stroke-width:2px
style PyDL fill:#ffdd57,stroke:#333,stroke-width:2px
```
### R Processing Engine Detail
This diagram zooms in on the R Processing Engine subsystem, detailing the internal components and data flow. It shows how raw satellite imagery and field data progress through various R scripts to produce crop indices and reports. The diagram highlights the data transformation pipeline within this analytical core of the SmartCane system.
```mermaid
graph TD
subgraph R Processing Engine
direction TB
subgraph Inputs
SatelliteImages["fa:fa-image Raw Satellite Imagery"]
FieldBoundaries["fa:fa-map-marker-alt Field Boundaries .geojson"]
HarvestData["fa:fa-file-excel Harvest Data .xlsx"]
ProjectParams["fa:fa-file-code Project Parameters .R"]
end
subgraph Core R Scripts & Processes
ParamConfig("fa:fa-cogs parameters_project.R")
MosaicScript("fa:fa-images mosaic_creation.R")
CIExtractionScript("fa:fa-microscope ci_extraction.R")
ReportUtils("fa:fa-tools executive_report_utils.R")
DashboardRmd("fa:fa-tachometer-alt CI_report_dashboard_planet_enhanced.Rmd")
SummaryRmd("fa:fa-list-alt CI_report_executive_summary.Rmd")
end
subgraph Outputs
WeeklyMosaics["fa:fa-file-image Weekly Mosaics .tif"]
CIDataRDS["fa:fa-database CI Data .rds"]
CIRasters["fa:fa-layer-group CI Rasters .tif"]
DashboardReport["fa:fa-chart-bar Dashboard Report .docx/.html"]
SummaryReport["fa:fa-file-invoice Executive Summary .docx/.html"]
end
%% Data Flow
ProjectParams --> ParamConfig;
SatelliteImages --> MosaicScript;
FieldBoundaries --> MosaicScript;
ParamConfig --> MosaicScript;
MosaicScript --> WeeklyMosaics;
WeeklyMosaics --> CIExtractionScript;
FieldBoundaries --> CIExtractionScript;
ParamConfig --> CIExtractionScript;
CIExtractionScript --> CIDataRDS;
CIExtractionScript --> CIRasters;
CIDataRDS --> ReportUtils;
CIRasters --> ReportUtils;
HarvestData --> ReportUtils;
ParamConfig --> ReportUtils;
ReportUtils --> DashboardRmd;
ReportUtils --> SummaryRmd;
ParamConfig --> DashboardRmd;
ParamConfig --> SummaryRmd;
DashboardRmd --> DashboardReport;
SummaryRmd --> SummaryReport;
end
ShellOrchestration["fa:fa-terminal Shell Scripts e.g., build_mosaic.sh, build_report.sh"] -->|Triggers| R_Processing_Engine["fa:fa-cogs R Processing Engine"]
style R_Processing_Engine fill:#f9f,stroke:#333,stroke-width:2px
style Inputs fill:#ccf,stroke:#333,stroke-width:1px
style Outputs fill:#cfc,stroke:#333,stroke-width:1px
style Core_R_Scripts_Processes fill:#ffc,stroke:#333,stroke-width:1px
```
### Python API Downloader Detail
This diagram focuses on the Python API Downloader subsystem, showing its internal components and workflow. It illustrates how API credentials, field boundaries, and other inputs are processed through various Python functions to download, process, and prepare satellite imagery. This view reveals the technical implementation details of the data acquisition layer.
```mermaid
graph TD
subgraph Python API Downloader
direction TB
subgraph Inputs_Py [Inputs]
APICreds["fa:fa-key API Credentials (SH_CLIENT_ID, SH_CLIENT_SECRET)"]
DateRangeParams["fa:fa-calendar-alt Date Range Parameters (days_needed, specific_date)"]
GeoJSONInput["fa:fa-map-marker-alt Field Boundaries (pivot.geojson)"]
ProjectConfig["fa:fa-cogs Project Configuration (project_name, paths)"]
EvalScripts["fa:fa-file-code Evalscripts (JS for cloud masking & band selection)"]
end
subgraph Core_Python_Logic_Py [Core Python Logic & Libraries]
SetupConfig["fa:fa-cog SentinelHubConfig & BYOC Definition"]
DateSlotGen["fa:fa-calendar-check Date Slot Generation (slots)"]
GeoProcessing["fa:fa-map GeoJSON Parsing & BBox Splitting (geopandas, BBoxSplitter)"]
AvailabilityCheck["fa:fa-search-location Image Availability Check (SentinelHubCatalog)"]
RequestHandler["fa:fa-paper-plane Request Generation (SentinelHubRequest, get_true_color_request_day)"]
DownloadClient["fa:fa-cloud-download-alt Image Download (SentinelHubDownloadClient, download_function)"]
MergeUtility["fa:fa-object-group Tile Merging (gdal.BuildVRT, gdal.Translate, merge_files)"]
CleanupUtility["fa:fa-trash-alt Intermediate File Cleanup (empty_folders)"]
end
subgraph Outputs_Py [Outputs]
RawSatImages["fa:fa-file-image Raw Downloaded Satellite Imagery Tiles (response.tiff in dated subfolders)"]
MergedTifs["fa:fa-images Merged TIFs (merged_tif/{slot}.tif)"]
VirtualRasters["fa:fa-layer-group Virtual Rasters (merged_virtual/merged{slot}.vrt)"]
DownloadLogs["fa:fa-file-alt Console Output Logs (print statements)"]
end
ExternalSatAPI["fa:fa-satellite External Satellite Data Providers API (Planet via Sentinel Hub)"]
%% Data Flow for Python Downloader
APICreds --> SetupConfig;
DateRangeParams --> DateSlotGen;
GeoJSONInput --> GeoProcessing;
ProjectConfig --> SetupConfig;
ProjectConfig --> GeoProcessing;
ProjectConfig --> MergeUtility;
ProjectConfig --> CleanupUtility;
EvalScripts --> RequestHandler;
DateSlotGen -- Available Slots --> AvailabilityCheck;
GeoProcessing -- BBox List --> AvailabilityCheck;
SetupConfig --> AvailabilityCheck;
AvailabilityCheck -- Filtered Slots & BBoxes --> RequestHandler;
RequestHandler -- Download Requests --> DownloadClient;
SetupConfig --> DownloadClient;
DownloadClient -- Downloads Data From --> ExternalSatAPI;
ExternalSatAPI -- Returns Image Data --> DownloadClient;
DownloadClient -- Writes --> RawSatImages;
DownloadClient -- Generates --> DownloadLogs;
RawSatImages --> MergeUtility;
MergeUtility -- Writes --> MergedTifs;
MergeUtility -- Writes --> VirtualRasters;
end
ShellOrchestratorPy["fa:fa-terminal Shell Scripts (e.g., runpython.sh triggering planet_download.ipynb)"] -->|Triggers| Python_API_Downloader["fa:fa-download Python API Downloader"];
style Python_API_Downloader fill:#ffdd57,stroke:#333,stroke-width:2px
style Inputs_Py fill:#cdeeff,stroke:#333,stroke-width:1px
style Outputs_Py fill:#d4efdf,stroke:#333,stroke-width:1px
style Core_Python_Logic_Py fill:#fff5cc,stroke:#333,stroke-width:1px
style ExternalSatAPI fill:#f5b7b1,stroke:#333,stroke-width:2px
```
### SmartCane Engine Integration Diagram
This diagram illustrates the integration of Python and R components within the SmartCane Engine. Unlike the first diagram that shows the overall system, this one specifically focuses on how the two processing components interact with each other and the rest of the system. It emphasizes the orchestration layer and data flows between the core processing components and external systems.
```mermaid
graph TD
%% External Systems & Users
Users_DataInput["fa:fa-user Users: Farm Data Input (GeoJSON, Excel, etc.)"] --> Laravel_WebApp;
ExternalSatAPI["fa:fa-satellite External Satellite Data Providers API"];
%% Main Application Components
Laravel_WebApp["fa:fa-globe Laravel Web App (Frontend & Control Plane)"];
Shell_Orchestration["fa:fa-terminal Shell Script Orchestration (e.g., runcane.sh, runpython.sh, build_mosaic.sh)"]; subgraph SmartCane_Engine ["SmartCane Engine (Data Processing Core)"]
direction TB
Python_Downloader["fa:fa-download Python API Downloader"];
R_Engine["fa:fa-chart-line R Processing Engine"];
end
%% Data Storage
FileSystem["fa:fa-folder File System (Raw Imagery, Rasters, RDS, Reports, Boundaries)"];
Database["fa:fa-database Database (Project Metadata, Users, Schedules)"];
%% User Outputs
Users_WebView["fa:fa-desktop Users: Web Interface (future)"];
Users_EmailReports["fa:fa-envelope Users: Email Reports (Agronomic Reports)"];
AgronomicReports["fa:fa-file-alt Agronomic Reports (DOCX, HTML)"];
%% --- Data Flows & Interactions ---
%% Laravel to Orchestration & Engine
Laravel_WebApp -- Manages/Triggers --> Shell_Orchestration;
Shell_Orchestration -- Executes --> Python_Downloader;
Shell_Orchestration -- Executes --> R_Engine;
%% Python Downloader within Engine
ExternalSatAPI -- Satellite Data --> Python_Downloader;
Python_Downloader -- Writes Raw Data --> FileSystem;
%% Inputs to Python (simplified for this view - details in Python-specific diagram)
%% Laravel_WebApp -- Provides Config/Boundaries --> Python_Downloader;
%% R Engine within Engine
%% Inputs to R (simplified - details in R-specific diagram)
%% Laravel_WebApp -- Provides Config/Boundaries --> R_Engine;
R_Engine -- Reads Processed Data/Imagery --> FileSystem;
R_Engine -- Writes Derived Products --> FileSystem;
R_Engine -- Generates --> AgronomicReports;
%% Laravel interaction with Data Storage
Laravel_WebApp -- Manages/Accesses --> FileSystem;
Laravel_WebApp -- Reads/Writes --> Database;
%% Output Delivery
Laravel_WebApp --> Users_WebView;
AgronomicReports --> Users_EmailReports;
%% Assuming a mechanism like SMTP, potentially triggered by Laravel or R-Engine completion
Laravel_WebApp -- Delivers/Displays --> AgronomicReports;
%% Styling
style SmartCane_Engine fill:#e6ffe6,stroke:#333,stroke-width:2px
style Python_Downloader fill:#ffdd57,stroke:#333,stroke-width:2px
style R_Engine fill:#f9f,stroke:#333,stroke-width:2px
style Laravel_WebApp fill:#bbf,stroke:#333,stroke-width:2px
style Shell_Orchestration fill:#f0ad4e,stroke:#333,stroke-width:2px
style FileSystem fill:#d1e0e0,stroke:#333,stroke-width:1px
style Database fill:#d1e0e0,stroke:#333,stroke-width:1px
style ExternalSatAPI fill:#f5b7b1,stroke:#333,stroke-width:2px
style AgronomicReports fill:#d4efdf,stroke:#333,stroke-width:1px
```
## Future Directions
The SmartCane platform is poised for significant evolution, with several key enhancements and new capabilities planned to further empower users and expand its utility:
- **Advanced Management Dashboard**: Development of a more comprehensive and interactive management dashboard to provide users with deeper insights and greater control over their operations.
- **Enhanced Yield Prediction Models**: Improving the accuracy and granularity of yield predictions by incorporating more variables and advanced machine learning techniques.
- **Integrated Weather and Irrigation Advice**: Leveraging weather forecast data and soil moisture information (potentially from new data sources) to provide precise irrigation scheduling and weather-related agronomic advice.
- **AI-Guided Agronomic Advice**: Implementing sophisticated AI algorithms to analyze integrated data (satellite, weather, soil, farm practices) and offer tailored, actionable agronomic recommendations.
- **Automated Advice Generation**: Developing capabilities for the system to automatically generate and disseminate critical advice and alerts to users based on real-time data analysis.
- **Expanded Data Source Integration**:
- **Radar Data**: Incorporating radar satellite imagery (e.g., Sentinel-1) for all-weather monitoring capabilities, particularly useful during cloudy seasons for assessing crop structure, soil moisture, and biomass.
- **IoT and Ground Sensors**: Integrating data from in-field IoT devices and soil sensors for highly localized and continuous monitoring of environmental and soil conditions.
- **Client-Facing Portal**: Exploration and potential development of a client-facing portal to allow end-users direct access to their data, dashboards, and reports, complementing the current internal management interface.
These future developments aim to transform SmartCane into an even more powerful decision support system, fostering sustainable and efficient agricultural practices.
## Conclusion and Integration Summary
The SmartCane system architecture demonstrates a well-integrated solution that combines different technologies and subsystems to solve complex agricultural challenges. Here is a summary of how the key subsystems work together:
### Subsystem Integration
1. **Data Flow Sequence**
- The Laravel Web App initiates the workflow and manages user interactions
- Shell scripts orchestrate the execution sequence of the processing subsystems
- The Python API Downloader acquires raw data from external sources
- The R Processing Engine transforms this data into actionable insights
- Results flow back to users through the web interface and email reports
2. **Technology Integration**
- **Python + R**: Different programming languages are leveraged for their respective strengths—Python for API communication and data acquisition, R for statistical analysis and report generation
- **Laravel + Processing Engine**: Clear separation between web presentation layer and computational backend
- **File System + Database**: Hybrid data storage approach with file system for imagery and reports, database for metadata and user information
3. **Key Integration Mechanisms**
- **File System Bridge**: The different subsystems primarily communicate through standardized file formats (GeoTIFF, GeoJSON, RDS, DOCX)
- **Shell Script Orchestration**: Acts as the "glue" between subsystems, ensuring proper execution sequence and environment setup
- **Standardized Data Formats**: Use of widely-accepted geospatial and data formats enables interoperability
4. **Extensibility and Scalability**
- The modular architecture allows for replacement or enhancement of individual components
- The clear subsystem boundaries enable parallel development and testing
- Standard interfaces simplify integration of new data sources, algorithms, or output methods
The SmartCane architecture balances complexity with maintainability by using well-established technologies and clear boundaries between subsystems. The separation of concerns between data acquisition, processing, and presentation layers ensures that changes in one area minimally impact others, while the consistent data flow pathways ensure that information moves smoothly through the system.