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 lookup 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.lookup(idobj, napi.LookupDetails())
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.lookup(napi.PlaceID(332), napi.LookupDetails())
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.lookup(napi.PlaceID(332),
125 napi.LookupDetails(geometry_output=napi.GeometryFormat.GEOJSON))
127 assert result.geometry == {'geojson': '{"type":"LineString","coordinates":[[23,34],[23.1,34]]}'}
130 def test_lookup_placex_with_address_details(apiobj):
131 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
132 class_='highway', type='residential', name='Street',
134 rank_search=27, rank_address=26)
135 apiobj.add_address_placex(332, fromarea=False, isaddress=False,
137 place_id=1000, osm_type='N', osm_id=3333,
138 class_='place', type='suburb', name='Smallplace',
139 country_code='pl', admin_level=13,
140 rank_search=24, rank_address=23)
141 apiobj.add_address_placex(332, fromarea=True, isaddress=True,
142 place_id=1001, osm_type='N', osm_id=3334,
143 class_='place', type='city', name='Bigplace',
145 rank_search=17, rank_address=16)
147 result = apiobj.api.lookup(napi.PlaceID(332),
148 napi.LookupDetails(address_details=True))
150 assert result.address_rows == [
151 napi.AddressLine(place_id=332, osm_object=('W', 4),
152 category=('highway', 'residential'),
153 names={'name': 'Street'}, extratags={},
154 admin_level=15, fromarea=True, isaddress=True,
155 rank_address=26, distance=0.0),
156 napi.AddressLine(place_id=1000, osm_object=('N', 3333),
157 category=('place', 'suburb'),
158 names={'name': 'Smallplace'}, extratags={},
159 admin_level=13, fromarea=False, isaddress=True,
160 rank_address=23, distance=0.0034),
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 napi.AddressLine(place_id=None, osm_object=None,
167 category=('place', 'country_code'),
168 names={'ref': 'pl'}, extratags={},
169 admin_level=None, fromarea=True, isaddress=False,
170 rank_address=4, distance=0.0)
174 def test_lookup_place_with_linked_places_none_existing(apiobj):
175 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
176 class_='highway', type='residential', name='Street',
177 country_code='pl', linked_place_id=45,
178 rank_search=27, rank_address=26)
180 result = apiobj.api.lookup(napi.PlaceID(332),
181 napi.LookupDetails(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.lookup(napi.PlaceID(332),
201 napi.LookupDetails(linked_places=True))
203 assert result.linked_rows == [
204 napi.AddressLine(place_id=1001, osm_object=('W', 5),
205 category=('highway', 'residential'),
206 names={'name': 'Street'}, extratags={},
207 admin_level=15, fromarea=False, isaddress=True,
208 rank_address=26, distance=0.0),
209 napi.AddressLine(place_id=1002, osm_object=('W', 6),
210 category=('highway', 'residential'),
211 names={'name': 'Street'}, extratags={},
212 admin_level=15, fromarea=False, isaddress=True,
213 rank_address=26, distance=0.0),
217 def test_lookup_place_with_parented_places_not_existing(apiobj):
218 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
219 class_='highway', type='residential', name='Street',
220 country_code='pl', parent_place_id=45,
221 rank_search=27, rank_address=26)
223 result = apiobj.api.lookup(napi.PlaceID(332),
224 napi.LookupDetails(parented_places=True))
226 assert result.parented_rows == []
229 def test_lookup_place_with_parented_places_existing(apiobj):
230 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
231 class_='highway', type='residential', name='Street',
232 country_code='pl', parent_place_id=45,
233 rank_search=27, rank_address=26)
234 apiobj.add_placex(place_id=1001, osm_type='N', osm_id=5,
235 class_='place', type='house', housenumber='23',
236 country_code='pl', parent_place_id=332,
237 rank_search=30, rank_address=30)
238 apiobj.add_placex(place_id=1002, osm_type='W', osm_id=6,
239 class_='highway', type='residential', name='Street',
240 country_code='pl', parent_place_id=332,
241 rank_search=27, rank_address=26)
243 result = apiobj.api.lookup(napi.PlaceID(332),
244 napi.LookupDetails(parented_places=True))
246 assert result.parented_rows == [
247 napi.AddressLine(place_id=1001, osm_object=('N', 5),
248 category=('place', 'house'),
249 names={'housenumber': '23'}, extratags={},
250 admin_level=15, fromarea=False, isaddress=True,
251 rank_address=30, distance=0.0),
255 @pytest.mark.parametrize('idobj', (napi.PlaceID(4924), napi.OsmID('W', 9928)))
256 def test_lookup_in_osmline(apiobj, idobj):
257 import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
258 apiobj.add_osmline(place_id=4924, osm_id=9928,
260 startnumber=1, endnumber=4, step=1,
261 country_code='gb', postcode='34425',
262 address={'city': 'Big'},
263 indexed_date=import_date,
264 geometry='LINESTRING(23 34, 23 35)')
266 result = apiobj.api.lookup(idobj, napi.LookupDetails())
268 assert result is not None
270 assert result.source_table.name == 'OSMLINE'
271 assert result.category == ('place', 'houses')
272 assert result.centroid == (pytest.approx(23.0), pytest.approx(34.5))
274 assert result.place_id == 4924
275 assert result.parent_place_id == 12
276 assert result.linked_place_id is None
277 assert result.osm_object == ('W', 9928)
278 assert result.admin_level == 15
280 assert result.names is None
281 assert result.address == {'city': 'Big'}
282 assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
284 assert result.housenumber is None
285 assert result.postcode == '34425'
286 assert result.wikipedia is None
288 assert result.rank_search == 30
289 assert result.rank_address == 30
290 assert result.importance is None
292 assert result.country_code == 'gb'
293 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
295 assert result.address_rows is None
296 assert result.linked_rows is None
297 assert result.parented_rows is None
298 assert result.name_keywords is None
299 assert result.address_keywords is None
301 assert result.geometry == {'type': 'ST_LineString'}
304 def test_lookup_in_osmline_split_interpolation(apiobj):
305 apiobj.add_osmline(place_id=1000, osm_id=9,
306 startnumber=2, endnumber=4, step=1)
307 apiobj.add_osmline(place_id=1001, osm_id=9,
308 startnumber=6, endnumber=9, step=1)
309 apiobj.add_osmline(place_id=1002, osm_id=9,
310 startnumber=11, endnumber=20, step=1)
312 for i in range(1, 6):
313 result = apiobj.api.lookup(napi.OsmID('W', 9, str(i)), napi.LookupDetails())
314 assert result.place_id == 1000
315 for i in range(7, 11):
316 result = apiobj.api.lookup(napi.OsmID('W', 9, str(i)), napi.LookupDetails())
317 assert result.place_id == 1001
318 for i in range(12, 22):
319 result = apiobj.api.lookup(napi.OsmID('W', 9, str(i)), napi.LookupDetails())
320 assert result.place_id == 1002
323 def test_lookup_osmline_with_address_details(apiobj):
324 apiobj.add_osmline(place_id=9000, osm_id=9,
325 startnumber=2, endnumber=4, step=1,
327 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
328 class_='highway', type='residential', name='Street',
330 rank_search=27, rank_address=26)
331 apiobj.add_address_placex(332, fromarea=False, isaddress=False,
333 place_id=1000, osm_type='N', osm_id=3333,
334 class_='place', type='suburb', name='Smallplace',
335 country_code='pl', admin_level=13,
336 rank_search=24, rank_address=23)
337 apiobj.add_address_placex(332, fromarea=True, isaddress=True,
338 place_id=1001, osm_type='N', osm_id=3334,
339 class_='place', type='city', name='Bigplace',
341 rank_search=17, rank_address=16)
343 result = apiobj.api.lookup(napi.PlaceID(9000),
344 napi.LookupDetails(address_details=True))
346 assert result.address_rows == [
347 napi.AddressLine(place_id=None, osm_object=None,
348 category=('place', 'house_number'),
349 names={'ref': '2'}, extratags={},
350 admin_level=None, fromarea=True, isaddress=True,
351 rank_address=28, distance=0.0),
352 napi.AddressLine(place_id=332, osm_object=('W', 4),
353 category=('highway', 'residential'),
354 names={'name': 'Street'}, extratags={},
355 admin_level=15, fromarea=True, isaddress=True,
356 rank_address=26, distance=0.0),
357 napi.AddressLine(place_id=1000, osm_object=('N', 3333),
358 category=('place', 'suburb'),
359 names={'name': 'Smallplace'}, extratags={},
360 admin_level=13, fromarea=False, isaddress=True,
361 rank_address=23, distance=0.0034),
362 napi.AddressLine(place_id=1001, osm_object=('N', 3334),
363 category=('place', 'city'),
364 names={'name': 'Bigplace'}, extratags={},
365 admin_level=15, fromarea=True, isaddress=True,
366 rank_address=16, distance=0.0),
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.lookup(napi.PlaceID(4924), napi.LookupDetails())
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.lookup(napi.PlaceID(9000),
445 napi.LookupDetails(address_details=True))
447 assert result.address_rows == [
448 napi.AddressLine(place_id=None, osm_object=None,
449 category=('place', 'house_number'),
450 names={'ref': '2'}, extratags={},
451 admin_level=None, fromarea=True, isaddress=True,
452 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 napi.AddressLine(place_id=1000, osm_object=('N', 3333),
459 category=('place', 'suburb'),
460 names={'name': 'Smallplace'}, extratags={},
461 admin_level=13, fromarea=False, isaddress=True,
462 rank_address=23, distance=0.0034),
463 napi.AddressLine(place_id=1001, osm_object=('N', 3334),
464 category=('place', 'city'),
465 names={'name': 'Bigplace'}, extratags={},
466 admin_level=15, fromarea=True, isaddress=True,
467 rank_address=16, distance=0.0),
468 napi.AddressLine(place_id=None, osm_object=None,
469 category=('place', 'country_code'),
470 names={'ref': 'us'}, extratags={},
471 admin_level=None, fromarea=True, isaddress=False,
472 rank_address=4, distance=0.0)
476 def test_lookup_in_postcode(apiobj):
477 import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
478 apiobj.add_postcode(place_id=554,
482 rank_search=20, rank_address=22,
483 indexed_date=import_date,
484 geometry='POINT(-9.45 5.6)')
486 result = apiobj.api.lookup(napi.PlaceID(554), napi.LookupDetails())
488 assert result is not None
490 assert result.source_table.name == 'POSTCODE'
491 assert result.category == ('place', 'postcode')
492 assert result.centroid == (pytest.approx(-9.45), pytest.approx(5.6))
494 assert result.place_id == 554
495 assert result.parent_place_id == 152
496 assert result.linked_place_id is None
497 assert result.osm_object is None
498 assert result.admin_level == 15
500 assert result.names == {'ref': '34 425'}
501 assert result.address is None
502 assert result.extratags is None
504 assert result.housenumber is None
505 assert result.postcode is None
506 assert result.wikipedia is None
508 assert result.rank_search == 20
509 assert result.rank_address == 22
510 assert result.importance is None
512 assert result.country_code == 'gb'
513 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
515 assert result.address_rows is None
516 assert result.linked_rows is None
517 assert result.parented_rows is None
518 assert result.name_keywords is None
519 assert result.address_keywords is None
521 assert result.geometry == {'type': 'ST_Point'}
524 def test_lookup_postcode_with_address_details(apiobj):
525 apiobj.add_postcode(place_id=9000,
529 rank_search=25, rank_address=25)
530 apiobj.add_placex(place_id=332, osm_type='N', osm_id=3333,
531 class_='place', type='suburb', name='Smallplace',
532 country_code='gb', admin_level=13,
533 rank_search=24, rank_address=23)
534 apiobj.add_address_placex(332, fromarea=True, isaddress=True,
535 place_id=1001, osm_type='N', osm_id=3334,
536 class_='place', type='city', name='Bigplace',
538 rank_search=17, rank_address=16)
540 result = apiobj.api.lookup(napi.PlaceID(9000),
541 napi.LookupDetails(address_details=True))
543 assert result.address_rows == [
544 napi.AddressLine(place_id=332, osm_object=('N', 3333),
545 category=('place', 'suburb'),
546 names={'name': 'Smallplace'}, extratags={},
547 admin_level=13, fromarea=True, isaddress=True,
548 rank_address=23, distance=0.0),
549 napi.AddressLine(place_id=1001, osm_object=('N', 3334),
550 category=('place', 'city'),
551 names={'name': 'Bigplace'}, extratags={},
552 admin_level=15, fromarea=True, isaddress=True,
553 rank_address=16, distance=0.0),
554 napi.AddressLine(place_id=None, osm_object=None,
555 category=('place', 'postcode'),
556 names={'ref': '34 425'}, extratags={},
557 admin_level=None, fromarea=False, isaddress=True,
558 rank_address=5, distance=0.0),
559 napi.AddressLine(place_id=None, osm_object=None,
560 category=('place', 'country_code'),
561 names={'ref': 'gb'}, extratags={},
562 admin_level=None, fromarea=True, isaddress=False,
563 rank_address=4, distance=0.0)
566 @pytest.mark.parametrize('objid', [napi.PlaceID(1736),
568 napi.OsmID('N', 55, 'amenity')])
569 def test_lookup_missing_object(apiobj, objid):
570 apiobj.add_placex(place_id=1, osm_type='N', osm_id=55,
571 class_='place', type='suburb')
573 assert apiobj.api.lookup(objid, napi.LookupDetails()) is None
576 @pytest.mark.parametrize('gtype', (napi.GeometryFormat.KML,
577 napi.GeometryFormat.SVG,
578 napi.GeometryFormat.TEXT))
579 def test_lookup_unsupported_geometry(apiobj, gtype):
580 apiobj.add_placex(place_id=332)
582 with pytest.raises(ValueError):
583 apiobj.api.lookup(napi.PlaceID(332),
584 napi.LookupDetails(geometry_output=gtype))