1 # SPDX-License-Identifier: GPL-3.0-or-later
3 # This file is part of Nominatim. (https://nominatim.org)
5 # Copyright (C) 2023 by the Nominatim developer community.
6 # For a full list of authors see the git log.
8 Tests for details API call.
14 import nominatim.api as napi
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',
27 postcode='34425', wikipedia='en:Faa',
28 rank_search=27, rank_address=26,
31 indexed_date=import_date,
32 geometry='LINESTRING(23 34, 23.1 34, 23.1 34.1, 23 34)')
34 result = apiobj.api.details(idobj)
36 assert result is not None
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))
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
48 assert result.names == {'name': 'Road'}
49 assert result.address == {'city': 'Barrow'}
50 assert result.extratags == {'surface': 'paved'}
52 assert result.housenumber == '4'
53 assert result.postcode == '34425'
54 assert result.wikipedia == 'en:Faa'
56 assert result.rank_search == 27
57 assert result.rank_address == 26
58 assert result.importance == pytest.approx(0.01)
60 assert result.country_code == 'gb'
61 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
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
69 assert result.geometry == {'type': 'ST_LineString'}
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',
77 rank_search=27, rank_address=26,
79 indexed_date=import_date,
80 geometry='LINESTRING(23 34, 23.1 34, 23.1 34.1, 23 34)')
82 result = apiobj.api.details(napi.PlaceID(332))
84 assert result is not None
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))
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
96 assert result.names is None
97 assert result.address is None
98 assert result.extratags is None
100 assert result.housenumber is None
101 assert result.postcode is None
102 assert result.wikipedia is None
104 assert result.rank_search == 27
105 assert result.rank_address == 26
106 assert result.importance is None
108 assert result.country_code is None
109 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
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
117 assert result.geometry == {'type': 'ST_LineString'}
120 def test_lookup_in_placex_with_geometry(apiobj):
121 apiobj.add_placex(place_id=332,
122 geometry='LINESTRING(23 34, 23.1 34)')
124 result = apiobj.api.details(napi.PlaceID(332), geometry_output=napi.GeometryFormat.GEOJSON)
126 assert result.geometry == {'geojson': '{"type":"LineString","coordinates":[[23,34],[23.1,34]]}'}
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',
133 rank_search=27, rank_address=26)
134 apiobj.add_address_placex(332, fromarea=False, isaddress=False,
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',
144 rank_search=17, rank_address=16)
146 result = apiobj.api.details(napi.PlaceID(332), address_details=True)
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)
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)
181 result = apiobj.api.details(napi.PlaceID(332), linked_places=True)
183 assert result.linked_rows == []
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)
200 result = apiobj.api.details(napi.PlaceID(332), linked_places=True)
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),
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)
222 result = apiobj.api.details(napi.PlaceID(332), parented_places=True)
224 assert result.parented_rows == []
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)
241 result = apiobj.api.details(napi.PlaceID(332), parented_places=True)
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),
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,
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)')
263 result = apiobj.api.details(idobj)
265 assert result is not None
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))
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
277 assert result.names is None
278 assert result.address == {'city': 'Big'}
279 assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
281 assert result.housenumber is None
282 assert result.postcode == '34425'
283 assert result.wikipedia is None
285 assert result.rank_search == 30
286 assert result.rank_address == 30
287 assert result.importance is None
289 assert result.country_code == 'gb'
290 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
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
298 assert result.geometry == {'type': 'ST_LineString'}
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)
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
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,
324 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
325 class_='highway', type='residential', name='Street',
327 rank_search=27, rank_address=26)
328 apiobj.add_address_placex(332, fromarea=False, isaddress=False,
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',
338 rank_search=17, rank_address=16)
340 result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
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)
369 def test_lookup_in_tiger(apiobj):
370 apiobj.add_tiger(place_id=4924,
372 startnumber=1, endnumber=4, step=1,
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)')
380 result = apiobj.api.details(napi.PlaceID(4924))
382 assert result is not None
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))
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
394 assert result.names is None
395 assert result.address is None
396 assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
398 assert result.housenumber is None
399 assert result.postcode == '34425'
400 assert result.wikipedia is None
402 assert result.rank_search == 30
403 assert result.rank_address == 30
404 assert result.importance is None
406 assert result.country_code == 'us'
407 assert result.indexed_date is None
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
415 assert result.geometry == {'type': 'ST_LineString'}
418 def test_lookup_tiger_with_address_details(apiobj):
419 apiobj.add_tiger(place_id=9000,
420 startnumber=2, endnumber=4, step=1,
422 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
423 class_='highway', type='residential', name='Street',
425 rank_search=27, rank_address=26)
426 apiobj.add_address_placex(332, fromarea=False, isaddress=False,
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',
436 rank_search=17, rank_address=16)
438 result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
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)
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,
473 rank_search=20, rank_address=22,
474 indexed_date=import_date,
475 geometry='POINT(-9.45 5.6)')
477 result = apiobj.api.details(napi.PlaceID(554))
479 assert result is not None
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))
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
491 assert result.names == {'ref': '34 425'}
492 assert result.address is None
493 assert result.extratags is None
495 assert result.housenumber is None
496 assert result.postcode is None
497 assert result.wikipedia is None
499 assert result.rank_search == 20
500 assert result.rank_address == 22
501 assert result.importance is None
503 assert result.country_code == 'gb'
504 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
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
512 assert result.geometry == {'type': 'ST_Point'}
515 def test_lookup_postcode_with_address_details(apiobj):
516 apiobj.add_postcode(place_id=9000,
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',
529 rank_search=17, rank_address=16)
531 result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
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)
559 @pytest.mark.parametrize('objid', [napi.PlaceID(1736),
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')
566 assert apiobj.api.details(objid) is None
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)
575 with pytest.raises(ValueError):
576 apiobj.api.details(napi.PlaceID(332), geometry_output=gtype)