From: Sarah Hoffmann Date: Thu, 16 Feb 2023 14:35:54 +0000 (+0100) Subject: add a WKB decoder for the Point class X-Git-Tag: v4.3.0~102^2 X-Git-Url: https://git.openstreetmap.org./nominatim.git/commitdiff_plain/ee0c5e24bb2bd89288a2fa7a9dada2c7ee8f44a9 add a WKB decoder for the Point class This allows to return point geometries from the database and makes the SQL a bit simpler. --- diff --git a/nominatim/api/lookup.py b/nominatim/api/lookup.py index 27706ff3..c42bf0c2 100644 --- a/nominatim/api/lookup.py +++ b/nominatim/api/lookup.py @@ -46,8 +46,7 @@ async def find_in_placex(conn: SearchConnection, place: ntyp.PlaceRef, t.c.importance, t.c.wikipedia, t.c.indexed_date, t.c.parent_place_id, t.c.rank_address, t.c.rank_search, t.c.linked_place_id, - sa.func.ST_X(t.c.centroid).label('x'), - sa.func.ST_Y(t.c.centroid).label('y'), + t.c.centroid, _select_column_geometry(t.c.geometry, details.geometry_output)) if isinstance(place, ntyp.PlaceID): @@ -76,8 +75,7 @@ async def find_in_osmline(conn: SearchConnection, place: ntyp.PlaceRef, sql = sa.select(t.c.place_id, t.c.osm_id, t.c.parent_place_id, t.c.indexed_date, t.c.startnumber, t.c.endnumber, t.c.step, t.c.address, t.c.postcode, t.c.country_code, - sa.func.ST_X(sa.func.ST_Centroid(t.c.linegeo)).label('x'), - sa.func.ST_Y(sa.func.ST_Centroid(t.c.linegeo)).label('y'), + t.c.linegeo.ST_Centroid().label('centroid'), _select_column_geometry(t.c.linegeo, details.geometry_output)) if isinstance(place, ntyp.PlaceID): @@ -106,8 +104,7 @@ async def find_in_tiger(conn: SearchConnection, place: ntyp.PlaceRef, sql = sa.select(t.c.place_id, t.c.parent_place_id, t.c.startnumber, t.c.endnumber, t.c.step, t.c.postcode, - sa.func.ST_X(sa.func.ST_Centroid(t.c.linegeo)).label('x'), - sa.func.ST_Y(sa.func.ST_Centroid(t.c.linegeo)).label('y'), + t.c.linegeo.ST_Centroid().label('centroid'), _select_column_geometry(t.c.linegeo, details.geometry_output)) if isinstance(place, ntyp.PlaceID): @@ -128,8 +125,7 @@ async def find_in_postcode(conn: SearchConnection, place: ntyp.PlaceRef, sql = sa.select(t.c.place_id, t.c.parent_place_id, t.c.rank_search, t.c.rank_address, t.c.indexed_date, t.c.postcode, t.c.country_code, - sa.func.ST_X(t.c.geometry).label('x'), - sa.func.ST_Y(t.c.geometry).label('y'), + t.c.geometry.label('centroid'), _select_column_geometry(t.c.geometry, details.geometry_output)) if isinstance(place, ntyp.PlaceID): diff --git a/nominatim/api/results.py b/nominatim/api/results.py index 23cb47f4..10f03393 100644 --- a/nominatim/api/results.py +++ b/nominatim/api/results.py @@ -132,13 +132,6 @@ class SearchResult: return self.importance or (0.7500001 - (self.rank_search/40.0)) - # pylint: disable=consider-using-f-string - def centroid_as_geojson(self) -> str: - """ Get the centroid in GeoJSON format. - """ - return '{"type": "Point","coordinates": [%f, %f]}' % self.centroid - - def _filter_geometries(row: SaRow) -> Dict[str, str]: return {k[9:]: v for k, v in row._mapping.items() # pylint: disable=W0212 if k.startswith('geometry_')} @@ -166,7 +159,7 @@ def create_from_placex_row(row: SaRow) -> SearchResult: importance=row.importance, country_code=row.country_code, indexed_date=getattr(row, 'indexed_date'), - centroid=Point(row.x, row.y), + centroid=Point.from_wkb(row.centroid.data), geometry=_filter_geometries(row)) @@ -186,7 +179,7 @@ def create_from_osmline_row(row: SaRow) -> SearchResult: 'step': str(row.step)}, country_code=row.country_code, indexed_date=getattr(row, 'indexed_date'), - centroid=Point(row.x, row.y), + centroid=Point.from_wkb(row.centroid.data), geometry=_filter_geometries(row)) @@ -203,7 +196,7 @@ def create_from_tiger_row(row: SaRow) -> SearchResult: 'endnumber': str(row.endnumber), 'step': str(row.step)}, country_code='us', - centroid=Point(row.x, row.y), + centroid=Point.from_wkb(row.centroid.data), geometry=_filter_geometries(row)) @@ -219,7 +212,7 @@ def create_from_postcode_row(row: SaRow) -> SearchResult: rank_search=row.rank_search, rank_address=row.rank_address, country_code=row.country_code, - centroid=Point(row.x, row.y), + centroid=Point.from_wkb(row.centroid.data), indexed_date=row.indexed_date, geometry=_filter_geometries(row)) diff --git a/nominatim/api/types.py b/nominatim/api/types.py index 89b81111..9dc3ff2e 100644 --- a/nominatim/api/types.py +++ b/nominatim/api/types.py @@ -10,6 +10,7 @@ Complex datatypes used by the Nominatim API. from typing import Optional, Union, NamedTuple import dataclasses import enum +from struct import unpack @dataclasses.dataclass class PlaceID: @@ -55,6 +56,33 @@ class Point(NamedTuple): return self.x + def to_geojson(self) -> str: + """ Return the point in GeoJSON format. + """ + return f'{{"type": "Point","coordinates": [{self.x}, {self.y}]}}' + + + @staticmethod + def from_wkb(wkb: bytes) -> 'Point': + """ Create a point from EWKB as returned from the database. + """ + if len(wkb) != 25: + raise ValueError("Point wkb has unexpected length") + if wkb[0] == 0: + gtype, srid, x, y = unpack('>iidd', wkb[1:]) + elif wkb[0] == 1: + gtype, srid, x, y = unpack(' str: locales = options.get('locales', napi.Locales()) geom = result.geometry.get('geojson') - centroid = result.centroid_as_geojson() + centroid = result.centroid.to_geojson() out = JsonWriter() out.start_object()\ diff --git a/test/python/cli/test_cmd_api.py b/test/python/cli/test_cmd_api.py index 966059c4..0b5dccfb 100644 --- a/test/python/cli/test_cmd_api.py +++ b/test/python/cli/test_cmd_api.py @@ -80,7 +80,7 @@ class TestCliDetailsCall: @pytest.fixture(autouse=True) def setup_status_mock(self, monkeypatch): result = napi.SearchResult(napi.SourceTable.PLACEX, ('place', 'thing'), - (1.0, -3.0)) + napi.Point(1.0, -3.0)) monkeypatch.setattr(napi.NominatimAPI, 'lookup', lambda *args: result) @@ -90,7 +90,7 @@ class TestCliDetailsCall: ('--relation', '1'), ('--place_id', '10001')]) - def test_status_json_format(self, cli_call, tmp_path, capsys, params): + def test_details_json_format(self, cli_call, tmp_path, capsys, params): result = cli_call('details', '--project-dir', str(tmp_path), *params) assert result == 0