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):
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):
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):
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):
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_')}
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))
'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))
'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))
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))
from typing import Optional, Union, NamedTuple
import dataclasses
import enum
+from struct import unpack
@dataclasses.dataclass
class PlaceID:
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('<iidd', wkb[1:])
+ else:
+ raise ValueError("WKB has unknown endian value.")
+
+ if gtype != 0x20000001:
+ raise ValueError("WKB must be a point geometry.")
+ if srid != 4326:
+ raise ValueError("Only WGS84 WKB supported.")
+
+ return Point(x, y)
+
+
class GeometryFormat(enum.Flag):
""" Geometry output formats supported by Nominatim.
"""
def _format_search_json(result: napi.SearchResult, options: Mapping[str, Any]) -> 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()\
@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)
('--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