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 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)
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)
178 result = apiobj.api.details(napi.PlaceID(332), linked_places=True)
180 assert result.linked_rows == []
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)
197 result = apiobj.api.details(napi.PlaceID(332), linked_places=True)
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),
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)
219 result = apiobj.api.details(napi.PlaceID(332), parented_places=True)
221 assert result.parented_rows == []
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)
238 result = apiobj.api.details(napi.PlaceID(332), parented_places=True)
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),
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,
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)')
260 result = apiobj.api.details(idobj)
262 assert result is not None
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))
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
274 assert result.names is None
275 assert result.address == {'city': 'Big'}
276 assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
278 assert result.housenumber is None
279 assert result.postcode == '34425'
280 assert result.wikipedia is None
282 assert result.rank_search == 30
283 assert result.rank_address == 30
284 assert result.importance is None
286 assert result.country_code == 'gb'
287 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
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
295 assert result.geometry == {'type': 'ST_LineString'}
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)
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
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,
321 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
322 class_='highway', type='residential', name='Street',
324 rank_search=27, rank_address=26)
325 apiobj.add_address_placex(332, fromarea=False, isaddress=False,
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',
335 rank_search=17, rank_address=16)
337 result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
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)
368 def test_lookup_in_tiger(apiobj):
369 apiobj.add_tiger(place_id=4924,
371 startnumber=1, endnumber=4, step=1,
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)')
379 result = apiobj.api.details(napi.PlaceID(4924))
381 assert result is not None
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))
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
393 assert result.names is None
394 assert result.address is None
395 assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
397 assert result.housenumber is None
398 assert result.postcode == '34425'
399 assert result.wikipedia is None
401 assert result.rank_search == 30
402 assert result.rank_address == 30
403 assert result.importance is None
405 assert result.country_code == 'us'
406 assert result.indexed_date is None
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
414 assert result.geometry == {'type': 'ST_LineString'}
417 def test_lookup_tiger_with_address_details(apiobj):
418 apiobj.add_tiger(place_id=9000,
419 startnumber=2, endnumber=4, step=1,
421 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
422 class_='highway', type='residential', name='Street',
424 rank_search=27, rank_address=26)
425 apiobj.add_address_placex(332, fromarea=False, isaddress=False,
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',
435 rank_search=17, rank_address=16)
437 result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
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)
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,
474 rank_search=20, rank_address=22,
475 indexed_date=import_date,
476 geometry='POINT(-9.45 5.6)')
478 result = apiobj.api.details(napi.PlaceID(554))
480 assert result is not None
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))
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
492 assert result.names == {'ref': '34 425'}
493 assert result.address is None
494 assert result.extratags is None
496 assert result.housenumber is None
497 assert result.postcode is None
498 assert result.wikipedia is None
500 assert result.rank_search == 20
501 assert result.rank_address == 22
502 assert result.importance is None
504 assert result.country_code == 'gb'
505 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
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
513 assert result.geometry == {'type': 'ST_Point'}
516 def test_lookup_postcode_with_address_details(apiobj):
517 apiobj.add_postcode(place_id=9000,
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',
530 rank_search=17, rank_address=16)
532 result = apiobj.api.details(napi.PlaceID(9000), address_details=True)
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)
557 @pytest.mark.parametrize('objid', [napi.PlaceID(1736),
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')
564 assert apiobj.api.details(objid) is None
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)
573 with pytest.raises(ValueError):
574 apiobj.api.details(napi.PlaceID(332), geometry_output=gtype)