]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_api_lookup.py
Merge pull request #3020 from lonvia/reverse-api
[nominatim.git] / test / python / api / test_api_lookup.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 lookup API call.
9 """
10 import datetime as dt
11
12 import pytest
13
14 import nominatim.api as napi
15
16 @pytest.mark.parametrize('idobj', (napi.PlaceID(332), napi.OsmID('W', 4),
17                                    napi.OsmID('W', 4, 'highway')))
18 def test_lookup_in_placex(apiobj, idobj):
19     import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
20     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
21                      class_='highway', type='residential',
22                      name={'name': 'Road'}, address={'city': 'Barrow'},
23                      extratags={'surface': 'paved'},
24                      parent_place_id=34, linked_place_id=55,
25                      admin_level=15, country_code='gb',
26                      housenumber='4',
27                      postcode='34425', wikipedia='en:Faa',
28                      rank_search=27, rank_address=26,
29                      importance=0.01,
30                      centroid=(23, 34),
31                      indexed_date=import_date,
32                      geometry='LINESTRING(23 34, 23.1 34, 23.1 34.1, 23 34)')
33
34     result = apiobj.api.lookup(idobj, napi.LookupDetails())
35
36     assert result is not None
37
38     assert result.source_table.name == 'PLACEX'
39     assert result.category == ('highway', 'residential')
40     assert result.centroid == (pytest.approx(23.0), pytest.approx(34.0))
41
42     assert result.place_id == 332
43     assert result.parent_place_id == 34
44     assert result.linked_place_id == 55
45     assert result.osm_object == ('W', 4)
46     assert result.admin_level == 15
47
48     assert result.names == {'name': 'Road'}
49     assert result.address == {'city': 'Barrow'}
50     assert result.extratags == {'surface': 'paved'}
51
52     assert result.housenumber == '4'
53     assert result.postcode == '34425'
54     assert result.wikipedia == 'en:Faa'
55
56     assert result.rank_search == 27
57     assert result.rank_address == 26
58     assert result.importance == pytest.approx(0.01)
59
60     assert result.country_code == 'gb'
61     assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
62
63     assert result.address_rows is None
64     assert result.linked_rows is None
65     assert result.parented_rows is None
66     assert result.name_keywords is None
67     assert result.address_keywords is None
68
69     assert result.geometry == {'type': 'ST_LineString'}
70
71
72 def test_lookup_in_placex_minimal_info(apiobj):
73     import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
74     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
75                      class_='highway', type='residential',
76                      admin_level=15,
77                      rank_search=27, rank_address=26,
78                      centroid=(23, 34),
79                      indexed_date=import_date,
80                      geometry='LINESTRING(23 34, 23.1 34, 23.1 34.1, 23 34)')
81
82     result = apiobj.api.lookup(napi.PlaceID(332), napi.LookupDetails())
83
84     assert result is not None
85
86     assert result.source_table.name == 'PLACEX'
87     assert result.category == ('highway', 'residential')
88     assert result.centroid == (pytest.approx(23.0), pytest.approx(34.0))
89
90     assert result.place_id == 332
91     assert result.parent_place_id is None
92     assert result.linked_place_id is None
93     assert result.osm_object == ('W', 4)
94     assert result.admin_level == 15
95
96     assert result.names is None
97     assert result.address is None
98     assert result.extratags is None
99
100     assert result.housenumber is None
101     assert result.postcode is None
102     assert result.wikipedia is None
103
104     assert result.rank_search == 27
105     assert result.rank_address == 26
106     assert result.importance is None
107
108     assert result.country_code is None
109     assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
110
111     assert result.address_rows is None
112     assert result.linked_rows is None
113     assert result.parented_rows is None
114     assert result.name_keywords is None
115     assert result.address_keywords is None
116
117     assert result.geometry == {'type': 'ST_LineString'}
118
119
120 def test_lookup_in_placex_with_geometry(apiobj):
121     apiobj.add_placex(place_id=332,
122                       geometry='LINESTRING(23 34, 23.1 34)')
123
124     result = apiobj.api.lookup(napi.PlaceID(332),
125                                napi.LookupDetails(geometry_output=napi.GeometryFormat.GEOJSON))
126
127     assert result.geometry == {'geojson': '{"type":"LineString","coordinates":[[23,34],[23.1,34]]}'}
128
129
130 def test_lookup_placex_with_address_details(apiobj):
131     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
132                      class_='highway', type='residential',  name='Street',
133                      country_code='pl',
134                      rank_search=27, rank_address=26)
135     apiobj.add_address_placex(332, fromarea=False, isaddress=False,
136                               distance=0.0034,
137                               place_id=1000, osm_type='N', osm_id=3333,
138                               class_='place', type='suburb', name='Smallplace',
139                               country_code='pl', admin_level=13,
140                               rank_search=24, rank_address=23)
141     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
142                               place_id=1001, osm_type='N', osm_id=3334,
143                               class_='place', type='city', name='Bigplace',
144                               country_code='pl',
145                               rank_search=17, rank_address=16)
146
147     result = apiobj.api.lookup(napi.PlaceID(332),
148                                napi.LookupDetails(address_details=True))
149
150     assert result.address_rows == [
151                napi.AddressLine(place_id=332, osm_object=('W', 4),
152                                 category=('highway', 'residential'),
153                                 names={'name': 'Street'}, extratags={},
154                                 admin_level=15, fromarea=True, isaddress=True,
155                                 rank_address=26, distance=0.0),
156                napi.AddressLine(place_id=1000, osm_object=('N', 3333),
157                                 category=('place', 'suburb'),
158                                 names={'name': 'Smallplace'}, extratags={},
159                                 admin_level=13, fromarea=False, isaddress=True,
160                                 rank_address=23, distance=0.0034),
161                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
162                                 category=('place', 'city'),
163                                 names={'name': 'Bigplace'}, extratags={},
164                                 admin_level=15, fromarea=True, isaddress=True,
165                                 rank_address=16, distance=0.0),
166                napi.AddressLine(place_id=None, osm_object=None,
167                                 category=('place', 'country_code'),
168                                 names={'ref': 'pl'}, extratags={},
169                                 admin_level=None, fromarea=True, isaddress=False,
170                                 rank_address=4, distance=0.0)
171            ]
172
173
174 def test_lookup_place_with_linked_places_none_existing(apiobj):
175     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
176                      class_='highway', type='residential',  name='Street',
177                      country_code='pl', linked_place_id=45,
178                      rank_search=27, rank_address=26)
179
180     result = apiobj.api.lookup(napi.PlaceID(332),
181                                napi.LookupDetails(linked_places=True))
182
183     assert result.linked_rows == []
184
185
186 def test_lookup_place_with_linked_places_existing(apiobj):
187     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
188                      class_='highway', type='residential',  name='Street',
189                      country_code='pl', linked_place_id=45,
190                      rank_search=27, rank_address=26)
191     apiobj.add_placex(place_id=1001, osm_type='W', osm_id=5,
192                      class_='highway', type='residential',  name='Street',
193                      country_code='pl', linked_place_id=332,
194                      rank_search=27, rank_address=26)
195     apiobj.add_placex(place_id=1002, osm_type='W', osm_id=6,
196                      class_='highway', type='residential',  name='Street',
197                      country_code='pl', linked_place_id=332,
198                      rank_search=27, rank_address=26)
199
200     result = apiobj.api.lookup(napi.PlaceID(332),
201                                napi.LookupDetails(linked_places=True))
202
203     assert result.linked_rows == [
204                napi.AddressLine(place_id=1001, osm_object=('W', 5),
205                                 category=('highway', 'residential'),
206                                 names={'name': 'Street'}, extratags={},
207                                 admin_level=15, fromarea=False, isaddress=True,
208                                 rank_address=26, distance=0.0),
209                napi.AddressLine(place_id=1002, osm_object=('W', 6),
210                                 category=('highway', 'residential'),
211                                 names={'name': 'Street'}, extratags={},
212                                 admin_level=15, fromarea=False, isaddress=True,
213                                 rank_address=26, distance=0.0),
214     ]
215
216
217 def test_lookup_place_with_parented_places_not_existing(apiobj):
218     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
219                      class_='highway', type='residential',  name='Street',
220                      country_code='pl', parent_place_id=45,
221                      rank_search=27, rank_address=26)
222
223     result = apiobj.api.lookup(napi.PlaceID(332),
224                                napi.LookupDetails(parented_places=True))
225
226     assert result.parented_rows == []
227
228
229 def test_lookup_place_with_parented_places_existing(apiobj):
230     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
231                      class_='highway', type='residential',  name='Street',
232                      country_code='pl', parent_place_id=45,
233                      rank_search=27, rank_address=26)
234     apiobj.add_placex(place_id=1001, osm_type='N', osm_id=5,
235                      class_='place', type='house', housenumber='23',
236                      country_code='pl', parent_place_id=332,
237                      rank_search=30, rank_address=30)
238     apiobj.add_placex(place_id=1002, osm_type='W', osm_id=6,
239                      class_='highway', type='residential',  name='Street',
240                      country_code='pl', parent_place_id=332,
241                      rank_search=27, rank_address=26)
242
243     result = apiobj.api.lookup(napi.PlaceID(332),
244                                napi.LookupDetails(parented_places=True))
245
246     assert result.parented_rows == [
247                napi.AddressLine(place_id=1001, osm_object=('N', 5),
248                                 category=('place', 'house'),
249                                 names={'housenumber': '23'}, extratags={},
250                                 admin_level=15, fromarea=False, isaddress=True,
251                                 rank_address=30, distance=0.0),
252     ]
253
254
255 @pytest.mark.parametrize('idobj', (napi.PlaceID(4924), napi.OsmID('W', 9928)))
256 def test_lookup_in_osmline(apiobj, idobj):
257     import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
258     apiobj.add_osmline(place_id=4924, osm_id=9928,
259                        parent_place_id=12,
260                        startnumber=1, endnumber=4, step=1,
261                        country_code='gb', postcode='34425',
262                        address={'city': 'Big'},
263                        indexed_date=import_date,
264                        geometry='LINESTRING(23 34, 23 35)')
265
266     result = apiobj.api.lookup(idobj, napi.LookupDetails())
267
268     assert result is not None
269
270     assert result.source_table.name == 'OSMLINE'
271     assert result.category == ('place', 'houses')
272     assert result.centroid == (pytest.approx(23.0), pytest.approx(34.5))
273
274     assert result.place_id == 4924
275     assert result.parent_place_id == 12
276     assert result.linked_place_id is None
277     assert result.osm_object == ('W', 9928)
278     assert result.admin_level == 15
279
280     assert result.names is None
281     assert result.address == {'city': 'Big'}
282     assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
283
284     assert result.housenumber is None
285     assert result.postcode == '34425'
286     assert result.wikipedia is None
287
288     assert result.rank_search == 30
289     assert result.rank_address == 30
290     assert result.importance is None
291
292     assert result.country_code == 'gb'
293     assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
294
295     assert result.address_rows is None
296     assert result.linked_rows is None
297     assert result.parented_rows is None
298     assert result.name_keywords is None
299     assert result.address_keywords is None
300
301     assert result.geometry == {'type': 'ST_LineString'}
302
303
304 def test_lookup_in_osmline_split_interpolation(apiobj):
305     apiobj.add_osmline(place_id=1000, osm_id=9,
306                        startnumber=2, endnumber=4, step=1)
307     apiobj.add_osmline(place_id=1001, osm_id=9,
308                        startnumber=6, endnumber=9, step=1)
309     apiobj.add_osmline(place_id=1002, osm_id=9,
310                        startnumber=11, endnumber=20, step=1)
311
312     for i in range(1, 6):
313         result = apiobj.api.lookup(napi.OsmID('W', 9, str(i)), napi.LookupDetails())
314         assert result.place_id == 1000
315     for i in range(7, 11):
316         result = apiobj.api.lookup(napi.OsmID('W', 9, str(i)), napi.LookupDetails())
317         assert result.place_id == 1001
318     for i in range(12, 22):
319         result = apiobj.api.lookup(napi.OsmID('W', 9, str(i)), napi.LookupDetails())
320         assert result.place_id == 1002
321
322
323 def test_lookup_osmline_with_address_details(apiobj):
324     apiobj.add_osmline(place_id=9000, osm_id=9,
325                        startnumber=2, endnumber=4, step=1,
326                        parent_place_id=332)
327     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
328                      class_='highway', type='residential',  name='Street',
329                      country_code='pl',
330                      rank_search=27, rank_address=26)
331     apiobj.add_address_placex(332, fromarea=False, isaddress=False,
332                               distance=0.0034,
333                               place_id=1000, osm_type='N', osm_id=3333,
334                               class_='place', type='suburb', name='Smallplace',
335                               country_code='pl', admin_level=13,
336                               rank_search=24, rank_address=23)
337     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
338                               place_id=1001, osm_type='N', osm_id=3334,
339                               class_='place', type='city', name='Bigplace',
340                               country_code='pl',
341                               rank_search=17, rank_address=16)
342
343     result = apiobj.api.lookup(napi.PlaceID(9000),
344                                napi.LookupDetails(address_details=True))
345
346     assert result.address_rows == [
347                napi.AddressLine(place_id=None, osm_object=None,
348                                 category=('place', 'house_number'),
349                                 names={'ref': '2'}, extratags={},
350                                 admin_level=None, fromarea=True, isaddress=True,
351                                 rank_address=28, distance=0.0),
352                napi.AddressLine(place_id=332, osm_object=('W', 4),
353                                 category=('highway', 'residential'),
354                                 names={'name': 'Street'}, extratags={},
355                                 admin_level=15, fromarea=True, isaddress=True,
356                                 rank_address=26, distance=0.0),
357                napi.AddressLine(place_id=1000, osm_object=('N', 3333),
358                                 category=('place', 'suburb'),
359                                 names={'name': 'Smallplace'}, extratags={},
360                                 admin_level=13, fromarea=False, isaddress=True,
361                                 rank_address=23, distance=0.0034),
362                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
363                                 category=('place', 'city'),
364                                 names={'name': 'Bigplace'}, extratags={},
365                                 admin_level=15, fromarea=True, isaddress=True,
366                                 rank_address=16, distance=0.0),
367                napi.AddressLine(place_id=None, osm_object=None,
368                                 category=('place', 'country_code'),
369                                 names={'ref': 'pl'}, extratags={},
370                                 admin_level=None, fromarea=True, isaddress=False,
371                                 rank_address=4, distance=0.0)
372            ]
373
374
375 def test_lookup_in_tiger(apiobj):
376     apiobj.add_tiger(place_id=4924,
377                      parent_place_id=12,
378                      startnumber=1, endnumber=4, step=1,
379                      postcode='34425',
380                      geometry='LINESTRING(23 34, 23 35)')
381     apiobj.add_placex(place_id=12,
382                       category=('highway', 'residential'),
383                       osm_type='W', osm_id=6601223,
384                       geometry='LINESTRING(23 34, 23 35)')
385
386     result = apiobj.api.lookup(napi.PlaceID(4924), napi.LookupDetails())
387
388     assert result is not None
389
390     assert result.source_table.name == 'TIGER'
391     assert result.category == ('place', 'houses')
392     assert result.centroid == (pytest.approx(23.0), pytest.approx(34.5))
393
394     assert result.place_id == 4924
395     assert result.parent_place_id == 12
396     assert result.linked_place_id is None
397     assert result.osm_object == ('W', 6601223)
398     assert result.admin_level == 15
399
400     assert result.names is None
401     assert result.address is None
402     assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
403
404     assert result.housenumber is None
405     assert result.postcode == '34425'
406     assert result.wikipedia is None
407
408     assert result.rank_search == 30
409     assert result.rank_address == 30
410     assert result.importance is None
411
412     assert result.country_code == 'us'
413     assert result.indexed_date is None
414
415     assert result.address_rows is None
416     assert result.linked_rows is None
417     assert result.parented_rows is None
418     assert result.name_keywords is None
419     assert result.address_keywords is None
420
421     assert result.geometry == {'type': 'ST_LineString'}
422
423
424 def test_lookup_tiger_with_address_details(apiobj):
425     apiobj.add_tiger(place_id=9000,
426                      startnumber=2, endnumber=4, step=1,
427                      parent_place_id=332)
428     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
429                      class_='highway', type='residential',  name='Street',
430                      country_code='us',
431                      rank_search=27, rank_address=26)
432     apiobj.add_address_placex(332, fromarea=False, isaddress=False,
433                               distance=0.0034,
434                               place_id=1000, osm_type='N', osm_id=3333,
435                               class_='place', type='suburb', name='Smallplace',
436                               country_code='us', admin_level=13,
437                               rank_search=24, rank_address=23)
438     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
439                               place_id=1001, osm_type='N', osm_id=3334,
440                               class_='place', type='city', name='Bigplace',
441                               country_code='us',
442                               rank_search=17, rank_address=16)
443
444     result = apiobj.api.lookup(napi.PlaceID(9000),
445                                napi.LookupDetails(address_details=True))
446
447     assert result.address_rows == [
448                napi.AddressLine(place_id=None, osm_object=None,
449                                 category=('place', 'house_number'),
450                                 names={'ref': '2'}, extratags={},
451                                 admin_level=None, fromarea=True, isaddress=True,
452                                 rank_address=28, distance=0.0),
453                napi.AddressLine(place_id=332, osm_object=('W', 4),
454                                 category=('highway', 'residential'),
455                                 names={'name': 'Street'}, extratags={},
456                                 admin_level=15, fromarea=True, isaddress=True,
457                                 rank_address=26, distance=0.0),
458                napi.AddressLine(place_id=1000, osm_object=('N', 3333),
459                                 category=('place', 'suburb'),
460                                 names={'name': 'Smallplace'}, extratags={},
461                                 admin_level=13, fromarea=False, isaddress=True,
462                                 rank_address=23, distance=0.0034),
463                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
464                                 category=('place', 'city'),
465                                 names={'name': 'Bigplace'}, extratags={},
466                                 admin_level=15, fromarea=True, isaddress=True,
467                                 rank_address=16, distance=0.0),
468                napi.AddressLine(place_id=None, osm_object=None,
469                                 category=('place', 'country_code'),
470                                 names={'ref': 'us'}, extratags={},
471                                 admin_level=None, fromarea=True, isaddress=False,
472                                 rank_address=4, distance=0.0)
473            ]
474
475
476 def test_lookup_in_postcode(apiobj):
477     import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
478     apiobj.add_postcode(place_id=554,
479                         parent_place_id=152,
480                         postcode='34 425',
481                         country_code='gb',
482                         rank_search=20, rank_address=22,
483                         indexed_date=import_date,
484                         geometry='POINT(-9.45 5.6)')
485
486     result = apiobj.api.lookup(napi.PlaceID(554), napi.LookupDetails())
487
488     assert result is not None
489
490     assert result.source_table.name == 'POSTCODE'
491     assert result.category == ('place', 'postcode')
492     assert result.centroid == (pytest.approx(-9.45), pytest.approx(5.6))
493
494     assert result.place_id == 554
495     assert result.parent_place_id == 152
496     assert result.linked_place_id is None
497     assert result.osm_object is None
498     assert result.admin_level == 15
499
500     assert result.names == {'ref': '34 425'}
501     assert result.address is None
502     assert result.extratags is None
503
504     assert result.housenumber is None
505     assert result.postcode is None
506     assert result.wikipedia is None
507
508     assert result.rank_search == 20
509     assert result.rank_address == 22
510     assert result.importance is None
511
512     assert result.country_code == 'gb'
513     assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
514
515     assert result.address_rows is None
516     assert result.linked_rows is None
517     assert result.parented_rows is None
518     assert result.name_keywords is None
519     assert result.address_keywords is None
520
521     assert result.geometry == {'type': 'ST_Point'}
522
523
524 def test_lookup_postcode_with_address_details(apiobj):
525     apiobj.add_postcode(place_id=9000,
526                         parent_place_id=332,
527                         postcode='34 425',
528                         country_code='gb',
529                         rank_search=25, rank_address=25)
530     apiobj.add_placex(place_id=332, osm_type='N', osm_id=3333,
531                       class_='place', type='suburb',  name='Smallplace',
532                       country_code='gb', admin_level=13,
533                       rank_search=24, rank_address=23)
534     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
535                               place_id=1001, osm_type='N', osm_id=3334,
536                               class_='place', type='city', name='Bigplace',
537                               country_code='gb',
538                               rank_search=17, rank_address=16)
539
540     result = apiobj.api.lookup(napi.PlaceID(9000),
541                                napi.LookupDetails(address_details=True))
542
543     assert result.address_rows == [
544                napi.AddressLine(place_id=332, osm_object=('N', 3333),
545                                 category=('place', 'suburb'),
546                                 names={'name': 'Smallplace'}, extratags={},
547                                 admin_level=13, fromarea=True, isaddress=True,
548                                 rank_address=23, distance=0.0),
549                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
550                                 category=('place', 'city'),
551                                 names={'name': 'Bigplace'}, extratags={},
552                                 admin_level=15, fromarea=True, isaddress=True,
553                                 rank_address=16, distance=0.0),
554                napi.AddressLine(place_id=None, osm_object=None,
555                                 category=('place', 'postcode'),
556                                 names={'ref': '34 425'}, extratags={},
557                                 admin_level=None, fromarea=False, isaddress=True,
558                                 rank_address=5, distance=0.0),
559                napi.AddressLine(place_id=None, osm_object=None,
560                                 category=('place', 'country_code'),
561                                 names={'ref': 'gb'}, extratags={},
562                                 admin_level=None, fromarea=True, isaddress=False,
563                                 rank_address=4, distance=0.0)
564            ]
565
566 @pytest.mark.parametrize('objid', [napi.PlaceID(1736),
567                                    napi.OsmID('W', 55),
568                                    napi.OsmID('N', 55, 'amenity')])
569 def test_lookup_missing_object(apiobj, objid):
570     apiobj.add_placex(place_id=1, osm_type='N', osm_id=55,
571                       class_='place', type='suburb')
572
573     assert apiobj.api.lookup(objid, napi.LookupDetails()) is None
574
575
576 @pytest.mark.parametrize('gtype', (napi.GeometryFormat.KML,
577                                     napi.GeometryFormat.SVG,
578                                     napi.GeometryFormat.TEXT))
579 def test_lookup_unsupported_geometry(apiobj, gtype):
580     apiobj.add_placex(place_id=332)
581
582     with pytest.raises(ValueError):
583         apiobj.api.lookup(napi.PlaceID(332),
584                           napi.LookupDetails(geometry_output=gtype))