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)')
382 result = apiobj.api.lookup(napi.PlaceID(4924), napi.LookupDetails())
384 assert result is not None
386 assert result.source_table.name == 'TIGER'
387 assert result.category == ('place', 'houses')
388 assert result.centroid == (pytest.approx(23.0), pytest.approx(34.5))
390 assert result.place_id == 4924
391 assert result.parent_place_id == 12
392 assert result.linked_place_id is None
393 assert result.osm_object is None
394 assert result.admin_level == 15
396 assert result.names is None
397 assert result.address is None
398 assert result.extratags == {'startnumber': '1', 'endnumber': '4', 'step': '1'}
400 assert result.housenumber is None
401 assert result.postcode == '34425'
402 assert result.wikipedia is None
404 assert result.rank_search == 30
405 assert result.rank_address == 30
406 assert result.importance is None
408 assert result.country_code == 'us'
409 assert result.indexed_date is None
411 assert result.address_rows is None
412 assert result.linked_rows is None
413 assert result.parented_rows is None
414 assert result.name_keywords is None
415 assert result.address_keywords is None
417 assert result.geometry == {'type': 'ST_LineString'}
420 def test_lookup_tiger_with_address_details(apiobj):
421 apiobj.add_tiger(place_id=9000,
422 startnumber=2, endnumber=4, step=1,
424 apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
425 class_='highway', type='residential', name='Street',
427 rank_search=27, rank_address=26)
428 apiobj.add_address_placex(332, fromarea=False, isaddress=False,
430 place_id=1000, osm_type='N', osm_id=3333,
431 class_='place', type='suburb', name='Smallplace',
432 country_code='us', admin_level=13,
433 rank_search=24, rank_address=23)
434 apiobj.add_address_placex(332, fromarea=True, isaddress=True,
435 place_id=1001, osm_type='N', osm_id=3334,
436 class_='place', type='city', name='Bigplace',
438 rank_search=17, rank_address=16)
440 result = apiobj.api.lookup(napi.PlaceID(9000),
441 napi.LookupDetails(address_details=True))
443 assert result.address_rows == [
444 napi.AddressLine(place_id=None, osm_object=None,
445 category=('place', 'house_number'),
446 names={'ref': '2'}, extratags={},
447 admin_level=None, fromarea=True, isaddress=True,
448 rank_address=28, distance=0.0),
449 napi.AddressLine(place_id=332, osm_object=('W', 4),
450 category=('highway', 'residential'),
451 names={'name': 'Street'}, extratags={},
452 admin_level=15, fromarea=True, isaddress=True,
453 rank_address=26, distance=0.0),
454 napi.AddressLine(place_id=1000, osm_object=('N', 3333),
455 category=('place', 'suburb'),
456 names={'name': 'Smallplace'}, extratags={},
457 admin_level=13, fromarea=False, isaddress=True,
458 rank_address=23, distance=0.0034),
459 napi.AddressLine(place_id=1001, osm_object=('N', 3334),
460 category=('place', 'city'),
461 names={'name': 'Bigplace'}, extratags={},
462 admin_level=15, fromarea=True, isaddress=True,
463 rank_address=16, distance=0.0),
464 napi.AddressLine(place_id=None, osm_object=None,
465 category=('place', 'country_code'),
466 names={'ref': 'us'}, extratags={},
467 admin_level=None, fromarea=True, isaddress=False,
468 rank_address=4, distance=0.0)
472 def test_lookup_in_postcode(apiobj):
473 import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
474 apiobj.add_postcode(place_id=554,
478 rank_search=20, rank_address=22,
479 indexed_date=import_date,
480 geometry='POINT(-9.45 5.6)')
482 result = apiobj.api.lookup(napi.PlaceID(554), napi.LookupDetails())
484 assert result is not None
486 assert result.source_table.name == 'POSTCODE'
487 assert result.category == ('place', 'postcode')
488 assert result.centroid == (pytest.approx(-9.45), pytest.approx(5.6))
490 assert result.place_id == 554
491 assert result.parent_place_id == 152
492 assert result.linked_place_id is None
493 assert result.osm_object is None
494 assert result.admin_level == 15
496 assert result.names == {'ref': '34 425'}
497 assert result.address is None
498 assert result.extratags is None
500 assert result.housenumber is None
501 assert result.postcode is None
502 assert result.wikipedia is None
504 assert result.rank_search == 20
505 assert result.rank_address == 22
506 assert result.importance is None
508 assert result.country_code == 'gb'
509 assert result.indexed_date == import_date.replace(tzinfo=dt.timezone.utc)
511 assert result.address_rows is None
512 assert result.linked_rows is None
513 assert result.parented_rows is None
514 assert result.name_keywords is None
515 assert result.address_keywords is None
517 assert result.geometry == {'type': 'ST_Point'}
520 def test_lookup_postcode_with_address_details(apiobj):
521 apiobj.add_postcode(place_id=9000,
525 rank_search=25, rank_address=25)
526 apiobj.add_placex(place_id=332, osm_type='N', osm_id=3333,
527 class_='place', type='suburb', name='Smallplace',
528 country_code='gb', admin_level=13,
529 rank_search=24, rank_address=23)
530 apiobj.add_address_placex(332, fromarea=True, isaddress=True,
531 place_id=1001, osm_type='N', osm_id=3334,
532 class_='place', type='city', name='Bigplace',
534 rank_search=17, rank_address=16)
536 result = apiobj.api.lookup(napi.PlaceID(9000),
537 napi.LookupDetails(address_details=True))
539 assert result.address_rows == [
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 napi.AddressLine(place_id=1001, osm_object=('N', 3334),
546 category=('place', 'city'),
547 names={'name': 'Bigplace'}, extratags={},
548 admin_level=15, fromarea=True, isaddress=True,
549 rank_address=16, distance=0.0),
550 napi.AddressLine(place_id=None, osm_object=None,
551 category=('place', 'postcode'),
552 names={'ref': '34 425'}, extratags={},
553 admin_level=None, fromarea=False, isaddress=True,
554 rank_address=5, distance=0.0),
555 napi.AddressLine(place_id=None, osm_object=None,
556 category=('place', 'country_code'),
557 names={'ref': 'gb'}, extratags={},
558 admin_level=None, fromarea=True, isaddress=False,
559 rank_address=4, distance=0.0)
562 @pytest.mark.parametrize('objid', [napi.PlaceID(1736),
564 napi.OsmID('N', 55, 'amenity')])
565 def test_lookup_missing_object(apiobj, objid):
566 apiobj.add_placex(place_id=1, osm_type='N', osm_id=55,
567 class_='place', type='suburb')
569 assert apiobj.api.lookup(objid, napi.LookupDetails()) is None
572 @pytest.mark.parametrize('gtype', (napi.GeometryFormat.KML,
573 napi.GeometryFormat.SVG,
574 napi.GeometryFormat.TEXT))
575 def test_lookup_unsupported_geometry(apiobj, gtype):
576 apiobj.add_placex(place_id=332)
578 with pytest.raises(ValueError):
579 apiobj.api.lookup(napi.PlaceID(332),
580 napi.LookupDetails(geometry_output=gtype))