Point as Point,
Bbox as Bbox,
GeometryFormat as GeometryFormat,
- LookupDetails as LookupDetails,
DataLayer as DataLayer)
from .results import (SourceTable as SourceTable,
AddressLine as AddressLine,
from nominatim.api.status import get_status, StatusResult
from nominatim.api.lookup import get_detailed_place, get_simple_place
from nominatim.api.reverse import ReverseGeocoder
-from nominatim.api.types import PlaceRef, LookupDetails, AnyPoint, DataLayer
+import nominatim.api.types as ntyp
from nominatim.api.results import DetailedResult, ReverseResult, SearchResults
return status
- async def details(self, place: PlaceRef,
- details: Optional[LookupDetails] = None) -> Optional[DetailedResult]:
+ async def details(self, place: ntyp.PlaceRef, **params: Any) -> Optional[DetailedResult]:
""" Get detailed information about a place in the database.
Returns None if there is no entry under the given ID.
"""
async with self.begin() as conn:
- return await get_detailed_place(conn, place, details or LookupDetails())
+ return await get_detailed_place(conn, place,
+ ntyp.LookupDetails.from_kwargs(params))
- async def lookup(self, places: Sequence[PlaceRef],
- details: Optional[LookupDetails] = None) -> SearchResults:
+ async def lookup(self, places: Sequence[ntyp.PlaceRef], **params: Any) -> SearchResults:
""" Get simple information about a list of places.
Returns a list of place information for all IDs that were found.
"""
- if details is None:
- details = LookupDetails()
+ details = ntyp.LookupDetails.from_kwargs(params)
async with self.begin() as conn:
return SearchResults(filter(None,
[await get_simple_place(conn, p, details) for p in places]))
- async def reverse(self, coord: AnyPoint, max_rank: Optional[int] = None,
- layer: Optional[DataLayer] = None,
- details: Optional[LookupDetails] = None) -> Optional[ReverseResult]:
+ async def reverse(self, coord: ntyp.AnyPoint, **params: Any) -> Optional[ReverseResult]:
""" Find a place by its coordinates. Also known as reverse geocoding.
Returns the closest result that can be found or None if
# There are no results to be expected outside valid coordinates.
return None
- if layer is None:
- layer = DataLayer.ADDRESS | DataLayer.POI
-
- max_rank = max(0, min(max_rank or 30, 30))
-
async with self.begin() as conn:
- geocoder = ReverseGeocoder(conn, max_rank, layer,
- details or LookupDetails())
+ geocoder = ReverseGeocoder(conn, ntyp.ReverseDetails.from_kwargs(params))
return await geocoder.lookup(coord)
return self._loop.run_until_complete(self._async_api.status())
- def details(self, place: PlaceRef,
- details: Optional[LookupDetails] = None) -> Optional[DetailedResult]:
+ def details(self, place: ntyp.PlaceRef, **params: Any) -> Optional[DetailedResult]:
""" Get detailed information about a place in the database.
"""
- return self._loop.run_until_complete(self._async_api.details(place, details))
+ return self._loop.run_until_complete(self._async_api.details(place, **params))
- def lookup(self, places: Sequence[PlaceRef],
- details: Optional[LookupDetails] = None) -> SearchResults:
+ def lookup(self, places: Sequence[ntyp.PlaceRef], **params: Any) -> SearchResults:
""" Get simple information about a list of places.
Returns a list of place information for all IDs that were found.
"""
- return self._loop.run_until_complete(self._async_api.lookup(places, details))
+ return self._loop.run_until_complete(self._async_api.lookup(places, **params))
- def reverse(self, coord: AnyPoint, max_rank: Optional[int] = None,
- layer: Optional[DataLayer] = None,
- details: Optional[LookupDetails] = None) -> Optional[ReverseResult]:
+ def reverse(self, coord: ntyp.AnyPoint, **params: Any) -> Optional[ReverseResult]:
""" Find a place by its coordinates. Also known as reverse geocoding.
Returns the closest result that can be found or None if
no place matches the given criteria.
"""
- return self._loop.run_until_complete(
- self._async_api.reverse(coord, max_rank, layer, details))
+ return self._loop.run_until_complete(self._async_api.reverse(coord, **params))
from nominatim.api.connection import SearchConnection
import nominatim.api.results as nres
from nominatim.api.logging import log
-from nominatim.api.types import AnyPoint, DataLayer, LookupDetails, GeometryFormat, Bbox
+from nominatim.api.types import AnyPoint, DataLayer, ReverseDetails, GeometryFormat, Bbox
# In SQLAlchemy expression which compare with NULL need to be expressed with
# the equal sign.
coordinate.
"""
- def __init__(self, conn: SearchConnection, max_rank: int, layer: DataLayer,
- details: LookupDetails) -> None:
+ def __init__(self, conn: SearchConnection, params: ReverseDetails) -> None:
self.conn = conn
- self.max_rank = max_rank
- self.layer = layer
- self.details = details
+ self.params = params
+
+
+ @property
+ def max_rank(self) -> int:
+ """ Return the maximum configured rank.
+ """
+ return self.params.max_rank
+
+
+ def has_geometries(self) -> bool:
+ """ Check if any geometries are requested.
+ """
+ return bool(self.params.geometry_output)
+
def layer_enabled(self, *layer: DataLayer) -> bool:
""" Return true when any of the given layer types are requested.
"""
- return any(self.layer & l for l in layer)
+ return any(self.params.layers & l for l in layer)
def layer_disabled(self, *layer: DataLayer) -> bool:
""" Return true when none of the given layer types is requested.
"""
- return not any(self.layer & l for l in layer)
+ return not any(self.params.layers & l for l in layer)
def has_feature_layers(self) -> bool:
return self.layer_enabled(DataLayer.RAILWAY, DataLayer.MANMADE, DataLayer.NATURAL)
def _add_geometry_columns(self, sql: SaSelect, col: SaColumn) -> SaSelect:
- if not self.details.geometry_output:
+ if not self.has_geometries():
return sql
out = []
- if self.details.geometry_simplification > 0.0:
- col = col.ST_SimplifyPreserveTopology(self.details.geometry_simplification)
+ if self.params.geometry_simplification > 0.0:
+ col = col.ST_SimplifyPreserveTopology(self.params.geometry_simplification)
- if self.details.geometry_output & GeometryFormat.GEOJSON:
+ if self.params.geometry_output & GeometryFormat.GEOJSON:
out.append(col.ST_AsGeoJSON().label('geometry_geojson'))
- if self.details.geometry_output & GeometryFormat.TEXT:
+ if self.params.geometry_output & GeometryFormat.TEXT:
out.append(col.ST_AsText().label('geometry_text'))
- if self.details.geometry_output & GeometryFormat.KML:
+ if self.params.geometry_output & GeometryFormat.KML:
out.append(col.ST_AsKML().label('geometry_kml'))
- if self.details.geometry_output & GeometryFormat.SVG:
+ if self.params.geometry_output & GeometryFormat.SVG:
out.append(col.ST_AsSVG().label('geometry_svg'))
return sql.add_columns(*out)
inner.c.postcode, inner.c.country_code,
inner.c.distance)
- if self.details.geometry_output:
+ if self.has_geometries():
sub = sql.subquery()
sql = self._add_geometry_columns(sql, sub.c.centroid)
inner.c.postcode,
inner.c.distance)
- if self.details.geometry_output:
+ if self.has_geometries():
sub = sql.subquery()
sql = self._add_geometry_columns(sql, sub.c.centroid)
""" Look up a single coordinate. Returns the place information,
if a place was found near the coordinates or None otherwise.
"""
- log().function('reverse_lookup',
- coord=coord, max_rank=self.max_rank,
- layer=self.layer, details=self.details)
+ log().function('reverse_lookup', coord=coord, params=self.params)
wkt = WKTElement(f'POINT({coord[0]} {coord[1]})', srid=4326)
result.distance = row.distance
if hasattr(row, 'bbox'):
result.bbox = Bbox.from_wkb(row.bbox.data)
- await nres.add_result_details(self.conn, result, self.details)
+ await nres.add_result_details(self.conn, result, self.params)
return result
"""
Complex datatypes used by the Nominatim API.
"""
-from typing import Optional, Union, Tuple, NamedTuple
+from typing import Optional, Union, Tuple, NamedTuple, TypeVar, Type, Dict, Any
import dataclasses
import enum
from struct import unpack
+from nominatim.errors import UsageError
+
@dataclasses.dataclass
class PlaceID:
""" Reference an object by Nominatim's internal ID.
TEXT = enum.auto()
+class DataLayer(enum.Flag):
+ """ Layer types that can be selected for reverse and forward search.
+ """
+ POI = enum.auto()
+ ADDRESS = enum.auto()
+ RAILWAY = enum.auto()
+ MANMADE = enum.auto()
+ NATURAL = enum.auto()
+
+
+TParam = TypeVar('TParam', bound='LookupDetails') # pylint: disable=invalid-name
+
@dataclasses.dataclass
class LookupDetails:
""" Collection of parameters that define the amount of details
- returned with a search result.
+ returned with a lookup or details result.
"""
geometry_output: GeometryFormat = GeometryFormat.NONE
""" Add the full geometry of the place to the result. Multiple
more the geometry gets simplified.
"""
+ @classmethod
+ def from_kwargs(cls: Type[TParam], kwargs: Dict[str, Any]) -> TParam:
+ """ Load the data fields of the class from a dictionary.
+ Unknown entries in the dictionary are ignored, missing ones
+ get the default setting.
-class DataLayer(enum.Flag):
- """ Layer types that can be selected for reverse and forward search.
+ The function supports type checking and throws a UsageError
+ when the value does not fit.
+ """
+ def _check_field(v: Any, field: 'dataclasses.Field[Any]') -> Any:
+ if v is None:
+ return field.default_factory() \
+ if field.default_factory != dataclasses.MISSING \
+ else field.default
+ if field.metadata and 'transform' in field.metadata:
+ return field.metadata['transform'](v)
+ if not isinstance(v, field.type):
+ raise UsageError(f"Parameter '{field.name}' needs to be of {field.type!s}.")
+ return v
+
+ return cls(**{f.name: _check_field(kwargs[f.name], f)
+ for f in dataclasses.fields(cls) if f.name in kwargs})
+
+
+@dataclasses.dataclass
+class ReverseDetails(LookupDetails):
+ """ Collection of parameters for the reverse call.
+ """
+ max_rank: int = dataclasses.field(default=30,
+ metadata={'transform': lambda v: max(0, min(v, 30))}
+ )
+ """ Highest address rank to return.
+ """
+ layers: DataLayer = DataLayer.ADDRESS | DataLayer.POI
+ """ Filter which kind of data to include.
"""
- POI = enum.auto()
- ADDRESS = enum.auto()
- RAILWAY = enum.auto()
- MANMADE = enum.auto()
- NATURAL = enum.auto()
Generic part of the server implementation of the v1 API.
Combine with the scaffolding provided for the various Python ASGI frameworks.
"""
-from typing import Optional, Any, Type, Callable, NoReturn, cast
+from typing import Optional, Any, Type, Callable, NoReturn, Dict, cast
from functools import reduce
import abc
import math
return fmt
- def parse_geometry_details(self, fmt: str) -> napi.LookupDetails:
+ def parse_geometry_details(self, fmt: str) -> Dict[str, Any]:
""" Create details strucutre from the supplied geometry parameters.
"""
- details = napi.LookupDetails(address_details=True,
- geometry_simplification=
- self.get_float('polygon_threshold', 0.0))
numgeoms = 0
+ output = napi.GeometryFormat.NONE
if self.get_bool('polygon_geojson', False):
- details.geometry_output |= napi.GeometryFormat.GEOJSON
+ output |= napi.GeometryFormat.GEOJSON
numgeoms += 1
if fmt not in ('geojson', 'geocodejson'):
if self.get_bool('polygon_text', False):
- details.geometry_output |= napi.GeometryFormat.TEXT
+ output |= napi.GeometryFormat.TEXT
numgeoms += 1
if self.get_bool('polygon_kml', False):
- details.geometry_output |= napi.GeometryFormat.KML
+ output |= napi.GeometryFormat.KML
numgeoms += 1
if self.get_bool('polygon_svg', False):
- details.geometry_output |= napi.GeometryFormat.SVG
+ output |= napi.GeometryFormat.SVG
numgeoms += 1
if numgeoms > self.config().get_int('POLYGON_OUTPUT_MAX_TYPES'):
self.raise_error('Too many polgyon output options selected.')
- return details
+ return {'address_details': True,
+ 'geometry_simplification': self.get_float('polygon_threshold', 0.0),
+ 'geometry_output': output
+ }
async def status_endpoint(api: napi.NominatimAPIAsync, params: ASGIAdaptor) -> Any:
debug = params.setup_debugging()
- details = napi.LookupDetails(address_details=params.get_bool('addressdetails', False),
- linked_places=params.get_bool('linkedplaces', False),
- parented_places=params.get_bool('hierarchy', False),
- keywords=params.get_bool('keywords', False))
-
- if params.get_bool('polygon_geojson', False):
- details.geometry_output = napi.GeometryFormat.GEOJSON
-
locales = napi.Locales.from_accept_languages(params.get_accepted_languages())
- result = await api.details(place, details)
+ result = await api.details(place,
+ address_details=params.get_bool('addressdetails', False),
+ linked_places=params.get_bool('linkedplaces', False),
+ parented_places=params.get_bool('hierarchy', False),
+ keywords=params.get_bool('keywords', False),
+ geometry_output = napi.GeometryFormat.GEOJSON
+ if params.get_bool('polygon_geojson', False)
+ else napi.GeometryFormat.NONE
+ )
if debug:
return params.build_response(loglib.get_and_disable())
debug = params.setup_debugging()
coord = napi.Point(params.get_float('lon'), params.get_float('lat'))
locales = napi.Locales.from_accept_languages(params.get_accepted_languages())
- details = params.parse_geometry_details(fmt)
-
zoom = max(0, min(18, params.get_int('zoom', 18)))
+ details = params.parse_geometry_details(fmt)
+ details['max_rank'] = REVERSE_MAX_RANKS[zoom]
+ details['layers'] = params.get_layers()
- result = await api.reverse(coord, REVERSE_MAX_RANKS[zoom],
- params.get_layers() or
- napi.DataLayer.ADDRESS | napi.DataLayer.POI,
- details)
+ result = await api.reverse(coord, **details)
if debug:
return params.build_response(loglib.get_and_disable())
places.append(napi.OsmID(oid[0], int(oid[1:])))
if places:
- results = await api.lookup(places, details)
+ results = await api.lookup(places, **details)
else:
results = napi.SearchResults()
def run(self, args: NominatimArgs) -> int:
api = napi.NominatimAPI(args.project_dir)
- details = napi.LookupDetails(address_details=True, # needed for display name
- geometry_output=args.get_geometry_output(),
- geometry_simplification=args.polygon_threshold or 0.0)
-
result = api.reverse(napi.Point(args.lon, args.lat),
- REVERSE_MAX_RANKS[max(0, min(18, args.zoom or 18))],
- args.get_layers(napi.DataLayer.ADDRESS | napi.DataLayer.POI),
- details)
+ max_rank=REVERSE_MAX_RANKS[max(0, min(18, args.zoom or 18))],
+ layers=args.get_layers(napi.DataLayer.ADDRESS | napi.DataLayer.POI),
+ address_details=True, # needed for display name
+ geometry_output=args.get_geometry_output(),
+ geometry_simplification=args.polygon_threshold)
if result:
output = api_output.format_result(
def run(self, args: NominatimArgs) -> int:
api = napi.NominatimAPI(args.project_dir)
- details = napi.LookupDetails(address_details=True, # needed for display name
- geometry_output=args.get_geometry_output(),
- geometry_simplification=args.polygon_threshold or 0.0)
-
places = [napi.OsmID(o[0], int(o[1:])) for o in args.ids]
- results = api.lookup(places, details)
+ results = api.lookup(places,
+ address_details=True, # needed for display name
+ geometry_output=args.get_geometry_output(),
+ geometry_simplification=args.polygon_threshold or 0.0)
output = api_output.format_result(
results,
api = napi.NominatimAPI(args.project_dir)
- details = napi.LookupDetails(address_details=args.addressdetails,
- linked_places=args.linkedplaces,
- parented_places=args.hierarchy,
- keywords=args.keywords)
- if args.polygon_geojson:
- details.geometry_output = napi.GeometryFormat.GEOJSON
+ result = api.details(place,
+ address_details=args.addressdetails,
+ linked_places=args.linkedplaces,
+ parented_places=args.hierarchy,
+ keywords=args.keywords,
+ geometry_output=napi.GeometryFormat.GEOJSON
+ if args.polygon_geojson
+ else napi.GeometryFormat.NONE)
- result = api.details(place, details)
if result:
output = api_output.format_result(
indexed_date=import_date,
geometry='LINESTRING(23 34, 23.1 34, 23.1 34.1, 23 34)')
- result = apiobj.api.details(idobj, napi.LookupDetails())
+ result = apiobj.api.details(idobj)
assert result is not None
indexed_date=import_date,
geometry='LINESTRING(23 34, 23.1 34, 23.1 34.1, 23 34)')
- result = apiobj.api.details(napi.PlaceID(332), napi.LookupDetails())
+ result = apiobj.api.details(napi.PlaceID(332))
assert result is not None
apiobj.add_placex(place_id=332,
geometry='LINESTRING(23 34, 23.1 34)')
- result = apiobj.api.details(napi.PlaceID(332),
- napi.LookupDetails(geometry_output=napi.GeometryFormat.GEOJSON))
+ result = apiobj.api.details(napi.PlaceID(332), geometry_output=napi.GeometryFormat.GEOJSON)
assert result.geometry == {'geojson': '{"type":"LineString","coordinates":[[23,34],[23.1,34]]}'}
country_code='pl',
rank_search=17, rank_address=16)
- result = apiobj.api.details(napi.PlaceID(332),
- napi.LookupDetails(address_details=True))
+ result = apiobj.api.details(napi.PlaceID(332), address_details=True)
assert result.address_rows == [
napi.AddressLine(place_id=332, osm_object=('W', 4),
country_code='pl', linked_place_id=45,
rank_search=27, rank_address=26)
- result = apiobj.api.details(napi.PlaceID(332),
- napi.LookupDetails(linked_places=True))
+ result = apiobj.api.details(napi.PlaceID(332), linked_places=True)
assert result.linked_rows == []
country_code='pl', linked_place_id=332,
rank_search=27, rank_address=26)
- result = apiobj.api.details(napi.PlaceID(332),
- napi.LookupDetails(linked_places=True))
+ result = apiobj.api.details(napi.PlaceID(332), linked_places=True)
assert result.linked_rows == [
napi.AddressLine(place_id=1001, osm_object=('W', 5),
country_code='pl', parent_place_id=45,
rank_search=27, rank_address=26)
- result = apiobj.api.details(napi.PlaceID(332),
- napi.LookupDetails(parented_places=True))
+ result = apiobj.api.details(napi.PlaceID(332), parented_places=True)
assert result.parented_rows == []
country_code='pl', parent_place_id=332,
rank_search=27, rank_address=26)
- result = apiobj.api.details(napi.PlaceID(332),
- napi.LookupDetails(parented_places=True))
+ result = apiobj.api.details(napi.PlaceID(332), parented_places=True)
assert result.parented_rows == [
napi.AddressLine(place_id=1001, osm_object=('N', 5),
indexed_date=import_date,
geometry='LINESTRING(23 34, 23 35)')
- result = apiobj.api.details(idobj, napi.LookupDetails())
+ result = apiobj.api.details(idobj)
assert result is not None
startnumber=11, endnumber=20, step=1)
for i in range(1, 6):
- result = apiobj.api.details(napi.OsmID('W', 9, str(i)), napi.LookupDetails())
+ result = apiobj.api.details(napi.OsmID('W', 9, str(i)))
assert result.place_id == 1000
for i in range(7, 11):
- result = apiobj.api.details(napi.OsmID('W', 9, str(i)), napi.LookupDetails())
+ result = apiobj.api.details(napi.OsmID('W', 9, str(i)))
assert result.place_id == 1001
for i in range(12, 22):
- result = apiobj.api.details(napi.OsmID('W', 9, str(i)), napi.LookupDetails())
+ result = apiobj.api.details(napi.OsmID('W', 9, str(i)))
assert result.place_id == 1002
country_code='pl',
rank_search=17, rank_address=16)
- result = apiobj.api.details(napi.PlaceID(9000),
- napi.LookupDetails(address_details=True))
+ result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
assert result.address_rows == [
napi.AddressLine(place_id=None, osm_object=None,
osm_type='W', osm_id=6601223,
geometry='LINESTRING(23 34, 23 35)')
- result = apiobj.api.details(napi.PlaceID(4924), napi.LookupDetails())
+ result = apiobj.api.details(napi.PlaceID(4924))
assert result is not None
country_code='us',
rank_search=17, rank_address=16)
- result = apiobj.api.details(napi.PlaceID(9000),
- napi.LookupDetails(address_details=True))
+ result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
assert result.address_rows == [
napi.AddressLine(place_id=None, osm_object=None,
indexed_date=import_date,
geometry='POINT(-9.45 5.6)')
- result = apiobj.api.details(napi.PlaceID(554), napi.LookupDetails())
+ result = apiobj.api.details(napi.PlaceID(554))
assert result is not None
country_code='gb',
rank_search=17, rank_address=16)
- result = apiobj.api.details(napi.PlaceID(9000),
- napi.LookupDetails(address_details=True))
+ result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
assert result.address_rows == [
napi.AddressLine(place_id=332, osm_object=('N', 3333),
apiobj.add_placex(place_id=1, osm_type='N', osm_id=55,
class_='place', type='suburb')
- assert apiobj.api.details(objid, napi.LookupDetails()) is None
+ assert apiobj.api.details(objid) is None
@pytest.mark.parametrize('gtype', (napi.GeometryFormat.KML,
apiobj.add_placex(place_id=332)
with pytest.raises(ValueError):
- apiobj.api.details(napi.PlaceID(332),
- napi.LookupDetails(geometry_output=gtype))
+ apiobj.api.details(napi.PlaceID(332), geometry_output=gtype)
result = apiobj.api.lookup((napi.OsmID('W', 1),
napi.OsmID('W', 4),
- napi.OsmID('W', 9928)), napi.LookupDetails())
+ napi.OsmID('W', 9928)))
assert len(result) == 2
rank_search=30,
centroid=(1.3, 0.70005))
- assert apiobj.api.reverse((1.3, y), layer=layer).place_id == place_id
+ assert apiobj.api.reverse((1.3, y), layers=layer).place_id == place_id
def test_reverse_poi_layer_with_no_pois(apiobj):
centroid=(1.3, 0.70001))
assert apiobj.api.reverse((1.3, 0.70001), max_rank=29,
- layer=napi.DataLayer.POI) is None
+ layers=napi.DataLayer.POI) is None
def test_reverse_housenumber_on_street(apiobj):
rank_search=16,
centroid=(1.3, 0.70005))
- assert apiobj.api.reverse((1.3, 0.7), layer=layer).place_id == place_id
+ assert apiobj.api.reverse((1.3, 0.7), layers=layer).place_id == place_id
def test_reverse_country_lookup_no_objects(apiobj):
country_code='xx',
centroid=(0.5, 0.5))
- details = napi.LookupDetails(geometry_output=gtype)
-
- assert apiobj.api.reverse((59.3, 80.70001), details=details).place_id == 1001
- assert apiobj.api.reverse((0.5, 0.5), details=details).place_id == 1003
+ assert apiobj.api.reverse((59.3, 80.70001), geometry_output=gtype).place_id == 1001
+ assert apiobj.api.reverse((0.5, 0.5), geometry_output=gtype).place_id == 1003
def test_reverse_simplified_geometry(apiobj):
rank_search=30,
centroid=(59.3, 80.70001))
- details = napi.LookupDetails(geometry_output=napi.GeometryFormat.GEOJSON,
- geometry_simplification=0.1)
- assert apiobj.api.reverse((59.3, 80.70001), details=details).place_id == 1001
+ details = dict(geometry_output=napi.GeometryFormat.GEOJSON,
+ geometry_simplification=0.1)
+ assert apiobj.api.reverse((59.3, 80.70001), **details).place_id == 1001
def test_reverse_interpolation_geometry(apiobj):
centroid=(10.0, 10.00001),
geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
- details = napi.LookupDetails(geometry_output=napi.GeometryFormat.TEXT)
- assert apiobj.api.reverse((10.0, 10.0), details=details)\
+ assert apiobj.api.reverse((10.0, 10.0), geometry_output=napi.GeometryFormat.TEXT)\
.geometry['text'] == 'POINT(10 10.00001)'
centroid=(10.0, 10.00001),
geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
- details = napi.LookupDetails(geometry_output=napi.GeometryFormat.GEOJSON)
- output = apiobj.api.reverse((10.0, 10.0), details=details).geometry['geojson']
+ output = apiobj.api.reverse((10.0, 10.0),
+ geometry_output=napi.GeometryFormat.GEOJSON).geometry['geojson']
assert json.loads(output) == {'coordinates': [10, 10.00001], 'type': 'Point'}
napi.Point(1.0, -3.0))
monkeypatch.setattr(napi.NominatimAPI, 'details',
- lambda *args: result)
+ lambda *args, **kwargs: result)
@pytest.mark.parametrize("params", [('--node', '1'),
('--way', '1'),
extratags={'extra':'Extra'})
monkeypatch.setattr(napi.NominatimAPI, 'reverse',
- lambda *args: result)
+ lambda *args, **kwargs: result)
def test_reverse_simple(self, cli_call, tmp_path, capsys):
extratags={'extra':'Extra'})
monkeypatch.setattr(napi.NominatimAPI, 'lookup',
- lambda *args: napi.SearchResults([result]))
+ lambda *args, **kwargs: napi.SearchResults([result]))
def test_lookup_simple(self, cli_call, tmp_path, capsys):
result = cli_call('lookup', '--project-dir', str(tmp_path),