]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_api_details.py
rerank results by query
[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=None, osm_object=None,
344                                 category=('place', 'house_number'),
345                                 names={'ref': '2'}, extratags={},
346                                 admin_level=None, fromarea=True, isaddress=True,
347                                 rank_address=28, distance=0.0,
348                                 local_name='2'),
349                napi.AddressLine(place_id=332, osm_object=('W', 4),
350                                 category=('highway', 'residential'),
351                                 names={'name': 'Street'}, extratags={},
352                                 admin_level=15, fromarea=True, isaddress=True,
353                                 rank_address=26, distance=0.0,
354                                 local_name='Street'),
355                napi.AddressLine(place_id=1000, osm_object=('N', 3333),
356                                 category=('place', 'suburb'),
357                                 names={'name': 'Smallplace'}, extratags={},
358                                 admin_level=13, fromarea=False, isaddress=True,
359                                 rank_address=23, distance=0.0034,
360                                 local_name='Smallplace'),
361                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
362                                 category=('place', 'city'),
363                                 names={'name': 'Bigplace'}, extratags={},
364                                 admin_level=15, fromarea=True, isaddress=True,
365                                 rank_address=16, distance=0.0,
366                                 local_name='Bigplace'),
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.details(napi.PlaceID(4924))
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.details(napi.PlaceID(9000), address_details=True)
445
446     assert result.address_rows == [
447                napi.AddressLine(place_id=None, osm_object=None,
448                                 category=('place', 'house_number'),
449                                 names={'ref': '2'}, extratags={},
450                                 admin_level=None, fromarea=True, isaddress=True,
451                                 rank_address=28, distance=0.0,
452                                 local_name='2'),
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                                 local_name='Street'),
459                napi.AddressLine(place_id=1000, osm_object=('N', 3333),
460                                 category=('place', 'suburb'),
461                                 names={'name': 'Smallplace'}, extratags={},
462                                 admin_level=13, fromarea=False, isaddress=True,
463                                 rank_address=23, distance=0.0034,
464                                 local_name='Smallplace'),
465                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
466                                 category=('place', 'city'),
467                                 names={'name': 'Bigplace'}, extratags={},
468                                 admin_level=15, fromarea=True, isaddress=True,
469                                 rank_address=16, distance=0.0,
470                                 local_name='Bigplace'),
471                napi.AddressLine(place_id=None, osm_object=None,
472                                 category=('place', 'country_code'),
473                                 names={'ref': 'us'}, extratags={},
474                                 admin_level=None, fromarea=True, isaddress=False,
475                                 rank_address=4, distance=0.0)
476            ]
477
478
479 def test_lookup_in_postcode(apiobj):
480     import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
481     apiobj.add_postcode(place_id=554,
482                         parent_place_id=152,
483                         postcode='34 425',
484                         country_code='gb',
485                         rank_search=20, rank_address=22,
486                         indexed_date=import_date,
487                         geometry='POINT(-9.45 5.6)')
488
489     result = apiobj.api.details(napi.PlaceID(554))
490
491     assert result is not None
492
493     assert result.source_table.name == 'POSTCODE'
494     assert result.category == ('place', 'postcode')
495     assert result.centroid == (pytest.approx(-9.45), pytest.approx(5.6))
496
497     assert result.place_id == 554
498     assert result.parent_place_id == 152
499     assert result.linked_place_id is None
500     assert result.osm_object is None
501     assert result.admin_level == 15
502
503     assert result.names == {'ref': '34 425'}
504     assert result.address is None
505     assert result.extratags is None
506
507     assert result.housenumber is None
508     assert result.postcode is None
509     assert result.wikipedia is None
510
511     assert result.rank_search == 20
512     assert result.rank_address == 22
513     assert result.importance is None
514
515     assert result.country_code == 'gb'
516     assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
517
518     assert result.address_rows is None
519     assert result.linked_rows is None
520     assert result.parented_rows is None
521     assert result.name_keywords is None
522     assert result.address_keywords is None
523
524     assert result.geometry == {'type': 'ST_Point'}
525
526
527 def test_lookup_postcode_with_address_details(apiobj):
528     apiobj.add_postcode(place_id=9000,
529                         parent_place_id=332,
530                         postcode='34 425',
531                         country_code='gb',
532                         rank_search=25, rank_address=25)
533     apiobj.add_placex(place_id=332, osm_type='N', osm_id=3333,
534                       class_='place', type='suburb',  name='Smallplace',
535                       country_code='gb', admin_level=13,
536                       rank_search=24, rank_address=23)
537     apiobj.add_address_placex(332, fromarea=True, isaddress=True,
538                               place_id=1001, osm_type='N', osm_id=3334,
539                               class_='place', type='city', name='Bigplace',
540                               country_code='gb',
541                               rank_search=17, rank_address=16)
542
543     result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
544
545     assert result.address_rows == [
546                napi.AddressLine(place_id=332, osm_object=('N', 3333),
547                                 category=('place', 'suburb'),
548                                 names={'name': 'Smallplace'}, extratags={},
549                                 admin_level=13, fromarea=True, isaddress=True,
550                                 rank_address=23, distance=0.0,
551                                 local_name='Smallplace'),
552                napi.AddressLine(place_id=1001, osm_object=('N', 3334),
553                                 category=('place', 'city'),
554                                 names={'name': 'Bigplace'}, extratags={},
555                                 admin_level=15, fromarea=True, isaddress=True,
556                                 rank_address=16, distance=0.0,
557                                 local_name='Bigplace'),
558                napi.AddressLine(place_id=None, osm_object=None,
559                                 category=('place', 'postcode'),
560                                 names={'ref': '34 425'}, extratags={},
561                                 admin_level=None, fromarea=False, isaddress=True,
562                                 rank_address=5, distance=0.0,
563                                 local_name='34 425'),
564                napi.AddressLine(place_id=None, osm_object=None,
565                                 category=('place', 'country_code'),
566                                 names={'ref': 'gb'}, extratags={},
567                                 admin_level=None, fromarea=True, isaddress=False,
568                                 rank_address=4, distance=0.0)
569            ]
570
571 @pytest.mark.parametrize('objid', [napi.PlaceID(1736),
572                                    napi.OsmID('W', 55),
573                                    napi.OsmID('N', 55, 'amenity')])
574 def test_lookup_missing_object(apiobj, objid):
575     apiobj.add_placex(place_id=1, osm_type='N', osm_id=55,
576                       class_='place', type='suburb')
577
578     assert apiobj.api.details(objid) is None
579
580
581 @pytest.mark.parametrize('gtype', (napi.GeometryFormat.KML,
582                                     napi.GeometryFormat.SVG,
583                                     napi.GeometryFormat.TEXT))
584 def test_lookup_unsupported_geometry(apiobj, gtype):
585     apiobj.add_placex(place_id=332)
586
587     with pytest.raises(ValueError):
588         apiobj.api.details(napi.PlaceID(332), geometry_output=gtype)