1 # SPDX-License-Identifier: GPL-3.0-or-later
3 # This file is part of Nominatim. (https://nominatim.org)
5 # Copyright (C) 2023 by the Nominatim developer community.
6 # For a full list of authors see the git log.
8 Tests for reverse API call.
10 These tests make sure that all Python code is correct and executable.
11 Functional tests can be found in the BDD test suite.
17 import nominatim.api as napi
19 def test_reverse_rank_30(apiobj):
20 apiobj.add_placex(place_id=223, class_='place', type='house',
23 geometry='POINT(1.3 0.7)')
25 result = apiobj.api.reverse((1.3, 0.7))
27 assert result is not None
28 assert result.place_id == 223
31 @pytest.mark.parametrize('country', ['de', 'us'])
32 def test_reverse_street(apiobj, country):
33 apiobj.add_placex(place_id=990, class_='highway', type='service',
34 rank_search=27, rank_address=27,
35 name = {'name': 'My Street'},
36 centroid=(10.0, 10.0),
38 geometry='LINESTRING(9.995 10, 10.005 10)')
40 assert apiobj.api.reverse((9.995, 10)).place_id == 990
43 def test_reverse_ignore_unindexed(apiobj):
44 apiobj.add_placex(place_id=223, class_='place', type='house',
48 geometry='POINT(1.3 0.7)')
50 result = apiobj.api.reverse((1.3, 0.7))
55 @pytest.mark.parametrize('y,layer,place_id', [(0.7, napi.DataLayer.ADDRESS, 223),
56 (0.70001, napi.DataLayer.POI, 224),
57 (0.7, napi.DataLayer.ADDRESS | napi.DataLayer.POI, 224),
58 (0.70001, napi.DataLayer.ADDRESS | napi.DataLayer.POI, 223),
59 (0.7, napi.DataLayer.MANMADE, 225),
60 (0.7, napi.DataLayer.RAILWAY, 226),
61 (0.7, napi.DataLayer.NATURAL, 227),
62 (0.70003, napi.DataLayer.MANMADE | napi.DataLayer.RAILWAY, 225),
63 (0.70003, napi.DataLayer.MANMADE | napi.DataLayer.NATURAL, 225),
64 (5, napi.DataLayer.ADDRESS, 229)])
65 def test_reverse_rank_30_layers(apiobj, y, layer, place_id):
66 apiobj.add_placex(place_id=223, class_='place', type='house',
70 centroid=(1.3, 0.70001))
71 apiobj.add_placex(place_id=224, class_='amenity', type='toilet',
75 apiobj.add_placex(place_id=225, class_='man_made', type='tower',
78 centroid=(1.3, 0.70003))
79 apiobj.add_placex(place_id=226, class_='railway', type='station',
82 centroid=(1.3, 0.70004))
83 apiobj.add_placex(place_id=227, class_='natural', type='cave',
86 centroid=(1.3, 0.70005))
87 apiobj.add_placex(place_id=229, class_='place', type='house',
88 name={'addr:housename': 'Old Cottage'},
93 assert apiobj.api.reverse((1.3, y), layers=layer).place_id == place_id
96 def test_reverse_poi_layer_with_no_pois(apiobj):
97 apiobj.add_placex(place_id=223, class_='place', type='house',
101 centroid=(1.3, 0.70001))
103 assert apiobj.api.reverse((1.3, 0.70001), max_rank=29,
104 layers=napi.DataLayer.POI) is None
107 def test_reverse_housenumber_on_street(apiobj):
108 apiobj.add_placex(place_id=990, class_='highway', type='service',
109 rank_search=27, rank_address=27,
110 name = {'name': 'My Street'},
111 centroid=(10.0, 10.0),
112 geometry='LINESTRING(9.995 10, 10.005 10)')
113 apiobj.add_placex(place_id=991, class_='place', type='house',
115 rank_search=30, rank_address=30,
117 centroid=(10.0, 10.00001))
119 assert apiobj.api.reverse((10.0, 10.0), max_rank=30).place_id == 991
120 assert apiobj.api.reverse((10.0, 10.0), max_rank=27).place_id == 990
121 assert apiobj.api.reverse((10.0, 10.00001), max_rank=30).place_id == 991
124 def test_reverse_housenumber_interpolation(apiobj):
125 apiobj.add_placex(place_id=990, class_='highway', type='service',
126 rank_search=27, rank_address=27,
127 name = {'name': 'My Street'},
128 centroid=(10.0, 10.0),
129 geometry='LINESTRING(9.995 10, 10.005 10)')
130 apiobj.add_placex(place_id=991, class_='place', type='house',
132 rank_search=30, rank_address=30,
134 centroid=(10.0, 10.00002))
135 apiobj.add_osmline(place_id=992,
137 startnumber=1, endnumber=3, step=1,
138 centroid=(10.0, 10.00001),
139 geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
141 assert apiobj.api.reverse((10.0, 10.0)).place_id == 992
144 def test_reverse_housenumber_point_interpolation(apiobj):
145 apiobj.add_placex(place_id=990, class_='highway', type='service',
146 rank_search=27, rank_address=27,
147 name = {'name': 'My Street'},
148 centroid=(10.0, 10.0),
149 geometry='LINESTRING(9.995 10, 10.005 10)')
150 apiobj.add_osmline(place_id=992,
152 startnumber=42, endnumber=42, step=1,
153 centroid=(10.0, 10.00001),
154 geometry='POINT(10.0 10.00001)')
156 res = apiobj.api.reverse((10.0, 10.0))
157 assert res.place_id == 992
158 assert res.housenumber == '42'
161 def test_reverse_tiger_number(apiobj):
162 apiobj.add_placex(place_id=990, class_='highway', type='service',
163 rank_search=27, rank_address=27,
164 name = {'name': 'My Street'},
165 centroid=(10.0, 10.0),
167 geometry='LINESTRING(9.995 10, 10.005 10)')
168 apiobj.add_tiger(place_id=992,
170 startnumber=1, endnumber=3, step=1,
171 centroid=(10.0, 10.00001),
172 geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
174 assert apiobj.api.reverse((10.0, 10.0)).place_id == 992
175 assert apiobj.api.reverse((10.0, 10.00001)).place_id == 992
178 def test_reverse_point_tiger(apiobj):
179 apiobj.add_placex(place_id=990, class_='highway', type='service',
180 rank_search=27, rank_address=27,
181 name = {'name': 'My Street'},
182 centroid=(10.0, 10.0),
184 geometry='LINESTRING(9.995 10, 10.005 10)')
185 apiobj.add_tiger(place_id=992,
187 startnumber=1, endnumber=1, step=1,
188 centroid=(10.0, 10.00001),
189 geometry='POINT(10.0 10.00001)')
191 res = apiobj.api.reverse((10.0, 10.0))
192 assert res.place_id == 992
193 assert res.housenumber == '1'
196 def test_reverse_low_zoom_address(apiobj):
197 apiobj.add_placex(place_id=1001, class_='place', type='house',
201 centroid=(59.3, 80.70001))
202 apiobj.add_placex(place_id=1002, class_='place', type='town',
203 name={'name': 'Town'},
206 centroid=(59.3, 80.70001),
207 geometry="""POLYGON((59.3 80.70001, 59.3001 80.70001,
208 59.3001 80.70101, 59.3 80.70101, 59.3 80.70001))""")
210 assert apiobj.api.reverse((59.30005, 80.7005)).place_id == 1001
211 assert apiobj.api.reverse((59.30005, 80.7005), max_rank=18).place_id == 1002
214 def test_reverse_place_node_in_area(apiobj):
215 apiobj.add_placex(place_id=1002, class_='place', type='town',
216 name={'name': 'Town Area'},
219 centroid=(59.3, 80.70001),
220 geometry="""POLYGON((59.3 80.70001, 59.3001 80.70001,
221 59.3001 80.70101, 59.3 80.70101, 59.3 80.70001))""")
222 apiobj.add_placex(place_id=1003, class_='place', type='suburb',
223 name={'name': 'Suburb Point'},
227 centroid=(59.30004, 80.70055))
229 assert apiobj.api.reverse((59.30004, 80.70055)).place_id == 1003
232 @pytest.mark.parametrize('layer,place_id', [(napi.DataLayer.MANMADE, 225),
233 (napi.DataLayer.RAILWAY, 226),
234 (napi.DataLayer.NATURAL, 227),
235 (napi.DataLayer.MANMADE | napi.DataLayer.RAILWAY, 225),
236 (napi.DataLayer.MANMADE | napi.DataLayer.NATURAL, 225)])
237 def test_reverse_larger_area_layers(apiobj, layer, place_id):
238 apiobj.add_placex(place_id=225, class_='man_made', type='dam',
239 name={'name': 'Dam'},
242 centroid=(1.3, 0.70003))
243 apiobj.add_placex(place_id=226, class_='railway', type='yard',
244 name={'name': 'Dam'},
247 centroid=(1.3, 0.70004))
248 apiobj.add_placex(place_id=227, class_='natural', type='spring',
249 name={'name': 'Dam'},
252 centroid=(1.3, 0.70005))
254 assert apiobj.api.reverse((1.3, 0.7), layers=layer).place_id == place_id
257 def test_reverse_country_lookup_no_objects(apiobj):
258 apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
260 assert apiobj.api.reverse((0.5, 0.5)) is None
263 @pytest.mark.parametrize('rank', [4, 30])
264 def test_reverse_country_lookup_country_only(apiobj, rank):
265 apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
266 apiobj.add_placex(place_id=225, class_='place', type='country',
267 name={'name': 'My Country'},
273 assert apiobj.api.reverse((0.5, 0.5), max_rank=rank).place_id == 225
276 def test_reverse_country_lookup_place_node_inside(apiobj):
277 apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
278 apiobj.add_placex(place_id=225, class_='place', type='state',
280 name={'name': 'My State'},
284 centroid=(0.5, 0.505))
286 assert apiobj.api.reverse((0.5, 0.5)).place_id == 225
289 @pytest.mark.parametrize('gtype', list(napi.GeometryFormat))
290 def test_reverse_geometry_output_placex(apiobj, gtype):
291 apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
292 apiobj.add_placex(place_id=1001, class_='place', type='house',
296 centroid=(59.3, 80.70001))
297 apiobj.add_placex(place_id=1003, class_='place', type='suburb',
298 name={'name': 'Suburb Point'},
305 assert apiobj.api.reverse((59.3, 80.70001), geometry_output=gtype).place_id == 1001
306 assert apiobj.api.reverse((0.5, 0.5), geometry_output=gtype).place_id == 1003
309 def test_reverse_simplified_geometry(apiobj):
310 apiobj.add_placex(place_id=1001, class_='place', type='house',
314 centroid=(59.3, 80.70001))
316 details = dict(geometry_output=napi.GeometryFormat.GEOJSON,
317 geometry_simplification=0.1)
318 assert apiobj.api.reverse((59.3, 80.70001), **details).place_id == 1001
321 def test_reverse_interpolation_geometry(apiobj):
322 apiobj.add_osmline(place_id=992,
324 startnumber=1, endnumber=3, step=1,
325 centroid=(10.0, 10.00001),
326 geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
328 assert apiobj.api.reverse((10.0, 10.0), geometry_output=napi.GeometryFormat.TEXT)\
329 .geometry['text'] == 'POINT(10 10.00001)'
332 def test_reverse_tiger_geometry(apiobj):
333 apiobj.add_placex(place_id=990, class_='highway', type='service',
334 rank_search=27, rank_address=27,
335 name = {'name': 'My Street'},
336 centroid=(10.0, 10.0),
338 geometry='LINESTRING(9.995 10, 10.005 10)')
339 apiobj.add_tiger(place_id=992,
341 startnumber=1, endnumber=3, step=1,
342 centroid=(10.0, 10.00001),
343 geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
345 output = apiobj.api.reverse((10.0, 10.0),
346 geometry_output=napi.GeometryFormat.GEOJSON).geometry['geojson']
348 assert json.loads(output) == {'coordinates': [10, 10.00001], 'type': 'Point'}