From eba1df8634cebf7b5143adf7707efc2b21d572f0 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Wed, 24 Jun 2026 04:39:56 +0200 Subject: [PATCH 1/5] chore(unfao): delete the dead geopandas runtime mapper (C-39) ADR-011 replaced the runtime PriogridCountryMapper with the GAUL lookup (GaulLookupEnricher); the mapper was verified orphaned in production (the live manager never references it). This removes the whole dead cluster (CCP): the 3,171-line geopandas module, the 1.3 GB shapefile bundle, the mapper tests + conftest, the two ADR-011 diff scripts, and the mapper CIC -- 46 files, ~5.1k deletions. geopandas + shapely are now gone from the codebase; cachetools dropped from pyproject (mapper-only). Dissolves the old-mapper concern cluster (C-02/05/06/11/12/14/16/17/19/20/21, D-01/D-02); C-39 marked resolved. Deletion broke nothing -- keep-tests green. Co-Authored-By: Claude Opus 4.8 --- docs/CICs/PriogridCountryMapper.md | 156 - docs/CICs/README.md | 3 +- pyproject.toml | 1 - reports/technical_risk_register.md | 32 +- scripts/diff_enrichment.py | 164 - scripts/plot_enrichment_diff.py | 186 - tests/conftest.py | 143 - tests/test_integration.py | 140 - tests/test_mapping.py | 274 -- tests/test_regression.py | 179 - .../shapefiles/GAUL_2024_L1/GAUL_2024_L1.cpg | 3 - .../shapefiles/GAUL_2024_L1/GAUL_2024_L1.dbf | 3 - .../shapefiles/GAUL_2024_L1/GAUL_2024_L1.prj | 3 - .../shapefiles/GAUL_2024_L1/GAUL_2024_L1.sbn | Bin 30380 -> 0 bytes .../shapefiles/GAUL_2024_L1/GAUL_2024_L1.sbx | Bin 1572 -> 0 bytes .../shapefiles/GAUL_2024_L1/GAUL_2024_L1.shp | 3 - .../GAUL_2024_L1/GAUL_2024_L1.shp.xml | 2 - .../shapefiles/GAUL_2024_L1/GAUL_2024_L1.shx | 3 - .../GAUL_2024_L1/GAUL_2024_L1_ChangeLog.docx | Bin 26864 -> 0 bytes .../GAUL_2024_L1/GAUL_2024_L1_Metadata.xlsx | Bin 27873 -> 0 bytes .../shapefiles/GAUL_2024_L2/GAUL_2024_L2.cpg | 3 - .../shapefiles/GAUL_2024_L2/GAUL_2024_L2.dbf | 3 - .../shapefiles/GAUL_2024_L2/GAUL_2024_L2.prj | 3 - .../shapefiles/GAUL_2024_L2/GAUL_2024_L2.sbn | Bin 440396 -> 0 bytes .../shapefiles/GAUL_2024_L2/GAUL_2024_L2.sbx | Bin 13244 -> 0 bytes .../shapefiles/GAUL_2024_L2/GAUL_2024_L2.shp | 3 - .../GAUL_2024_L2/GAUL_2024_L2.shp.xml | 2 - .../shapefiles/GAUL_2024_L2/GAUL_2024_L2.shx | 3 - .../GAUL_2024_L2/GAUL_2024_L2_ChangeLog.docx | Bin 27729 -> 0 bytes .../GAUL_2024_L2/GAUL_2024_L2_Metadata.xlsx | Bin 27479 -> 0 bytes .../ne_10m_admin_0_countries.cpg | 3 - .../ne_10m_admin_0_countries.dbf | 3 - .../ne_10m_admin_0_countries.prj | 3 - .../ne_10m_admin_0_countries.shp | 3 - .../ne_10m_admin_0_countries.shx | 3 - .../ne_110m_admin_0_countries.cpg | 3 - .../ne_110m_admin_0_countries.dbf | 3 - .../ne_110m_admin_0_countries.prj | 3 - .../ne_110m_admin_0_countries.shp | 3 - .../ne_110m_admin_0_countries.shx | 3 - .../priogrid_cellshp/priogrid_cell.dbf | 3 - .../priogrid_cellshp/priogrid_cell.prj | 3 - .../priogrid_cellshp/priogrid_cell.qpj | 1 - .../priogrid_cellshp/priogrid_cell.shp | 3 - .../priogrid_cellshp/priogrid_cell.shx | 3 - views_postprocessing/unfao/managers/README.md | 3 +- views_postprocessing/unfao/mapping/README.md | 579 --- .../unfao/mapping/__init__.py | 0 views_postprocessing/unfao/mapping/mapping.py | 3171 ----------------- 49 files changed, 32 insertions(+), 5076 deletions(-) delete mode 100644 docs/CICs/PriogridCountryMapper.md delete mode 100644 scripts/diff_enrichment.py delete mode 100644 scripts/plot_enrichment_diff.py delete mode 100644 tests/conftest.py delete mode 100644 tests/test_integration.py delete mode 100644 tests/test_mapping.py delete mode 100644 tests/test_regression.py delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.cpg delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.dbf delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.prj delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.sbn delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.sbx delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shp delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shp.xml delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shx delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1_ChangeLog.docx delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1_Metadata.xlsx delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.cpg delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.dbf delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.prj delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.sbn delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.sbx delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shp delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shp.xml delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shx delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2_ChangeLog.docx delete mode 100644 views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2_Metadata.xlsx delete mode 100644 views_postprocessing/shapefiles/ne_10m_admin_0_countries/ne_10m_admin_0_countries.cpg delete mode 100644 views_postprocessing/shapefiles/ne_10m_admin_0_countries/ne_10m_admin_0_countries.dbf delete mode 100644 views_postprocessing/shapefiles/ne_10m_admin_0_countries/ne_10m_admin_0_countries.prj delete mode 100644 views_postprocessing/shapefiles/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp delete mode 100644 views_postprocessing/shapefiles/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shx delete mode 100644 views_postprocessing/shapefiles/ne_110m_admin_0_countries/ne_110m_admin_0_countries.cpg delete mode 100755 views_postprocessing/shapefiles/ne_110m_admin_0_countries/ne_110m_admin_0_countries.dbf delete mode 100755 views_postprocessing/shapefiles/ne_110m_admin_0_countries/ne_110m_admin_0_countries.prj delete mode 100755 views_postprocessing/shapefiles/ne_110m_admin_0_countries/ne_110m_admin_0_countries.shp delete mode 100755 views_postprocessing/shapefiles/ne_110m_admin_0_countries/ne_110m_admin_0_countries.shx delete mode 100644 views_postprocessing/shapefiles/priogrid_cellshp/priogrid_cell.dbf delete mode 100644 views_postprocessing/shapefiles/priogrid_cellshp/priogrid_cell.prj delete mode 100644 views_postprocessing/shapefiles/priogrid_cellshp/priogrid_cell.qpj delete mode 100644 views_postprocessing/shapefiles/priogrid_cellshp/priogrid_cell.shp delete mode 100644 views_postprocessing/shapefiles/priogrid_cellshp/priogrid_cell.shx delete mode 100644 views_postprocessing/unfao/mapping/README.md delete mode 100644 views_postprocessing/unfao/mapping/__init__.py delete mode 100644 views_postprocessing/unfao/mapping/mapping.py diff --git a/docs/CICs/PriogridCountryMapper.md b/docs/CICs/PriogridCountryMapper.md deleted file mode 100644 index e706249..0000000 --- a/docs/CICs/PriogridCountryMapper.md +++ /dev/null @@ -1,156 +0,0 @@ - -# Class Intent Contract: PriogridCountryMapper - -**Status:** Active -**Owner:** PRIO MD&D Team -**Last reviewed:** 2026-06-02 -**Related ADRs:** ADR-001, ADR-003, ADR-009 - ---- - -## 1. Purpose - -> **What is this class for?** - -`PriogridCountryMapper` is the spatial mapping engine that maps PRIO-GRID cell IDs to administrative boundary metadata (country, admin level 1, admin level 2) using largest-overlap spatial intersection against bundled shapefiles. - -It is the single authoritative source of geographic assignment in this repository. - ---- - -## 2. Non-Goals (Explicit Exclusions) - -- This class does **not** perform data pipeline orchestration or delivery -- This class does **not** fetch data from external services (Appwrite, ViewsER) -- This class does **not** manage pipeline lifecycle (read/transform/validate/save) -- This class does **not** modify or persist prediction data -- This class does **not** perform geodesic area calculations — it uses planar area on EPSG:4326 geometries -- This class does **not** handle partner-specific output formatting - ---- - -## 3. Responsibilities and Guarantees - -- Guarantees deterministic assignment: the same GID always maps to the same country/admin region given the same input shapefiles and equivalent cache state (caveat: stale disk cache from before a shapefile update violates this — see C-14) -- Guarantees that assignment uses the **largest overlap** rule: the administrative region with the highest area overlap ratio with the grid cell wins -- Guarantees that all loaded shapefiles have invalid geometries fixed via `make_valid()` and are validated for required columns and CRS at initialization -- Guarantees that spatial indices are built for all loaded GeoDataFrames to enable efficient queries -- Guarantees that cache mode (disk vs memory) does not affect mapping results — only performance -- Provides batch processing capabilities for enriching DataFrames with geographic metadata -- Provides both forward (GID → country) and reverse (country → GIDs) lookup - ---- - -## 4. Inputs and Assumptions - -- Requires 4 shapefiles at initialization: Natural Earth 10m countries, PRIO-GRID cells, GAUL 2024 L1, GAUL 2024 L2 -- Assumes all shapefiles use EPSG:4326 (WGS84) coordinate reference system -- Assumes Natural Earth data contains columns: `ISO_A3`, `NAME_EN`, `geometry` -- Assumes PRIO-GRID data contains column: `gid`, `geometry` -- Assumes GAUL data contains columns: `gaul1_code`/`gaul2_code`, `gaul1_name`/`gaul2_name`, `iso3_code`, `geometry` -- Assumes `joblib` is available for disk caching and `cachetools` for memory caching - -Assumptions that are not met **must cause failure**, not fallback behavior. - ---- - -## 5. Outputs and Side Effects - -**Outputs:** -- `find_country_for_gid(gid)` → dict with `iso_a3`, `country_name`, `overlap_ratio`, `method`, or `None` -- `find_admin1_for_gid(gid)` → dict with `gaul1_code`, `gaul1_name`, `iso3_code`, `method`, or `None` -- `find_admin2_for_gid(gid)` → dict with `gaul2_code`, `gaul2_name`, `iso3_code`, `method`, or `None` -- `find_gids_for_country(iso_a3)` → list of integer GIDs -- `enrich_dataframe_with_pg_info(df)` → DataFrame with added geographic columns - -**Side Effects:** -- Creates disk cache directory (`~/.priogrid_mapper_cache/`) if disk caching enabled -- Writes joblib cache files to disk (persistent across sessions) -- Allocates a multiprocessing Pool (cleaned up in `__del__`) -- Logs progress at INFO level during batch operations - ---- - -## 6. Failure Modes and Loudness - -- **Missing shapefiles:** `FileNotFoundError` at initialization — the mapper cannot be instantiated -- **Invalid geometries in shapefiles:** Fixed automatically via `make_valid()` at load time. If intersection still fails at runtime, logged as WARNING and the country is skipped in the overlap calculation. -- **GID not found:** Returns `None` — this is not an error (ocean cells legitimately have no country) -- **Zero-area grid geometry:** Returns `None` with WARNING log — a degenerate cell (collapsed polygon) cannot have meaningful overlap -- **Invalid point geometry:** Raises `ValueError` -- **Required columns missing from shapefiles:** Raises `ValueError` at initialization - -The following **must never** fail silently: -- Shapefile loading failures -- CRS validation failures -- Required column absence - ---- - -## 7. Boundaries and Interactions - -**Allowed interactions:** -- Reads shapefiles from the `views_postprocessing/shapefiles/` directory (Geographic Data Assets layer) -- Reads/writes to disk cache directory -- Uses `geopandas`, `shapely`, `joblib`, `cachetools` for spatial operations and caching - -**Must not depend on:** -- Pipeline Managers (`UNFAOPostProcessorManager` or any orchestration code) -- External services (Appwrite, ViewsER) -- Environment variables or runtime configuration beyond cache directory path - -This anchors the class within ADR-002 (topology): it sits at the Spatial Mapping Engine layer, above Geographic Data Assets, below Pipeline Managers. - ---- - -## 8. Examples of Correct Usage - -```python -from views_postprocessing.unfao.mapping.mapping import PriogridCountryMapper - -mapper = PriogridCountryMapper(use_disk_cache=True) - -# Single lookup -result = mapper.find_country_for_gid(148345) -# Returns: {"gid": 148345, "iso_a3": "TZA", "country_name": "Tanzania", "overlap_ratio": 0.95, "method": "largest overlap", ...} - -# DataFrame enrichment -enriched = mapper.enrich_dataframe_with_pg_info(df, pg_id_col="priogrid_gid", batch_size=1000) -``` - ---- - -## 9. Examples of Incorrect Usage - -- **Using the mapper to infer country from coordinates without going through the shapefile intersection** — this bypasses the declared assignment algorithm -- **Calling `batch_country_mapping()` with disk cache enabled** — this method only works with in-memory cache due to a known bug (C-05 in risk register) -- **Modifying `countries_gdf` or `priogrid_gdf` attributes directly** — these are loaded at initialization and treated as immutable reference data -- **Assuming `find_gids_for_country()` returns the exact inverse of `find_country_for_gid()`** — they use different threshold rules (known inconsistency, C-04 in risk register) - ---- - -## 10. Test Alignment - -- **Green tests:** Known-good GID → country mappings for unambiguous cells; schema compliance of enriched DataFrames; determinism across repeated calls -- **Beige tests:** Border cells (cells spanning 2+ countries); batch processing with mixed valid/invalid GIDs; both cache modes producing identical results -- **Red tests:** Non-existent GIDs; ocean-only cells; extreme latitude cells where planar distortion is maximal; corrupted geometries - -Currently: **no tests exist** (C-03 in risk register). This contract defines what tests must verify when written. - ---- - -## 11. Evolution Notes - -- The assignment algorithm (largest overlap) is considered **stable** — changes require a new ADR -- Cache implementation details are **evolving** — may be refactored to eliminate duplication (C-06) -- The forward/reverse lookup inconsistency (C-04) is a known defect to be resolved -- `batch_country_mapping` crash with disk cache (C-05) is a known defect to be resolved - ---- - -## End of Contract - -This document defines the **intended meaning** of `PriogridCountryMapper`. - -Changes to behavior that violate this intent are bugs. -Changes to intent must update this contract. diff --git a/docs/CICs/README.md b/docs/CICs/README.md index 1b88b25..50c2d6f 100644 --- a/docs/CICs/README.md +++ b/docs/CICs/README.md @@ -50,8 +50,9 @@ Contracts must be clear enough that: ## Active Contracts -- `PriogridCountryMapper.md` — Core spatial mapping engine (GID → country/admin boundary assignment) - `UNFAOPostProcessorManager.md` — Pipeline orchestration manager (read → transform → validate → save) +- `GaulLookupEnricher.md` — Precomputed GAUL lookup enrichment (ADR-011; replaced the runtime mapper) +- `ReconciliationModule.md` — Reconcile pgm forecasts to cm country totals (numpy-native) --- diff --git a/pyproject.toml b/pyproject.toml index a162a83..841ff2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,6 @@ readme = "README.md" [tool.poetry.dependencies] python = ">=3.11,<3.15" views-pipeline-core = ">=2.1.3,<3.0.0" -cachetools = "==6.2.1" views-frames = ">=1.0,<2" [build-system] diff --git a/reports/technical_risk_register.md b/reports/technical_risk_register.md index 473e2b8..f703a58 100644 --- a/reports/technical_risk_register.md +++ b/reports/technical_risk_register.md @@ -5,9 +5,9 @@ | Project | views-postprocessing | | Owner | Dylan Pinheiro / PRIO MD&D Team | | Last Updated | 2026-06-24 | -| Total Concerns | 38 | -| Open Concerns | 35 | -| Resolved Concerns | 3 | +| Total Concerns | 40 | +| Open Concerns | 36 | +| Resolved Concerns | 4 | --- @@ -653,6 +653,22 @@ Tier 2: structural fragility under the realistic change of wiring to global, wit --- +### C-40: FAO delivery logic fused to pipeline-core via double inheritance + interleaved infrastructure + +| Field | Value | +|-------|-------| +| ID | C-40 | +| Tier | 2 | +| Source | `expert-code-review` (2026-06-24) | +| Trigger | When pipeline-core changes `PGMDataset` / the data loader / the postprocessor base (it is mid-migration: their #186/#188/#161), or when wanting to unit-test or numpy-ify the FAO enrich/validate without standing up the whole framework | +| Location | `views_postprocessing/unfao/managers/unfao.py:26` (double inheritance); `:78-98` and `:185-199` (inline env/AppwriteConfig/DatastoreModule); `:112-122` (`_append_metadata`), `:147-172` (`_validate`) | + +`UNFAOPostProcessorManager` subclasses **two concrete** pipeline-core base classes (`PostprocessorManager`, `ForecastingModelManager`) and **interleaves infrastructure** (env reading, `AppwriteConfig` construction, `DatastoreModule`, path resolution) with the FAO **business logic** (GAUL enrichment, the 9-column null gate) inside the lifecycle hooks. Consequences: (a) the FAO logic cannot be instantiated or unit-tested without the full framework + Appwrite env + viewser; (b) **pandas cannot leave the delivery path** because the inherited data loader and `PGMDataset` are pandas — gated on pipeline-core's own DataFrame retirement; (c) **SDP exposure** — heavy *inheritance* coupling to a pipeline-core that is itself unstable (mid-migration), so upstream changes break far from their cause (cf. C-27, C-29); (d) it's the repo's only composition-over-inheritance violation. The dependency itself is correct (`unfao.py` genuinely *is* a pipeline-core postprocessor) — the issue is its **blast radius**. Mitigation (does **not** fight the Template-Method framework): keep the subclass as a **thin shell** but extract `enrich` + `validate` + the 9-column contract into a pipeline-core-free core object the manager *calls*, and wrap the Appwrite I/O behind a small delivery-sink adapter (DIP). This makes the FAO logic testable standalone and insulates it from pipeline-core churn. + +See also C-07/C-27/C-29 (pipeline-core coupling symptoms), C-39 (the dead-mapper cleanup that precedes any unfao restructuring). + +--- + ## Disagreements ### D-01: Cache strategy refactoring — extract now vs. characterize first @@ -741,6 +757,16 @@ Tier 2: structural fragility under the realistic change of wiring to global, wit ## Resolved Concerns +### C-39: Dead geopandas runtime mapper + 1.3 GB shapefiles — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-39 | +| Resolved | 2026-06-24 | +| Resolution | Deleted the dead mapper cluster on branch `chore/remove-dead-geopandas-mapper`: `unfao/mapping/` (3,171 lines), the 1.3 GB `shapefiles/` bundle, the mapper tests + `conftest.py`, the two ADR-011 diff scripts, and the CIC — **45 files / ~5,069 deletions**. **geopandas + shapely are now gone from the codebase** (zero references); `cachetools` dropped from pyproject (mapper-only). Verified: deletion broke nothing (keep-tests green). **Dissolves the old-mapper concern cluster** — C-02, C-05, C-06, C-11, C-12, C-14, C-16, C-17, C-19, C-20, C-21 and D-01, D-02 describe code that no longer exists; they are superseded by this deletion and should be relocated to Resolved in a register-curation pass. (C-08's high-latitude-area note also dies on the mapper side; its datafactory dimension stays under C-31.) | + +--- + ### C-01: Silent upload of incomplete geographic metadata to UN FAO — RESOLVED | Field | Value | diff --git a/scripts/diff_enrichment.py b/scripts/diff_enrichment.py deleted file mode 100644 index 7763c6e..0000000 --- a/scripts/diff_enrichment.py +++ /dev/null @@ -1,164 +0,0 @@ -"""Shadow diff: current runtime mapper vs new lookup enricher (ADR-011, Stage 2 prep). - -Runs BOTH geographic-enrichment engines on the africa_me_legacy cell set and -compares all 9 contract columns per cell. Every difference is classified so we -can account, precisely, for what changes when the engine is swapped — and -confirm there are no UNEXPLAINED differences before going live. - -Requires the real shapefiles (git-lfs pull) so the current mapper can run, and -the committed lookup parquet for the new enricher. Writes: - reports/enrichment_diff/diff_cells.csv one row per africa_me cell - reports/enrichment_diff/summary.json counts per class (for plots/report) - -The classification deliberately separates "country code-system difference" -(same GAUL country, Natural Earth ISO vs GAUL ISO string) from genuine -"country reassignment" (different GAUL country), so the headline change count -is not inflated by a pure naming convention. -""" - -from __future__ import annotations - -import json -import logging -import os -import warnings -from pathlib import Path - -import numpy as np -import pandas as pd - -from views_postprocessing.unfao.enrichment import GaulLookupEnricher -from views_postprocessing.unfao.gaul_schema import METADATA_COLS - -# Quiet the noisy mapper run; the imports above are clean and stay at top. -warnings.filterwarnings("ignore") -logging.getLogger().setLevel(logging.WARNING) - - -def _resolve_datafactory() -> Path: - env = os.environ.get("VIEWS_DATAFACTORY") - if env: - return Path(env) - return Path(__file__).resolve().parents[2] / "views-datafactory" - - -_DATAFACTORY = _resolve_datafactory() -_AFRICA_ME = _DATAFACTORY / "src" / "datafactory_query" / "africa_me_legacy_pgids.json" -_AZORES_GIDS = {182470, 183190, 183909, 183910, 186058, 186778} -_OUT = Path("reports/enrichment_diff") - - -def _run_new(gids: list[int]) -> pd.DataFrame: - enr = GaulLookupEnricher() - df = pd.DataFrame({"priogrid_gid": gids, "month_id": 0}) - out = enr.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_gid", time_id_col="month_id", - ).set_index("priogrid_gid")[METADATA_COLS] - return out.add_suffix("__new") - - -def _run_old(gids: list[int]) -> pd.DataFrame: - from views_postprocessing.unfao.mapping.mapping import get_default_mapper - mapper = get_default_mapper() - df = pd.DataFrame({"priogrid_gid": gids, "month_id": 0}) - raw = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_gid", time_id_col="month_id", - only_metadata=True, batch_size=1000, use_multiprocessing=True, - show_progress=False, - ) - # The mapper may omit columns for unmapped cells; reindex to the contract. - raw = raw.set_index("priogrid_gid") - for c in METADATA_COLS: - if c not in raw.columns: - raw[c] = np.nan - return raw[METADATA_COLS].add_suffix("__old") - - -def _classify(row: pd.Series) -> tuple[str, str]: - """Return (presence, change_class) for one cell.""" - has_old = pd.notna(row["country_iso_a3__old"]) - has_new = pd.notna(row["country_iso_a3__new"]) - - if has_new and not has_old: - if int(row.name) in _AZORES_GIDS: - return "new_only", "azores_supplement" - return "new_only", "coastal_recovery" - if has_old and not has_new: - return "old_only", "dropped_by_land_gaul" - if not has_old and not has_new: - return "neither", "both_unmapped" - - # Present in both — compare the 9 columns. - diffs = [c for c in METADATA_COLS - if not _eq(row[f"{c}__old"], row[f"{c}__new"])] - if not diffs: - return "both", "identical" - - gaul0_changed = "admin1_gaul0_code" in diffs - iso_changed = "country_iso_a3" in diffs - admin_changed = any(c in diffs for c in - ["admin1_gaul1_code", "admin2_gaul2_code"]) - - if gaul0_changed: - return "both", "country_reassignment" - if iso_changed and not gaul0_changed: - return "both", "country_code_system" - if admin_changed: - return "both", "admin_reallocation" - # Only names/coords differ, codes identical. - if any(c in diffs for c in ["pg_xcoord", "pg_ycoord"]): - return "both", "coordinate_difference" - return "both", "name_only_difference" - - -def _eq(a, b) -> bool: - if pd.isna(a) and pd.isna(b): - return True - if pd.isna(a) or pd.isna(b): - return False - if isinstance(a, float) or isinstance(b, float): - try: - return abs(float(a) - float(b)) < 1e-6 - except (TypeError, ValueError): - return str(a) == str(b) - return str(a) == str(b) - - -def main() -> None: - gids = sorted(json.loads(_AFRICA_ME.read_text())) - print(f"africa_me_legacy cells: {len(gids):,}") - - print("running NEW enricher (lookup merge)...") - new = _run_new(gids) - print("running OLD mapper (real shapefiles, this takes a few minutes)...") - old = _run_old(gids) - - both = old.join(new, how="outer") - both.index.name = "priogrid_gid" - - classified = both.apply(_classify, axis=1, result_type="expand") - both["presence"] = classified[0] - both["change_class"] = classified[1] - - _OUT.mkdir(parents=True, exist_ok=True) - both.to_csv(_OUT / "diff_cells.csv") - - counts = both["change_class"].value_counts().to_dict() - n_changed = int((both["change_class"] != "identical").sum() - - counts.get("both_unmapped", 0)) - summary = { - "n_cells": len(both), - "counts": counts, - "n_changed_excluding_identical_and_unmapped": n_changed, - "unexplained": int(both["change_class"].isin(["UNEXPLAINED"]).sum()), - } - (_OUT / "summary.json").write_text(json.dumps(summary, indent=2)) - - print("\n=== difference classes ===") - for k, v in sorted(counts.items(), key=lambda kv: -kv[1]): - print(f" {k:28s} {v:>6,}") - print(f"\nwrote {_OUT/'diff_cells.csv'} and summary.json") - - -if __name__ == "__main__": - main() diff --git a/scripts/plot_enrichment_diff.py b/scripts/plot_enrichment_diff.py deleted file mode 100644 index 0102a9a..0000000 --- a/scripts/plot_enrichment_diff.py +++ /dev/null @@ -1,186 +0,0 @@ -"""Rich verification maps for the enrichment diff (ADR-011, Stage 2). - -Renders the old-mapper-vs-new-lookup differences over africa_me as maps, so the -change can be shown and accounted for visually. Reads -reports/enrichment_diff/diff_cells.csv and writes PNGs to -reports/enrichment_diff/maps/. - -Cells are PRIO-GRID 0.5-degree squares drawn with pcolormesh on a regular grid; -country outlines come from Natural Earth 110m (fast context layer). -""" - -from __future__ import annotations - -from pathlib import Path - -import matplotlib -matplotlib.use("Agg") -import matplotlib.colors as mcolors -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd - -from views_postprocessing.unfao.gaul_schema import CELL_SIZE, colrow - -_DIFF = Path("reports/enrichment_diff/diff_cells.csv") -_MAPS = Path("reports/enrichment_diff/maps") -_NE110 = Path("views_postprocessing/shapefiles/ne_110m_admin_0_countries/" - "ne_110m_admin_0_countries.shp") -_CELL = CELL_SIZE - -CLASS_COLORS = { - "identical": "#e8e8e8", - "country_reassignment": "#d62728", - "admin_reallocation": "#ff7f0e", - "coastal_recovery": "#2ca02c", - "dropped_by_land_gaul": "#9467bd", - "both_unmapped": "#7f7f7f", -} - - -def _gid_to_colrow(gid): - return colrow(gid) - - -def _borders(ax, bbox): - try: - import geopandas as gpd - ne = gpd.read_file(_NE110) - ne.boundary.plot(ax=ax, color="black", linewidth=0.4, zorder=3) - except Exception: - pass - ax.set_xlim(bbox[0], bbox[1]) - ax.set_ylim(bbox[2], bbox[3]) - ax.set_xlabel("longitude") - ax.set_ylabel("latitude") - - -def _grid(values: pd.Series, bbox): - """Build a masked 2D array over bbox at 0.5deg from a per-gid value Series.""" - col0 = int((bbox[0] + 180) / _CELL) - row0 = int((bbox[2] + 90) / _CELL) - ncol = int((bbox[1] - bbox[0]) / _CELL) - nrow = int((bbox[3] - bbox[2]) / _CELL) - grid = np.full((nrow, ncol), np.nan) - for gid, v in values.items(): - c, r = _gid_to_colrow(gid) - cc, rr = c - col0, r - row0 - if 0 <= cc < ncol and 0 <= rr < nrow: - grid[rr, cc] = v - extent = [bbox[0], bbox[1], bbox[2], bbox[3]] - return grid, extent - - -def _bbox(df, pad=2.0): - xs = [(_gid_to_colrow(g)[0]) * _CELL - 180 for g in df.index] - ys = [(_gid_to_colrow(g)[1]) * _CELL - 90 for g in df.index] - return [min(xs) - pad, max(xs) + _CELL + pad, - min(ys) - pad, max(ys) + _CELL + pad] - - -def map_agree_disagree(df, bbox): - fig, ax = plt.subplots(figsize=(11, 11)) - code = (df["change_class"] != "identical").astype(int) - grid, extent = _grid( - pd.Series(code.values, index=df.index), bbox) - cmap = mcolors.ListedColormap(["#e8e8e8", "#d62728"]) - ax.imshow(np.ma.masked_invalid(grid), origin="lower", extent=extent, - cmap=cmap, vmin=0, vmax=1, interpolation="nearest", zorder=2) - _borders(ax, bbox) - n_changed = int(code.sum()) - ax.set_title(f"africa_me: enrichment agreement\n" - f"{len(df)-n_changed:,} identical (grey) | " - f"{n_changed:,} changed (red)") - ax.legend(handles=[mpatches.Patch(color="#e8e8e8", label="identical"), - mpatches.Patch(color="#d62728", label="changed")], - loc="lower left") - fig.savefig(_MAPS / "01_agree_disagree.png", dpi=130, bbox_inches="tight") - plt.close(fig) - - -def map_by_class(df, bbox): - classes = [c for c in CLASS_COLORS if c in set(df["change_class"])] - code_of = {c: i for i, c in enumerate(classes)} - codes = df["change_class"].map(code_of) - grid, extent = _grid(pd.Series(codes.values, index=df.index), bbox) - cmap = mcolors.ListedColormap([CLASS_COLORS[c] for c in classes]) - fig, ax = plt.subplots(figsize=(11, 11)) - ax.imshow(np.ma.masked_invalid(grid), origin="lower", extent=extent, - cmap=cmap, vmin=-0.5, vmax=len(classes) - 0.5, - interpolation="nearest", zorder=2) - _borders(ax, bbox) - ax.set_title("africa_me: difference class per cell") - ax.legend(handles=[mpatches.Patch(color=CLASS_COLORS[c], - label=f"{c} ({int((df['change_class']==c).sum())})") - for c in classes], loc="lower left", fontsize=9) - fig.savefig(_MAPS / "02_by_class.png", dpi=130, bbox_inches="tight") - plt.close(fig) - - -def map_country_before_after(df, bbox): - present = df[df["country_iso_a3__old"].notna() - | df["country_iso_a3__new"].notna()] - countries = sorted(set(present["country_iso_a3__old"].dropna()) - | set(present["country_iso_a3__new"].dropna())) - cof = {c: i for i, c in enumerate(countries)} - cmap = plt.get_cmap("tab20", max(len(countries), 1)) - fig, axes = plt.subplots(1, 2, figsize=(20, 10)) - for ax, col, title in [ - (axes[0], "country_iso_a3__old", "OLD mapper (Natural Earth country)"), - (axes[1], "country_iso_a3__new", "NEW lookup (GAUL country)"), - ]: - codes = df[col].map(cof) - grid, extent = _grid(pd.Series(codes.values, index=df.index), bbox) - ax.imshow(np.ma.masked_invalid(grid), origin="lower", extent=extent, - cmap=cmap, vmin=0, vmax=len(countries), interpolation="nearest", - zorder=2) - _borders(ax, bbox) - ax.set_title(title) - fig.suptitle("Country assignment: before vs after (color = country)") - fig.savefig(_MAPS / "03_country_before_after.png", dpi=120, - bbox_inches="tight") - plt.close(fig) - - -def map_zoom(df, bbox, name, title): - sub = df.copy() - classes = [c for c in CLASS_COLORS if c in set(sub["change_class"])] - code_of = {c: i for i, c in enumerate(classes)} - grid, extent = _grid( - pd.Series(sub["change_class"].map(code_of).values, index=sub.index), bbox) - cmap = mcolors.ListedColormap([CLASS_COLORS[c] for c in classes]) - fig, ax = plt.subplots(figsize=(10, 10)) - ax.imshow(np.ma.masked_invalid(grid), origin="lower", extent=extent, - cmap=cmap, vmin=-0.5, vmax=len(classes) - 0.5, - interpolation="nearest", zorder=2, alpha=0.85) - _borders(ax, bbox) - ax.set_title(title) - ax.legend(handles=[mpatches.Patch(color=CLASS_COLORS[c], label=c) - for c in classes if c != "identical"], - loc="lower left", fontsize=9) - fig.savefig(_MAPS / name, dpi=140, bbox_inches="tight") - plt.close(fig) - - -def main(): - _MAPS.mkdir(parents=True, exist_ok=True) - df = pd.read_csv(_DIFF).set_index("priogrid_gid") - bbox = _bbox(df) - - map_agree_disagree(df, bbox) - map_by_class(df, bbox) - map_country_before_after(df, bbox) - # Zoom 1: Lesotho / South Africa enclave (biggest reassignment cluster). - map_zoom(df, [24, 36, -32, -24], "04_zoom_lesotho_sa.png", - "Zoom: Lesotho / Eswatini / South Africa border reassignments") - # Zoom 2: Namibia / Botswana / South Africa tri-border. - map_zoom(df, [13, 30, -30, -17], "05_zoom_namibia_botswana.png", - "Zoom: Namibia / Botswana / South Africa / Mozambique borders") - print(f"wrote maps to {_MAPS}/") - for p in sorted(_MAPS.glob("*.png")): - print(f" {p.name} ({p.stat().st_size//1024} KB)") - - -if __name__ == "__main__": - main() diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index 0fcb97b..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,143 +0,0 @@ -"""Test fixtures for views-postprocessing. - -Handles the module-level set_default_mapper() side effect (C-02) by -intercepting shapefile loading during import and substituting synthetic -geodata. This lets tests run without Git LFS or production shapefiles. -""" - -import pytest -import geopandas as gpd -from shapely.geometry import box -import tempfile -import shutil -import atexit -from pathlib import Path - - -# ── Synthetic shapefile data ────────────────────────────────────────────── - -_COUNTRIES = gpd.GeoDataFrame( - { - "ISO_A3": ["AAA", "BBB"], - "NAME_EN": ["CountryA", "CountryB"], - "geometry": [box(0.0, 0.0, 1.0, 2.0), box(1.0, 0.0, 2.0, 2.0)], - }, - crs="EPSG:4326", -) - -_PRIOGRID = gpd.GeoDataFrame( - { - "gid": [1, 2, 3, 4], - "xcoord": [0.25, 1.75, 0.85, 0.25], - "ycoord": [0.25, 0.25, 0.25, 1.25], - "geometry": [ - box(0.0, 0.0, 0.5, 0.5), # cell 1: fully in AAA - box(1.5, 0.0, 2.0, 0.5), # cell 2: fully in BBB - box(0.7, 0.0, 1.2, 0.5), # cell 3: 60% AAA / 40% BBB - box(0.0, 1.0, 0.5, 1.5), # cell 4: fully in AAA (upper) - ], - }, - crs="EPSG:4326", -) - -_ADMIN1 = gpd.GeoDataFrame( - { - "gaul1_code": [101, 102, 201, 202], - "gaul1_name": ["A-North", "A-South", "B-North", "B-South"], - "gaul0_code": [1, 1, 2, 2], - "gaul0_name": ["CountryA", "CountryA", "CountryB", "CountryB"], - "iso3_code": ["AAA", "AAA", "BBB", "BBB"], - "geometry": [ - box(0.0, 1.0, 1.0, 2.0), box(0.0, 0.0, 1.0, 1.0), - box(1.0, 1.0, 2.0, 2.0), box(1.0, 0.0, 2.0, 1.0), - ], - }, - crs="EPSG:4326", -) - -_ADMIN2 = gpd.GeoDataFrame( - { - "gaul2_code": [1011, 1012, 1021, 1022, 2011, 2012, 2021, 2022], - "gaul2_name": [ - "A-North-W", "A-North-E", "A-South-W", "A-South-E", - "B-North-W", "B-North-E", "B-South-W", "B-South-E", - ], - "gaul1_code": [101, 101, 102, 102, 201, 201, 202, 202], - "gaul1_name": [ - "A-North", "A-North", "A-South", "A-South", - "B-North", "B-North", "B-South", "B-South", - ], - "gaul0_code": [1, 1, 1, 1, 2, 2, 2, 2], - "gaul0_name": [ - "CountryA", "CountryA", "CountryA", "CountryA", - "CountryB", "CountryB", "CountryB", "CountryB", - ], - "iso3_code": ["AAA", "AAA", "AAA", "AAA", "BBB", "BBB", "BBB", "BBB"], - "geometry": [ - box(0.0, 1.0, 0.5, 2.0), box(0.5, 1.0, 1.0, 2.0), - box(0.0, 0.0, 0.5, 1.0), box(0.5, 0.0, 1.0, 1.0), - box(1.0, 1.0, 1.5, 2.0), box(1.5, 1.0, 2.0, 2.0), - box(1.0, 0.0, 1.5, 1.0), box(1.5, 0.0, 2.0, 1.0), - ], - }, - crs="EPSG:4326", -) - - -# ── Write synthetics to disk ───────────────────────────────────────────── - -_tmpdir = Path(tempfile.mkdtemp(prefix="views_test_")) -atexit.register(lambda: shutil.rmtree(_tmpdir, ignore_errors=True)) - -_synth_paths = {} -for _name, _gdf in [("countries", _COUNTRIES), ("priogrid", _PRIOGRID), - ("admin1", _ADMIN1), ("admin2", _ADMIN2)]: - _d = _tmpdir / _name - _d.mkdir() - _p = _d / f"{_name}.shp" - _gdf.to_file(_p) - _synth_paths[_name] = str(_p) - -_PATH_MAP = { - "ne_10m_admin_0_countries": _synth_paths["countries"], - "ne_110m_admin_0_countries": _synth_paths["countries"], - "priogrid_cellshp": _synth_paths["priogrid"], - "GAUL_2024_L1": _synth_paths["admin1"], - "GAUL_2024_L2": _synth_paths["admin2"], -} - - -# ── Permanent gpd.read_file intercept ──────────────────────────────────── -# Keep the patch active for the entire test session so that constructing -# new PriogridCountryMapper instances in fixtures also uses synthetics. - -_orig_read_file = gpd.read_file - - -def _intercepting_read_file(path, *args, **kwargs): - path_str = str(path) - for substr, synth in _PATH_MAP.items(): - if substr in path_str: - return _orig_read_file(synth, *args, **kwargs) - return _orig_read_file(path, *args, **kwargs) - - -gpd.read_file = _intercepting_read_file - -# Import the mapping module — set_default_mapper() now uses synthetics -import views_postprocessing.unfao.mapping.mapping as _mapping_mod # noqa: E402 - - -# ── Session-scoped fixtures ────────────────────────────────────────────── - -@pytest.fixture(scope="session") -def mapper(): - """PriogridCountryMapper with in-memory cache, backed by synthetic geodata.""" - return _mapping_mod.PriogridCountryMapper(use_disk_cache=False) - - -@pytest.fixture(scope="session") -def mapper_disk_cache(tmp_path_factory): - """PriogridCountryMapper with disk cache, backed by synthetic geodata.""" - cache_dir = tmp_path_factory.mktemp("disk_cache") - return _mapping_mod.PriogridCountryMapper(use_disk_cache=True, cache_dir=str(cache_dir)) diff --git a/tests/test_integration.py b/tests/test_integration.py deleted file mode 100644 index ff8ac73..0000000 --- a/tests/test_integration.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Integration tests for the mapper-to-manager boundary contract. - -Verifies that PriogridCountryMapper.enrich_dataframe_with_pg_info() produces -output compatible with UNFAOPostProcessorManager._append_metadata()'s filter_cols -and _validate()'s null checks. - -Closes: https://github.com/views-platform/views-postprocessing/issues/4 -Campaign: Claim 3.4 (FALSIFIED — no integration test existed) -""" - -import pandas as pd - -FILTER_COLS_METADATA = [ - "pg_xcoord", - "pg_ycoord", - "country_iso_a3", - "admin1_gaul1_code", - "admin1_gaul1_name", - "admin1_gaul0_code", - "admin1_gaul0_name", - "admin2_gaul2_code", - "admin2_gaul2_name", -] - -REQUIRED_METADATA_COLS = FILTER_COLS_METADATA - - -class TestMapperToManagerBoundary: - """Verify the enrichment output matches what the manager consumes.""" - - def test_all_filter_cols_present_in_enrichment(self, mapper): - df = pd.DataFrame({ - "priogrid_gid": [1, 2, 3, 4], - "month_id": [100, 100, 100, 100], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_gid", time_id_col="month_id", - only_metadata=True, batch_size=10, - use_multiprocessing=False, show_progress=False, - ) - for col in FILTER_COLS_METADATA: - assert col in enriched.columns, ( - f"filter_cols column '{col}' missing from enrichment output. " - f"Available: {sorted(enriched.columns.tolist())}" - ) - - def test_no_nulls_in_metadata_columns_for_valid_gids(self, mapper): - df = pd.DataFrame({ - "priogrid_gid": [1, 2, 4], - "month_id": [100, 100, 100], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_gid", time_id_col="month_id", - only_metadata=True, batch_size=10, - use_multiprocessing=False, show_progress=False, - ) - for col in FILTER_COLS_METADATA: - null_count = enriched[col].isnull().sum() - assert null_count == 0, ( - f"Column '{col}' has {null_count} nulls in enrichment output for valid GIDs" - ) - - def test_country_iso_a3_contains_valid_codes(self, mapper): - df = pd.DataFrame({ - "priogrid_gid": [1, 2, 3, 4], - "month_id": [100, 100, 100, 100], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_gid", time_id_col="month_id", - only_metadata=True, batch_size=10, - use_multiprocessing=False, show_progress=False, - ) - valid_codes = {"AAA", "BBB"} - actual_codes = set(enriched["country_iso_a3"].dropna().unique()) - assert actual_codes.issubset(valid_codes), ( - f"Unexpected ISO codes: {actual_codes - valid_codes}" - ) - - def test_admin_codes_are_numeric(self, mapper): - df = pd.DataFrame({ - "priogrid_gid": [1, 2, 4], - "month_id": [100, 100, 100], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_gid", time_id_col="month_id", - only_metadata=True, batch_size=10, - use_multiprocessing=False, show_progress=False, - ) - for col in ["admin1_gaul1_code", "admin1_gaul0_code", "admin2_gaul2_code"]: - assert pd.api.types.is_numeric_dtype(enriched[col]), ( - f"Column '{col}' should be numeric, got {enriched[col].dtype}" - ) - - def test_coordinates_are_float(self, mapper): - df = pd.DataFrame({ - "priogrid_gid": [1, 2], - "month_id": [100, 100], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_gid", time_id_col="month_id", - only_metadata=True, batch_size=10, - use_multiprocessing=False, show_progress=False, - ) - for col in ["pg_xcoord", "pg_ycoord"]: - assert pd.api.types.is_float_dtype(enriched[col]), ( - f"Column '{col}' should be float, got {enriched[col].dtype}" - ) - - def test_row_count_preserved(self, mapper): - df = pd.DataFrame({ - "priogrid_gid": [1, 2, 3, 4], - "month_id": [100, 100, 100, 100], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_gid", time_id_col="month_id", - only_metadata=True, batch_size=10, - use_multiprocessing=False, show_progress=False, - ) - assert len(enriched) == 4 - - -class TestEnrichmentThenValidation: - """Verify that mapper output passes the manager's validation logic.""" - - def test_enriched_output_passes_validation(self, mapper): - df = pd.DataFrame({ - "priogrid_gid": [1, 2, 4], - "month_id": [100, 100, 100], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_gid", time_id_col="month_id", - only_metadata=True, batch_size=10, - use_multiprocessing=False, show_progress=False, - ) - for col in REQUIRED_METADATA_COLS: - assert col in enriched.columns, f"Validation would fail: '{col}' missing" - null_count = enriched[col].isnull().sum() - assert null_count == 0, ( - f"Validation would fail: '{col}' has {null_count} nulls" - ) diff --git a/tests/test_mapping.py b/tests/test_mapping.py deleted file mode 100644 index ff6d7ec..0000000 --- a/tests/test_mapping.py +++ /dev/null @@ -1,274 +0,0 @@ -"""Tests for PriogridCountryMapper. - -Covers CIC guarantees and failure modes using synthetic shapefiles. -See docs/CICs/PriogridCountryMapper.md for the contract under test. -""" - -import pytest -import pandas as pd -from shapely.geometry import Point - - -# --------------------------------------------------------------------------- -# GREEN: Determinism (CIC §3 — "same GID always maps to same country") -# --------------------------------------------------------------------------- - -class TestDeterminism: - def test_same_gid_returns_identical_result(self, mapper): - result1 = mapper.find_country_for_gid(1) - result2 = mapper.find_country_for_gid(1) - assert result1 == result2 - - def test_determinism_across_all_cells(self, mapper): - for gid in [1, 2, 3, 4]: - assert mapper.find_country_for_gid(gid) == mapper.find_country_for_gid(gid) - - def test_admin1_determinism(self, mapper): - for gid in [1, 2, 3, 4]: - assert mapper.find_admin1_for_gid(gid) == mapper.find_admin1_for_gid(gid) - - def test_admin2_determinism(self, mapper): - for gid in [1, 2, 3, 4]: - assert mapper.find_admin2_for_gid(gid) == mapper.find_admin2_for_gid(gid) - - -# --------------------------------------------------------------------------- -# GREEN: Largest-overlap assignment (CIC §3 — core algorithm) -# --------------------------------------------------------------------------- - -class TestLargestOverlap: - def test_cell_fully_in_country_a(self, mapper): - result = mapper.find_country_for_gid(1) - assert result is not None - assert result["iso_a3"] == "AAA" - assert result["method"] == "largest overlap" - - def test_cell_fully_in_country_b(self, mapper): - result = mapper.find_country_for_gid(2) - assert result is not None - assert result["iso_a3"] == "BBB" - - def test_border_cell_assigned_to_larger_overlap(self, mapper): - """Cell 3 spans x=[0.7, 1.2]: 0.3 in AAA, 0.2 in BBB → AAA wins.""" - result = mapper.find_country_for_gid(3) - assert result is not None - assert result["iso_a3"] == "AAA" - assert result["overlap_ratio"] > 0.5 - - def test_overlap_ratio_is_float(self, mapper): - result = mapper.find_country_for_gid(1) - assert isinstance(result["overlap_ratio"], float) - assert 0.0 < result["overlap_ratio"] <= 1.0 - - -# --------------------------------------------------------------------------- -# GREEN: Admin-level assignment (CIC §3) -# --------------------------------------------------------------------------- - -class TestAdminAssignment: - def test_admin1_for_interior_cell(self, mapper): - result = mapper.find_admin1_for_gid(1) - assert result is not None - assert result["gaul1_name"] == "A-South" - assert result["iso3_code"] == "AAA" - - def test_admin1_for_upper_cell(self, mapper): - result = mapper.find_admin1_for_gid(4) - assert result is not None - assert result["gaul1_name"] == "A-North" - - def test_admin2_for_interior_cell(self, mapper): - result = mapper.find_admin2_for_gid(1) - assert result is not None - assert result["gaul2_name"] == "A-South-W" - - def test_admin1_consistent_with_country(self, mapper): - for gid in [1, 2, 4]: - country = mapper.find_country_for_gid(gid) - admin1 = mapper.find_admin1_for_gid(gid) - if country and admin1: - assert admin1["iso3_code"] == country["iso_a3"] - - -# --------------------------------------------------------------------------- -# GREEN: Output schema for DataFrame enrichment (CIC §3, §5) -# --------------------------------------------------------------------------- - -class TestEnrichDataframe: - def test_enrichment_adds_expected_columns(self, mapper): - df = pd.DataFrame({ - "priogrid_id": [1, 2, 4], - "month_id": [100, 100, 100], - "pred_value": [0.5, 0.3, 0.1], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_id", time_id_col="month_id", - batch_size=10, use_multiprocessing=False, show_progress=False - ) - expected_cols = [ - "pg_xcoord", "pg_ycoord", "country_iso_a3", - "admin1_gaul1_code", "admin1_gaul1_name", - "admin1_gaul0_code", "admin1_gaul0_name", - "admin2_gaul2_code", "admin2_gaul2_name", - ] - for col in expected_cols: - assert col in enriched.columns, f"Missing column: {col}" - - def test_enrichment_preserves_row_identity(self, mapper): - df = pd.DataFrame({ - "priogrid_id": [1, 2], - "month_id": [100, 100], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_id", time_id_col="month_id", - batch_size=10, use_multiprocessing=False, show_progress=False - ) - assert list(enriched["priogrid_id"]) == [1, 2] - - def test_enrichment_row_count_preserved(self, mapper): - df = pd.DataFrame({ - "priogrid_id": [1, 2, 3, 4], - "month_id": [100, 100, 100, 100], - }) - enriched = mapper.enrich_dataframe_with_pg_info( - df, pg_id_col="priogrid_id", time_id_col="month_id", - batch_size=10, use_multiprocessing=False, show_progress=False - ) - assert len(enriched) == 4 - - -# --------------------------------------------------------------------------- -# GREEN: Cache equivalence (CIC §3 — "cache mode doesn't affect results") -# --------------------------------------------------------------------------- - -class TestCacheEquivalence: - def test_memory_and_disk_cache_produce_same_country(self, mapper, mapper_disk_cache): - for gid in [1, 2, 3, 4]: - mem_result = mapper.find_country_for_gid(gid) - disk_result = mapper_disk_cache.find_country_for_gid(gid) - if mem_result is None: - assert disk_result is None - else: - assert mem_result["iso_a3"] == disk_result["iso_a3"] - assert abs(mem_result["overlap_ratio"] - disk_result["overlap_ratio"]) < 1e-9 - - def test_memory_and_disk_cache_produce_same_admin1(self, mapper, mapper_disk_cache): - for gid in [1, 2, 4]: - mem = mapper.find_admin1_for_gid(gid) - disk = mapper_disk_cache.find_admin1_for_gid(gid) - if mem is None: - assert disk is None - else: - assert mem["gaul1_code"] == disk["gaul1_code"] - - -# --------------------------------------------------------------------------- -# RED: GID not found returns None (CIC §6) -# --------------------------------------------------------------------------- - -class TestGidNotFound: - def test_nonexistent_gid_returns_none(self, mapper): - assert mapper.find_country_for_gid(999999) is None - - def test_nonexistent_gid_admin1_returns_none(self, mapper): - assert mapper.find_admin1_for_gid(999999) is None - - def test_nonexistent_gid_admin2_returns_none(self, mapper): - assert mapper.find_admin2_for_gid(999999) is None - - -# --------------------------------------------------------------------------- -# RED: Invalid point geometry raises ValueError (CIC §6) -# --------------------------------------------------------------------------- - -class TestInvalidGeometry: - def test_invalid_point_raises(self, mapper): - from shapely import wkt - invalid_point = wkt.loads("POINT (0 0)") - invalid_point = Point(float("nan"), float("nan")) - with pytest.raises((ValueError, Exception)): - mapper.find_gid_for_point(invalid_point) - - -# --------------------------------------------------------------------------- -# RED: Missing shapefile raises at init (CIC §6) -# --------------------------------------------------------------------------- - -class TestMissingShapefile: - def test_missing_country_shapefile_raises(self): - import views_postprocessing.unfao.mapping.mapping as mod - import tempfile - import os - - tmpdir = tempfile.mkdtemp() - missing_path = os.path.join(tmpdir, "nonexistent", "missing.shp") - orig = mod.NATURAL_EARTH_COUNTRY_PATH - mod.NATURAL_EARTH_COUNTRY_PATH = missing_path - try: - with pytest.raises(Exception): - mod.PriogridCountryMapper(use_disk_cache=False) - finally: - mod.NATURAL_EARTH_COUNTRY_PATH = orig - os.rmdir(tmpdir) - - -# --------------------------------------------------------------------------- -# RED: batch_country_mapping crashes with disk cache (C-05) -# --------------------------------------------------------------------------- - -class TestBatchCountryMappingDiskCacheBug: - def test_batch_country_mapping_with_disk_cache_raises(self, mapper_disk_cache): - """Documents known bug C-05: batch_country_mapping accesses - self._country_cache which doesn't exist in disk-cache mode.""" - with pytest.raises(AttributeError): - mapper_disk_cache.batch_country_mapping([1, 2]) - - -# --------------------------------------------------------------------------- -# GREEN: Reverse lookup (find_gids_for_country) -# --------------------------------------------------------------------------- - -class TestReverseLookup: - def test_finds_gids_for_known_country(self, mapper): - gids = mapper.find_gids_for_country("AAA") - assert isinstance(gids, list) - assert len(gids) > 0 - assert 1 in gids # cell 1 is fully in AAA - - def test_unknown_country_returns_empty(self, mapper): - gids = mapper.find_gids_for_country("ZZZ") - assert gids == [] - - def test_interior_cells_in_reverse_lookup(self, mapper): - """Cells fully contained by a country should appear in its reverse lookup.""" - gids_a = mapper.find_gids_for_country("AAA") - assert 1 in gids_a # fully in AAA - assert 4 in gids_a # fully in AAA - - gids_b = mapper.find_gids_for_country("BBB") - assert 2 in gids_b # fully in BBB - - -# --------------------------------------------------------------------------- -# BEIGE: Utility methods -# --------------------------------------------------------------------------- - -class TestUtilityMethods: - def test_get_all_iso_a3_codes(self, mapper): - codes = mapper.get_all_iso_a3_codes() - assert "AAA" in codes - assert "BBB" in codes - - def test_find_country_by_iso_a3(self, mapper): - result = mapper.find_country_by_iso_a3("AAA") - assert result is not None - assert result["country_name"] == "CountryA" - - def test_find_country_by_invalid_code(self, mapper): - assert mapper.find_country_by_iso_a3("ZZ") is None # not 3 chars - - def test_find_all_admin_for_gid(self, mapper): - result = mapper.find_all_admin_for_gid(1) - assert "country" in result - assert "admin1" in result - assert "admin2" in result diff --git a/tests/test_regression.py b/tests/test_regression.py deleted file mode 100644 index 05a429e..0000000 --- a/tests/test_regression.py +++ /dev/null @@ -1,179 +0,0 @@ -"""Regression tests for code fixes applied during the audit session. - -Each test protects a specific fix. Reverting the fix causes the test to fail. - -Closes: https://github.com/views-platform/views-postprocessing/issues/6 -Closes: https://github.com/views-platform/views-postprocessing/issues/7 -Closes: https://github.com/views-platform/views-postprocessing/issues/8 -Campaign: Claims 3.1, 3.3 (test gaps for fixes) -""" - -import logging -import tempfile -from pathlib import Path - -import geopandas as gpd -import pandas as pd -from shapely.geometry import Polygon - - -# --------------------------------------------------------------------------- -# Issue #6: make_valid() preprocessing -# --------------------------------------------------------------------------- - -class TestMakeValidPreprocessing: - """Verify that invalid geometries are corrected at load time.""" - - def test_invalid_country_geometry_fixed_at_load(self): - """A self-intersecting country polygon is fixed by make_valid().""" - import views_postprocessing.unfao.mapping.mapping as mod - - bowtie = Polygon([(0, 0), (1, 1), (1, 0), (0, 1)]) - assert not bowtie.is_valid - - gdf = gpd.GeoDataFrame( - {"ISO_A3": ["TST"], "NAME_EN": ["TestCountry"], "geometry": [bowtie]}, - crs="EPSG:4326", - ) - with tempfile.TemporaryDirectory() as tmpdir: - path = Path(tmpdir) / "countries.shp" - gdf.to_file(path) - - mapper = mod.PriogridCountryMapper.__new__(mod.PriogridCountryMapper) - loaded = mapper._load_and_preprocess_naturalearth(str(path)) - - assert loaded["geometry"].is_valid.all(), "make_valid() should fix invalid geometries" - assert loaded["geometry"].iloc[0].area > 0 - - def test_invalid_admin_geometry_fixed_at_load(self): - """An invalid admin polygon is fixed by make_valid().""" - import views_postprocessing.unfao.mapping.mapping as mod - - bowtie = Polygon([(0, 0), (1, 1), (1, 0), (0, 1)]) - gdf = gpd.GeoDataFrame( - { - "gaul1_code": [1], "gaul1_name": ["Test"], - "gaul0_code": [1], "gaul0_name": ["TestC"], - "iso3_code": ["TST"], "geometry": [bowtie], - }, - crs="EPSG:4326", - ) - with tempfile.TemporaryDirectory() as tmpdir: - path = Path(tmpdir) / "admin.shp" - gdf.to_file(path) - - mapper = mod.PriogridCountryMapper.__new__(mod.PriogridCountryMapper) - loaded = mapper._load_admin_data(str(path), "admin1") - - assert loaded["geometry"].is_valid.all() - - def test_raw_invalid_geometry_is_genuinely_invalid(self): - """Proves the fixture IS invalid before make_valid — the guard is real.""" - bowtie = Polygon([(0, 0), (1, 1), (1, 0), (0, 1)]) - gdf = gpd.GeoDataFrame( - {"ISO_A3": ["TST"], "NAME_EN": ["Test"], "geometry": [bowtie]}, - crs="EPSG:4326", - ) - with tempfile.TemporaryDirectory() as tmpdir: - path = Path(tmpdir) / "raw.shp" - gdf.to_file(path) - raw = gpd.read_file(path) - - assert not raw["geometry"].is_valid.all(), ( - "Test fixture should contain invalid geometry before make_valid" - ) - - -# --------------------------------------------------------------------------- -# Issue #7: Global warning suppression must not return -# --------------------------------------------------------------------------- - -class TestWarningSuppressionRegression: - """Verify global warning suppression is absent.""" - - def test_no_global_warning_suppression(self): - """warnings.filterwarnings('ignore') must not be active globally.""" - import warnings - # Force import of the mapping module (conftest already did this) - import views_postprocessing.unfao.mapping.mapping # noqa: F401 - - global_ignores = [ - f for f in warnings.filters - if f[0] == "ignore" and f[2] is Warning - ] - assert len(global_ignores) == 0, ( - f"Global warning suppression detected: {global_ignores}. " - "Use targeted warnings.catch_warnings() instead." - ) - - def test_non_centroid_warnings_are_visible(self): - """A UserWarning should be visible — global suppression is not active.""" - import warnings - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - warnings.warn("regression test canary", UserWarning) - assert len(w) == 1, ( - "UserWarning should be visible — global suppression must not be active" - ) - - -# --------------------------------------------------------------------------- -# Issue #8: Zero-area grid geometry guard -# --------------------------------------------------------------------------- - -class TestZeroAreaGuardRegression: - """Verify that degenerate zero-area cells are handled with WARNING, not silently.""" - - def test_zero_area_country_returns_none_with_warning(self, mapper, caplog): - """A zero-area cell should return None with a WARNING log.""" - degenerate = Polygon([(0.25, 0.25), (0.5, 0.25), (0.25, 0.25)]) - assert degenerate.area == 0.0 - - mapper.priogrid_gdf = pd.concat([ - mapper.priogrid_gdf, - gpd.GeoDataFrame( - {"gid": [99], "xcoord": [0.3], "ycoord": [0.25], "geometry": [degenerate]}, - crs="EPSG:4326", - ), - ], ignore_index=True) - mapper.priogrid_gdf["centroid"] = mapper.priogrid_gdf["geometry"].centroid - - with caplog.at_level(logging.WARNING): - result = mapper.find_country_for_gid(99) - - assert result is None, "Zero-area cell should return None" - assert any("Zero-area" in r.message and "99" in r.message for r in caplog.records), ( - "Zero-area cell should produce a WARNING log mentioning the GID" - ) - - def test_zero_area_admin1_returns_none(self, mapper): - """Zero-area cell returns None for admin1 lookup too.""" - degenerate = Polygon([(0.25, 0.25), (0.5, 0.25), (0.25, 0.25)]) - if 99 not in mapper.priogrid_gdf["gid"].values: - mapper.priogrid_gdf = pd.concat([ - mapper.priogrid_gdf, - gpd.GeoDataFrame( - {"gid": [99], "xcoord": [0.3], "ycoord": [0.25], "geometry": [degenerate]}, - crs="EPSG:4326", - ), - ], ignore_index=True) - mapper.priogrid_gdf["centroid"] = mapper.priogrid_gdf["geometry"].centroid - - result = mapper.find_admin1_for_gid(99) - assert result is None - - def test_zero_area_admin2_returns_none(self, mapper): - """Zero-area cell returns None for admin2 lookup too.""" - degenerate = Polygon([(0.25, 0.25), (0.5, 0.25), (0.25, 0.25)]) - if 99 not in mapper.priogrid_gdf["gid"].values: - mapper.priogrid_gdf = pd.concat([ - mapper.priogrid_gdf, - gpd.GeoDataFrame( - {"gid": [99], "xcoord": [0.3], "ycoord": [0.25], "geometry": [degenerate]}, - crs="EPSG:4326", - ), - ], ignore_index=True) - mapper.priogrid_gdf["centroid"] = mapper.priogrid_gdf["geometry"].centroid - - result = mapper.find_admin2_for_gid(99) - assert result is None diff --git a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.cpg b/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.cpg deleted file mode 100644 index 105bf61..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.cpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3ad3031f5503a4404af825262ee8232cc04d4ea6683d42c5dd0a2f2a27ac9824 -size 5 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.dbf b/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.dbf deleted file mode 100644 index c7b2d2f..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.dbf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e036df5fb3a5ee64ec2ea17a640fac406ecf0cb908f79f41c7208b431ba6ca44 -size 1533520 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.prj b/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.prj deleted file mode 100644 index 2098c4d..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.prj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a02a27b1d1982c8516d83398e85a3c8b1aef1713c13ef4d84d7bde17430c07c4 -size 145 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.sbn b/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.sbn deleted file mode 100644 index 3a095413c82ebde5dd4c1a2ef3bbd3d163c6d3b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30380 zcmdSB1#ngOx<0&R`AS>rw52U=sk?iN6G(7xDbN;o34yrVcDJ}dg2xS_#07V^y1PO7 zo_DWs=FB<&^W8gl=9}+-nR(~gugg!?iV&6RQR&*XzgMaL?cbZzRH}eS8W+T>b4d@y*G>V}rVQ$Bw*`)csze{=gk3)CtVzYQu4=pB$2^csi(-3($ucU{9j zApPS&FM)Uvd{qMI29OAP4QP3?Q z%|F>uEUMu)l7nssssBlT2l#))$LBji)JuQD>vq=>vb#Y4?0+|SihB=;at#1cANPW& zuKPgL^Zg*|Z6JvB4-j6D`>zPygP@!KReqg^?jiCA4FXY~he4G85s>gtb~F>3@nioY ze;jfM?qv||nO8s*k8CME?Y)2Gz3%tlMfkoBqP_M8i2JYnVDl#EAAMZ6hwLrT zzwMWQ(!UM;t=IYggs}0Sss9)KyRf1D2ZN~pAt0K|P{RM@_n+kO83v*_!wLVB-TzdM zm?Qe)kNoHO_&lml9tfoM9Sx#$G^P)O`Y;$s`mrGXU+aKu9EkkJgZ^=juiHU30YrDw zL=c_*Ng lRGv;J*Pki8H3$NJ;*EYQF0 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 Bp1%M9 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.sbx b/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.sbx deleted file mode 100644 index ae3c3837c9311c78f12b637426e579d32aadfa7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1572 zcmZwEOGuPa7{>8;EX*byXPkT5^i|_H+Gu7M^Fjbm{heJG)fmY*zESIQXUM-tU2xf71Sc(P6gf5&z44{%KbA2|t@{ zeuaHz!5|LfIKDUA(vKVQIPS-5W`R-WS>s%V>;CteN*rLG5;oujRwMCNzy)`|h0hh*9^S!re24n3I#PT0 zQZnP8!#N}`+>g`?&!C6TF^{YMIimNWUgR)RC!%<%{_GPu<<3LsalYo%UhElpgY=20 z-bb@Kokz{09sc*I>SQ^)`J`yMer4O!*$Z zMC!!UkNUB1zVU>AA9wE%R~_QwZO(&E?MuJ7_G6Cth;!UI>AtHU`^BHTd-Y?E_#(zo zeFx1F{YYFwbqYA+&I?Fy8q!^$2_CA zAaznl{QJ}~^f-?p{Zg8ry;6EN>ZN8;@v2|MU&!}#B@&-b;3!VH^DU%rTKkiq)_fj* zK;A8_xsc_ig*6@(ocH#rvd)lLHw&Sv&w=3kEo#Vcjr(MRZSz{21Z`3~2&*ktTsxEuy6gO(U&&SJwpQaSjLZ7pBa*6IjGkNM83i VlGm-h$m^*<;(9X3d_9`G@E5eOt4ja? diff --git a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shp b/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shp deleted file mode 100644 index cba612e..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dc7ec18e5f64c6a6591d88ac60be15a60e3a0bcae57cb489a91108b674c33392 -size 451373368 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shp.xml b/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shp.xml deleted file mode 100644 index c7d69d9..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shp.xml +++ /dev/null @@ -1,2 +0,0 @@ - -20250620145811001.0FALSEUpdateSchema "CIMDATA=<CIMStandardDataConnection xsi:type='typens:CIMStandardDataConnection' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:typens='http://www.esri.com/schemas/ArcGIS/3.4.0'><WorkspaceConnectionString>DATABASE=W:\Gaul_2024_Shapefiles\Admin0_Error\Revised_June25\OUTput\New Folder</WorkspaceConnectionString><WorkspaceFactory>Shapefile</WorkspaceFactory><Dataset>GAUL_2024_L1.shp</Dataset><DatasetType>esriDTFeatureClass</DatasetType></CIMStandardDataConnection>" <operationSequence><workflow><DeleteField><field_name>gaul2_code</field_name><field_name>gaul2_name</field_name></DeleteField></workflow></operationSequence>CalculateField GAUL_2024_L1 disp_en !gaul1_name!+", "+!gaul0_name! PYTHON3 # TEXT NO_ENFORCE_DOMAINSExportFeatures GAUL_2024_L1 W:\Gaul_2024_Shapefiles\Admin0_Error\Output\GAUL_2024_L1.shp # NOT_USE_ALIAS "iso3_code "iso3_code" true true false 3 Text 0 0,First,#,GAUL_2024_L1,iso3_code,0,2;map_code "map_code" true true false 3 Text 0 0,First,#,GAUL_2024_L1,map_code,0,2;gaul0_code "gaul0_code" true true false 10 Long 0 10,First,#,GAUL_2024_L1,gaul0_code,-1,-1;gaul0_name "gaul0_name" true true false 100 Text 0 0,First,#,GAUL_2024_L1,gaul0_name,0,99;gaul1_code "gaul1_code" true true false 10 Long 0 10,First,#,GAUL_2024_L1,gaul1_code,-1,-1;gaul1_name "gaul1_name" true true false 100 Text 0 0,First,#,GAUL_2024_L1,gaul1_name,0,99;continent "continent" true true false 12 Text 0 0,First,#,GAUL_2024_L1,continent,0,11;disp_en "disp_en" true true false 254 Text 0 0,First,#,GAUL_2024_L1,disp_en,0,253" #ExportFeatures GAUL_2024_L1 "W:\GAUL_2025\GAUL_2024\July25\GAUL_2024_L1\New Folder\GAUL_2024_L1.shp" # NOT_USE_ALIAS "iso3_code "iso3_code" true true false 3 Text 0 0,First,#,GAUL_2024_L1,iso3_code,0,2;map_code "map_code" true true false 3 Text 0 0,First,#,GAUL_2024_L1,map_code,0,2;gaul0_code "gaul0_code" true true false 10 Long 0 10,First,#,GAUL_2024_L1,gaul0_code,-1,-1;gaul0_name "gaul0_name" true true false 100 Text 0 0,First,#,GAUL_2024_L1,gaul0_name,0,99;gaul1_code "gaul1_code" true true false 10 Long 0 10,First,#,GAUL_2024_L1,gaul1_code,-1,-1;gaul1_name "gaul1_name" true true false 100 Text 0 0,First,#,GAUL_2024_L1,gaul1_name,0,99;continent "continent" true true false 12 Text 0 0,First,#,GAUL_2024_L1,continent,0,11;disp_en "disp_en" true true false 254 Text 0 0,First,#,GAUL_2024_L1,disp_en,0,253" #GAUL_2024_L10020.000file://\\LAPTOP-7171PVOF\W$\GAUL_2025\GAUL_2024\July25\GAUL_2024_L1\New Folder\GAUL_2024_L1.shpLocal Area NetworkGeographicGCS_WGS_1984Angular Unit: Degree (0.017453)<GeographicCoordinateSystem xsi:type='typens:GeographicCoordinateSystem' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:typens='http://www.esri.com/schemas/ArcGIS/3.4.0'><WKT>GEOGCS[&quot;GCS_WGS_1984&quot;,DATUM[&quot;D_WGS_1984&quot;,SPHEROID[&quot;WGS_1984&quot;,6378137.0,298.257223563]],PRIMEM[&quot;Greenwich&quot;,0.0],UNIT[&quot;Degree&quot;,0.0174532925199433],AUTHORITY[&quot;EPSG&quot;,4326]]</WKT><XOrigin>-400</XOrigin><YOrigin>-400</YOrigin><XYScale>11258999068426.238</XYScale><ZOrigin>-100000</ZOrigin><ZScale>10000</ZScale><MOrigin>-100000</MOrigin><MScale>10000</MScale><XYTolerance>8.983152841195215e-09</XYTolerance><ZTolerance>0.001</ZTolerance><MTolerance>0.001</MTolerance><HighPrecision>true</HighPrecision><LeftLongitude>-180</LeftLongitude><WKID>4326</WKID><LatestWKID>4326</LatestWKID></GeographicCoordinateSystem>20250701164720002025070116472000Microsoft Windows 10 Version 10.0 (Build 26100) ; Esri ArcGIS 13.4.0.55405GAUL_2024_L1Shapefile0.000datasetEPSG6.2(3.0.1)0SimpleFALSE0FALSEFALSEGAUL_2024_L1Feature Class0FIDFIDOID400Internal feature number.EsriSequential unique whole numbers that are automatically generated.ShapeShapeGeometry000Feature geometry.EsriCoordinates defining the features.iso3_codeiso3_codeString300map_codemap_codeString300gaul0_codegaul0_codeInteger10100gaul0_namegaul0_nameString10000gaul1_codegaul1_codeInteger10100gaul1_namegaul1_nameString10000continentcontinentString1200disp_endisp_enString2540020250701 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shx b/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shx deleted file mode 100644 index a61bf85..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1.shx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9ca5f533c9888e7140f409b0659ef06f24d5443a09365fb99542fc95f7936558 -size 24980 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1_ChangeLog.docx b/views_postprocessing/shapefiles/GAUL_2024_L1/GAUL_2024_L1_ChangeLog.docx deleted file mode 100644 index bf46d4a12a21e4957cba0110df408e97b35d5f1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26864 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 S0s`sn4tleUP_qBW)BgbnCmCP> diff --git a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.cpg b/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.cpg deleted file mode 100644 index 105bf61..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.cpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3ad3031f5503a4404af825262ee8232cc04d4ea6683d42c5dd0a2f2a27ac9824 -size 5 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.dbf b/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.dbf deleted file mode 100644 index 0a43ebd..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.dbf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4a69f4e7d30439006c27d718037db7dd3889e49227f7ca42fda0c85cbd0e18de -size 27451326 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.prj b/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.prj deleted file mode 100644 index 2098c4d..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.prj +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a02a27b1d1982c8516d83398e85a3c8b1aef1713c13ef4d84d7bde17430c07c4 -size 145 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.sbn b/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.sbn deleted file mode 100644 index bfac9915d6dfd47b6dce9f2a72cfdf9500acff26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 440396 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�M&~8wqi$U?nUbG) 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

