]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_api_lookup.py
Merge remote-tracking branch 'upstream/master'
[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
382     result = apiobj.api.lookup(napi.PlaceID(4924), napi.LookupDetails())
383
384     assert result is not None
385
386     assert result.source_table.name == 'TIGER'
387     assert result.category == ('place', 'houses')
388     assert result.centroid == (pytest.approx(23.0), pytest.approx(34.5))
389
390     assert result.place_id == 4924
391     assert result.parent_place_id == 12
392     assert result.linked_place_id is None
393     assert result.osm_object is None
394     assert result.admin_level == 15
395
396     assert result.names is None
397     assert result.address is None
398     assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
399
400     assert result.housenumber is None
401     assert result.postcode == '34425'
402     assert result.wikipedia is None
403
404     assert result.rank_search == 30
405     assert result.rank_address == 30
406     assert result.importance is None
407
408     assert result.country_code == 'us'
409     assert result.indexed_date is None
410
411     assert result.address_rows is None
412     assert result.linked_rows is None
413     assert result.parented_rows is None
414     assert result.name_keywords is None
415     assert result.address_keywords is None
416
417     assert result.geometry == {'type': 'ST_LineString'}
418
419
420 def test_lookup_tiger_with_address_details(apiobj):
421     apiobj.add_tiger(place_id=9000,
422                      startnumber=2, endnumber=4, step=1,
423                      parent_place_id=332)
424     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
425                      class_='highway', type='residential',  name='Street',
426                      country_code='us',
427                      rank_search=27, rank_address=26)
428     apiobj.add_address_placex(332, fromarea=False, isaddress=False,
429                               distance=0.0034,
430                               place_id=1000, osm_type='N', osm_id=3333,
431                               class_='place', type='suburb', name='Smallplace',
432                               country_code='us', admin_level=13,
433                               rank_search=24, rank_address=23)
434     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
435                               place_id=1001, osm_type='N', osm_id=3334,
436                               class_='place', type='city', name='Bigplace',
437                               country_code='us',
438                               rank_search=17, rank_address=16)
439
440     result = apiobj.api.lookup(napi.PlaceID(9000),
441                                napi.LookupDetails(address_details=True))
442
443     assert result.address_rows == [
444                napi.AddressLine(place_id=None, osm_object=None,
445                                 category=('place', 'house_number'),
446                                 names={'ref': '2'}, extratags={},
447                                 admin_level=None, fromarea=True, isaddress=True,
448                                 rank_address=28, distance=0.0),
449                napi.AddressLine(place_id=332, osm_object=('W', 4),
450                                 category=('highway', 'residential'),
451                                 names={'name': 'Street'}, extratags={},
452                                 admin_level=15, fromarea=True, isaddress=True,
453                                 rank_address=26, distance=0.0),
454                napi.AddressLine(place_id=1000, osm_object=('N', 3333),
455                                 category=('place', 'suburb'),
456                                 names={'name': 'Smallplace'}, extratags={},
457                                 admin_level=13, fromarea=False, isaddress=True,
458                                 rank_address=23, distance=0.0034),
459                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
460                                 category=('place', 'city'),
461                                 names={'name': 'Bigplace'}, extratags={},
462                                 admin_level=15, fromarea=True, isaddress=True,
463                                 rank_address=16, distance=0.0),
464                napi.AddressLine(place_id=None, osm_object=None,
465                                 category=('place', 'country_code'),
466                                 names={'ref': 'us'}, extratags={},
467                                 admin_level=None, fromarea=True, isaddress=False,
468                                 rank_address=4, distance=0.0)
469            ]
470
471
472 def test_lookup_in_postcode(apiobj):
473     import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
474     apiobj.add_postcode(place_id=554,
475                         parent_place_id=152,
476                         postcode='34 425',
477                         country_code='gb',
478                         rank_search=20, rank_address=22,
479                         indexed_date=import_date,
480                         geometry='POINT(-9.45 5.6)')
481
482     result = apiobj.api.lookup(napi.PlaceID(554), napi.LookupDetails())
483
484     assert result is not None
485
486     assert result.source_table.name == 'POSTCODE'
487     assert result.category == ('place', 'postcode')
488     assert result.centroid == (pytest.approx(-9.45), pytest.approx(5.6))
489
490     assert result.place_id == 554
491     assert result.parent_place_id == 152
492     assert result.linked_place_id is None
493     assert result.osm_object is None
494     assert result.admin_level == 15
495
496     assert result.names == {'ref': '34 425'}
497     assert result.address is None
498     assert result.extratags is None
499
500     assert result.housenumber is None
501     assert result.postcode is None
502     assert result.wikipedia is None
503
504     assert result.rank_search == 20
505     assert result.rank_address == 22
506     assert result.importance is None
507
508     assert result.country_code == 'gb'
509     assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
510
511     assert result.address_rows is None
512     assert result.linked_rows is None
513     assert result.parented_rows is None
514     assert result.name_keywords is None
515     assert result.address_keywords is None
516
517     assert result.geometry == {'type': 'ST_Point'}
518
519
520 def test_lookup_postcode_with_address_details(apiobj):
521     apiobj.add_postcode(place_id=9000,
522                         parent_place_id=332,
523                         postcode='34 425',
524                         country_code='gb',
525                         rank_search=25, rank_address=25)
526     apiobj.add_placex(place_id=332, osm_type='N', osm_id=3333,
527                       class_='place', type='suburb',  name='Smallplace',
528                       country_code='gb', admin_level=13,
529                       rank_search=24, rank_address=23)
530     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
531                               place_id=1001, osm_type='N', osm_id=3334,
532                               class_='place', type='city', name='Bigplace',
533                               country_code='gb',
534                               rank_search=17, rank_address=16)
535
536     result = apiobj.api.lookup(napi.PlaceID(9000),
537                                napi.LookupDetails(address_details=True))
538
539     assert result.address_rows == [
540                napi.AddressLine(place_id=332, osm_object=('N', 3333),
541                                 category=('place', 'suburb'),
542                                 names={'name': 'Smallplace'}, extratags={},
543                                 admin_level=13, fromarea=True, isaddress=True,
544                                 rank_address=23, distance=0.0),
545                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
546                                 category=('place', 'city'),
547                                 names={'name': 'Bigplace'}, extratags={},
548                                 admin_level=15, fromarea=True, isaddress=True,
549                                 rank_address=16, distance=0.0),
550                napi.AddressLine(place_id=None, osm_object=None,
551                                 category=('place', 'postcode'),
552                                 names={'ref': '34 425'}, extratags={},
553                                 admin_level=None, fromarea=False, isaddress=True,
554                                 rank_address=5, distance=0.0),
555                napi.AddressLine(place_id=None, osm_object=None,
556                                 category=('place', 'country_code'),
557                                 names={'ref': 'gb'}, extratags={},
558                                 admin_level=None, fromarea=True, isaddress=False,
559                                 rank_address=4, distance=0.0)
560            ]
561
562 @pytest.mark.parametrize('objid', [napi.PlaceID(1736),
563                                    napi.OsmID('W', 55),
564                                    napi.OsmID('N', 55, 'amenity')])
565 def test_lookup_missing_object(apiobj, objid):
566     apiobj.add_placex(place_id=1, osm_type='N', osm_id=55,
567                       class_='place', type='suburb')
568
569     assert apiobj.api.lookup(objid, napi.LookupDetails()) is None
570
571
572 @pytest.mark.parametrize('gtype', (napi.GeometryFormat.KML,
573                                     napi.GeometryFormat.SVG,
574                                     napi.GeometryFormat.TEXT))
575 def test_lookup_unsupported_geometry(apiobj, gtype):
576     apiobj.add_placex(place_id=332)
577
578     with pytest.raises(ValueError):
579         apiobj.api.lookup(napi.PlaceID(332),
580                           napi.LookupDetails(geometry_output=gtype))