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 API_OPTIONS = {'reverse'}
21 def test_reverse_rank_30(apiobj, frontend):
22 apiobj.add_placex(place_id=223, class_='place', type='house',
25 geometry='POINT(1.3 0.7)')
27 api = frontend(apiobj, options=API_OPTIONS)
28 result = api.reverse((1.3, 0.7))
30 assert result is not None
31 assert result.place_id == 223
34 @pytest.mark.parametrize('country', ['de', 'us'])
35 def test_reverse_street(apiobj, frontend, country):
36 apiobj.add_placex(place_id=990, class_='highway', type='service',
37 rank_search=27, rank_address=27,
38 name = {'name': 'My Street'},
39 centroid=(10.0, 10.0),
41 geometry='LINESTRING(9.995 10, 10.005 10)')
43 api = frontend(apiobj, options=API_OPTIONS)
44 assert api.reverse((9.995, 10)).place_id == 990
47 def test_reverse_ignore_unindexed(apiobj, frontend):
48 apiobj.add_placex(place_id=223, class_='place', type='house',
52 geometry='POINT(1.3 0.7)')
54 api = frontend(apiobj, options=API_OPTIONS)
55 result = api.reverse((1.3, 0.7))
60 @pytest.mark.parametrize('y,layer,place_id', [(0.7, napi.DataLayer.ADDRESS, 223),
61 (0.70001, napi.DataLayer.POI, 224),
62 (0.7, napi.DataLayer.ADDRESS | napi.DataLayer.POI, 224),
63 (0.70001, napi.DataLayer.ADDRESS | napi.DataLayer.POI, 223),
64 (0.7, napi.DataLayer.MANMADE, 225),
65 (0.7, napi.DataLayer.RAILWAY, 226),
66 (0.7, napi.DataLayer.NATURAL, 227),
67 (0.70003, napi.DataLayer.MANMADE | napi.DataLayer.RAILWAY, 225),
68 (0.70003, napi.DataLayer.MANMADE | napi.DataLayer.NATURAL, 225),
69 (5, napi.DataLayer.ADDRESS, 229)])
70 def test_reverse_rank_30_layers(apiobj, frontend, y, layer, place_id):
71 apiobj.add_placex(place_id=223, class_='place', type='house',
75 centroid=(1.3, 0.70001))
76 apiobj.add_placex(place_id=224, class_='amenity', type='toilet',
80 apiobj.add_placex(place_id=225, class_='man_made', type='tower',
83 centroid=(1.3, 0.70003))
84 apiobj.add_placex(place_id=226, class_='railway', type='station',
87 centroid=(1.3, 0.70004))
88 apiobj.add_placex(place_id=227, class_='natural', type='cave',
91 centroid=(1.3, 0.70005))
92 apiobj.add_placex(place_id=229, class_='place', type='house',
93 name={'addr:housename': 'Old Cottage'},
98 api = frontend(apiobj, options=API_OPTIONS)
99 assert api.reverse((1.3, y), layers=layer).place_id == place_id
102 def test_reverse_poi_layer_with_no_pois(apiobj, frontend):
103 apiobj.add_placex(place_id=223, class_='place', type='house',
107 centroid=(1.3, 0.70001))
109 api = frontend(apiobj, options=API_OPTIONS)
110 assert api.reverse((1.3, 0.70001), max_rank=29,
111 layers=napi.DataLayer.POI) is None
114 def test_reverse_housenumber_on_street(apiobj, frontend):
115 apiobj.add_placex(place_id=990, class_='highway', type='service',
116 rank_search=27, rank_address=27,
117 name = {'name': 'My Street'},
118 centroid=(10.0, 10.0),
119 geometry='LINESTRING(9.995 10, 10.005 10)')
120 apiobj.add_placex(place_id=991, class_='place', type='house',
122 rank_search=30, rank_address=30,
124 centroid=(10.0, 10.00001))
126 api = frontend(apiobj, options=API_OPTIONS)
127 assert api.reverse((10.0, 10.0), max_rank=30).place_id == 991
128 assert api.reverse((10.0, 10.0), max_rank=27).place_id == 990
129 assert api.reverse((10.0, 10.00001), max_rank=30).place_id == 991
132 def test_reverse_housenumber_interpolation(apiobj, frontend):
133 apiobj.add_placex(place_id=990, class_='highway', type='service',
134 rank_search=27, rank_address=27,
135 name = {'name': 'My Street'},
136 centroid=(10.0, 10.0),
137 geometry='LINESTRING(9.995 10, 10.005 10)')
138 apiobj.add_placex(place_id=991, class_='place', type='house',
140 rank_search=30, rank_address=30,
142 centroid=(10.0, 10.00002))
143 apiobj.add_osmline(place_id=992,
145 startnumber=1, endnumber=3, step=1,
146 centroid=(10.0, 10.00001),
147 geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
149 api = frontend(apiobj, options=API_OPTIONS)
150 assert api.reverse((10.0, 10.0)).place_id == 992
153 def test_reverse_housenumber_point_interpolation(apiobj, frontend):
154 apiobj.add_placex(place_id=990, class_='highway', type='service',
155 rank_search=27, rank_address=27,
156 name = {'name': 'My Street'},
157 centroid=(10.0, 10.0),
158 geometry='LINESTRING(9.995 10, 10.005 10)')
159 apiobj.add_osmline(place_id=992,
161 startnumber=42, endnumber=42, step=1,
162 centroid=(10.0, 10.00001),
163 geometry='POINT(10.0 10.00001)')
165 api = frontend(apiobj, options=API_OPTIONS)
166 res = api.reverse((10.0, 10.0))
167 assert res.place_id == 992
168 assert res.housenumber == '42'
171 def test_reverse_tiger_number(apiobj, frontend):
172 apiobj.add_placex(place_id=990, class_='highway', type='service',
173 rank_search=27, rank_address=27,
174 name = {'name': 'My Street'},
175 centroid=(10.0, 10.0),
177 geometry='LINESTRING(9.995 10, 10.005 10)')
178 apiobj.add_tiger(place_id=992,
180 startnumber=1, endnumber=3, step=1,
181 centroid=(10.0, 10.00001),
182 geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
184 api = frontend(apiobj, options=API_OPTIONS)
185 assert api.reverse((10.0, 10.0)).place_id == 992
186 assert api.reverse((10.0, 10.00001)).place_id == 992
189 def test_reverse_point_tiger(apiobj, frontend):
190 apiobj.add_placex(place_id=990, class_='highway', type='service',
191 rank_search=27, rank_address=27,
192 name = {'name': 'My Street'},
193 centroid=(10.0, 10.0),
195 geometry='LINESTRING(9.995 10, 10.005 10)')
196 apiobj.add_tiger(place_id=992,
198 startnumber=1, endnumber=1, step=1,
199 centroid=(10.0, 10.00001),
200 geometry='POINT(10.0 10.00001)')
202 api = frontend(apiobj, options=API_OPTIONS)
203 res = api.reverse((10.0, 10.0))
204 assert res.place_id == 992
205 assert res.housenumber == '1'
208 def test_reverse_low_zoom_address(apiobj, frontend):
209 apiobj.add_placex(place_id=1001, class_='place', type='house',
213 centroid=(59.3, 80.70001))
214 apiobj.add_placex(place_id=1002, class_='place', type='town',
215 name={'name': 'Town'},
218 centroid=(59.3, 80.70001),
219 geometry="""POLYGON((59.3 80.70001, 59.3001 80.70001,
220 59.3001 80.70101, 59.3 80.70101, 59.3 80.70001))""")
222 api = frontend(apiobj, options=API_OPTIONS)
223 assert api.reverse((59.30005, 80.7005)).place_id == 1001
224 assert api.reverse((59.30005, 80.7005), max_rank=18).place_id == 1002
227 def test_reverse_place_node_in_area(apiobj, frontend):
228 apiobj.add_placex(place_id=1002, class_='place', type='town',
229 name={'name': 'Town Area'},
232 centroid=(59.3, 80.70001),
233 geometry="""POLYGON((59.3 80.70001, 59.3001 80.70001,
234 59.3001 80.70101, 59.3 80.70101, 59.3 80.70001))""")
235 apiobj.add_placex(place_id=1003, class_='place', type='suburb',
236 name={'name': 'Suburb Point'},
240 centroid=(59.30004, 80.70055))
242 api = frontend(apiobj, options=API_OPTIONS)
243 assert api.reverse((59.30004, 80.70055)).place_id == 1003
246 @pytest.mark.parametrize('layer,place_id', [(napi.DataLayer.MANMADE, 225),
247 (napi.DataLayer.RAILWAY, 226),
248 (napi.DataLayer.NATURAL, 227),
249 (napi.DataLayer.MANMADE | napi.DataLayer.RAILWAY, 225),
250 (napi.DataLayer.MANMADE | napi.DataLayer.NATURAL, 225)])
251 def test_reverse_larger_area_layers(apiobj, frontend, layer, place_id):
252 apiobj.add_placex(place_id=225, class_='man_made', type='dam',
253 name={'name': 'Dam'},
256 centroid=(1.3, 0.70003))
257 apiobj.add_placex(place_id=226, class_='railway', type='yard',
258 name={'name': 'Dam'},
261 centroid=(1.3, 0.70004))
262 apiobj.add_placex(place_id=227, class_='natural', type='spring',
263 name={'name': 'Dam'},
266 centroid=(1.3, 0.70005))
268 api = frontend(apiobj, options=API_OPTIONS)
269 assert api.reverse((1.3, 0.7), layers=layer).place_id == place_id
272 def test_reverse_country_lookup_no_objects(apiobj, frontend):
273 apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
275 api = frontend(apiobj, options=API_OPTIONS)
276 assert api.reverse((0.5, 0.5)) is None
279 @pytest.mark.parametrize('rank', [4, 30])
280 def test_reverse_country_lookup_country_only(apiobj, frontend, rank):
281 apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
282 apiobj.add_placex(place_id=225, class_='place', type='country',
283 name={'name': 'My Country'},
289 api = frontend(apiobj, options=API_OPTIONS)
290 assert api.reverse((0.5, 0.5), max_rank=rank).place_id == 225
293 def test_reverse_country_lookup_place_node_inside(apiobj, frontend):
294 apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
295 apiobj.add_placex(place_id=225, class_='place', type='state',
297 name={'name': 'My State'},
301 centroid=(0.5, 0.505))
303 api = frontend(apiobj, options=API_OPTIONS)
304 assert api.reverse((0.5, 0.5)).place_id == 225
307 @pytest.mark.parametrize('gtype', list(napi.GeometryFormat))
308 def test_reverse_geometry_output_placex(apiobj, frontend, gtype):
309 apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
310 apiobj.add_placex(place_id=1001, class_='place', type='house',
314 centroid=(59.3, 80.70001))
315 apiobj.add_placex(place_id=1003, class_='place', type='suburb',
316 name={'name': 'Suburb Point'},
323 api = frontend(apiobj, options=API_OPTIONS)
324 assert api.reverse((59.3, 80.70001), geometry_output=gtype).place_id == 1001
325 assert api.reverse((0.5, 0.5), geometry_output=gtype).place_id == 1003
328 def test_reverse_simplified_geometry(apiobj, frontend):
329 apiobj.add_placex(place_id=1001, class_='place', type='house',
333 centroid=(59.3, 80.70001))
335 api = frontend(apiobj, options=API_OPTIONS)
336 details = dict(geometry_output=napi.GeometryFormat.GEOJSON,
337 geometry_simplification=0.1)
338 assert api.reverse((59.3, 80.70001), **details).place_id == 1001
341 def test_reverse_interpolation_geometry(apiobj, frontend):
342 apiobj.add_osmline(place_id=992,
344 startnumber=1, endnumber=3, step=1,
345 centroid=(10.0, 10.00001),
346 geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
348 api = frontend(apiobj, options=API_OPTIONS)
349 assert api.reverse((10.0, 10.0), geometry_output=napi.GeometryFormat.TEXT)\
350 .geometry['text'] == 'POINT(10 10.00001)'
353 def test_reverse_tiger_geometry(apiobj, frontend):
354 apiobj.add_placex(place_id=990, class_='highway', type='service',
355 rank_search=27, rank_address=27,
356 name = {'name': 'My Street'},
357 centroid=(10.0, 10.0),
359 geometry='LINESTRING(9.995 10, 10.005 10)')
360 apiobj.add_tiger(place_id=992,
362 startnumber=1, endnumber=3, step=1,
363 centroid=(10.0, 10.00001),
364 geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
366 api = frontend(apiobj, options=API_OPTIONS)
367 output = api.reverse((10.0, 10.0),
368 geometry_output=napi.GeometryFormat.GEOJSON).geometry['geojson']
370 assert json.loads(output) == {'coordinates': [10, 10.00001], 'type': 'Point'}