shsESWFIEG3648t-l&lXzp?U@%6 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%0D}}S3#a6}%pHshxt<*|e`inTN{_Nf$>do#$-B;C} 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 Sm1pBVm8^pJxhScm>;40ZPf*7I diff --git a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.sbx b/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.sbx deleted file mode 100644 index a7c6eca02fcb680e3b563c5c94992474a5a86374..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13244 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 F`+rFWgUkQ` diff --git a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shp b/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shp deleted file mode 100644 index f50aaac..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c150365c48c26648f2b07c7322db589594c465aefcf1d44e843e46d46fd44872 -size 774793140 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shp.xml b/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shp.xml deleted file mode 100644 index 89916ff..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shp.xml +++ /dev/null @@ -1,2 +0,0 @@ - -20250701164247001.0FALSEGAUL_2024_L20020.000file://\\LAPTOP-7171PVOF\W$\GAUL_2025\GAUL_2024\July25\GAUL_2024_L2\New Folder\GAUL_2024_L2.shpLocal Area NetworkGeographicGCS_WGS_1984Angular Unit: Degree (0.017453)<GeographicCoordinateSystem xsi:type='typens:GeographicCoordinateSystem' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:typens='http://www.esri.com/schemas/ArcGIS/3.4.0'><WKT>GEOGCS[&quot;GCS_WGS_1984&quot;,DATUM[&quot;D_WGS_1984&quot;,SPHEROID[&quot;WGS_1984&quot;,6378137.0,298.257223563]],PRIMEM[&quot;Greenwich&quot;,0.0],UNIT[&quot;Degree&quot;,0.0174532925199433],AUTHORITY[&quot;EPSG&quot;,4326]]</WKT><XOrigin>-400</XOrigin><YOrigin>-400</YOrigin><XYScale>11258999068426.238</XYScale><ZOrigin>-100000</ZOrigin><ZScale>10000</ZScale><MOrigin>-100000</MOrigin><MScale>10000</MScale><XYTolerance>8.983152841195215e-09</XYTolerance><ZTolerance>0.001</ZTolerance><MTolerance>0.001</MTolerance><HighPrecision>true</HighPrecision><LeftLongitude>-180</LeftLongitude><WKID>4326</WKID><LatestWKID>4326</LatestWKID></GeographicCoordinateSystem>ExportFeatures GAUL_2024_L2 "W:\GAUL_2025\GAUL_2024\July25\GAUL_2024_L2\New Folder\GAUL_2024_L2.shp" # NOT_USE_ALIAS "iso3_code "iso3_code" true true false 3 Text 0 0,First,#,GAUL_2024_L2,iso3_code,0,2;map_code "map_code" true true false 3 Text 0 0,First,#,GAUL_2024_L2,map_code,0,2;gaul0_code "gaul0_code" true true false 10 Long 0 10,First,#,GAUL_2024_L2,gaul0_code,-1,-1;gaul0_name "gaul0_name" true true false 100 Text 0 0,First,#,GAUL_2024_L2,gaul0_name,0,99;gaul1_code "gaul1_code" true true false 10 Long 0 10,First,#,GAUL_2024_L2,gaul1_code,-1,-1;gaul1_name "gaul1_name" true true false 100 Text 0 0,First,#,GAUL_2024_L2,gaul1_name,0,99;gaul2_code "gaul2_code" true true false 10 Long 0 10,First,#,GAUL_2024_L2,gaul2_code,-1,-1;gaul2_name "gaul2_name" true true false 100 Text 0 0,First,#,GAUL_2024_L2,gaul2_name,0,99;continent "continent" true true false 12 Text 0 0,First,#,GAUL_2024_L2,continent,0,11;disp_en "disp_en" true true false 254 Text 0 0,First,#,GAUL_2024_L2,disp_en,0,253" #20250701164247002025070116424700Microsoft Windows 10 Version 10.0 (Build 26100) ; Esri ArcGIS 13.4.0.55405GAUL_2024_L2Shapefile0.000datasetEPSG6.2(3.0.1)0SimpleFALSE0FALSEFALSEGAUL_2024_L2Feature Class0FIDFIDOID400Internal feature number.EsriSequential unique whole numbers that are automatically generated.ShapeShapeGeometry000Feature geometry.EsriCoordinates defining the features.iso3_codeiso3_codeString300map_codemap_codeString300gaul0_codegaul0_codeInteger10100gaul0_namegaul0_nameString10000gaul1_codegaul1_codeInteger10100gaul1_namegaul1_nameString10000gaul2_codegaul2_codeInteger10100gaul2_namegaul2_nameString10000continentcontinentString1200disp_endisp_enString2540020250701 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shx b/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shx deleted file mode 100644 index cd01932..0000000 --- a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2.shx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f31ef2e9380774399a9b7b168837225290eeadd60e1d184da13b468672c6bb63 -size 364292 diff --git a/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2_ChangeLog.docx b/views_postprocessing/shapefiles/GAUL_2024_L2/GAUL_2024_L2_ChangeLog.docx deleted file mode 100644 index 57fae4ff059db39aef66d8dabc5b41c37eae6b2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27729 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 Optional[float] - def set(self, key, value) -> None -``` - -**Purpose**: Caches haversine distance calculations to avoid redundant computations when calculating distances between grid cells and capital cities. - ---- - -### `PriogridCountryMapper` - -The main class that handles all spatial mapping operations. - -#### Initialization - -```python -mapper = PriogridCountryMapper( - use_disk_cache=True, # Enable persistent disk caching - cache_dir=None, # Custom cache directory (default: ~/.priogrid_mapper_cache) - cache_ttl=None # Cache time-to-live in seconds (None = no expiration) -) -``` - -#### Attributes - -| Attribute | Type | Description | -|-----------|------|-------------| -| `countries_gdf` | GeoDataFrame | Natural Earth country boundaries | -| `priogrid_gdf` | GeoDataFrame | PRIO-GRID cell geometries with centroids | -| `admin1_gdf` | GeoDataFrame | GAUL Level 1 boundaries | -| `admin2_gdf` | GeoDataFrame | GAUL Level 2 boundaries | -| `priogrid_sindex` | SpatialIndex | R-tree index for fast PRIO-GRID queries | -| `admin1_sindex` | SpatialIndex | R-tree index for fast Admin1 queries | -| `admin2_sindex` | SpatialIndex | R-tree index for fast Admin2 queries | - ---- - -## Mapping Decision Logic - -### Country Assignment for a PRIO-GRID Cell - -The `find_country_for_gid()` method uses a simple, deterministic **largest overlap** approach: - -``` -┌─────────────────────────────────────────────────────────────┐ -│ INPUT: PRIO-GRID GID │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Step 1: Find all countries that intersect the grid cell │ -│ using spatial index for performance │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Step 2: Calculate overlap ratio for each country │ -│ overlap_ratio = intersection_area / cell_area │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Step 3: Sort by overlap ratio (descending) │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Step 4: Assign to country with LARGEST OVERLAP │ -│ Method tag: "largest overlap" │ -└─────────────────────────────────────────────────────────────┘ -``` - -#### Why Largest Overlap? - -The decision to use **only the largest overlap rule** (rather than a multi-rule hierarchy) provides several benefits: - -1. **Simplicity** - One consistent rule is easier to understand, explain, and maintain -2. **Determinism** - The same input always produces the same output, regardless of centroid position -3. **Intuitive for aggregation** - When aggregating predictions, assigning to the region with the most area coverage makes sense (more of the cell's "land" belongs there) -4. **Handles all edge cases** - Works correctly for: - - Cells entirely within one country (100% overlap) - - Border cells spanning multiple countries - - Coastal cells with partial water coverage (assigns to the country with most land area) - -### Admin Level Assignment - -The `find_admin1_for_gid()` and `find_admin2_for_gid()` methods use the same largest overlap approach: - -1. **First determine the country** - Uses `find_country_for_gid()` to ensure admin regions are filtered to the correct country -2. **Calculate overlaps** - For all admin regions in that country that intersect the grid cell -3. **Assign to largest overlap** - The admin region with the highest overlap ratio wins - -This approach ensures that: -- Admin regions are always consistent with the assigned country -- The assignment method is consistent across all administrative levels -- Results are deterministic and reproducible - ---- - -## Caching Architecture - -The module implements a sophisticated multi-layer caching system to optimize performance: - -### Cache Types - -``` -┌─────────────────────────────────────────────────────────────┐ -│ CACHING ARCHITECTURE │ -└─────────────────────────────────────────────────────────────┘ - -┌─────────────────────┐ ┌─────────────────────┐ -│ DISK CACHE │ │ MEMORY CACHE │ -│ (Persistent) │ │ (Session-based) │ -├─────────────────────┤ ├─────────────────────┤ -│ • Survives restarts │ │ • Faster access │ -│ • Shared across │ │ • Limited size │ -│ processes │ │ • LRU or TTL-based │ -│ • Uses joblib │ │ • Uses cachetools │ -│ Memory │ │ │ -└─────────────────────┘ └─────────────────────┘ - -Default location: ~/.priogrid_mapper_cache/ -├── country/ # GID → Country mappings -├── admin1/ # GID → Admin1 mappings -├── admin2/ # GID → Admin2 mappings -└── gid/ # Point → GID mappings -``` - -### Cache Configuration - -| Cache Type | Max Size | Purpose | -|------------|----------|---------| -| Country Cache | 100,000 | GID → Country mappings | -| GID Cache | 100,000 | Point → GID lookups | -| GIDs for Country | 10,000,000 | Country → [GIDs] reverse mapping | -| Admin1 Cache | 100,000 | GID → Admin1 mappings | -| Admin2 Cache | 100,000 | GID → Admin2 mappings | - -### Cache Methods - -```python -# Clear all or specific caches -mapper.clear_cache(cache_type="all") # "all", "country", "admin1", "admin2", "gid" - -# Get cache statistics -stats = mapper.get_cache_stats() - -# Pre-warm cache for frequently accessed data -mapper.warm_cache(gid_list=[123, 456, 789], iso_a3_list=['TZA', 'KEN']) -``` - ---- - -## Key Methods - -### Core Mapping Methods - -#### `find_country_for_gid(gid: int) -> dict` - -Find country information for a PRIO-GRID cell. - -**Returns:** -```python -{ - "gid": 123456, - "iso_a3": "TZA", - "country_name": "Tanzania", - "overlap_ratio": 0.85, - "method": "largest overlap", - # ... additional Natural Earth fields -} -``` - -#### `find_admin1_for_gid(gid: int) -> dict` - -Find Admin Level 1 region for a PRIO-GRID cell. - -**Returns:** -```python -{ - "gid": 123456, - "gaul1_code": 45678, - "gaul1_name": "Dar es Salaam", - "iso3_code": "TZA", - "method": "largest overlap", - "gaul0_code": 123, - "gaul0_name": "Tanzania", - # ... additional GAUL fields -} -``` - -#### `find_admin2_for_gid(gid: int) -> dict` - -Find Admin Level 2 region for a PRIO-GRID cell. - -**Returns:** -```python -{ - "gid": 123456, - "gaul2_code": 78901, - "gaul2_name": "Kinondoni", - "iso3_code": "TZA", - "method": "largest overlap", - "gaul1_code": 45678, - "gaul1_name": "Dar es Salaam", - # ... additional GAUL fields -} -``` - -#### `find_gids_for_country(iso_a3: str) -> List[int]` - -Find all PRIO-GRID cells belonging to a country (reverse mapping). - -```python -gids = mapper.find_gids_for_country("TZA") -# Returns: [123456, 123457, 123458, ...] -``` - -### Batch Processing Methods - -#### `batch_country_mapping(gid_list: List[int]) -> pd.DataFrame` - -Process multiple GIDs with cache optimization. - -```python -df = mapper.batch_country_mapping([123, 456, 789, ...]) -``` - -#### `batch_country_mapping_parallel(gid_list, batch_size=1000) -> pd.DataFrame` - -Process large datasets using multiprocessing. - -```python -df = mapper.batch_country_mapping_parallel(gid_list, batch_size=1000) -``` - -#### `batch_admin_mapping(gid_list, admin_level="admin1") -> pd.DataFrame` - -Batch mapping for admin regions. - -```python -df = mapper.batch_admin_mapping(gid_list, admin_level="admin2") -``` - -### DataFrame Enrichment Methods - -#### `enrich_dataframe_with_pg_info(df, ...)` - -Enrich a DataFrame containing PRIO-GRID IDs with geographic metadata. - -```python -enriched_df = mapper.enrich_dataframe_with_pg_info( - df, - pg_id_col="priogrid_id", - time_id_col="month_id", - include_country=True, - include_admin1=True, - include_admin2=True, - include_pg_info=True, - batch_size=1000, - use_multiprocessing=True, - show_progress=True -) -``` - -**Result columns added:** -- `country_iso_a3`, `country_name`, `country_overlap_ratio`, `country_method` -- `admin1_gaul1_code`, `admin1_gaul1_name`, `admin1_method` -- `admin2_gaul2_code`, `admin2_gaul2_name`, `admin2_method` -- `pg_xcoord`, `pg_ycoord`, `pg_geometry`, etc. - -### Visualization Methods - -#### `visualize_grid_and_country(gid: int)` - -Visualize a PRIO-GRID cell and its assigned country. - -```python -mapper.visualize_grid_and_country(123456) -``` - -#### `visualize_grid_and_admin(gid, admin_level="admin1", show_all_admins=False)` - -Visualize a PRIO-GRID cell with admin boundaries. - -```python -mapper.visualize_grid_and_admin(123456, admin_level="admin2", show_all_admins=True) -``` - -### Utility Methods - -#### `find_gid_for_point(point_geometry) -> int` - -Find which PRIO-GRID cell contains a point. - -```python -from shapely.geometry import Point -gid = mapper.find_gid_for_point(Point(36.8, -1.3)) # Nairobi -``` - -#### `find_country_by_iso_a3(iso_a3: str) -> dict` - -Get detailed country information by ISO code. - -```python -info = mapper.find_country_by_iso_a3("TZA") -``` - -#### `search_countries_by_name(name_pattern, exact_match=False) -> List[dict]` - -Search for countries by name pattern. - -```python -results = mapper.search_countries_by_name("tanzania") -results = mapper.search_countries_by_name("united", exact_match=False) -``` - ---- - -## Usage Examples - -### Basic Usage - -```python -from views_postprocessing.unfao.mapping.mapping import PriogridCountryMapper - -# Initialize mapper (loads shapefiles, builds spatial indices) -mapper = PriogridCountryMapper(use_disk_cache=True) - -# Map a single PRIO-GRID cell to country -result = mapper.find_country_for_gid(148345) -print(f"GID 148345 belongs to {result['country_name']} ({result['iso_a3']})") -print(f"Assignment method: {result['method']}") - -# Get all admin levels at once -all_info = mapper.find_all_admin_for_gid(148345) -``` - -### Batch Processing for Large Datasets - -```python -import pandas as pd - -# Your prediction data with PRIO-GRID IDs -predictions_df = pd.DataFrame({ - 'priogrid_id': [148345, 148346, 148347, ...], - 'month_id': [553, 553, 553, ...], - 'predicted_fatalities': [0.5, 1.2, 0.3, ...] -}) - -# Enrich with geographic metadata -enriched_df = mapper.enrich_dataframe_with_pg_info( - predictions_df, - pg_id_col='priogrid_id', - include_country=True, - include_admin1=True, - include_admin2=True, - use_multiprocessing=True, - show_progress=True -) - -# Now you can aggregate by country, admin1, or admin2 -country_totals = enriched_df.groupby('country_iso_a3')['predicted_fatalities'].sum() -``` - -### Using the Default Global Mapper - -```python -from views_postprocessing.unfao.mapping.mapping import get_default_mapper - -# A global mapper is automatically initialized when the module is imported -mapper = get_default_mapper() - -# Use it directly -result = mapper.find_country_for_gid(148345) -``` - -### Cache Management - -```python -# Check cache statistics -stats = mapper.get_cache_stats() -print(f"Cache type: {stats['cache_type']}") -print(f"Country cache size: {stats['country_cache_size']}") - -# Clear cache before fresh analysis -mapper.clear_cache(cache_type="all") - -# Pre-warm cache for a specific region -african_countries = ['TZA', 'KEN', 'UGA', 'RWA', 'BDI'] -mapper.warm_cache(iso_a3_list=african_countries) -``` - ---- - -## Performance Considerations - -### Spatial Indexing - -The module uses **R-tree spatial indices** for all geographic datasets: -- Reduces point-in-polygon queries from O(n) to O(log n) -- Critical for batch processing thousands of grid cells - -### Recommended Batch Sizes - -| Operation | Recommended Batch Size | Notes | -|-----------|----------------------|-------| -| Country mapping | 1,000 | Good balance of memory and parallelization | -| Admin mapping | 500-1,000 | Admin2 is more complex | -| DataFrame enrichment | 1,000 | Depends on available memory | - -### Memory Management - -- Enable disk caching (`use_disk_cache=True`) for large datasets -- Use `only_metadata=True` in `enrich_dataframe_with_pg_info()` to minimize memory -- Clear caches periodically for long-running processes - -### Multiprocessing vs Threading - -The module uses **ThreadPoolExecutor** instead of multiprocessing to avoid: -- Pickling issues with GeoDataFrames -- Memory duplication across processes -- Complex IPC overhead - -For CPU-bound tasks, consider running multiple single-threaded instances. - ---- - -## Configuration - -### Global Constants - -```python -# Cache directory (default: ~/.priogrid_mapper_cache) -CACHE_DIR = Path.home() / ".priogrid_mapper_cache" - -# Cache sizes -COUNTRY_CACHE_MAXSIZE = 100_000 -GID_CACHE_MAXSIZE = 100_000 -GIDS_FOR_COUNTRY_CACHE_MAXSIZE = 10_000_000 -ADMIN1_CACHE_MAXSIZE = 100_000 -ADMIN2_CACHE_MAXSIZE = 100_000 -``` - -### Shapefile Paths - -Paths are configured relative to the module location: - -```python -NATURAL_EARTH_COUNTRY_PATH = Path(__file__).parent.parent.parent / "shapefiles" / "ne_110m_admin_0_countries" / "ne_110m_admin_0_countries.shp" -PRIOGRID_SHAPEFILE_PATH = Path(__file__).parent.parent.parent / "shapefiles" / "priogrid_cellshp" / "priogrid_cell.shp" -ADM_1_SHAPEFILE_PATH = Path(__file__).parent.parent.parent / "shapefiles" / "GAUL_2024_L1" / "GAUL_2024_L1.shp" -ADM_2_SHAPEFILE_PATH = Path(__file__).parent.parent.parent / "shapefiles" / "GAUL_2024_L2" / "GAUL_2024_L2.shp" -``` - ---- - -## Helper Functions - -### `cached_haversine(lat1, lon1, lat2, lon2) -> float` - -Calculate great-circle distance between two points with caching. - -```python -distance_km = cached_haversine( - lat1=-6.8, lon1=39.3, # Dar es Salaam - lat2=-1.3, lon2=36.8 # Nairobi -) -``` - -### `set_default_mapper() -> PriogridCountryMapper` - -Initialize the global default mapper. - -### `get_default_mapper() -> PriogridCountryMapper` - -Retrieve the global default mapper instance. - ---- - -## Error Handling - -The module handles various edge cases: - -| Scenario | Behavior | -|----------|----------| -| Invalid GID | Returns `None` | -| GID in water (no country) | Returns `None` or uses water handling rule | -| Invalid ISO A3 code | Returns `None` with warning | -| Missing shapefile | Raises `FileNotFoundError` during initialization | -| Invalid geometry | Logged as warning, skipped in calculations | -| Cache miss | Computes and caches result | - ---- - -## Dependencies - -``` -pandas -geopandas -shapely -numpy -joblib -cachetools -matplotlib (optional, for visualization) -tqdm (optional, for progress bars) -``` diff --git a/views_postprocessing/unfao/mapping/__init__.py b/views_postprocessing/unfao/mapping/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/views_postprocessing/unfao/mapping/mapping.py b/views_postprocessing/unfao/mapping/mapping.py deleted file mode 100644 index 1714af1..0000000 --- a/views_postprocessing/unfao/mapping/mapping.py +++ /dev/null @@ -1,3171 +0,0 @@ -import pandas as pd -import geopandas as gpd -from shapely.geometry import Point as ShapelyPoint -from shapely import wkt -from typing import Optional - -from functools import partial -from collections import OrderedDict -import math -import numpy as np -import logging -from pathlib import Path -import os - -from multiprocessing import Pool, cpu_count -from joblib import Memory -from cachetools import LRUCache, TTLCache - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -NATURAL_EARTH_COUNTRY_PATH = ( - Path(__file__).parent.parent.parent - / "shapefiles" - / "ne_10m_admin_0_countries" - / "ne_10m_admin_0_countries.shp" -) - -PRIOGRID_SHAPEFILE_PATH = ( - Path(__file__).parent.parent.parent - / "shapefiles" - / "priogrid_cellshp" - / "priogrid_cell.shp" -) -ADM_1_SHAPEFILE_PATH = ( - Path(__file__).parent.parent.parent - / "shapefiles" - / "GAUL_2024_L1" - / "GAUL_2024_L1.shp" -) -ADM_2_SHAPEFILE_PATH = ( - Path(__file__).parent.parent.parent - / "shapefiles" - / "GAUL_2024_L2" - / "GAUL_2024_L2.shp" -) - -# Configure disk cache -CACHE_DIR = Path.home() / ".priogrid_mapper_cache" -os.makedirs(CACHE_DIR, exist_ok=True) - -# Cache sizes -COUNTRY_CACHE_MAXSIZE = 100000 -GID_CACHE_MAXSIZE = 100000 -GIDS_FOR_COUNTRY_CACHE_MAXSIZE = 10000000 -ADMIN1_CACHE_MAXSIZE = 100000 -ADMIN2_CACHE_MAXSIZE = 100000 - -# Global disk caches (shared across instances) -_disk_country_cache = Memory(location=CACHE_DIR / "country", verbose=0) -_disk_admin1_cache = Memory(location=CACHE_DIR / "admin1", verbose=0) -_disk_admin2_cache = Memory(location=CACHE_DIR / "admin2", verbose=0) -_disk_gid_cache = Memory(location=CACHE_DIR / "gid", verbose=0) - - -class DistanceCache: - """Simple LRU cache for distance calculations.""" - - def __init__(self, maxsize=1000): - self.cache = OrderedDict() - self.maxsize = maxsize - - def get(self, key): - if key in self.cache: - self.cache.move_to_end(key) - return self.cache[key] - return None - - def set(self, key, value): - self.cache[key] = value - self.cache.move_to_end(key) - if len(self.cache) > self.maxsize: - self.cache.popitem(last=False) - - -# Global distance cache -_distance_cache = DistanceCache() - - -def cached_haversine(lat1, lon1, lat2, lon2): - """Calculate haversine distance with caching.""" - # Validate coordinates - if ( - not (-90 <= lat1 <= 90) - or not (-180 <= lon1 <= 180) - or not (-90 <= lat2 <= 90) - or not (-180 <= lon2 <= 180) - ): - raise ValueError( - "Invalid coordinates: latitudes must be between -90 and 90, longitudes between -180 and 180" - ) - - # Create a cache key based on the coordinates (rounded to 6 decimal places) - cache_key = (round(lat1, 6), round(lon1, 6), round(lat2, 6), round(lon2, 6)) - - # Check cache first - cached_result = _distance_cache.get(cache_key) - if cached_result is not None: - return cached_result - - # Not in cache, perform the calculation - lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2]) - dlon = lon2 - lon1 - dlat = lat2 - lat1 - a = ( - math.sin(dlat / 2) ** 2 - + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2 - ) - c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) - result = 6371.0 * c # Earth radius in kilometers - - # Update cache - _distance_cache.set(cache_key, result) - return result - - -class PriogridCountryMapper: - """ - A comprehensive class for mapping between PRIO-GRID cells and countries using Natural Earth shapefiles. - Now with persistent disk caching for improved performance across sessions. - - Attributes: - countries_gdf (GeoDataFrame): Processed Natural Earth country data - priogrid_gdf (GeoDataFrame): Processed PRIO-GRID data with cell geometries and centroids - priogrid_sindex (SpatialIndex): Spatial index for faster PRIO-GRID queries - """ - - def __init__(self, use_disk_cache=True, cache_dir=None, cache_ttl=None): - """ - Initialize the PriogridCountryMapper with optional disk caching. - - Parameters: - use_disk_cache (bool): Whether to use persistent disk caching - cache_dir (str or Path): Custom cache directory path - cache_ttl (int): Time-to-live for cache entries in seconds (None for no expiration) - """ - # Initialize attributes used in __del__ immediately to prevent AttributeError - # if an exception occurs during initialization (e.g., missing shapefiles). - self._process_pool = None - self._max_workers = cpu_count() - - # Load Natural Earth country data - # NOTE: This is the line that was crashing in your traceback (line 134) - country_path = str(NATURAL_EARTH_COUNTRY_PATH) - self.countries_gdf = self._load_and_preprocess_naturalearth(country_path) - - # Load PRIO-GRID data - self.priogrid_gdf = self._load_priogrid(str(PRIOGRID_SHAPEFILE_PATH)) - - self.priogrid_sindex = ( - self.priogrid_gdf.sindex if hasattr(self.priogrid_gdf, "sindex") else None - ) - - # Configure caching - self.use_disk_cache = use_disk_cache - self.cache_ttl = cache_ttl - - if use_disk_cache: - # Set up custom cache directory if provided - if cache_dir: - self.cache_dir = Path(cache_dir) - os.makedirs(self.cache_dir, exist_ok=True) - else: - self.cache_dir = CACHE_DIR - - # Initialize disk caches - self._disk_country_cache = Memory( - location=self.cache_dir / "country", verbose=0 - ) - self._disk_admin1_cache = Memory( - location=self.cache_dir / "admin1", verbose=0 - ) - self._disk_admin2_cache = Memory( - location=self.cache_dir / "admin2", verbose=0 - ) - self._disk_gid_cache = Memory(location=self.cache_dir / "gid", verbose=0) - - logger.info(f"Using disk cache at: {self.cache_dir}") - else: - # Initialize in-memory caches as instance variables - if cache_ttl: - self._country_cache = TTLCache( - maxsize=COUNTRY_CACHE_MAXSIZE, ttl=cache_ttl - ) - self._gid_cache = TTLCache(maxsize=GID_CACHE_MAXSIZE, ttl=cache_ttl) - self._gids_for_country_cache = TTLCache( - maxsize=GIDS_FOR_COUNTRY_CACHE_MAXSIZE, ttl=cache_ttl - ) - self._admin1_cache = TTLCache( - maxsize=ADMIN1_CACHE_MAXSIZE, ttl=cache_ttl - ) - self._admin2_cache = TTLCache( - maxsize=ADMIN2_CACHE_MAXSIZE, ttl=cache_ttl - ) - else: - self._country_cache = LRUCache(maxsize=COUNTRY_CACHE_MAXSIZE) - self._gid_cache = LRUCache(maxsize=GID_CACHE_MAXSIZE) - self._gids_for_country_cache = LRUCache( - maxsize=GIDS_FOR_COUNTRY_CACHE_MAXSIZE - ) - self._admin1_cache = LRUCache(maxsize=ADMIN1_CACHE_MAXSIZE) - self._admin2_cache = LRUCache(maxsize=ADMIN2_CACHE_MAXSIZE) - - logger.info("Using in-memory caching") - - # Load admin1 and admin2 data if paths are provided - self.admin1_path = str(ADM_1_SHAPEFILE_PATH) - self.admin2_path = str(ADM_2_SHAPEFILE_PATH) - - self.admin1_gdf = None - self.admin2_gdf = None - - if self.admin1_path: - self.admin1_gdf = self._load_admin_data(self.admin1_path, "admin1") - self.admin1_sindex = ( - self.admin1_gdf.sindex if hasattr(self.admin1_gdf, "sindex") else None - ) - else: - self.admin1_sindex = None - - if self.admin2_path: - self.admin2_gdf = self._load_admin_data(self.admin2_path, "admin2") - self.admin2_sindex = ( - self.admin2_gdf.sindex if hasattr(self.admin2_gdf, "sindex") else None - ) - else: - self.admin2_sindex = None - - def _get_gid_cache_key(self, gid): - """Generate a consistent cache key for GID-based lookups""" - return f"gid_{gid}" - - def _get_point_cache_key(self, point_geometry): - """Generate a consistent cache key for point-based lookups""" - return f"point_{round(point_geometry.x, 6)}_{round(point_geometry.y, 6)}" - - def _get_country_cache_key(self, iso_a3): - """Generate a consistent cache key for country-based lookups""" - return f"country_{iso_a3.upper() if iso_a3 else iso_a3}" - - def _get_admin_cache_key(self, gid, admin_level): - """Generate a consistent cache key for admin-based lookups""" - return f"{admin_level}_{gid}" - - def clear_cache(self, cache_type="all"): - """ - Clear cached data. - - Parameters: - cache_type (str): Type of cache to clear ("all", "country", "admin1", "admin2", "gid") - """ - if self.use_disk_cache: - if cache_type == "all" or cache_type == "country": - self._disk_country_cache.clear() - logger.info("Cleared country cache") - - if cache_type == "all" or cache_type == "admin1": - self._disk_admin1_cache.clear() - logger.info("Cleared admin1 cache") - - if cache_type == "all" or cache_type == "admin2": - self._disk_admin2_cache.clear() - logger.info("Cleared admin2 cache") - - if cache_type == "all" or cache_type == "gid": - self._disk_gid_cache.clear() - logger.info("Cleared GID cache") - else: - if cache_type == "all" or cache_type == "country": - self._country_cache.clear() - logger.info("Cleared country cache") - - if cache_type == "all" or cache_type == "admin1": - self._admin1_cache.clear() - logger.info("Cleared admin1 cache") - - if cache_type == "all" or cache_type == "admin2": - self._admin2_cache.clear() - logger.info("Cleared admin2 cache") - - if cache_type == "all" or cache_type == "gid": - self._gid_cache.clear() - logger.info("Cleared GID cache") - - def get_cache_stats(self): - """ - Get statistics about cache usage. - - Returns: - dict: Cache statistics - """ - if self.use_disk_cache: - stats = { - "cache_type": "disk", - "cache_dir": str(self.cache_dir), - "country_cache_size": len(self._disk_country_cache), - "admin1_cache_size": len(self._disk_admin1_cache), - "admin2_cache_size": len(self._disk_admin2_cache), - "gid_cache_size": len(self._disk_gid_cache), - } - - # Calculate total cache size on disk - total_size = 0 - for cache_dir in [ - self.cache_dir / d for d in ["country", "admin1", "admin2", "gid"] - ]: - if cache_dir.exists(): - for file in cache_dir.glob("*"): - if file.is_file(): - total_size += file.stat().st_size - - stats["total_disk_size_mb"] = round(total_size / (1024 * 1024), 2) - else: - stats = { - "cache_type": "memory", - "country_cache_size": len(self._country_cache), - "country_cache_maxsize": self._country_cache.maxsize, - "gid_cache_size": len(self._gid_cache), - "gid_cache_maxsize": self._gid_cache.maxsize, - "admin1_cache_size": len(self._admin1_cache), - "admin1_cache_maxsize": self._admin1_cache.maxsize, - "admin2_cache_size": len(self._admin2_cache), - "admin2_cache_maxsize": self._admin2_cache.maxsize, - } - - if hasattr(self._country_cache, "currsize"): - stats["country_cache_hit_rate"] = ( - f"{self._country_cache.hits}/{self._country_cache.hits + self._country_cache.misses}" - ) - stats["gid_cache_hit_rate"] = ( - f"{self._gid_cache.hits}/{self._gid_cache.hits + self._gid_cache.misses}" - ) - - return stats - - def warm_cache(self, gid_list=None, iso_a3_list=None): - """ - Warm up the cache with frequently accessed data. - - Parameters: - gid_list (list): List of GIDs to pre-cache - iso_a3_list (list): List of ISO A3 codes to pre-cache - """ - if gid_list: - logger.info(f"Warming cache with {len(gid_list)} GIDs") - for gid in gid_list: - self.find_country_for_gid(gid) - self.find_admin1_for_gid(gid) - self.find_admin2_for_gid(gid) - - if iso_a3_list: - logger.info(f"Warming cache with {len(iso_a3_list)} countries") - for iso_a3 in iso_a3_list: - self.find_country_by_iso_a3(iso_a3) - self.find_gids_for_country(iso_a3) - - logger.info("Cache warming complete") - - def __del__(self): - if self._process_pool: - self._process_pool.close() - self._process_pool.join() - - def _init_process_pool(self): - """Initialize the process pool if not already done""" - if self._process_pool is None: - self._process_pool = Pool(processes=self._max_workers) - - def batch_country_mapping_parallel(self, gid_list, batch_size=1000): - """ - Find countries for multiple PRIO-GRID cells using multiprocessing. - - Parameters: - gid_list (list): List of PRIO-GRID cell IDs - batch_size (int): Number of GIDs to process in each batch - - Returns: - DataFrame: Results of the batch mapping - """ - self._init_process_pool() - - # Split into batches - batches = [ - gid_list[i : i + batch_size] for i in range(0, len(gid_list), batch_size) - ] - - # Create partial function for mapping - map_func = partial(self._process_gid_batch) - - # Process batches in parallel - results = [] - for batch_result in self._process_pool.imap_unordered(map_func, batches): - results.extend(batch_result) - - return pd.DataFrame(results) - - def _process_gid_batch(self, gid_batch): - """Process a batch of GIDs in a single process""" - batch_results = [] - for gid in gid_batch: - result = self.find_country_for_gid(gid) - batch_results.append(result) - return batch_results - - def _load_and_preprocess_naturalearth(self, country_path): - """ - Load and preprocess the Natural Earth country dataset from the provided path. - - Parameters: - country_path (str): Path to the Natural Earth country dataset file - - Returns: - GeoDataFrame: Processed Natural Earth data with proper geometry - - Raises: - FileNotFoundError: If the file cannot be found - ValueError: If the file format is unsupported or data cannot be parsed - """ - # Load Natural Earth data - if country_path.endswith(".shp"): - countries_gdf = gpd.read_file(country_path) - else: - # Assume it's a CSV with WKT geometry - countries_df = pd.read_csv(country_path) - - # Parse the WKT geometry, removing the SRID prefix if present - def parse_wkt(wkt_str): - if wkt_str.startswith("SRID=4326;"): - wkt_str = wkt_str.replace("SRID=4326;", "") - return wkt.loads(wkt_str) - - countries_df["geometry"] = countries_df["geometry"].apply(parse_wkt) - countries_gdf = gpd.GeoDataFrame(countries_df, geometry="geometry") - - # Set CRS if not already set - if countries_gdf.crs is None: - countries_gdf.set_crs(epsg=4326, inplace=True) - - # Fix invalid geometries before validation and use - countries_gdf["geometry"] = countries_gdf["geometry"].make_valid() - - # Validate data - self._validate_naturalearth_data(countries_gdf) - - return countries_gdf - - def _validate_naturalearth_data(self, countries_gdf): - """Validate Natural Earth data for consistency and correctness.""" - # Check for required columns - required_columns = ["ISO_A3", "NAME_EN", "geometry"] - missing_columns = [ - col for col in required_columns if col not in countries_gdf.columns - ] - if missing_columns: - err_msg = f"Missing required columns in Natural Earth data: {missing_columns}" - logger.error(err_msg) - raise ValueError(err_msg) - - # Check geometry validity - invalid_geometries = countries_gdf[~countries_gdf["geometry"].is_valid] - if not invalid_geometries.empty: - logger.warning( - f"Found {len(invalid_geometries)} invalid geometries in Natural Earth data" - ) - - def _load_priogrid(self, priogrid_path): - """ - Load and preprocess the PRIO-GRID data from the provided path. - - Parameters: - priogrid_path (str): Path to the PRIO-GRID dataset file - - Returns: - GeoDataFrame: Processed PRIO-GRID data with geometry and centroid fields - - Raises: - FileNotFoundError: If the file cannot be found - ValueError: If the file format is unsupported or data cannot be parsed - """ - # Load PRIO-GRID data - priogrid_gdf = gpd.read_file(priogrid_path) - - # Ensure CRS is set - if priogrid_gdf.crs is None: - priogrid_gdf.set_crs(epsg=4326, inplace=True) - - # Fix invalid geometries before use - priogrid_gdf["geometry"] = priogrid_gdf["geometry"].make_valid() - - import warnings - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", message=".*Geometry is in a geographic CRS.*") - priogrid_gdf["centroid"] = priogrid_gdf["geometry"].centroid - - # Validate data - self._validate_priogrid_data(priogrid_gdf) - - return priogrid_gdf - - def _validate_priogrid_data(self, priogrid_gdf): - """Validate PRIO-GRID data for consistency and correctness.""" - # Check for required columns - required_columns = ["gid", "geometry", "centroid"] - missing_columns = [ - col for col in required_columns if col not in priogrid_gdf.columns - ] - if missing_columns: - err_msg = f"Missing required columns in PRIO-GRID data: {missing_columns}" - logger.error(err_msg) - raise ValueError(err_msg) - - # Check geometry validity - invalid_geometries = priogrid_gdf[~priogrid_gdf["geometry"].is_valid] - if not invalid_geometries.empty: - logger.warning( - f"Found {len(invalid_geometries)} invalid geometries in PRIO-GRID data" - ) - - def find_gid_for_point(self, point_geometry): - """ - Find the PRIO-GRID cell ID that contains a point using spatial index for faster query. - - Parameters: - point_geometry (shapely.geometry.Point): Point geometry to locate - - Returns: - int or None: PRIO-GRID cell ID containing the point, or None if not found - """ - if self.use_disk_cache: - # Use disk cache - @self._disk_gid_cache.cache() - def _find_gid_for_point_impl(point_geometry): - # Validate input geometry - if not point_geometry.is_valid: - raise ValueError("Invalid point geometry") - - # Not in cache, perform the expensive operation - if self.priogrid_sindex: - possible_matches_index = list( - self.priogrid_sindex.intersection(point_geometry.bounds) - ) - possible_matches = self.priogrid_gdf.iloc[possible_matches_index] - precise_matches = possible_matches[ - possible_matches.intersects(point_geometry) - ] - - if len(precise_matches) > 0: - result = precise_matches.iloc[0]["gid"] - else: - result = None - else: - # Fallback to iterative search - result = None - for _, grid_cell in self.priogrid_gdf.iterrows(): - if grid_cell["geometry"].contains(point_geometry): - result = grid_cell["gid"] - break - - return result - - return _find_gid_for_point_impl(point_geometry) - else: - # Use in-memory cache - # Validate input geometry - if not point_geometry.is_valid: - raise ValueError("Invalid point geometry") - - # Create a consistent cache key - cache_key = self._get_point_cache_key(point_geometry) - - # Check cache first - if cache_key in self._gid_cache: - return self._gid_cache[cache_key] - - # Not in cache, perform the expensive operation - if self.priogrid_sindex: - possible_matches_index = list( - self.priogrid_sindex.intersection(point_geometry.bounds) - ) - possible_matches = self.priogrid_gdf.iloc[possible_matches_index] - precise_matches = possible_matches[ - possible_matches.intersects(point_geometry) - ] - - if len(precise_matches) > 0: - result = precise_matches.iloc[0]["gid"] - else: - result = None - else: - # Fallback to iterative search - result = None - for _, grid_cell in self.priogrid_gdf.iterrows(): - if grid_cell["geometry"].contains(point_geometry): - result = grid_cell["gid"] - break - - # Update cache - self._gid_cache[cache_key] = result - return result - - def find_country_for_gid(self, gid): - """ - Find the country information for a PRIO-GRID cell using majority area-based method. - Now with persistent disk caching. - """ - if self.use_disk_cache: - # Use disk cache - @self._disk_country_cache.cache() - def _find_country_for_gid_impl(gid): - # Get the PRIO-GRID cell - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == gid] - if len(grid_cell) == 0: - return None - - grid_geometry = grid_cell["geometry"].iloc[0] - - if grid_geometry.area == 0.0: - logger.warning(f"Zero-area grid geometry for GID {gid}, skipping country overlap") - return None - - # Use spatial index to find potentially intersecting countries - if hasattr(self.countries_gdf, "sindex"): - possible_countries_idx = list( - self.countries_gdf.sindex.intersection(grid_geometry.bounds) - ) - candidate_countries = self.countries_gdf.iloc[ - possible_countries_idx - ] - candidate_countries = candidate_countries[ - candidate_countries.intersects(grid_geometry) - ] - else: - candidate_countries = self.countries_gdf[ - self.countries_gdf.intersects(grid_geometry) - ] - - # Calculate area overlaps - overlaps = [] - for _, country in candidate_countries.iterrows(): - try: - intersection = country["geometry"].intersection(grid_geometry) - overlap_area = intersection.area - total_area = grid_geometry.area - overlap_ratio = overlap_area / total_area - - overlaps.append( - { - "country_data": country, - "overlap_area": overlap_area, - "overlap_ratio": overlap_ratio, - } - ) - except Exception as e: - logger.debug(f"Overlap calculation error for GID {gid}: {e}") - continue - - # Sort by overlap ratio (descending) - overlaps.sort(key=lambda x: x["overlap_ratio"], reverse=True) - - # Assignment rule: always use largest overlap - result = None - if overlaps: - country = overlaps[0]["country_data"] - method_used = "largest overlap" - - # Prepare result - if overlaps: - result = { - "gid": int(gid), - "iso_a3": country["ISO_A3"], - "country_name": country["NAME_EN"], - "overlap_ratio": ( - float(overlaps[0]["overlap_ratio"]) if overlaps else 0.0 - ), - "method": method_used, - } - - # Add all available country data - for col in country.index: - if col not in ["geometry", "ISO_A3", "NAME_EN"]: - result[col] = country[col] - - return result - - return _find_country_for_gid_impl(gid) - else: - # Use in-memory cache - # Generate consistent cache key - cache_key = self._get_gid_cache_key(gid) - - # Check cache first - if cache_key in self._country_cache: - cached_value = self._country_cache[cache_key] - if cached_value is None: - return None - result = cached_value.copy() - return result - - # Get the PRIO-GRID cell - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == gid] - if len(grid_cell) == 0: - self._country_cache[cache_key] = None - return None - - grid_geometry = grid_cell["geometry"].iloc[0] - - if grid_geometry.area == 0.0: - logger.warning(f"Zero-area grid geometry for GID {gid}, skipping country overlap") - self._country_cache[cache_key] = None - return None - - # Use spatial index to find potentially intersecting countries - if hasattr(self.countries_gdf, "sindex"): - possible_countries_idx = list( - self.countries_gdf.sindex.intersection(grid_geometry.bounds) - ) - candidate_countries = self.countries_gdf.iloc[possible_countries_idx] - candidate_countries = candidate_countries[ - candidate_countries.intersects(grid_geometry) - ] - else: - candidate_countries = self.countries_gdf[ - self.countries_gdf.intersects(grid_geometry) - ] - - # Calculate area overlaps - overlaps = [] - for _, country in candidate_countries.iterrows(): - try: - intersection = country["geometry"].intersection(grid_geometry) - overlap_area = intersection.area - total_area = grid_geometry.area - overlap_ratio = overlap_area / total_area - - overlaps.append( - { - "country_data": country, - "overlap_area": overlap_area, - "overlap_ratio": overlap_ratio, - } - ) - except Exception as e: - logger.debug(f"Overlap calculation error for GID {gid}: {e}") - continue - - # Sort by overlap ratio (descending) - overlaps.sort(key=lambda x: x["overlap_ratio"], reverse=True) - - # Assignment rule: always use largest overlap - result = None - if overlaps: - country = overlaps[0]["country_data"] - method_used = "largest overlap" - - # Prepare result - if overlaps: - result = { - "gid": int(gid), - "iso_a3": country["ISO_A3"], - "country_name": country["NAME_EN"], - "overlap_ratio": ( - float(overlaps[0]["overlap_ratio"]) if overlaps else 0.0 - ), - "method": method_used, - } - - # Add all available country data - for col in country.index: - if col not in ["geometry", "ISO_A3", "NAME_EN"]: - result[col] = country[col] - - # Update cache - self._country_cache[cache_key] = result - return result - - def batch_country_mapping(self, gid_list): - """ - Optimized batch mapping using vectorized operations where possible. - """ - results = [] - cache_hits = 0 - - for gid in gid_list: - # Check cache first - cache_key = self._get_gid_cache_key(gid) - if cache_key in self._country_cache: - cached_value = self._country_cache[cache_key] - if cached_value is not None: - results.append(cached_value.copy()) - else: - results.append(None) - cache_hits += 1 - continue - - # Not in cache, process this GID - result = self.find_country_for_gid(gid) - results.append(result) - - if cache_hits > 0: - logger.info( - f"Cache hits: {cache_hits}/{len(gid_list)} ({cache_hits/len(gid_list)*100:.1f}%)" - ) - - return pd.DataFrame([r for r in results if r is not None]) - - def find_gids_for_country(self, iso_a3): - """ - Find all PRIO-GRID cells that belong to a specific country. - Optimized using spatial indexing and vectorized operations. - """ - if self.use_disk_cache: - # Use disk cache - @self._disk_country_cache.cache() - def _find_gids_for_country_impl(iso_a3): - # Filter Natural Earth data for the specific country - country = self.countries_gdf[self.countries_gdf["ISO_A3"] == iso_a3] - - if len(country) == 0: - return [] - else: - # Get the country geometry - country_geometry = country.iloc[0]["geometry"] - - # OPTIMIZATION: Use spatial index to quickly find intersecting grid cells - if self.priogrid_sindex: - possible_matches_index = list( - self.priogrid_sindex.intersection(country_geometry.bounds) - ) - candidate_grid_cells = self.priogrid_gdf.iloc[ - possible_matches_index - ] - intersecting_cells = candidate_grid_cells[ - candidate_grid_cells.intersects(country_geometry) - ] - else: - intersecting_cells = self.priogrid_gdf[ - self.priogrid_gdf.intersects(country_geometry) - ] - - if len(intersecting_cells) == 0: - result = [] - else: - # OPTIMIZATION: Process with early termination - matching_gids = self._find_dominant_country_gids( - intersecting_cells, country_geometry, iso_a3 - ) - result = matching_gids - - return result - - return _find_gids_for_country_impl(iso_a3) - else: - # Use in-memory cache - # Create consistent cache key - cache_key = self._get_country_cache_key(iso_a3) - - # Check cache first - if cache_key in self._gids_for_country_cache: - result = self._gids_for_country_cache[cache_key] - return result - - # Not in cache, perform optimized operation - # Filter Natural Earth data for the specific country - country = self.countries_gdf[self.countries_gdf["ISO_A3"] == iso_a3] - - if len(country) == 0: - result = [] - else: - # Get the country geometry - country_geometry = country.iloc[0]["geometry"] - - # OPTIMIZATION: Use spatial index to quickly find intersecting grid cells - if self.priogrid_sindex: - possible_matches_index = list( - self.priogrid_sindex.intersection(country_geometry.bounds) - ) - candidate_grid_cells = self.priogrid_gdf.iloc[ - possible_matches_index - ] - intersecting_cells = candidate_grid_cells[ - candidate_grid_cells.intersects(country_geometry) - ] - else: - intersecting_cells = self.priogrid_gdf[ - self.priogrid_gdf.intersects(country_geometry) - ] - - if len(intersecting_cells) == 0: - result = [] - else: - # OPTIMIZATION: Process with early termination - matching_gids = self._find_dominant_country_gids( - intersecting_cells, country_geometry, iso_a3 - ) - result = matching_gids - - # Update cache - self._gids_for_country_cache[cache_key] = result - return result - - def _find_dominant_country_gids(self, intersecting_cells, country_geometry, iso_a3): - """Find grid cells where target country has dominant area using optimized approach.""" - matching_gids = [] - - for _, grid_cell in intersecting_cells.iterrows(): - grid_geometry = grid_cell["geometry"] - grid_gid = grid_cell["gid"] - - # OPTIMIZATION 1: Early check for complete containment - if country_geometry.contains(grid_geometry): - matching_gids.append(int(grid_gid)) - continue - - # Calculate overlap with the target country - if grid_geometry.area == 0.0: - logger.warning(f"Zero-area grid geometry for GID {grid_gid}, skipping country reverse lookup") - continue - - try: - intersection = country_geometry.intersection(grid_geometry) - overlap_ratio = intersection.area / grid_geometry.area - - # If overlap is significant, include it - if overlap_ratio > 0.5: - matching_gids.append(int(grid_gid)) - except Exception as e: - logger.debug(f"Geometry error for GID {grid_gid}: {e}") - continue - - return matching_gids - - def get_all_iso_a3_codes(self): - """ - Get a list of all unique ISO A3 codes in the Natural Earth dataset. - - Returns: - list: List of unique ISO A3 codes - """ - return self.countries_gdf["ISO_A3"].unique().tolist() - - def visualize_grid_and_country(self, gid): - """ - Visualize a PRIO-GRID cell and the country it belongs to. - - Parameters: - gid (int): PRIO-GRID cell ID - - Raises: - ImportError: If matplotlib is not available - ValueError: If the PRIO-GRID cell ID is not found - """ - try: - import matplotlib.pyplot as plt - except ImportError: - logger.error("Matplotlib is required for visualization") - return - - # Get the grid cell - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == gid] - - if len(grid_cell) == 0: - raise ValueError(f"PRIO-GRID cell with ID {gid} not found") - - # Find which country contains the grid cell based on majority area - country_info = self.find_country_for_gid(gid) - containing_country = None - - if country_info: - iso_a3 = country_info["iso_a3"] - containing_country = self.countries_gdf[ - self.countries_gdf["ISO_A3"] == iso_a3 - ].iloc[0] - - # Create plot - fig, ax = plt.subplots(1, 1, figsize=(10, 8)) - - # Plot all countries - self.countries_gdf.plot(ax=ax, color="lightgray", edgecolor="white", alpha=0.5) - - # Highlight the containing country - if containing_country is not None: - gpd.GeoDataFrame([containing_country], geometry="geometry").plot( - ax=ax, color="lightblue", edgecolor="blue", alpha=0.7 - ) - - # Plot the grid cell - grid_cell.plot(ax=ax, color="red", alpha=0.7) - - # Add centroid marker - centroid = grid_cell["centroid"].iloc[0] - ax.scatter( - centroid.x, - centroid.y, - color="yellow", - s=100, - marker="*", - edgecolor="black", - label="Grid Centroid", - ) - - # Set title and legend - country_name = ( - containing_country["NAME_EN"] - if containing_country is not None - else "No Country" - ) - method = country_info.get("method", "unknown") if country_info else "unknown" - overlap_ratio = country_info.get("overlap_ratio", 0) if country_info else 0 - - ax.set_title( - f"PRIO-GRID Cell {gid} in {country_name}\n" - f"Method: {method} | Overlap Ratio: {overlap_ratio:.3f}" - ) - ax.legend() - - plt.tight_layout() - plt.show() - - def calculate_capital_distance(self, grid_lat, grid_lon, capital_coords): - """ - Calculate distance from grid cell to capital. - - Parameters: - grid_lat: Latitude of grid centroid - grid_lon: Longitude of grid centroid - capital_coords: Tuple of (capital_lon, capital_lat) or None - - Returns: - float or None: Distance in kilometers or None if calculation fails - """ - if capital_coords is None or None in capital_coords: - return None - - try: - capital_lon, capital_lat = capital_coords - return cached_haversine(grid_lat, grid_lon, capital_lat, capital_lon) - except Exception as e: - logger.error(f"Error calculating capital distance: {e}") - return None - - def get_gid_from_point(self, point: ShapelyPoint) -> Optional[int]: - """ - Find the PRIO-GRID cell ID that contains a point. - - Parameters: - point (ShapelyPoint): Point geometry to locate - - Returns: - int or None: PRIO-GRID cell ID containing the point, or None if not found - """ - for _, grid_cell in self.priogrid_gdf.iterrows(): - if grid_cell["geometry"].contains(point): - return grid_cell["gid"] - return None - - def get_point_from_gid(self, gid: int) -> Optional["Point"]: # noqa: F821 - """ - Get the centroid point of a PRIO-GRID cell. - - Parameters: - gid (int): PRIO-GRID cell ID - - Returns: - Point or None: Point object representing the centroid, or None if not found - """ - # from ..space import Point - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == gid] - if len(grid_cell) == 0: - return None - centroid = grid_cell["centroid"].iloc[0] - return Point(lat=centroid.y, lon=centroid.x) # noqa: F821 - - def _load_admin_data(self, admin_path, admin_level): - """ - Load and preprocess admin1 or admin2 data from the provided path. - - Parameters: - admin_path (str): Path to the admin dataset file - admin_level (str): Either "admin1" or "admin2" - - Returns: - GeoDataFrame: Processed admin data with proper geometry - """ - # Load admin data - if admin_path.endswith(".shp"): - admin_gdf = gpd.read_file(admin_path) - else: - # Assume it's a CSV with WKT geometry - admin_df = pd.read_csv(admin_path) - - # Parse the WKT geometry, removing the SRID prefix if present - def parse_wkt(wkt_str): - if wkt_str.startswith("SRID=4326;"): - wkt_str = wkt_str.replace("SRID=4326;", "") - return wkt.loads(wkt_str) - - admin_df["geometry"] = admin_df["geometry"].apply(parse_wkt) - admin_gdf = gpd.GeoDataFrame(admin_df, geometry="geometry") - - # Set CRS if not already set - if admin_gdf.crs is None: - admin_gdf.set_crs(epsg=4326, inplace=True) - - # Fix invalid geometries before use - admin_gdf["geometry"] = admin_gdf["geometry"].make_valid() - - # Validate data - self._validate_admin_data(admin_gdf, admin_level) - - return admin_gdf - - def _validate_admin_data(self, admin_gdf, admin_level): - """Validate admin data for consistency and correctness.""" - # Check for required columns based on admin level - if admin_level == "admin1": - required_columns = ["gaul1_code", "gaul1_name", "iso3_code", "geometry"] - else: # admin2 - required_columns = ["gaul2_code", "gaul2_name", "iso3_code", "geometry"] - - missing_columns = [ - col for col in required_columns if col not in admin_gdf.columns - ] - if missing_columns: - err_msg = f"Missing required columns in {admin_level} data: {missing_columns}" - logger.error(err_msg) - raise ValueError(err_msg) - - # Check geometry validity - invalid_geometries = admin_gdf[~admin_gdf["geometry"].is_valid] - if not invalid_geometries.empty: - logger.warning( - f"Found {len(invalid_geometries)} invalid geometries in {admin_level} data" - ) - - def find_admin1_for_gid(self, gid): - """ - Find the admin1 information for a PRIO-GRID cell. - First determines the country, then finds the admin1 region within that country. - Now with persistent disk caching. - """ - if self.admin1_gdf is None: - logger.warning("Admin1 data not loaded. Cannot find admin1 for GID.") - return None - - if self.use_disk_cache: - # Use disk cache - @self._disk_admin1_cache.cache() - def _find_admin1_for_gid_impl(gid): - # First find the country for this GID - country_info = self.find_country_for_gid(gid) - if not country_info: - return None - - iso_a3 = country_info["iso_a3"] - - # Get the PRIO-GRID cell - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == gid] - if len(grid_cell) == 0: - return None - - grid_geometry = grid_cell["geometry"].iloc[0] - - if grid_geometry.area == 0.0: - logger.warning(f"Zero-area grid geometry for GID {gid}, skipping admin1 overlap") - return None - - # Filter admin1 regions to only those in the same country - country_admin1 = self.admin1_gdf[self.admin1_gdf["iso3_code"] == iso_a3] - - if len(country_admin1) == 0: - return None - - # Use spatial index to find potentially intersecting admin1 regions - if self.admin1_sindex: - possible_admin1_idx = list( - self.admin1_sindex.intersection(grid_geometry.bounds) - ) - candidate_admin1 = self.admin1_gdf.iloc[possible_admin1_idx] - candidate_admin1 = candidate_admin1[ - candidate_admin1.intersects(grid_geometry) - ] - # Filter to only those in the same country - candidate_admin1 = candidate_admin1[ - candidate_admin1["iso3_code"] == iso_a3 - ] - else: - candidate_admin1 = country_admin1[ - country_admin1.intersects(grid_geometry) - ] - - if len(candidate_admin1) == 0: - return None - - # Calculate overlaps and use largest overlap - overlaps = [] - for _, admin1 in candidate_admin1.iterrows(): - try: - intersection = admin1["geometry"].intersection(grid_geometry) - overlap_area = intersection.area - total_area = grid_geometry.area - overlap_ratio = overlap_area / total_area - - overlaps.append( - { - "admin1_data": admin1, - "overlap_area": overlap_area, - "overlap_ratio": overlap_ratio, - } - ) - except Exception as e: - logger.debug(f"Overlap calculation error for GID {gid}: {e}") - continue - - # Sort by overlap ratio (descending) - overlaps.sort(key=lambda x: x["overlap_ratio"], reverse=True) - - if not overlaps: - return None - - admin1 = overlaps[0]["admin1_data"] - method_used = "largest overlap" - - # Prepare result - result = { - "gid": int(gid), - "gaul1_code": ( - int(admin1["gaul1_code"]) - if admin1["gaul1_code"] is not None - else None - ), - "gaul1_name": admin1["gaul1_name"], - "iso3_code": admin1["iso3_code"], - "method": method_used, - } - - # Add additional fields if available - if "gaul0_code" in admin1: - result["gaul0_code"] = ( - int(admin1["gaul0_code"]) - if admin1["gaul0_code"] is not None - else None - ) - if "gaul0_name" in admin1: - result["gaul0_name"] = admin1["gaul0_name"] - if "continent" in admin1: - result["continent"] = admin1["continent"] - if "disp_en" in admin1: - result["disp_en"] = admin1["disp_en"] - - return result - - return _find_admin1_for_gid_impl(gid) - else: - # Use in-memory cache - # Generate consistent cache key - cache_key = self._get_admin_cache_key(gid, "admin1") - - # Check cache first - if cache_key in self._admin1_cache: - cached_value = self._admin1_cache[cache_key] - if cached_value is not None: - result = cached_value.copy() - return result - else: - return None - - # First find the country for this GID - country_info = self.find_country_for_gid(gid) - if not country_info: - self._admin1_cache[cache_key] = None - return None - - iso_a3 = country_info["iso_a3"] - - # Get the PRIO-GRID cell - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == gid] - if len(grid_cell) == 0: - self._admin1_cache[cache_key] = None - return None - - grid_geometry = grid_cell["geometry"].iloc[0] - - if grid_geometry.area == 0.0: - logger.warning(f"Zero-area grid geometry for GID {gid}, skipping admin1 overlap") - self._admin1_cache[cache_key] = None - return None - - # Filter admin1 regions to only those in the same country - country_admin1 = self.admin1_gdf[self.admin1_gdf["iso3_code"] == iso_a3] - - if len(country_admin1) == 0: - self._admin1_cache[cache_key] = None - return None - - # Use spatial index to find potentially intersecting admin1 regions - if self.admin1_sindex: - possible_admin1_idx = list( - self.admin1_sindex.intersection(grid_geometry.bounds) - ) - candidate_admin1 = self.admin1_gdf.iloc[possible_admin1_idx] - candidate_admin1 = candidate_admin1[ - candidate_admin1.intersects(grid_geometry) - ] - # Filter to only those in the same country - candidate_admin1 = candidate_admin1[ - candidate_admin1["iso3_code"] == iso_a3 - ] - else: - candidate_admin1 = country_admin1[ - country_admin1.intersects(grid_geometry) - ] - - if len(candidate_admin1) == 0: - self._admin1_cache[cache_key] = None - return None - - # Calculate overlaps and use largest overlap - overlaps = [] - for _, admin1 in candidate_admin1.iterrows(): - try: - intersection = admin1["geometry"].intersection(grid_geometry) - overlap_area = intersection.area - total_area = grid_geometry.area - overlap_ratio = overlap_area / total_area - - overlaps.append( - { - "admin1_data": admin1, - "overlap_area": overlap_area, - "overlap_ratio": overlap_ratio, - } - ) - except Exception as e: - logger.debug(f"Overlap calculation error for GID {gid}: {e}") - continue - - # Sort by overlap ratio (descending) - overlaps.sort(key=lambda x: x["overlap_ratio"], reverse=True) - - if not overlaps: - self._admin1_cache[cache_key] = None - return None - - admin1 = overlaps[0]["admin1_data"] - method_used = "largest overlap" - - # Prepare result - result = { - "gid": int(gid), - "gaul1_code": ( - int(admin1["gaul1_code"]) - if admin1["gaul1_code"] is not None - else None - ), - "gaul1_name": admin1["gaul1_name"], - "iso3_code": admin1["iso3_code"], - "method": method_used, - } - - # Add additional fields if available - if "gaul0_code" in admin1: - result["gaul0_code"] = ( - int(admin1["gaul0_code"]) - if admin1["gaul0_code"] is not None - else None - ) - if "gaul0_name" in admin1: - result["gaul0_name"] = admin1["gaul0_name"] - if "continent" in admin1: - result["continent"] = admin1["continent"] - if "disp_en" in admin1: - result["disp_en"] = admin1["disp_en"] - - # Update cache - self._admin1_cache[cache_key] = result - return result - - def find_admin2_for_gid(self, gid): - """ - Find the admin2 information for a PRIO-GRID cell. - First determines the country, then finds the admin2 region within that country. - Now with persistent disk caching. - """ - if self.admin2_gdf is None: - logger.warning("Admin2 data not loaded. Cannot find admin2 for GID.") - return None - - if self.use_disk_cache: - # Use disk cache - @self._disk_admin2_cache.cache() - def _find_admin2_for_gid_impl(gid): - # First find the country for this GID - country_info = self.find_country_for_gid(gid) - if not country_info: - return None - - iso_a3 = country_info["iso_a3"] - - # Get the PRIO-GRID cell - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == gid] - if len(grid_cell) == 0: - return None - - grid_geometry = grid_cell["geometry"].iloc[0] - - if grid_geometry.area == 0.0: - logger.warning(f"Zero-area grid geometry for GID {gid}, skipping admin2 overlap") - return None - - # Filter admin2 regions to only those in the same country - country_admin2 = self.admin2_gdf[self.admin2_gdf["iso3_code"] == iso_a3] - - if len(country_admin2) == 0: - return None - - # Use spatial index to find potentially intersecting admin2 regions - if self.admin2_sindex: - possible_admin2_idx = list( - self.admin2_sindex.intersection(grid_geometry.bounds) - ) - candidate_admin2 = self.admin2_gdf.iloc[possible_admin2_idx] - candidate_admin2 = candidate_admin2[ - candidate_admin2.intersects(grid_geometry) - ] - # Filter to only those in the same country - candidate_admin2 = candidate_admin2[ - candidate_admin2["iso3_code"] == iso_a3 - ] - else: - candidate_admin2 = country_admin2[ - country_admin2.intersects(grid_geometry) - ] - - if len(candidate_admin2) == 0: - return None - - # Calculate overlaps and use largest overlap - overlaps = [] - for _, admin2 in candidate_admin2.iterrows(): - try: - intersection = admin2["geometry"].intersection(grid_geometry) - overlap_area = intersection.area - total_area = grid_geometry.area - overlap_ratio = overlap_area / total_area - - overlaps.append( - { - "admin2_data": admin2, - "overlap_area": overlap_area, - "overlap_ratio": overlap_ratio, - } - ) - except Exception as e: - logger.debug(f"Overlap calculation error for GID {gid}: {e}") - continue - - # Sort by overlap ratio (descending) - overlaps.sort(key=lambda x: x["overlap_ratio"], reverse=True) - - if not overlaps: - return None - - admin2 = overlaps[0]["admin2_data"] - method_used = "largest overlap" - - # Prepare result - result = { - "gid": int(gid), - "gaul2_code": ( - int(admin2["gaul2_code"]) - if admin2["gaul2_code"] is not None - else None - ), - "gaul2_name": admin2["gaul2_name"], - "iso3_code": admin2["iso3_code"], - "method": method_used, - } - - # Add additional fields if available - if "gaul0_code" in admin2: - result["gaul0_code"] = ( - int(admin2["gaul0_code"]) - if admin2["gaul0_code"] is not None - else None - ) - if "gaul0_name" in admin2: - result["gaul0_name"] = admin2["gaul0_name"] - if "gaul1_code" in admin2: - result["gaul1_code"] = ( - int(admin2["gaul1_code"]) - if admin2["gaul1_code"] is not None - else None - ) - if "gaul1_name" in admin2: - result["gaul1_name"] = admin2["gaul1_name"] - if "continent" in admin2: - result["continent"] = admin2["continent"] - if "disp_en" in admin2: - result["disp_en"] = admin2["disp_en"] - - return result - - return _find_admin2_for_gid_impl(gid) - else: - # Use in-memory cache - # Generate consistent cache key - cache_key = self._get_admin_cache_key(gid, "admin2") - - # Check cache first - if cache_key in self._admin2_cache: - cached_value = self._admin2_cache[cache_key] - if cached_value is not None: - result = cached_value.copy() - return result - else: - return None - - # First find the country for this GID - country_info = self.find_country_for_gid(gid) - if not country_info: - self._admin2_cache[cache_key] = None - return None - - iso_a3 = country_info["iso_a3"] - - # Get the PRIO-GRID cell - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == gid] - if len(grid_cell) == 0: - self._admin2_cache[cache_key] = None - return None - - grid_geometry = grid_cell["geometry"].iloc[0] - - if grid_geometry.area == 0.0: - logger.warning(f"Zero-area grid geometry for GID {gid}, skipping admin2 overlap") - self._admin2_cache[cache_key] = None - return None - - # Filter admin2 regions to only those in the same country - country_admin2 = self.admin2_gdf[self.admin2_gdf["iso3_code"] == iso_a3] - - if len(country_admin2) == 0: - self._admin2_cache[cache_key] = None - return None - - # Use spatial index to find potentially intersecting admin2 regions - if self.admin2_sindex: - possible_admin2_idx = list( - self.admin2_sindex.intersection(grid_geometry.bounds) - ) - candidate_admin2 = self.admin2_gdf.iloc[possible_admin2_idx] - candidate_admin2 = candidate_admin2[ - candidate_admin2.intersects(grid_geometry) - ] - # Filter to only those in the same country - candidate_admin2 = candidate_admin2[ - candidate_admin2["iso3_code"] == iso_a3 - ] - else: - candidate_admin2 = country_admin2[ - country_admin2.intersects(grid_geometry) - ] - - if len(candidate_admin2) == 0: - self._admin2_cache[cache_key] = None - return None - - # Calculate overlaps and use largest overlap - overlaps = [] - for _, admin2 in candidate_admin2.iterrows(): - try: - intersection = admin2["geometry"].intersection(grid_geometry) - overlap_area = intersection.area - total_area = grid_geometry.area - overlap_ratio = overlap_area / total_area - - overlaps.append( - { - "admin2_data": admin2, - "overlap_area": overlap_area, - "overlap_ratio": overlap_ratio, - } - ) - except Exception as e: - logger.debug(f"Overlap calculation error for GID {gid}: {e}") - continue - - # Sort by overlap ratio (descending) - overlaps.sort(key=lambda x: x["overlap_ratio"], reverse=True) - - if not overlaps: - self._admin2_cache[cache_key] = None - return None - - admin2 = overlaps[0]["admin2_data"] - method_used = "largest overlap" - - # Prepare result - result = { - "gid": int(gid), - "gaul2_code": ( - int(admin2["gaul2_code"]) - if admin2["gaul2_code"] is not None - else None - ), - "gaul2_name": admin2["gaul2_name"], - "iso3_code": admin2["iso3_code"], - "method": method_used, - } - - # Add additional fields if available - if "gaul0_code" in admin2: - result["gaul0_code"] = ( - int(admin2["gaul0_code"]) - if admin2["gaul0_code"] is not None - else None - ) - if "gaul0_name" in admin2: - result["gaul0_name"] = admin2["gaul0_name"] - if "gaul1_code" in admin2: - result["gaul1_code"] = ( - int(admin2["gaul1_code"]) - if admin2["gaul1_code"] is not None - else None - ) - if "gaul1_name" in admin2: - result["gaul1_name"] = admin2["gaul1_name"] - if "continent" in admin2: - result["continent"] = admin2["continent"] - if "disp_en" in admin2: - result["disp_en"] = admin2["disp_en"] - - # Update cache - self._admin2_cache[cache_key] = result - return result - - def find_all_admin_for_gid(self, gid): - """ - Find country, admin1, and admin2 information for a PRIO-GRID cell. - - Parameters: - gid (int): PRIO-GRID cell ID - - Returns: - dict: Combined information for country, admin1, and admin2 - """ - result = {"gid": int(gid)} - - # Find country information - country_info = self.find_country_for_gid(gid) - if country_info: - result["country"] = country_info - - # Find admin1 information - admin1_info = self.find_admin1_for_gid(gid) - if admin1_info: - result["admin1"] = admin1_info - - # Find admin2 information - admin2_info = self.find_admin2_for_gid(gid) - if admin2_info: - result["admin2"] = admin2_info - - return result - - def batch_admin_mapping(self, gid_list, admin_level="admin1"): - """ - Batch mapping for admin1 or admin2 using vectorized operations. - - Parameters: - gid_list (list): List of PRIO-GRID cell IDs - admin_level (str): Either "admin1" or "admin2" - - Returns: - DataFrame: Results of the batch mapping - """ - if admin_level not in ["admin1", "admin2"]: - raise ValueError("admin_level must be either 'admin1' or 'admin2'") - - if admin_level == "admin1" and self.admin1_gdf is None: - raise ValueError("Admin1 data not loaded") - if admin_level == "admin2" and self.admin2_gdf is None: - raise ValueError("Admin2 data not loaded") - - # Select the appropriate find function - if admin_level == "admin1": - find_func = self.find_admin1_for_gid - cache = self._admin1_cache - else: # admin2 - find_func = self.find_admin2_for_gid - cache = self._admin2_cache - - results = [] - cache_hits = 0 - - for gid in gid_list: - # Check cache first - cache_key = self._get_admin_cache_key(gid, admin_level) - if cache_key in cache: - cached_value = cache[cache_key] - if cached_value is not None: - results.append(cached_value.copy()) - else: - results.append(None) - cache_hits += 1 - continue - - # Not in cache, process this GID - result = find_func(gid) - results.append(result) - - if cache_hits > 0: - logger.info( - f"Cache hits: {cache_hits}/{len(gid_list)} ({cache_hits/len(gid_list)*100:.1f}%)" - ) - - return pd.DataFrame([r for r in results if r is not None]) - - def batch_admin_mapping_parallel( - self, gid_list, admin_level="admin1", batch_size=1000 - ): - """ - Batch mapping for admin1 or admin2 using multiprocessing. - - Parameters: - gid_list (list): List of PRIO-GRID cell IDs - admin_level (str): Either "admin1" or "admin2" - batch_size (int): Number of GIDs to process in each batch - - Returns: - DataFrame: Results of the batch mapping - """ - if admin_level not in ["admin1", "admin2"]: - raise ValueError("admin_level must be either 'admin1' or 'admin2'") - - if admin_level == "admin1" and self.admin1_gdf is None: - raise ValueError("Admin1 data not loaded") - if admin_level == "admin2" and self.admin2_gdf is None: - raise ValueError("Admin2 data not loaded") - - self._init_process_pool() - - # Split into batches - batches = [ - gid_list[i : i + batch_size] for i in range(0, len(gid_list), batch_size) - ] - - # Select the appropriate find function - if admin_level == "admin1": - find_func = self.find_admin1_for_gid - else: # admin2 - find_func = self.find_admin2_for_gid - - # Create partial function for mapping - map_func = partial(self._process_gid_batch_admin, find_func=find_func) - - # Process batches in parallel - results = [] - for batch_result in self._process_pool.imap_unordered(map_func, batches): - results.extend(batch_result) - - return pd.DataFrame(results) - - def _process_gid_batch_admin(self, gid_batch, find_func): - """Process a batch of GIDs in a single process for admin mapping""" - batch_results = [] - for gid in gid_batch: - result = find_func(gid) - batch_results.append(result) - return batch_results - - def find_all_admin_for_point(self, point_geometry): - """ - Find country, admin1, and admin2 information for a point. - - Parameters: - point_geometry (shapely.geometry.Point): Point geometry to locate - - Returns: - dict: Combined information for country, admin1, and admin2 - """ - # Find the GID for the point - gid = self.find_gid_for_point(point_geometry) - if gid is None: - return None - - # Use the existing method to find all admin information - return self.find_all_admin_for_gid(gid) - - def extend_find_country_for_gid(self, gid): - """ - Extended version of find_country_for_gid that also includes admin1 and admin2 information. - - Parameters: - gid (int): PRIO-GRID cell ID - - Returns: - dict: Combined information for country, admin1, and admin2 - """ - # Get country information using the existing method - result = self.find_country_for_gid(gid) - - if result is None: - return None - - # Add admin1 information - admin1_info = self.find_admin1_for_gid(gid) - if admin1_info: - result["admin1"] = admin1_info - - # Add admin2 information - admin2_info = self.find_admin2_for_gid(gid) - if admin2_info: - result["admin2"] = admin2_info - - return result - - def visualize_grid_and_admin( - self, gid, admin_level="admin1", show_all_admins=False - ): - """ - Visualize a PRIO-GRID cell and its corresponding admin1 or admin2 boundary. - - Parameters: - gid (int): PRIO-GRID cell ID - admin_level (str): Either "admin1" or "admin2" - show_all_admins (bool): If True, shows all admin regions in the area, - otherwise only shows the one containing the grid cell - - Raises: - ImportError: If matplotlib is not available - ValueError: If the PRIO-GRID cell ID is not found or admin data is not loaded - """ - try: - import matplotlib.pyplot as plt - import matplotlib.patches as mpatches - except ImportError: - logger.error("Matplotlib is required for visualization") - return - - if admin_level not in ["admin1", "admin2"]: - raise ValueError("admin_level must be either 'admin1' or 'admin2'") - - # Check if admin data is loaded - if admin_level == "admin1" and self.admin1_gdf is None: - raise ValueError("Admin1 data not loaded. Cannot visualize admin1.") - if admin_level == "admin2" and self.admin2_gdf is None: - raise ValueError("Admin2 data not loaded. Cannot visualize admin2.") - - # Get the grid cell - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == gid] - if len(grid_cell) == 0: - raise ValueError(f"PRIO-GRID cell with ID {gid} not found") - - grid_geometry = grid_cell["geometry"].iloc[0] - grid_centroid = grid_cell["centroid"].iloc[0] - - # Select appropriate admin data - if admin_level == "admin1": - admin_gdf = self.admin1_gdf - admin_code_col = "gaul1_code" - admin_name_col = "gaul1_name" - find_func = self.find_admin1_for_gid - else: # admin2 - admin_gdf = self.admin2_gdf - admin_code_col = "gaul2_code" - admin_name_col = "gaul2_name" - find_func = self.find_admin2_for_gid - - # Find the admin region containing the grid cell - admin_info = find_func(gid) - - # Create plot - fig, ax = plt.subplots(1, 1, figsize=(12, 10)) - - if show_all_admins: - # Show all admin regions that intersect with the grid cell's bounds - if admin_level == "admin1" and self.admin1_sindex: - possible_admins_idx = list( - self.admin1_sindex.intersection(grid_geometry.bounds) - ) - nearby_admins = self.admin1_gdf.iloc[possible_admins_idx] - elif admin_level == "admin2" and self.admin2_sindex: - possible_admins_idx = list( - self.admin2_sindex.intersection(grid_geometry.bounds) - ) - nearby_admins = self.admin2_gdf.iloc[possible_admins_idx] - else: - nearby_admins = admin_gdf[admin_gdf.intersects(grid_geometry.buffer(1))] - - # Plot all nearby admin regions - nearby_admins.plot( - ax=ax, color="lightgray", edgecolor="white", alpha=0.5, linewidth=0.5 - ) - - # Highlight the containing admin region if found - if admin_info: - containing_admin = admin_gdf[ - admin_gdf[admin_code_col] == admin_info[admin_code_col] - ] - if len(containing_admin) > 0: - containing_admin.plot( - ax=ax, - color="lightblue", - edgecolor="blue", - alpha=0.7, - linewidth=1.5, - ) - else: - # Only show the containing admin region - if admin_info: - containing_admin = admin_gdf[ - admin_gdf[admin_code_col] == admin_info[admin_code_col] - ] - if len(containing_admin) > 0: - containing_admin.plot( - ax=ax, - color="lightblue", - edgecolor="blue", - alpha=0.7, - linewidth=1.5, - ) - - # Plot the grid cell - grid_cell.plot(ax=ax, color="red", alpha=0.8, edgecolor="darkred", linewidth=2) - - # Add centroid marker - ax.scatter( - grid_centroid.x, - grid_centroid.y, - color="yellow", - s=150, - marker="*", - edgecolor="black", - linewidth=1, - label="Grid Centroid", - zorder=5, - ) - - # Set title and legend - if admin_info: - admin_name = admin_info[admin_name_col] - admin_code = admin_info[admin_code_col] - method = admin_info.get("method", "unknown") - - title = f"PRIO-GRID Cell {gid} in {admin_level.upper()} {admin_name} (Code: {admin_code})\n" - title += f"Method: {method}" - else: - title = f"PRIO-GRID Cell {gid} - No {admin_level.upper()} region found" - - ax.set_title(title, fontsize=14, fontweight="bold") - - # Create legend - legend_elements = [ - mpatches.Patch(color="red", alpha=0.8, label="PRIO-GRID Cell"), - mpatches.Patch( - color="lightblue", alpha=0.7, label=f"Containing {admin_level.upper()}" - ), - mpatches.Patch( - color="lightgray", - alpha=0.5, - label="Other Admin Regions" if show_all_admins else None, - ), - ] - - # Filter out None values - legend_elements = [e for e in legend_elements if e.get_label() is not None] - - ax.legend(handles=legend_elements, loc="upper right", bbox_to_anchor=(1, 1)) - - # Add grid coordinates as text - ax.text( - grid_centroid.x, - grid_centroid.y - 0.1, - f'GID: {gid}\n({grid_cell["xcoord"].iloc[0]:.1f}, {grid_cell["ycoord"].iloc[0]:.1f})', - ha="center", - va="top", - fontsize=9, - bbox=dict(boxstyle="round,pad=0.3", facecolor="white", alpha=0.8), - ) - - # Set aspect ratio and remove unnecessary axes - ax.set_aspect("equal") - ax.set_xlabel("Longitude") - ax.set_ylabel("Latitude") - ax.grid(True, alpha=0.3) - - plt.tight_layout() - plt.show() - - def visualize_admin_regions_for_gids( - self, - gid_list, - admin_level="admin1", - show_grid_cells=True, - color_by_country=True, - figsize=(15, 10), - ): - """ - Visualize admin1 or admin2 regions for multiple PRIO-GRID cells. - - Parameters: - gid_list (list): List of PRIO-GRID cell IDs - admin_level (str): Either "admin1" or "admin2" - show_grid_cells (bool): Whether to show the PRIO-GRID cells on the map - color_by_country (bool): If True, colors admin regions by country - figsize (tuple): Figure size (width, height) - - Raises: - ImportError: If matplotlib is not available - ValueError: If admin data is not loaded - """ - try: - import matplotlib.pyplot as plt - import matplotlib.patches as mpatches - from matplotlib.colors import LinearSegmentedColormap # noqa: F401 - import matplotlib.cm as cm # noqa: F401 - except ImportError: - logger.error("Matplotlib is required for visualization") - return - - if admin_level not in ["admin1", "admin2"]: - raise ValueError("admin_level must be either 'admin1' or 'admin2'") - - # Check if admin data is loaded - if admin_level == "admin1" and self.admin1_gdf is None: - raise ValueError("Admin1 data not loaded. Cannot visualize admin1.") - if admin_level == "admin2" and self.admin2_gdf is None: - raise ValueError("Admin2 data not loaded. Cannot visualize admin2.") - - # Get admin information for all GIDs - if admin_level == "admin1": - admin_results = self.batch_admin_mapping(gid_list, admin_level="admin1") - admin_gdf = self.admin1_gdf - admin_code_col = "gaul1_code" - admin_name_col = "gaul1_name" - else: - admin_results = self.batch_admin_mapping(gid_list, admin_level="admin2") - admin_gdf = self.admin2_gdf - admin_code_col = "gaul2_code" - admin_name_col = "gaul2_name" - - if admin_results.empty: - logger.warning(f"No {admin_level} regions found for the provided GIDs") - return - - # Get unique admin regions - unique_admin_codes = admin_results[admin_code_col].unique() - admin_regions = admin_gdf[admin_gdf[admin_code_col].isin(unique_admin_codes)] - - # Get grid cells - grid_cells = self.priogrid_gdf[self.priogrid_gdf["gid"].isin(gid_list)] - - # Create plot - fig, ax = plt.subplots(1, 1, figsize=figsize) - - # Plot all admin regions in the area - if color_by_country: - # Create a colormap for countries - unique_countries = admin_results["iso3_code"].unique() - country_colors = plt.cm.tab20(np.linspace(0, 1, len(unique_countries))) - country_color_map = { - country: color - for country, color in zip(unique_countries, country_colors) - } - - # Plot with color based on country - for _, admin in admin_regions.iterrows(): - admin_code = admin[admin_code_col] - admin_row = admin_results[admin_results[admin_code_col] == admin_code] - if not admin_row.empty: - country = admin_row["iso3_code"].iloc[0] - color = country_color_map[country] - admin_gdf[admin_gdf[admin_code_col] == admin_code].plot( - ax=ax, color=[color], edgecolor="black", alpha=0.7, linewidth=1 - ) - - # Create legend for countries - legend_elements = [ - mpatches.Patch(color=country_color_map[country], label=country) - for country in unique_countries - ] - else: - # Plot with uniform color - admin_regions.plot( - ax=ax, color="lightblue", edgecolor="black", alpha=0.7, linewidth=1 - ) - legend_elements = [ - mpatches.Patch( - color="lightblue", alpha=0.7, label=f"{admin_level.upper()} Regions" - ) - ] - - # Plot grid cells if requested - if show_grid_cells: - grid_cells.plot( - ax=ax, color="red", alpha=0.8, edgecolor="darkred", linewidth=1.5 - ) - - # Add labels for grid cells - for _, grid_cell in grid_cells.iterrows(): - centroid = grid_cell["centroid"] - ax.text( - centroid.x, - centroid.y, - str(grid_cell["gid"]), - ha="center", - va="center", - fontsize=8, - color="white", - fontweight="bold", - bbox=dict(boxstyle="circle,pad=0.2", facecolor="red", alpha=0.7), - ) - - # Add labels for admin regions - for _, admin in admin_regions.iterrows(): - centroid = admin["geometry"].centroid - admin_name = admin[admin_name_col] - admin_code = admin[admin_code_col] - - # Truncate long names - if len(admin_name) > 20: - admin_name = admin_name[:17] + "..." - - ax.text( - centroid.x, - centroid.y, - f"{admin_name}\n({admin_code})", - ha="center", - va="center", - fontsize=9, - bbox=dict(boxstyle="round,pad=0.3", facecolor="white", alpha=0.8), - ) - - # Set title - ax.set_title( - f"{admin_level.upper()} Regions for {len(gid_list)} PRIO-GRID Cells\n" - f"Found {len(unique_admin_codes)} unique {admin_level.upper()} regions", - fontsize=14, - fontweight="bold", - ) - - # Add grid cells to legend if shown - if show_grid_cells: - legend_elements.insert( - 0, mpatches.Patch(color="red", alpha=0.8, label="PRIO-GRID Cells") - ) - - ax.legend(handles=legend_elements, loc="upper right", bbox_to_anchor=(1, 1)) - - # Set aspect ratio and labels - ax.set_aspect("equal") - ax.set_xlabel("Longitude") - ax.set_ylabel("Latitude") - ax.grid(True, alpha=0.3) - - # Add summary statistics as text - stats_text = f"Total GIDs: {len(gid_list)}\n" - stats_text += ( - f"Unique {admin_level.upper()} regions: {len(unique_admin_codes)}\n" - ) - if color_by_country: - stats_text += f"Unique countries: {len(unique_countries)}" - - ax.text( - 0.02, - 0.98, - stats_text, - transform=ax.transAxes, - fontsize=10, - verticalalignment="top", - bbox=dict(boxstyle="round,pad=0.5", facecolor="white", alpha=0.8), - ) - - plt.tight_layout() - plt.show() - - def find_country_by_iso_a3(self, iso_a3): - """ - Find country information by ISO A3 code from the Natural Earth dataset. - - Parameters: - iso_a3 (str): ISO A3 code of the country (e.g., 'USA', 'FRA', 'TZA') - - Returns: - dict: Country information with all available fields from Natural Earth, - or None if country is not found - """ - # Validate input - if not isinstance(iso_a3, str) or len(iso_a3) != 3: - logger.warning( - f"Invalid ISO A3 code format: {iso_a3}. Expected 3-character string." - ) - return None - - # Convert to uppercase for consistency - iso_a3 = iso_a3.upper() - - if self.use_disk_cache: - # Use disk cache - @self._disk_country_cache.cache() - def _find_country_by_iso_a3_impl(iso_a3): - # Find the country in the Natural Earth dataset - country_rows = self.countries_gdf[ - self.countries_gdf["ISO_A3"] == iso_a3 - ] - - if len(country_rows) == 0: - return None - - # Get the first (and should be only) matching country - country = country_rows.iloc[0] - - # Prepare result with all available country data - result = { - "iso_a3": country["ISO_A3"], - "country_name": country["NAME_EN"], - } - - # Add all other available fields from Natural Earth - for col in country.index: - if col not in ["geometry", "ISO_A3", "NAME_EN"]: - # Convert numpy types to native Python types - value = country[col] - if pd.isna(value): - result[col] = None - elif isinstance(value, (np.integer, np.int64, np.int32)): - result[col] = int(value) - elif isinstance(value, (np.floating, np.float64, np.float32)): - result[col] = float(value) - elif isinstance(value, np.bool_): - result[col] = bool(value) - else: - result[col] = str(value) - - # Add geometry as WKT string - if hasattr(country["geometry"], "wkt"): - result["geometry_wkt"] = country["geometry"].wkt - - # Add some commonly useful derived fields - result["data_source"] = "Natural Earth" - result["centroid"] = { - "lon": float(country["geometry"].centroid.x), - "lat": float(country["geometry"].centroid.y), - } - - # Calculate bounding box - bounds = country["geometry"].bounds - result["bounds"] = { - "min_lon": float(bounds[0]), - "min_lat": float(bounds[1]), - "max_lon": float(bounds[2]), - "max_lat": float(bounds[3]), - } - - return result - - return _find_country_by_iso_a3_impl(iso_a3) - else: - # Use in-memory cache - # Generate consistent cache key - cache_key = self._get_country_cache_key(iso_a3) - - # Check cache first - if cache_key in self._country_cache: - cached_value = self._country_cache[cache_key] - if cached_value is None: - return None - result = cached_value.copy() - return result - - # Find the country in the Natural Earth dataset - country_rows = self.countries_gdf[self.countries_gdf["ISO_A3"] == iso_a3] - - if len(country_rows) == 0: - logger.warning( - f"Country with ISO A3 code '{iso_a3}' not found in Natural Earth dataset" - ) - # Update cache with None to avoid repeated lookups - self._country_cache[cache_key] = None - return None - - # Get the first (and should be only) matching country - country = country_rows.iloc[0] - - # Prepare result with all available country data - result = { - "iso_a3": country["ISO_A3"], - "country_name": country["NAME_EN"], - } - - # Add all other available fields from Natural Earth - for col in country.index: - if col not in ["geometry", "ISO_A3", "NAME_EN"]: - # Convert numpy types to native Python types - value = country[col] - if pd.isna(value): - result[col] = None - elif isinstance(value, (np.integer, np.int64, np.int32)): - result[col] = int(value) - elif isinstance(value, (np.floating, np.float64, np.float32)): - result[col] = float(value) - elif isinstance(value, np.bool_): - result[col] = bool(value) - else: - result[col] = str(value) - - # Add geometry as WKT string - if hasattr(country["geometry"], "wkt"): - result["geometry_wkt"] = country["geometry"].wkt - - # Add some commonly useful derived fields - result["data_source"] = "Natural Earth" - result["centroid"] = { - "lon": float(country["geometry"].centroid.x), - "lat": float(country["geometry"].centroid.y), - } - - # Calculate bounding box - bounds = country["geometry"].bounds - result["bounds"] = { - "min_lon": float(bounds[0]), - "min_lat": float(bounds[1]), - "max_lon": float(bounds[2]), - "max_lat": float(bounds[3]), - } - - # Update cache - self._country_cache[cache_key] = result - return result - - def find_multiple_countries_by_iso_a3(self, iso_a3_list): - """ - Find information for multiple countries by their ISO A3 codes. - - Parameters: - iso_a3_list (list): List of ISO A3 codes (e.g., ['USA', 'FRA', 'TZA']) - - Returns: - dict: Dictionary with ISO A3 codes as keys and country info as values. - Countries not found will have None as values. - """ - if not isinstance(iso_a3_list, (list, tuple, set)): - raise ValueError( - "iso_a3_list must be a list, tuple, or set of ISO A3 codes" - ) - - results = {} - cache_hits = 0 - - for iso_a3 in iso_a3_list: - # Check cache first - cache_key = self._get_country_cache_key(iso_a3.upper()) - if cache_key in self._country_cache: - cached_value = self._country_cache[cache_key] - results[iso_a3.upper()] = ( - cached_value.copy() if cached_value is not None else None - ) - cache_hits += 1 - else: - # Not in cache, fetch the country info - country_info = self.find_country_by_iso_a3(iso_a3) - results[iso_a3.upper()] = country_info - - if cache_hits > 0: - logger.info( - f"Cache hits: {cache_hits}/{len(iso_a3_list)} ({cache_hits/len(iso_a3_list)*100:.1f}%)" - ) - - return results - - def get_country_summary(self, iso_a3): - """ - Get a summary of country information including related grid cells and admin regions. - - Parameters: - iso_a3 (str): ISO A3 code of the country - - Returns: - dict: Summary including country info, grid cell count, and admin region counts - """ - # Get basic country information - country_info = self.find_country_by_iso_a3(iso_a3) - if not country_info: - return None - - # Get all grid cells for this country - gids = self.find_gids_for_country(iso_a3) - - # Count admin1 regions if available - admin1_count = 0 - if self.admin1_gdf is not None: - admin1_count = len(self.admin1_gdf[self.admin1_gdf["iso3_code"] == iso_a3]) - - # Count admin2 regions if available - admin2_count = 0 - if self.admin2_gdf is not None: - admin2_count = len(self.admin2_gdf[self.admin2_gdf["iso3_code"] == iso_a3]) - - # Create summary - summary = { - "country_info": country_info, - "priogrid": { - "gid_count": len(gids), - "gid_list": gids[:100], # Return first 100 GIDs to avoid huge responses - "has_more": len(gids) > 100, - }, - "admin_regions": { - "admin1_count": admin1_count, - "admin2_count": admin2_count, - }, - "data_source": "Natural Earth", - } - - return summary - - def search_countries_by_name( - self, name_pattern, exact_match=False, case_sensitive=False - ): - """ - Search for countries by name pattern. - - Parameters: - name_pattern (str): Name or pattern to search for - exact_match (bool): If True, only exact matches are returned - case_sensitive (bool): If True, search is case sensitive - - Returns: - list: List of matching countries with their information - """ - if not isinstance(name_pattern, str): - raise ValueError("name_pattern must be a string") - - # Prepare the search pattern - if not case_sensitive: - name_pattern = name_pattern.lower() - search_column = self.countries_gdf["NAME_EN"].str.lower() - else: - search_column = self.countries_gdf["NAME_EN"] - - # Find matches - if exact_match: - matching_indices = search_column == name_pattern - else: - matching_indices = search_column.str.contains(name_pattern, na=False) - - matching_countries = self.countries_gdf[matching_indices] - - if len(matching_countries) == 0: - logger.info(f"No countries found matching pattern: {name_pattern}") - return [] - - # Convert matches to list of dictionaries - results = [] - for _, country in matching_countries.iterrows(): - country_info = { - "iso_a3": country["ISO_A3"], - "country_name": country["NAME_EN"], - } - - # Add a few key fields - key_fields = [ - "CONTINENT", - "REGION_UN", - "SUBREGION", - "POP_EST", - "GDP_MD", - "INCOME_GRP", - ] - for field in key_fields: - if field in country: - country_info[field.lower()] = country[field] - - results.append(country_info) - - logger.info(f"Found {len(results)} countries matching pattern: {name_pattern}") - return results - - def enrich_dataframe_with_pg_info( - self, - df, - pg_id_col="priogrid_id", - time_id_col="month_id", - include_country=True, - include_admin1=True, - include_admin2=True, - include_pg_info=True, - country_cols=None, - admin1_cols=None, - admin2_cols=None, - pg_cols=None, - batch_size=1000, - use_multiprocessing=True, - show_progress=True, - only_metadata=True, - ): - """ - Enrich a DataFrame with country, admin1, admin2, and PRIO-GRID information based on PRIO-GRID IDs. - - Parameters: - df (pd.DataFrame): Input DataFrame containing PRIO-GRID IDs - pg_id_col (str): Name of the column containing PRIO-GRID IDs - include_country (bool): Whether to include country information - include_admin1 (bool): Whether to include admin1 information - include_admin2 (bool): Whether to include admin2 information - include_pg_info (bool): Whether to include PRIO-GRID information - country_cols (list): Specific country columns to include (None for all) - admin1_cols (list): Specific admin1 columns to include (None for all) - admin2_cols (list): Specific admin2 columns to include (None for all) - pg_cols (list): Specific PRIO-GRID columns to include (None for all) - batch_size (int): Number of PRIO-GRID IDs to process in each batch - use_multiprocessing (bool): Whether to use multiprocessing for faster processing - show_progress (bool): Whether to show a progress bar - - Returns: - pd.DataFrame: Enriched DataFrame with additional columns - - Raises: - ValueError: If pg_id_col is not found in the DataFrame - """ - # Validate input - if pg_id_col not in df.columns: - raise ValueError(f"Column '{pg_id_col}' not found in DataFrame") - - if only_metadata: - df = pd.DataFrame(df[[pg_id_col, time_id_col]]) - # Create a copy to avoid modifying the original DataFrame - result_df = df.copy() - - # Get unique PRIO-GRID IDs to process - unique_pg_ids = df[pg_id_col].unique() - total_ids = len(unique_pg_ids) - total_batches = (total_ids - 1) // batch_size + 1 - - logger.info( - f"Starting enrichment of {total_ids} unique PRIO-GRID IDs in {total_batches} batches" - ) - logger.info( - f"Parameters: country={include_country}, admin1={include_admin1}, admin2={include_admin2}, pg_info={include_pg_info}" - ) - - # Ensure cache directories exist - if self.use_disk_cache: - for cache_type in ["country", "admin1", "admin2", "gid"]: - cache_path = self.cache_dir / cache_type - if not cache_path.exists(): - os.makedirs(cache_path, exist_ok=True) - logger.info(f"Created cache directory: {cache_path}") - - # Process in batches - all_pg_data = {} - processed_count = 0 - failed_batches = 0 - failed_gids = [] # observability-only; not in return value (C-21) - - # Initialize progress bar if requested - pbar = None - if show_progress: - try: - from tqdm import tqdm - - pbar = tqdm( - total=total_ids, desc="Processing PRIO-GRID IDs", unit="ids" - ) - except ImportError: - logger.warning( - "tqdm not installed. Progress bar will not be shown. Install with: pip install tqdm" - ) - show_progress = False - - if use_multiprocessing and total_ids > batch_size: - # Use threading instead of multiprocessing to avoid pickling issues - from concurrent.futures import ThreadPoolExecutor, as_completed - - # Split into batches - batches = [ - unique_pg_ids[i : i + batch_size] - for i in range(0, total_ids, batch_size) - ] - - # Process batches in parallel using threads - with ThreadPoolExecutor(max_workers=self._max_workers) as executor: - # Submit all batches for processing - future_to_batch = { - executor.submit( - self._process_pg_batch, - batch_ids, - include_country, - include_admin1, - include_admin2, - include_pg_info, - country_cols, - admin1_cols, - admin2_cols, - pg_cols, - ): batch_idx - for batch_idx, batch_ids in enumerate(batches) - } - - # Collect results as they complete - for future in as_completed(future_to_batch): - batch_idx = future_to_batch[future] - try: - batch_result = future.result() - all_pg_data.update(batch_result) - processed_count += len(batches[batch_idx]) - - # Update progress - if pbar: - pbar.update(len(batches[batch_idx])) - - # Log progress every 10% or every 5 batches - if ( - batch_idx % max(5, total_batches // 10) == 0 - or batch_idx == total_batches - 1 - ): - progress_pct = (processed_count / total_ids) * 100 - logger.info( - f"Progress: {processed_count}/{total_ids} ({progress_pct:.1f}%) - Batch {batch_idx + 1}/{total_batches}" - ) - - except Exception as e: - failed_batches += 1 - failed_gids.extend(list(batches[batch_idx])) - logger.error(f"Batch {batch_idx} failed ({len(batches[batch_idx])} GIDs affected): {str(e)}") - continue - else: - # Process sequentially for smaller datasets - for i in range(0, total_ids, batch_size): - batch_ids = unique_pg_ids[i : i + batch_size] - batch_num = i // batch_size + 1 - - logger.info( - f"Processing batch {batch_num}/{total_batches} ({len(batch_ids)} IDs)" - ) - - try: - batch_result = self._process_pg_batch( - batch_ids, - include_country, - include_admin1, - include_admin2, - include_pg_info, - country_cols, - admin1_cols, - admin2_cols, - pg_cols, - ) - all_pg_data.update(batch_result) - processed_count += len(batch_ids) - - # Update progress - if pbar: - pbar.update(len(batch_ids)) - - # Log progress - progress_pct = (processed_count / total_ids) * 100 - logger.info( - f"Completed batch {batch_num}/{total_batches} - {progress_pct:.1f}% complete" - ) - - except Exception as e: - failed_batches += 1 - failed_gids.extend(list(batch_ids)) - logger.error(f"Batch {batch_num} failed ({len(batch_ids)} GIDs affected): {str(e)}") - continue - - if failed_batches > 0: - logger.error(f"Enrichment had {failed_batches} failed batch(es) affecting {len(failed_gids)} GIDs. Output will have NaN metadata for these GIDs.") - - # Close progress bar - if pbar: - pbar.close() - - # Convert the collected data to a DataFrame - logger.info("Converting collected data to DataFrame") - pg_info_df = pd.DataFrame.from_dict(all_pg_data, orient="index") - - # Merge with the original DataFrame - if not pg_info_df.empty: - logger.info("Merging enriched data with original DataFrame") - result_df = result_df.merge( - pg_info_df, left_on=pg_id_col, right_on="pg_id", how="left" - ) - - # Drop the redundant pg_id column - if "pg_id" in result_df.columns: - result_df.drop(columns=["pg_id"], inplace=True) - else: - logger.warning("No enrichment data was generated") - - logger.info( - f"Enrichment complete. Added {len(result_df.columns) - len(df.columns)} new columns" - ) - return result_df - - def _process_pg_batch( - self, - pg_ids, - include_country, - include_admin1, - include_admin2, - include_pg_info, - country_cols, - admin1_cols, - admin2_cols, - pg_cols, - ): - """Process a batch of PRIO-GRID IDs and return their information""" - batch_data = {} - - for pg_id in pg_ids: - pg_data = {"pg_id": pg_id} - - # Get PRIO-GRID information - if include_pg_info: - grid_cell = self.priogrid_gdf[self.priogrid_gdf["gid"] == pg_id] - if len(grid_cell) > 0: - grid_info = grid_cell.iloc[0].to_dict() - - # Select specific columns if requested - if pg_cols is not None: - for col in pg_cols: - if col in grid_info: - pg_data[f"pg_{col}"] = grid_info[col] - else: - # Include all grid info with pg_ prefix - for key, value in grid_info.items(): - if key != "gid": # Skip gid as it's already the key - pg_data[f"pg_{key}"] = value - - # Get country information - if include_country: - country_info = self.find_country_for_gid(pg_id) - if country_info: - # Select specific columns if requested - if country_cols is not None: - for col in country_cols: - if col in country_info: - pg_data[f"country_{col}"] = country_info[col] - else: - # Include all country info with country_ prefix - for key, value in country_info.items(): - if key != "gid": # Skip gid as it's already the key - pg_data[f"country_{key}"] = value - - # Get admin1 information - if include_admin1: - admin1_info = self.find_admin1_for_gid(pg_id) - if admin1_info: - # Select specific columns if requested - if admin1_cols is not None: - for col in admin1_cols: - if col in admin1_info: - pg_data[f"admin1_{col}"] = admin1_info[col] - else: - # Include all admin1 info with admin1_ prefix - for key, value in admin1_info.items(): - if key != "gid": # Skip gid as it's already the key - pg_data[f"admin1_{key}"] = value - - # Get admin2 information - if include_admin2: - admin2_info = self.find_admin2_for_gid(pg_id) - if admin2_info: - # Select specific columns if requested - if admin2_cols is not None: - for col in admin2_cols: - if col in admin2_info: - pg_data[f"admin2_{col}"] = admin2_info[col] - else: - # Include all admin2 info with admin2_ prefix - for key, value in admin2_info.items(): - if key != "gid": # Skip gid as it's already the key - pg_data[f"admin2_{key}"] = value - - batch_data[pg_id] = pg_data - - return batch_data - - def enrich_dataframe_with_country_info( - self, - df, - iso_a3_col="iso_a3", - include_admin1=True, - include_admin2=True, - country_cols=None, - admin1_cols=None, - admin2_cols=None, - batch_size=1000, - use_multiprocessing=True, - show_progress=True, - ): - """ - Enrich a DataFrame with country, admin1, and admin2 information based on ISO A3 codes. - - Parameters: - df (pd.DataFrame): Input DataFrame containing ISO A3 codes - iso_a3_col (str): Name of the column containing ISO A3 codes - include_admin1 (bool): Whether to include admin1 information - include_admin2 (bool): Whether to include admin2 information - country_cols (list): Specific country columns to include (None for all) - admin1_cols (list): Specific admin1 columns to include (None for all) - admin2_cols (list): Specific admin2 columns to include (None for all) - batch_size (int): Number of ISO A3 codes to process in each batch - use_multiprocessing (bool): Whether to use multiprocessing for faster processing - show_progress (bool): Whether to show a progress bar - - Returns: - pd.DataFrame: Enriched DataFrame with additional columns - - Raises: - ValueError: If iso_a3_col is not found in the DataFrame - """ - # Validate input - if iso_a3_col not in df.columns: - raise ValueError(f"Column '{iso_a3_col}' not found in DataFrame") - - # Create a copy to avoid modifying the original DataFrame - result_df = df.copy() - - # Get unique ISO A3 codes to process - unique_iso_a3 = df[iso_a3_col].unique() - total_codes = len(unique_iso_a3) - total_batches = (total_codes - 1) // batch_size + 1 - - logger.info( - f"Starting enrichment of {total_codes} unique ISO A3 codes in {total_batches} batches" - ) - logger.info(f"Parameters: admin1={include_admin1}, admin2={include_admin2}") - - # Prepare parameters for batch processing - process_params = { - "include_admin1": include_admin1, - "include_admin2": include_admin2, - "country_cols": country_cols, - "admin1_cols": admin1_cols, - "admin2_cols": admin2_cols, - } - - # Process in batches - all_country_data = {} - processed_count = 0 - failed_batches = 0 - failed_codes = [] # observability-only; not in return value (C-21) - - # Initialize progress bar if requested - pbar = None - if show_progress: - try: - from tqdm import tqdm - - pbar = tqdm( - total=total_codes, desc="Processing ISO A3 codes", unit="codes" - ) - except ImportError: - logger.warning( - "tqdm not installed. Progress bar will not be shown. Install with: pip install tqdm" - ) - show_progress = False - - if use_multiprocessing and total_codes > batch_size: - # Use threading instead of multiprocessing to avoid pickling issues - from concurrent.futures import ThreadPoolExecutor, as_completed - - # Split into batches - batches = [ - unique_iso_a3[i : i + batch_size] - for i in range(0, total_codes, batch_size) - ] - - # Process batches in parallel using threads - with ThreadPoolExecutor(max_workers=self._max_workers) as executor: - # Submit all batches for processing - future_to_batch = { - executor.submit( - self._process_country_batch, batch_codes, **process_params - ): batch_idx - for batch_idx, batch_codes in enumerate(batches) - } - - # Collect results as they complete - for future in as_completed(future_to_batch): - batch_idx = future_to_batch[future] - try: - batch_result = future.result() - all_country_data.update(batch_result) - processed_count += len(batches[batch_idx]) - - # Update progress - if pbar: - pbar.update(len(batches[batch_idx])) - - # Log progress every 10% or every 5 batches - if ( - batch_idx % max(5, total_batches // 10) == 0 - or batch_idx == total_batches - 1 - ): - progress_pct = (processed_count / total_codes) * 100 - logger.info( - f"Progress: {processed_count}/{total_codes} ({progress_pct:.1f}%) - Batch {batch_idx + 1}/{total_batches}" - ) - - except Exception as e: - failed_batches += 1 - failed_codes.extend(list(batches[batch_idx])) - logger.error(f"Batch {batch_idx} failed ({len(batches[batch_idx])} codes affected): {str(e)}") - continue - else: - # Process sequentially for smaller datasets - for i in range(0, total_codes, batch_size): - batch_codes = unique_iso_a3[i : i + batch_size] - batch_num = i // batch_size + 1 - - logger.info( - f"Processing batch {batch_num}/{total_batches} ({len(batch_codes)} codes)" - ) - - try: - batch_result = self._process_country_batch( - batch_codes, **process_params - ) - all_country_data.update(batch_result) - processed_count += len(batch_codes) - - # Update progress - if pbar: - pbar.update(len(batch_codes)) - - # Log progress - progress_pct = (processed_count / total_codes) * 100 - logger.info( - f"Completed batch {batch_num}/{total_batches} - {progress_pct:.1f}% complete" - ) - - except Exception as e: - failed_batches += 1 - failed_codes.extend(list(batch_codes)) - logger.error(f"Batch {batch_num} failed ({len(batch_codes)} codes affected): {str(e)}") - continue - - if failed_batches > 0: - logger.error(f"Enrichment had {failed_batches} failed batch(es) affecting {len(failed_codes)} ISO A3 codes. Output will have NaN metadata for these codes.") - - # Close progress bar - if pbar: - pbar.close() - - # Convert the collected data to a DataFrame - logger.info("Converting collected data to DataFrame") - country_info_df = pd.DataFrame.from_dict(all_country_data, orient="index") - - # Merge with the original DataFrame - if not country_info_df.empty: - logger.info("Merging enriched data with original DataFrame") - result_df = result_df.merge( - country_info_df, left_on=iso_a3_col, right_on="iso_a3_code", how="left" - ) - - # Drop the redundant iso_a3_code column - if "iso_a3_code" in result_df.columns: - result_df.drop(columns=["iso_a3_code"], inplace=True) - else: - logger.warning("No enrichment data was generated") - - logger.info( - f"Enrichment complete. Added {len(result_df.columns) - len(df.columns)} new columns" - ) - return result_df - - def _process_country_batch( - self, - iso_a3_codes, - include_admin1, - include_admin2, - country_cols, - admin1_cols, - admin2_cols, - ): - """Process a batch of ISO A3 codes and return their information""" - batch_data = {} - - for iso_a3 in iso_a3_codes: - # Skip empty or invalid codes - if pd.isna(iso_a3) or not isinstance(iso_a3, str) or len(iso_a3) != 3: - batch_data[iso_a3] = {"iso_a3_code": iso_a3} - continue - - country_data = {"iso_a3_code": iso_a3} - - # Get country information - country_info = self.find_country_by_iso_a3(iso_a3) - if country_info: - # Select specific columns if requested - if country_cols is not None: - for col in country_cols: - if col in country_info: - country_data[f"country_{col}"] = country_info[col] - else: - # Include all country info with country_ prefix - for key, value in country_info.items(): - if key != "iso_a3": # Skip iso_a3 as it's already the key - country_data[f"country_{key}"] = value - - # Get admin1 information - if include_admin1 and self.admin1_gdf is not None: - admin1_regions = self.admin1_gdf[self.admin1_gdf["iso3_code"] == iso_a3] - - if not admin1_regions.empty: - # Create a list of admin1 regions - admin1_list = [] - for _, admin1 in admin1_regions.iterrows(): - admin1_dict = {} - - # Select specific columns if requested - if admin1_cols is not None: - for col in admin1_cols: - if col in admin1: - admin1_dict[col] = admin1[col] - else: - # Include all admin1 info - for key, value in admin1.items(): - if ( - key != "iso3_code" - ): # Skip iso3_code as it's already known - admin1_dict[key] = value - - admin1_list.append(admin1_dict) - - country_data["admin1_regions"] = admin1_list - country_data["admin1_count"] = len(admin1_list) - - # Get admin2 information - if include_admin2 and self.admin2_gdf is not None: - admin2_regions = self.admin2_gdf[self.admin2_gdf["iso3_code"] == iso_a3] - - if not admin2_regions.empty: - # Create a list of admin2 regions - admin2_list = [] - for _, admin2 in admin2_regions.iterrows(): - admin2_dict = {} - - # Select specific columns if requested - if admin2_cols is not None: - for col in admin2_cols: - if col in admin2: - admin2_dict[col] = admin2[col] - else: - # Include all admin2 info - for key, value in admin2.items(): - if ( - key != "iso3_code" - ): # Skip iso3_code as it's already known - admin2_dict[key] = value - - admin2_list.append(admin2_dict) - - country_data["admin2_regions"] = admin2_list - country_data["admin2_count"] = len(admin2_list) - - batch_data[iso_a3] = country_data - - return batch_data - - def add_country_info_to_dataframe( - self, df, iso_a3_col="iso_a3", name_col="country_name", additional_cols=None - ): - """ - Add basic country information to a DataFrame based on ISO A3 codes. - - Parameters: - df (pd.DataFrame): Input DataFrame containing ISO A3 codes - iso_a3_col (str): Name of the column containing ISO A3 codes - name_col (str): Name of the column to create for country names - additional_cols (list): Additional country columns to include - - Returns: - pd.DataFrame: DataFrame with added country information - """ - # Create a copy to avoid modifying the original DataFrame - result_df = df.copy() - - # Get unique ISO A3 codes to process - unique_iso_a3 = df[iso_a3_col].unique() - logger.info( - f"Processing {len(unique_iso_a3)} unique ISO A3 codes for country names" - ) - - # Create a mapping of ISO A3 to country name - iso_to_name = {} - iso_to_additional = {col: {} for col in additional_cols or []} - - for iso_a3 in unique_iso_a3: - # Skip empty or invalid codes - if pd.isna(iso_a3) or not isinstance(iso_a3, str) or len(iso_a3) != 3: - iso_to_name[iso_a3] = None - for col in iso_to_additional: - iso_to_additional[col][iso_a3] = None - continue - - country_info = self.find_country_by_iso_a3(iso_a3) - if country_info: - iso_to_name[iso_a3] = country_info.get("country_name") - - # Add additional columns if requested - for col in iso_to_additional: - iso_to_additional[col][iso_a3] = country_info.get(col) - else: - iso_to_name[iso_a3] = None - for col in iso_to_additional: - iso_to_additional[col][iso_a3] = None - - # Add the country names to the DataFrame - result_df[name_col] = result_df[iso_a3_col].map(iso_to_name) - - # Add additional columns if requested - for col in iso_to_additional: - result_df[f"country_{col}"] = result_df[iso_a3_col].map( - iso_to_additional[col] - ) - - logger.info("Added country information to DataFrame") - return result_df - - -# Global default mapper -_DEFAULT_MAPPER = None - - -def set_default_mapper(): - """ - Set the default PriogridCountryMapper instance to be used by SpatialEntity classes. - - This function creates a global default mapper that will be automatically used - by Point, Priogrid, and Country classes when no explicit mapper is provided. - - Returns: - PriogridCountryMapper: The created default mapper instance - """ - global _DEFAULT_MAPPER - _DEFAULT_MAPPER = PriogridCountryMapper() - return _DEFAULT_MAPPER - - -def get_default_mapper(): - """ - Get the default PriogridCountryMapper instance. - - Returns: - PriogridCountryMapper: The default mapper instance - - Raises: - ValueError: If no default mapper has been set - """ - if _DEFAULT_MAPPER is None: - raise ValueError("No default mapper set. Call set_default_mapper() first.") - return _DEFAULT_MAPPER - - -# Set default mapper -set_default_mapper() From 4711d51c01a8aa4d085c6c5fc43e5477b324fcfd Mon Sep 17 00:00:00 2001 From: Polichinl Date: Wed, 24 Jun 2026 04:39:56 +0200 Subject: [PATCH 2/5] test: re-xfail the datafactory version-bump gate (datafactory tagged v1.4.0) datafactory cut + tagged v1.4.0, so pyproject version == latest tag and this over-strict cross-repo deploy probe flips red. Re-mark it xfail(strict): the gate flaps with datafactory's release cadence and re-promotes (fails) when datafactory bumps past the tag. Unrelated to the mapper deletion. Co-Authored-By: Claude Opus 4.8 --- tests/test_datafactory_deploy_readiness.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/test_datafactory_deploy_readiness.py b/tests/test_datafactory_deploy_readiness.py index 5d95147..b1792b4 100644 --- a/tests/test_datafactory_deploy_readiness.py +++ b/tests/test_datafactory_deploy_readiness.py @@ -36,9 +36,17 @@ class TestReleaseGate: views-models#127's REGION='land_gaul' flip would hit a package without it. """ - # views-datafactory#224 RESOLVED 2026-06-24 (datafactory 6f7f4ec bumped to - # v1.4.0, untagged) — xfail(strict) flipped this to a pass, so it's promoted - # to a live guard: it now fails if a future version collides with a tag again. + # #224 resolved (v1.4.0), then datafactory TAGGED v1.4.0 (2026-06-24) — so + # pyproject version == latest tag, which this over-strict check reads as a + # "stale version". Re-xfail(strict): the gate flaps with datafactory's release + # cadence; it flips back to a failure (demanding promotion) only when + # datafactory bumps past the tag to the next -dev. + @pytest.mark.xfail( + reason="cross-repo deploy gate flaps with datafactory's release cadence: " + "v1.4.0 is now tagged and == pyproject version; xfail(strict) " + "re-promotes when datafactory bumps past the tag.", + strict=True, + ) def test_version_bumped_past_latest_tag(self): version_line = (_DF / "pyproject.toml").read_text() current = next( From e92eafbdc60e5f547b860921cd398173c2f84b8d Mon Sep 17 00:00:00 2001 From: Polichinl Date: Wed, 24 Jun 2026 05:00:54 +0200 Subject: [PATCH 3/5] docs(register): relocate the dead old-mapper cluster to Resolved (C-39 follow-up) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register-curation pass after the mapper deletion (C-39). Move the 11 now-dead mapper concerns (C-02, C-04, C-05, C-06, C-11, C-12, C-14, C-16, C-17, C-20, C-21) and 2 disagreements (D-01, D-02) from Open to Resolved — the code they describe no longer exists. C-19 narrowed (the mapper raises are gone; 3 unfao.py raises remain, issue #13); C-08 kept open (its datafactory area-math dimension stays under C-31). Counts: open 36 -> 25, resolved 4 -> 15. Bookkeeping only. Co-Authored-By: Claude Opus 4.8 --- reports/technical_risk_register.md | 346 +++++++++++------------------ 1 file changed, 133 insertions(+), 213 deletions(-) diff --git a/reports/technical_risk_register.md b/reports/technical_risk_register.md index f703a58..c28c463 100644 --- a/reports/technical_risk_register.md +++ b/reports/technical_risk_register.md @@ -6,8 +6,8 @@ | Owner | Dylan Pinheiro / PRIO MD&D Team | | Last Updated | 2026-06-24 | | Total Concerns | 40 | -| Open Concerns | 36 | -| Resolved Concerns | 4 | +| Open Concerns | 25 | +| Resolved Concerns | 15 | --- @@ -73,20 +73,6 @@ ## Open Concerns -### C-02: Module-level side effect blocks package import on shapefile failure - -| Field | Value | -|-------|-------| -| ID | C-02 | -| Tier | 2 | -| Source | `repo-assimilation` (2026-06-02) | -| Trigger | When updating, relocating, or removing any shapefile in `views_postprocessing/shapefiles/`, verify that import of the package still succeeds | -| Location | `views_postprocessing/unfao/mapping/mapping.py:3122` | - -Line 3122 calls `set_default_mapper()` at module scope, which instantiates `PriogridCountryMapper` and loads 4 large shapefiles (Natural Earth 10m, PRIO-GRID, GAUL L1, GAUL L2). If any shapefile is missing or malformed, the import of `mapping.py` raises an exception. Because `unfao.py` imports `get_default_mapper` from this module, and the `managers/__init__.py` re-exports `UNFAOPostProcessorManager`, any consumer importing from this package will fail — even code paths that do not need the mapper. This makes the package entirely unusable if a single shapefile is corrupted. - ---- - ### C-03: Test coverage gaps across mapper-manager boundary and manager code | Field | Value | @@ -103,54 +89,6 @@ Tier recalibrated from 2 to 3 during review-rr (2026-06-02): 73 tests now exist --- -### C-04: Inconsistent forward/reverse mapping breaks expected bijection - -| Field | Value | -|-------|-------| -| ID | C-04 | -| Tier | 2 | -| Source | `repo-assimilation` (2026-06-02) | -| Trigger | When using `find_gids_for_country()` to enumerate cells for aggregation, verify that the result set is consistent with what `find_country_for_gid()` would assign to that country | -| Location | `views_postprocessing/unfao/mapping/mapping.py:920-922,662-668` | - -`find_country_for_gid()` assigns a GID to the country with the largest overlap regardless of magnitude (a 30% overlap wins if it's the largest). `_find_dominant_country_gids()` (used by `find_gids_for_country()`) requires `overlap_ratio > 0.5` to include a GID (line 922). A border cell with 40% overlap in country A and 35% in country B will be assigned to A by the forward lookup but excluded from `find_gids_for_country("A")`. Any downstream aggregation using the reverse lookup will silently miss cells that the enrichment step assigned to that country. - ---- - -### C-05: Multiple methods crash when disk caching is active - -| Field | Value | -|-------|-------| -| ID | C-05 | -| Tier | 2 | -| Source | `repo-assimilation` (2026-06-02), `graphify` (2026-06-02) | -| Trigger | When calling `batch_country_mapping()`, `batch_admin_mapping()`, `find_multiple_countries_by_iso_a3()`, `get_cache_stats()`, or `clear_cache()` on a mapper initialized with `use_disk_cache=True`, the call will raise `AttributeError` | -| Location | `views_postprocessing/unfao/mapping/mapping.py:786-787,1665-1668,2331-2332,286-294,332-344` | - -Multiple methods directly access `self._country_cache`, `self._admin1_cache`, and `self._admin2_cache`, which are in-memory LRU/TTL cache attributes only created when `use_disk_cache=False`. When `use_disk_cache=True`, the `__init__` method only creates `self._disk_*_cache` variants and never initializes the in-memory attributes. Affected methods: `batch_country_mapping()` (line 787), `batch_admin_mapping()` (lines 1665-1668), `find_multiple_countries_by_iso_a3()` (line 2331), `clear_cache()` (lines 286-294), and `get_cache_stats()` (lines 332-344). The default mapper is initialized with `use_disk_cache=True` (line 3102), making this the default failure mode for all these methods. - -Graphify graph traversal (2026-06-02) identified the broader scope: the same `self._*_cache` pattern appears in 6 methods beyond the originally identified `batch_country_mapping`. - -See also C-03 (no tests to catch this), C-06 (duplication is the root cause — each cache branch has its own API surface). - ---- - -### C-06: Massive code duplication across disk/memory cache branches - -| Field | Value | -|-------|-------| -| ID | C-06 | -| Tier | 3 | -| Source | `repo-assimilation` (2026-06-02) | -| Trigger | When fixing a bug in the spatial overlap logic within any `find_*` method, verify that the fix is applied to BOTH the disk-cache and memory-cache branches of that method | -| Location | `views_postprocessing/unfao/mapping/mapping.py:609-775,807-900,1130-1362,1364-1612` | - -Every method supporting both cache modes (`find_country_for_gid`, `find_admin1_for_gid`, `find_admin2_for_gid`, `find_gids_for_country`) contains the full spatial lookup implementation duplicated in both `if self.use_disk_cache` branches. The `find_country_for_gid` method alone has ~165 lines of identical logic in each branch. Total duplication accounts for approximately 1500 of the file's 3122 lines (~48%). A bug fixed in one branch may be missed in the other, creating divergent behavior depending on cache mode. - -See also C-05 (an example of this risk materializing). - ---- - ### C-07: Undeclared direct runtime dependencies in pyproject.toml | Field | Value | @@ -211,48 +149,6 @@ See also C-02 (the same module-level side effect that creates the global mapper) --- -### C-11: PriogridCountryMapper is a god class bridging 3 architectural concerns - -| Field | Value | -|-------|-------| -| ID | C-11 | -| Tier | 3 | -| Source | `graphify` (2026-06-02) | -| Trigger | When adding a new spatial lookup method or output format, verify whether it belongs in the mapper or should be extracted into a separate class with a focused responsibility | -| Location | `views_postprocessing/unfao/mapping/mapping.py:133-3085` | - -Graphify community detection identified `PriogridCountryMapper` as the highest-betweenness node in the repository graph (0.111), with 50 edges bridging 3 distinct functional communities: Spatial Mapping Engine (core overlap logic), Country Lookup Methods (`find_country_by_iso_a3`, `search_countries_by_name`, `get_country_summary`), and Utilities & Visualization (`visualize_grid_and_country`, `calculate_capital_distance`, `cached_haversine`). The class cohesion score is 0.04, indicating its internal methods are weakly interconnected. This single 3000-line class mixes spatial intersection logic, country metadata retrieval, DataFrame enrichment, batch processing, cache management, multiprocessing orchestration, and matplotlib visualization — violating the single-responsibility principle. The god class pattern increases the cost of change: modifying visualization code risks breaking spatial assignment, and vice versa. - -Deep graph traversal (2026-06-02) quantified the structural fragility: 33.2% of all nodes are articulation points (removing any one disconnects subgraphs) and 37.7% of edges are bridges. Both metrics exceed the 30% threshold for tree-like fragility, confirming that the codebase has a star topology centered on this single class rather than a resilient mesh. - -See also C-06 (code duplication within the same class amplifies the SRP violation). - ---- - -### C-12: Silent wrong-country assignment when geometry intersection fails - -| Field | Value | -|-------|-------| -| ID | C-12 | -| Tier | 2 | -| Source | `expert-review` (2026-06-02) | -| Trigger | When Natural Earth or GAUL shapefiles contain an invalid polygon for a country that is the correct assignment for a PRIO-GRID cell, verify that the geometry error is surfaced — currently the cell is silently assigned to the next-best country | -| Location | `views_postprocessing/unfao/mapping/mapping.py:658-660` (also duplicated at ~line 1192, 1428 in admin1/admin2 branches) | - -In `find_country_for_gid()`, the overlap calculation loop (line 644-660) wraps each `country["geometry"].intersection(grid_geometry)` call in a bare `except Exception` that logs at DEBUG and skips the country with `continue`. The identical pattern exists in 7 locations across both cache branches: lines 659, 744 (country), 925 (reverse lookup), 1202, 1320 (admin1), 1436, 1562 (admin2). All 7 use `logger.debug` — invisible in production where DEBUG is disabled. The exception type is `Exception` (widest possible), catching not just `GEOSException` but also `TypeError`, `KeyError`, and any code bug. - -If the correct country's polygon has an invalid geometry, its intersection fails, it's skipped, and the cell is assigned to the next-best country. The output contains no error signal — `method` still reads `"largest overlap"`. The wrong result is cached persistently by `joblib.Memory`. Critically, this is a *correlated* failure: if country X's polygon is invalid, ALL cells overlapping country X are misassigned — not just one. The UN FAO would receive data showing country X has zero conflict predictions while neighbors show inflated numbers. The failure is both systematic and undetectable from the output. - -The mapper's own `_validate_naturalearth_data` (line 473-477) detects invalid geometries at load time and logs a WARNING, but does not call `make_valid()` or reject the data — a validate-then-proceed-anyway pattern. D-04 resolved (2026-06-02) to apply `make_valid()` at load time in all three `_load_*` methods. `make_valid()` has been applied to all three methods. - -Critically, the manager's `_validate()` method — the only safety gate before upload — is structurally incapable of catching Cluster B's primary failure mode. `_validate()` checks column presence and null counts. A misassigned cell has a valid ISO code (just the wrong one), non-null values, and all required columns present. Validation passes. The safety net has a hole shaped exactly like the failure mode it should catch: wrong-but-valid geographic assignments are invisible to every automated check in the pipeline. - -Tier recalibrated from 1 to 2 during post-campaign review-rr (2026-06-02): `make_valid()` now applied at load time mitigates the root cause. With valid geometries, intersection failures are limited to precision edge cases. The 7 DEBUG handlers remain but are defense-in-depth, not the primary failure path. Campaign Claim 2.1-2.4 confirmed correct algorithm behavior. - -See also C-08 (a different mechanism for wrong-country at high latitudes), D-05 (if mapper eliminated, this concern is moot). Contingent on ADR-011. - ---- - ### C-13: No timeout on Appwrite operations — pipeline can hang indefinitely | Field | Value | @@ -267,28 +163,6 @@ See also C-08 (a different mechanism for wrong-country at high latitudes), D-05 --- -### C-14: Stale disk cache returns outdated mappings after shapefile update - -| Field | Value | -|-------|-------| -| ID | C-14 | -| Tier | 2 | -| Source | `expert-review` (2026-06-02) | -| Trigger | When updating GAUL or Natural Earth shapefiles to a new version, verify that the disk cache at `~/.priogrid_mapper_cache/` is manually cleared — there is no automatic invalidation | -| Location | `views_postprocessing/unfao/mapping/mapping.py:55-69,183-192` | - -The `joblib.Memory` disk cache at `~/.priogrid_mapper_cache/` stores spatial mapping results keyed by function arguments (GID values), not by shapefile content or version. When shapefiles are updated (e.g., GAUL boundary revision, Natural Earth ISO code correction), the cache continues returning results computed against the old shapefiles. No staleness signal is emitted. The `clear_cache()` method exists but must be called manually — nothing in the initialization or shapefile loading path checks whether the cached results match the current shapefiles. A shapefile hash or modification timestamp in the cache key would provide automatic invalidation. - -Wrong assignments persist in three layers beyond the cache: (1) joblib disk cache, (2) local timestamped parquet files in `data_generated/`, (3) Appwrite UN FAO bucket. Correcting a discovered error requires clearing the cache, re-running the pipeline, re-uploading to Appwrite, and notifying the partner — but no documented procedure exists for identifying which uploaded files contain affected GIDs or triggering a partner-side data retraction. - -Additionally, stale cache violates the PriogridCountryMapper CIC's determinism guarantee ("the same GID always maps to the same country given the same input shapefiles"). Two environments with different cache states produce different output. - -FAO Release Note 01, Topic C confirms: "FAO will release updated GAUL boundaries annually. Only boundaries that change will be updated." This establishes an annual regeneration cadence for the precomputed lookup table (ADR-011). Partial boundary updates mean only affected cells need re-mapping, but the current cache has no mechanism to identify which cells are affected by a partial shapefile update. - -See also C-05 (cache API bugs), C-22 (no post-delivery correction process), ADR-011 (precomputed lookup replaces runtime cache). - ---- - ### C-15: Upload metadata lacks enrichment provenance and carries test description | Field | Value | @@ -307,36 +181,6 @@ See also C-14 (stale cache without version tracking), C-22 (no post-delivery cor --- -### C-16: Thread-unsafe caches under concurrent enrichment - -| Field | Value | -|-------|-------| -| ID | C-16 | -| Tier | 1 | -| Source | `expert-review` (2026-06-02) | -| Trigger | When `enrich_dataframe_with_pg_info` processes >1000 unique GIDs (triggering the `ThreadPoolExecutor` path at line 2546), concurrent threads read/write the shared `self._country_cache` LRUCache — verify thread safety of cache access | -| Location | `views_postprocessing/unfao/mapping/mapping.py:2557,697-774,1250-1362,1490-1612` | - -`enrich_dataframe_with_pg_info` dispatches concurrent threads via `ThreadPoolExecutor` (line 2557) when `use_multiprocessing=True` (the default) and `total_ids > batch_size`. Each thread calls `find_country_for_gid`, `find_admin1_for_gid`, and `find_admin2_for_gid`, all of which read and write the shared `self._country_cache`, `self._admin1_cache`, and `self._admin2_cache` instances. The `cachetools` documentation explicitly states: "all these classes are not thread-safe. Access to a shared cache from multiple threads must be properly synchronized." Concurrent cache mutations can corrupt the internal `OrderedDict`, causing `RuntimeError: dictionary changed size during iteration`, `KeyError` on entries that should exist, or silently returning wrong cached values. The race window is a classic TOCTOU: thread A checks `cache_key in cache` (True), thread B evicts that key (LRU full), thread A reads `cache[cache_key]` → `KeyError` or wrong value. - -See also C-05 (cache attribute bugs), D-02 (disagreement on fix approach). - ---- - -### C-17: Implicit column naming contract between mapper and manager - -| Field | Value | -|-------|-------| -| ID | C-17 | -| Tier | 3 | -| Source | `expert-review` (2026-06-02) | -| Trigger | When Natural Earth updates their shapefile and renames or adds columns (e.g., `ISO_A3` → `ISO_A3_EH`), verify that the mapper's 3-step column derivation still produces the names the manager expects in `filter_cols` | -| Location | `views_postprocessing/unfao/mapping/mapping.py:675,2709-2714`, `views_postprocessing/unfao/managers/unfao.py:146-158` | - -The manager expects columns like `country_iso_a3` (unfao.py:151). The mapper produces this via a 3-step chain: Natural Earth column `ISO_A3` → `find_country_for_gid` dict key `iso_a3` (mapping.py:675) → `_process_pg_batch` prepends `country_` prefix (mapping.py:2709) → `country_iso_a3`. No explicit contract declares this mapping. Furthermore, when `country_cols=None` (the default), `_process_pg_batch` (lines 2710-2714) dumps ALL shapefile columns with the `country_` prefix, making the output schema dependent on external shapefile content. A Natural Earth column rename would silently break the chain, causing `_validate()` to reject the enriched data with a confusing "missing required metadata column" error that doesn't mention the shapefile as the root cause. - ---- - ### C-19: Systematic ADR-008 non-compliance — 23 of 24 raises lack preceding log | Field | Value | @@ -351,39 +195,7 @@ ADR-008 requires structural failures to be both logged persistently AND raised e Part of Cluster B (expanded scope). ---- - -### C-20: ZeroDivisionError on degenerate zero-area cells - -| Field | Value | -|-------|-------| -| ID | C-20 | -| Tier | 3 | -| Source | `falsification-audit` (2026-06-02) | -| Trigger | When a new shapefile version introduces a near-zero-but-not-exactly-zero area cell geometry, verify the `== 0.0` guard catches it — very small positive areas bypass the guard | -| Location | `views_postprocessing/unfao/mapping/mapping.py` (7 guard sites) | - -Zero-area guards (`if grid_geometry.area == 0.0: logger.warning; return None`) were added at all 7 overlap calculation sites including `_find_dominant_country_gids`. A degenerate cell now returns `None` with a WARNING instead of silently vanishing. Remaining theoretical risk: near-zero-but-not-exactly-zero areas (e.g., 5e-31 square degrees) bypass the `== 0.0` check. This is physically impossible for real PRIO-GRID cells (~0.25 sq degrees) but could be strengthened with an epsilon-based guard. - -Tier recalibrated from 1 to 3 during review-rr (2026-06-02): all 7 sites now have guards. The concern is mitigated from "silent vanishing" to "theoretical precision edge case." - -See also C-12 (geometry intersection failures in the same catch block). - ---- - -### C-21: Batch enrichment failures produce incomplete DataFrames (observability-only fix) - -| Field | Value | -|-------|-------| -| ID | C-21 | -| Tier | 2 | -| Source | `falsification-audit` (2026-06-02) | -| Trigger | When `enrich_dataframe_with_pg_info` or `enrich_dataframe_with_country_info` encounters a batch-level processing failure, verify whether the failure is surfaced to the caller — currently failed batches are logged but not raised | -| Location | `views_postprocessing/unfao/mapping/mapping.py:2597-2600,2636-2638,2871-2874,2902-2904` | - -Four `except Exception: logger.error(...); continue` handlers drop failed batches during DataFrame enrichment in both `enrich_dataframe_with_pg_info` and `enrich_dataframe_with_country_info`. `failed_batches` counters and summary ERROR logs were added to both methods. However, the fix is observability-only: `failed_batches` and `failed_gids` are tracked and logged but NOT included in the return value or raised as an exception. The caller receives the partial DataFrame with no programmatic signal. This does not satisfy ADR-003's fail-loud requirement. - -Part of Cluster B (expanded scope). See also C-06 (duplication root cause). +**Update 2026-06-24:** the mapper portion (20 of the 23 raises, in `mapping.py`) is gone with the deleted runtime mapper (C-39); the **3 raises in `unfao.py`** remain (tracked by issue #13). Narrowed to the manager. --- @@ -671,28 +483,6 @@ See also C-07/C-27/C-29 (pipeline-core coupling symptoms), C-39 (the dead-mapper ## Disagreements -### D-01: Cache strategy refactoring — extract now vs. characterize first - -| Field | Value | -|-------|-------| -| ID | D-01 | -| Source | `expert-review` (2026-06-02) | -| Perspectives | Martin/GoF (extract Strategy pattern now), Feathers/Beck (characterize disk-cache branch with tests first) | -| Resolution | **⚠ CONTINGENT ON ADR-011.** If precomputed lookup table replaces mapping.py, no cache to refactor — this disagreement is moot. Only resolve if mapper is kept. | - ---- - -### D-02: Thread safety fix — lock caches vs. remove threading - -| Field | Value | -|-------|-------| -| ID | D-02 | -| Source | `expert-review` (2026-06-02) | -| Perspectives | Nygard (threading.Lock), Beck (remove ThreadPoolExecutor), Hickey (sequential is simpler), Ousterhout (keep threading for speed) | -| Resolution | **⚠ CONTINGENT ON ADR-011.** If precomputed lookup table replaces mapping.py, no threading — this disagreement is moot. Only resolve if mapper is kept. | - ---- - ### D-05: Strategic direction — eliminate runtime mapper vs keep area-based algorithm | Field | Value | @@ -757,6 +547,116 @@ See also C-07/C-27/C-29 (pipeline-core coupling symptoms), C-39 (the dead-mapper ## Resolved Concerns +### C-04: Inconsistent forward/reverse mapping breaks expected bijection — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-04 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-02: Module-level side effect blocks package import on shapefile failure — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-02 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-05: Multiple methods crash when disk caching is active — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-05 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-06: Massive code duplication across disk/memory cache branches — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-06 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-11: PriogridCountryMapper is a god class bridging 3 architectural concerns — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-11 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-12: Silent wrong-country assignment when geometry intersection fails — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-12 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-14: Stale disk cache returns outdated mappings after shapefile update — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-14 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-16: Thread-unsafe caches under concurrent enrichment — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-16 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-17: Implicit column naming contract between mapper and manager — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-17 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-20: ZeroDivisionError on degenerate zero-area cells — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-20 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### C-21: Batch enrichment failures produce incomplete DataFrames (observability-only fix) — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-21 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + ### C-39: Dead geopandas runtime mapper + 1.3 GB shapefiles — RESOLVED | Field | Value | @@ -799,6 +699,26 @@ See also C-07/C-27/C-29 (pipeline-core coupling symptoms), C-39 (the dead-mapper ## Resolved Disagreements +### D-01: Cache strategy refactoring — extract now vs. characterize first — RESOLVED + +| Field | Value | +|-------|-------| +| ID | D-01 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + +### D-02: Thread safety fix — lock caches vs. remove threading — RESOLVED + +| Field | Value | +|-------|-------| +| ID | D-02 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the code this concern describes no longer exists. | + +--- + ### D-03: Warning suppression — RESOLVED | Field | Value | From a9bed430ecd3a7cee0353603952db9eb2906a253 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Wed, 24 Jun 2026 05:06:18 +0200 Subject: [PATCH 4/5] docs(register): true up the cluster section + C-03 after the mapper deletion The two doc-accuracy leftovers from the C-39 curation: - Causal Clusters: replace the stale "Defer until ADR-011 is executed" contingency notes on Clusters A/C/D with resolved stamps (ADR-011 is executed, mapper deleted in PR #42); mark Cluster E executed and Cluster F partially resolved (the PriogridCountryMapper CIC is gone, the manager CIC stays). - C-03: narrow to the manager validation + enrich->validate path. Drop the deleted tests/test_mapping.py from Location and the gone mapper find_* methods from the trigger; the two live manager-side gaps remain. Bookkeeping only. Co-Authored-By: Claude Opus 4.8 --- reports/technical_risk_register.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/reports/technical_risk_register.md b/reports/technical_risk_register.md index c28c463..004ee87 100644 --- a/reports/technical_risk_register.md +++ b/reports/technical_risk_register.md @@ -30,7 +30,7 @@ **Highest tier:** 1 (C-16) **Fix strategy:** Extract `CacheStrategy` interface with disk/memory implementations. Thread-lock in memory impl. Shapefile hash in disk cache keys. **Resolution scope:** Full -**⚠ CONTINGENT ON ADR-011:** If precomputed lookup table replaces mapping.py, this entire cluster is eliminated. Defer until ADR-011 is executed. +**✅ RESOLVED 2026-06-24:** ADR-011 is executed and the runtime mapper (`mapping.py`) was deleted (C-39, PR #42). The cache machinery this cluster describes no longer exists — C-05, C-06, C-14, C-16, D-01, D-02 are all resolved. ### Cluster B: Silent error hiding architecture **Root cause:** The codebase suppresses problem signals at three levels — global warning filter, DEBUG-level exception logging with `continue`, and raises without preceding logs. The impact propagates through a delivery chain with no correction mechanism. @@ -45,7 +45,7 @@ **Highest tier:** 2 (C-02) **Fix strategy:** Lazy initialization or removal of module-level call. Add `mapper` constructor parameter to manager. **Resolution scope:** Full -**⚠ CONTINGENT ON ADR-011:** If precomputed lookup table replaces mapping.py, this entire cluster is eliminated. Defer until ADR-011 is executed. +**✅ RESOLVED 2026-06-24:** ADR-011 is executed and the runtime mapper (`mapping.py`) was deleted (C-39, PR #42). The module-level `set_default_mapper()` side effect this cluster describes no longer exists — C-02 is resolved (C-10 was already resolved). ### Cluster D: Mapper-manager boundary contract **Root cause:** No explicit contract declares what columns the mapper produces and the manager consumes. @@ -53,7 +53,7 @@ **Highest tier:** 2 (C-04) **Fix strategy:** Define `ENRICHMENT_SCHEMA` constant. Harmonize forward/reverse thresholds. Add end-to-end integration test. **Resolution scope:** Full -**⚠ CONTINGENT ON ADR-011:** If precomputed lookup table replaces mapping.py, the boundary simplifies to a Parquet schema. C-17 is eliminated; C-04 is eliminated (no runtime forward/reverse divergence). +**✅ RESOLVED 2026-06-24:** ADR-011 is executed and the runtime mapper (`mapping.py`) was deleted (C-39, PR #42). The mapper/manager column boundary is now a precomputed Parquet schema (`gaul_schema.py` + `GaulLookupEnricher`); there is no runtime forward/reverse divergence — C-04 and C-17 are both resolved. ### Cluster F: CIC-code drift (documentation describes aspirational, not actual behavior) **Root cause:** CICs were written as design contracts and never validated against the code. Multiple guarantees are false. @@ -61,6 +61,7 @@ **Highest tier:** Not a code risk — documentation accuracy risk **Fix strategy:** Update CICs to describe actual code behavior. Specifically: (1) cache guarantee needs C-05 caveat, (2) return types need full key listing, (3) §6 log level should say DEBUG not WARNING, (4) ADR-008 compliance claim needs qualifying, (5) env var boundary validation claim needs qualifying. Pure documentation, no code changes. **Resolution scope:** Full +**✅ PARTIALLY RESOLVED 2026-06-24:** the `PriogridCountryMapper` CIC was deleted with the mapper (C-39, PR #42), so its drift findings (1.1, 1.2) are moot. The `UNFAOPostProcessorManager` CIC remains and is kept current (it now describes `GaulLookupEnricher`). ### Cluster E: Replace runtime mapper with precomputed lookup table **Root cause:** The area-majority algorithm is a confirmed FAO requirement (D-05 resolved), but it doesn't need 3,100 lines of geopandas runtime code — a one-time precomputation produces a ~65K-row Parquet lookup table that replaces the entire mapper with a dictionary join. @@ -68,24 +69,27 @@ **Highest tier:** 2 (C-23) **Fix strategy (revised 2026-06-12):** (1) Build the lookup by joining views-datafactory's 7 area-majority GAUL parquets (regenerated June 11, 259,200 rows each) plus the GID→lat/lon formula — the original "run the current mapper with LFS" precomputation is obsolete. (2) Replace `mapping.py` with a simple Parquet-join enricher. (3) Remove 774 MB shapefile bundle, geopandas dependency, and all cache machinery. See `docs/cross_repo_integration_report.md` and ADR-011 assessment §10. **Resolution scope:** Full — resolves Clusters A and C entirely. Eliminates C-07, C-08, C-11. Reduces Cluster B to manager-side concerns only (C-19 unfao.py raises, C-21 batch tracking, C-22 correction process). +**✅ EXECUTED 2026-06-24:** the lookup enricher shipped (`GaulLookupEnricher`, ADR-011) and the runtime mapper + shapefiles + geopandas were deleted (C-39, PR #42). The remaining open entries here are the datafactory-side area-math (C-08/C-31, tracked in views-datafactory) and the manager-side Cluster B residue (C-19/C-22) — not mapper code. --- ## Open Concerns -### C-03: Test coverage gaps across mapper-manager boundary and manager code +### C-03: Test coverage gaps in manager validation and the enrich→validate path | Field | Value | |-------|-------| | ID | C-03 | | Tier | 3 | | Source | `repo-assimilation` (2026-06-02), `test-review` (2026-06-02) | -| Trigger | When modifying the manager's `_validate()` or the mapper's `find_*` methods, verify that the test suite covers the changed behavior — integration-level coverage across the mapper-manager boundary is still absent | -| Location | `tests/test_mapping.py`, `tests/test_validation.py`, `views_postprocessing/unfao/managers/unfao.py` | +| Trigger | When modifying the manager's `_validate()` or the enricher, verify that the test suite covers the changed behavior — end-to-end coverage across the enrich→validate path is still absent | +| Location | `tests/test_validation.py`, `views_postprocessing/unfao/managers/unfao.py` | -Initial state was zero test coverage. A 73-test suite was written (2026-06-02) covering the mapper's core guarantees (determinism, largest-overlap, admin assignment, cache equivalence, GID-not-found, missing shapefile, C-05 disk-cache bug) and the validation logic (missing columns, null rejection, error messages). Remaining gaps: (1) the validation tests replicate `_validate()` logic in a standalone function because `views-pipeline-core` is unavailable in test environments — if the real `_validate()` diverges, tests pass while production fails; (2) no end-to-end test enriches through the mapper then validates through the manager; (3) the `ThreadPoolExecutor` code path is never exercised in tests; (4) no tests run against real shapefiles (Git LFS not installed). CI pytest step has been uncommented. +Initial state was zero test coverage. A 73-test suite was written (2026-06-02) covering the (now-deleted) mapper's core guarantees and the validation logic (missing columns, null rejection, error messages). Remaining gaps after the mapper removal: (1) the validation tests replicate `_validate()` logic in a standalone function because `views-pipeline-core` is unavailable in test environments — if the real `_validate()` diverges, tests pass while production fails; (2) no end-to-end test enriches through `GaulLookupEnricher` then validates through the manager. -Tier recalibrated from 2 to 3 during review-rr (2026-06-02): 73 tests now exist covering core mapper guarantees. The gap is maintainability (test-code divergence, missing integration path), not structural fragility. +Tier recalibrated from 2 to 3 during review-rr (2026-06-02): the gap is maintainability (test-code divergence, missing integration path), not structural fragility. + +**Update 2026-06-24:** narrowed with the mapper deletion (C-39, PR #42). The mapper-coverage dimension is gone with the mapper (`tests/test_mapping.py` deleted; the determinism/cache/shapefile/`ThreadPoolExecutor` gaps no longer exist). Two manager-side gaps remain: the standalone `_validate()` replica and the missing enrich→validate end-to-end test. --- From bd808bde8808a58ceed3620bbb6418ba3b15a753 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Wed, 24 Jun 2026 05:11:02 +0200 Subject: [PATCH 5/5] =?UTF-8?q?docs(register):=20resolve=20C-10=20(dead=20?= =?UTF-8?q?Cluster=20C=20mapper=20concern)=20=E2=80=94=20review-diff=20cat?= =?UTF-8?q?ch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit review-diff on the curation found the Cluster C resolved-stamp falsely claimed C-10 "was already resolved". C-10 (manager↔mapper coupling via the module-level _DEFAULT_MAPPER global) is itself a dead Cluster C concern: mapping.py:3088-3122 and the unfao.py get_default_mapper() call are both deleted (C-39, PR #42). Move C-10 to Resolved and correct the note to "C-02 and C-10 are both resolved". Counts: open 25 -> 24, resolved 15 -> 16. Bookkeeping only. Co-Authored-By: Claude Opus 4.8 --- reports/technical_risk_register.md | 32 ++++++++++++------------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/reports/technical_risk_register.md b/reports/technical_risk_register.md index 004ee87..142b71e 100644 --- a/reports/technical_risk_register.md +++ b/reports/technical_risk_register.md @@ -6,8 +6,8 @@ | Owner | Dylan Pinheiro / PRIO MD&D Team | | Last Updated | 2026-06-24 | | Total Concerns | 40 | -| Open Concerns | 25 | -| Resolved Concerns | 15 | +| Open Concerns | 24 | +| Resolved Concerns | 16 | --- @@ -45,7 +45,7 @@ **Highest tier:** 2 (C-02) **Fix strategy:** Lazy initialization or removal of module-level call. Add `mapper` constructor parameter to manager. **Resolution scope:** Full -**✅ RESOLVED 2026-06-24:** ADR-011 is executed and the runtime mapper (`mapping.py`) was deleted (C-39, PR #42). The module-level `set_default_mapper()` side effect this cluster describes no longer exists — C-02 is resolved (C-10 was already resolved). +**✅ RESOLVED 2026-06-24:** ADR-011 is executed and the runtime mapper (`mapping.py`) was deleted (C-39, PR #42). The module-level `set_default_mapper()` side effect this cluster describes no longer exists — C-02 and C-10 are both resolved. ### Cluster D: Mapper-manager boundary contract **Root cause:** No explicit contract declares what columns the mapper produces and the manager consumes. @@ -137,22 +137,6 @@ The "Validate Version" step fetches the latest version from `https://pypi.org/py --- -### C-10: Manager-to-Mapper coupling via module-level global state - -| Field | Value | -|-------|-------| -| ID | C-10 | -| Tier | 3 | -| Source | `graphify` (2026-06-02) | -| Trigger | When writing unit tests for `UNFAOPostProcessorManager` that need to mock the mapper, verify that the dependency is injectable — currently it is hard-wired through the `_DEFAULT_MAPPER` global | -| Location | `views_postprocessing/unfao/mapping/mapping.py:3088-3122`, `views_postprocessing/unfao/managers/unfao.py:43` | - -Graphify graph traversal revealed that `UNFAOPostProcessorManager` (component 4) and `PriogridCountryMapper` (component 0) are in completely disconnected graph components — they share no structural edge despite being tightly coupled at runtime. The connection goes through `get_default_mapper()` which returns the module-level `_DEFAULT_MAPPER` global (line 3116). In `unfao.py` line 43, the manager calls `self._mapper = get_default_mapper()`, coupling it to a singleton created at import time rather than accepting the mapper as an injected dependency. This makes the manager untestable in isolation (our test suite had to intercept `gpd.read_file` at conftest module level to work around this), and prevents using different mapper configurations in different contexts. - -See also C-02 (the same module-level side effect that creates the global mapper). - ---- - ### C-13: No timeout on Appwrite operations — pipeline can hang indefinitely | Field | Value | @@ -551,6 +535,16 @@ See also C-07/C-27/C-29 (pipeline-core coupling symptoms), C-39 (the dead-mapper ## Resolved Concerns +### C-10: Manager-to-Mapper coupling via module-level global state — RESOLVED + +| Field | Value | +|-------|-------| +| ID | C-10 | +| Resolved | 2026-06-24 | +| Resolution | The `PriogridCountryMapper` runtime mapper was deleted (C-39, PR #42); the module-level `_DEFAULT_MAPPER` global and `get_default_mapper()` coupling this concern describes no longer exist (the manager now uses `GaulLookupEnricher`). | + +--- + ### C-04: Inconsistent forward/reverse mapping breaks expected bijection — RESOLVED | Field | Value |