Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion geocoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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["properties"]
if properties["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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions tests/geocoding_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand Down
6 changes: 6 additions & 0 deletions tests/service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"})
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading