From 4ecdf52799cccb3093f20a751f9142c76ef4ef04 Mon Sep 17 00:00:00 2001 From: Rup-Narayan-Rajbanshi Date: Fri, 4 Sep 2026 16:30:10 +0545 Subject: [PATCH] feat: include iso3 in the country geometry response --- geocoding.py | 7 ++++++- tests/geocoding_test.py | 2 ++ tests/service_test.py | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/geocoding.py b/geocoding.py index 6fb5390..d35ddb3 100644 --- a/geocoding.py +++ b/geocoding.py @@ -18,6 +18,8 @@ class Country(pydantic.BaseModel): class AdminGeometry(pydantic.BaseModel): bbox: tuple[float, float, float, float] geometry: dict[str, typing.Any] + # Only available for country level (WAB) lookups, not for admin code lookups. + iso3: str | None = None class FastGeocoder: @@ -75,11 +77,13 @@ def get_geometry_from_country_name(self, country_name: str) -> AdminGeometry | N with fiona.open(self._wab_path, layer=WAB_LAYER) as src: for feature in src: - if feature["properties"]["name"].lower().strip() == country_name: + properties: dict[str, typing.Any] = feature.get("properties") + if properties and properties.get("name", "").lower().strip() == country_name: geom = shape(feature["geometry"]) val = AdminGeometry( geometry=mapping(geom), bbox=geom.bounds, + iso3=properties["iso3"] or None, ) self._geom_from_country_name_cache[country_name] = val return val @@ -132,6 +136,7 @@ def get_geometry_from_iso3(self, iso3: str) -> AdminGeometry | None: val = AdminGeometry( geometry=mapping(geom), bbox=geom.bounds, + iso3=iso3_from_feature, ) self._geom_from_iso3_cache[iso3] = val return val diff --git a/tests/geocoding_test.py b/tests/geocoding_test.py index 73d68be..23305bd 100644 --- a/tests/geocoding_test.py +++ b/tests/geocoding_test.py @@ -103,6 +103,7 @@ def test_returns_real_nepal_geometry(self, geocoder): result = geocoder.get_geometry_from_country_name("nepal") assert isinstance(result, AdminGeometry) assert result.bbox == NEPAL_BBOX + assert result.iso3 == "NPL" assert shape(result.geometry).contains(Point(*KATHMANDU)) def test_returns_none_when_not_found(self, geocoder): @@ -114,6 +115,7 @@ def test_returns_real_france_geometry(self, geocoder): result = geocoder.get_geometry_from_iso3("fra") assert result is not None assert result.bbox == FRANCE_BBOX + assert result.iso3 == "FRA" assert shape(result.geometry).contains(Point(*PARIS)) def test_returns_none_for_unknown_iso3(self, geocoder): diff --git a/tests/service_test.py b/tests/service_test.py index e051599..ac10643 100644 --- a/tests/service_test.py +++ b/tests/service_test.py @@ -85,6 +85,7 @@ def test_returns_geometry_by_iso3(self, mock_geocoder): mock_geocoder.get_geometry_from_iso3.return_value = AdminGeometry( bbox=(0.0, 0.0, 1.0, 1.0), geometry={"type": "Point", "coordinates": [0.123456, 0.654321]}, + iso3="AAA", ) response = client.get("/country/geometry", params={"iso3": "AAA"}) @@ -93,17 +94,20 @@ def test_returns_geometry_by_iso3(self, mock_geocoder): mock_geocoder.get_geometry_from_iso3.assert_called_once_with("aaa") # coordinates get rounded to 3 decimal places by round_geojson_coordinates assert response.json()["geometry"]["coordinates"] == [0.123, 0.654] + assert response.json()["iso3"] == "AAA" def test_returns_geometry_by_country_name(self, mock_geocoder): mock_geocoder.get_geometry_from_country_name.return_value = AdminGeometry( bbox=(0.0, 0.0, 1.0, 1.0), geometry={"type": "Point", "coordinates": [0.0, 0.0]}, + iso3="NPL", ) response = client.get("/country/geometry", params={"country_name": " Nepal "}) assert response.status_code == 200 mock_geocoder.get_geometry_from_country_name.assert_called_once_with("nepal") + assert response.json()["iso3"] == "NPL" def test_returns_404_when_geometry_not_found(self, mock_geocoder): mock_geocoder.get_geometry_from_iso3.return_value = None @@ -144,6 +148,8 @@ def test_returns_geometry_for_given_codes(self, mock_geocoder): assert response.status_code == 200 mock_geocoder.get_geometry_from_adm_codes.assert_called_once_with([1, 2], [3]) + # admin code lookups are not country scoped, so there is no iso3 to report + assert response.json()["iso3"] is None def test_returns_404_when_geometry_not_found(self, mock_geocoder): mock_geocoder.get_geometry_from_adm_codes.return_value = None