]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_api_reverse.py
add support for search by houenumber
[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 def test_reverse_rank_30_layers(apiobj, y, layer, place_id):
65     apiobj.add_placex(place_id=223, class_='place', type='house',
66                       housenumber='1',
67                       rank_address=30,
68                       rank_search=30,
69                       centroid=(1.3, 0.70001))
70     apiobj.add_placex(place_id=224, class_='amenity', type='toilet',
71                       rank_address=30,
72                       rank_search=30,
73                       centroid=(1.3, 0.7))
74     apiobj.add_placex(place_id=225, class_='man_made', type='tower',
75                       rank_address=0,
76                       rank_search=30,
77                       centroid=(1.3, 0.70003))
78     apiobj.add_placex(place_id=226, class_='railway', type='station',
79                       rank_address=0,
80                       rank_search=30,
81                       centroid=(1.3, 0.70004))
82     apiobj.add_placex(place_id=227, class_='natural', type='cave',
83                       rank_address=0,
84                       rank_search=30,
85                       centroid=(1.3, 0.70005))
86
87     assert apiobj.api.reverse((1.3, y), layers=layer).place_id == place_id
88
89
90 def test_reverse_poi_layer_with_no_pois(apiobj):
91     apiobj.add_placex(place_id=223, class_='place', type='house',
92                       housenumber='1',
93                       rank_address=30,
94                       rank_search=30,
95                       centroid=(1.3, 0.70001))
96
97     assert apiobj.api.reverse((1.3, 0.70001), max_rank=29,
98                               layers=napi.DataLayer.POI) is None
99
100
101 def test_reverse_housenumber_on_street(apiobj):
102     apiobj.add_placex(place_id=990, class_='highway', type='service',
103                       rank_search=27, rank_address=27,
104                       name = {'name': 'My Street'},
105                       centroid=(10.0, 10.0),
106                       geometry='LINESTRING(9.995 10, 10.005 10)')
107     apiobj.add_placex(place_id=991, class_='place', type='house',
108                       parent_place_id=990,
109                       rank_search=30, rank_address=30,
110                       housenumber='23',
111                       centroid=(10.0, 10.00001))
112
113     assert apiobj.api.reverse((10.0, 10.0), max_rank=30).place_id == 991
114     assert apiobj.api.reverse((10.0, 10.0), max_rank=27).place_id == 990
115     assert apiobj.api.reverse((10.0, 10.00001), max_rank=30).place_id == 991
116
117
118 def test_reverse_housenumber_interpolation(apiobj):
119     apiobj.add_placex(place_id=990, class_='highway', type='service',
120                       rank_search=27, rank_address=27,
121                       name = {'name': 'My Street'},
122                       centroid=(10.0, 10.0),
123                       geometry='LINESTRING(9.995 10, 10.005 10)')
124     apiobj.add_placex(place_id=991, class_='place', type='house',
125                       parent_place_id=990,
126                       rank_search=30, rank_address=30,
127                       housenumber='23',
128                       centroid=(10.0, 10.00002))
129     apiobj.add_osmline(place_id=992,
130                        parent_place_id=990,
131                        startnumber=1, endnumber=3, step=1,
132                        centroid=(10.0, 10.00001),
133                        geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
134
135     assert apiobj.api.reverse((10.0, 10.0)).place_id == 992
136
137
138 def test_reverse_housenumber_point_interpolation(apiobj):
139     apiobj.add_placex(place_id=990, class_='highway', type='service',
140                       rank_search=27, rank_address=27,
141                       name = {'name': 'My Street'},
142                       centroid=(10.0, 10.0),
143                       geometry='LINESTRING(9.995 10, 10.005 10)')
144     apiobj.add_osmline(place_id=992,
145                        parent_place_id=990,
146                        startnumber=42, endnumber=42, step=1,
147                        centroid=(10.0, 10.00001),
148                        geometry='POINT(10.0 10.00001)')
149
150     res = apiobj.api.reverse((10.0, 10.0))
151     assert res.place_id == 992
152     assert res.housenumber == '42'
153
154
155 def test_reverse_tiger_number(apiobj):
156     apiobj.add_placex(place_id=990, class_='highway', type='service',
157                       rank_search=27, rank_address=27,
158                       name = {'name': 'My Street'},
159                       centroid=(10.0, 10.0),
160                       country_code='us',
161                       geometry='LINESTRING(9.995 10, 10.005 10)')
162     apiobj.add_tiger(place_id=992,
163                      parent_place_id=990,
164                      startnumber=1, endnumber=3, step=1,
165                      centroid=(10.0, 10.00001),
166                      geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
167
168     assert apiobj.api.reverse((10.0, 10.0)).place_id == 992
169     assert apiobj.api.reverse((10.0, 10.00001)).place_id == 992
170
171
172 def test_reverse_point_tiger(apiobj):
173     apiobj.add_placex(place_id=990, class_='highway', type='service',
174                       rank_search=27, rank_address=27,
175                       name = {'name': 'My Street'},
176                       centroid=(10.0, 10.0),
177                       country_code='us',
178                       geometry='LINESTRING(9.995 10, 10.005 10)')
179     apiobj.add_tiger(place_id=992,
180                      parent_place_id=990,
181                      startnumber=1, endnumber=1, step=1,
182                      centroid=(10.0, 10.00001),
183                      geometry='POINT(10.0 10.00001)')
184
185     res = apiobj.api.reverse((10.0, 10.0))
186     assert res.place_id == 992
187     assert res.housenumber == '1'
188
189
190 def test_reverse_low_zoom_address(apiobj):
191     apiobj.add_placex(place_id=1001, class_='place', type='house',
192                       housenumber='1',
193                       rank_address=30,
194                       rank_search=30,
195                       centroid=(59.3, 80.70001))
196     apiobj.add_placex(place_id=1002, class_='place', type='town',
197                       name={'name': 'Town'},
198                       rank_address=16,
199                       rank_search=16,
200                       centroid=(59.3, 80.70001),
201                       geometry="""POLYGON((59.3 80.70001, 59.3001 80.70001,
202                                         59.3001 80.70101, 59.3 80.70101, 59.3 80.70001))""")
203
204     assert apiobj.api.reverse((59.30005, 80.7005)).place_id == 1001
205     assert apiobj.api.reverse((59.30005, 80.7005), max_rank=18).place_id == 1002
206
207
208 def test_reverse_place_node_in_area(apiobj):
209     apiobj.add_placex(place_id=1002, class_='place', type='town',
210                       name={'name': 'Town Area'},
211                       rank_address=16,
212                       rank_search=16,
213                       centroid=(59.3, 80.70001),
214                       geometry="""POLYGON((59.3 80.70001, 59.3001 80.70001,
215                                         59.3001 80.70101, 59.3 80.70101, 59.3 80.70001))""")
216     apiobj.add_placex(place_id=1003, class_='place', type='suburb',
217                       name={'name': 'Suburb Point'},
218                       osm_type='N',
219                       rank_address=18,
220                       rank_search=18,
221                       centroid=(59.30004, 80.70055))
222
223     assert apiobj.api.reverse((59.30004, 80.70055)).place_id == 1003
224
225
226 @pytest.mark.parametrize('layer,place_id', [(napi.DataLayer.MANMADE, 225),
227                                             (napi.DataLayer.RAILWAY, 226),
228                                             (napi.DataLayer.NATURAL, 227),
229                                             (napi.DataLayer.MANMADE | napi.DataLayer.RAILWAY, 225),
230                                             (napi.DataLayer.MANMADE | napi.DataLayer.NATURAL, 225)])
231 def test_reverse_larger_area_layers(apiobj, layer, place_id):
232     apiobj.add_placex(place_id=225, class_='man_made', type='dam',
233                       name={'name': 'Dam'},
234                       rank_address=0,
235                       rank_search=25,
236                       centroid=(1.3, 0.70003))
237     apiobj.add_placex(place_id=226, class_='railway', type='yard',
238                       name={'name': 'Dam'},
239                       rank_address=0,
240                       rank_search=20,
241                       centroid=(1.3, 0.70004))
242     apiobj.add_placex(place_id=227, class_='natural', type='spring',
243                       name={'name': 'Dam'},
244                       rank_address=0,
245                       rank_search=16,
246                       centroid=(1.3, 0.70005))
247
248     assert apiobj.api.reverse((1.3, 0.7), layers=layer).place_id == place_id
249
250
251 def test_reverse_country_lookup_no_objects(apiobj):
252     apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
253
254     assert apiobj.api.reverse((0.5, 0.5)) is None
255
256
257 @pytest.mark.parametrize('rank', [4, 30])
258 def test_reverse_country_lookup_country_only(apiobj, rank):
259     apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
260     apiobj.add_placex(place_id=225, class_='place', type='country',
261                       name={'name': 'My Country'},
262                       rank_address=4,
263                       rank_search=4,
264                       country_code='xx',
265                       centroid=(0.7, 0.7))
266
267     assert apiobj.api.reverse((0.5, 0.5), max_rank=rank).place_id == 225
268
269
270 def test_reverse_country_lookup_place_node_inside(apiobj):
271     apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
272     apiobj.add_placex(place_id=225, class_='place', type='state',
273                       osm_type='N',
274                       name={'name': 'My State'},
275                       rank_address=6,
276                       rank_search=6,
277                       country_code='xx',
278                       centroid=(0.5, 0.505))
279
280     assert apiobj.api.reverse((0.5, 0.5)).place_id == 225
281
282
283 @pytest.mark.parametrize('gtype', list(napi.GeometryFormat))
284 def test_reverse_geometry_output_placex(apiobj, gtype):
285     apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
286     apiobj.add_placex(place_id=1001, class_='place', type='house',
287                       housenumber='1',
288                       rank_address=30,
289                       rank_search=30,
290                       centroid=(59.3, 80.70001))
291     apiobj.add_placex(place_id=1003, class_='place', type='suburb',
292                       name={'name': 'Suburb Point'},
293                       osm_type='N',
294                       rank_address=18,
295                       rank_search=18,
296                       country_code='xx',
297                       centroid=(0.5, 0.5))
298
299     assert apiobj.api.reverse((59.3, 80.70001), geometry_output=gtype).place_id == 1001
300     assert apiobj.api.reverse((0.5, 0.5), geometry_output=gtype).place_id == 1003
301
302
303 def test_reverse_simplified_geometry(apiobj):
304     apiobj.add_placex(place_id=1001, class_='place', type='house',
305                       housenumber='1',
306                       rank_address=30,
307                       rank_search=30,
308                       centroid=(59.3, 80.70001))
309
310     details = dict(geometry_output=napi.GeometryFormat.GEOJSON,
311                    geometry_simplification=0.1)
312     assert apiobj.api.reverse((59.3, 80.70001), **details).place_id == 1001
313
314
315 def test_reverse_interpolation_geometry(apiobj):
316     apiobj.add_osmline(place_id=992,
317                        parent_place_id=990,
318                        startnumber=1, endnumber=3, step=1,
319                        centroid=(10.0, 10.00001),
320                        geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
321
322     assert apiobj.api.reverse((10.0, 10.0), geometry_output=napi.GeometryFormat.TEXT)\
323                      .geometry['text'] == 'POINT(10 10.00001)'
324
325
326 def test_reverse_tiger_geometry(apiobj):
327     apiobj.add_placex(place_id=990, class_='highway', type='service',
328                       rank_search=27, rank_address=27,
329                       name = {'name': 'My Street'},
330                       centroid=(10.0, 10.0),
331                       country_code='us',
332                       geometry='LINESTRING(9.995 10, 10.005 10)')
333     apiobj.add_tiger(place_id=992,
334                      parent_place_id=990,
335                      startnumber=1, endnumber=3, step=1,
336                      centroid=(10.0, 10.00001),
337                      geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
338
339     output = apiobj.api.reverse((10.0, 10.0),
340                                 geometry_output=napi.GeometryFormat.GEOJSON).geometry['geojson']
341
342     assert json.loads(output) == {'coordinates': [10, 10.00001], 'type': 'Point'}
343