]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_api_reverse.py
Merge remote-tracking branch 'upstream/master'
[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 def test_reverse_rank_30(apiobj):
20     apiobj.add_placex(place_id=223, class_='place', type='house',
21                       housenumber='1',
22                       centroid=(1.3, 0.7),
23                       geometry='POINT(1.3 0.7)')
24
25     result = apiobj.api.reverse((1.3, 0.7))
26
27     assert result is not None
28     assert result.place_id == 223
29
30
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),
37                       country_code=country,
38                       geometry='LINESTRING(9.995 10, 10.005 10)')
39
40     assert apiobj.api.reverse((9.995, 10)).place_id == 990
41
42
43 def test_reverse_ignore_unindexed(apiobj):
44     apiobj.add_placex(place_id=223, class_='place', type='house',
45                       housenumber='1',
46                       indexed_status=2,
47                       centroid=(1.3, 0.7),
48                       geometry='POINT(1.3 0.7)')
49
50     result = apiobj.api.reverse((1.3, 0.7))
51
52     assert result is None
53
54
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',
67                       housenumber='1',
68                       rank_address=30,
69                       rank_search=30,
70                       centroid=(1.3, 0.70001))
71     apiobj.add_placex(place_id=224, class_='amenity', type='toilet',
72                       rank_address=30,
73                       rank_search=30,
74                       centroid=(1.3, 0.7))
75     apiobj.add_placex(place_id=225, class_='man_made', type='tower',
76                       rank_address=0,
77                       rank_search=30,
78                       centroid=(1.3, 0.70003))
79     apiobj.add_placex(place_id=226, class_='railway', type='station',
80                       rank_address=0,
81                       rank_search=30,
82                       centroid=(1.3, 0.70004))
83     apiobj.add_placex(place_id=227, class_='natural', type='cave',
84                       rank_address=0,
85                       rank_search=30,
86                       centroid=(1.3, 0.70005))
87     apiobj.add_placex(place_id=229, class_='place', type='house',
88                       name={'addr:housename': 'Old Cottage'},
89                       rank_address=30,
90                       rank_search=30,
91                       centroid=(1.3, 5))
92
93     assert apiobj.api.reverse((1.3, y), layers=layer).place_id == place_id
94
95
96 def test_reverse_poi_layer_with_no_pois(apiobj):
97     apiobj.add_placex(place_id=223, class_='place', type='house',
98                       housenumber='1',
99                       rank_address=30,
100                       rank_search=30,
101                       centroid=(1.3, 0.70001))
102
103     assert apiobj.api.reverse((1.3, 0.70001), max_rank=29,
104                               layers=napi.DataLayer.POI) is None
105
106
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',
114                       parent_place_id=990,
115                       rank_search=30, rank_address=30,
116                       housenumber='23',
117                       centroid=(10.0, 10.00001))
118
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
122
123
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',
131                       parent_place_id=990,
132                       rank_search=30, rank_address=30,
133                       housenumber='23',
134                       centroid=(10.0, 10.00002))
135     apiobj.add_osmline(place_id=992,
136                        parent_place_id=990,
137                        startnumber=1, endnumber=3, step=1,
138                        centroid=(10.0, 10.00001),
139                        geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
140
141     assert apiobj.api.reverse((10.0, 10.0)).place_id == 992
142
143
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,
151                        parent_place_id=990,
152                        startnumber=42, endnumber=42, step=1,
153                        centroid=(10.0, 10.00001),
154                        geometry='POINT(10.0 10.00001)')
155
156     res = apiobj.api.reverse((10.0, 10.0))
157     assert res.place_id == 992
158     assert res.housenumber == '42'
159
160
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),
166                       country_code='us',
167                       geometry='LINESTRING(9.995 10, 10.005 10)')
168     apiobj.add_tiger(place_id=992,
169                      parent_place_id=990,
170                      startnumber=1, endnumber=3, step=1,
171                      centroid=(10.0, 10.00001),
172                      geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
173
174     assert apiobj.api.reverse((10.0, 10.0)).place_id == 992
175     assert apiobj.api.reverse((10.0, 10.00001)).place_id == 992
176
177
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),
183                       country_code='us',
184                       geometry='LINESTRING(9.995 10, 10.005 10)')
185     apiobj.add_tiger(place_id=992,
186                      parent_place_id=990,
187                      startnumber=1, endnumber=1, step=1,
188                      centroid=(10.0, 10.00001),
189                      geometry='POINT(10.0 10.00001)')
190
191     res = apiobj.api.reverse((10.0, 10.0))
192     assert res.place_id == 992
193     assert res.housenumber == '1'
194
195
196 def test_reverse_low_zoom_address(apiobj):
197     apiobj.add_placex(place_id=1001, class_='place', type='house',
198                       housenumber='1',
199                       rank_address=30,
200                       rank_search=30,
201                       centroid=(59.3, 80.70001))
202     apiobj.add_placex(place_id=1002, class_='place', type='town',
203                       name={'name': 'Town'},
204                       rank_address=16,
205                       rank_search=16,
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))""")
209
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
212
213
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'},
217                       rank_address=16,
218                       rank_search=16,
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'},
224                       osm_type='N',
225                       rank_address=18,
226                       rank_search=18,
227                       centroid=(59.30004, 80.70055))
228
229     assert apiobj.api.reverse((59.30004, 80.70055)).place_id == 1003
230
231
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'},
240                       rank_address=0,
241                       rank_search=25,
242                       centroid=(1.3, 0.70003))
243     apiobj.add_placex(place_id=226, class_='railway', type='yard',
244                       name={'name': 'Dam'},
245                       rank_address=0,
246                       rank_search=20,
247                       centroid=(1.3, 0.70004))
248     apiobj.add_placex(place_id=227, class_='natural', type='spring',
249                       name={'name': 'Dam'},
250                       rank_address=0,
251                       rank_search=16,
252                       centroid=(1.3, 0.70005))
253
254     assert apiobj.api.reverse((1.3, 0.7), layers=layer).place_id == place_id
255
256
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))')
259
260     assert apiobj.api.reverse((0.5, 0.5)) is None
261
262
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'},
268                       rank_address=4,
269                       rank_search=4,
270                       country_code='xx',
271                       centroid=(0.7, 0.7))
272
273     assert apiobj.api.reverse((0.5, 0.5), max_rank=rank).place_id == 225
274
275
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',
279                       osm_type='N',
280                       name={'name': 'My State'},
281                       rank_address=6,
282                       rank_search=6,
283                       country_code='xx',
284                       centroid=(0.5, 0.505))
285
286     assert apiobj.api.reverse((0.5, 0.5)).place_id == 225
287
288
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',
293                       housenumber='1',
294                       rank_address=30,
295                       rank_search=30,
296                       centroid=(59.3, 80.70001))
297     apiobj.add_placex(place_id=1003, class_='place', type='suburb',
298                       name={'name': 'Suburb Point'},
299                       osm_type='N',
300                       rank_address=18,
301                       rank_search=18,
302                       country_code='xx',
303                       centroid=(0.5, 0.5))
304
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
307
308
309 def test_reverse_simplified_geometry(apiobj):
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
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
319
320
321 def test_reverse_interpolation_geometry(apiobj):
322     apiobj.add_osmline(place_id=992,
323                        parent_place_id=990,
324                        startnumber=1, endnumber=3, step=1,
325                        centroid=(10.0, 10.00001),
326                        geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
327
328     assert apiobj.api.reverse((10.0, 10.0), geometry_output=napi.GeometryFormat.TEXT)\
329                      .geometry['text'] == 'POINT(10 10.00001)'
330
331
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),
337                       country_code='us',
338                       geometry='LINESTRING(9.995 10, 10.005 10)')
339     apiobj.add_tiger(place_id=992,
340                      parent_place_id=990,
341                      startnumber=1, endnumber=3, step=1,
342                      centroid=(10.0, 10.00001),
343                      geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
344
345     output = apiobj.api.reverse((10.0, 10.0),
346                                 geometry_output=napi.GeometryFormat.GEOJSON).geometry['geojson']
347
348     assert json.loads(output) == {'coordinates': [10, 10.00001], 'type': 'Point'}
349