]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_api_reverse.py
add tests for interaction of category parameter with category terms
[nominatim.git] / test / python / api / test_api_reverse.py
1 # SPDX-License-Identifier: GPL-3.0-or-later
2 #
3 # This file is part of Nominatim. (https://nominatim.org)
4 #
5 # Copyright (C) 2023 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Tests for reverse API call.
9
10 These tests make sure that all Python code is correct and executable.
11 Functional tests can be found in the BDD test suite.
12 """
13 import json
14
15 import pytest
16
17 import nominatim.api as napi
18
19 API_OPTIONS = {'reverse'}
20
21 def test_reverse_rank_30(apiobj, frontend):
22     apiobj.add_placex(place_id=223, class_='place', type='house',
23                       housenumber='1',
24                       centroid=(1.3, 0.7),
25                       geometry='POINT(1.3 0.7)')
26
27     api = frontend(apiobj, options=API_OPTIONS)
28     result = api.reverse((1.3, 0.7))
29
30     assert result is not None
31     assert result.place_id == 223
32
33
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),
40                       country_code=country,
41                       geometry='LINESTRING(9.995 10, 10.005 10)')
42
43     api = frontend(apiobj, options=API_OPTIONS)
44     assert api.reverse((9.995, 10)).place_id == 990
45
46
47 def test_reverse_ignore_unindexed(apiobj, frontend):
48     apiobj.add_placex(place_id=223, class_='place', type='house',
49                       housenumber='1',
50                       indexed_status=2,
51                       centroid=(1.3, 0.7),
52                       geometry='POINT(1.3 0.7)')
53
54     api = frontend(apiobj, options=API_OPTIONS)
55     result = api.reverse((1.3, 0.7))
56
57     assert result is None
58
59
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',
72                       housenumber='1',
73                       rank_address=30,
74                       rank_search=30,
75                       centroid=(1.3, 0.70001))
76     apiobj.add_placex(place_id=224, class_='amenity', type='toilet',
77                       rank_address=30,
78                       rank_search=30,
79                       centroid=(1.3, 0.7))
80     apiobj.add_placex(place_id=225, class_='man_made', type='tower',
81                       rank_address=0,
82                       rank_search=30,
83                       centroid=(1.3, 0.70003))
84     apiobj.add_placex(place_id=226, class_='railway', type='station',
85                       rank_address=0,
86                       rank_search=30,
87                       centroid=(1.3, 0.70004))
88     apiobj.add_placex(place_id=227, class_='natural', type='cave',
89                       rank_address=0,
90                       rank_search=30,
91                       centroid=(1.3, 0.70005))
92     apiobj.add_placex(place_id=229, class_='place', type='house',
93                       name={'addr:housename': 'Old Cottage'},
94                       rank_address=30,
95                       rank_search=30,
96                       centroid=(1.3, 5))
97
98     api = frontend(apiobj, options=API_OPTIONS)
99     assert api.reverse((1.3, y), layers=layer).place_id == place_id
100
101
102 def test_reverse_poi_layer_with_no_pois(apiobj, frontend):
103     apiobj.add_placex(place_id=223, class_='place', type='house',
104                       housenumber='1',
105                       rank_address=30,
106                       rank_search=30,
107                       centroid=(1.3, 0.70001))
108
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
112
113
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',
121                       parent_place_id=990,
122                       rank_search=30, rank_address=30,
123                       housenumber='23',
124                       centroid=(10.0, 10.00001))
125
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
130
131
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',
139                       parent_place_id=990,
140                       rank_search=30, rank_address=30,
141                       housenumber='23',
142                       centroid=(10.0, 10.00002))
143     apiobj.add_osmline(place_id=992,
144                        parent_place_id=990,
145                        startnumber=1, endnumber=3, step=1,
146                        centroid=(10.0, 10.00001),
147                        geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
148
149     api = frontend(apiobj, options=API_OPTIONS)
150     assert api.reverse((10.0, 10.0)).place_id == 992
151
152
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,
160                        parent_place_id=990,
161                        startnumber=42, endnumber=42, step=1,
162                        centroid=(10.0, 10.00001),
163                        geometry='POINT(10.0 10.00001)')
164
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'
169
170
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),
176                       country_code='us',
177                       geometry='LINESTRING(9.995 10, 10.005 10)')
178     apiobj.add_tiger(place_id=992,
179                      parent_place_id=990,
180                      startnumber=1, endnumber=3, step=1,
181                      centroid=(10.0, 10.00001),
182                      geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
183
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
187
188
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),
194                       country_code='us',
195                       geometry='LINESTRING(9.995 10, 10.005 10)')
196     apiobj.add_tiger(place_id=992,
197                      parent_place_id=990,
198                      startnumber=1, endnumber=1, step=1,
199                      centroid=(10.0, 10.00001),
200                      geometry='POINT(10.0 10.00001)')
201
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'
206
207
208 def test_reverse_low_zoom_address(apiobj, frontend):
209     apiobj.add_placex(place_id=1001, class_='place', type='house',
210                       housenumber='1',
211                       rank_address=30,
212                       rank_search=30,
213                       centroid=(59.3, 80.70001))
214     apiobj.add_placex(place_id=1002, class_='place', type='town',
215                       name={'name': 'Town'},
216                       rank_address=16,
217                       rank_search=16,
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))""")
221
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
225
226
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'},
230                       rank_address=16,
231                       rank_search=16,
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'},
237                       osm_type='N',
238                       rank_address=18,
239                       rank_search=18,
240                       centroid=(59.30004, 80.70055))
241
242     api = frontend(apiobj, options=API_OPTIONS)
243     assert api.reverse((59.30004, 80.70055)).place_id == 1003
244
245
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'},
254                       rank_address=0,
255                       rank_search=25,
256                       centroid=(1.3, 0.70003))
257     apiobj.add_placex(place_id=226, class_='railway', type='yard',
258                       name={'name': 'Dam'},
259                       rank_address=0,
260                       rank_search=20,
261                       centroid=(1.3, 0.70004))
262     apiobj.add_placex(place_id=227, class_='natural', type='spring',
263                       name={'name': 'Dam'},
264                       rank_address=0,
265                       rank_search=16,
266                       centroid=(1.3, 0.70005))
267
268     api = frontend(apiobj, options=API_OPTIONS)
269     assert api.reverse((1.3, 0.7), layers=layer).place_id == place_id
270
271
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))')
274
275     api = frontend(apiobj, options=API_OPTIONS)
276     assert api.reverse((0.5, 0.5)) is None
277
278
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'},
284                       rank_address=4,
285                       rank_search=4,
286                       country_code='xx',
287                       centroid=(0.7, 0.7))
288
289     api = frontend(apiobj, options=API_OPTIONS)
290     assert api.reverse((0.5, 0.5), max_rank=rank).place_id == 225
291
292
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',
296                       osm_type='N',
297                       name={'name': 'My State'},
298                       rank_address=6,
299                       rank_search=6,
300                       country_code='xx',
301                       centroid=(0.5, 0.505))
302
303     api = frontend(apiobj, options=API_OPTIONS)
304     assert api.reverse((0.5, 0.5)).place_id == 225
305
306
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',
311                       housenumber='1',
312                       rank_address=30,
313                       rank_search=30,
314                       centroid=(59.3, 80.70001))
315     apiobj.add_placex(place_id=1003, class_='place', type='suburb',
316                       name={'name': 'Suburb Point'},
317                       osm_type='N',
318                       rank_address=18,
319                       rank_search=18,
320                       country_code='xx',
321                       centroid=(0.5, 0.5))
322
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
326
327
328 def test_reverse_simplified_geometry(apiobj, frontend):
329     apiobj.add_placex(place_id=1001, class_='place', type='house',
330                       housenumber='1',
331                       rank_address=30,
332                       rank_search=30,
333                       centroid=(59.3, 80.70001))
334
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
339
340
341 def test_reverse_interpolation_geometry(apiobj, frontend):
342     apiobj.add_osmline(place_id=992,
343                        parent_place_id=990,
344                        startnumber=1, endnumber=3, step=1,
345                        centroid=(10.0, 10.00001),
346                        geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
347
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)'
351
352
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),
358                       country_code='us',
359                       geometry='LINESTRING(9.995 10, 10.005 10)')
360     apiobj.add_tiger(place_id=992,
361                      parent_place_id=990,
362                      startnumber=1, endnumber=3, step=1,
363                      centroid=(10.0, 10.00001),
364                      geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
365
366     api = frontend(apiobj, options=API_OPTIONS)
367     output = api.reverse((10.0, 10.0),
368                                 geometry_output=napi.GeometryFormat.GEOJSON).geometry['geojson']
369
370     assert json.loads(output) == {'coordinates': [10, 10.00001], 'type': 'Point'}
371