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=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,
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)
375 def test_lookup_in_tiger(apiobj):
376 apiobj.add_tiger(place_id=4924,
378 startnumber=1, endnumber=4, step=1,
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)')
386 result = apiobj.api.details(napi.PlaceID(4924))
388 assert result is not None
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))
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
400 assert result.names is None
401 assert result.address is None
402 assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
404 assert result.housenumber is None
405 assert result.postcode == '34425'
406 assert result.wikipedia is None
408 assert result.rank_search == 30
409 assert result.rank_address == 30
410 assert result.importance is None
412 assert result.country_code == 'us'
413 assert result.indexed_date is None
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
421 assert result.geometry == {'type': 'ST_LineString'}
424 def test_lookup_tiger_with_address_details(apiobj):
425 apiobj.add_tiger(place_id=9000,
426 startnumber=2, endnumber=4, step=1,
428 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
429 class_='highway', type='residential', name='Street',
431 rank_search=27, rank_address=26)
432 apiobj.add_address_placex(332, fromarea=False, isaddress=False,
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',
442 rank_search=17, rank_address=16)
444 result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
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,
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)
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,
485 rank_search=20, rank_address=22,
486 indexed_date=import_date,
487 geometry='POINT(-9.45 5.6)')
489 result = apiobj.api.details(napi.PlaceID(554))
491 assert result is not None
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))
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
503 assert result.names == {'ref': '34 425'}
504 assert result.address is None
505 assert result.extratags is None
507 assert result.housenumber is None
508 assert result.postcode is None
509 assert result.wikipedia is None
511 assert result.rank_search == 20
512 assert result.rank_address == 22
513 assert result.importance is None
515 assert result.country_code == 'gb'
516 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
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
524 assert result.geometry == {'type': 'ST_Point'}
527 def test_lookup_postcode_with_address_details(apiobj):
528 apiobj.add_postcode(place_id=9000,
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',
541 rank_search=17, rank_address=16)
543 result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
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)
571 @pytest.mark.parametrize('objid', [napi.PlaceID(1736),
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')
578 assert apiobj.api.details(objid) is None
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)
587 with pytest.raises(ValueError):
588 apiobj.api.details(napi.PlaceID(332), geometry_output=gtype)