]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_api_reverse.py
python: implement reverse lookup function
[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), layer=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                               layer=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_tiger_number(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                       country_code='us',
144                       geometry='LINESTRING(9.995 10, 10.005 10)')
145     apiobj.add_tiger(place_id=992,
146                      parent_place_id=990,
147                      startnumber=1, endnumber=3, step=1,
148                      centroid=(10.0, 10.00001),
149                      geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
150
151     assert apiobj.api.reverse((10.0, 10.0)).place_id == 992
152     assert apiobj.api.reverse((10.0, 10.00001)).place_id == 992
153
154
155 def test_reverse_low_zoom_address(apiobj):
156     apiobj.add_placex(place_id=1001, class_='place', type='house',
157                       housenumber='1',
158                       rank_address=30,
159                       rank_search=30,
160                       centroid=(59.3, 80.70001))
161     apiobj.add_placex(place_id=1002, class_='place', type='town',
162                       name={'name': 'Town'},
163                       rank_address=16,
164                       rank_search=16,
165                       centroid=(59.3, 80.70001),
166                       geometry="""POLYGON((59.3 80.70001, 59.3001 80.70001,
167                                         59.3001 80.70101, 59.3 80.70101, 59.3 80.70001))""")
168
169     assert apiobj.api.reverse((59.30005, 80.7005)).place_id == 1001
170     assert apiobj.api.reverse((59.30005, 80.7005), max_rank=18).place_id == 1002
171
172
173 def test_reverse_place_node_in_area(apiobj):
174     apiobj.add_placex(place_id=1002, class_='place', type='town',
175                       name={'name': 'Town Area'},
176                       rank_address=16,
177                       rank_search=16,
178                       centroid=(59.3, 80.70001),
179                       geometry="""POLYGON((59.3 80.70001, 59.3001 80.70001,
180                                         59.3001 80.70101, 59.3 80.70101, 59.3 80.70001))""")
181     apiobj.add_placex(place_id=1003, class_='place', type='suburb',
182                       name={'name': 'Suburb Point'},
183                       osm_type='N',
184                       rank_address=18,
185                       rank_search=18,
186                       centroid=(59.30004, 80.70055))
187
188     assert apiobj.api.reverse((59.30004, 80.70055)).place_id == 1003
189
190
191 @pytest.mark.parametrize('layer,place_id', [(napi.DataLayer.MANMADE, 225),
192                                             (napi.DataLayer.RAILWAY, 226),
193                                             (napi.DataLayer.NATURAL, 227),
194                                             (napi.DataLayer.MANMADE | napi.DataLayer.RAILWAY, 225),
195                                             (napi.DataLayer.MANMADE | napi.DataLayer.NATURAL, 225)])
196 def test_reverse_larger_area_layers(apiobj, layer, place_id):
197     apiobj.add_placex(place_id=225, class_='man_made', type='dam',
198                       name={'name': 'Dam'},
199                       rank_address=0,
200                       rank_search=25,
201                       centroid=(1.3, 0.70003))
202     apiobj.add_placex(place_id=226, class_='railway', type='yard',
203                       name={'name': 'Dam'},
204                       rank_address=0,
205                       rank_search=20,
206                       centroid=(1.3, 0.70004))
207     apiobj.add_placex(place_id=227, class_='natural', type='spring',
208                       name={'name': 'Dam'},
209                       rank_address=0,
210                       rank_search=16,
211                       centroid=(1.3, 0.70005))
212
213     assert apiobj.api.reverse((1.3, 0.7), layer=layer).place_id == place_id
214
215
216 def test_reverse_country_lookup_no_objects(apiobj):
217     apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
218
219     assert apiobj.api.reverse((0.5, 0.5)) is None
220
221
222 @pytest.mark.parametrize('rank', [4, 30])
223 def test_reverse_country_lookup_country_only(apiobj, rank):
224     apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
225     apiobj.add_placex(place_id=225, class_='place', type='country',
226                       name={'name': 'My Country'},
227                       rank_address=4,
228                       rank_search=4,
229                       country_code='xx',
230                       centroid=(0.7, 0.7))
231
232     assert apiobj.api.reverse((0.5, 0.5), max_rank=rank).place_id == 225
233
234
235 def test_reverse_country_lookup_place_node_inside(apiobj):
236     apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
237     apiobj.add_placex(place_id=225, class_='place', type='state',
238                       osm_type='N',
239                       name={'name': 'My State'},
240                       rank_address=6,
241                       rank_search=6,
242                       country_code='xx',
243                       centroid=(0.5, 0.505))
244
245     assert apiobj.api.reverse((0.5, 0.5)).place_id == 225
246
247
248 @pytest.mark.parametrize('gtype', list(napi.GeometryFormat))
249 def test_reverse_geometry_output_placex(apiobj, gtype):
250     apiobj.add_country('xx', 'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
251     apiobj.add_placex(place_id=1001, class_='place', type='house',
252                       housenumber='1',
253                       rank_address=30,
254                       rank_search=30,
255                       centroid=(59.3, 80.70001))
256     apiobj.add_placex(place_id=1003, class_='place', type='suburb',
257                       name={'name': 'Suburb Point'},
258                       osm_type='N',
259                       rank_address=18,
260                       rank_search=18,
261                       country_code='xx',
262                       centroid=(0.5, 0.5))
263
264     details = napi.LookupDetails(geometry_output=gtype)
265
266     assert apiobj.api.reverse((59.3, 80.70001), details=details).place_id == 1001
267     assert apiobj.api.reverse((0.5, 0.5), details=details).place_id == 1003
268
269
270 def test_reverse_simplified_geometry(apiobj):
271     apiobj.add_placex(place_id=1001, class_='place', type='house',
272                       housenumber='1',
273                       rank_address=30,
274                       rank_search=30,
275                       centroid=(59.3, 80.70001))
276
277     details = napi.LookupDetails(geometry_output=napi.GeometryFormat.GEOJSON,
278                                  geometry_simplification=0.1)
279     assert apiobj.api.reverse((59.3, 80.70001), details=details).place_id == 1001
280
281
282 def test_reverse_interpolation_geometry(apiobj):
283     apiobj.add_osmline(place_id=992,
284                        parent_place_id=990,
285                        startnumber=1, endnumber=3, step=1,
286                        centroid=(10.0, 10.00001),
287                        geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
288
289     details = napi.LookupDetails(geometry_output=napi.GeometryFormat.TEXT)
290     assert apiobj.api.reverse((10.0, 10.0), details=details)\
291                      .geometry['text'] == 'POINT(10 10.00001)'
292
293
294 def test_reverse_tiger_geometry(apiobj):
295     apiobj.add_placex(place_id=990, class_='highway', type='service',
296                       rank_search=27, rank_address=27,
297                       name = {'name': 'My Street'},
298                       centroid=(10.0, 10.0),
299                       country_code='us',
300                       geometry='LINESTRING(9.995 10, 10.005 10)')
301     apiobj.add_tiger(place_id=992,
302                      parent_place_id=990,
303                      startnumber=1, endnumber=3, step=1,
304                      centroid=(10.0, 10.00001),
305                      geometry='LINESTRING(9.995 10.00001, 10.005 10.00001)')
306
307     details = napi.LookupDetails(geometry_output=napi.GeometryFormat.GEOJSON)
308     output = apiobj.api.reverse((10.0, 10.0), details=details).geometry['geojson']
309
310     assert json.loads(output) == {'coordinates': [10, 10.00001], 'type': 'Point'}
311