From 8abefb38cc944e1b53e197c3428448dc115e4cb0 Mon Sep 17 00:00:00 2001
From: Ivan Ruiz Manuel <72193617+irm-codebase@users.noreply.github.com>
Date: Sat, 6 Jun 2026 10:14:51 +0200
Subject: [PATCH 1/6] Add scenario processing
---
INTERFACE.yaml | 6 +-
README.md | 13 +-
config/china_example.yaml | 9 -
config/config.yaml | 237 +++++++++++++++++---
config/europe_example.yaml | 157 -------------
config/usa_example.yaml | 13 --
tests/conftest.py | 11 +
tests/integration/Snakefile | 4 +-
tests/integration/test_config.yaml | 28 +--
tests/integration_test.py | 6 -
tests/local_test.py | 21 +-
workflow/Snakefile | 6 +-
workflow/internal/config.schema.yaml | 205 +++++++++--------
workflow/report/build_combined_area.rst | 2 +
workflow/report/build_country.rst | 2 +-
workflow/report/download_harmonised_eez.rst | 2 +-
workflow/rules/_utils.smk | 50 ++++-
workflow/rules/build.smk | 66 ++++--
workflow/rules/harmonise.smk | 12 +-
workflow/scripts/_utils.py | 1 +
workflow/scripts/build_country.py | 93 ++++++--
workflow/scripts/build_eez.py | 48 ++++
workflow/scripts/download_harmonised_eez.py | 64 +++---
workflow/scripts/harmonise_gadm.py | 4 +-
24 files changed, 635 insertions(+), 425 deletions(-)
delete mode 100644 config/china_example.yaml
delete mode 100644 config/europe_example.yaml
delete mode 100644 config/usa_example.yaml
create mode 100644 tests/conftest.py
create mode 100644 workflow/scripts/build_eez.py
diff --git a/INTERFACE.yaml b/INTERFACE.yaml
index ee35e7c..b4b9ace 100644
--- a/INTERFACE.yaml
+++ b/INTERFACE.yaml
@@ -12,6 +12,8 @@ pathvars:
description: "location of module results."
results:
shapes:
- default: "/shapes.parquet"
+ default: "/{scenario}/shapes.parquet"
description: |
- Harmonised geoparquet dataset with all the configured countries, including non-contested exclusive economic zones.
+ Harmonised geoparquet dataset for a configured scenario, including land and maritime non-contested EEZs.
+wildcards:
+ scenario: Scenario name from the module configuration.
diff --git a/README.md b/README.md
index 96c2c40..787b5f3 100644
--- a/README.md
+++ b/README.md
@@ -26,18 +26,19 @@ Data processing steps:
-1. The configuration file is read to identify the datasets to use as well as the specific countries and regional aggregation (`subtype` in the configuration) to process.
+1. For each requested country combination (`scenarios`), the configuration file is read to identify the datasets (`source`) to use as well as the specific countries and subnational aggregation (`subtype`) to process.
- Country landmass data: [eurostat NUTS](https://ec.europa.eu/eurostat/web/gisco/geodata/statistical-units/territorial-units-statistics), [GADM](https://gadm.org/), [geoBoundaries](https://www.geoboundaries.org/), and [Overture Maps](https://overturemaps.org/) are supported.
- Exclusive Economic Zone (EEZ) data: [MarineRegions.org](https://www.marineregions.org/).
2. Individual country files are downloaded and harmonised to fit a standardised schema.
- If identified, contested regions are removed at this stage.
- Land is clipped using maritime Exclusive Economic Zones (EEZ).
- Optionally, a Voronoi algorithm is run to separate EEZ areas to fit subnational regions.
-3. Each country file is combined and then clipped using its neighbours to minimise overlapping polygons.
+3. The country files requested in the scenario are combined and then clipped using their neighbours to minimise overlapping polygons.
-> [!TIP]
-> The `subtype` naming matches that of the source database. For example, NUTS uses 0, 1, 2 and 3 (NUTS0, NUTS1, NUTS2, etc.).
-> Use the references at the bottom of this page for more details.
+> [!TIP] Keep in mind the following
+> - The `subtype` naming matches that of the source database. For example, NUTS uses 0, 1, 2 and 3 (NUTS0, NUTS1, NUTS2, etc.).
+>Use the references at the bottom of this page for more details.
+> - The downloaded data is always kept locally for future re-use.
> [!CAUTION]
> To increase the replicability of your workflow, we recommend using NUTS and geoBoundaries as sources whenever possible as they have more stable hosting methods than Overture Maps and GADM.
@@ -50,7 +51,7 @@ Please consult the configuration [README](./config/README.md) and the [configura
## Input / output structure
-This module only has one output: a geoparquet file with your requested geo-boundary "shapes".
+This module only has one output: a geoparquet file with your requested geo-boundary "shapes" for each of the the configured `scenarios`.
Please consult the [interface file](./INTERFACE.yaml) for more information.
diff --git a/config/china_example.yaml b/config/china_example.yaml
deleted file mode 100644
index fd19204..0000000
--- a/config/china_example.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-# Example of regional disaggregation of a large country
-# China has contested borders, which will be removed by the module
-crs:
- projected: "epsg:3857"
- geographic: "epsg:4326"
-countries:
- "CHN":
- subtype: "country"
- source: "overture"
diff --git a/config/config.yaml b/config/config.yaml
index 31116a0..ba7ae3e 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -1,33 +1,218 @@
# A minimal example of how to configure this module
+
+# Default global settings across scenarios
voronoi_eez:
enabled: True
sample_spacing: 10000 # sample every 10 km
crs:
projected: "epsg:3857"
geographic: "epsg:4326"
-countries:
- DNK:
- subtype: "1"
- source: "nuts"
- resolution: 01M
- year: 2024
- GBR:
- subtype: "country"
- source: "overture"
- NLD:
- subtype: 0
- source: "geoboundaries"
- release_type: "gbOpen"
- BEL:
- subtype: 1
- source: "geoboundaries"
- release_type: "gbOpen"
- CHE:
- subtype: "country"
- source: "overture"
- ESP:
- subtype: "2"
- source: "nuts"
- resolution: 01M
- year: 2024
- extra_eez: 8364
+
+# At least one scenario.
+# We include four cases as examples.
+scenarios:
+ mixed_example:
+ # Assorted countries
+ countries:
+ DNK:
+ subtype: "1"
+ source: "nuts"
+ resolution: 01M
+ year: 2024
+ GBR:
+ subtype: "country"
+ source: "overture"
+ NLD:
+ subtype: 0
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ BEL:
+ subtype: 1
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ CHE:
+ subtype: "country"
+ source: "overture"
+ ESP:
+ subtype: "2"
+ source: "nuts"
+ resolution: 01M
+ year: 2024
+ extra_eez: 8364
+ china_national:
+ # Large country at national level
+ # China has contested borders, which will be removed by the module
+ voronoi_eez:
+ enabled: False
+ sample_spacing: 10000
+ countries:
+ "CHN":
+ subtype: "country"
+ source: "overture"
+ USA_states:
+ # Example of regional disaggregation of a large country crossing the antimeridean.
+ # USA has multiple marine zones, which can be appended as extras
+ crs:
+ projected: "EPSG:10598"
+ geographic: "epsg:4326"
+ countries:
+ "USA":
+ subtype: "1"
+ source: "gadm"
+ extra_eez: [8463, 8453]
+ europe_regions:
+ # A large continental example.
+ # Europe at regional resolution.
+ crs:
+ projected: "epsg:3035"
+ geographic: "epsg:4326"
+ countries:
+ "ALB":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "AUT":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "BEL":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "BGR":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "BIH":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "CHE":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "CYP":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "CZE":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "DEU":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "DNK":
+ subtype: "1"
+ source: "nuts"
+ resolution: 01M
+ year: 2024
+ "ESP":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ extra_eez: 8364
+ "EST":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "FIN":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "FRA":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "GBR":
+ subtype: "region"
+ source: "overture"
+ "GGY":
+ subtype: "0"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "GRC":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "HRV":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "HUN":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "IMN":
+ subtype: "0"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "IRL":
+ subtype: "region"
+ source: "overture"
+ "ITA":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "LTU":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "LUX":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "LVA":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "MKD":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "MNE":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "NLD":
+ subtype: "region"
+ source: "overture"
+ "NOR":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "POL":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "PRT":
+ subtype: "1"
+ source: "nuts"
+ resolution: 01M
+ year: 2024
+ extra_eez: [8361, 8363]
+ "ROU":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "SRB":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "SVK":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "SVN":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "SWE":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "XKX":
+ subtype: "1"
+ source: "geoboundaries"
+ release_type: "gbOpen"
diff --git a/config/europe_example.yaml b/config/europe_example.yaml
deleted file mode 100644
index bfa1eb5..0000000
--- a/config/europe_example.yaml
+++ /dev/null
@@ -1,157 +0,0 @@
-# A minimal example of how to configure this module
-voronoi_eez:
- enabled: True
- sample_spacing: 10000
-crs:
- projected: "epsg:3035"
- geographic: "epsg:4326"
-countries:
- "ALB":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "AUT":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "BEL":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "BGR":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "BIH":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "CHE":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "CYP":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "CZE":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "DEU":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "DNK":
- subtype: "1"
- source: "nuts"
- resolution: 01M
- year: 2024
- "ESP":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- extra_eez: 8364
- "EST":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "FIN":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "FRA":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "GBR":
- subtype: "region"
- source: "overture"
- "GGY":
- subtype: "0"
- source: "geoboundaries"
- release_type: "gbOpen"
- "GRC":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "HRV":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "HUN":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "IMN":
- subtype: "0"
- source: "geoboundaries"
- release_type: "gbOpen"
- "IRL":
- subtype: "region"
- source: "overture"
- "ITA":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "LTU":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "LUX":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "LVA":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "MKD":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "MNE":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "NLD":
- subtype: "region"
- source: "overture"
- "NOR":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "POL":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "PRT":
- subtype: "1"
- source: "nuts"
- resolution: 01M
- year: 2024
- extra_eez: [8361, 8363]
- "ROU":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "SRB":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "SVK":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "SVN":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "SWE":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
- "XKX":
- subtype: "1"
- source: "geoboundaries"
- release_type: "gbOpen"
diff --git a/config/usa_example.yaml b/config/usa_example.yaml
deleted file mode 100644
index 2d7fe8d..0000000
--- a/config/usa_example.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-# Example of regional disaggregation of a large country
-# USA has multiple marine zones, which can be appended as extras
-voronoi_eez:
- enabled: True
- sample_spacing: 10000 # sample every 10 km
-crs:
- projected: "epsg:3857"
- geographic: "epsg:4326"
-countries:
- "USA":
- subtype: "1"
- source: "gadm"
- extra_eez: [8463, 8453]
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..fef1d4d
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,11 @@
+"""Shared pytest fixtures."""
+
+from pathlib import Path
+
+import pytest
+
+
+@pytest.fixture(scope="module")
+def module_path():
+ """Parent directory of the project."""
+ return Path(__file__).parent.parent
diff --git a/tests/integration/Snakefile b/tests/integration/Snakefile
index ad71d73..564b3e4 100644
--- a/tests/integration/Snakefile
+++ b/tests/integration/Snakefile
@@ -9,7 +9,7 @@ configfile: workflow.source_path("./test_config.yaml")
module module_geo_boundaries:
pathvars:
# Redirect specific module results (outputs)
- shapes="results/outputs/shapes.parquet",
+ shapes="results/outputs/{scenario}/shapes.parquet",
# Redirect module intermediate files
logs="resources/module/logs",
resources="resources/module/resources",
@@ -28,6 +28,6 @@ use rule * from module_geo_boundaries as geo_boundaries_module_*
rule all:
default_target: True
input:
- "results/outputs/shapes.parquet",
+ "results/outputs/integration/shapes.parquet",
message:
"A generic test case for this module."
diff --git a/tests/integration/test_config.yaml b/tests/integration/test_config.yaml
index ea3ad94..25d8bd0 100644
--- a/tests/integration/test_config.yaml
+++ b/tests/integration/test_config.yaml
@@ -2,16 +2,18 @@ module_geo_boundaries:
crs:
projected: "epsg:3857"
geographic: "epsg:4326"
- countries:
- "MNE":
- subtype: "0"
- source: "geoboundaries"
- release_type: "gbOpen"
- "SVK":
- subtype: "country"
- source: "overture"
- "MKD":
- subtype: "0"
- source: "nuts"
- resolution: 03M
- year: 2024
+ scenarios:
+ integration:
+ countries:
+ "MNE":
+ subtype: "0"
+ source: "geoboundaries"
+ release_type: "gbOpen"
+ "SVK":
+ subtype: "country"
+ source: "overture"
+ "MKD":
+ subtype: "0"
+ source: "nuts"
+ resolution: 03M
+ year: 2024
diff --git a/tests/integration_test.py b/tests/integration_test.py
index 009e87f..7210a3a 100644
--- a/tests/integration_test.py
+++ b/tests/integration_test.py
@@ -11,12 +11,6 @@
from clio_tools.data_module import ModuleInterface
-@pytest.fixture(scope="module")
-def module_path():
- """Parent directory of the project."""
- return Path(__file__).parent.parent
-
-
def test_interface_file(module_path):
"""The interfacing file should be correct."""
assert ModuleInterface.from_yaml(module_path / "INTERFACE.yaml")
diff --git a/tests/local_test.py b/tests/local_test.py
index 8c1fe56..d8b5ce3 100644
--- a/tests/local_test.py
+++ b/tests/local_test.py
@@ -4,7 +4,7 @@
You'll likely run out of memory.
Instead, run a relevant case:
-pytest tests/local_test.py::test_config_example["europe_example"]
+pytest tests/local_test.py::test_config_example
"""
import subprocess
@@ -13,29 +13,22 @@
import pytest
-@pytest.fixture(scope="module")
-def module_path():
- """Parent directory of the project."""
- return Path(__file__).parent.parent
-
-
@pytest.mark.parametrize(
- "scenario", ["config", "china_example", "europe_example", "usa_example"]
+ "scenario", ["mixed_example", "china_national", "USA_states", "europe_regions"]
)
-def test_config_example(module_path, scenario):
+def test_scenario(module_path: Path, scenario: str):
"""Example files should result in a successful run."""
- result_file = "results/shapes.parquet"
- config_file = Path(module_path / f"config/{scenario}.yaml")
- smk_command = f"snakemake --cores 4 --replace-workflow-config --configfile={config_file} {result_file}"
+ result_file = f"results/{scenario}/shapes.parquet"
+ smk_command = f"snakemake --cores 4 {result_file}"
subprocess.run(smk_command + " --forceall", shell=True, check=True, cwd=module_path)
subprocess.run(
- smk_command + " --report results/report.html",
+ smk_command + f" --report results/{scenario}/report.html",
shell=True,
check=True,
cwd=module_path,
)
subprocess.run(
- smk_command + " --rulegraph | dot -Tpng > results/rulegraph.png",
+ smk_command + f" --rulegraph | dot -Tpng > results/{scenario}/rulegraph.png",
shell=True,
check=True,
cwd=module_path,
diff --git a/workflow/Snakefile b/workflow/Snakefile
index a546796..64971cb 100644
--- a/workflow/Snakefile
+++ b/workflow/Snakefile
@@ -10,7 +10,11 @@ min_version("9.19")
# This allows users to re-wire how the module is used in their workflow with ease.
pathvars:
# Module results
- shapes="/shapes.parquet",
+ shapes="/{scenario}/shapes.parquet",
+
+wildcard_constraints:
+ country="[A-Z]{3}",
+ eez_key="[0-9_]+",
# Load the example configuration. This will be overridden by users.
diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml
index 07d49f8..122064f 100644
--- a/workflow/internal/config.schema.yaml
+++ b/workflow/internal/config.schema.yaml
@@ -10,6 +10,108 @@ $defs:
- type: integer
minimum: 1
+ partialCrsSettings:
+ description: CRS codes (i.e., 'epsg:3035', 'EPSG:4326', 8857).
+ type: object
+ additionalProperties: false
+ properties:
+ projected:
+ description: "Projected CRS code (for area and length calculation)."
+ allOf:
+ - $ref: "#/$defs/crsCode"
+ geographic:
+ description: "Geographic CRS code (for latitude / longitude positioning)."
+ allOf:
+ - $ref: "#/$defs/crsCode"
+
+ crsSettings:
+ allOf:
+ - $ref: "#/$defs/partialCrsSettings"
+ required:
+ - projected
+ - geographic
+
+ partialVoronoiEEZSettings:
+ description: >
+ Optional configuration for the Voronoi EEZ splitting algorithm.
+ type: object
+ additionalProperties: false
+ properties:
+ enabled:
+ description: Activation of the splitting algorithm.
+ type: boolean
+ default: false
+ sample_spacing:
+ description: >
+ Maximum distance between sample points for shorelines.
+ Lower for finer splits at the cost of slower processing.
+ Matches the unit of the projected CRS.
+ type: integer
+ default: 10000
+ minimum: 1
+ min_samples_per_shoreline:
+ description: >
+ Minimum number of sampled points per touching shoreline component.
+ This helps small islands influence the split even when their coastlines
+ are shorter than `sample_spacing`.
+ type: integer
+ default: 8
+ minimum: 1
+ max_samples_per_maritime:
+ description: >
+ Approximate cap for sampled shoreline points per maritime shape.
+ If exceeded, the algorithm raises the effective sample spacing and
+ skips raw shoreline vertices to keep processing bounded.
+ type: integer
+ default: 50000
+ minimum: 1
+ include_shoreline_vertices:
+ description: >
+ Include existing shoreline vertices as Voronoi generators in addition
+ to adaptive midpoint samples.
+ type: boolean
+ default: true
+ shoreline_search_radius:
+ description: >
+ Distance around maritime polygons used to find the marine-facing
+ country exterior boundary. This handles land and EEZ datasets whose
+ coastlines do not exactly coincide. Matches the unit of the projected
+ CRS.
+ type: integer
+ default: 10000
+ minimum: 0
+ uncovered_area_tolerance:
+ description: >
+ Maximum area loss permitted per EEZ polygon after splitting. An error
+ will be raised if exceeded. Matches the unit of the projected CRS.
+ type: number
+ default: 1
+ minimum: 0
+
+ scenario:
+ type: object
+ required:
+ - countries
+ additionalProperties: false
+ properties:
+ crs:
+ $ref: "#/$defs/partialCrsSettings"
+ voronoi_eez:
+ $ref: "#/$defs/partialVoronoiEEZSettings"
+ countries:
+ $ref: "#/$defs/countries"
+
+ countries:
+ description: >
+ Dictionary of countries with each country code as the key.
+ Country code must be in ISO alpha-3 format (e.g., MEX, CHN, DEU).
+ type: object
+ patternProperties:
+ "^[A-Z]{3}$": # Keys must be exactly three uppercase letters
+ $ref: "#/$defs/country"
+ minProperties: 1
+ additionalProperties: false
+
countryBase:
type: object
properties:
@@ -111,35 +213,15 @@ $defs:
description: "Schema for user-provided configuration files."
type: object
+required:
+ - scenarios
+ - crs
+additionalProperties: false
properties:
crs:
- description: CRS codes (i.e., 'epsg:3035', 'EPSG:4326', 8857).
- type: object
- properties:
- projected:
- description: "Projected CRS code (for area and length calculation)."
- allOf:
- - $ref: "#/$defs/crsCode"
- geographic:
- description: "Geographic CRS code (for latitude / longitude positioning)."
- allOf:
- - $ref: "#/$defs/crsCode"
- required:
- - projected
- - geographic
- additionalProperties: false
-
- countries:
- description: >
- Dictionary of countries with each country code as the key.
- Country code must be in ISO alpha-3 format (e.g., MEX, CHN, DEU).
- type: object
- patternProperties:
- "^[A-Z]{3}$": # Keys must be exactly three uppercase letters
- $ref: "#/$defs/country"
- minProperties: 1
- additionalProperties: false
-
+ $ref: "#/$defs/crsSettings"
+ voronoi_eez:
+ $ref: "#/$defs/partialVoronoiEEZSettings"
overture_release:
description: |
Overture data release to use. Two options are possible:
@@ -148,66 +230,13 @@ properties:
type: string
default: "latest"
pattern: "^(?:latest|(?:\\d{4})-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\\d|3[01])\\.\\d+)$"
- voronoi_eez:
+ scenarios:
description: >
- Optional configuration for the Voronoi EEZ splitting algorithm.
+ Shape-building scenarios to run.
+ Downloaded source files will be re-used between them if possible.
type: object
- required:
- - enabled
- - sample_spacing
- properties:
- enabled:
- description: Activation of the splitting algorithm.
- type: boolean
- default: false
- sample_spacing:
- description: >
- Maximum distance between sample points for shorelines.
- Lower for finer splits at the cost of slower processing.
- Matches the unit of the projected CRS.
- type: integer
- default: 10000
- minimum: 1
- min_samples_per_shoreline:
- description: >
- Minimum number of sampled points per touching shoreline component.
- This helps small islands influence the split even when their coastlines
- are shorter than `sample_spacing`.
- type: integer
- default: 8
- minimum: 1
- max_samples_per_maritime:
- description: >
- Approximate cap for sampled shoreline points per maritime shape.
- If exceeded, the algorithm raises the effective sample spacing and
- skips raw shoreline vertices to keep processing bounded.
- type: integer
- default: 50000
- minimum: 1
- include_shoreline_vertices:
- description: >
- Include existing shoreline vertices as Voronoi generators in addition
- to adaptive midpoint samples.
- type: boolean
- default: true
- shoreline_search_radius:
- description: >
- Distance around maritime polygons used to find the marine-facing
- country exterior boundary. This handles land and EEZ datasets whose
- coastlines do not exactly coincide. Matches the unit of the projected
- CRS.
- type: integer
- default: 10000
- minimum: 0
- uncovered_area_tolerance:
- description: >
- Maximum area loss permitted per EEZ polygon after splitting. An error
- will be raised if exceeded. Matches the unit of the projected CRS.
- type: number
- default: 1
- minimum: 0
-
-required:
- - countries
- - crs
-additionalProperties: false
+ patternProperties:
+ "^[A-Za-z0-9_-]+$":
+ $ref: "#/$defs/scenario"
+ minProperties: 1
+ additionalProperties: false
diff --git a/workflow/report/build_combined_area.rst b/workflow/report/build_combined_area.rst
index bcbf5bf..8272f54 100644
--- a/workflow/report/build_combined_area.rst
+++ b/workflow/report/build_combined_area.rst
@@ -1,5 +1,7 @@
Resulting polygonal data of country boundaries and marine exclusive economic zones (EEZ).
+Scenario: {{ snakemake.wildcards.scenario }}
+
Countries: {{ snakemake.params.countries }}
Source datasets: {{ snakemake.params.sources }}
diff --git a/workflow/report/build_country.rst b/workflow/report/build_country.rst
index c1b0e33..88d1e0d 100644
--- a/workflow/report/build_country.rst
+++ b/workflow/report/build_country.rst
@@ -1,4 +1,4 @@
-Built combined file for {{ snakemake.wildcards.country }}.
+Built combined file for {{ snakemake.wildcards.scenario}}-{{ snakemake.wildcards.country }}.
* If present, contested EEZ regions are removed.
* Optionally, EEZ regions are broken down using a Voronoi algorithm.
diff --git a/workflow/report/download_harmonised_eez.rst b/workflow/report/download_harmonised_eez.rst
index 2a70269..1651d2b 100644
--- a/workflow/report/download_harmonised_eez.rst
+++ b/workflow/report/download_harmonised_eez.rst
@@ -1,3 +1,3 @@
-Downloaded and harmonised EEZ for {{ snakemake.wildcards.country }}.
+Downloaded and harmonised EEZ for {{ snakemake.wildcards.eez }}.
Source: Flanders Marine Institute: MarineRegions.org. Available online at `www.marineregions.org `_.
diff --git a/workflow/rules/_utils.smk b/workflow/rules/_utils.smk
index e951c1b..6db3347 100644
--- a/workflow/rules/_utils.smk
+++ b/workflow/rules/_utils.smk
@@ -1,18 +1,54 @@
"""Utility functions for snakemake rule handling."""
-def get_country_file(country: str):
- """Build unique file names to avoid overwriting source files."""
- source = config["countries"][country]["source"]
- subtype = config["countries"][country]["subtype"]
+def get_country_file(scenario: str, country: str):
+ """Build unique file names to avoid overwriting source files.
+
+ Scenarios do not affect the downloaded source files.
+ """
+ country_settings: dict = config["scenarios"][scenario]["countries"][country]
+
+ source = country_settings["source"]
+ subtype = country_settings["subtype"]
filename = f"{source}/harmonise/{country}_{subtype}"
if source == "nuts":
- resolution = config["countries"][country]["resolution"]
- year = config["countries"][country]["year"]
+ resolution = country_settings["resolution"]
+ year = country_settings["year"]
filename += f"_{year}_{resolution}"
elif source == "geoboundaries":
- release = config["countries"][country]["release_type"]
+ release = country_settings["release_type"]
filename += f"_{release}"
return filename
+
+
+def get_crs_config(scenario: str) -> dict:
+ """Get CRS configuration in order of priority."""
+ return config["crs"] | config["scenarios"][scenario].get("crs", {})
+
+
+def get_voronoi_eez_config(scenario: str) -> dict:
+ """Get Voronoi configuration in order of priority."""
+ scenario_override = config["scenarios"][scenario].get("voronoi_eez", {})
+ user_overrides = config.get("voronoi_eez", {}) | scenario_override
+ return internal["voronoi_eez"] | user_overrides
+
+
+def get_eez_file(scenario: str, country: str) -> str:
+ """Build an EEZ filename reusable by scenarios with matching EEZ requests."""
+ extra_eez = config["scenarios"][scenario]["countries"][country].get("extra_eez", [])
+ if not isinstance(extra_eez, list):
+ extra_eez = [extra_eez]
+
+ extra_eez = sorted([int(i) for i in extra_eez])
+ if extra_eez:
+ file_path = f"combined/{country}_{'_'.join([str(i) for i in extra_eez])}"
+ else:
+ file_path = f"single/{country}"
+ return file_path
+
+
+def get_extra_eez_from_key(eez_key: str) -> list[int]:
+ """Recover additional EEZ identifiers from an EEZ output wildcard."""
+ return [int(i) for i in eez_key.split("_")]
diff --git a/workflow/rules/build.smk b/workflow/rules/build.smk
index 3fb64f8..9a3de48 100644
--- a/workflow/rules/build.smk
+++ b/workflow/rules/build.smk
@@ -1,54 +1,86 @@
"""Rules used to construct the final dataset."""
+rule build_eez:
+ input:
+ country="/automatic/eez/single/{country}.parquet",
+ extra=lambda wc: [
+ f"/automatic/eez/single/{mrgid}.parquet"
+ for mrgid in get_extra_eez_from_key(wc.eez_key)
+ ],
+ output:
+ path="/automatic/eez/combined/{country}_{eez_key}.parquet",
+ log:
+ "/eez/build/{country}_{eez_key}.log",
+ conda:
+ "../envs/shape.yaml"
+ message:
+ "Build '{wildcards.country}' EEZ dataset ({wildcards.eez_key})."
+ script:
+ "../scripts/build_eez.py"
+
+
rule build_country:
input:
- land=lambda wc: f"/automatic/{get_country_file(wc.country)}.parquet",
- maritime="/automatic/eez/{country}.parquet",
+ land=lambda wc: (
+ f"/automatic/{get_country_file(wc.scenario, wc.country)}.parquet"
+ ),
+ maritime=lambda wc: (
+ f"/automatic/eez/{get_eez_file(wc.scenario, wc.country)}.parquet"
+ ),
output:
- country="/automatic/country/{country}.parquet",
+ country="/automatic/scenarios/{scenario}/{country}.parquet",
plot=report(
- "/automatic/country/{country}.png",
+ "/automatic/scenarios/{scenario}/{country}.png",
caption="../report/build_country.rst",
category="Module Geo-Boundaries",
subcategory="Country area",
),
log:
- "/{country}/build_country.log",
+ "/scenarios/{scenario}/build_country/{country}.log",
conda:
"../envs/shape.yaml"
params:
- crs=config["crs"],
- voronoi=internal["voronoi_eez"] | config.get("voronoi_eez", {}),
+ crs=lambda wc: get_crs_config(wc.scenario),
+ voronoi=lambda wc: get_voronoi_eez_config(wc.scenario),
message:
- "{wildcards.country}: build combined land and marine polygons."
+ "{wildcards.scenario}:{wildcards.country}: build combined land and marine polygons."
script:
"../scripts/build_country.py"
rule build_combined_area:
input:
- countries=[
- f"/automatic/country/{country}.parquet"
- for country in config["countries"]
+ countries=lambda wc: [
+ f"/automatic/scenarios/{wc.scenario}/{country}.parquet"
+ for country in config["scenarios"][wc.scenario]["countries"]
],
output:
combined="",
plot=report(
- "/shapes.png",
+ "/{scenario}/shapes.png",
caption="../report/build_combined_area.rst",
category="Module Geo-Boundaries",
subcategory="Combined area",
),
log:
- "/build_combined_area.log",
+ "/scenarios/{scenario}/build_combined_area.log",
conda:
"../envs/shape.yaml"
params:
- crs=config["crs"],
- countries=sorted([i for i in config["countries"]]),
- sources=sorted(set([i["source"] for i in config["countries"].values()])),
+ crs=lambda wc: get_crs_config(wc.scenario),
+ countries=lambda wc: sorted(
+ [i for i in config["scenarios"][wc.scenario]["countries"]]
+ ),
+ sources=lambda wc: sorted(
+ set(
+ [
+ i["source"]
+ for i in config["scenarios"][wc.scenario]["countries"].values()
+ ]
+ )
+ ),
message:
- "Combine land and marine polygons."
+ "{wildcards.scenario}: combine land and marine polygons."
script:
"../scripts/build_combined_area.py"
diff --git a/workflow/rules/harmonise.smk b/workflow/rules/harmonise.smk
index 2723a1a..e59987f 100644
--- a/workflow/rules/harmonise.smk
+++ b/workflow/rules/harmonise.smk
@@ -41,9 +41,6 @@ rule harmonise_gadm:
"/gadm/harmonise/{country}_{subtype}.log",
conda:
"../envs/shape.yaml"
- params:
- country_id=lambda wc: str(wc.country),
- subtype=lambda wc: str(wc.subtype),
message:
"Harmonising '{wildcards.country}_{wildcards.subtype}' GADM dataset."
script:
@@ -67,22 +64,21 @@ rule harmonise_nuts:
rule download_harmonised_eez:
output:
- path="/automatic/eez/{country}.parquet",
+ path="/automatic/eez/single/{eez}.parquet",
plot=report(
- "/automatic/eez/{country}.png",
+ "/automatic/eez/single/{eez}.png",
caption="../report/download_harmonised_eez.rst",
category="Module Geo-Boundaries",
subcategory="EEZ area",
),
log:
- "/eez/download_harmonised/{country}.log",
+ "/eez/download_harmonised/{eez}.log",
localrule: True
conda:
"../envs/shape.yaml"
params:
- extra_eez=lambda wc: config["countries"][wc.country].get("extra_eez", []),
timeouts=internal["timeouts"],
message:
- "Download and harmonise '{wildcards.country}' EEZ dataset."
+ "Download and harmonise EEZ dataset '{wildcards.eez}'."
script:
"../scripts/download_harmonised_eez.py"
diff --git a/workflow/scripts/_utils.py b/workflow/scripts/_utils.py
index 266d441..8ae4c39 100644
--- a/workflow/scripts/_utils.py
+++ b/workflow/scripts/_utils.py
@@ -17,6 +17,7 @@
requests.exceptions.Timeout,
requests.exceptions.ChunkedEncodingError,
)
+CRS_MARINE_REGIONS = "EPSG:4326"
@dataclass
diff --git a/workflow/scripts/build_country.py b/workflow/scripts/build_country.py
index 47e6e7e..33ac6cd 100644
--- a/workflow/scripts/build_country.py
+++ b/workflow/scripts/build_country.py
@@ -21,6 +21,7 @@
)
from shapely.geometry.base import BaseGeometry
from shapely.ops import linemerge, unary_union
+from shapely.validation import explain_validity
if TYPE_CHECKING:
snakemake: Any
@@ -49,6 +50,57 @@ class ShorelineLine:
geometry: LineString
+def validate_reprojections(
+ land: gpd.GeoDataFrame, maritime: gpd.GeoDataFrame, crs: dict[str, CRS]
+) -> None:
+ """Fail early if configured CRS settings corrupt country inputs."""
+ problems = []
+ for layer, gdf in [("land", land), ("maritime", maritime)]:
+ if gdf.empty:
+ continue
+
+ for crs_name in ["projected", "geographic"]:
+ target_crs = crs[crs_name]
+ try:
+ projected = gdf.to_crs(target_crs)
+ except Exception as exc:
+ problems.append(
+ f"{layer} to {crs_name} CRS {target_crs.to_string()!r}: "
+ f"{type(exc).__name__}: {exc}"
+ )
+ continue
+
+ invalid = projected.loc[~projected.geometry.is_valid]
+ if invalid.empty:
+ continue
+
+ details = []
+ for row in invalid.head(10).itertuples():
+ details.append(
+ ", ".join(
+ [
+ f"shape_id={getattr(row, 'shape_id', '')!r}",
+ f"parent_id={getattr(row, 'parent_id', '')!r}",
+ f"reason={explain_validity(row.geometry)!r}",
+ ]
+ )
+ )
+ if len(invalid) > len(details):
+ details.append(f"{len(invalid) - len(details)} more geometries")
+
+ problems.append(
+ f"{layer} to {crs_name} CRS {target_crs.to_string()!r} "
+ "created invalid geometries: " + "; ".join(details)
+ )
+
+ if problems:
+ raise ValueError(
+ "Configured CRS settings cannot safely reproject the inputs. "
+ "Choose CRS settings that cover all requested land and EEZ geometries.\n"
+ "- " + "\n- ".join(problems)
+ )
+
+
def _iter_lines(geom: BaseGeometry) -> Iterator[LineString]:
"""Yield line components from a possibly nested geometry."""
if geom.is_empty:
@@ -200,7 +252,7 @@ def _split_one_maritime(
maritime_row,
land: gpd.GeoDataFrame,
*,
- crs: int | str,
+ crs: CRS,
voronoi_config: VoronoiConfig,
) -> tuple[gpd.GeoDataFrame, gpd.GeoDataFrame]:
"""Split a single maritime shape to fit the coastline."""
@@ -297,10 +349,10 @@ def split_maritime_by_shoreline_voronoi(
if not voronoi_config.enabled:
return shapes, cells
- shapes = shapes.copy().to_crs(crs["projected"])
-
- land = shapes.loc[shapes["shape_class"].eq("land")]
- maritime = shapes.loc[shapes["shape_class"].eq("maritime")]
+ land = shapes.loc[shapes["shape_class"].eq("land")].copy().to_crs(crs["projected"])
+ maritime = (
+ shapes.loc[shapes["shape_class"].eq("maritime")].copy().to_crs(crs["projected"])
+ )
if maritime.empty:
raise ValueError("Requested voronoi without maritime shapes.")
@@ -310,7 +362,7 @@ def split_maritime_by_shoreline_voronoi(
maritime_row,
land.loc[land["country_id"].eq(maritime_row.country_id)],
voronoi_config=voronoi_config,
- crs=shapes.crs,
+ crs=crs["projected"],
)
for maritime_row in maritime.itertuples(index=False)
]
@@ -323,7 +375,7 @@ def split_maritime_by_shoreline_voronoi(
cells = gpd.GeoDataFrame(
pd.concat(cell_frames, ignore_index=True),
geometry="geometry",
- crs=shapes.crs,
+ crs=crs["projected"],
)
result = result.to_crs(crs["geographic"])
@@ -332,19 +384,23 @@ def split_maritime_by_shoreline_voronoi(
def combine_shapes(
- land: gpd.GeoDataFrame, maritime: gpd.GeoDataFrame, geo_crs: CRS
+ land: gpd.GeoDataFrame, maritime: gpd.GeoDataFrame, crs: dict[str, CRS]
) -> gpd.GeoDataFrame:
"""Combine land and marine shapes."""
- combined = land.copy().to_crs(geo_crs)
- if not maritime.empty:
- # remove contested zones and clip to give priority to maritime polygons
- eez = maritime.copy().to_crs(geo_crs)
- eez = eez[eez["contested"].eq(False)].drop(columns="contested")
- combined.geometry = combined.geometry.difference(eez.geometry.union_all())
- combined = pd.concat([combined, eez], ignore_index=True)
-
- # Resolve floating point mismatches that occur during CRS conversion
+ if maritime.empty:
+ return land.copy().to_crs(crs["geographic"])
+
+ combined = land.copy().to_crs(crs["projected"])
+
+ # Remove contested zones and clip in projected coordinates.
+ eez = maritime.copy().to_crs(crs["projected"])
+ eez = eez[eez["contested"].eq(False)].drop(columns="contested")
+ combined.geometry = combined.geometry.difference(eez.geometry.union_all())
+ combined = pd.concat([combined, eez], ignore_index=True)
+
+ # Resolve floating point mismatches before converting back to output CRS.
combined.geometry = combined.geometry.buffer(0)
+ combined = combined.to_crs(crs["geographic"])
return combined
@@ -367,8 +423,9 @@ def main() -> None:
raise ValueError(
f"Country processing mismatch for {country!r}. Found {country_ids!r}."
)
+ validate_reprojections(land, maritime, crs)
- shapes = combine_shapes(land, maritime, crs["geographic"])
+ shapes = combine_shapes(land, maritime, crs)
shapes = _schemas.ShapesSchema.validate(shapes)
voronoi_config = VoronoiConfig(**snakemake.params.voronoi)
diff --git a/workflow/scripts/build_eez.py b/workflow/scripts/build_eez.py
new file mode 100644
index 0000000..b7d2f1c
--- /dev/null
+++ b/workflow/scripts/build_eez.py
@@ -0,0 +1,48 @@
+"""Build one configured EEZ dataset from cached MarineRegions downloads."""
+
+import sys
+from typing import TYPE_CHECKING, Any
+
+import _schemas
+import geopandas as gpd
+import pandas as pd
+from _utils import CRS_MARINE_REGIONS
+
+if TYPE_CHECKING:
+ snakemake: Any
+
+
+def build_eez(country: str, paths: list[str]) -> gpd.GeoDataFrame:
+ """Combine a country EEZ file with optional extra MarineRegions ID files."""
+ frames = [
+ _schemas.EEZSchema.validate(gpd.read_parquet(path)).to_crs(CRS_MARINE_REGIONS)
+ for path in paths
+ ]
+ frames = [frame for frame in frames if not frame.empty]
+
+ if frames:
+ combined = gpd.GeoDataFrame(pd.concat(frames, ignore_index=True))
+ combined["country_id"] = country
+ combined["shape_id"] = combined["parent_id"].apply(
+ lambda parent_id: "_".join([country, "marineregions", str(parent_id)])
+ )
+ else:
+ combined = gpd.GeoDataFrame(
+ columns=_schemas.EEZSchema.to_schema().columns,
+ geometry="geometry",
+ crs=CRS_MARINE_REGIONS,
+ )
+
+ return _schemas.EEZSchema.validate(combined)
+
+
+def main() -> None:
+ """Main snakemake process."""
+ input_paths = [snakemake.input.country, *snakemake.input.extra]
+ gdf = build_eez(snakemake.wildcards.country, input_paths)
+ gdf.to_parquet(snakemake.output.path)
+
+
+if __name__ == "__main__":
+ sys.stderr = open(snakemake.log[0], "w")
+ main()
diff --git a/workflow/scripts/download_harmonised_eez.py b/workflow/scripts/download_harmonised_eez.py
index 3a52578..40d73d5 100644
--- a/workflow/scripts/download_harmonised_eez.py
+++ b/workflow/scripts/download_harmonised_eez.py
@@ -1,10 +1,9 @@
"""Download data from the Marineregions database.
https://www.marineregions.org/
-Constructs EEZ datasets per country
+
- In cases where no EEZ is available for a country an empty dataframe will be saved.
-- Optionally, additional EEZs may be downloaded if specified in `extra_eez`.
- - These should result in an error if the code is invalid.
+- Individual MarineRegions IDs can be downloaded separately and combined later.
"""
import sys
@@ -13,7 +12,6 @@
import _schemas
import _utils
import geopandas as gpd
-import pandas as pd
import requests
from matplotlib import pyplot as plt
@@ -22,7 +20,6 @@
WFS_BASE = "https://geo.vliz.be/geoserver/MarineRegions/wfs"
WFS_VERSION = "2.0.0"
-CRS_MARINE_REGIONS = "EPSG:4326"
def _raise_for_geoserver_exception(response: requests.Response) -> None:
@@ -89,7 +86,7 @@ def get_eez_by_cql(
"typeNames": "eez",
"outputFormat": "application/json",
"cql_filter": cql_filter,
- "srsName": CRS_MARINE_REGIONS,
+ "srsName": _utils.CRS_MARINE_REGIONS,
}
response = _get_wfs_response(params, timeouts)
@@ -103,7 +100,7 @@ def get_eez_by_cql(
if data["features"]:
# Let geopandas build the frame, CRS will match request
result = gpd.GeoDataFrame.from_features(
- data["features"], crs=CRS_MARINE_REGIONS
+ data["features"], crs=_utils.CRS_MARINE_REGIONS
)
if result.empty:
result = None
@@ -179,45 +176,44 @@ def plot(gdf: gpd.GeoDataFrame, country: str):
return fig, ax
-def download_eezs(
- country: str, extra_eez: list[str], timeouts: _utils.DownloadTimeouts
+def download_eez(
+ cql_filter: str,
+ country_id: str,
+ timeouts: _utils.DownloadTimeouts,
+ *,
+ allow_empty: bool,
) -> gpd.GeoDataFrame:
- """Download EEZ data as requested.
+ """Download and harmonise one EEZ query.
- If no EEZ exists for a country, the dataframe will be empty.
+ If no EEZ exists for a country query, the dataframe will be empty.
+ MarineRegions ID queries are expected to return exactly one dataset.
"""
- eez_gdfs: list[gpd.GeoDataFrame] = []
-
- iso3_eez = get_eez_by_cql(f"iso_ter1='{country}'", timeouts)
- if iso3_eez is not None:
- eez_gdfs.append(iso3_eez)
-
- for mrgid in extra_eez:
- extra_gdf = get_eez_by_cql(f"mrgid={int(mrgid)}", timeouts)
- if extra_gdf is None:
- raise RuntimeError(f"Configured extra_eez {mrgid!r} returned no features")
- eez_gdfs.append(extra_gdf)
-
- combined_gdf = None
- if eez_gdfs:
- combined_gdf = pd.concat(eez_gdfs, ignore_index=True)
+ gdf = get_eez_by_cql(cql_filter, timeouts)
+ if gdf is None and not allow_empty:
+ raise RuntimeError(f"Configured EEZ query {cql_filter!r} returned no features")
- return transform_to_schema(combined_gdf, country)
+ return transform_to_schema(gdf, country_id)
def main() -> None:
"""Main snakemake process."""
- country = snakemake.wildcards.country
- extra_eez = snakemake.params.extra_eez
-
timeouts = _utils.DownloadTimeouts(**snakemake.params.timeouts)
- if not isinstance(extra_eez, list):
- extra_eez = [extra_eez]
+ eez = snakemake.wildcards.eez
+
+ if eez.isdigit():
+ label = f"mrgid {eez}"
+ gdf = download_eez(
+ f"mrgid={int(eez)}", "extra_eez", timeouts, allow_empty=False
+ )
+ elif len(eez) == 3 and eez.isalpha() and eez.isupper():
+ label = eez
+ gdf = download_eez(f"iso_ter1='{eez}'", eez, timeouts, allow_empty=True)
+ else:
+ raise ValueError(f"Unsupported EEZ identifier: {eez!r}")
- gdf = download_eezs(country, extra_eez, timeouts)
gdf.to_parquet(snakemake.output.path)
- fig, _ = plot(gdf, country)
+ fig, _ = plot(gdf, label)
fig.savefig(snakemake.output.plot, bbox_inches="tight", dpi=200)
diff --git a/workflow/scripts/harmonise_gadm.py b/workflow/scripts/harmonise_gadm.py
index dfa34b0..c263941 100644
--- a/workflow/scripts/harmonise_gadm.py
+++ b/workflow/scripts/harmonise_gadm.py
@@ -46,7 +46,7 @@ def standardise_country_gadm(
if __name__ == "__main__":
standardise_country_gadm(
input_path=snakemake.input.raw,
- country_id=snakemake.params.country_id,
- subtype=snakemake.params.subtype,
+ country_id=snakemake.wildcards.country,
+ subtype=snakemake.wildcards.subtype,
output_path=snakemake.output.standardised,
)
From 1dac369a252ef24c8da4d7b5e6a4041812b3a5bb Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Sun, 7 Jun 2026 10:21:30 +0000
Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
workflow/Snakefile | 1 +
workflow/scripts/build_country.py | 6 +-----
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/workflow/Snakefile b/workflow/Snakefile
index 64971cb..44429ff 100644
--- a/workflow/Snakefile
+++ b/workflow/Snakefile
@@ -12,6 +12,7 @@ pathvars:
# Module results
shapes="/{scenario}/shapes.parquet",
+
wildcard_constraints:
country="[A-Z]{3}",
eez_key="[0-9_]+",
diff --git a/workflow/scripts/build_country.py b/workflow/scripts/build_country.py
index 33ac6cd..383c81b 100644
--- a/workflow/scripts/build_country.py
+++ b/workflow/scripts/build_country.py
@@ -249,11 +249,7 @@ def _make_shoreline_seed_records(
def _split_one_maritime(
- maritime_row,
- land: gpd.GeoDataFrame,
- *,
- crs: CRS,
- voronoi_config: VoronoiConfig,
+ maritime_row, land: gpd.GeoDataFrame, *, crs: CRS, voronoi_config: VoronoiConfig
) -> tuple[gpd.GeoDataFrame, gpd.GeoDataFrame]:
"""Split a single maritime shape to fit the coastline."""
if land.empty:
From 416f48fc267ee0499be594562f3591fc4d80fb13 Mon Sep 17 00:00:00 2001
From: Ivan Ruiz Manuel <72193617+irm-codebase@users.noreply.github.com>
Date: Sun, 14 Jun 2026 12:00:30 +0200
Subject: [PATCH 3/6] Add antimeridian fixing (DOES NOT WORK 100% OF THE TIME)
---
README.md | 20 +-
config/config.yaml | 3 -
pixi.lock | 471 ++++++++++++--------
pixi.toml | 4 +-
workflow/Snakefile | 1 +
workflow/envs/shape.linux-64.pin.txt | 120 ++---
workflow/envs/shape.osx-arm64.pin.txt | 124 +++---
workflow/envs/shape.win-64.pin.txt | 110 ++---
workflow/envs/shape.yaml | 4 +-
workflow/internal/config.schema.yaml | 2 +-
workflow/rules/build.smk | 6 +-
workflow/scripts/_geo.py | 131 ++++++
workflow/scripts/_schemas.py | 34 +-
workflow/scripts/_utils.py | 17 +-
workflow/scripts/build_combined_area.py | 25 +-
workflow/scripts/build_country.py | 125 ++----
workflow/scripts/build_eez.py | 2 +-
workflow/scripts/download_harmonised_eez.py | 5 +-
18 files changed, 704 insertions(+), 500 deletions(-)
create mode 100644 workflow/scripts/_geo.py
diff --git a/README.md b/README.md
index 787b5f3..fc3f840 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ Data processing steps:
-1. For each requested country combination (`scenarios`), the configuration file is read to identify the datasets (`source`) to use as well as the specific countries and subnational aggregation (`subtype`) to process.
+1. The configuration file is read to identify which requested geopolitical combination (`scenarios`) was requested. These can be any number of nations (`countries`), each comming from a distinct dataset (`source`) and with its own subnational aggregation (`subtype`).
- Country landmass data: [eurostat NUTS](https://ec.europa.eu/eurostat/web/gisco/geodata/statistical-units/territorial-units-statistics), [GADM](https://gadm.org/), [geoBoundaries](https://www.geoboundaries.org/), and [Overture Maps](https://overturemaps.org/) are supported.
- Exclusive Economic Zone (EEZ) data: [MarineRegions.org](https://www.marineregions.org/).
2. Individual country files are downloaded and harmonised to fit a standardised schema.
@@ -35,13 +35,21 @@ Data processing steps:
- Optionally, a Voronoi algorithm is run to separate EEZ areas to fit subnational regions.
3. The country files requested in the scenario are combined and then clipped using their neighbours to minimise overlapping polygons.
-> [!TIP] Keep in mind the following
-> - The `subtype` naming matches that of the source database. For example, NUTS uses 0, 1, 2 and 3 (NUTS0, NUTS1, NUTS2, etc.).
->Use the references at the bottom of this page for more details.
-> - The downloaded data is always kept locally for future re-use.
+> [!TIP]
+> Keep in mind the following
+>
+> - All downloaded data is kept locally for future reuse across scenarios to minimise stress on the data sources the module relies on.
+> - Data source availability can vary.
+> Always consult the data source website to identify if a country is available at the desired resolution.
> [!CAUTION]
-> To increase the replicability of your workflow, we recommend using NUTS and geoBoundaries as sources whenever possible as they have more stable hosting methods than Overture Maps and GADM.
+> Be aware of the following known issues.
+>
+>- Overture Maps replicability: this data source [does not retain versions](https://github.com/orgs/OvertureMaps/discussions/422) for long.
+> If replicability is important to you, we suggest configuring other sources.
+>- Anti-meridian distortions: regions near the 180/-180 line (e.g., Fiji, Hawaii, New Zealand, Alaska) might be distorted during processing.
+> This is a known issue in the libraries we rely on (`geopandas`, `GDAL`) and something that is being actively [worked on at the moment](https://geopandas.org/en/v1.1.3/about/roadmap.html#s2-geometry-engine).
+> For now, we advice to use global projections (EPSG:3857, EPSG:8857) as a way to mitigate it.
## Configuration
diff --git a/config/config.yaml b/config/config.yaml
index ba7ae3e..70bcd68 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -52,9 +52,6 @@ scenarios:
USA_states:
# Example of regional disaggregation of a large country crossing the antimeridean.
# USA has multiple marine zones, which can be appended as extras
- crs:
- projected: "EPSG:10598"
- geographic: "epsg:4326"
countries:
"USA":
subtype: "1"
diff --git a/pixi.lock b/pixi.lock
index e34830e..6dc220e 100644
--- a/pixi.lock
+++ b/pixi.lock
@@ -803,6 +803,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.0-h480dda7_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.4-h1000f5c_4.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda
@@ -837,17 +838,15 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.11.4-h6c36cd4_6.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.10.3-h05c3bbc_24.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-h9d11ab5_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h10be129_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.2-h174a0a3_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-6_h47877c9_openblas.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda
@@ -882,7 +881,6 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-h5888daf_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_1.conda
@@ -899,13 +897,14 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-19.0.1-py312h7900ff3_2.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-19.0.1-py312hc195796_2_cpu.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py312h868fb18_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.11.1-py312h6e8b602_1.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.11.0-py312h02b19dd_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312he675c61_3.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/python-duckdb-1.2.0-py312h2ec8cdc_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/pytokens-0.4.1-py312h5253ce2_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.4.4-py312h762fea3_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py312h868fb18_0.conda
@@ -925,8 +924,10 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/antimeridian-0.4.7-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/black-26.3.1-pyh866005b_0.conda
@@ -938,14 +939,16 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/noarch/chardet-7.4.3-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.3-pyhc90fa1f_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.2.0-h6c4a22f_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/frictionless-4.40.8-pyh6c4a22f_0.tar.bz2
- - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.0.1-pyhd8ed1ab_3.conda
- - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.0.1-pyha770c72_3.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.3-pyhd8ed1ab_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.3-pyha770c72_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
@@ -1000,6 +1003,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/simpleeval-1.0.7-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/stringcase-1.2.0-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda
@@ -1020,8 +1024,10 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.1-pyhcf101f3_0.conda
osx-arm64:
+ - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/antimeridian-0.4.7-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/black-26.3.1-pyh866005b_0.conda
@@ -1033,14 +1039,16 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/noarch/chardet-7.4.3-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.3-pyhc90fa1f_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/duckdb-1.2.0-h6c4a22f_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/frictionless-4.40.8-pyh6c4a22f_0.tar.bz2
- - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.0.1-pyhd8ed1ab_3.conda
- - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.0.1-pyha770c72_3.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.3-pyhd8ed1ab_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.3-pyha770c72_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
@@ -1095,6 +1103,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/simpleeval-1.0.7-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/stringcase-1.2.0-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda
@@ -1145,6 +1154,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.0-h4bcf65f_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/geotiff-1.7.4-hf862be1_4.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda
@@ -1177,16 +1187,14 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.11.4-h693e041_6.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.10.3-h694df76_24.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.39.0-h2f60c08_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.39.0-ha114238_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-ha332bbd_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.11.2-h934fa54_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-6_hd9741b5_openblas.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda
@@ -1218,7 +1226,6 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py313h65a2061_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py313h36cb854_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h11e0b38_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py313h16eae64_1.conda
@@ -1235,13 +1242,14 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-19.0.1-py313h39782a4_2.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-19.0.1-py313hcc89289_2_cpu.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.4-py313h212e517_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.11.1-py313hd8ca31c_1.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.11.0-py313h4ad91d6_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py313h6de5794_3.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.13-h20e6be0_100_cp313.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-duckdb-1.2.0-py313h928ef07_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pytokens-0.4.1-py313h6688731_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py313h65a2061_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.4.4-py313hb3bd904_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.30.0-py313h2c089d5_0.conda
@@ -1260,8 +1268,10 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda
win-64:
+ - conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/antimeridian-0.4.7-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/black-26.3.1-pyh866005b_0.conda
@@ -1273,6 +1283,8 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/noarch/chardet-7.4.3-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.3-pyh6dadd2b_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda
@@ -1280,8 +1292,8 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/frictionless-4.40.8-pyh6c4a22f_0.tar.bz2
- - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.0.1-pyhd8ed1ab_3.conda
- - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.0.1-pyha770c72_3.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.3-pyhd8ed1ab_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.3-pyha770c72_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
@@ -1335,6 +1347,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/simpleeval-1.0.7-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda
+ - conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/stringcase-1.2.0-pyhd8ed1ab_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda
@@ -1381,6 +1394,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.14.3-h57928b3_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.0-hdade9fe_0.conda
+ - conda: https://conda.anaconda.org/conda-forge/win-64/geotiff-1.7.4-h73469f5_4.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.5.0-py313h1a38498_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h0ea6238_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/lcms2-2.19.1-hf2c6c5f_0.conda
@@ -1405,16 +1419,14 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.14.3-h57928b3_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.14.3-hdbac1cb_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libgcc-15.2.0-h8ee18e1_19.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.11.4-h8a8bf46_6.conda
+ - conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.10.3-h2f24e33_24.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_19.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-2.39.0-h01c467a_1.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-storage-2.39.0-he04ea4c_1.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.78.1-h9ff2b3e_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h172a326_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.4.1-hfd05255_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.11.2-h932607e_1.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-6_hf9ab0e9_mkl.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda
@@ -1444,7 +1456,6 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.9-py313he1ded55_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_13.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-he0c23c2_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py313hce7ae62_1.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2025.3.1-h57928b3_13.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda
@@ -1459,13 +1470,14 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-19.0.1-py313hfa70ccb_2.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/pyarrow-core-19.0.1-py313h5921983_2_cpu.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.4-py313hfbe8231_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.11.1-py313h0dbd5a6_1.conda
+ - conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.11.0-py313h75b81ac_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py313hbf73894_3.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.13-h09917c8_100_cp313.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/python-duckdb-1.2.0-py313h5813708_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/pytokens-0.4.1-py313h5fd188c_1.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py313hd650c13_1.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda
+ - conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.4.4-py313ha5c5119_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-ha104f34_1.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-0.30.0-py313hfbe8231_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.8.0-np2py313h4ce4a18_1.conda
@@ -2419,6 +2431,25 @@ packages:
license: LGPL-2.1-only
size: 1977241
timestamp: 1755851798617
+- conda: https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.4-h1000f5c_4.conda
+ sha256: d17e2f34fe5c61eb620e8679368d8ee90be36379cd6023f8690bdcba1c60761c
+ md5: ff1966654a6cd1cf06a6e44c13e60b8a
+ depends:
+ - proj
+ - zlib
+ - libjpeg-turbo
+ - libtiff
+ - __glibc >=2.17,<3.0.a0
+ - libstdcxx >=14
+ - libgcc >=14
+ - libzlib >=1.3.1,<2.0a0
+ - proj >=9.7.0,<9.8.0a0
+ - libjpeg-turbo >=3.1.0,<4.0a0
+ - libtiff >=4.7.0,<4.8.0a0
+ license: MIT
+ license_family: MIT
+ size: 144495
+ timestamp: 1757965550923
- conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda
sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a
md5: d411fc29e338efb48c5fd4576d71d881
@@ -3234,13 +3265,14 @@ packages:
license_family: BSD
size: 177306
timestamp: 1766331805898
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.11.4-h6c36cd4_6.conda
- sha256: 5bcca9be1cd9c3e0e7c7bbc1b4d2ebefbb5b1004e21f5bdbd993a0e5b9c0872c
- md5: c6c5d093e8a60acdd93f731cc3593c97
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.10.3-h05c3bbc_24.conda
+ sha256: 1a1aebbc173afbb90602c93b6cdff31bd781998c228717797f941caef6434a74
+ md5: a69445ccff7ec3d60fe14110bd4d86ae
depends:
- __glibc >=2.17,<3.0.a0
- blosc >=1.21.6,<2.0a0
- geos >=3.14.0,<3.14.1.0a0
+ - geotiff >=1.7.4,<1.8.0a0
- giflib >=5.2.2,<5.3.0a0
- json-c >=0.18,<0.19.0a0
- lerc >=4.0.0,<5.0a0
@@ -3251,30 +3283,29 @@ packages:
- libgcc >=14
- libiconv >=1.18,<2.0a0
- libjpeg-turbo >=3.1.0,<4.0a0
- - libjxl >=0.11,<0.12.0a0
- libkml >=1.3.0,<1.4.0a0
- liblzma >=5.8.1,<6.0a0
- libpng >=1.6.50,<1.7.0a0
- libspatialite >=5.1.0,<5.2.0a0
- libsqlite >=3.50.4,<4.0a0
- libstdcxx >=14
+ - libtiff >=4.7.1,<4.8.0a0
- libwebp-base >=1.6.0,<2.0a0
- libxml2
- libxml2-16 >=2.14.6
- libzlib >=1.3.1,<2.0a0
- lz4-c >=1.10.0,<1.11.0a0
- - muparser >=2.3.5,<2.4.0a0
- openssl >=3.5.4,<4.0a0
- pcre2 >=10.46,<10.47.0a0
- proj >=9.7.0,<9.8.0a0
- xerces-c >=3.3.0,<3.4.0a0
- zstd >=1.5.7,<1.6.0a0
constrains:
- - libgdal 3.11.4.*
+ - libgdal 3.10.3.*
license: MIT
license_family: MIT
- size: 12131233
- timestamp: 1761259370721
+ size: 11051744
+ timestamp: 1761690729608
- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda
sha256: 8a7b01e1ee1c462ad243524d76099e7174ebdd94ff045fe3e9b1e58db196463b
md5: 40d9b534410403c821ff64f00d0adc22
@@ -3460,16 +3491,6 @@ packages:
license_family: APACHE
size: 7021360
timestamp: 1774020290672
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h10be129_0.conda
- sha256: 8b70955d5e9a49d08945d4f8e2eab855b2efa5fce9cb9bc5e75d86764e6f2f38
- md5: 3a9428b74c403c71048104d38437b48c
- depends:
- - __glibc >=2.17,<3.0.a0
- - libgcc >=14
- - libstdcxx >=14
- license: Apache-2.0 OR BSD-3-Clause
- size: 1435782
- timestamp: 1776989559668
- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda
sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f
md5: 915f5995e94f60e9a4826e0b0920ee88
@@ -3501,20 +3522,6 @@ packages:
license: IJG AND BSD-3-Clause AND Zlib
size: 633831
timestamp: 1775962768273
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.2-h174a0a3_1.conda
- sha256: 0c8a78c6a42a6e4c6de3a5e82d692f60400d43f4cc80591745f28b37daad9c70
- md5: 850f48943d6b4589800a303f0de6a816
- depends:
- - __glibc >=2.17,<3.0.a0
- - libgcc >=14
- - libstdcxx >=14
- - libhwy >=1.4.0,<1.5.0a0
- - libbrotlienc >=1.2.0,<1.3.0a0
- - libbrotlidec >=1.2.0,<1.3.0a0
- license: BSD-3-Clause
- license_family: BSD
- size: 1846962
- timestamp: 1777065125966
- conda: https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda
sha256: f6348ac9cebbc03f765ea6d2da88d57d19531585c8f3a963b98635b208e8bef1
md5: 953b7cca897e21215302dbfe2af5cd0c
@@ -4279,17 +4286,6 @@ packages:
license_family: Apache
size: 102525
timestamp: 1762504116832
-- conda: https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-h5888daf_0.conda
- sha256: 320dfc59a94cb9e3635bda71b9e62278b34aa2fdaea0caa6832ddb9b37e9ccd5
- md5: ab3e3db511033340e75e7002e80ce8c0
- depends:
- - __glibc >=2.17,<3.0.a0
- - libgcc >=13
- - libstdcxx >=13
- license: MIT
- license_family: MIT
- size: 203174
- timestamp: 1747116762269
- conda: https://conda.anaconda.org/conda-forge/linux-64/mypy-1.19.1-py312h4c3975b_0.conda
sha256: d0e0765e5ec08141b10da9e03ef620d2e3e571d81cc2bc14025c52a48bb01856
md5: c3ad8cc29400fe5ca1b6a6e5ae46538e
@@ -4760,22 +4756,22 @@ packages:
license_family: MIT
size: 1895409
timestamp: 1778084226169
-- conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.11.1-py312h6e8b602_1.conda
- sha256: 0c44f249204ca6886abcc8a03cf8c8c8681d18cc533ede3697a3f00ac99e4740
- md5: 71e2dd5aa884ab062c2d41fe10f9cefe
+- conda: https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.11.0-py312h02b19dd_0.conda
+ sha256: 28ad34f1e1ddad99bbbd7d2609fe46855e920f6985644f52852adf9ecfddc868
+ md5: b4e4e057ab327b7a1270612587a75523
depends:
- __glibc >=2.17,<3.0.a0
- - libgcc >=14
- - libgdal-core >=3.11.3,<3.12.0a0
- - libstdcxx >=14
+ - libgcc >=13
+ - libgdal-core >=3.10.3,<3.11.0a0
+ - libstdcxx >=13
- numpy
- packaging
- python >=3.12,<3.13.0a0
- python_abi 3.12.* *_cp312
license: MIT
license_family: MIT
- size: 638127
- timestamp: 1756824422270
+ size: 665062
+ timestamp: 1746734790035
- conda: https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312he675c61_3.conda
sha256: ea0299f9699547531e81dd685f7ff0e50f7bc6dad932d8005ef24c3393cc07c1
md5: c3258c31e507a81f0b52156bcca81e73
@@ -4933,6 +4929,31 @@ packages:
license: LicenseRef-Qhull
size: 552937
timestamp: 1720813982144
+- conda: https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.4.4-py312h762fea3_0.conda
+ sha256: 23ffbe589064e224b3ac69d05454062836971f57c4c734261c58dfb7b9d14f37
+ md5: df884dc5a76b2e2b2d13901f0d5d1668
+ depends:
+ - __glibc >=2.17,<3.0.a0
+ - affine
+ - attrs
+ - certifi
+ - click >=4,!=8.2.*
+ - click-plugins
+ - cligj >=0.5
+ - libgcc >=14
+ - libgdal-core <3.11
+ - libgdal-core >=3.10.3,<3.11.0a0
+ - libstdcxx >=14
+ - numpy >=1.23,<3
+ - proj >=9.7.1,<9.8.0a0
+ - python >=3.12,<3.13.0a0
+ - python_abi 3.12.* *_cp312
+ - setuptools >=0.9.8
+ - snuggs >=1.4.1
+ license: BSD-3-Clause
+ license_family: BSD
+ size: 7835544
+ timestamp: 1765553234963
- conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda
sha256: 3fc684b81631348540e9a42f6768b871dfeab532d3f47d5c341f1f83e2a2b2b2
md5: 66a715bc01c77d43aca1f9fcb13dde3c
@@ -5552,6 +5573,15 @@ packages:
license_family: LGPL
size: 631452
timestamp: 1758743294412
+- conda: https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda
+ sha256: 0deeaf0c001d5543719db9b2686bc1920c86c7e142f9bec74f35e1ce611b1fc2
+ md5: 8c4061f499edec6b8ac7000f6d586829
+ depends:
+ - python >=3.9
+ license: BSD-3-Clause
+ license_family: BSD
+ size: 19164
+ timestamp: 1733762153202
- conda: https://conda.anaconda.org/conda-forge/noarch/amply-0.1.6-pyhd8ed1ab_1.conda
sha256: e8d87cb66bcc62bc8d8168037b776de962ebf659e45acb1a813debde558f7339
md5: 5a81866192811f3a0827f5f93e589f02
@@ -5582,6 +5612,18 @@ packages:
license_family: MIT
size: 18074
timestamp: 1733247158254
+- conda: https://conda.anaconda.org/conda-forge/noarch/antimeridian-0.4.7-pyhd8ed1ab_0.conda
+ sha256: 56d4aa5e64b00989dd5b31d2e41af5ba2869775a1df7eb4e4155729c6bc1b9fa
+ md5: 56e9ab4ca5ec150ad52ca420f2cec646
+ depends:
+ - click >=8.1,=8.*
+ - numpy >=1.22.4
+ - python >=3.10
+ - shapely >=2.0
+ license: Apache-2.0
+ license_family: APACHE
+ size: 21300
+ timestamp: 1775308729010
- conda: https://conda.anaconda.org/conda-forge/noarch/appdirs-1.4.4-pyhd8ed1ab_1.conda
sha256: 5b9ef6d338525b332e17c3ed089ca2f53a5d74b7a7b432747d29c6466e39346d
md5: f4e90937bbfc3a4a92539545a37bb448
@@ -5827,6 +5869,26 @@ packages:
license_family: BSD
size: 100048
timestamp: 1777219902525
+- conda: https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda
+ sha256: ba1ee6e2b2be3da41d70d0d51d1159010de900aa3f33fceaea8c52e9bd30a26e
+ md5: e9b05deb91c013e5224672a4ba9cf8d1
+ depends:
+ - click >=4.0
+ - python >=3.9
+ license: BSD-3-Clause
+ license_family: BSD
+ size: 12683
+ timestamp: 1750848314962
+- conda: https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda
+ sha256: 1a52ae1febfcfb8f56211d1483a1ac4419b0028b7c3e9e61960a298978a42396
+ md5: 55c7804f428719241a90b152016085a1
+ depends:
+ - click >=4.0
+ - python >=3.9,<4.0
+ license: BSD-3-Clause
+ license_family: BSD
+ size: 12521
+ timestamp: 1733750069604
- conda: https://conda.anaconda.org/conda-forge/noarch/clio-tools-2026.03.30-pyhd8ed1ab_0.conda
sha256: a8b34e4bb8854ad899e461430fbabad81fa694805e45421bfe3e56da031d44ad
md5: 169bb144044321bb1cec1bfa4ea82a5b
@@ -6108,35 +6170,35 @@ packages:
license_family: MIT
size: 152965
timestamp: 1661419601518
-- conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.0.1-pyhd8ed1ab_3.conda
- sha256: 04f7e616ebbf6352ff852b53c57901e43f14e2b3c92411f99b5547f106bc192e
- md5: 1baca589eb35814a392eaad6d152447e
+- conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.3-pyhd8ed1ab_0.conda
+ sha256: c9ed18fb6270202299671f8075dd4f2fdff42220e4fd958e84629375769747f0
+ md5: 4eb8b870142ca06d2a1d2c74662eac7d
depends:
- folium
- - geopandas-base 1.0.1 pyha770c72_3
- - mapclassify >=2.4.0
+ - geopandas-base 1.1.3 pyha770c72_0
+ - mapclassify >=2.5.0
- matplotlib-base
- pyogrio >=0.7.2
- - pyproj >=3.3.0
- - python >=3.9
+ - pyproj >=3.5.0
+ - python >=3.10
- xyzservices
license: BSD-3-Clause
license_family: BSD
- size: 7583
- timestamp: 1734346218849
-- conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.0.1-pyha770c72_3.conda
- sha256: 2d031871b57c6d4e5e2d6cc23bd6d4e0084bb52ebca5c1b20bf06d03749e0f24
- md5: e8343d1b635bf09dafdd362d7357f395
+ size: 8761
+ timestamp: 1773131235020
+- conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.3-pyha770c72_0.conda
+ sha256: b07fc3edb5cb86df52081e5cb120a03a178767ed079b5d2cd313212351460620
+ md5: 18789a85c307970ae1786dfc6dfd234f
depends:
- - numpy >=1.22
+ - numpy >=1.24
- packaging
- - pandas >=1.4.0
- - python >=3.9
+ - pandas >=2.0.0
+ - python >=3.10
- shapely >=2.0.0
license: BSD-3-Clause
license_family: BSD
- size: 239261
- timestamp: 1734346217454
+ size: 254983
+ timestamp: 1773131233972
- conda: https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.12-pyhd8ed1ab_0.conda
sha256: dbbec21a369872c8ebe23cb9a3b9d63638479ee30face165aa0fccc96e93eec3
md5: 7c14f3706e099f8fcd47af2d494616cc
@@ -7338,6 +7400,17 @@ packages:
license_family: BSD
size: 26051
timestamp: 1739781801801
+- conda: https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda
+ sha256: 61f9373709e7d9009e3a062b135dbe44b16e684a4fcfe2dd624143bc0f80d402
+ md5: 9aa358575bbd4be126eaa5e0039f835c
+ depends:
+ - numpy
+ - pyparsing >=2.1.6
+ - python >=3.9
+ license: MIT
+ license_family: MIT
+ size: 11313
+ timestamp: 1733818738919
- conda: https://conda.anaconda.org/conda-forge/noarch/sqlmodel-0.0.37-pyhcf101f3_0.conda
sha256: 9cbf4805021fd817fde2654ccc1a1bd0352647614819a28381e81098efe4da20
md5: 00e6147bef9a85139099c9861c3b976b
@@ -8387,6 +8460,24 @@ packages:
license: LGPL-2.1-only
size: 1536680
timestamp: 1755851888781
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/geotiff-1.7.4-hf862be1_4.conda
+ sha256: 139909705857801b2cf4ff738a70c035365248a35583ba6ecab7c981ac5356da
+ md5: 111fe25c7b56f8e8f10322b4d99abe69
+ depends:
+ - proj
+ - zlib
+ - libjpeg-turbo
+ - libtiff
+ - __osx >=11.0
+ - libcxx >=19
+ - libzlib >=1.3.1,<2.0a0
+ - libjpeg-turbo >=3.1.0,<4.0a0
+ - libtiff >=4.7.0,<4.8.0a0
+ - proj >=9.7.0,<9.8.0a0
+ license: MIT
+ license_family: MIT
+ size: 128471
+ timestamp: 1757965588361
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda
sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20
md5: 57a511a5905caa37540eb914dfcbf1fb
@@ -9075,13 +9166,14 @@ packages:
license_family: BSD
size: 159247
timestamp: 1766331953491
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.11.4-h693e041_6.conda
- sha256: b10496eac3bfc9509009ea9e9ffd2fd4aea4409d780b0fc2c763c63e1656b14d
- md5: dbc028bb7adefb044f57a3d1ef7d5577
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.10.3-h694df76_24.conda
+ sha256: 30c224f677f1d2779b955434fef8c16b3b391e7461e18f4855053ffeb7a9d6f2
+ md5: 021a4f4c798ede069f985f6bbeb2bb88
depends:
- __osx >=11.0
- blosc >=1.21.6,<2.0a0
- geos >=3.14.0,<3.14.1.0a0
+ - geotiff >=1.7.4,<1.8.0a0
- giflib >=5.2.2,<5.3.0a0
- json-c >=0.18,<0.19.0a0
- lerc >=4.0.0,<5.0a0
@@ -9092,29 +9184,28 @@ packages:
- libexpat >=2.7.1,<3.0a0
- libiconv >=1.18,<2.0a0
- libjpeg-turbo >=3.1.0,<4.0a0
- - libjxl >=0.11,<0.12.0a0
- libkml >=1.3.0,<1.4.0a0
- liblzma >=5.8.1,<6.0a0
- libpng >=1.6.50,<1.7.0a0
- libspatialite >=5.1.0,<5.2.0a0
- libsqlite >=3.50.4,<4.0a0
+ - libtiff >=4.7.1,<4.8.0a0
- libwebp-base >=1.6.0,<2.0a0
- libxml2
- libxml2-16 >=2.14.6
- libzlib >=1.3.1,<2.0a0
- lz4-c >=1.10.0,<1.11.0a0
- - muparser >=2.3.5,<2.4.0a0
- openssl >=3.5.4,<4.0a0
- pcre2 >=10.46,<10.47.0a0
- proj >=9.7.0,<9.8.0a0
- xerces-c >=3.3.0,<3.4.0a0
- zstd >=1.5.7,<1.6.0a0
constrains:
- - libgdal 3.11.4.*
+ - libgdal 3.10.3.*
license: MIT
license_family: MIT
- size: 9273075
- timestamp: 1761262580865
+ size: 8497903
+ timestamp: 1761693365540
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda
sha256: 68a6c1384d209f8654112c4c57c68c540540dd8e09e17dd1facf6cf3467798b5
md5: 11e09edf0dde4c288508501fe621bab4
@@ -9228,15 +9319,6 @@ packages:
license_family: APACHE
size: 4820402
timestamp: 1774012715207
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-ha332bbd_0.conda
- sha256: 4fcad3cbec60da940312e883b7866816517acc5f9baecfe9a778de57327a1b1b
- md5: 7394850583ca88325244b68b532c7a39
- depends:
- - __osx >=11.0
- - libcxx >=19
- license: Apache-2.0 OR BSD-3-Clause
- size: 609931
- timestamp: 1776990524407
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda
sha256: de0336e800b2af9a40bdd694b03870ac4a848161b35c8a2325704f123f185f03
md5: 4d5a7445f0b25b6a3ddbb56e790f5251
@@ -9274,19 +9356,6 @@ packages:
license: IJG AND BSD-3-Clause AND Zlib
size: 555681
timestamp: 1775962975624
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.11.2-h934fa54_1.conda
- sha256: 948cf1370abb58e06a7c9554838c68672ef1d78e01c3fc4e62ccfc3072579645
- md5: 05bead8980f5ae6a070117dacec38b5b
- depends:
- - libcxx >=19
- - __osx >=11.0
- - libhwy >=1.4.0,<1.5.0a0
- - libbrotlienc >=1.2.0,<1.3.0a0
- - libbrotlidec >=1.2.0,<1.3.0a0
- license: BSD-3-Clause
- license_family: BSD
- size: 1032419
- timestamp: 1777065264956
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda
sha256: bfc6c0b1f30de524e3270eed2171d87e6b96552ff90cf2a5eb34c00d6bcb3dfa
md5: 3d8eeedb8e541d1cb593e8204fcc397d
@@ -9957,17 +10026,6 @@ packages:
license_family: Apache
size: 91725
timestamp: 1762504404391
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h11e0b38_0.conda
- sha256: 5533e7e3d4b0819b4426f8a1b3f680e6b9c922cdae2b7fabcd0e8c59df22772a
- md5: 1cdbe54881794ee356d3cba7e3ed6668
- depends:
- - __osx >=11.0
- - libcxx >=18
- - llvm-openmp >=18.1.8
- license: MIT
- license_family: MIT
- size: 154087
- timestamp: 1747117056226
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/mypy-1.19.1-py313hd3e6d80_0.conda
sha256: 7f52c1ede45433ae03b1ea4a45e1e6ed3fdf4f6e9e54d9ac05718205f49856e7
md5: dd6f5c085908e1945002e50d60c5c4d8
@@ -10425,13 +10483,13 @@ packages:
license_family: MIT
size: 1720475
timestamp: 1778084300413
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.11.1-py313hd8ca31c_1.conda
- sha256: a028079ee0884dd778a94840733f864e7336c12ca2182b46a33592242b3a59cc
- md5: d617721ce482cbba8c40f4a9872a2c4f
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.11.0-py313h4ad91d6_0.conda
+ sha256: 8fbeefdb9f67cd188c38cb50a9c1ffa360fd4c987666060e2cf7773eb38c54f6
+ md5: dafffca1d5d08b8966815eed08aece8a
depends:
- __osx >=11.0
- - libcxx >=19
- - libgdal-core >=3.11.3,<3.12.0a0
+ - libcxx >=18
+ - libgdal-core >=3.10.3,<3.11.0a0
- numpy
- packaging
- python >=3.13,<3.14.0a0
@@ -10439,8 +10497,8 @@ packages:
- python_abi 3.13.* *_cp313
license: MIT
license_family: MIT
- size: 570550
- timestamp: 1756824596862
+ size: 601750
+ timestamp: 1746734883266
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py313h6de5794_3.conda
sha256: 7a65aa08a4dd6060289a73d461a379ee8e6c183f1b6dd78e0c01332acec8f651
md5: 1f2ae983e8f36a664dbe220b8d1f7e97
@@ -10589,6 +10647,31 @@ packages:
license: LicenseRef-Qhull
size: 516376
timestamp: 1720814307311
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.4.4-py313hb3bd904_0.conda
+ sha256: fbbaf8378343197c30aea6d796847ac792e5f38e263383d6360d49122a33d611
+ md5: 699df60947da770aa55348cccfa4f082
+ depends:
+ - __osx >=11.0
+ - affine
+ - attrs
+ - certifi
+ - click >=4,!=8.2.*
+ - click-plugins
+ - cligj >=0.5
+ - libcxx >=19
+ - libgdal-core <3.11
+ - libgdal-core >=3.10.3,<3.11.0a0
+ - numpy >=1.23,<3
+ - proj >=9.7.1,<9.8.0a0
+ - python >=3.13,<3.14.0a0
+ - python >=3.13,<3.14.0a0 *_cp313
+ - python_abi 3.13.* *_cp313
+ - setuptools >=0.9.8
+ - snuggs >=1.4.1
+ license: BSD-3-Clause
+ license_family: BSD
+ size: 7141339
+ timestamp: 1765553472902
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda
sha256: 5bab972e8f2bff1b5b3574ffec8ecb89f7937578bd107584ed3fde507ff132f9
md5: a1ff22f664b0affa3de712749ccfbf04
@@ -11584,6 +11667,28 @@ packages:
license: LGPL-2.1-only
size: 1728594
timestamp: 1755852570331
+- conda: https://conda.anaconda.org/conda-forge/win-64/geotiff-1.7.4-h73469f5_4.conda
+ sha256: 5f01f23fc4447b130238668a40d1c7baa916f3ad175e5772c8482fc1f8c9e2e7
+ md5: 2a62961eeffe28d84c166600e4bf6e25
+ depends:
+ - proj
+ - zlib
+ - libjpeg-turbo
+ - libtiff
+ - vc >=14.3,<15
+ - vc14_runtime >=14.44.35208
+ - ucrt >=10.0.20348.0
+ - vc >=14.3,<15
+ - vc14_runtime >=14.44.35208
+ - ucrt >=10.0.20348.0
+ - libzlib >=1.3.1,<2.0a0
+ - libtiff >=4.7.0,<4.8.0a0
+ - proj >=9.7.0,<9.8.0a0
+ - libjpeg-turbo >=3.1.0,<4.0a0
+ license: MIT
+ license_family: MIT
+ size: 137535
+ timestamp: 1757965585058
- conda: https://conda.anaconda.org/conda-forge/win-64/getopt-win32-0.1-h6a83c73_3.conda
sha256: d04c4a6c11daa72c4a0242602e1d00c03291ef66ca2d7cd0e171088411d57710
md5: 49c36fcad2e9af6b91e91f2ce5be8ebd
@@ -12190,12 +12295,13 @@ packages:
license_family: BSD
size: 166711
timestamp: 1766331770351
-- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.11.4-h8a8bf46_6.conda
- sha256: 2602a1e0503dc2083524d8428d89ccc66e73ed09e706334c62e1fd85c9375eb2
- md5: fd3141a9ef5384b6ecad819ffe429378
+- conda: https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.10.3-h2f24e33_24.conda
+ sha256: e23a12478480266f7ac6bffbc22082f534c23e251dfec2923edfff9f60947b70
+ md5: 0489c36d03b99d3f71d9d0b4742f1620
depends:
- blosc >=1.21.6,<2.0a0
- geos >=3.14.0,<3.14.1.0a0
+ - geotiff >=1.7.4,<1.8.0a0
- lerc >=4.0.0,<5.0a0
- libarchive >=3.8.2,<3.9.0a0
- libcurl >=8.16.0,<9.0a0
@@ -12203,18 +12309,17 @@ packages:
- libexpat >=2.7.1,<3.0a0
- libiconv >=1.18,<2.0a0
- libjpeg-turbo >=3.1.0,<4.0a0
- - libjxl >=0.11,<0.12.0a0
- libkml >=1.3.0,<1.4.0a0
- liblzma >=5.8.1,<6.0a0
- libpng >=1.6.50,<1.7.0a0
- libspatialite >=5.1.0,<5.2.0a0
- libsqlite >=3.50.4,<4.0a0
+ - libtiff >=4.7.1,<4.8.0a0
- libwebp-base >=1.6.0,<2.0a0
- libxml2
- libxml2-16 >=2.14.6
- libzlib >=1.3.1,<2.0a0
- lz4-c >=1.10.0,<1.11.0a0
- - muparser >=2.3.5,<2.4.0a0
- openssl >=3.5.4,<4.0a0
- pcre2 >=10.46,<10.47.0a0
- proj >=9.7.0,<9.8.0a0
@@ -12224,11 +12329,11 @@ packages:
- xerces-c >=3.3.0,<3.4.0a0
- zstd >=1.5.7,<1.6.0a0
constrains:
- - libgdal 3.11.4.*
+ - libgdal 3.10.3.*
license: MIT
license_family: MIT
- size: 9195264
- timestamp: 1761261707518
+ size: 8467648
+ timestamp: 1761692955596
- conda: https://conda.anaconda.org/conda-forge/win-64/libglib-2.86.3-h0c9aed9_0.conda
sha256: 84b74fc81fff745f3d21a26c317ace44269a563a42ead3500034c27e407e1021
md5: c2d5b6b790ef21abac0b5331094ccb56
@@ -12351,16 +12456,6 @@ packages:
license_family: BSD
size: 2411241
timestamp: 1765104337762
-- conda: https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h172a326_0.conda
- sha256: 4b45bf59ee46d3c746272c27651da9ce709fda4eee8536c7424acea60d0e2ad0
- md5: aeca1cb6665f19e560c1fbd20b5bcf34
- depends:
- - ucrt >=10.0.20348.0
- - vc >=14.3,<15
- - vc14_runtime >=14.44.35208
- license: Apache-2.0 OR BSD-3-Clause
- size: 562583
- timestamp: 1776989522919
- conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda
sha256: 0dcdb1a5f01863ac4e8ba006a8b0dc1a02d2221ec3319b5915a1863254d7efa7
md5: 64571d1dd6cdcfa25d0664a5950fdaa2
@@ -12403,20 +12498,6 @@ packages:
license: IJG AND BSD-3-Clause AND Zlib
size: 842806
timestamp: 1775962811457
-- conda: https://conda.anaconda.org/conda-forge/win-64/libjxl-0.11.2-h932607e_1.conda
- sha256: 4715e22c602526c85da09f73865676add67e0995a944b821fbff84547a9db533
- md5: 327bce3eb1ef1875c7145e915d25bcd3
- depends:
- - vc >=14.3,<15
- - vc14_runtime >=14.44.35208
- - ucrt >=10.0.20348.0
- - libhwy >=1.4.0,<1.5.0a0
- - libbrotlienc >=1.2.0,<1.3.0a0
- - libbrotlidec >=1.2.0,<1.3.0a0
- license: BSD-3-Clause
- license_family: BSD
- size: 1194926
- timestamp: 1777065171989
- conda: https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda
sha256: 6b2c6506dc7d7bb3bc4128d575e4ae6c2cf18d3f9828a4be331e0b5e49edf108
md5: b44e0d9eb84c6c086d809787aab0a1da
@@ -13073,17 +13154,6 @@ packages:
license_family: Apache
size: 88214
timestamp: 1762504204957
-- conda: https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-he0c23c2_0.conda
- sha256: 57f78d8cd9a282d03cd7a7ffb1f42d570e1bbfb42d606e99de5c16e089067185
- md5: 013aabb169d59009bdf7d70319360e9b
- depends:
- - ucrt >=10.0.20348.0
- - vc >=14.2,<15
- - vc14_runtime >=14.29.30139
- license: MIT
- license_family: MIT
- size: 148557
- timestamp: 1747117340968
- conda: https://conda.anaconda.org/conda-forge/win-64/mypy-1.19.1-py313h5ea7bf4_0.conda
sha256: cd7b84c381f42944de2deabe98e3f79f39f9fe5eeb9d5a9d071b3476e686a3ac
md5: 1a933205527bdbc6ada5dab36347c993
@@ -13535,22 +13605,22 @@ packages:
license_family: MIT
size: 1888029
timestamp: 1778084253466
-- conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.11.1-py313h0dbd5a6_1.conda
- sha256: 0c37a6adf7f04180911bf46e676ca8ee0eefb5b3be76e872fd6854b953467e15
- md5: 7d1eaf4ed949aeb268394cf2857e20b5
+- conda: https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.11.0-py313h75b81ac_0.conda
+ sha256: a3294855e5682b79a5921d3749fa45286249f2c22782885c14903e6bae8cb507
+ md5: 7a3d31c8db0ac99557cb2faf51fc42b5
depends:
- - libgdal-core >=3.11.3,<3.12.0a0
+ - libgdal-core >=3.10.3,<3.11.0a0
- numpy
- packaging
- python >=3.13,<3.14.0a0
- python_abi 3.13.* *_cp313
- ucrt >=10.0.20348.0
- - vc >=14.3,<15
- - vc14_runtime >=14.44.35208
+ - vc >=14.2,<15
+ - vc14_runtime >=14.29.30139
license: MIT
license_family: MIT
- size: 817932
- timestamp: 1756824550436
+ size: 834342
+ timestamp: 1746735042949
- conda: https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py313hbf73894_3.conda
sha256: fa38aae6747307dd46747052988b54e2cd340c98b3b969065f52460d9e66b50c
md5: 779b40a8eb5e2aa5ffc5eddd3b136fb7
@@ -13735,6 +13805,31 @@ packages:
license: LicenseRef-Qhull
size: 1377020
timestamp: 1720814433486
+- conda: https://conda.anaconda.org/conda-forge/win-64/rasterio-1.4.4-py313ha5c5119_0.conda
+ sha256: 6891f73e7272ce530d36a5e01ade4c93b5e62133439a0b21bda9b431298f6a1a
+ md5: c094475643548d60959967b5be3638bb
+ depends:
+ - affine
+ - attrs
+ - certifi
+ - click >=4,!=8.2.*
+ - click-plugins
+ - cligj >=0.5
+ - libgdal-core <3.11
+ - libgdal-core >=3.10.3,<3.11.0a0
+ - numpy >=1.23,<3
+ - proj >=9.7.1,<9.8.0a0
+ - python >=3.13,<3.14.0a0
+ - python_abi 3.13.* *_cp313
+ - setuptools >=0.9.8
+ - snuggs >=1.4.1
+ - ucrt >=10.0.20348.0
+ - vc >=14.3,<15
+ - vc14_runtime >=14.44.35208
+ license: BSD-3-Clause
+ license_family: BSD
+ size: 7468329
+ timestamp: 1765553619782
- conda: https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-ha104f34_1.conda
sha256: 345b1ed8288d81510101f886aaf547e3294370e5dab340c4c3fcb0b25e5d99e0
md5: 6807f05dcf3f1736ad6cc9525b8b8725
diff --git a/pixi.toml b/pixi.toml
index fc18afa..c423cd1 100644
--- a/pixi.toml
+++ b/pixi.toml
@@ -59,7 +59,7 @@ channels = ["conda-forge"]
[feature.shape.dependencies]
boto3 = "1.40.46.*"
duckdb = "1.2.0.*"
-geopandas = "1.0.1.*"
+geopandas = "1.1.3.*"
ipdb = "0.13.13.*"
pandera-geopandas = "0.24.*"
pandera-io = "0.24.*"
@@ -70,6 +70,8 @@ numpy = "2.3.5.*"
pandas = "3.0.2.*"
pytz = "2026.2.*"
requests = "2.33.1.*"
+antimeridian = "0.4.7.*"
+rasterio = "1.4.4.*"
[environments]
shape = { features = ["shape"], no-default-feature = true }
diff --git a/workflow/Snakefile b/workflow/Snakefile
index 44429ff..6ff7a30 100644
--- a/workflow/Snakefile
+++ b/workflow/Snakefile
@@ -38,6 +38,7 @@ include: "rules/build.smk"
# Add all files to be delivered alongside the workflow here
+workflow.source_path("scripts/_geo.py")
workflow.source_path("scripts/_schemas.py")
workflow.source_path("scripts/_utils.py")
diff --git a/workflow/envs/shape.linux-64.pin.txt b/workflow/envs/shape.linux-64.pin.txt
index a434d88..cdcaf4b 100644
--- a/workflow/envs/shape.linux-64.pin.txt
+++ b/workflow/envs/shape.linux-64.pin.txt
@@ -23,20 +23,24 @@ https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda#4a13
https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda#18335a698559cdbcd86150a48bf54ba6
https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda#d2ffd7602c02f2b316fd921d39876885
https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda#7eccb41177e15cc672e1babe9056018e
-https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda#03cb60f505ad3ada0a95277af5faeb1a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd
-https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223
+https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5
https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda#c3efd25ac4d74b1584d2f7a57195ddf1
https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda#5794b3bdc38177caf969dabd3af08549
-https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda#98b6c9dc80eb87b2519b97bcf7e578dd
-https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda#6f7b4302263347698fd24565fbf11310
-https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda#ced7f10b6cfb4389385556f47c0ad949
-https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda#66a715bc01c77d43aca1f9fcb13dde3c
-https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda#9de5350a85c4a20c685259b889aa6393
-https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h2b00c02_0.conda#11ac478fa72cf12c214199b8a96523f4
-https://conda.anaconda.org/conda-forge/linux-64/orc-2.2.2-hbb90d81_1.conda#9269175175f18091b8844c8e9f213205
-https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda#1247168fe4a0b8912e3336bccdbf98a5
-https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda#c2a01a08fc991620a74b32420e97868a
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda#85072b0ad177c966294f129b7c04a2d5
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda#42bf7eca1a951735fa06c0e3c0d5c8e6
+https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda#89d61bc91d3f39fda0ca10fcd3c68594
+https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda#6d6d225559bfa6e2f3c90ee9c03d4e2e
+https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-6_h47877c9_openblas.conda#881d801569b201c2e753f03c84b85e15
+https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda#36ae340a916635b97ac8a0655ace2a35
+https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_1.conda#03baecffb72fa96fe234fd505908065f
+https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c
+https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd
+https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.1-hbc0de68_0.conda#8e0b8654ead18e50af552e54b5a08a61
+https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9
+https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda#6178c6f2fb254558238ef4e6c56fb782
+https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda#6c77a605a7a689d17d4819c0f8ac9a00
+https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda#a752488c68f2e7c456bcbd8f16eec275
+https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda#cd5a90476766d53e901500df9215e927
https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda#eecce068c7e4eddeb169591baac20ac4
https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda#172bf1cd1ff8629f2b1179945ed45055
https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda#920bb03579f15389b9e512095ad995b7
@@ -45,13 +49,54 @@ https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949
https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda#fb53fb07ce46a575c5d004bbc96032c2
https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.20.0-hcf29cc6_0.conda#c3cc2864f82a944bc90a7beb4d3b0e88
+https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.1-he0df7b0_3.conda#031e33ae075b336c0ce92b14efa886c5
+https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda#c80d8a3b84358cb967fa81e7075fbc8a
+https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-hd9031aa_1.conda#66a1db55ecdb7377d2b91f54cd56eafa
+https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda#7fa07cb0fb1b625a089ccc01218ee5b1
+https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda#9de5350a85c4a20c685259b889aa6393
+https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda#915f5995e94f60e9a4826e0b0920ee88
+https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda#e79d2c2f24b027aa8d5ab1b1ba3061e7
+https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda#995d8c8bad2a3cc8db14675a153dec2b
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.2-h25fd6f3_2.conda#c2a01a08fc991620a74b32420e97868a
+https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_0.conda#be70cb50dd0ecc1531c58034d38f72e0
+https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.0-h480dda7_0.conda#5dc479effdabf54a0ff240d565287495
+https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h96cd706_19.conda#212a9378a85ad020b8dc94853fdbeb6c
+https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda#fe7b4ff5792fee512aad843294ea809a
+https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda#ecb5d11305b8ba1801543002e69d2f2f
+https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hcb59c51_118.conda#ce07b32efdf860ed996fdedcff3ea96e
+https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda#eba48a68a1a2b9d3c0d9511548db85db
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda#e5ce228e579726c07255dbf90dc62101
+https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda#d71d3a66528853c0a1ac2c02d79a0284
+https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda#953b7cca897e21215302dbfe2af5cd0c
+https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda#45161d96307e3a447cc3eb5896cf6f8c
+https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.7-gpl_hc2c16d8_100.conda#dbeb5c8321cb2408d406a3da16a0ff0d
+https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda#38f5dbc9ac808e31c00650f7be1db93f
+https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda#3bf7b9fd5a7136126e0234db4b87c8b6
+https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.4-h1000f5c_4.conda#ff1966654a6cd1cf06a6e44c13e60b8a
+https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda#98b6c9dc80eb87b2519b97bcf7e578dd
+https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda#2c2fae981fd2afd00812c92ac47d023d
+https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.10.3-h05c3bbc_24.conda#a69445ccff7ec3d60fe14110bd4d86ae
+https://conda.anaconda.org/conda-forge/noarch/click-8.3.3-pyhc90fa1f_0.conda#2266262ce8a425ecb6523d765f79b303
+https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1
+https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1
+https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda#929471569c93acefb30282a22060dcd5
+https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2
+https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda#8c4061f499edec6b8ac7000f6d586829
+https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.4.4-py312h762fea3_0.conda#df884dc5a76b2e2b2d13901f0d5d1668
+https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda#03cb60f505ad3ada0a95277af5faeb1a
+https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223
+https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda#6f7b4302263347698fd24565fbf11310
+https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda#ced7f10b6cfb4389385556f47c0ad949
+https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda#66a715bc01c77d43aca1f9fcb13dde3c
+https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h2b00c02_0.conda#11ac478fa72cf12c214199b8a96523f4
+https://conda.anaconda.org/conda-forge/linux-64/orc-2.2.2-hbb90d81_1.conda#9269175175f18091b8844c8e9f213205
+https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda#1247168fe4a0b8912e3336bccdbf98a5
https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda#a83f6a2fdc079e643237887a37460668
https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda#16c2a0e9c4a166e53632cfca4f68d020
https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.21.0-ha770c72_2.conda#253e70376a8ae74f9d99d44712b3e087
https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda#b5fb6d6c83f63d83ef2721dca6ff7091
https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.21.0-h9692893_2.conda#c3de1cc30bc11edbc98aed352381449d
https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-h9d11ab5_1.conda#cd398eb8374fb626a710b7a35b7ffa98
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda#e5ce228e579726c07255dbf90dc62101
https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2#c965a5aa0d5c1c37ffc62dff36e28400
https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_1.conda#384a1730ea66a72692e377cb45996d61
https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda#72c8fd1af66bd67bf580645b426513ed
@@ -59,10 +104,6 @@ https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.co
https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda#366b40a69f0ad6072561c1d09301c886
https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda#d411fc29e338efb48c5fd4576d71d881
https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda#ff862eebdfeb2fd048ae9dc92510baca
-https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda#915f5995e94f60e9a4826e0b0920ee88
-https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda#c80d8a3b84358cb967fa81e7075fbc8a
-https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda#e79d2c2f24b027aa8d5ab1b1ba3061e7
-https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda#995d8c8bad2a3cc8db14675a153dec2b
https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.2-h206d751_0.conda#5492abf806c45298ae642831c670bba0
https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.12.0-ha7a2c86_1.conda#6400f73fe5ebe19fe7aca3616f1f1de7
https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.16.0-hdd73cc9_1.conda#939d9ce324e51961c7c4c0046733dbb7
@@ -108,13 +149,6 @@ https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#
https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.24.0-pyhd8ed1ab_2.conda#e1c50e117a98e39d297d9290132f032b
https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda#85072b0ad177c966294f129b7c04a2d5
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda#42bf7eca1a951735fa06c0e3c0d5c8e6
-https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda#89d61bc91d3f39fda0ca10fcd3c68594
-https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda#6d6d225559bfa6e2f3c90ee9c03d4e2e
-https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-6_h47877c9_openblas.conda#881d801569b201c2e753f03c84b85e15
-https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda#36ae340a916635b97ac8a0655ace2a35
-https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_1.conda#03baecffb72fa96fe234fd505908065f
https://conda.anaconda.org/conda-forge/linux-64/pandas-3.0.2-py312h8ecdadd_0.conda#42050f82a0c0f6fa23eda3d93b251c18
https://conda.anaconda.org/conda-forge/noarch/pandera-0.24.0-hd8ed1ab_2.conda#a12a21a89519f0cc224e44da86f5be2c
https://conda.anaconda.org/conda-forge/noarch/validators-0.35.0-pyhd8ed1ab_0.conda#3449ef730c7d483adde81993994092b9
@@ -123,7 +157,6 @@ https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda#592132998493b3ff25fd7479396e8351
https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda#6d03368f2b2b0a5fb6839df53b2eb5e0
https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda#0242025a3c804966bf71aa04eee82f66
-https://conda.anaconda.org/conda-forge/noarch/click-8.3.3-pyhc90fa1f_0.conda#2266262ce8a425ecb6523d765f79b303
https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda#52be5139047efadaeeb19c6a5103f92a
https://conda.anaconda.org/conda-forge/noarch/typer-0.25.1-pyhcf101f3_0.conda#ef114c2eb2ff19f6bf616c81f4710841
https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda#3b887b7b3468b0f494b4fad40178b043
@@ -139,14 +172,12 @@ https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.4.0-py312h90b7f
https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda#cbb88288f74dbe6ada1c6c7d0a97223e
https://conda.anaconda.org/conda-forge/noarch/idna-3.13-pyhcf101f3_0.conda#fb7130c190f9b4ec91219840a05ba3ac
https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda#a9167b9571f3baa9d448faa2139d1089
-https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda#929471569c93acefb30282a22060dcd5
https://conda.anaconda.org/conda-forge/noarch/requests-2.33.1-pyhcf101f3_1.conda#9659f587a8ceacc21864260acd02fc67
https://conda.anaconda.org/conda-forge/noarch/text-unidecode-1.3-pyhd8ed1ab_2.conda#23b4ba5619c4752976eb7ba1f5acb7e8
https://conda.anaconda.org/conda-forge/noarch/python-slugify-8.0.4-pyhd8ed1ab_1.conda#a4059bc12930bddeb41aef71537ffaed
https://conda.anaconda.org/conda-forge/noarch/petl-1.7.17-pyhd8ed1ab_0.conda#4c2498dcda0d58cf25466e82f7287b32
https://conda.anaconda.org/conda-forge/noarch/marko-2.2.2-pyhd8ed1ab_0.conda#3c3e9339e46fffba5920be28d9233860
https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py312h868fb18_0.conda#3ffc5a3572db8751c2f15bacf6a0e937
-https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2
https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda#ada41c863af263cc4c5fcbaff7c3e4dc
@@ -160,39 +191,11 @@ https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.co
https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda#11adc78451c998c0fd162584abfa3559
https://conda.anaconda.org/conda-forge/noarch/black-26.3.1-pyh866005b_0.conda#c7e43448266209d766a229cada982884
https://conda.anaconda.org/conda-forge/noarch/pandera-io-0.24.0-hd8ed1ab_2.conda#d456360daad56c802e27504979ef1e19
-https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.0-h480dda7_0.conda#5dc479effdabf54a0ff240d565287495
https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.1-py312h950be2a_2.conda#6333340e60402c7456811a14e4e12b26
https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc
-https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.1-hbc0de68_0.conda#8e0b8654ead18e50af552e54b5a08a61
-https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9
-https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda#6178c6f2fb254558238ef4e6c56fb782
-https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda#6c77a605a7a689d17d4819c0f8ac9a00
-https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda#a752488c68f2e7c456bcbd8f16eec275
-https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda#cd5a90476766d53e901500df9215e927
-https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.1-he0df7b0_3.conda#031e33ae075b336c0ce92b14efa886c5
https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312he675c61_3.conda#c3258c31e507a81f0b52156bcca81e73
-https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-hd9031aa_1.conda#66a1db55ecdb7377d2b91f54cd56eafa
-https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda#7fa07cb0fb1b625a089ccc01218ee5b1
-https://conda.anaconda.org/conda-forge/linux-64/muparser-2.3.5-h5888daf_0.conda#ab3e3db511033340e75e7002e80ce8c0
-https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.3-h49c6c72_0.conda#be70cb50dd0ecc1531c58034d38f72e0
-https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h96cd706_19.conda#212a9378a85ad020b8dc94853fdbeb6c
-https://conda.anaconda.org/conda-forge/linux-64/minizip-4.2.1-hb71707f_0.conda#fe7b4ff5792fee512aad843294ea809a
-https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda#ecb5d11305b8ba1801543002e69d2f2f
-https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hcb59c51_118.conda#ce07b32efdf860ed996fdedcff3ea96e
-https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda#eba48a68a1a2b9d3c0d9511548db85db
-https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda#d71d3a66528853c0a1ac2c02d79a0284
-https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1023.conda#953b7cca897e21215302dbfe2af5cd0c
-https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.4.0-h10be129_0.conda#3a9428b74c403c71048104d38437b48c
-https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.2-h174a0a3_1.conda#850f48943d6b4589800a303f0de6a816
-https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda#45161d96307e3a447cc3eb5896cf6f8c
-https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.7-gpl_hc2c16d8_100.conda#dbeb5c8321cb2408d406a3da16a0ff0d
-https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda#38f5dbc9ac808e31c00650f7be1db93f
-https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda#3bf7b9fd5a7136126e0234db4b87c8b6
-https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda#2c2fae981fd2afd00812c92ac47d023d
-https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.11.4-h6c36cd4_6.conda#c6c5d093e8a60acdd93f731cc3593c97
-https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.11.1-py312h6e8b602_1.conda#71e2dd5aa884ab062c2d41fe10f9cefe
+https://conda.anaconda.org/conda-forge/linux-64/pyogrio-0.11.0-py312h02b19dd_0.conda#b4e4e057ab327b7a1270612587a75523
https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6
-https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5
https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda#2aadb0d17215603a82a2a6b0afd9a4cb
https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda#1dafce8548e38671bea82e3f5c6ce22f
@@ -219,10 +222,10 @@ https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#61
https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda#38decbeae260892040709cafc0514162
https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b
https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda#cc293b4cad9909bf66ca117ea90d4631
-https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.0.1-pyha770c72_3.conda#e8343d1b635bf09dafdd362d7357f395
+https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.3-pyha770c72_0.conda#18789a85c307970ae1786dfc6dfd234f
https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda#1fcdf88e7a8c296d3df8409bf0690db4
https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda#a6997a7dcd6673c0692c61dfeaea14ab
-https://conda.anaconda.org/conda-forge/noarch/geopandas-1.0.1-pyhd8ed1ab_3.conda#1baca589eb35814a392eaad6d152447e
+https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.3-pyhd8ed1ab_0.conda#4eb8b870142ca06d2a1d2c74662eac7d
https://conda.anaconda.org/conda-forge/noarch/pandera-geopandas-0.24.0-hd8ed1ab_2.conda#fb21509f073465506ea994dc4667bf66
https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda#d0fc809fa4c4d85e959ce4ab6e1de800
https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.0-pyhcf101f3_0.conda#4bada6a6d908a27262af8ebddf4f7492
@@ -248,3 +251,4 @@ https://conda.anaconda.org/conda-forge/noarch/jmespath-1.1.0-pyhcf101f3_1.conda#
https://conda.anaconda.org/conda-forge/noarch/botocore-1.40.76-pyhd8ed1ab_0.conda#4babebd5361bc44503358a519a81a465
https://conda.anaconda.org/conda-forge/noarch/s3transfer-0.14.0-pyhd8ed1ab_0.conda#4e3089ce93822a25408c480dd53561b6
https://conda.anaconda.org/conda-forge/noarch/boto3-1.40.46-pyhd8ed1ab_0.conda#a93057bba32eef7b217733734642bfea
+https://conda.anaconda.org/conda-forge/noarch/antimeridian-0.4.7-pyhd8ed1ab_0.conda#56e9ab4ca5ec150ad52ca420f2cec646
diff --git a/workflow/envs/shape.osx-arm64.pin.txt b/workflow/envs/shape.osx-arm64.pin.txt
index eff4525..b2dc58f 100644
--- a/workflow/envs/shape.osx-arm64.pin.txt
+++ b/workflow/envs/shape.osx-arm64.pin.txt
@@ -16,20 +16,27 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda#4
https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.0-hf6b4638_0.conda#65466e82c09e888ca7560c11a97d5450
https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda#620b85a3f45526a8bc4d23fd78fc22f0
https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.13-h20e6be0_100_cp313.conda#9991a930e81d3873eba7a299ba783ec4
-https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda#03cb60f505ad3ada0a95277af5faeb1a
-https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd
-https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223
+https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5
+https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.4-hc7d1edf_0.conda#46d04a647df7a4525e487d88068d19ef
+https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda#a44032f282e7d2acdeb1c240308052dd
+https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda#644058123986582db33aebd4ae2ca184
+https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda#ba36d8c606a6a53fe0b8c12d47267b3d
+https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda#1ea03f87cdb1078fbc0e2b2deb63752c
+https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.32-openmp_he657e61_0.conda#3a1111a4b6626abebe8b978bb5a323bf
+https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-6_h51639a9_openblas.conda#e551103471911260488a02155cef9c94
+https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-6_hd9741b5_openblas.conda#ee33d2d05a7c5ea1f67653b37eb74db1
https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.5-h55c6f16_1.conda#ff484b683fecf1e875dfc7aa01d19796
+https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-6_hb0561ab_openblas.conda#805c6d31c5621fd75e53dfcf21fb243a
+https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py313h16eae64_1.conda#c72599556b49dc853839f4439c1eea32
+https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c
+https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd
+https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.1-h85ec8f2_0.conda#f7a7a885f173730179da53e02b98ca93
https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda#ab136e4c34e97f34fb621d2592a393d8
-https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda#fca4a2222994acd7f691e57f94b750c5
-https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda#bb65152e0d7c7178c0f1ee25692c9fd1
-https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda#40d8ad21be4ccfff83a314076c3563f4
-https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda#a1ff22f664b0affa3de712749ccfbf04
-https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda#01511afc6cc1909c5303cf31be17b44f
-https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h4a5acfd_0.conda#b839e3295b66434f20969c8b940f056a
-https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.2.2-h578b684_1.conda#5ed1fedefe1098670f8d8e8189dcda7c
-https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda#2255add2f6ae77d0a96624a5cbde6d45
-https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda#f1c0bce276210bed45a04949cfe8dc20
+https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda#e5e7d467f80da752be17796b87fe6385
+https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda#b8a7544c83a67258b0e8592ec6a5d322
+https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda#a6130c709305cd9828b4e1bd9ba0000c
+https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda#095e5749868adab9cae42d4b460e5443
+https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda#e2a72ab2fa54ecb6abab2b26cde93500
https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda#b68e8f66b94b44aaa8de4583d3d4cc40
https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda#36d33e440c31857372a72137f78bacf5
https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda#bcb3cba70cf1eec964a03b4ba7775f01
@@ -37,6 +44,47 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.co
https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda#44083d2d2c2025afca315c7a172eab2b
https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda#e446e1822f4da8e5080a9de93474184d
https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda#2f57b7d0c6adda88957586b7afd78438
+https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.7.1-hfb14a63_3.conda#8f33a4a2b856de0e8f006c489beca62a
+https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda#f1182c91c0de31a7abd40cedf6a5ebef
+https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h25f632f_1.conda#0b886d06130b774f086d3b2ce0b7277a
+https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.46-h7125dd6_0.conda#0e6e82c3cc3835f4692022e9b9cd5df8
+https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda#01511afc6cc1909c5303cf31be17b44f
+https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda#4d5a7445f0b25b6a3ddbb56e790f5251
+https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda#19edaa53885fc8205614b03da2482282
+https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.conda#8e037d73747d6fe34e12d7bcac10cf21
+https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.2-h8088a28_2.conda#f1c0bce276210bed45a04949cfe8dc20
+https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_0.conda#469f4af737803bf7deda25d41c557593
+https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.0-h4bcf65f_0.conda#adc2b527bb017db48415a1b243abcb68
+https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-hf7cb3ef_19.conda#093cc30c0744bf29a51209fd50543186
+https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda#fcd860469a8fa003e25d472b40b0ff81
+https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda#dd655a29b40fe0d1bf95c64cf3cb348d
+https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_he9e465b_118.conda#2f7972081a15d940ee21708828773a72
+https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda#2259ae0949dbe20c0665850365109b27
+https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda#e8ff9e11babbc8cd77af5a4258dc2802
+https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda#3d8eeedb8e541d1cb593e8204fcc397d
+https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda#e56eaa1beab0e7fed559ae9c0264dd88
+https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.7-gpl_h6fbacd7_100.conda#014dc9c74fef186581342c4844591ac6
+https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda#94f14ef6157687c30feb44e1abecd577
+https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda#95fa1486c77505330c20f7202492b913
+https://conda.anaconda.org/conda-forge/osx-arm64/geotiff-1.7.4-hf862be1_4.conda#111fe25c7b56f8e8f10322b4d99abe69
+https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda#fca4a2222994acd7f691e57f94b750c5
+https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda#925acfb50a750aa178f7a0aced77f351
+https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.10.3-h694df76_24.conda#021a4f4c798ede069f985f6bbeb2bb88
+https://conda.anaconda.org/conda-forge/noarch/click-8.3.3-pyhc90fa1f_0.conda#2266262ce8a425ecb6523d765f79b303
+https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1
+https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1
+https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda#929471569c93acefb30282a22060dcd5
+https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2
+https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda#8c4061f499edec6b8ac7000f6d586829
+https://conda.anaconda.org/conda-forge/osx-arm64/rasterio-1.4.4-py313hb3bd904_0.conda#699df60947da770aa55348cccfa4f082
+https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda#03cb60f505ad3ada0a95277af5faeb1a
+https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223
+https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda#bb65152e0d7c7178c0f1ee25692c9fd1
+https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda#40d8ad21be4ccfff83a314076c3563f4
+https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda#a1ff22f664b0affa3de712749ccfbf04
+https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h4a5acfd_0.conda#b839e3295b66434f20969c8b940f056a
+https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.2.2-h578b684_1.conda#5ed1fedefe1098670f8d8e8189dcda7c
+https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.3-h2431656_0.conda#2255add2f6ae77d0a96624a5cbde6d45
https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda#7172339b49c94275ba42fec3eaeda34f
https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda#755cfa6c08ed7b7acbee20ccbf15a47c
https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.21.0-hce30654_2.conda#d1adb8f085e35aa6335c2a4e6f025fb6
@@ -50,10 +98,6 @@ https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.c
https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda#079e88933963f3f149054eec2c487bc2
https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda#57a511a5905caa37540eb914dfcbf1fb
https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda#fef68d0a95aa5b84b5c1a4f6f3bf40e1
-https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda#4d5a7445f0b25b6a3ddbb56e790f5251
-https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda#f1182c91c0de31a7abd40cedf6a5ebef
-https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.3-h5ef1a60_0.conda#19edaa53885fc8205614b03da2482282
-https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.3-h5654f7c_0.conda#8e037d73747d6fe34e12d7bcac10cf21
https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.2-he5ae378_0.conda#7efe92d28599c224a24de11bb14d395e
https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.12.0-he467506_1.conda#b658a3fb0fc412b2a4d30da3fcec036f
https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.16.0-hc57151b_1.conda#f08b3b9d7333dc427b79897e6e3e7f29
@@ -98,16 +142,6 @@ https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda#
https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.24.0-pyhd8ed1ab_2.conda#e1c50e117a98e39d297d9290132f032b
https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
-https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.4-hc7d1edf_0.conda#46d04a647df7a4525e487d88068d19ef
-https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda#a44032f282e7d2acdeb1c240308052dd
-https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda#644058123986582db33aebd4ae2ca184
-https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda#ba36d8c606a6a53fe0b8c12d47267b3d
-https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda#1ea03f87cdb1078fbc0e2b2deb63752c
-https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.32-openmp_he657e61_0.conda#3a1111a4b6626abebe8b978bb5a323bf
-https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-6_h51639a9_openblas.conda#e551103471911260488a02155cef9c94
-https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-6_hd9741b5_openblas.conda#ee33d2d05a7c5ea1f67653b37eb74db1
-https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-6_hb0561ab_openblas.conda#805c6d31c5621fd75e53dfcf21fb243a
-https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py313h16eae64_1.conda#c72599556b49dc853839f4439c1eea32
https://conda.anaconda.org/conda-forge/osx-arm64/pandas-3.0.2-py313h1188861_0.conda#13410787da0135eec56a4bb8d674fc42
https://conda.anaconda.org/conda-forge/noarch/pandera-0.24.0-hd8ed1ab_2.conda#a12a21a89519f0cc224e44da86f5be2c
https://conda.anaconda.org/conda-forge/noarch/validators-0.35.0-pyhd8ed1ab_0.conda#3449ef730c7d483adde81993994092b9
@@ -116,7 +150,6 @@ https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda#592132998493b3ff25fd7479396e8351
https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda#6d03368f2b2b0a5fb6839df53b2eb5e0
https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda#0242025a3c804966bf71aa04eee82f66
-https://conda.anaconda.org/conda-forge/noarch/click-8.3.3-pyhc90fa1f_0.conda#2266262ce8a425ecb6523d765f79b303
https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda#52be5139047efadaeeb19c6a5103f92a
https://conda.anaconda.org/conda-forge/noarch/typer-0.25.1-pyhcf101f3_0.conda#ef114c2eb2ff19f6bf616c81f4710841
https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda#3b887b7b3468b0f494b4fad40178b043
@@ -132,14 +165,12 @@ https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.4.0-py313h7208
https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda#cbb88288f74dbe6ada1c6c7d0a97223e
https://conda.anaconda.org/conda-forge/noarch/idna-3.13-pyhcf101f3_0.conda#fb7130c190f9b4ec91219840a05ba3ac
https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda#a9167b9571f3baa9d448faa2139d1089
-https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda#929471569c93acefb30282a22060dcd5
https://conda.anaconda.org/conda-forge/noarch/requests-2.33.1-pyhcf101f3_1.conda#9659f587a8ceacc21864260acd02fc67
https://conda.anaconda.org/conda-forge/noarch/text-unidecode-1.3-pyhd8ed1ab_2.conda#23b4ba5619c4752976eb7ba1f5acb7e8
https://conda.anaconda.org/conda-forge/noarch/python-slugify-8.0.4-pyhd8ed1ab_1.conda#a4059bc12930bddeb41aef71537ffaed
https://conda.anaconda.org/conda-forge/noarch/petl-1.7.17-pyhd8ed1ab_0.conda#4c2498dcda0d58cf25466e82f7287b32
https://conda.anaconda.org/conda-forge/noarch/marko-2.2.2-pyhd8ed1ab_0.conda#3c3e9339e46fffba5920be28d9233860
https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.30.0-py313h2c089d5_0.conda#190c2d0d4e98ec97df48cdb74caf44d8
-https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2
https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda#ada41c863af263cc4c5fcbaff7c3e4dc
@@ -153,39 +184,11 @@ https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.co
https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda#11adc78451c998c0fd162584abfa3559
https://conda.anaconda.org/conda-forge/noarch/black-26.3.1-pyh866005b_0.conda#c7e43448266209d766a229cada982884
https://conda.anaconda.org/conda-forge/noarch/pandera-io-0.24.0-hd8ed1ab_2.conda#d456360daad56c802e27504979ef1e19
-https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.14.0-h4bcf65f_0.conda#adc2b527bb017db48415a1b243abcb68
https://conda.anaconda.org/conda-forge/osx-arm64/shapely-2.1.1-py313hfb5a6ed_2.conda#fe3b80b75f6ca392c17e9d51d9c527d6
https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc
-https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.1-h85ec8f2_0.conda#f7a7a885f173730179da53e02b98ca93
-https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda#e5e7d467f80da752be17796b87fe6385
-https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda#b8a7544c83a67258b0e8592ec6a5d322
-https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda#a6130c709305cd9828b4e1bd9ba0000c
-https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda#095e5749868adab9cae42d4b460e5443
-https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda#e2a72ab2fa54ecb6abab2b26cde93500
-https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.7.1-hfb14a63_3.conda#8f33a4a2b856de0e8f006c489beca62a
https://conda.anaconda.org/conda-forge/osx-arm64/pyproj-3.7.2-py313h6de5794_3.conda#1f2ae983e8f36a664dbe220b8d1f7e97
-https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.3.0-h25f632f_1.conda#0b886d06130b774f086d3b2ce0b7277a
-https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.46-h7125dd6_0.conda#0e6e82c3cc3835f4692022e9b9cd5df8
-https://conda.anaconda.org/conda-forge/osx-arm64/muparser-2.3.5-h11e0b38_0.conda#1cdbe54881794ee356d3cba7e3ed6668
-https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-devel-2.15.3-h5654f7c_0.conda#469f4af737803bf7deda25d41c557593
-https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-hf7cb3ef_19.conda#093cc30c0744bf29a51209fd50543186
-https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.2.1-hdb7fadc_0.conda#fcd860469a8fa003e25d472b40b0ff81
-https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda#dd655a29b40fe0d1bf95c64cf3cb348d
-https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-gpl_he9e465b_118.conda#2f7972081a15d940ee21708828773a72
-https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda#2259ae0949dbe20c0665850365109b27
-https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda#e8ff9e11babbc8cd77af5a4258dc2802
-https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-hb833057_1023.conda#3d8eeedb8e541d1cb593e8204fcc397d
-https://conda.anaconda.org/conda-forge/osx-arm64/libhwy-1.4.0-ha332bbd_0.conda#7394850583ca88325244b68b532c7a39
-https://conda.anaconda.org/conda-forge/osx-arm64/libjxl-0.11.2-h934fa54_1.conda#05bead8980f5ae6a070117dacec38b5b
-https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h925e9cb_1002.conda#e56eaa1beab0e7fed559ae9c0264dd88
-https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.8.7-gpl_h6fbacd7_100.conda#014dc9c74fef186581342c4844591ac6
-https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda#94f14ef6157687c30feb44e1abecd577
-https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda#95fa1486c77505330c20f7202492b913
-https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda#925acfb50a750aa178f7a0aced77f351
-https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.11.4-h693e041_6.conda#dbc028bb7adefb044f57a3d1ef7d5577
-https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.11.1-py313hd8ca31c_1.conda#d617721ce482cbba8c40f4a9872a2c4f
+https://conda.anaconda.org/conda-forge/osx-arm64/pyogrio-0.11.0-py313h4ad91d6_0.conda#dafffca1d5d08b8966815eed08aece8a
https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda#6483b1f59526e05d7d894e466b5b6924
-https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5
https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda#d99c2a23a31b0172e90f456f580b695e
https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda#4b5d3a91320976eec71678fad1e3569b
https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda#9d1299ace1924aa8f4e0bc8e71dd0cf7
@@ -211,10 +214,10 @@ https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#61
https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py313h3b23316_1.conda#4434adab69e6300db1e98aff4c3565f3
https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b
https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda#cc293b4cad9909bf66ca117ea90d4631
-https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.0.1-pyha770c72_3.conda#e8343d1b635bf09dafdd362d7357f395
+https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.3-pyha770c72_0.conda#18789a85c307970ae1786dfc6dfd234f
https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda#1fcdf88e7a8c296d3df8409bf0690db4
https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda#a6997a7dcd6673c0692c61dfeaea14ab
-https://conda.anaconda.org/conda-forge/noarch/geopandas-1.0.1-pyhd8ed1ab_3.conda#1baca589eb35814a392eaad6d152447e
+https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.3-pyhd8ed1ab_0.conda#4eb8b870142ca06d2a1d2c74662eac7d
https://conda.anaconda.org/conda-forge/noarch/pandera-geopandas-0.24.0-hd8ed1ab_2.conda#fb21509f073465506ea994dc4667bf66
https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda#d0fc809fa4c4d85e959ce4ab6e1de800
https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.0-pyhcf101f3_0.conda#4bada6a6d908a27262af8ebddf4f7492
@@ -240,3 +243,4 @@ https://conda.anaconda.org/conda-forge/noarch/jmespath-1.1.0-pyhcf101f3_1.conda#
https://conda.anaconda.org/conda-forge/noarch/botocore-1.40.76-pyhd8ed1ab_0.conda#4babebd5361bc44503358a519a81a465
https://conda.anaconda.org/conda-forge/noarch/s3transfer-0.14.0-pyhd8ed1ab_0.conda#4e3089ce93822a25408c480dd53561b6
https://conda.anaconda.org/conda-forge/noarch/boto3-1.40.46-pyhd8ed1ab_0.conda#a93057bba32eef7b217733734642bfea
+https://conda.anaconda.org/conda-forge/noarch/antimeridian-0.4.7-pyhd8ed1ab_0.conda#56e9ab4ca5ec150ad52ca420f2cec646
diff --git a/workflow/envs/shape.win-64.pin.txt b/workflow/envs/shape.win-64.pin.txt
index cbb1289..dd67ef8 100644
--- a/workflow/envs/shape.win-64.pin.txt
+++ b/workflow/envs/shape.win-64.pin.txt
@@ -18,23 +18,70 @@ https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda#720b
https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.0-hac47afa_0.conda#264e350e035092b5135a2147c238aec4
https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda#4cb8e6b48f67de0b018719cdf1136306
https://conda.anaconda.org/conda-forge/win-64/python-3.13.13-h09917c8_100_cp313.conda#7065f7067762c4c2bda1912f18d16239
-https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda#03cb60f505ad3ada0a95277af5faeb1a
+https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5
+https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda#64571d1dd6cdcfa25d0664a5950fdaa2
+https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda#f7d6fcda29570e20851b78d92ea2154e
+https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda#e3b5acbb857a12f5d59e8d174bc536c0
+https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda#8a86073cf3b343b87d03f41790d8b4e5
+https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda#3b576f6860f838f950c570f4433b086e
+https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-ha3553a1_1.conda#34aa94d586fe95fa121966c0d4e73cf4
+https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2025.3.1-h57928b3_13.conda#eded4ef8a94852a2e3e4c779b3b6fdda
+https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.4-h4fa8253_0.conda#761757ab617e8bfef18cc422dd02bbad
+https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_13.conda#c058cdc8796d5eb260a1aef2cbb7fbbf
+https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda#95543eec964b4a4a7ca3c4c9be481aa1
+https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-6_hf9ab0e9_mkl.conda#7e9cdaf6f302142bc363bbab3b5e7074
+https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda#9e4bf521c07f4d423cba9296b7927e3c
+https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py313hce7ae62_1.conda#78749843445581c6dcc0cb80d146982d
+https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c
https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda#8e194e7b992f99a5015edbd4ebd38efd
-https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223
+https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.1-hdb435a2_0.conda#5833ae18b609bf0790e0bfe6f95b3351
https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda#053b84beec00b71ea8ff7a4f84b55207
+https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.4.1-hfd05255_0.conda#25a127bad5470852b30b239f030ec95b
+https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda#e77030e67343e28b084fabd7db0ce43e
+https://conda.anaconda.org/conda-forge/win-64/lerc-4.1.0-hd936e49_0.conda#54b231d595bc1ff9bff668dd443ee012
+https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda#549845d5133100142452812feb9ba2e8
+https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda#9dce2f112bfd3400f4f432b3d0ac07b2
+https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h0ea6238_0.conda#4432f52dc0c8eb6a7a6abc00a037d93c
+https://conda.anaconda.org/conda-forge/win-64/libcurl-8.20.0-h8206538_0.conda#7bee27a8f0a295117ccb864f30d2d87e
+https://conda.anaconda.org/conda-forge/win-64/proj-9.7.1-hd30e2cd_3.conda#f2b0478a02d35bac5b872d4d63b96be3
+https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_1.conda#d1097e01041cfed41c81f1e3d1f52572
+https://conda.anaconda.org/conda-forge/win-64/pcre2-10.46-h3402e2f_0.conda#889053e920d15353c2665fa6310d7a7a
+https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda#0b69331897a92fac3d8923549d48d092
+https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda#f9bbae5e2537e3b06e0f7310ba76c893
+https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_2.conda#5187ecf958be3c39110fe691cbd6873e
+https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-hbc0d294_0.conda#62b6eff67ddb7d8657e92fc3d1382a2d
+https://conda.anaconda.org/conda-forge/win-64/geos-3.14.0-hdade9fe_0.conda#605225b71402d12f4bf0324b0cc1db97
+https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-h5ff11c1_19.conda#41949b02b89c2f2e08c5bd457295f237
+https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda#5d55038c4d640e5df06de1cfb4e8d741
+https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda#d6a8059de245e53478b581742b53f71d
+https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h3bf7137_118.conda#a00265bc2dea705fdae1c629f69e65d9
+https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda#52f1280563f3b48b5f75414cd2d15dd1
+https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda#28b4cf9065681f43cc567410edf8243d
+https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda#b44e0d9eb84c6c086d809787aab0a1da
+https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda#c5cb4159f0eea65663b31dd1e49bbb71
+https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.7-gpl_he24518a_100.conda#e8575b817fa0ed38f4f31415f1b4accf
+https://conda.anaconda.org/conda-forge/win-64/geotiff-1.7.4-h73469f5_4.conda#2a62961eeffe28d84c166600e4bf6e25
https://conda.anaconda.org/conda-forge/win-64/snappy-1.2.2-h7fa0ca8_1.conda#3075846de68f942150069d4289aaad63
+https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda#357d7be4146d5fec543bfaa96a8a40de
+https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.10.3-h2f24e33_24.conda#0489c36d03b99d3f71d9d0b4742f1620
+https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7
+https://conda.anaconda.org/conda-forge/noarch/click-8.3.3-pyh6dadd2b_0.conda#8f03c7a39b01ebc57b647f7b48bbeed9
+https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1
+https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1
+https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda#929471569c93acefb30282a22060dcd5
+https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2
+https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda#8c4061f499edec6b8ac7000f6d586829
+https://conda.anaconda.org/conda-forge/win-64/rasterio-1.4.4-py313ha5c5119_0.conda#c094475643548d60959967b5be3638bb
+https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda#03cb60f505ad3ada0a95277af5faeb1a
+https://conda.anaconda.org/conda-forge/noarch/pycountry-24.6.1-pyhd8ed1ab_0.conda#62ed8c560f1b5b8d74ed11e68e9ae223
https://conda.anaconda.org/conda-forge/win-64/libabseil-20260107.1-cxx17_h0eb2380_0.conda#60da39dd5fd93b2a4a0f986f3acc2520
https://conda.anaconda.org/conda-forge/win-64/libre2-11-2025.11.05-h04e5de1_1.conda#3d863f1a19f579ca511f6ac02038ab5a
https://conda.anaconda.org/conda-forge/win-64/re2-2025.11.05-ha104f34_1.conda#6807f05dcf3f1736ad6cc9525b8b8725
-https://conda.anaconda.org/conda-forge/win-64/lz4-c-1.10.0-h2466b09_1.conda#0b69331897a92fac3d8923549d48d092
https://conda.anaconda.org/conda-forge/win-64/libprotobuf-6.33.5-h61fc761_0.conda#69e5855826e56ea4b67fb888ef879afd
https://conda.anaconda.org/conda-forge/win-64/orc-2.2.2-h0a1ad0e_1.conda#aa6701a960f0e94478229af1e061c237
https://conda.anaconda.org/conda-forge/win-64/libutf8proc-2.11.3-hb980946_0.conda#5f34fcb6578ea9bdbfd53cc2cfb88200
https://conda.anaconda.org/conda-forge/win-64/c-ares-1.34.6-hfd05255_0.conda#7c6da34e5b6e60b414592c74582e28bf
https://conda.anaconda.org/conda-forge/win-64/libgrpc-1.78.1-h9ff2b3e_0.conda#26dbb65607f8fe485df5ee98fa6eb79f
-https://conda.anaconda.org/conda-forge/win-64/libssh2-1.11.1-h9aa295b_0.conda#9dce2f112bfd3400f4f432b3d0ac07b2
-https://conda.anaconda.org/conda-forge/win-64/krb5-1.22.2-h0ea6238_0.conda#4432f52dc0c8eb6a7a6abc00a037d93c
-https://conda.anaconda.org/conda-forge/win-64/libcurl-8.20.0-h8206538_0.conda#7bee27a8f0a295117ccb864f30d2d87e
https://conda.anaconda.org/conda-forge/win-64/libgoogle-cloud-2.39.0-h01c467a_1.conda#453d3a0347fe049b922a2a851c1c0110
https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.44.35208-h38c0c73_34.conda#f276d1de4553e8fca1dfb6988551ebb4
https://conda.anaconda.org/conda-forge/win-64/libcrc32c-1.1.2-h0e60522_0.tar.bz2#cd4cc2d0c610c8cb5419ccc979f2d6ce
@@ -82,19 +129,6 @@ https://conda.anaconda.org/conda-forge/noarch/pandera-base-0.24.0-pyhd8ed1ab_2.c
https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda#f6ad7450fc21e00ecc23812baed6d2e4
https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda#3339e3b65d58accf4ca4fb8748ab16b3
https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8
-https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda#64571d1dd6cdcfa25d0664a5950fdaa2
-https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda#f7d6fcda29570e20851b78d92ea2154e
-https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda#e3b5acbb857a12f5d59e8d174bc536c0
-https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda#8a86073cf3b343b87d03f41790d8b4e5
-https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda#3b576f6860f838f950c570f4433b086e
-https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-ha3553a1_1.conda#34aa94d586fe95fa121966c0d4e73cf4
-https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2025.3.1-h57928b3_13.conda#eded4ef8a94852a2e3e4c779b3b6fdda
-https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.4-h4fa8253_0.conda#761757ab617e8bfef18cc422dd02bbad
-https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_13.conda#c058cdc8796d5eb260a1aef2cbb7fbbf
-https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda#95543eec964b4a4a7ca3c4c9be481aa1
-https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-6_hf9ab0e9_mkl.conda#7e9cdaf6f302142bc363bbab3b5e7074
-https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda#9e4bf521c07f4d423cba9296b7927e3c
-https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py313hce7ae62_1.conda#78749843445581c6dcc0cb80d146982d
https://conda.anaconda.org/conda-forge/win-64/pandas-3.0.2-py313h26f5e95_0.conda#4e6201ece5bfb083570145791feab397
https://conda.anaconda.org/conda-forge/noarch/pandera-0.24.0-hd8ed1ab_2.conda#a12a21a89519f0cc224e44da86f5be2c
https://conda.anaconda.org/conda-forge/noarch/validators-0.35.0-pyhd8ed1ab_0.conda#3449ef730c7d483adde81993994092b9
@@ -103,8 +137,6 @@ https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda#592132998493b3ff25fd7479396e8351
https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda#6d03368f2b2b0a5fb6839df53b2eb5e0
https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda#0242025a3c804966bf71aa04eee82f66
-https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7
-https://conda.anaconda.org/conda-forge/noarch/click-8.3.3-pyh6dadd2b_0.conda#8f03c7a39b01ebc57b647f7b48bbeed9
https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda#52be5139047efadaeeb19c6a5103f92a
https://conda.anaconda.org/conda-forge/noarch/typer-0.25.1-pyhcf101f3_0.conda#ef114c2eb2ff19f6bf616c81f4710841
https://conda.anaconda.org/conda-forge/noarch/tabulate-0.10.0-pyhcf101f3_0.conda#3b887b7b3468b0f494b4fad40178b043
@@ -121,14 +153,12 @@ https://conda.anaconda.org/conda-forge/win-64/backports.zstd-1.4.0-py313h2a31948
https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda#cbb88288f74dbe6ada1c6c7d0a97223e
https://conda.anaconda.org/conda-forge/noarch/idna-3.13-pyhcf101f3_0.conda#fb7130c190f9b4ec91219840a05ba3ac
https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda#a9167b9571f3baa9d448faa2139d1089
-https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda#929471569c93acefb30282a22060dcd5
https://conda.anaconda.org/conda-forge/noarch/requests-2.33.1-pyhcf101f3_1.conda#9659f587a8ceacc21864260acd02fc67
https://conda.anaconda.org/conda-forge/noarch/text-unidecode-1.3-pyhd8ed1ab_2.conda#23b4ba5619c4752976eb7ba1f5acb7e8
https://conda.anaconda.org/conda-forge/noarch/python-slugify-8.0.4-pyhd8ed1ab_1.conda#a4059bc12930bddeb41aef71537ffaed
https://conda.anaconda.org/conda-forge/noarch/petl-1.7.17-pyhd8ed1ab_0.conda#4c2498dcda0d58cf25466e82f7287b32
https://conda.anaconda.org/conda-forge/noarch/marko-2.2.2-pyhd8ed1ab_0.conda#3c3e9339e46fffba5920be28d9233860
https://conda.anaconda.org/conda-forge/win-64/rpds-py-0.30.0-py313hfbe8231_0.conda#58ae648b12cfa6df3923b5fd219931cb
-https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2
https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab
https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a
https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda#ada41c863af263cc4c5fcbaff7c3e4dc
@@ -142,38 +172,11 @@ https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.co
https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda#11adc78451c998c0fd162584abfa3559
https://conda.anaconda.org/conda-forge/noarch/black-26.3.1-pyh866005b_0.conda#c7e43448266209d766a229cada982884
https://conda.anaconda.org/conda-forge/noarch/pandera-io-0.24.0-hd8ed1ab_2.conda#d456360daad56c802e27504979ef1e19
-https://conda.anaconda.org/conda-forge/win-64/geos-3.14.0-hdade9fe_0.conda#605225b71402d12f4bf0324b0cc1db97
https://conda.anaconda.org/conda-forge/win-64/shapely-2.1.1-py313hae85795_2.conda#8fad5990956ba5a8bbbdb966e7d017b7
https://conda.anaconda.org/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda#4487b9c371d0161d54b5c7bbd890c0fc
-https://conda.anaconda.org/conda-forge/win-64/sqlite-3.53.1-hdb435a2_0.conda#5833ae18b609bf0790e0bfe6f95b3351
-https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.4.1-hfd05255_0.conda#25a127bad5470852b30b239f030ec95b
-https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.25-h51727cc_0.conda#e77030e67343e28b084fabd7db0ce43e
-https://conda.anaconda.org/conda-forge/win-64/lerc-4.1.0-hd936e49_0.conda#54b231d595bc1ff9bff668dd443ee012
-https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.1-h8f73337_1.conda#549845d5133100142452812feb9ba2e8
-https://conda.anaconda.org/conda-forge/win-64/proj-9.7.1-hd30e2cd_3.conda#f2b0478a02d35bac5b872d4d63b96be3
https://conda.anaconda.org/conda-forge/win-64/pyproj-3.7.2-py313hbf73894_3.conda#779b40a8eb5e2aa5ffc5eddd3b136fb7
-https://conda.anaconda.org/conda-forge/win-64/xerces-c-3.3.0-hac47afa_1.conda#d1097e01041cfed41c81f1e3d1f52572
-https://conda.anaconda.org/conda-forge/win-64/pcre2-10.46-h3402e2f_0.conda#889053e920d15353c2665fa6310d7a7a
-https://conda.anaconda.org/conda-forge/win-64/muparser-2.3.5-he0c23c2_0.conda#013aabb169d59009bdf7d70319360e9b
-https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda#f9bbae5e2537e3b06e0f7310ba76c893
-https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.2-hfd05255_2.conda#5187ecf958be3c39110fe691cbd6873e
-https://conda.anaconda.org/conda-forge/win-64/libxml2-devel-2.15.3-hbc0d294_0.conda#62b6eff67ddb7d8657e92fc3d1382a2d
-https://conda.anaconda.org/conda-forge/win-64/librttopo-1.1.0-h5ff11c1_19.conda#41949b02b89c2f2e08c5bd457295f237
-https://conda.anaconda.org/conda-forge/win-64/minizip-4.2.1-h0ffbb96_0.conda#5d55038c4d640e5df06de1cfb4e8d741
-https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda#d6a8059de245e53478b581742b53f71d
-https://conda.anaconda.org/conda-forge/win-64/libspatialite-5.1.0-gpl_h3bf7137_118.conda#a00265bc2dea705fdae1c629f69e65d9
-https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.58-h7351971_0.conda#52f1280563f3b48b5f75414cd2d15dd1
-https://conda.anaconda.org/conda-forge/win-64/uriparser-0.9.8-h5a68840_0.conda#28b4cf9065681f43cc567410edf8243d
-https://conda.anaconda.org/conda-forge/win-64/libkml-1.3.0-h68a222c_1023.conda#b44e0d9eb84c6c086d809787aab0a1da
-https://conda.anaconda.org/conda-forge/win-64/libhwy-1.4.0-h172a326_0.conda#aeca1cb6665f19e560c1fbd20b5bcf34
-https://conda.anaconda.org/conda-forge/win-64/libjxl-0.11.2-h932607e_1.conda#327bce3eb1ef1875c7145e915d25bcd3
-https://conda.anaconda.org/conda-forge/win-64/lzo-2.10-h6a83c73_1002.conda#c5cb4159f0eea65663b31dd1e49bbb71
-https://conda.anaconda.org/conda-forge/win-64/libarchive-3.8.7-gpl_he24518a_100.conda#e8575b817fa0ed38f4f31415f1b4accf
-https://conda.anaconda.org/conda-forge/win-64/blosc-1.21.6-hfd34d9b_1.conda#357d7be4146d5fec543bfaa96a8a40de
-https://conda.anaconda.org/conda-forge/win-64/libgdal-core-3.11.4-h8a8bf46_6.conda#fd3141a9ef5384b6ecad819ffe429378
-https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.11.1-py313h0dbd5a6_1.conda#7d1eaf4ed949aeb268394cf2857e20b5
+https://conda.anaconda.org/conda-forge/win-64/pyogrio-0.11.0-py313h75b81ac_0.conda#7a3d31c8db0ac99557cb2faf51fc42b5
https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda#854fbdff64b572b5c0b470f334d34c11
-https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda#3687cc0b82a8b4c17e1f0eb7e47163d5
https://conda.anaconda.org/conda-forge/win-64/zlib-ng-2.3.3-h0261ad2_1.conda#46a21c0a4e65f1a135251fc7c8663f83
https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.4-h0e57b4f_0.conda#e723ab7cc2794c954e1b22fde51c16e4
https://conda.anaconda.org/conda-forge/win-64/libgomp-15.2.0-h8ee18e1_19.conda#f1147651e3fdd585e2f442c0c2fc8f2d
@@ -202,10 +205,10 @@ https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda#61
https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.8.0-np2py313h4ce4a18_1.conda#1a636c8e6f5b92fca019972db0ed348e
https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b
https://conda.anaconda.org/conda-forge/noarch/mapclassify-2.10.0-pyhd8ed1ab_1.conda#cc293b4cad9909bf66ca117ea90d4631
-https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.0.1-pyha770c72_3.conda#e8343d1b635bf09dafdd362d7357f395
+https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.3-pyha770c72_0.conda#18789a85c307970ae1786dfc6dfd234f
https://conda.anaconda.org/conda-forge/noarch/branca-0.8.2-pyhd8ed1ab_0.conda#1fcdf88e7a8c296d3df8409bf0690db4
https://conda.anaconda.org/conda-forge/noarch/folium-0.20.0-pyhd8ed1ab_0.conda#a6997a7dcd6673c0692c61dfeaea14ab
-https://conda.anaconda.org/conda-forge/noarch/geopandas-1.0.1-pyhd8ed1ab_3.conda#1baca589eb35814a392eaad6d152447e
+https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.3-pyhd8ed1ab_0.conda#4eb8b870142ca06d2a1d2c74662eac7d
https://conda.anaconda.org/conda-forge/noarch/pandera-geopandas-0.24.0-hd8ed1ab_2.conda#fb21509f073465506ea994dc4667bf66
https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda#d0fc809fa4c4d85e959ce4ab6e1de800
https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.0-pyhcf101f3_0.conda#4bada6a6d908a27262af8ebddf4f7492
@@ -229,3 +232,4 @@ https://conda.anaconda.org/conda-forge/noarch/jmespath-1.1.0-pyhcf101f3_1.conda#
https://conda.anaconda.org/conda-forge/noarch/botocore-1.40.76-pyhd8ed1ab_0.conda#4babebd5361bc44503358a519a81a465
https://conda.anaconda.org/conda-forge/noarch/s3transfer-0.14.0-pyhd8ed1ab_0.conda#4e3089ce93822a25408c480dd53561b6
https://conda.anaconda.org/conda-forge/noarch/boto3-1.40.46-pyhd8ed1ab_0.conda#a93057bba32eef7b217733734642bfea
+https://conda.anaconda.org/conda-forge/noarch/antimeridian-0.4.7-pyhd8ed1ab_0.conda#56e9ab4ca5ec150ad52ca420f2cec646
diff --git a/workflow/envs/shape.yaml b/workflow/envs/shape.yaml
index 9857891..21e5f99 100644
--- a/workflow/envs/shape.yaml
+++ b/workflow/envs/shape.yaml
@@ -5,7 +5,7 @@ channels:
dependencies:
- boto3 1.40.46.*
- duckdb 1.2.0.*
-- geopandas 1.0.1.*
+- geopandas 1.1.3.*
- ipdb 0.13.13.*
- pandera-geopandas 0.24.*
- pandera-io 0.24.*
@@ -16,3 +16,5 @@ dependencies:
- pandas 3.0.2.*
- pytz 2026.2.*
- requests 2.33.1.*
+- antimeridian 0.4.7.*
+- rasterio 1.4.4.*
diff --git a/workflow/internal/config.schema.yaml b/workflow/internal/config.schema.yaml
index 122064f..8dfa47f 100644
--- a/workflow/internal/config.schema.yaml
+++ b/workflow/internal/config.schema.yaml
@@ -233,7 +233,7 @@ properties:
scenarios:
description: >
Shape-building scenarios to run.
- Downloaded source files will be re-used between them if possible.
+ Downloaded source files will be reused between them if possible.
type: object
patternProperties:
"^[A-Za-z0-9_-]+$":
diff --git a/workflow/rules/build.smk b/workflow/rules/build.smk
index 9a3de48..28e8944 100644
--- a/workflow/rules/build.smk
+++ b/workflow/rules/build.smk
@@ -15,7 +15,7 @@ rule build_eez:
conda:
"../envs/shape.yaml"
message:
- "Build '{wildcards.country}' EEZ dataset ({wildcards.eez_key})."
+ "{wildcards.country}: build EEZ dataset {wildcards.eez_key}."
script:
"../scripts/build_eez.py"
@@ -44,7 +44,7 @@ rule build_country:
crs=lambda wc: get_crs_config(wc.scenario),
voronoi=lambda wc: get_voronoi_eez_config(wc.scenario),
message:
- "{wildcards.scenario}:{wildcards.country}: build combined land and marine polygons."
+ "{wildcards.scenario}-{wildcards.country}: building combined single-country dataset."
script:
"../scripts/build_country.py"
@@ -81,6 +81,6 @@ rule build_combined_area:
)
),
message:
- "{wildcards.scenario}: combine land and marine polygons."
+ "{wildcards.scenario}: building combined dataset with all countries."
script:
"../scripts/build_combined_area.py"
diff --git a/workflow/scripts/_geo.py b/workflow/scripts/_geo.py
new file mode 100644
index 0000000..0e48a82
--- /dev/null
+++ b/workflow/scripts/_geo.py
@@ -0,0 +1,131 @@
+"""Reusable geometry helpers."""
+
+from collections.abc import Iterator
+from itertools import pairwise
+from warnings import warn
+
+import antimeridian
+import geopandas as gpd
+from pyproj import CRS
+from rasterio import warp
+from shapely import MultiPolygon, Polygon
+from shapely.geometry import GeometryCollection, mapping, shape
+from shapely.geometry.base import BaseGeometry
+
+CRS_MARINE_REGIONS = "EPSG:4326"
+
+
+def check_crs_config(crs: dict[str, int | str]) -> dict[str, CRS]:
+ """Check the crs configuration settings."""
+ result = {k: CRS.from_user_input(v) for k, v in crs.items()}
+ if not result["projected"].is_projected:
+ raise ValueError(f"CRS must be projected. Got {crs['projected']!r}.")
+ if not result["geographic"].is_geographic:
+ raise ValueError(f"CRS must be geographic. Got {crs['geographic']!r}.")
+ return result
+
+
+def _iter_polygons(geom: BaseGeometry | None) -> Iterator[Polygon]:
+ """Yield polygon parts from possibly mixed geometry."""
+ if geom is None or geom.is_empty:
+ return
+
+ if isinstance(geom, Polygon):
+ yield geom
+ elif isinstance(geom, (MultiPolygon, GeometryCollection)):
+ for part in geom.geoms:
+ yield from _iter_polygons(part)
+
+
+def extract_polygonal_geometry(
+ geom: BaseGeometry | None,
+) -> Polygon | MultiPolygon | None:
+ """Return only Polygon/MultiPolygon components from a geometry."""
+ polygons = list(_iter_polygons(geom))
+ result = None
+ if polygons:
+ result = polygons[0] if len(polygons) == 1 else MultiPolygon(polygons)
+ return result
+
+
+def _rasterio_to_crs(gdf: gpd.GeoDataFrame, to_crs: CRS) -> gpd.GeoDataFrame:
+ """CRS conversion using rasterio's more powerful toolset.
+
+ Compared to geopandas this should adequately handle antimeridian "splitting".
+ Order: geopandas -> GeoJSON-like dict -> rasterio -> geopandas
+ """
+ input_geoms = [mapping(geom) for geom in gdf.geometry]
+ transformed = warp.transform_geom(
+ src_crs=gdf.crs, dst_crs=to_crs, geom=input_geoms, antimeridian_cutting=True
+ )
+ output_geoms = [shape(geom) for geom in transformed]
+ return gdf.set_geometry(gpd.GeoSeries(output_geoms, index=gdf.index, crs=to_crs))
+
+
+def to_projected_crs(gdf: gpd.GeoDataFrame, crs: int | str | CRS) -> gpd.GeoDataFrame:
+ """CRS conversion using rasterio's more powerful toolset.
+
+ Compared to geopandas this adequately handles handles antimeridian "splitting".
+ """
+ to_crs = CRS.from_user_input(crs)
+ if not to_crs.is_projected:
+ raise ValueError(f"This function only converts to geographic CRS. Got {crs!r}.")
+ return _rasterio_to_crs(gdf, to_crs)
+
+
+def _crosses_antimeridian(poly: Polygon) -> bool:
+ rings = [poly.exterior, *poly.interiors]
+
+ crosses = any(
+ 180 < abs(x2 - x1) < 360
+ for ring in rings
+ for (x1, _), (x2, _) in pairwise(ring.coords)
+ )
+
+ return crosses
+
+
+def _fix_antimeridian_geometry(geom: BaseGeometry) -> BaseGeometry:
+ fixed_geom = geom
+
+ if geom.geom_type == "Polygon":
+ if _crosses_antimeridian(geom):
+ fixed_geom = antimeridian.fix_polygon(geom)
+
+ elif geom.geom_type == "MultiPolygon":
+ parts = []
+
+ for poly in geom.geoms:
+ fixed_poly = poly
+
+ if _crosses_antimeridian(poly):
+ fixed_poly = antimeridian.fix_polygon(poly)
+
+ if fixed_poly.geom_type == "MultiPolygon":
+ parts.extend(fixed_poly.geoms)
+ else:
+ parts.append(fixed_poly)
+
+ fixed_geom = MultiPolygon(parts)
+
+ return fixed_geom
+
+
+def to_geographic_crs(gdf: gpd.GeoDataFrame, crs: int | str | CRS) -> gpd.GeoDataFrame:
+ """Convert to a geographic CRS fixing antimeridian-crossings."""
+ target_crs = CRS.from_user_input(crs)
+ if not target_crs.is_geographic:
+ raise ValueError(f"This function only converts to geographic CRS. Got {crs!r}.")
+
+ # Map to 4326 to ensure we match antimeridian algorithm requirements
+ fixed = _rasterio_to_crs(gdf, CRS.from_user_input("EPSG:4326"))
+ fixed.geometry = fixed.geometry.map(_fix_antimeridian_geometry)
+
+ if not fixed.crs.equals(target_crs):
+ warn(
+ f"Target CRS {crs!r} does not match {fixed.crs!r}. "
+ "This might behave oddly near the antimeridian."
+ )
+ fixed = fixed.to_crs(target_crs)
+
+ return fixed
diff --git a/workflow/scripts/_schemas.py b/workflow/scripts/_schemas.py
index af705f5..4cf52e5 100644
--- a/workflow/scripts/_schemas.py
+++ b/workflow/scripts/_schemas.py
@@ -1,7 +1,11 @@
-import pandera.pandas as pa
+"""Reusable shemas."""
+
+import geopandas as gpd
+from _geo import extract_polygonal_geometry
+from pandera import pandas as pa
from pandera.typing.geopandas import GeoSeries
from pandera.typing.pandas import Series
-from shapely.validation import make_valid
+from shapely.geometry import MultiPolygon, Polygon
SUPPORTED_DATASETS = ["gadm", "overture", "marineregions", "nuts", "geoboundaries"]
@@ -31,18 +35,24 @@ class Config:
"Human-readable name in the parent dataset."
@pa.dataframe_parser
- def fix_geometries(cls, df):
- """Attempt to correct empty or malformed geometries."""
- mask = df["geometry"].apply(lambda g: (g is not None) and (not g.is_empty))
- df = df.loc[mask]
- df["geometry"] = df["geometry"].apply(
- lambda g: g if g.is_valid else make_valid(g)
- )
- return df
+ def fix_geometries(cls, gdf: gpd.GeoDataFrame) -> gpd.GeoDataFrame: # type: ignore[misc]
+ """Attempt to correct empty, malformed, or non-polygonal geometries."""
+ mask = gdf["geometry"].notna() & ~gdf["geometry"].is_empty
+ gdf = gdf.loc[mask].copy()
+
+ invalid = ~gdf.geometry.is_valid
+ gdf.loc[invalid, "geometry"] = gdf.loc[invalid, "geometry"].make_valid()
+
+ gdf["geometry"] = gdf["geometry"].apply(extract_polygonal_geometry)
+ return gdf.loc[gdf["geometry"].notna()]
@pa.check("geometry", element_wise=True)
- def check_geometries(cls, geom):
- return (geom is not None) and (not geom.is_empty) and geom.is_valid
+ def check_geometries(cls, geom) -> bool:
+ return (
+ isinstance(geom, (Polygon, MultiPolygon))
+ and not geom.is_empty
+ and geom.is_valid
+ )
class EEZSchema(ShapesSchema):
diff --git a/workflow/scripts/_utils.py b/workflow/scripts/_utils.py
index 8ae4c39..a550990 100644
--- a/workflow/scripts/_utils.py
+++ b/workflow/scripts/_utils.py
@@ -17,8 +17,6 @@
requests.exceptions.Timeout,
requests.exceptions.ChunkedEncodingError,
)
-CRS_MARINE_REGIONS = "EPSG:4326"
-
@dataclass
class DownloadTimeouts:
@@ -34,24 +32,15 @@ def request_timeout(self) -> tuple[int, int]:
return (self.connect_seconds, self.read_seconds)
-def check_crs_config(crs: dict[str, int | str]) -> dict[str, CRS]:
- """Check the crs configuration settings."""
- result = {k: CRS.from_user_input(v) for k, v in crs.items()}
- if not result["projected"].is_projected:
- raise ValueError(f"CRS must be projected. Got {crs['projected']!r}.")
- if not result["geographic"].is_geographic:
- raise ValueError(f"CRS must be geographic. Got {crs['geographic']!r}.")
- return result
-
-
def plot_shapes(shapes: gpd.GeoDataFrame, crs: str | int | CRS) -> tuple[Figure, Axes]:
"""Generate a nice figure of dataframes that fit the module's schema."""
- gdf = shapes.copy().to_crs(crs)
+ # NOTE: the use of geopandas' to_crs is purposeful.
+ # It's likely what users of the module will rely on later.
+ gdf = shapes.to_crs(crs)
colors = {"land": "olive", "maritime": "tab:blue"}
fig, ax = plt.subplots(layout="constrained")
ax = gdf.plot(
ax=ax,
- column="shape_class",
color=gdf["shape_class"].map(colors),
legend=False,
zorder=-1,
diff --git a/workflow/scripts/build_combined_area.py b/workflow/scripts/build_combined_area.py
index ce63718..6a99b9c 100644
--- a/workflow/scripts/build_combined_area.py
+++ b/workflow/scripts/build_combined_area.py
@@ -4,6 +4,7 @@
from collections import defaultdict
from typing import TYPE_CHECKING, Any
+import _geo
import _schemas
import _utils
import geopandas as gpd
@@ -21,7 +22,7 @@ def remove_overlaps(gdf: gpd.GeoDataFrame, crs: dict[str, CRS]) -> gpd.GeoDataFr
The first row wins. Any area shared with an earlier kept geometry is removed
from the later geometry.
"""
- projected = gdf.to_crs(crs["projected"]).copy()
+ projected = _geo.to_projected_crs(gdf, crs["projected"])
left_idx, right_idx = projected.sindex.query(
projected.geometry, predicate="intersects"
@@ -57,22 +58,24 @@ def remove_overlaps(gdf: gpd.GeoDataFrame, crs: dict[str, CRS]) -> gpd.GeoDataFr
projected["geometry"] = geoms
projected = projected.loc[projected.geometry.notna() & ~projected.geometry.is_empty]
-
- result = projected.to_crs(crs["geographic"])
- result.geometry = result.geometry.buffer(0)
+ result = _geo.to_geographic_crs(projected, crs["geographic"])
return result
def main() -> None:
"""Main snakemake process."""
- crs = _utils.check_crs_config(snakemake.params.crs)
-
- country_list = [
- gpd.read_parquet(i).to_crs(crs["geographic"]) for i in snakemake.input.countries
- ]
- combined = _schemas.ShapesSchema.validate(
- pd.concat(country_list, ignore_index=True)
+ crs = _geo.check_crs_config(snakemake.params.crs)
+
+ # Load and ensure inputs are healthy
+ country_list = [gpd.read_parquet(i) for i in snakemake.input.countries]
+ crs_mismatch = [not crs["geographic"].equals(i.crs) for i in country_list]
+ if any(crs_mismatch):
+ raise ValueError(f"Received datasets with invalid CRS: {sum(crs_mismatch)!r} .")
+ combined = gpd.GeoDataFrame(
+ pd.concat(country_list, ignore_index=True),
+ crs=crs["geographic"],
+ geometry="geometry",
)
combined = remove_overlaps(combined, crs)
diff --git a/workflow/scripts/build_country.py b/workflow/scripts/build_country.py
index 383c81b..25da076 100644
--- a/workflow/scripts/build_country.py
+++ b/workflow/scripts/build_country.py
@@ -6,6 +6,7 @@
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any
+import _geo
import _schemas
import _utils
import geopandas as gpd
@@ -21,7 +22,6 @@
)
from shapely.geometry.base import BaseGeometry
from shapely.ops import linemerge, unary_union
-from shapely.validation import explain_validity
if TYPE_CHECKING:
snakemake: Any
@@ -50,57 +50,6 @@ class ShorelineLine:
geometry: LineString
-def validate_reprojections(
- land: gpd.GeoDataFrame, maritime: gpd.GeoDataFrame, crs: dict[str, CRS]
-) -> None:
- """Fail early if configured CRS settings corrupt country inputs."""
- problems = []
- for layer, gdf in [("land", land), ("maritime", maritime)]:
- if gdf.empty:
- continue
-
- for crs_name in ["projected", "geographic"]:
- target_crs = crs[crs_name]
- try:
- projected = gdf.to_crs(target_crs)
- except Exception as exc:
- problems.append(
- f"{layer} to {crs_name} CRS {target_crs.to_string()!r}: "
- f"{type(exc).__name__}: {exc}"
- )
- continue
-
- invalid = projected.loc[~projected.geometry.is_valid]
- if invalid.empty:
- continue
-
- details = []
- for row in invalid.head(10).itertuples():
- details.append(
- ", ".join(
- [
- f"shape_id={getattr(row, 'shape_id', '')!r}",
- f"parent_id={getattr(row, 'parent_id', '')!r}",
- f"reason={explain_validity(row.geometry)!r}",
- ]
- )
- )
- if len(invalid) > len(details):
- details.append(f"{len(invalid) - len(details)} more geometries")
-
- problems.append(
- f"{layer} to {crs_name} CRS {target_crs.to_string()!r} "
- "created invalid geometries: " + "; ".join(details)
- )
-
- if problems:
- raise ValueError(
- "Configured CRS settings cannot safely reproject the inputs. "
- "Choose CRS settings that cover all requested land and EEZ geometries.\n"
- "- " + "\n- ".join(problems)
- )
-
-
def _iter_lines(geom: BaseGeometry) -> Iterator[LineString]:
"""Yield line components from a possibly nested geometry."""
if geom.is_empty:
@@ -316,6 +265,8 @@ def _split_one_maritime(
.dissolve(by="assigned_shape_id", as_index=False)
.rename(columns={"assigned_shape_id": "_assigned_shape_id"})
)
+ # FIXME: check if rerunning `warp` is necessary to fix things near the antimeridian
+ pieces = _geo.to_projected_crs(pieces, crs)
uncovered_area = maritime_geometry.difference(pieces.geometry.union_all()).area
if uncovered_area > voronoi_config.uncovered_area_tolerance:
raise RuntimeError(
@@ -345,11 +296,9 @@ def split_maritime_by_shoreline_voronoi(
if not voronoi_config.enabled:
return shapes, cells
- land = shapes.loc[shapes["shape_class"].eq("land")].copy().to_crs(crs["projected"])
- maritime = (
- shapes.loc[shapes["shape_class"].eq("maritime")].copy().to_crs(crs["projected"])
- )
-
+ shapes_proj = _geo.to_projected_crs(shapes, crs["projected"])
+ land = shapes_proj.loc[shapes_proj["shape_class"].eq("land")].copy()
+ maritime = shapes_proj.loc[shapes_proj["shape_class"].eq("maritime")].copy()
if maritime.empty:
raise ValueError("Requested voronoi without maritime shapes.")
@@ -364,7 +313,11 @@ def split_maritime_by_shoreline_voronoi(
]
split_maritime = [pieces for pieces, _ in split_results]
- result = pd.concat([land, *split_maritime], ignore_index=True)
+ result = gpd.GeoDataFrame(
+ pd.concat([land, *split_maritime], ignore_index=True),
+ geometry="geometry",
+ crs=crs["projected"]
+ )
cell_frames = [i for _, i in split_results if not i.empty]
if cell_frames:
@@ -373,57 +326,57 @@ def split_maritime_by_shoreline_voronoi(
geometry="geometry",
crs=crs["projected"],
)
-
- result = result.to_crs(crs["geographic"])
- result.geometry = result.geometry.buffer(0)
+ result.geometry = result.geometry.make_valid()
+ result = _geo.to_geographic_crs(result, crs["geographic"])
return result, cells
-def combine_shapes(
+def build_country(
land: gpd.GeoDataFrame, maritime: gpd.GeoDataFrame, crs: dict[str, CRS]
) -> gpd.GeoDataFrame:
- """Combine land and marine shapes."""
+ """Build country dataset, with maritime regions if necessary."""
if maritime.empty:
- return land.copy().to_crs(crs["geographic"])
-
- combined = land.copy().to_crs(crs["projected"])
-
- # Remove contested zones and clip in projected coordinates.
- eez = maritime.copy().to_crs(crs["projected"])
- eez = eez[eez["contested"].eq(False)].drop(columns="contested")
- combined.geometry = combined.geometry.difference(eez.geometry.union_all())
- combined = pd.concat([combined, eez], ignore_index=True)
-
- # Resolve floating point mismatches before converting back to output CRS.
- combined.geometry = combined.geometry.buffer(0)
- combined = combined.to_crs(crs["geographic"])
- return combined
+ return _geo.to_geographic_crs(land, crs["geographic"])
+
+ # Reproject
+ p_land = _geo.to_projected_crs(land, crs["projected"])
+ p_marine = _geo.to_projected_crs(maritime, crs["projected"])
+ # Remove contested zones
+ p_marine = p_marine[p_marine["contested"].eq(False)].drop(columns="contested")
+ # Give priority to EEZs
+ p_land.geometry = p_land.geometry.difference(p_marine.geometry.union_all())
+ combined = gpd.GeoDataFrame(
+ pd.concat([p_land, p_marine], ignore_index=True), crs=crs["projected"]
+ )
+ combined.geometry = combined.geometry.make_valid()
+ return _geo.to_geographic_crs(combined, crs["geographic"])
def plot_voronoi_cells(ax: _utils.Axes, cells: gpd.GeoDataFrame, crs: CRS) -> None:
"""Show voronoi tessellation."""
- projected_cells = cells.copy().to_crs(crs)
+ projected_cells = cells.to_crs(crs)
projected_cells.boundary.plot(ax=ax, lw=0.2, color="lightgrey", zorder=0)
def main() -> None:
"""Main snakemake process."""
- crs = _utils.check_crs_config(snakemake.params.crs)
-
+ crs = _geo.check_crs_config(snakemake.params.crs)
country = snakemake.wildcards.country
+
+ # Load and ensure request matches expectations
land = _schemas.ShapesSchema.validate(gpd.read_parquet(snakemake.input.land))
maritime = _schemas.EEZSchema.validate(gpd.read_parquet(snakemake.input.maritime))
-
country_ids = set(land["country_id"]) | set(maritime["country_id"])
- if set(country_ids) - set([country]):
+ if set(country_ids) - {country}:
raise ValueError(
- f"Country processing mismatch for {country!r}. Found {country_ids!r}."
+ f"Country mismatch: expected {country!r}, found {country_ids!r}."
)
- validate_reprojections(land, maritime, crs)
- shapes = combine_shapes(land, maritime, crs)
+ # Build default country dataset
+ shapes = build_country(land, maritime, crs)
shapes = _schemas.ShapesSchema.validate(shapes)
+ # If requested, run Voronoi splitting
voronoi_config = VoronoiConfig(**snakemake.params.voronoi)
cells = None
if not maritime.empty and voronoi_config.enabled:
@@ -432,11 +385,11 @@ def main() -> None:
)
shapes = _schemas.ShapesSchema.validate(shapes)
+ # Save and show a pretty plot
shapes.to_parquet(snakemake.output.country)
fig, ax = _utils.plot_shapes(shapes, crs["projected"])
if cells is not None and not cells.empty:
plot_voronoi_cells(ax, cells, crs["projected"])
-
fig.savefig(snakemake.output.plot, dpi=200, bbox_inches="tight")
diff --git a/workflow/scripts/build_eez.py b/workflow/scripts/build_eez.py
index b7d2f1c..819cfee 100644
--- a/workflow/scripts/build_eez.py
+++ b/workflow/scripts/build_eez.py
@@ -6,7 +6,7 @@
import _schemas
import geopandas as gpd
import pandas as pd
-from _utils import CRS_MARINE_REGIONS
+from _geo import CRS_MARINE_REGIONS
if TYPE_CHECKING:
snakemake: Any
diff --git a/workflow/scripts/download_harmonised_eez.py b/workflow/scripts/download_harmonised_eez.py
index 40d73d5..8a4014d 100644
--- a/workflow/scripts/download_harmonised_eez.py
+++ b/workflow/scripts/download_harmonised_eez.py
@@ -13,6 +13,7 @@
import _utils
import geopandas as gpd
import requests
+from _geo import CRS_MARINE_REGIONS
from matplotlib import pyplot as plt
if TYPE_CHECKING:
@@ -86,7 +87,7 @@ def get_eez_by_cql(
"typeNames": "eez",
"outputFormat": "application/json",
"cql_filter": cql_filter,
- "srsName": _utils.CRS_MARINE_REGIONS,
+ "srsName": CRS_MARINE_REGIONS,
}
response = _get_wfs_response(params, timeouts)
@@ -100,7 +101,7 @@ def get_eez_by_cql(
if data["features"]:
# Let geopandas build the frame, CRS will match request
result = gpd.GeoDataFrame.from_features(
- data["features"], crs=_utils.CRS_MARINE_REGIONS
+ data["features"], crs=CRS_MARINE_REGIONS
)
if result.empty:
result = None
From c0ed3f4b94b1faaea5474c994c1e6d75efc52c38 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Sun, 14 Jun 2026 10:00:49 +0000
Subject: [PATCH 4/6] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
workflow/scripts/_utils.py | 8 ++------
workflow/scripts/build_country.py | 2 +-
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/workflow/scripts/_utils.py b/workflow/scripts/_utils.py
index a550990..5cbf4f4 100644
--- a/workflow/scripts/_utils.py
+++ b/workflow/scripts/_utils.py
@@ -18,6 +18,7 @@
requests.exceptions.ChunkedEncodingError,
)
+
@dataclass
class DownloadTimeouts:
"""Generic class for handling timeouts across data sources."""
@@ -39,12 +40,7 @@ def plot_shapes(shapes: gpd.GeoDataFrame, crs: str | int | CRS) -> tuple[Figure,
gdf = shapes.to_crs(crs)
colors = {"land": "olive", "maritime": "tab:blue"}
fig, ax = plt.subplots(layout="constrained")
- ax = gdf.plot(
- ax=ax,
- color=gdf["shape_class"].map(colors),
- legend=False,
- zorder=-1,
- )
+ ax = gdf.plot(ax=ax, color=gdf["shape_class"].map(colors), legend=False, zorder=-1)
gdf.boundary.plot(ax=ax, color="black", lw=0.5, zorder=1)
ax.set(xticks=[], yticks=[], xlabel="", ylabel="")
return fig, ax
diff --git a/workflow/scripts/build_country.py b/workflow/scripts/build_country.py
index 25da076..25a7dd0 100644
--- a/workflow/scripts/build_country.py
+++ b/workflow/scripts/build_country.py
@@ -316,7 +316,7 @@ def split_maritime_by_shoreline_voronoi(
result = gpd.GeoDataFrame(
pd.concat([land, *split_maritime], ignore_index=True),
geometry="geometry",
- crs=crs["projected"]
+ crs=crs["projected"],
)
cell_frames = [i for _, i in split_results if not i.empty]
From 494326c61275f04f3a760d3c9ae82df0251ee3c2 Mon Sep 17 00:00:00 2001
From: Ivan Ruiz Manuel <72193617+irm-codebase@users.noreply.github.com>
Date: Sun, 14 Jun 2026 12:40:23 +0200
Subject: [PATCH 5/6] fix typo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index fc3f840..03bbeb7 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ Data processing steps:
-1. The configuration file is read to identify which requested geopolitical combination (`scenarios`) was requested. These can be any number of nations (`countries`), each comming from a distinct dataset (`source`) and with its own subnational aggregation (`subtype`).
+1. The configuration file is read to identify which requested geopolitical combination (`scenarios`) was requested. These can be any number of nations (`countries`), each coming from a distinct dataset (`source`) and with its own subnational aggregation (`subtype`).
- Country landmass data: [eurostat NUTS](https://ec.europa.eu/eurostat/web/gisco/geodata/statistical-units/territorial-units-statistics), [GADM](https://gadm.org/), [geoBoundaries](https://www.geoboundaries.org/), and [Overture Maps](https://overturemaps.org/) are supported.
- Exclusive Economic Zone (EEZ) data: [MarineRegions.org](https://www.marineregions.org/).
2. Individual country files are downloaded and harmonised to fit a standardised schema.
From d4ca4d1bdb121077410ba5d1cae87e30dbd8f5a7 Mon Sep 17 00:00:00 2001
From: Ivan Ruiz Manuel <72193617+irm-codebase@users.noreply.github.com>
Date: Sun, 14 Jun 2026 12:41:54 +0200
Subject: [PATCH 6/6] more typo fixes
---
workflow/scripts/_schemas.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/workflow/scripts/_schemas.py b/workflow/scripts/_schemas.py
index 4cf52e5..1e878c3 100644
--- a/workflow/scripts/_schemas.py
+++ b/workflow/scripts/_schemas.py
@@ -1,4 +1,4 @@
-"""Reusable shemas."""
+"""Reusable schemas."""
import geopandas as gpd
from _geo import extract_polygonal_geometry