forked from hans/Nominatim
implement BDD osm2pgsql tests with pytest-bdd
This commit is contained in:
@@ -11,18 +11,24 @@ import sys
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
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
|
# always test against the source
|
||||||
SRC_DIR = (Path(__file__) / '..' / '..' / '..').resolve()
|
SRC_DIR = (Path(__file__) / '..' / '..' / '..').resolve()
|
||||||
sys.path.insert(0, str(SRC_DIR / 'src'))
|
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):
|
def _strlist(inp):
|
||||||
return [s.strip() for s in inp.split(',')]
|
return [s.strip() for s in inp.split(',')]
|
||||||
@@ -60,6 +66,35 @@ def datatable():
|
|||||||
return None
|
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.-]*)'),
|
@when(step_parse(r'reverse geocoding (?P<lat>[\d.-]*),(?P<lon>[\d.-]*)'),
|
||||||
target_fixture='nominatim_result')
|
target_fixture='nominatim_result')
|
||||||
def reverse_geocode_via_api(test_config_env, pytestconfig, datatable, lat, lon):
|
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:
|
for k, v in pairs:
|
||||||
assert ResultAttr(nominatim_result.result[num], prefix + k) == v
|
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
|
Then a HTTP 200 is returned
|
||||||
And the result is valid json
|
And the result is valid json
|
||||||
And the result contains
|
And the result contains
|
||||||
| geotext!fm |
|
| geotext!wkt |
|
||||||
| LINESTRING\(9.5039353 47.0657546, ?9.5040437 47.0657781, ?9.5040808 47.065787, ?9.5054298 47.0661407\) |
|
| 9.5039353 47.0657546, 9.5040437 47.0657781, 9.5040808 47.065787, 9.5054298 47.0661407 |
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| format |
|
| format |
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ Feature: XML output for Reverse API
|
|||||||
Then a HTTP 200 is returned
|
Then a HTTP 200 is returned
|
||||||
And the result is valid xml
|
And the result is valid xml
|
||||||
And the result contains
|
And the result contains
|
||||||
| geotext!fm |
|
| geotext!wkt |
|
||||||
| LINESTRING\(9.5039353 47.0657546, ?9.5040437 47.0657781, ?9.5040808 47.065787, ?9.5054298 47.0661407\) |
|
| 9.5039353 47.0657546, 9.5040437 47.0657781, 9.5040808 47.065787, 9.5054298 47.0661407 |
|
||||||
|
|
||||||
Scenario: Reverse XML - Output of SVG
|
Scenario: Reverse XML - Output of SVG
|
||||||
When sending v1/reverse with format xml
|
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 json
|
||||||
import re
|
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 = {
|
COMPARATOR_TERMS = {
|
||||||
'exactly': lambda exp, act: exp == act,
|
'exactly': lambda exp, act: exp == act,
|
||||||
@@ -43,10 +49,12 @@ COMPARISON_FUNCS = {
|
|||||||
None: lambda val, exp: str(val) == exp,
|
None: lambda val, exp: str(val) == exp,
|
||||||
'i': lambda val, exp: str(val).lower() == exp.lower(),
|
'i': lambda val, exp: str(val).lower() == exp.lower(),
|
||||||
'fm': lambda val, exp: re.fullmatch(exp, val) is not None,
|
'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
|
'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:
|
class ResultAttr:
|
||||||
@@ -60,12 +68,15 @@ class ResultAttr:
|
|||||||
|
|
||||||
Available formatters:
|
Available formatters:
|
||||||
|
|
||||||
!:... - use a formatting expression according to Python Mini Format Spec
|
!:... - use a formatting expression according to Python Mini Format Spec
|
||||||
!i - make case-insensitive comparison
|
!i - make case-insensitive comparison
|
||||||
!fm - consider comparison string a regular expression and match full value
|
!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
|
self.obj = obj
|
||||||
if '!' in key:
|
if '!' in key:
|
||||||
self.key, self.fmt = key.rsplit('!', 1)
|
self.key, self.fmt = key.rsplit('!', 1)
|
||||||
@@ -100,6 +111,9 @@ class ResultAttr:
|
|||||||
if self.fmt.startswith(':'):
|
if self.fmt.startswith(':'):
|
||||||
return other == f"{{{self.fmt}}}".format(self.subobj)
|
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}'.")
|
raise RuntimeError(f"Unknown format string '{self.fmt}'.")
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -107,3 +121,125 @@ class ResultAttr:
|
|||||||
if self.fmt:
|
if self.fmt:
|
||||||
k += '!' + self.fmt
|
k += '!' + self.fmt
|
||||||
return f"result[{k}]({self.subobj})"
|
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.
|
Helper functions for managing test databases.
|
||||||
"""
|
"""
|
||||||
|
import asyncio
|
||||||
import psycopg
|
import psycopg
|
||||||
from psycopg import sql as pysql
|
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:
|
class DBManager:
|
||||||
|
|
||||||
@@ -42,3 +49,53 @@ class DBManager:
|
|||||||
cur = conn.execute('select count(*) from pg_database where datname = %s',
|
cur = conn.execute('select count(*) from pg_database where datname = %s',
|
||||||
(dbname,))
|
(dbname,))
|
||||||
return cur.fetchone()[0] == 1
|
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