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