]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_api_details.py
Merge remote-tracking branch 'upstream/master'
[nominatim.git] / test / python / api / test_api_details.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 details 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.details(idobj)
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.details(napi.PlaceID(332))
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.details(napi.PlaceID(332), geometry_output=napi.GeometryFormat.GEOJSON)
125
126     assert result.geometry == {'geojson': '{"type":"LineString","coordinates":[[23,34],[23.1,34]]}'}
127
128
129 def test_lookup_placex_with_address_details(apiobj):
130     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
131                      class_='highway', type='residential',  name='Street',
132                      country_code='pl',
133                      rank_search=27, rank_address=26)
134     apiobj.add_address_placex(332, fromarea=False, isaddress=False,
135                               distance=0.0034,
136                               place_id=1000, osm_type='N', osm_id=3333,
137                               class_='place', type='suburb', name='Smallplace',
138                               country_code='pl', admin_level=13,
139                               rank_search=24, rank_address=23)
140     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
141                               place_id=1001, osm_type='N', osm_id=3334,
142                               class_='place', type='city', name='Bigplace',
143                               country_code='pl',
144                               rank_search=17, rank_address=16)
145
146     result = apiobj.api.details(napi.PlaceID(332), address_details=True)
147
148     assert result.address_rows == [
149                napi.AddressLine(place_id=332, osm_object=('W', 4),
150                                 category=('highway', 'residential'),
151                                 names={'name': 'Street'}, extratags={},
152                                 admin_level=15, fromarea=True, isaddress=True,
153                                 rank_address=26, distance=0.0,
154                                 local_name='Street'),
155                napi.AddressLine(place_id=1000, osm_object=('N', 3333),
156                                 category=('place', 'suburb'),
157                                 names={'name': 'Smallplace'}, extratags={},
158                                 admin_level=13, fromarea=False, isaddress=True,
159                                 rank_address=23, distance=0.0034,
160                                 local_name='Smallplace'),
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                                 local_name='Bigplace'),
167                napi.AddressLine(place_id=None, osm_object=None,
168                                 category=('place', 'country_code'),
169                                 names={'ref': 'pl'}, extratags={},
170                                 admin_level=None, fromarea=True, isaddress=False,
171                                 rank_address=4, distance=0.0)
172            ]
173
174
175 def test_lookup_place_with_linked_places_none_existing(apiobj):
176     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
177                      class_='highway', type='residential',  name='Street',
178                      country_code='pl', linked_place_id=45,
179                      rank_search=27, rank_address=26)
180
181     result = apiobj.api.details(napi.PlaceID(332), 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.details(napi.PlaceID(332), linked_places=True)
201
202     assert result.linked_rows == [
203                napi.AddressLine(place_id=1001, osm_object=('W', 5),
204                                 category=('highway', 'residential'),
205                                 names={'name': 'Street'}, extratags={},
206                                 admin_level=15, fromarea=False, isaddress=True,
207                                 rank_address=26, distance=0.0),
208                napi.AddressLine(place_id=1002, osm_object=('W', 6),
209                                 category=('highway', 'residential'),
210                                 names={'name': 'Street'}, extratags={},
211                                 admin_level=15, fromarea=False, isaddress=True,
212                                 rank_address=26, distance=0.0),
213     ]
214
215
216 def test_lookup_place_with_parented_places_not_existing(apiobj):
217     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
218                      class_='highway', type='residential',  name='Street',
219                      country_code='pl', parent_place_id=45,
220                      rank_search=27, rank_address=26)
221
222     result = apiobj.api.details(napi.PlaceID(332), parented_places=True)
223
224     assert result.parented_rows == []
225
226
227 def test_lookup_place_with_parented_places_existing(apiobj):
228     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
229                      class_='highway', type='residential',  name='Street',
230                      country_code='pl', parent_place_id=45,
231                      rank_search=27, rank_address=26)
232     apiobj.add_placex(place_id=1001, osm_type='N', osm_id=5,
233                      class_='place', type='house', housenumber='23',
234                      country_code='pl', parent_place_id=332,
235                      rank_search=30, rank_address=30)
236     apiobj.add_placex(place_id=1002, osm_type='W', osm_id=6,
237                      class_='highway', type='residential',  name='Street',
238                      country_code='pl', parent_place_id=332,
239                      rank_search=27, rank_address=26)
240
241     result = apiobj.api.details(napi.PlaceID(332), parented_places=True)
242
243     assert result.parented_rows == [
244                napi.AddressLine(place_id=1001, osm_object=('N', 5),
245                                 category=('place', 'house'),
246                                 names={'housenumber': '23'}, extratags={},
247                                 admin_level=15, fromarea=False, isaddress=True,
248                                 rank_address=30, distance=0.0),
249     ]
250
251
252 @pytest.mark.parametrize('idobj', (napi.PlaceID(4924), napi.OsmID('W', 9928)))
253 def test_lookup_in_osmline(apiobj, idobj):
254     import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
255     apiobj.add_osmline(place_id=4924, osm_id=9928,
256                        parent_place_id=12,
257                        startnumber=1, endnumber=4, step=1,
258                        country_code='gb', postcode='34425',
259                        address={'city': 'Big'},
260                        indexed_date=import_date,
261                        geometry='LINESTRING(23 34, 23 35)')
262
263     result = apiobj.api.details(idobj)
264
265     assert result is not None
266
267     assert result.source_table.name == 'OSMLINE'
268     assert result.category == ('place', 'houses')
269     assert result.centroid == (pytest.approx(23.0), pytest.approx(34.5))
270
271     assert result.place_id == 4924
272     assert result.parent_place_id == 12
273     assert result.linked_place_id is None
274     assert result.osm_object == ('W', 9928)
275     assert result.admin_level == 15
276
277     assert result.names is None
278     assert result.address == {'city': 'Big'}
279     assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
280
281     assert result.housenumber is None
282     assert result.postcode == '34425'
283     assert result.wikipedia is None
284
285     assert result.rank_search == 30
286     assert result.rank_address == 30
287     assert result.importance is None
288
289     assert result.country_code == 'gb'
290     assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
291
292     assert result.address_rows is None
293     assert result.linked_rows is None
294     assert result.parented_rows is None
295     assert result.name_keywords is None
296     assert result.address_keywords is None
297
298     assert result.geometry == {'type': 'ST_LineString'}
299
300
301 def test_lookup_in_osmline_split_interpolation(apiobj):
302     apiobj.add_osmline(place_id=1000, osm_id=9,
303                        startnumber=2, endnumber=4, step=1)
304     apiobj.add_osmline(place_id=1001, osm_id=9,
305                        startnumber=6, endnumber=9, step=1)
306     apiobj.add_osmline(place_id=1002, osm_id=9,
307                        startnumber=11, endnumber=20, step=1)
308
309     for i in range(1, 6):
310         result = apiobj.api.details(napi.OsmID('W', 9, str(i)))
311         assert result.place_id == 1000
312     for i in range(7, 11):
313         result = apiobj.api.details(napi.OsmID('W', 9, str(i)))
314         assert result.place_id == 1001
315     for i in range(12, 22):
316         result = apiobj.api.details(napi.OsmID('W', 9, str(i)))
317         assert result.place_id == 1002
318
319
320 def test_lookup_osmline_with_address_details(apiobj):
321     apiobj.add_osmline(place_id=9000, osm_id=9,
322                        startnumber=2, endnumber=4, step=1,
323                        parent_place_id=332)
324     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
325                      class_='highway', type='residential',  name='Street',
326                      country_code='pl',
327                      rank_search=27, rank_address=26)
328     apiobj.add_address_placex(332, fromarea=False, isaddress=False,
329                               distance=0.0034,
330                               place_id=1000, osm_type='N', osm_id=3333,
331                               class_='place', type='suburb', name='Smallplace',
332                               country_code='pl', admin_level=13,
333                               rank_search=24, rank_address=23)
334     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
335                               place_id=1001, osm_type='N', osm_id=3334,
336                               class_='place', type='city', name='Bigplace',
337                               country_code='pl',
338                               rank_search=17, rank_address=16)
339
340     result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
341
342     assert result.address_rows == [
343                napi.AddressLine(place_id=332, osm_object=('W', 4),
344                                 category=('highway', 'residential'),
345                                 names={'name': 'Street'}, extratags={},
346                                 admin_level=15, fromarea=True, isaddress=True,
347                                 rank_address=26, distance=0.0,
348                                 local_name='Street'),
349                napi.AddressLine(place_id=1000, osm_object=('N', 3333),
350                                 category=('place', 'suburb'),
351                                 names={'name': 'Smallplace'}, extratags={},
352                                 admin_level=13, fromarea=False, isaddress=True,
353                                 rank_address=23, distance=0.0034,
354                                 local_name='Smallplace'),
355                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
356                                 category=('place', 'city'),
357                                 names={'name': 'Bigplace'}, extratags={},
358                                 admin_level=15, fromarea=True, isaddress=True,
359                                 rank_address=16, distance=0.0,
360                                 local_name='Bigplace'),
361                napi.AddressLine(place_id=None, osm_object=None,
362                                 category=('place', 'country_code'),
363                                 names={'ref': 'pl'}, extratags={},
364                                 admin_level=None, fromarea=True, isaddress=False,
365                                 rank_address=4, distance=0.0)
366            ]
367
368
369 def test_lookup_in_tiger(apiobj):
370     apiobj.add_tiger(place_id=4924,
371                      parent_place_id=12,
372                      startnumber=1, endnumber=4, step=1,
373                      postcode='34425',
374                      geometry='LINESTRING(23 34, 23 35)')
375     apiobj.add_placex(place_id=12,
376                       category=('highway', 'residential'),
377                       osm_type='W', osm_id=6601223,
378                       geometry='LINESTRING(23 34, 23 35)')
379
380     result = apiobj.api.details(napi.PlaceID(4924))
381
382     assert result is not None
383
384     assert result.source_table.name == 'TIGER'
385     assert result.category == ('place', 'houses')
386     assert result.centroid == (pytest.approx(23.0), pytest.approx(34.5))
387
388     assert result.place_id == 4924
389     assert result.parent_place_id == 12
390     assert result.linked_place_id is None
391     assert result.osm_object == ('W', 6601223)
392     assert result.admin_level == 15
393
394     assert result.names is None
395     assert result.address is None
396     assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
397
398     assert result.housenumber is None
399     assert result.postcode == '34425'
400     assert result.wikipedia is None
401
402     assert result.rank_search == 30
403     assert result.rank_address == 30
404     assert result.importance is None
405
406     assert result.country_code == 'us'
407     assert result.indexed_date is None
408
409     assert result.address_rows is None
410     assert result.linked_rows is None
411     assert result.parented_rows is None
412     assert result.name_keywords is None
413     assert result.address_keywords is None
414
415     assert result.geometry == {'type': 'ST_LineString'}
416
417
418 def test_lookup_tiger_with_address_details(apiobj):
419     apiobj.add_tiger(place_id=9000,
420                      startnumber=2, endnumber=4, step=1,
421                      parent_place_id=332)
422     apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
423                      class_='highway', type='residential',  name='Street',
424                      country_code='us',
425                      rank_search=27, rank_address=26)
426     apiobj.add_address_placex(332, fromarea=False, isaddress=False,
427                               distance=0.0034,
428                               place_id=1000, osm_type='N', osm_id=3333,
429                               class_='place', type='suburb', name='Smallplace',
430                               country_code='us', admin_level=13,
431                               rank_search=24, rank_address=23)
432     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
433                               place_id=1001, osm_type='N', osm_id=3334,
434                               class_='place', type='city', name='Bigplace',
435                               country_code='us',
436                               rank_search=17, rank_address=16)
437
438     result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
439
440     assert result.address_rows == [
441                napi.AddressLine(place_id=332, osm_object=('W', 4),
442                                 category=('highway', 'residential'),
443                                 names={'name': 'Street'}, extratags={},
444                                 admin_level=15, fromarea=True, isaddress=True,
445                                 rank_address=26, distance=0.0,
446                                 local_name='Street'),
447                napi.AddressLine(place_id=1000, osm_object=('N', 3333),
448                                 category=('place', 'suburb'),
449                                 names={'name': 'Smallplace'}, extratags={},
450                                 admin_level=13, fromarea=False, isaddress=True,
451                                 rank_address=23, distance=0.0034,
452                                 local_name='Smallplace'),
453                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
454                                 category=('place', 'city'),
455                                 names={'name': 'Bigplace'}, extratags={},
456                                 admin_level=15, fromarea=True, isaddress=True,
457                                 rank_address=16, distance=0.0,
458                                 local_name='Bigplace'),
459                napi.AddressLine(place_id=None, osm_object=None,
460                                 category=('place', 'country_code'),
461                                 names={'ref': 'us'}, extratags={},
462                                 admin_level=None, fromarea=True, isaddress=False,
463                                 rank_address=4, distance=0.0)
464            ]
465
466
467 def test_lookup_in_postcode(apiobj):
468     import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
469     apiobj.add_postcode(place_id=554,
470                         parent_place_id=152,
471                         postcode='34 425',
472                         country_code='gb',
473                         rank_search=20, rank_address=22,
474                         indexed_date=import_date,
475                         geometry='POINT(-9.45 5.6)')
476
477     result = apiobj.api.details(napi.PlaceID(554))
478
479     assert result is not None
480
481     assert result.source_table.name == 'POSTCODE'
482     assert result.category == ('place', 'postcode')
483     assert result.centroid == (pytest.approx(-9.45), pytest.approx(5.6))
484
485     assert result.place_id == 554
486     assert result.parent_place_id == 152
487     assert result.linked_place_id is None
488     assert result.osm_object is None
489     assert result.admin_level == 15
490
491     assert result.names == {'ref': '34 425'}
492     assert result.address is None
493     assert result.extratags is None
494
495     assert result.housenumber is None
496     assert result.postcode is None
497     assert result.wikipedia is None
498
499     assert result.rank_search == 20
500     assert result.rank_address == 22
501     assert result.importance is None
502
503     assert result.country_code == 'gb'
504     assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
505
506     assert result.address_rows is None
507     assert result.linked_rows is None
508     assert result.parented_rows is None
509     assert result.name_keywords is None
510     assert result.address_keywords is None
511
512     assert result.geometry == {'type': 'ST_Point'}
513
514
515 def test_lookup_postcode_with_address_details(apiobj):
516     apiobj.add_postcode(place_id=9000,
517                         parent_place_id=332,
518                         postcode='34 425',
519                         country_code='gb',
520                         rank_search=25, rank_address=25)
521     apiobj.add_placex(place_id=332, osm_type='N', osm_id=3333,
522                       class_='place', type='suburb',  name='Smallplace',
523                       country_code='gb', admin_level=13,
524                       rank_search=24, rank_address=23)
525     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
526                               place_id=1001, osm_type='N', osm_id=3334,
527                               class_='place', type='city', name='Bigplace',
528                               country_code='gb',
529                               rank_search=17, rank_address=16)
530
531     result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
532
533     assert result.address_rows == [
534                napi.AddressLine(place_id=9000, osm_object=None,
535                                 category=('place', 'postcode'),
536                                 names={'ref': '34 425'}, extratags={},
537                                 admin_level=15, fromarea=True, isaddress=True,
538                                 rank_address=25, distance=0.0,
539                                 local_name='34 425'),
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                                 local_name='Smallplace'),
546                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
547                                 category=('place', 'city'),
548                                 names={'name': 'Bigplace'}, extratags={},
549                                 admin_level=15, fromarea=True, isaddress=True,
550                                 rank_address=16, distance=0.0,
551                                 local_name='Bigplace'),
552                napi.AddressLine(place_id=None, osm_object=None,
553                                 category=('place', 'country_code'),
554                                 names={'ref': 'gb'}, extratags={},
555                                 admin_level=None, fromarea=True, isaddress=False,
556                                 rank_address=4, distance=0.0)
557            ]
558
559 @pytest.mark.parametrize('objid', [napi.PlaceID(1736),
560                                    napi.OsmID('W', 55),
561                                    napi.OsmID('N', 55, 'amenity')])
562 def test_lookup_missing_object(apiobj, objid):
563     apiobj.add_placex(place_id=1, osm_type='N', osm_id=55,
564                       class_='place', type='suburb')
565
566     assert apiobj.api.details(objid) is None
567
568
569 @pytest.mark.parametrize('gtype', (napi.GeometryFormat.KML,
570                                     napi.GeometryFormat.SVG,
571                                     napi.GeometryFormat.TEXT))
572 def test_lookup_unsupported_geometry(apiobj, gtype):
573     apiobj.add_placex(place_id=332)
574
575     with pytest.raises(ValueError):
576         apiobj.api.details(napi.PlaceID(332), geometry_output=gtype)