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