mirror of
https://github.com/osm-search/Nominatim.git
synced 2026-02-16 15:47:58 +00:00
implement BDD osm2pgsql tests with pytest-bdd
This commit is contained in:
@@ -11,18 +11,24 @@ import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from pytest_bdd.parsers import re as step_parse
|
||||
from pytest_bdd import when, then
|
||||
|
||||
from utils.api_runner import APIRunner
|
||||
from utils.api_result import APIResult
|
||||
from utils.checks import ResultAttr, COMPARATOR_TERMS
|
||||
|
||||
# always test against the source
|
||||
SRC_DIR = (Path(__file__) / '..' / '..' / '..').resolve()
|
||||
sys.path.insert(0, str(SRC_DIR / 'src'))
|
||||
|
||||
import pytest
|
||||
from pytest_bdd.parsers import re as step_parse
|
||||
from pytest_bdd import given, when, then
|
||||
|
||||
pytest.register_assert_rewrite('utils')
|
||||
|
||||
from utils.api_runner import APIRunner
|
||||
from utils.api_result import APIResult
|
||||
from utils.checks import ResultAttr, COMPARATOR_TERMS
|
||||
from utils.geometry_alias import ALIASES
|
||||
from utils.grid import Grid
|
||||
from utils.db import DBManager
|
||||
|
||||
from nominatim_db.config import Configuration
|
||||
|
||||
def _strlist(inp):
|
||||
return [s.strip() for s in inp.split(',')]
|
||||
@@ -60,6 +66,35 @@ def datatable():
|
||||
return None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def node_grid():
|
||||
""" Default fixture for node grids. Nothing set.
|
||||
"""
|
||||
return Grid([[]], None, None)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def template_db(pytestconfig):
|
||||
""" Create a template database containing the extensions and base data
|
||||
needed by Nominatim. Using the template instead of doing the full
|
||||
setup can speed up the tests.
|
||||
|
||||
The template database will only be created if it does not exist yet
|
||||
or a purge has been explicitly requested.
|
||||
"""
|
||||
dbm = DBManager(purge=pytestconfig.option.NOMINATIM_PURGE)
|
||||
|
||||
template_db = pytestconfig.getini('nominatim_template_db')
|
||||
|
||||
template_config = Configuration(
|
||||
None, environ={'NOMINATIM_DATABASE_DSN': f"pgsql:dbname={template_db}"})
|
||||
|
||||
dbm.setup_template_db(template_config)
|
||||
|
||||
return template_db
|
||||
|
||||
|
||||
|
||||
@when(step_parse(r'reverse geocoding (?P<lat>[\d.-]*),(?P<lon>[\d.-]*)'),
|
||||
target_fixture='nominatim_result')
|
||||
def reverse_geocode_via_api(test_config_env, pytestconfig, datatable, lat, lon):
|
||||
@@ -223,3 +258,23 @@ def check_specific_result_for_fields(nominatim_result, datatable, num, field):
|
||||
|
||||
for k, v in pairs:
|
||||
assert ResultAttr(nominatim_result.result[num], prefix + k) == v
|
||||
|
||||
|
||||
@given(step_parse(r'the (?P<step>[0-9.]+ )?grid(?: with origin (?P<origin>.*))?'),
|
||||
target_fixture='node_grid')
|
||||
def set_node_grid(datatable, step, origin):
|
||||
if step is not None:
|
||||
step = float(step)
|
||||
|
||||
if origin:
|
||||
if ',' in origin:
|
||||
coords = origin.split(',')
|
||||
if len(coords) != 2:
|
||||
raise RuntimeError('Grid origin expects origin with x,y coordinates.')
|
||||
origin = list(map(float, coords))
|
||||
elif origin in ALIASES:
|
||||
origin = ALIASES[origin]
|
||||
else:
|
||||
raise RuntimeError('Grid origin must be either coordinate or alias.')
|
||||
|
||||
return Grid(datatable, step, origin)
|
||||
|
||||
@@ -136,8 +136,8 @@ Feature: Json output for Reverse API
|
||||
Then a HTTP 200 is returned
|
||||
And the result is valid json
|
||||
And the result contains
|
||||
| geotext!fm |
|
||||
| LINESTRING\(9.5039353 47.0657546, ?9.5040437 47.0657781, ?9.5040808 47.065787, ?9.5054298 47.0661407\) |
|
||||
| geotext!wkt |
|
||||
| 9.5039353 47.0657546, 9.5040437 47.0657781, 9.5040808 47.065787, 9.5054298 47.0661407 |
|
||||
|
||||
Examples:
|
||||
| format |
|
||||
|
||||
@@ -92,8 +92,8 @@ Feature: XML output for Reverse API
|
||||
Then a HTTP 200 is returned
|
||||
And the result is valid xml
|
||||
And the result contains
|
||||
| geotext!fm |
|
||||
| LINESTRING\(9.5039353 47.0657546, ?9.5040437 47.0657781, ?9.5040808 47.065787, ?9.5054298 47.0661407\) |
|
||||
| geotext!wkt |
|
||||
| 9.5039353 47.0657546, 9.5040437 47.0657781, 9.5040808 47.065787, 9.5054298 47.0661407 |
|
||||
|
||||
Scenario: Reverse XML - Output of SVG
|
||||
When sending v1/reverse with format xml
|
||||
|
||||
35
test/bdd/features/osm2pgsql/import/broken.feature
Normal file
35
test/bdd/features/osm2pgsql/import/broken.feature
Normal file
@@ -0,0 +1,35 @@
|
||||
Feature: Import of objects with broken geometries by osm2pgsql
|
||||
|
||||
Scenario: Import way with double nodes
|
||||
When loading osm data
|
||||
"""
|
||||
n100 x0 y0
|
||||
n101 x0 y0.1
|
||||
n102 x0.1 y0.2
|
||||
w1 Thighway=primary Nn100,n101,n101,n102
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type | geometry!wkt |
|
||||
| W1 | highway | primary | 0 0, 0 0.1, 0.1 0.2 |
|
||||
|
||||
Scenario: Import of ballon areas
|
||||
Given the grid
|
||||
| 2 | | 3 |
|
||||
| 1 | | 4 |
|
||||
| 5 | | |
|
||||
When loading osm data
|
||||
"""
|
||||
n1
|
||||
n2
|
||||
n3
|
||||
n4
|
||||
n5
|
||||
w1 Thighway=unclassified Nn1,n2,n3,n4,n1,n5
|
||||
w2 Thighway=unclassified Nn1,n2,n3,n4,n1
|
||||
w3 Thighway=unclassified Nn1,n2,n3,n4,n3
|
||||
"""
|
||||
Then place contains
|
||||
| object | geometry!wkt |
|
||||
| W1 | 1,2,3,4,1,5 |
|
||||
| W2 | (1,2,3,4,1) |
|
||||
| W3 | 1,2,3,4 |
|
||||
318
test/bdd/features/osm2pgsql/import/custom_style.feature
Normal file
318
test/bdd/features/osm2pgsql/import/custom_style.feature
Normal file
@@ -0,0 +1,318 @@
|
||||
Feature: Import with custom styles by osm2pgsql
|
||||
Tests for the example customizations given in the documentation.
|
||||
|
||||
Scenario: Custom main tags (set new ones)
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
flex.set_main_tags{
|
||||
boundary = {administrative = 'named'},
|
||||
highway = {'always', street_lamp = 'named'},
|
||||
landuse = 'fallback'
|
||||
}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n10 Tboundary=administrative x0 y0
|
||||
n11 Tboundary=administrative,name=Foo x0 y0
|
||||
n12 Tboundary=electoral x0 y0
|
||||
n13 Thighway=primary x0 y0
|
||||
n14 Thighway=street_lamp x0 y0
|
||||
n15 Thighway=primary,landuse=street x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N11 | boundary | administrative |
|
||||
| N13 | highway | primary |
|
||||
| N15 | highway | primary |
|
||||
|
||||
Scenario: Custom main tags (modify existing)
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
flex.modify_main_tags{
|
||||
amenity = {prison = 'delete'},
|
||||
highway = {stop = 'named'},
|
||||
aeroway = 'named'
|
||||
}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n10 Tamenity=hotel x0 y0
|
||||
n11 Tamenity=prison x0 y0
|
||||
n12 Thighway=stop x0 y0
|
||||
n13 Thighway=stop,name=BigStop x0 y0
|
||||
n14 Thighway=give_way x0 y0
|
||||
n15 Thighway=bus_stop x0 y0
|
||||
n16 Taeroway=no,name=foo x0 y0
|
||||
n17 Taeroway=taxiway,name=D15 x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N10 | amenity | hotel |
|
||||
| N13 | highway | stop |
|
||||
| N15 | highway | bus_stop |
|
||||
| N17 | aeroway | taxiway |
|
||||
|
||||
Scenario: Prefiltering tags
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
flex.set_prefilters{
|
||||
delete_keys = {'source', 'source:*'},
|
||||
extra_tags = {amenity = {'yes', 'no'}}
|
||||
}
|
||||
flex.set_main_tags{
|
||||
amenity = 'always',
|
||||
tourism = 'always'
|
||||
}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Tamenity=yes x0 y6
|
||||
n2 Tamenity=hospital,source=survey x3 y6
|
||||
n3 Ttourism=hotel,amenity=yes x0 y0
|
||||
n4 Ttourism=hotel,amenity=telephone x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | extratags!dict |
|
||||
| N2 | amenity | - |
|
||||
| N3 | tourism | 'amenity': 'yes' |
|
||||
| N4 | tourism | - |
|
||||
| N4 | amenity | - |
|
||||
|
||||
Scenario: Ignore some tags
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-extratags')
|
||||
|
||||
flex.ignore_keys{'ref:*', 'surface'}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n100 Thighway=residential,ref=34,ref:bodo=34,surface=gray,extra=1 x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | name!dict | extratags!dict |
|
||||
| N100 | 'ref' : '34' | 'extra': '1' |
|
||||
|
||||
|
||||
Scenario: Add for extratags
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
flex.add_for_extratags{'ref:*', 'surface'}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n100 Thighway=residential,ref=34,ref:bodo=34,surface=gray,extra=1 x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | name!dict | extratags!dict |
|
||||
| N100 | 'ref' : '34' | 'ref:bodo': '34', 'surface': 'gray' |
|
||||
|
||||
|
||||
Scenario: Name tags
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('flex-base')
|
||||
|
||||
flex.set_main_tags{highway = {traffic_light = 'named'}}
|
||||
flex.set_name_tags{main = {'name', 'name:*'},
|
||||
extra = {'ref'}
|
||||
}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Thighway=stop,name=Something x0 y0
|
||||
n2 Thighway=traffic_light,ref=453-4 x0 y0
|
||||
n3 Thighway=traffic_light,name=Greens x0 y0
|
||||
n4 Thighway=traffic_light,name=Red,ref=45 x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | name!dict |
|
||||
| N3 | highway | 'name': 'Greens' |
|
||||
| N4 | highway | 'name': 'Red', 'ref': '45' |
|
||||
|
||||
Scenario: Modify name tags
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
flex.modify_name_tags{house = {}, extra = {'o'}}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Ttourism=hotel,ref=45,o=good
|
||||
n2 Taddr:housename=Old,addr:street=Away
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | name!dict |
|
||||
| N1 | tourism | 'o': 'good' |
|
||||
|
||||
Scenario: Address tags
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
flex.set_address_tags{
|
||||
main = {'addr:housenumber'},
|
||||
extra = {'addr:*'},
|
||||
postcode = {'postal_code', 'postcode', 'addr:postcode'},
|
||||
country = {'country-code', 'ISO3166-1'}
|
||||
}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Ttourism=hotel,addr:street=Foo x0 y0
|
||||
n2 Taddr:housenumber=23,addr:street=Budd,postal_code=5567 x0 y0
|
||||
n3 Taddr:street=None,addr:city=Where x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | address!dict |
|
||||
| N1 | tourism | hotel | 'street': 'Foo' |
|
||||
| N2 | place | house | 'housenumber': '23', 'street': 'Budd', 'postcode': '5567' |
|
||||
|
||||
Scenario: Modify address tags
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
flex.set_address_tags{
|
||||
extra = {'addr:*'},
|
||||
}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n2 Taddr:housenumber=23,addr:street=Budd,is_in:city=Faraway,postal_code=5567 x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | address!dict |
|
||||
| N2 | place | house | 'housenumber': '23', 'street': 'Budd', 'postcode': '5567' |
|
||||
|
||||
Scenario: Unused handling (delete)
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
flex.set_address_tags{
|
||||
main = {'addr:housenumber'},
|
||||
extra = {'addr:*', 'tiger:county'}
|
||||
}
|
||||
flex.set_unused_handling{delete_keys = {'tiger:*'}}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Ttourism=hotel,tiger:county=Fargo x0 y0
|
||||
n2 Ttourism=hotel,tiger:xxd=56,else=other x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | address!dict | extratags!dict |
|
||||
| N1 | tourism | hotel | 'tiger:county': 'Fargo' | - |
|
||||
| N2 | tourism | hotel | - | 'else': 'other' |
|
||||
|
||||
Scenario: Unused handling (extra)
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('flex-base')
|
||||
flex.set_main_tags{highway = 'always',
|
||||
wikipedia = 'extra'}
|
||||
flex.add_for_extratags{'wikipedia:*', 'wikidata'}
|
||||
flex.set_unused_handling{extra_keys = {'surface'}}
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n100 Thighway=path,foo=bar,wikipedia=en:Path x0 y0
|
||||
n234 Thighway=path,surface=rough x0 y0
|
||||
n445 Thighway=path,name=something x0 y0
|
||||
n446 Thighway=path,wikipedia:en=Path,wikidata=Q23 x0 y0
|
||||
n567 Thighway=path,surface=dirt,wikipedia:en=Path x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | extratags!dict |
|
||||
| N100 | highway | path | 'wikipedia': 'en:Path' |
|
||||
| N234 | highway | path | 'surface': 'rough' |
|
||||
| N445 | highway | path | - |
|
||||
| N446 | highway | path | 'wikipedia:en': 'Path', 'wikidata': 'Q23' |
|
||||
| N567 | highway | path | 'surface': 'dirt', 'wikipedia:en': 'Path' |
|
||||
|
||||
Scenario: Additional relation types
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
flex.RELATION_TYPES['site'] = flex.relation_as_multipolygon
|
||||
"""
|
||||
And the grid
|
||||
| 1 | 2 |
|
||||
| 4 | 3 |
|
||||
When loading osm data
|
||||
"""
|
||||
n1
|
||||
n2
|
||||
n3
|
||||
n4
|
||||
w1 Nn1,n2,n3,n4,n1
|
||||
r1 Ttype=multipolygon,amenity=school Mw1@
|
||||
r2 Ttype=site,amenity=school Mw1@
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| R1 | amenity | school |
|
||||
| R2 | amenity | school |
|
||||
|
||||
Scenario: Exclude country relations
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
function osm2pgsql.process_relation(object)
|
||||
if object.tags.boundary ~= 'administrative' or object.tags.admin_level ~= '2' then
|
||||
flex.process_relation(object)
|
||||
end
|
||||
end
|
||||
"""
|
||||
And the grid
|
||||
| 1 | 2 |
|
||||
| 4 | 3 |
|
||||
When loading osm data
|
||||
"""
|
||||
n1
|
||||
n2
|
||||
n3
|
||||
n4
|
||||
w1 Nn1,n2,n3,n4,n1
|
||||
r1 Ttype=multipolygon,boundary=administrative,admin_level=4,name=Small Mw1@
|
||||
r2 Ttype=multipolygon,boundary=administrative,admin_level=2,name=Big Mw1@
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| R1 | boundary | administrative |
|
||||
|
||||
Scenario: Customize processing functions
|
||||
Given the lua style file
|
||||
"""
|
||||
local flex = require('import-full')
|
||||
|
||||
local original_process_tags = flex.process_tags
|
||||
|
||||
function flex.process_tags(o)
|
||||
if o.object.tags.highway ~= nil and o.object.tags.access == 'no' then
|
||||
return
|
||||
end
|
||||
|
||||
original_process_tags(o)
|
||||
end
|
||||
"""
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Thighway=residential x0 y0
|
||||
n2 Thighway=residential,access=no x0 y0
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N1 | highway | residential |
|
||||
10
test/bdd/features/osm2pgsql/import/relation.feature
Normal file
10
test/bdd/features/osm2pgsql/import/relation.feature
Normal file
@@ -0,0 +1,10 @@
|
||||
Feature: Import of relations by osm2pgsql
|
||||
Testing specific relation problems related to members.
|
||||
|
||||
Scenario: Don't import empty waterways
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Tamenity=prison,name=foo
|
||||
r1 Ttype=waterway,waterway=river,name=XZ Mn1@
|
||||
"""
|
||||
Then place has no entry for R1
|
||||
42
test/bdd/features/osm2pgsql/import/simple.feature
Normal file
42
test/bdd/features/osm2pgsql/import/simple.feature
Normal file
@@ -0,0 +1,42 @@
|
||||
Feature: Import of simple objects by osm2pgsql
|
||||
Testing basic tagging in osm2pgsql imports.
|
||||
|
||||
Scenario: Import simple objects
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Tamenity=prison,name=foo x34.3 y-23
|
||||
n100 x0 y0
|
||||
n101 x0 y0.1
|
||||
n102 x0.1 y0.2
|
||||
n200 x0 y0
|
||||
n201 x0 y1
|
||||
n202 x1 y1
|
||||
n203 x1 y0
|
||||
w1 Tshop=toys,name=tata Nn100,n101,n102
|
||||
w2 Tref=45 Nn200,n201,n202,n203,n200
|
||||
r1 Ttype=multipolygon,tourism=hotel,name=XZ Mn1@,w2@
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict | geometry!wkt |
|
||||
| N1 | amenity | prison | 'name' : 'foo' | 34.3 -23 |
|
||||
| W1 | shop | toys | 'name' : 'tata' | 0 0, 0 0.1, 0.1 0.2 |
|
||||
| R1 | tourism | hotel | 'name' : 'XZ' | (0 0, 0 1, 1 1, 1 0, 0 0) |
|
||||
|
||||
Scenario: Import object with two main tags
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Ttourism=hotel,amenity=restaurant,name=foo
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N1 | tourism | hotel | 'name' : 'foo' |
|
||||
| N1 | amenity | restaurant | 'name' : 'foo' |
|
||||
|
||||
Scenario: Import stand-alone house number with postcode
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=4,addr:postcode=3345
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
289
test/bdd/features/osm2pgsql/import/tags.feature
Normal file
289
test/bdd/features/osm2pgsql/import/tags.feature
Normal file
@@ -0,0 +1,289 @@
|
||||
Feature: Tag evaluation
|
||||
Tests if tags are correctly imported into the place table
|
||||
|
||||
Scenario: Main tags as fallback
|
||||
When loading osm data
|
||||
"""
|
||||
n100 Tjunction=yes,highway=bus_stop
|
||||
n101 Tjunction=yes,name=Bar
|
||||
n200 Tbuilding=yes,amenity=cafe
|
||||
n201 Tbuilding=yes,name=Intersting
|
||||
n202 Tbuilding=yes
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N100 | highway | bus_stop |
|
||||
| N101 | junction | yes |
|
||||
| N200 | amenity | cafe |
|
||||
| N201 | building | yes |
|
||||
|
||||
|
||||
Scenario: Name and reg tags
|
||||
When loading osm data
|
||||
"""
|
||||
n2001 Thighway=road,name=Foo,alt_name:de=Bar,ref=45
|
||||
n2002 Thighway=road,name:prefix=Pre,name:suffix=Post,ref:de=55
|
||||
n2003 Thighway=yes,name:%20%de=Foo,name=real1
|
||||
n2004 Thighway=yes,name:%a%de=Foo,name=real2
|
||||
n2005 Thighway=yes,name:%9%de=Foo,name:\\=real3
|
||||
n2006 Thighway=yes,name:%9%de=Foo,name=rea\l3
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N2001 | highway | road | 'name': 'Foo', 'alt_name:de': 'Bar', 'ref': '45' |
|
||||
| N2002 | highway | road | - |
|
||||
| N2003 | highway | yes | 'name: de': 'Foo', 'name': 'real1' |
|
||||
| N2004 | highway | yes | 'name:\\nde': 'Foo', 'name': 'real2' |
|
||||
| N2005 | highway | yes | 'name:\tde': 'Foo', r'name:\\\\': 'real3' |
|
||||
| N2006 | highway | yes | 'name:\tde': 'Foo', 'name': r'rea\l3' |
|
||||
|
||||
And place contains
|
||||
| object | extratags!dict |
|
||||
| N2002 | 'name:prefix': 'Pre', 'name:suffix': 'Post', 'ref:de': '55' |
|
||||
|
||||
|
||||
Scenario: Name when using with_name flag
|
||||
When loading osm data
|
||||
"""
|
||||
n3001 Tbridge=yes,bridge:name=GoldenGate
|
||||
n3002 Tbridge=yes,bridge:name:en=Rainbow
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N3001 | bridge | yes | 'name': 'GoldenGate' |
|
||||
| N3002 | bridge | yes | 'name:en': 'Rainbow' |
|
||||
|
||||
|
||||
Scenario: Address tags
|
||||
When loading osm data
|
||||
"""
|
||||
n4001 Taddr:housenumber=34,addr:city=Esmarald,addr:county=Land
|
||||
n4002 Taddr:streetnumber=10,is_in:city=Rootoo,is_in=Gold
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | address!dict |
|
||||
| N4001 | place | 'housenumber': '34', 'city': 'Esmarald', 'county': 'Land' |
|
||||
| N4002 | place | 'streetnumber': '10', 'city': 'Rootoo' |
|
||||
|
||||
|
||||
Scenario: Country codes
|
||||
When loading osm data
|
||||
"""
|
||||
n5001 Tshop=yes,country_code=DE
|
||||
n5002 Tshop=yes,country_code=toolong
|
||||
n5003 Tshop=yes,country_code=x
|
||||
n5004 Tshop=yes,addr:country=us
|
||||
n5005 Tshop=yes,country=be
|
||||
n5006 Tshop=yes,addr:country=France
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | address!dict |
|
||||
| N5001 | shop | 'country': 'DE' |
|
||||
| N5002 | shop | - |
|
||||
| N5003 | shop | - |
|
||||
| N5004 | shop | 'country': 'us' |
|
||||
| N5005 | shop | - |
|
||||
| N5006 | shop | - |
|
||||
|
||||
|
||||
Scenario: Postcodes
|
||||
When loading osm data
|
||||
"""
|
||||
n6001 Tshop=bank,addr:postcode=12345
|
||||
n6002 Tshop=bank,tiger:zip_left=34343
|
||||
n6003 Tshop=bank,is_in:postcode=9009
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | address!dict |
|
||||
| N6001 | shop | 'postcode': '12345' |
|
||||
| N6002 | shop | 'postcode': '34343' |
|
||||
| N6003 | shop | - |
|
||||
|
||||
|
||||
Scenario: Postcode areas
|
||||
When loading osm data
|
||||
"""
|
||||
n1 x12.36853 y51.50618
|
||||
n2 x12.36853 y51.42362
|
||||
n3 x12.63666 y51.42362
|
||||
n4 x12.63666 y51.50618
|
||||
w1 Tboundary=postal_code,ref=3456 Nn1,n2,n3,n4,n1
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| W1 | boundary | postal_code | 'ref': '3456' |
|
||||
|
||||
Scenario: Main with extra
|
||||
When loading osm data
|
||||
"""
|
||||
n7001 Thighway=primary,bridge=yes,name=1
|
||||
n7002 Thighway=primary,bridge=yes,bridge:name=1
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict | extratags!dict |
|
||||
| N7001 | highway | primary | 'name': '1' | 'bridge': 'yes' |
|
||||
| N7002 | highway | primary | - | 'bridge': 'yes', 'bridge:name': '1' |
|
||||
| N7002 | bridge | yes | 'name': '1' | 'highway': 'primary', 'bridge:name': '1' |
|
||||
|
||||
|
||||
Scenario: Global fallback and skipping
|
||||
When loading osm data
|
||||
"""
|
||||
n8001 Tshop=shoes,note:de=Nein,xx=yy
|
||||
n8002 Tshop=shoes,natural=no,ele=234
|
||||
n8003 Tshop=shoes,name:source=survey
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | name!dict | extratags!dict |
|
||||
| N8001 | shop | - | 'xx': 'yy' |
|
||||
| N8002 | shop | - | 'ele': '234' |
|
||||
| N8003 | shop | - | - |
|
||||
|
||||
|
||||
Scenario: Admin levels
|
||||
When loading osm data
|
||||
"""
|
||||
n9001 Tplace=city
|
||||
n9002 Tplace=city,admin_level=16
|
||||
n9003 Tplace=city,admin_level=x
|
||||
n9004 Tplace=city,admin_level=1
|
||||
n9005 Tplace=city,admin_level=0
|
||||
n9006 Tplace=city,admin_level=2.5
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | admin_level |
|
||||
| N9001 | place | 15 |
|
||||
| N9002 | place | 15 |
|
||||
| N9003 | place | 15 |
|
||||
| N9004 | place | 1 |
|
||||
| N9005 | place | 15 |
|
||||
| N9006 | place | 15 |
|
||||
|
||||
|
||||
Scenario: Administrative boundaries with place tags
|
||||
When loading osm data
|
||||
"""
|
||||
n10001 Tboundary=administrative,place=city,name=A
|
||||
n10002 Tboundary=natural,place=city,name=B
|
||||
n10003 Tboundary=administrative,place=island,name=C
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type | extratags!dict |
|
||||
| N10001 | boundary | administrative | 'place': 'city' |
|
||||
And place contains
|
||||
| object | class | type |
|
||||
| N10002 | boundary | natural |
|
||||
| N10002 | place | city |
|
||||
| N10003 | boundary | administrative |
|
||||
| N10003 | place | island |
|
||||
|
||||
|
||||
Scenario: Building fallbacks
|
||||
When loading osm data
|
||||
"""
|
||||
n12001 Ttourism=hotel,building=yes
|
||||
n12002 Tbuilding=house
|
||||
n12003 Tbuilding=shed,addr:housenumber=1
|
||||
n12004 Tbuilding=yes,name=Das-Haus
|
||||
n12005 Tbuilding=yes,addr:postcode=12345
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N12001 | tourism | hotel |
|
||||
| N12003 | building | shed |
|
||||
| N12004 | building | yes |
|
||||
| N12005 | place | postcode |
|
||||
|
||||
|
||||
Scenario: Address interpolations
|
||||
When loading osm data
|
||||
"""
|
||||
n13001 Taddr:interpolation=odd
|
||||
n13002 Taddr:interpolation=even,place=city
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | address!dict |
|
||||
| N13001 | place | houses | 'interpolation': 'odd' |
|
||||
| N13002 | place | houses | 'interpolation': 'even' |
|
||||
|
||||
|
||||
Scenario: Footways
|
||||
When loading osm data
|
||||
"""
|
||||
n1 x0.0 y0.0
|
||||
n2 x0 y0.0001
|
||||
w1 Thighway=footway Nn1,n2
|
||||
w2 Thighway=footway,name=Road Nn1,n2
|
||||
w3 Thighway=footway,name=Road,footway=sidewalk Nn1,n2
|
||||
w4 Thighway=footway,name=Road,footway=crossing Nn1,n2
|
||||
w5 Thighway=footway,name=Road,footway=residential Nn1,n2
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | name+name |
|
||||
| W2 | Road |
|
||||
| W5 | Road |
|
||||
|
||||
|
||||
Scenario: Tourism information
|
||||
When loading osm data
|
||||
"""
|
||||
n100 Ttourism=information
|
||||
n101 Ttourism=information,name=Generic
|
||||
n102 Ttourism=information,information=guidepost
|
||||
n103 Thighway=information,information=house
|
||||
n104 Ttourism=information,information=yes,name=Something
|
||||
n105 Ttourism=information,information=route_marker,name=3
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N100 | tourism | information |
|
||||
| N101 | tourism | information |
|
||||
| N102 | information | guidepost |
|
||||
| N103 | highway | information |
|
||||
| N104 | tourism | information |
|
||||
|
||||
|
||||
Scenario: Water features
|
||||
When loading osm data
|
||||
"""
|
||||
n20 Tnatural=water
|
||||
n21 Tnatural=water,name=SomePond
|
||||
n22 Tnatural=water,water=pond
|
||||
n23 Tnatural=water,water=pond,name=Pond
|
||||
n24 Tnatural=water,water=river,name=BigRiver
|
||||
n25 Tnatural=water,water=yes
|
||||
n26 Tnatural=water,water=yes,name=Random
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N21 | natural | water |
|
||||
| N23 | water | pond |
|
||||
| N26 | natural | water |
|
||||
|
||||
Scenario: Drop name for address fallback
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=23,name=Foo
|
||||
n2 Taddr:housenumber=23,addr:housename=Foo
|
||||
n3 Taddr:housenumber=23
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | address!dict | name!dict |
|
||||
| N1 | place | house | 'housenumber': '23' | - |
|
||||
| N2 | place | house | 'housenumber': '23' | 'addr:housename': 'Foo' |
|
||||
| N3 | place | house | 'housenumber': '23' | - |
|
||||
|
||||
|
||||
Scenario: Waterway locks
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Twaterway=river,lock=yes
|
||||
n2 Twaterway=river,lock=yes,lock_name=LeLock
|
||||
n3 Twaterway=river,lock=yes,name=LeWater
|
||||
n4 Tamenity=parking,lock=yes,lock_name=Gold
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N2 | lock | yes | 'name': 'LeLock' |
|
||||
| N3 | waterway | river | 'name': 'LeWater' |
|
||||
| N4 | amenity | parking | - |
|
||||
135
test/bdd/features/osm2pgsql/update/interpolations.feature
Normal file
135
test/bdd/features/osm2pgsql/update/interpolations.feature
Normal file
@@ -0,0 +1,135 @@
|
||||
Feature: Updates of address interpolation objects
|
||||
Test that changes to address interpolation objects are correctly
|
||||
propagated.
|
||||
|
||||
Background:
|
||||
Given the grid
|
||||
| 1 | 2 |
|
||||
|
||||
|
||||
Scenario: Adding a new interpolation
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=3
|
||||
n2 Taddr:housenumber=17
|
||||
w33 Thighway=residential,name=Tao Nn1,n2
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w99 Taddr:interpolation=odd Nn1,n2
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W99 | place | houses |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W33 | highway | residential |
|
||||
Then location_property_osmline contains exactly
|
||||
| osm_id | startnumber |
|
||||
| 99 | 5 |
|
||||
|
||||
|
||||
Scenario: Delete an existing interpolation
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=2
|
||||
n2 Taddr:housenumber=7
|
||||
w99 Taddr:interpolation=odd Nn1,n2
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W99 | place | houses |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w99 v2 dD
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
Then location_property_osmline contains exactly
|
||||
| osm_id |
|
||||
|
||||
|
||||
Scenario: Changing an object to an interpolation
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=3
|
||||
n2 Taddr:housenumber=17
|
||||
w33 Thighway=residential Nn1,n2
|
||||
w99 Thighway=residential Nn1,n2
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W99 | highway | residential |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w99 Taddr:interpolation=odd Nn1,n2
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W99 | place | houses |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W33 | highway | residential |
|
||||
And location_property_osmline contains exactly
|
||||
| osm_id | startnumber |
|
||||
| 99 | 5 |
|
||||
|
||||
|
||||
Scenario: Changing an interpolation to something else
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=3
|
||||
n2 Taddr:housenumber=17
|
||||
w99 Taddr:interpolation=odd Nn1,n2
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W99 | place | houses |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w99 Thighway=residential Nn1,n2
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W99 | highway | residential |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W99 | highway | residential |
|
||||
And location_property_osmline contains exactly
|
||||
| osm_id |
|
||||
167
test/bdd/features/osm2pgsql/update/postcodes.feature
Normal file
167
test/bdd/features/osm2pgsql/update/postcodes.feature
Normal file
@@ -0,0 +1,167 @@
|
||||
Feature: Update of postcode only objects
|
||||
Tests that changes to objects containing only a postcode are
|
||||
propagated correctly.
|
||||
|
||||
|
||||
Scenario: Adding a postcode-only node
|
||||
When loading osm data
|
||||
"""
|
||||
n1
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n34 Tpostcode=4456
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N34 | place | postcode |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object |
|
||||
|
||||
|
||||
Scenario: Deleting a postcode-only node
|
||||
When loading osm data
|
||||
"""
|
||||
n34 Tpostcode=4456
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N34 | place | postcode |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n34 v2 dD
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object |
|
||||
|
||||
|
||||
Scenario Outline: Converting a regular object into a postcode-only node
|
||||
When loading osm data
|
||||
"""
|
||||
n34 T<class>=<type>
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N34 | <class> | <type> |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n34 Tpostcode=4456
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N34 | place | postcode |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object |
|
||||
|
||||
Examples:
|
||||
| class | type |
|
||||
| amenity | restaurant |
|
||||
| place | hamlet |
|
||||
|
||||
|
||||
Scenario Outline: Converting a postcode-only node into a regular object
|
||||
When loading osm data
|
||||
"""
|
||||
n34 Tpostcode=4456
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N34 | place | postcode |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n34 T<class>=<type>
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N34 | <class> | <type> |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type |
|
||||
| N34 | <class> | <type> |
|
||||
|
||||
Examples:
|
||||
| class | type |
|
||||
| amenity | restaurant |
|
||||
| place | hamlet |
|
||||
|
||||
|
||||
Scenario: Converting na interpolation into a postcode-only node
|
||||
Given the grid
|
||||
| 1 | 2 |
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=3
|
||||
n2 Taddr:housenumber=17
|
||||
w34 Taddr:interpolation=odd Nn1,n2
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W34 | place | houses |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w34 Tpostcode=4456 Nn1,n2
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W34 | place | postcode |
|
||||
When indexing
|
||||
Then location_property_osmline contains exactly
|
||||
| osm_id |
|
||||
And placex contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
|
||||
|
||||
Scenario: Converting a postcode-only node into an interpolation
|
||||
Given the grid
|
||||
| 1 | 2 |
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=3
|
||||
n2 Taddr:housenumber=17
|
||||
w33 Thighway=residential Nn1,n2
|
||||
w34 Tpostcode=4456 Nn1,n2
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W33 | highway | residential |
|
||||
| W34 | place | postcode |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w34 Taddr:interpolation=odd Nn1,n2
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W33 | highway | residential |
|
||||
| W34 | place | houses |
|
||||
When indexing
|
||||
Then location_property_osmline contains exactly
|
||||
| osm_id | startnumber | endnumber |
|
||||
| 34 | 5 | 15 |
|
||||
And placex contains exactly
|
||||
| object | class | type |
|
||||
| N1 | place | house |
|
||||
| N2 | place | house |
|
||||
| W33 | highway | residential |
|
||||
140
test/bdd/features/osm2pgsql/update/relation.feature
Normal file
140
test/bdd/features/osm2pgsql/update/relation.feature
Normal file
@@ -0,0 +1,140 @@
|
||||
Feature: Update of relations by osm2pgsql
|
||||
Testing relation update by osm2pgsql.
|
||||
|
||||
Scenario: Remove all members of a relation
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Tamenity=prison,name=foo
|
||||
n200 x0 y0
|
||||
n201 x0 y0.0001
|
||||
n202 x0.0001 y0.0001
|
||||
n203 x0.0001 y0
|
||||
w2 Tref=45' Nn200,n201,n202,n203,n200
|
||||
r1 Ttype=multipolygon,tourism=hotel,name=XZ Mw2@
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type | name!dict |
|
||||
| R1 | tourism | hotel | 'name' : 'XZ' |
|
||||
When updating osm data
|
||||
"""
|
||||
r1 Ttype=multipolygon,tourism=hotel,name=XZ Mn1@
|
||||
"""
|
||||
Then place has no entry for R1
|
||||
|
||||
|
||||
Scenario: Change type of a relation
|
||||
When loading osm data
|
||||
"""
|
||||
n200 x0 y0
|
||||
n201 x0 y0.0001
|
||||
n202 x0.0001 y0.0001
|
||||
n203 x0.0001 y0
|
||||
w2 Tref=45 Nn200,n201,n202,n203,n200
|
||||
r1 Ttype=multipolygon,tourism=hotel,name=XZ Mw2@
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type | name!dict |
|
||||
| R1 | tourism | hotel | 'name' : 'XZ' |
|
||||
When updating osm data
|
||||
"""
|
||||
r1 Ttype=multipolygon,amenity=prison,name=XZ Mw2@
|
||||
"""
|
||||
Then place has no entry for R1:tourism
|
||||
And place contains
|
||||
| object | class | type | name!dict |
|
||||
| R1 | amenity | prison | 'name' : 'XZ' |
|
||||
|
||||
Scenario: Change name of a relation
|
||||
When loading osm data
|
||||
"""
|
||||
n200 x0 y0
|
||||
n201 x0 y0.0001
|
||||
n202 x0.0001 y0.0001
|
||||
n203 x0.0001 y0
|
||||
w2 Tref=45 Nn200,n201,n202,n203,n200
|
||||
r1 Ttype=multipolygon,tourism=hotel,name=AB Mw2@
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type | name!dict |
|
||||
| R1 | tourism | hotel | 'name' : 'AB' |
|
||||
When updating osm data
|
||||
"""
|
||||
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type | name!dict |
|
||||
| R1 | tourism | hotel | 'name' : 'XY' |
|
||||
|
||||
Scenario: Change type of a relation into something unknown
|
||||
When loading osm data
|
||||
"""
|
||||
n200 x0 y0
|
||||
n201 x0 y0.0001
|
||||
n202 x0.0001 y0.0001
|
||||
n203 x0.0001 y0
|
||||
w2 Tref=45 Nn200,n201,n202,n203,n200
|
||||
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type | name!dict |
|
||||
| R1 | tourism | hotel | 'name' : 'XY' |
|
||||
When updating osm data
|
||||
"""
|
||||
r1 Ttype=multipolygon,amenities=prison,name=XY Mw2@
|
||||
"""
|
||||
Then place has no entry for R1
|
||||
|
||||
Scenario: Type tag is removed
|
||||
When loading osm data
|
||||
"""
|
||||
n200 x0 y0
|
||||
n201 x0 y0.0001
|
||||
n202 x0.0001 y0.0001
|
||||
n203 x0.0001 y0
|
||||
w2 Tref=45 Nn200,n201,n202,n203,n200
|
||||
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type | name!dict |
|
||||
| R1 | tourism | hotel | 'name' : 'XY' |
|
||||
When updating osm data
|
||||
"""
|
||||
r1 Ttourism=hotel,name=XY Mw2@
|
||||
"""
|
||||
Then place has no entry for R1
|
||||
|
||||
Scenario: Type tag is renamed to something unknown
|
||||
When loading osm data
|
||||
"""
|
||||
n200 x0 y0
|
||||
n201 x0 y0.0001
|
||||
n202 x0.0001 y0.0001
|
||||
n203 x0.0001 y0
|
||||
w2 Tref=45 Nn200,n201,n202,n203,n200
|
||||
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
||||
"""
|
||||
Then place contains
|
||||
| object | class | type | name!dict |
|
||||
| R1 | tourism | hotel | 'name' : 'XY' |
|
||||
When updating osm data
|
||||
"""
|
||||
r1 Ttype=multipolygonn,tourism=hotel,name=XY Mw2@
|
||||
"""
|
||||
Then place has no entry for R1
|
||||
|
||||
Scenario: Country boundary names are left untouched when country_code unknown
|
||||
When loading osm data
|
||||
"""
|
||||
n200 Tamenity=prison x0 y0
|
||||
n201 x0 y0.0001
|
||||
n202 x0.0001 y0.0001
|
||||
n203 x0.0001 y0
|
||||
"""
|
||||
And updating osm data
|
||||
"""
|
||||
w1 Nn200,n201,n202,n203,n200
|
||||
r1 Ttype=boundary,boundary=administrative,name=Foo,country_code=XX,admin_level=2 Mw1@
|
||||
"""
|
||||
Then place contains
|
||||
| object | address+country | name!dict |
|
||||
| R1 | XX | 'name' : 'Foo' |
|
||||
48
test/bdd/features/osm2pgsql/update/simple.feature
Normal file
48
test/bdd/features/osm2pgsql/update/simple.feature
Normal file
@@ -0,0 +1,48 @@
|
||||
Feature: Update of simple objects by osm2pgsql
|
||||
Testing basic update functions of osm2pgsql.
|
||||
|
||||
Scenario: Adding a new object
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Tplace=town,name=Middletown
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name+name |
|
||||
| N1 | place | town | Middletown |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n2 Tamenity=hotel,name=Posthotel
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name+name |
|
||||
| N1 | place | town | Middletown |
|
||||
| N2 | amenity | hotel | Posthotel |
|
||||
And placex contains exactly
|
||||
| object | class | type | name+name | indexed_status |
|
||||
| N1 | place | town | Middletown | 0 |
|
||||
| N2 | amenity | hotel | Posthotel | 1 |
|
||||
|
||||
|
||||
Scenario: Deleting an existing object
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Tplace=town,name=Middletown
|
||||
n2 Tamenity=hotel,name=Posthotel
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name+name |
|
||||
| N1 | place | town | Middletown |
|
||||
| N2 | amenity | hotel | Posthotel |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n2 dD
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name+name |
|
||||
| N1 | place | town | Middletown |
|
||||
And placex contains exactly
|
||||
| object | class | type | name+name | indexed_status |
|
||||
| N1 | place | town | Middletown | 0 |
|
||||
| N2 | amenity | hotel | Posthotel | 100 |
|
||||
512
test/bdd/features/osm2pgsql/update/tags.feature
Normal file
512
test/bdd/features/osm2pgsql/update/tags.feature
Normal file
@@ -0,0 +1,512 @@
|
||||
Feature: Tag evaluation
|
||||
Tests if tags are correctly updated in the place table
|
||||
|
||||
Background:
|
||||
Given the grid
|
||||
| 1 | 2 | 3 |
|
||||
| 10 | 11 | |
|
||||
| 45 | 46 | |
|
||||
|
||||
Scenario: Main tag deleted
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Tamenity=restaurant
|
||||
n2 Thighway=bus_stop,railway=stop,name=X
|
||||
n3 Tamenity=prison
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N1 | amenity | restaurant |
|
||||
| N2 | highway | bus_stop |
|
||||
| N2 | railway | stop |
|
||||
| N3 | amenity | prison |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n1 Tnot_a=restaurant
|
||||
n2 Thighway=bus_stop,name=X
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N2 | highway | bus_stop |
|
||||
| N3 | amenity | prison |
|
||||
And placex contains
|
||||
| object | class | indexed_status |
|
||||
| N3 | amenity | 0 |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N2 | highway | bus_stop | 'name': 'X' |
|
||||
| N3 | amenity | prison | - |
|
||||
|
||||
|
||||
Scenario: Main tag added
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Tatity=restaurant
|
||||
n2 Thighway=bus_stop,name=X
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N2 | highway | bus_stop |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n1 Tamenity=restaurant
|
||||
n2 Thighway=bus_stop,railway=stop,name=X
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N1 | amenity | restaurant |
|
||||
| N2 | highway | bus_stop |
|
||||
| N2 | railway | stop |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N1 | amenity | restaurant | - |
|
||||
| N2 | highway | bus_stop | 'name': 'X' |
|
||||
| N2 | railway | stop | 'name': 'X' |
|
||||
|
||||
|
||||
Scenario: Main tag modified
|
||||
When loading osm data
|
||||
"""
|
||||
n10 Thighway=footway,name=X
|
||||
n11 Tamenity=atm
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N10 | highway | footway |
|
||||
| N11 | amenity | atm |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n10 Thighway=path,name=X
|
||||
n11 Thighway=primary
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N10 | highway | path |
|
||||
| N11 | highway | primary |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N10 | highway | path | 'name': 'X' |
|
||||
| N11 | highway | primary | - |
|
||||
|
||||
|
||||
Scenario: Main tags with name, name added
|
||||
When loading osm data
|
||||
"""
|
||||
n45 Tlanduse=cemetry
|
||||
n46 Tbuilding=yes
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n45 Tlanduse=cemetry,name=TODO
|
||||
n46 Tbuilding=yes,addr:housenumber=1
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N45 | landuse | cemetry |
|
||||
| N46 | building| yes |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | name!dict | address!dict |
|
||||
| N45 | landuse | cemetry | 'name': 'TODO' | - |
|
||||
| N46 | building| yes | - | 'housenumber': '1' |
|
||||
|
||||
|
||||
Scenario: Main tags with name, name removed
|
||||
When loading osm data
|
||||
"""
|
||||
n45 Tlanduse=cemetry,name=TODO
|
||||
n46 Tbuilding=yes,addr:housenumber=1
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| N45 | landuse | cemetry |
|
||||
| N46 | building| yes |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n45 Tlanduse=cemetry
|
||||
n46 Tbuilding=yes
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object |
|
||||
|
||||
Scenario: Main tags with name, name modified
|
||||
When loading osm data
|
||||
"""
|
||||
n45 Tlanduse=cemetry,name=TODO
|
||||
n46 Tbuilding=yes,addr:housenumber=1
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict | address!dict |
|
||||
| N45 | landuse | cemetry | 'name' : 'TODO' | - |
|
||||
| N46 | building| yes | - | 'housenumber': '1'|
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n45 Tlanduse=cemetry,name=DONE
|
||||
n46 Tbuilding=yes,addr:housenumber=10
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict | address!dict |
|
||||
| N45 | landuse | cemetry | 'name' : 'DONE' | - |
|
||||
| N46 | building| yes | - | 'housenumber': '10'|
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | name!dict | address!dict |
|
||||
| N45 | landuse | cemetry | 'name' : 'DONE' | - |
|
||||
| N46 | building| yes | - | 'housenumber': '10'|
|
||||
|
||||
|
||||
Scenario: Main tag added to address only node
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=345
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | address!dict |
|
||||
| N1 | place | house | 'housenumber': '345'|
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=345,building=yes
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | address!dict |
|
||||
| N1 | building | yes | 'housenumber': '345'|
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | address!dict |
|
||||
| N1 | building | yes | 'housenumber': '345'|
|
||||
|
||||
|
||||
Scenario: Main tag removed from address only node
|
||||
When loading osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=345,building=yes
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | address!dict |
|
||||
| N1 | building | yes | 'housenumber': '345'|
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n1 Taddr:housenumber=345
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | address!dict |
|
||||
| N1 | place | house | 'housenumber': '345'|
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | address!dict |
|
||||
| N1 | place | house | 'housenumber': '345'|
|
||||
|
||||
|
||||
Scenario: Main tags with name key, adding key name
|
||||
When loading osm data
|
||||
"""
|
||||
n2 Tbridge=yes
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n2 Tbridge=yes,bridge:name=high
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N2 | bridge | yes | 'name': 'high' |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N2 | bridge | yes | 'name': 'high' |
|
||||
|
||||
|
||||
Scenario: Main tags with name key, deleting key name
|
||||
When loading osm data
|
||||
"""
|
||||
n2 Tbridge=yes,bridge:name=high
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N2 | bridge | yes | 'name': 'high' |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n2 Tbridge=yes
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object |
|
||||
|
||||
|
||||
Scenario: Main tags with name key, changing key name
|
||||
When loading osm data
|
||||
"""
|
||||
n2 Tbridge=yes,bridge:name=high
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N2 | bridge | yes | 'name': 'high' |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n2 Tbridge=yes,bridge:name:en=high
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N2 | bridge | yes | 'name:en': 'high' |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | name!dict |
|
||||
| N2 | bridge | yes | 'name:en': 'high' |
|
||||
|
||||
|
||||
Scenario: Downgrading a highway to one that is dropped without name
|
||||
When loading osm data
|
||||
"""
|
||||
n100 x0 y0
|
||||
n101 x0.0001 y0.0001
|
||||
w1 Thighway=residential Nn100,n101
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class |
|
||||
| W1 | highway |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w1 Thighway=service Nn100,n101
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object |
|
||||
|
||||
|
||||
Scenario: Upgrading a highway to one that is not dropped without name
|
||||
When loading osm data
|
||||
"""
|
||||
n100 x0 y0
|
||||
n101 x0.0001 y0.0001
|
||||
w1 Thighway=service Nn100,n101
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w1 Thighway=unclassified Nn100,n101
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class |
|
||||
| W1 | highway |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class |
|
||||
| W1 | highway |
|
||||
|
||||
|
||||
Scenario: Downgrading a highway when a second tag is present
|
||||
When loading osm data
|
||||
"""
|
||||
n100 x0 y0
|
||||
n101 x0.0001 y0.0001
|
||||
w1 Thighway=residential,tourism=hotel Nn100,n101
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| W1 | highway | residential |
|
||||
| W1 | tourism | hotel |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w1 Thighway=service,tourism=hotel Nn100,n101
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| W1 | tourism | hotel |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type |
|
||||
| W1 | tourism | hotel |
|
||||
|
||||
|
||||
Scenario: Upgrading a highway when a second tag is present
|
||||
When loading osm data
|
||||
"""
|
||||
n100 x0 y0
|
||||
n101 x0.0001 y0.0001
|
||||
w1 Thighway=service,tourism=hotel Nn100,n101
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| W1 | tourism | hotel |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w1 Thighway=residential,tourism=hotel Nn100,n101
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| W1 | highway | residential |
|
||||
| W1 | tourism | hotel |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type |
|
||||
| W1 | highway | residential |
|
||||
| W1 | tourism | hotel |
|
||||
|
||||
|
||||
Scenario: Replay on administrative boundary
|
||||
When loading osm data
|
||||
"""
|
||||
n10 x34.0 y-4.23
|
||||
n11 x34.1 y-4.23
|
||||
n12 x34.2 y-4.13
|
||||
w10 Tboundary=administrative,waterway=river,name=Border,admin_level=2 Nn12,n11,n10
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | admin_level | name!dict |
|
||||
| W10 | waterway | river | 2 | 'name': 'Border' |
|
||||
| W10 | boundary | administrative | 2 | 'name': 'Border' |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
w10 Tboundary=administrative,waterway=river,name=Border,admin_level=2 Nn12,n11,n10
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | admin_level | name!dict |
|
||||
| W10 | waterway | river | 2 | 'name': 'Border' |
|
||||
| W10 | boundary | administrative | 2 | 'name': 'Border' |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | admin_level | name!dict |
|
||||
| W10 | waterway | river | 2 | 'name': 'Border' |
|
||||
|
||||
|
||||
Scenario: Change admin_level on administrative boundary
|
||||
Given the grid
|
||||
| 10 | 11 |
|
||||
| 13 | 12 |
|
||||
When loading osm data
|
||||
"""
|
||||
n10
|
||||
n11
|
||||
n12
|
||||
n13
|
||||
w10 Nn10,n11,n12,n13,n10
|
||||
r10 Ttype=multipolygon,boundary=administrative,name=Border,admin_level=2 Mw10@
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | admin_level |
|
||||
| R10 | boundary | 2 |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
r10 Ttype=multipolygon,boundary=administrative,name=Border,admin_level=4 Mw10@
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | admin_level |
|
||||
| R10 | boundary | administrative | 4 |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | admin_level |
|
||||
| R10 | boundary | administrative | 4 |
|
||||
|
||||
|
||||
Scenario: Change boundary to administrative
|
||||
Given the grid
|
||||
| 10 | 11 |
|
||||
| 13 | 12 |
|
||||
When loading osm data
|
||||
"""
|
||||
n10
|
||||
n11
|
||||
n12
|
||||
n13
|
||||
w10 Nn10,n11,n12,n13,n10
|
||||
r10 Ttype=multipolygon,boundary=informal,name=Border,admin_level=4 Mw10@
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | admin_level |
|
||||
| R10 | boundary | informal | 4 |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
r10 Ttype=multipolygon,boundary=administrative,name=Border,admin_level=4 Mw10@
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | admin_level |
|
||||
| R10 | boundary | administrative | 4 |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | admin_level |
|
||||
| R10 | boundary | administrative | 4 |
|
||||
|
||||
|
||||
Scenario: Change boundary away from administrative
|
||||
Given the grid
|
||||
| 10 | 11 |
|
||||
| 13 | 12 |
|
||||
When loading osm data
|
||||
"""
|
||||
n10
|
||||
n11
|
||||
n12
|
||||
n13
|
||||
w10 Nn10,n11,n12,n13,n10
|
||||
r10 Ttype=multipolygon,boundary=administrative,name=Border,admin_level=4 Mw10@
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | admin_level |
|
||||
| R10 | boundary | administrative | 4 |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
r10 Ttype=multipolygon,boundary=informal,name=Border,admin_level=4 Mw10@
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type | admin_level |
|
||||
| R10 | boundary | informal | 4 |
|
||||
When indexing
|
||||
Then placex contains exactly
|
||||
| object | class | type | admin_level |
|
||||
| R10 | boundary | informal | 4 |
|
||||
|
||||
|
||||
Scenario: Main tag and geometry is changed
|
||||
When loading osm data
|
||||
"""
|
||||
n1 x40 y40
|
||||
n2 x40.0001 y40
|
||||
n3 x40.0001 y40.0001
|
||||
n4 x40 y40.0001
|
||||
w5 Tbuilding=house,name=Foo Nn1,n2,n3,n4,n1
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| W5 | building | house |
|
||||
|
||||
When updating osm data
|
||||
"""
|
||||
n1 x39.999 y40
|
||||
w5 Tbuilding=terrace,name=Bar Nn1,n2,n3,n4,n1
|
||||
"""
|
||||
Then place contains exactly
|
||||
| object | class | type |
|
||||
| W5 | building | terrace |
|
||||
152
test/bdd/test_osm2pgsql.py
Normal file
152
test/bdd/test_osm2pgsql.py
Normal file
@@ -0,0 +1,152 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This file is part of Nominatim. (https://nominatim.org)
|
||||
#
|
||||
# Copyright (C) 2025 by the Nominatim developer community.
|
||||
# For a full list of authors see the git log.
|
||||
"""
|
||||
Collector for BDD osm2pgsql import style tests.
|
||||
"""
|
||||
import asyncio
|
||||
import random
|
||||
|
||||
import psycopg
|
||||
|
||||
import pytest
|
||||
from pytest_bdd.parsers import re as step_parse
|
||||
from pytest_bdd import scenarios, when, given, then
|
||||
|
||||
from nominatim_db import cli
|
||||
from nominatim_db.config import Configuration
|
||||
from nominatim_db.tools.exec_utils import run_osm2pgsql
|
||||
from nominatim_db.tools.database_import import load_data, create_table_triggers
|
||||
from nominatim_db.tools.replication import run_osm2pgsql_updates
|
||||
|
||||
from utils.db import DBManager
|
||||
from utils.checks import check_table_content, check_table_has_lines
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def def_config(pytestconfig):
|
||||
dbname = pytestconfig.getini('nominatim_test_db')
|
||||
|
||||
return Configuration(None,
|
||||
environ={'NOMINATIM_DATABASE_DSN': f"pgsql:dbname={dbname}"})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db(template_db, pytestconfig):
|
||||
""" Set up an empty database for use with osm2pgsql.
|
||||
"""
|
||||
dbm = DBManager(purge=pytestconfig.option.NOMINATIM_PURGE)
|
||||
|
||||
dbname = pytestconfig.getini('nominatim_test_db')
|
||||
|
||||
dbm.create_db_from_template(dbname, template_db)
|
||||
|
||||
yield dbname
|
||||
|
||||
if not pytestconfig.option.NOMINATIM_KEEP_DB:
|
||||
dbm.drop_db(dbname)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db_conn(def_config):
|
||||
with psycopg.connect(def_config.get_libpq_dsn()) as conn:
|
||||
info = psycopg.types.TypeInfo.fetch(conn, "hstore")
|
||||
psycopg.types.hstore.register_hstore(info, conn)
|
||||
yield conn
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def osm2pgsql_options(def_config):
|
||||
return dict(osm2pgsql='osm2pgsql',
|
||||
osm2pgsql_cache=50,
|
||||
osm2pgsql_style=str(def_config.get_import_style_file()),
|
||||
osm2pgsql_style_path=def_config.lib_dir.lua,
|
||||
threads=1,
|
||||
dsn=def_config.get_libpq_dsn(),
|
||||
flatnode_file='',
|
||||
tablespaces=dict(slim_data='', slim_index='',
|
||||
main_data='', main_index=''),
|
||||
append=False)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def opl_writer(tmp_path, node_grid):
|
||||
nr = [0]
|
||||
|
||||
def _write(data):
|
||||
fname = tmp_path / f"test_osm_{nr[0]}.opl"
|
||||
nr[0] += 1
|
||||
with fname.open('wt') as fd:
|
||||
for line in data.split('\n'):
|
||||
if line.startswith('n') and ' x' not in line:
|
||||
coord = node_grid.get(line[1:].split(' ')[0]) \
|
||||
or (random.uniform(-180, 180), random.uniform(-90, 90))
|
||||
line = f"{line} x{coord[0]:.7f} y{coord[1]:.7f}"
|
||||
fd.write(line)
|
||||
fd.write('\n')
|
||||
return fname
|
||||
|
||||
return _write
|
||||
|
||||
|
||||
@given('the lua style file', target_fixture='osm2pgsql_options')
|
||||
def set_lua_style_file(osm2pgsql_options, docstring, tmp_path):
|
||||
style = tmp_path / 'custom.lua'
|
||||
style.write_text(docstring)
|
||||
osm2pgsql_options['osm2pgsql_style'] = str(style)
|
||||
|
||||
return osm2pgsql_options
|
||||
|
||||
|
||||
@when('loading osm data')
|
||||
def load_from_osm_file(db, osm2pgsql_options, opl_writer, docstring):
|
||||
""" Load the given data into a freshly created test database using osm2pgsql.
|
||||
No further indexing is done.
|
||||
|
||||
The data is expected as attached text in OPL format.
|
||||
"""
|
||||
osm2pgsql_options['import_file'] = opl_writer(docstring.replace(r'//', r'/'))
|
||||
osm2pgsql_options['append'] = False
|
||||
run_osm2pgsql(osm2pgsql_options)
|
||||
|
||||
|
||||
@when('updating osm data')
|
||||
def update_from_osm_file(db_conn, def_config, osm2pgsql_options, opl_writer, docstring):
|
||||
""" Update a database previously populated with 'loading osm data'.
|
||||
Needs to run indexing on the existing data first to yield the correct
|
||||
result.
|
||||
|
||||
The data is expected as attached text in OPL format.
|
||||
"""
|
||||
create_table_triggers(db_conn, def_config)
|
||||
asyncio.run(load_data(def_config.get_libpq_dsn(), 1))
|
||||
cli.nominatim(['index'], def_config.environ)
|
||||
cli.nominatim(['refresh', '--functions'], def_config.environ)
|
||||
|
||||
osm2pgsql_options['import_file'] = opl_writer(docstring.replace(r'//', r'/'))
|
||||
run_osm2pgsql_updates(db_conn, osm2pgsql_options)
|
||||
|
||||
|
||||
@when('indexing')
|
||||
def do_index(def_config):
|
||||
""" Run Nominatim's indexing step.
|
||||
"""
|
||||
cli.nominatim(['index'], def_config.environ)
|
||||
|
||||
|
||||
@then(step_parse(r'(?P<table>\w+) contains(?P<exact> exactly)?'))
|
||||
def check_place_content(db_conn, datatable, node_grid, table, exact):
|
||||
check_table_content(db_conn, table, datatable, grid=node_grid, exact=bool(exact))
|
||||
|
||||
|
||||
@then(step_parse('(?P<table>placex?) has no entry for '
|
||||
r'(?P<osm_type>[NRW])(?P<osm_id>\d+)(?::(?P<osm_class>\S+))?'),
|
||||
converters={'osm_id': int})
|
||||
def check_place_missing_lines(db_conn, table, osm_type, osm_id, osm_class):
|
||||
check_table_has_lines(db_conn, table, osm_type, osm_id, osm_class)
|
||||
|
||||
|
||||
scenarios('features/osm2pgsql')
|
||||
@@ -9,6 +9,12 @@ Helper functions to compare expected values.
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import math
|
||||
import itertools
|
||||
|
||||
from psycopg import sql as pysql
|
||||
from psycopg.rows import dict_row, tuple_row
|
||||
from .geometry_alias import ALIASES
|
||||
|
||||
COMPARATOR_TERMS = {
|
||||
'exactly': lambda exp, act: exp == act,
|
||||
@@ -43,10 +49,12 @@ COMPARISON_FUNCS = {
|
||||
None: lambda val, exp: str(val) == exp,
|
||||
'i': lambda val, exp: str(val).lower() == exp.lower(),
|
||||
'fm': lambda val, exp: re.fullmatch(exp, val) is not None,
|
||||
'dict': lambda val, exp: val is None if exp == '-' else (val == eval('{' + exp + '}')),
|
||||
'in_box': within_box
|
||||
}
|
||||
|
||||
OSM_TYPE = {'node': 'n', 'way': 'w', 'relation': 'r'}
|
||||
OSM_TYPE = {'node': 'n', 'way': 'w', 'relation': 'r',
|
||||
'N': 'n', 'W': 'w', 'R': 'r'}
|
||||
|
||||
|
||||
class ResultAttr:
|
||||
@@ -60,12 +68,15 @@ class ResultAttr:
|
||||
|
||||
Available formatters:
|
||||
|
||||
!:... - use a formatting expression according to Python Mini Format Spec
|
||||
!i - make case-insensitive comparison
|
||||
!fm - consider comparison string a regular expression and match full value
|
||||
!:... - use a formatting expression according to Python Mini Format Spec
|
||||
!i - make case-insensitive comparison
|
||||
!fm - consider comparison string a regular expression and match full value
|
||||
!wkt - convert the expected value to a WKT string before comparing
|
||||
!in_box - the expected value is a comma-separated bbox description
|
||||
"""
|
||||
|
||||
def __init__(self, obj, key):
|
||||
def __init__(self, obj, key, grid=None):
|
||||
self.grid = grid
|
||||
self.obj = obj
|
||||
if '!' in key:
|
||||
self.key, self.fmt = key.rsplit('!', 1)
|
||||
@@ -100,6 +111,9 @@ class ResultAttr:
|
||||
if self.fmt.startswith(':'):
|
||||
return other == f"{{{self.fmt}}}".format(self.subobj)
|
||||
|
||||
if self.fmt == 'wkt':
|
||||
return self.compare_wkt(self.subobj, other)
|
||||
|
||||
raise RuntimeError(f"Unknown format string '{self.fmt}'.")
|
||||
|
||||
def __repr__(self):
|
||||
@@ -107,3 +121,125 @@ class ResultAttr:
|
||||
if self.fmt:
|
||||
k += '!' + self.fmt
|
||||
return f"result[{k}]({self.subobj})"
|
||||
|
||||
def compare_wkt(self, value, expected):
|
||||
""" Compare a WKT value against a compact geometry format.
|
||||
The function understands the following formats:
|
||||
|
||||
country:<country code>
|
||||
Point geometry guaranteed to be in the given country
|
||||
<P>
|
||||
Point geometry
|
||||
<P>,...,<P>
|
||||
Line geometry
|
||||
(<P>,...,<P>)
|
||||
Polygon geometry
|
||||
|
||||
<P> may either be a coordinate of the form '<x> <y>' or a single
|
||||
number. In the latter case it must refer to a point in
|
||||
a previously defined grid.
|
||||
"""
|
||||
m = re.fullmatch(r'(POINT)\(([0-9. -]*)\)', value) \
|
||||
or re.fullmatch(r'(LINESTRING)\(([0-9,. -]*)\)', value) \
|
||||
or re.fullmatch(r'(POLYGON)\(\(([0-9,. -]*)\)\)', value)
|
||||
if not m:
|
||||
return False
|
||||
|
||||
converted = [list(map(float, pt.split(' ', 1)))
|
||||
for pt in map(str.strip, m[2].split(','))]
|
||||
|
||||
if expected.startswith('country:'):
|
||||
ccode = geom[8:].upper()
|
||||
assert ccode in ALIASES, f"Geometry error: unknown country {ccode}"
|
||||
return m[1] == 'POINT' and \
|
||||
all(math.isclose(p1, p2) for p1, p2 in
|
||||
zip(converted[0], ALIASES[ccode]))
|
||||
|
||||
if ',' not in expected:
|
||||
return m[1] == 'POINT' and \
|
||||
all(math.isclose(p1, p2) for p1, p2 in
|
||||
zip(converted[0], self.get_point(expected)))
|
||||
|
||||
if '(' not in expected:
|
||||
return m[1] == 'LINESTRING' and \
|
||||
all(math.isclose(p1[0], p2[0]) and math.isclose(p1[1], p2[1]) for p1, p2 in
|
||||
zip(converted, (self.get_point(p) for p in expected.split(','))))
|
||||
|
||||
if m[1] != 'POLYGON':
|
||||
return False
|
||||
|
||||
# Polygon comparison is tricky because the polygons don't necessarily
|
||||
# end at the same point or have the same winding order.
|
||||
# Brute force all possible variants of the expected polygon
|
||||
exp_coords = [self.get_point(p) for p in expected[1:-1].split(',')]
|
||||
if exp_coords[0] != exp_coords[-1]:
|
||||
raise RuntimeError(f"Invalid polygon {expected}. "
|
||||
"First and last point need to be the same")
|
||||
for line in (exp_coords[:-1], exp_coords[-1:0:-1]):
|
||||
for i in range(len(line)):
|
||||
if all(math.isclose(p1[0], p2[0]) and math.isclose(p1[1], p2[1]) for p1, p2 in
|
||||
zip(converted, line[i:] + line[:i])):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_point(self, pt):
|
||||
pt = pt.strip()
|
||||
if ' ' in pt:
|
||||
return list(map(float, pt.split(' ', 1)))
|
||||
|
||||
assert self.grid
|
||||
|
||||
return self.grid.get(pt)
|
||||
|
||||
|
||||
def check_table_content(conn, tablename, data, grid=None, exact=False):
|
||||
lines = set(range(1, len(data)))
|
||||
|
||||
cols = []
|
||||
for col in data[0]:
|
||||
if col == 'object':
|
||||
cols.extend(('osm_id', 'osm_type'))
|
||||
elif '!' in col:
|
||||
name, fmt = col.rsplit('!', 1)
|
||||
if fmt == 'wkt':
|
||||
cols.append(f"ST_AsText({name}) as {name}")
|
||||
else:
|
||||
cols.append(name.split('+')[0])
|
||||
else:
|
||||
cols.append(col.split('+')[0])
|
||||
|
||||
with conn.cursor(row_factory=dict_row) as cur:
|
||||
cur.execute(pysql.SQL(f"SELECT {','.join(cols)} FROM")
|
||||
+ pysql.Identifier(tablename))
|
||||
|
||||
table_content = ''
|
||||
for row in cur:
|
||||
table_content += '\n' + str(row)
|
||||
for i in lines:
|
||||
for col, value in zip(data[0], data[i]):
|
||||
if ResultAttr(row, col, grid=grid) != value:
|
||||
break
|
||||
else:
|
||||
lines.remove(i)
|
||||
break
|
||||
else:
|
||||
assert not exact, f"Unexpected row in table {tablename}: {row}"
|
||||
|
||||
assert not lines, \
|
||||
"Rows not found:\n" \
|
||||
+ '\n'.join(str(data[i]) for i in lines) \
|
||||
+ "\nTable content:\n" \
|
||||
+ table_content
|
||||
|
||||
|
||||
def check_table_has_lines(conn, tablename, osm_type, osm_id, osm_class):
|
||||
sql = pysql.SQL("""SELECT count(*) FROM {}
|
||||
WHERE osm_type = %s and osm_id = %s""").format(pysql.Identifier(tablename))
|
||||
params = [osm_type, int(osm_id)]
|
||||
if osm_class:
|
||||
sql += pysql.SQL(' AND class = %s')
|
||||
params.append(osm_class)
|
||||
|
||||
with conn.cursor(row_factory=tuple_row) as cur:
|
||||
assert cur.execute(sql, params).fetchone()[0] == 0
|
||||
|
||||
@@ -7,9 +7,16 @@
|
||||
"""
|
||||
Helper functions for managing test databases.
|
||||
"""
|
||||
import asyncio
|
||||
import psycopg
|
||||
from psycopg import sql as pysql
|
||||
|
||||
from nominatim_db.tools.database_import import setup_database_skeleton, create_tables, \
|
||||
create_partition_tables, create_search_indices
|
||||
from nominatim_db.data.country_info import setup_country_tables
|
||||
from nominatim_db.tools.refresh import create_functions, load_address_levels_from_config
|
||||
from nominatim_db.tools.exec_utils import run_osm2pgsql
|
||||
from nominatim_db.tokenizer import factory as tokenizer_factory
|
||||
|
||||
class DBManager:
|
||||
|
||||
@@ -42,3 +49,53 @@ class DBManager:
|
||||
cur = conn.execute('select count(*) from pg_database where datname = %s',
|
||||
(dbname,))
|
||||
return cur.fetchone()[0] == 1
|
||||
|
||||
def create_db_from_template(self, dbname, template):
|
||||
""" Create a new database from the given template database.
|
||||
Any existing database with the same name will be dropped.
|
||||
"""
|
||||
with psycopg.connect(dbname='postgres') as conn:
|
||||
conn.autocommit = True
|
||||
conn.execute(pysql.SQL('DROP DATABASE IF EXISTS')
|
||||
+ pysql.Identifier(dbname))
|
||||
conn.execute(pysql.SQL('CREATE DATABASE {} WITH TEMPLATE {}')
|
||||
.format(pysql.Identifier(dbname),
|
||||
pysql.Identifier(template)))
|
||||
|
||||
def setup_template_db(self, config):
|
||||
""" Create a template DB which contains the necessary extensions
|
||||
and basic static tables.
|
||||
|
||||
The template will only be created if the database does not yet
|
||||
exist or 'purge' is set.
|
||||
"""
|
||||
dsn = config.get_libpq_dsn()
|
||||
|
||||
if self.check_for_db(config.get_database_params()['dbname']):
|
||||
return
|
||||
|
||||
setup_database_skeleton(dsn)
|
||||
|
||||
run_osm2pgsql(dict(osm2pgsql='osm2pgsql',
|
||||
osm2pgsql_cache=1,
|
||||
osm2pgsql_style=str(config.get_import_style_file()),
|
||||
osm2pgsql_style_path=config.lib_dir.lua,
|
||||
threads=1,
|
||||
dsn=dsn,
|
||||
flatnode_file='',
|
||||
tablespaces=dict(slim_data='', slim_index='',
|
||||
main_data='', main_index=''),
|
||||
append=False,
|
||||
import_data=b'<osm version="0.6"></osm>'))
|
||||
|
||||
setup_country_tables(dsn, config.lib_dir.data)
|
||||
|
||||
with psycopg.connect(dsn) as conn:
|
||||
create_tables(conn, config)
|
||||
load_address_levels_from_config(conn, config)
|
||||
create_partition_tables(conn, config)
|
||||
create_functions(conn, config, enable_diff_updates=False)
|
||||
asyncio.run(create_search_indices(conn, config))
|
||||
|
||||
tokenizer_factory.create_tokenizer(config)
|
||||
|
||||
|
||||
262
test/bdd/utils/geometry_alias.py
Normal file
262
test/bdd/utils/geometry_alias.py
Normal file
@@ -0,0 +1,262 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# This file is part of Nominatim. (https://nominatim.org)
|
||||
#
|
||||
# Copyright (C) 2025 by the Nominatim developer community.
|
||||
# For a full list of authors see the git log.
|
||||
"""
|
||||
Collection of aliases for various world coordinates.
|
||||
"""
|
||||
|
||||
ALIASES = {
|
||||
# Country aliases
|
||||
'AD': (1.58972, 42.54241),
|
||||
'AE': (54.61589, 24.82431),
|
||||
'AF': (65.90264, 34.84708),
|
||||
'AG': (-61.72430, 17.069),
|
||||
'AI': (-63.10571, 18.25461),
|
||||
'AL': (19.84941, 40.21232),
|
||||
'AM': (44.64229, 40.37821),
|
||||
'AO': (16.21924, -12.77014),
|
||||
'AQ': (44.99999, -75.65695),
|
||||
'AR': (-61.10759, -34.37615),
|
||||
'AS': (-170.68470, -14.29307),
|
||||
'AT': (14.25747, 47.36542),
|
||||
'AU': (138.23155, -23.72068),
|
||||
'AW': (-69.98255, 12.555),
|
||||
'AX': (19.91839, 59.81682),
|
||||
'AZ': (48.38555, 40.61639),
|
||||
'BA': (17.18514, 44.25582),
|
||||
'BB': (-59.53342, 13.19),
|
||||
'BD': (89.75989, 24.34205),
|
||||
'BE': (4.90078, 50.34682),
|
||||
'BF': (-0.56743, 11.90471),
|
||||
'BG': (24.80616, 43.09859),
|
||||
'BH': (50.52032, 25.94685),
|
||||
'BI': (29.54561, -2.99057),
|
||||
'BJ': (2.70062, 10.02792),
|
||||
'BL': (-62.79349, 17.907),
|
||||
'BM': (-64.77406, 32.30199),
|
||||
'BN': (114.52196, 4.28638),
|
||||
'BO': (-62.02473, -17.77723),
|
||||
'BQ': (-63.14322, 17.566),
|
||||
'BR': (-45.77065, -9.58685),
|
||||
'BS': (-77.60916, 23.8745),
|
||||
'BT': (90.01350, 27.28137),
|
||||
'BV': (3.35744, -54.4215),
|
||||
'BW': (23.51505, -23.48391),
|
||||
'BY': (26.77259, 53.15885),
|
||||
'BZ': (-88.63489, 16.33951),
|
||||
'CA': (-107.74817, 67.12612),
|
||||
'CC': (96.84420, -12.01734),
|
||||
'CD': (24.09544, -1.67713),
|
||||
'CF': (22.58701, 5.98438),
|
||||
'CG': (15.78875, 0.40388),
|
||||
'CH': (7.65705, 46.57446),
|
||||
'CI': (-6.31190, 6.62783),
|
||||
'CK': (-159.77835, -21.23349),
|
||||
'CL': (-70.41790, -53.77189),
|
||||
'CM': (13.26022, 5.94519),
|
||||
'CN': (96.44285, 38.04260),
|
||||
'CO': (-72.52951, 2.45174),
|
||||
'CR': (-83.83314, 9.93514),
|
||||
'CU': (-80.81673, 21.88852),
|
||||
'CV': (-24.50810, 14.929),
|
||||
'CW': (-68.96409, 12.1845),
|
||||
'CX': (105.62411, -10.48417),
|
||||
'CY': (32.95922, 35.37010),
|
||||
'CZ': (16.32098, 49.50692),
|
||||
'DE': (9.30716, 50.21289),
|
||||
'DJ': (42.96904, 11.41542),
|
||||
'DK': (9.18490, 55.98916),
|
||||
'DM': (-61.00358, 15.65470),
|
||||
'DO': (-69.62855, 18.58841),
|
||||
'DZ': (4.24749, 25.79721),
|
||||
'EC': (-77.45831, -0.98284),
|
||||
'EE': (23.94288, 58.43952),
|
||||
'EG': (28.95293, 28.17718),
|
||||
'EH': (-13.69031, 25.01241),
|
||||
'ER': (39.01223, 14.96033),
|
||||
'ES': (-2.59110, 38.79354),
|
||||
'ET': (38.61697, 7.71399),
|
||||
'FI': (26.89798, 63.56194),
|
||||
'FJ': (177.91853, -17.74237),
|
||||
'FK': (-58.99044, -51.34509),
|
||||
'FM': (151.95358, 8.5045),
|
||||
'FO': (-6.60483, 62.10000),
|
||||
'FR': (0.28410, 47.51045),
|
||||
'GA': (10.81070, -0.07429),
|
||||
'GB': (-0.92823, 52.01618),
|
||||
'GD': (-61.64524, 12.191),
|
||||
'GE': (44.16664, 42.00385),
|
||||
'GF': (-53.46524, 3.56188),
|
||||
'GG': (-2.50580, 49.58543),
|
||||
'GH': (-0.46348, 7.16051),
|
||||
'GI': (-5.32053, 36.11066),
|
||||
'GL': (-33.85511, 74.66355),
|
||||
'GM': (-16.40960, 13.25),
|
||||
'GN': (-13.83940, 10.96291),
|
||||
'GP': (-61.68712, 16.23049),
|
||||
'GQ': (10.23973, 1.43119),
|
||||
'GR': (23.17850, 39.06206),
|
||||
'GS': (-36.49430, -54.43067),
|
||||
'GT': (-90.74368, 15.20428),
|
||||
'GU': (144.73362, 13.44413),
|
||||
'GW': (-14.83525, 11.92486),
|
||||
'GY': (-58.45167, 5.73698),
|
||||
'HK': (114.18577, 22.34923),
|
||||
'HM': (73.68230, -53.22105),
|
||||
'HN': (-86.95414, 15.23820),
|
||||
'HR': (17.49966, 45.52689),
|
||||
'HT': (-73.51925, 18.32492),
|
||||
'HU': (20.35362, 47.51721),
|
||||
'ID': (123.34505, -0.83791),
|
||||
'IE': (-9.00520, 52.87725),
|
||||
'IL': (35.46314, 32.86165),
|
||||
'IM': (-4.86740, 54.023),
|
||||
'IN': (88.67620, 27.86155),
|
||||
'IO': (71.42743, -6.14349),
|
||||
'IQ': (42.58109, 34.26103),
|
||||
'IR': (56.09355, 30.46751),
|
||||
'IS': (-17.51785, 64.71687),
|
||||
'IT': (10.42639, 44.87904),
|
||||
'JE': (-2.19261, 49.12458),
|
||||
'JM': (-76.84020, 18.3935),
|
||||
'JO': (36.55552, 30.75741),
|
||||
'JP': (138.72531, 35.92099),
|
||||
'KE': (36.90602, 1.08512),
|
||||
'KG': (76.15571, 41.66497),
|
||||
'KH': (104.31901, 12.95555),
|
||||
'KI': (173.63353, 0.139),
|
||||
'KM': (44.31474, -12.241),
|
||||
'KN': (-62.69379, 17.2555),
|
||||
'KP': (126.65575, 39.64575),
|
||||
'KR': (127.27740, 36.41388),
|
||||
'KW': (47.30684, 29.69180),
|
||||
'KY': (-81.07455, 19.29949),
|
||||
'KZ': (72.00811, 49.88855),
|
||||
'LA': (102.44391, 19.81609),
|
||||
'LB': (35.48464, 33.41766),
|
||||
'LC': (-60.97894, 13.891),
|
||||
'LI': (9.54693, 47.15934),
|
||||
'LK': (80.38520, 8.41649),
|
||||
'LR': (-11.16960, 4.04122),
|
||||
'LS': (28.66984, -29.94538),
|
||||
'LT': (24.51735, 55.49293),
|
||||
'LU': (6.08649, 49.81533),
|
||||
'LV': (23.51033, 56.67144),
|
||||
'LY': (15.36841, 28.12177),
|
||||
'MA': (-4.03061, 33.21696),
|
||||
'MC': (7.47743, 43.62917),
|
||||
'MD': (29.61725, 46.66517),
|
||||
'ME': (19.72291, 43.02441),
|
||||
'MF': (-63.06666, 18.08102),
|
||||
'MG': (45.86378, -20.50245),
|
||||
'MH': (171.94982, 5.983),
|
||||
'MK': (21.42108, 41.08980),
|
||||
'ML': (-1.93310, 16.46993),
|
||||
'MM': (95.54624, 21.09620),
|
||||
'MN': (99.81138, 48.18615),
|
||||
'MO': (113.56441, 22.16209),
|
||||
'MP': (145.21345, 14.14902),
|
||||
'MQ': (-60.81128, 14.43706),
|
||||
'MR': (-9.42324, 22.59251),
|
||||
'MS': (-62.19455, 16.745),
|
||||
'MT': (14.38363, 35.94467),
|
||||
'MU': (57.55121, -20.41),
|
||||
'MV': (73.39292, 4.19375),
|
||||
'MW': (33.95722, -12.28218),
|
||||
'MX': (-105.89221, 25.86826),
|
||||
'MY': (112.71154, 2.10098),
|
||||
'MZ': (37.58689, -13.72682),
|
||||
'NA': (16.68569, -21.46572),
|
||||
'NC': (164.95322, -20.38889),
|
||||
'NE': (10.06041, 19.08273),
|
||||
'NF': (167.95718, -29.0645),
|
||||
'NG': (10.17781, 10.17804),
|
||||
'NI': (-85.87974, 13.21715),
|
||||
'NL': (-68.57062, 12.041),
|
||||
'NO': (23.11556, 70.09934),
|
||||
'NP': (83.36259, 28.13107),
|
||||
'NR': (166.93479, -0.5275),
|
||||
'NU': (-169.84873, -19.05305),
|
||||
'NZ': (167.97209, -45.13056),
|
||||
'OM': (56.86055, 20.47413),
|
||||
'PA': (-79.40160, 8.80656),
|
||||
'PE': (-78.66540, -7.54711),
|
||||
'PF': (-145.05719, -16.70862),
|
||||
'PG': (146.64600, -7.37427),
|
||||
'PH': (121.48359, 15.09965),
|
||||
'PK': (72.11347, 31.14629),
|
||||
'PL': (17.88136, 52.77182),
|
||||
'PM': (-56.19515, 46.78324),
|
||||
'PN': (-130.10642, -25.06955),
|
||||
'PR': (-65.88755, 18.37169),
|
||||
'PS': (35.39801, 32.24773),
|
||||
'PT': (-8.45743, 40.11154),
|
||||
'PW': (134.49645, 7.3245),
|
||||
'PY': (-59.51787, -22.41281),
|
||||
'QA': (51.49903, 24.99816),
|
||||
'RE': (55.77345, -21.36388),
|
||||
'RO': (26.37632, 45.36120),
|
||||
'RS': (20.40371, 44.56413),
|
||||
'RU': (116.44060, 59.06780),
|
||||
'RW': (29.57882, -1.62404),
|
||||
'SA': (47.73169, 22.43790),
|
||||
'SB': (164.63894, -10.23606),
|
||||
'SC': (46.36566, -9.454),
|
||||
'SD': (28.14720, 14.56423),
|
||||
'SE': (15.68667, 60.35568),
|
||||
'SG': (103.84187, 1.304),
|
||||
'SH': (-12.28155, -37.11546),
|
||||
'SI': (14.04738, 46.39085),
|
||||
'SJ': (15.27552, 79.23365),
|
||||
'SK': (20.41603, 48.86970),
|
||||
'SL': (-11.47773, 8.78156),
|
||||
'SM': (12.46062, 43.94279),
|
||||
'SN': (-15.37111, 14.99477),
|
||||
'SO': (46.93383, 9.34094),
|
||||
'SR': (-55.42864, 4.56985),
|
||||
'SS': (28.13573, 8.50933),
|
||||
'ST': (6.61025, 0.2215),
|
||||
'SV': (-89.36665, 13.43072),
|
||||
'SX': (-63.15393, 17.9345),
|
||||
'SY': (38.15513, 35.34221),
|
||||
'SZ': (31.78263, -26.14244),
|
||||
'TC': (-71.32554, 21.35),
|
||||
'TD': (17.42092, 13.46223),
|
||||
'TF': (137.5, -67.5),
|
||||
'TG': (1.06983, 7.87677),
|
||||
'TH': (102.00877, 16.42310),
|
||||
'TJ': (71.91349, 39.01527),
|
||||
'TK': (-171.82603, -9.20990),
|
||||
'TL': (126.22520, -8.72636),
|
||||
'TM': (57.71603, 39.92534),
|
||||
'TN': (9.04958, 34.84199),
|
||||
'TO': (-176.99320, -23.11104),
|
||||
'TR': (32.82002, 39.86350),
|
||||
'TT': (-60.70793, 11.1385),
|
||||
'TV': (178.77499, -9.41685),
|
||||
'TW': (120.30074, 23.17002),
|
||||
'TZ': (33.53892, -5.01840),
|
||||
'UA': (33.44335, 49.30619),
|
||||
'UG': (32.96523, 2.08584),
|
||||
'UM': (-169.50993, 16.74605),
|
||||
'US': (-116.39535, 40.71379),
|
||||
'UY': (-56.46505, -33.62658),
|
||||
'UZ': (61.35529, 42.96107),
|
||||
'VA': (12.33197, 42.04931),
|
||||
'VC': (-61.09905, 13.316),
|
||||
'VE': (-64.88323, 7.69849),
|
||||
'VG': (-64.62479, 18.419),
|
||||
'VI': (-64.88950, 18.32263),
|
||||
'VN': (104.20179, 10.27644),
|
||||
'VU': (167.31919, -15.88687),
|
||||
'WF': (-176.20781, -13.28535),
|
||||
'WS': (-172.10966, -13.85093),
|
||||
'YE': (45.94562, 16.16338),
|
||||
'YT': (44.93774, -12.60882),
|
||||
'ZA': (23.19488, -30.43276),
|
||||
'ZM': (26.38618, -14.39966),
|
||||
'ZW': (30.12419, -19.86907)
|
||||
}
|
||||
34
test/bdd/utils/grid.py
Normal file
34
test/bdd/utils/grid.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This file is part of Nominatim. (https://nominatim.org)
|
||||
#
|
||||
# Copyright (C) 2025 by the Nominatim developer community.
|
||||
# For a full list of authors see the git log.
|
||||
"""
|
||||
A grid describing node placement in an area.
|
||||
Useful for visually describing geometries.
|
||||
"""
|
||||
|
||||
|
||||
class Grid:
|
||||
|
||||
def __init__(self, table, step, origin):
|
||||
if step is None:
|
||||
step = 0.00001
|
||||
if origin is None:
|
||||
origin = (0.0, 0.0)
|
||||
self.grid = {}
|
||||
|
||||
y = origin[1]
|
||||
for line in table:
|
||||
x = origin[0]
|
||||
for pt_id in line:
|
||||
if pt_id:
|
||||
self.grid[pt_id] = (x, y)
|
||||
x += step
|
||||
y += step
|
||||
|
||||
def get(self, nodeid):
|
||||
""" Get the coordinates for the given grid node.
|
||||
"""
|
||||
return self.grid.get(nodeid)
|
||||
Reference in New Issue
Block a user