From b34991d85fa3c0d7171e2b12e235815997ab5d36 Mon Sep 17 00:00:00 2001 From: Sarah Hoffmann Date: Wed, 9 Apr 2025 14:52:34 +0200 Subject: [PATCH] add BDD tests for DB --- src/nominatim_db/tokenizer/factory.py | 2 - test/bdd/conftest.py | 63 +- .../bdd/features/db/import/addressing.feature | 531 ++++++++++++++ test/bdd/features/db/import/country.feature | 91 +++ .../features/db/import/interpolation.feature | 616 +++++++++++++++++ test/bdd/features/db/import/linking.feature | 332 +++++++++ test/bdd/features/db/import/naming.feature | 104 +++ test/bdd/features/db/import/parenting.feature | 651 ++++++++++++++++++ test/bdd/features/db/import/placex.feature | 193 ++++++ test/bdd/features/db/import/postcodes.feature | 208 ++++++ .../db/import/rank_computation.feature | 295 ++++++++ .../features/db/import/search_name.feature | 389 +++++++++++ .../features/db/query/housenumbers.feature | 320 +++++++++ .../features/db/query/interpolation.feature | 57 ++ test/bdd/features/db/query/japanese.feature | 28 + test/bdd/features/db/query/linking.feature | 63 ++ .../features/db/query/normalization.feature | 225 ++++++ test/bdd/features/db/query/postcodes.feature | 109 +++ test/bdd/features/db/query/reverse.feature | 21 + .../features/db/query/search_simple.feature | 84 +++ test/bdd/features/db/update/country.feature | 108 +++ .../features/db/update/interpolation.feature | 418 +++++++++++ .../features/db/update/linked_places.feature | 340 +++++++++ test/bdd/features/db/update/naming.feature | 20 + test/bdd/features/db/update/parenting.feature | 163 +++++ test/bdd/features/db/update/postcode.feature | 139 ++++ test/bdd/features/db/update/simple.feature | 117 ++++ test/bdd/test_db.py | 245 +++++++ test/bdd/test_osm2pgsql.py | 12 +- test/bdd/utils/checks.py | 47 +- test/bdd/utils/db.py | 5 +- test/bdd/utils/grid.py | 2 +- test/bdd/utils/place_inserter.py | 143 ++++ 33 files changed, 6095 insertions(+), 46 deletions(-) create mode 100644 test/bdd/features/db/import/addressing.feature create mode 100644 test/bdd/features/db/import/country.feature create mode 100644 test/bdd/features/db/import/interpolation.feature create mode 100644 test/bdd/features/db/import/linking.feature create mode 100644 test/bdd/features/db/import/naming.feature create mode 100644 test/bdd/features/db/import/parenting.feature create mode 100644 test/bdd/features/db/import/placex.feature create mode 100644 test/bdd/features/db/import/postcodes.feature create mode 100644 test/bdd/features/db/import/rank_computation.feature create mode 100644 test/bdd/features/db/import/search_name.feature create mode 100644 test/bdd/features/db/query/housenumbers.feature create mode 100644 test/bdd/features/db/query/interpolation.feature create mode 100644 test/bdd/features/db/query/japanese.feature create mode 100644 test/bdd/features/db/query/linking.feature create mode 100644 test/bdd/features/db/query/normalization.feature create mode 100644 test/bdd/features/db/query/postcodes.feature create mode 100644 test/bdd/features/db/query/reverse.feature create mode 100644 test/bdd/features/db/query/search_simple.feature create mode 100644 test/bdd/features/db/update/country.feature create mode 100644 test/bdd/features/db/update/interpolation.feature create mode 100644 test/bdd/features/db/update/linked_places.feature create mode 100644 test/bdd/features/db/update/naming.feature create mode 100644 test/bdd/features/db/update/parenting.feature create mode 100644 test/bdd/features/db/update/postcode.feature create mode 100644 test/bdd/features/db/update/simple.feature create mode 100644 test/bdd/test_db.py create mode 100644 test/bdd/utils/place_inserter.py diff --git a/src/nominatim_db/tokenizer/factory.py b/src/nominatim_db/tokenizer/factory.py index 570f9f86..43b65bae 100644 --- a/src/nominatim_db/tokenizer/factory.py +++ b/src/nominatim_db/tokenizer/factory.py @@ -70,8 +70,6 @@ def get_tokenizer_for_db(config: Configuration) -> AbstractTokenizer: The function looks up the appropriate tokenizer in the database and initialises it. """ - assert config.project_dir is not None - with connect(config.get_libpq_dsn()) as conn: name = properties.get_property(conn, 'tokenizer') diff --git a/test/bdd/conftest.py b/test/bdd/conftest.py index dd119cfa..7827796a 100644 --- a/test/bdd/conftest.py +++ b/test/bdd/conftest.py @@ -12,6 +12,7 @@ import json from pathlib import Path import psycopg +from psycopg import sql as pysql # always test against the source SRC_DIR = (Path(__file__) / '..' / '..' / '..').resolve() @@ -25,13 +26,13 @@ pytest.register_assert_rewrite('utils') from utils.api_runner import APIRunner from utils.api_result import APIResult -from utils.checks import ResultAttr, COMPARATOR_TERMS, check_table_content, check_table_has_lines +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 -from nominatim_db import cli +from nominatim_db.data.country_info import setup_country_config def _strlist(inp): @@ -77,6 +78,11 @@ def node_grid(): return Grid([[]], None, None) +@pytest.fixture(scope='session', autouse=True) +def setup_country_info(): + setup_country_config(Configuration(None)) + + @pytest.fixture(scope='session') def template_db(pytestconfig): """ Create a template database containing the extensions and base data @@ -147,6 +153,28 @@ def reverse_geocode_via_api(test_config_env, pytestconfig, datatable, lat, lon): return result +@when(step_parse(r'reverse geocoding at node (?P[\d]+)'), + target_fixture='nominatim_result') +def reverse_geocode_via_api_and_grid(test_config_env, pytestconfig, node_grid, datatable, node): + coords = node_grid.get(node) + if coords is None: + raise ValueError('Unknown node id') + runner = APIRunner(test_config_env, pytestconfig.option.NOMINATIM_API_ENGINE) + api_response = runner.run_step('reverse', + {'lat': coords[1], 'lon': coords[0]}, + datatable, 'jsonv2', {}) + + assert api_response.status == 200 + assert api_response.headers['content-type'] == 'application/json; charset=utf-8' + + result = APIResult('json', 'reverse', api_response.body) + assert result.is_simple() + + result.result['centroid'] = f"POINT({result.result['lon']:.7f} {result.result['lat']:.7f})" + + return result + + @when(step_parse(r'geocoding(?: "(?P.*)")?'), target_fixture='nominatim_result') def forward_geocode_via_api(test_config_env, pytestconfig, datatable, query): @@ -164,6 +192,9 @@ def forward_geocode_via_api(test_config_env, pytestconfig, datatable, query): result = APIResult('json', 'search', api_response.body) assert not result.is_simple() + for res in result.result: + res['centroid'] = f"POINT({res['lon']:.7f} {res['lat']:.7f})" + return result @@ -194,7 +225,7 @@ def check_metadata_for_field_presence(nominatim_result, attributes): @then(step_parse(r'the result contains(?: in field (?P\S+))?')) -def check_result_for_fields(nominatim_result, datatable, field): +def check_result_for_fields(nominatim_result, datatable, node_grid, field): assert nominatim_result.is_simple() if datatable[0] == ['param', 'value']: @@ -205,7 +236,7 @@ def check_result_for_fields(nominatim_result, datatable, field): prefix = field + '+' if field else '' for k, v in pairs: - assert ResultAttr(nominatim_result.result, prefix + k) == v + assert ResultAttr(nominatim_result.result, prefix + k, grid=node_grid) == v @then(step_parse('the result has attributes (?P.*)'), @@ -248,6 +279,7 @@ def check_result_list_match(nominatim_result, datatable, exact): converters={'attributes': _strlist}) def check_all_results_for_field_presence(nominatim_result, attributes): assert not nominatim_result.is_simple() + assert len(nominatim_result) > 0 for res in nominatim_result.result: assert all(a in res for a in attributes), \ f"Missing one of the attributes '{attributes}' in\n{_pretty_json(res)}" @@ -257,14 +289,16 @@ def check_all_results_for_field_presence(nominatim_result, attributes): converters={'attributes': _strlist}) def check_all_result_for_field_absence(nominatim_result, attributes): assert not nominatim_result.is_simple() + assert len(nominatim_result) > 0 for res in nominatim_result.result: assert all(a not in res for a in attributes), \ f"Unexpectedly have one of the attributes '{attributes}' in\n{_pretty_json(res)}" @then(step_parse(r'all results contain(?: in field (?P\S+))?')) -def check_all_results_contain(nominatim_result, datatable, field): +def check_all_results_contain(nominatim_result, datatable, node_grid, field): assert not nominatim_result.is_simple() + assert len(nominatim_result) > 0 if datatable[0] == ['param', 'value']: pairs = datatable[1:] @@ -275,14 +309,14 @@ def check_all_results_contain(nominatim_result, datatable, field): for k, v in pairs: for r in nominatim_result.result: - assert ResultAttr(r, prefix + k) == v + assert ResultAttr(r, prefix + k, grid=node_grid) == v @then(step_parse(r'result (?P\d+) contains(?: in field (?P\S+))?'), converters={'num': int}) def check_specific_result_for_fields(nominatim_result, datatable, num, field): assert not nominatim_result.is_simple() - assert len(nominatim_result) >= num + 1 + assert len(nominatim_result) > num if datatable[0] == ['param', 'value']: pairs = datatable[1:] @@ -313,3 +347,18 @@ def set_node_grid(datatable, step, origin): raise RuntimeError('Grid origin must be either coordinate or alias.') return Grid(datatable, step, origin) + + +@then(step_parse('(?Pplacex?) has no entry for ' + r'(?P[NRW])(?P\d+)(?::(?P\S+))?'), + converters={'osm_id': int}) +def check_place_missing_lines(db_conn, table, osm_type, osm_id, osm_class): + sql = pysql.SQL("""SELECT count(*) FROM {} + WHERE osm_type = %s and osm_id = %s""").format(pysql.Identifier(table)) + params = [osm_type, int(osm_id)] + if osm_class: + sql += pysql.SQL(' AND class = %s') + params.append(osm_class) + + with db_conn.cursor() as cur: + assert cur.execute(sql, params).fetchone()[0] == 0 diff --git a/test/bdd/features/db/import/addressing.feature b/test/bdd/features/db/import/addressing.feature new file mode 100644 index 00000000..e61a4777 --- /dev/null +++ b/test/bdd/features/db/import/addressing.feature @@ -0,0 +1,531 @@ +Feature: Address computation + Tests for filling of place_addressline + + Scenario: place nodes are added to the address when they are close enough + Given the 0.002 grid + | 2 | | | | | | 1 | | 3 | + And the places + | osm | class | type | name | geometry | + | N1 | place | square | Square | 1 | + | N2 | place | hamlet | West Farm | 2 | + | N3 | place | hamlet | East Farm | 3 | + When importing + Then place_addressline contains exactly + | object | address | fromarea | + | N1 | N3 | False | + When geocoding "Square" + Then the result set contains + | object | display_name | + | N1 | Square, East Farm | + + Scenario: given two place nodes, the closer one wins for the address + Given the grid + | 2 | | | 1 | | 3 | + And the named places + | osm | class | type | geometry | + | N1 | place | square | 1 | + | N2 | place | hamlet | 2 | + | N3 | place | hamlet | 3 | + When importing + Then place_addressline contains + | object | address | fromarea | isaddress | + | N1 | N3 | False | True | + | N1 | N2 | False | False | + + Scenario: boundaries around the place are added to the address + Given the grid + | 1 | | 4 | | 7 | 10 | + | 2 | | 5 | | 8 | 11 | + | | | | | | | + | | | | | | | + | | | 6 | | 9 | | + | | 99 | | | | | + | 3 | | | | | 12 | + And the named places + | osm | class | type | admin | geometry | + | R1 | boundary | administrative | 3 | (1,2,3,12,11,10,7,8,9,6,5,4,1) | + | R2 | boundary | administrative | 4 | (2,3,12,11,8,9,6,5,2) | + | N1 | place | square | 15 | 99 | + When importing + Then place_addressline contains + | object | address | isaddress | + | N1 | R1 | True | + | N1 | R2 | True | + + Scenario: with boundaries of same rank the one with the closer centroid is preferred + Given the grid + | 1 | | | 3 | | 5 | + | | 9 | | | | | + | 2 | | | 4 | | 6 | + And the named places + | osm | class | type | admin | geometry | + | R1 | boundary | administrative | 8 | (1,2,4,3,1) | + | R2 | boundary | administrative | 8 | (1,2,6,5,1) | + | N1 | place | square | 15 | 9 | + When importing + Then place_addressline contains + | object | address | isaddress | + | N1 | R1 | True | + | N1 | R2 | False | + + Scenario: boundary areas are preferred over place nodes in the address + Given the grid + | 1 | | | | 10 | | 3 | + | | 5 | | | | | | + | | 6 | | | | | | + | 2 | | | | 11 | | 4 | + And the named places + | osm | class | type | admin | geometry | + | N1 | place | square | 15 | 5 | + | N2 | place | city | 15 | 6 | + | R1 | place | city | 8 | (1,2,4,3,1) | + | R2 | boundary | administrative | 9 | (1,10,11,2,1) | + When importing + Then place_addressline contains + | object | address | isaddress | cached_rank_address | + | N1 | R1 | True | 16 | + | N1 | R2 | True | 18 | + | N1 | N2 | False | 18 | + + Scenario: place nodes outside a smaller ranked area are ignored + Given the grid + | 1 | | 2 | | + | | 7 | | 9 | + | 4 | | 3 | | + And the named places + | osm | class | type | admin | geometry | + | N1 | place | square | 15 | 7 | + | N2 | place | city | 15 | 9 | + | R1 | place | city | 8 | (1,2,3,4,1) | + When importing + Then place_addressline contains exactly + | object | address | isaddress | cached_rank_address | + | N1 | R1 | True | 16 | + + + Scenario: place nodes close enough to smaller ranked place nodes are included + Given the 0.002 grid + | 2 | | 3 | 1 | + And the named places + | osm | class | type | geometry | + | N1 | place | square | 1 | + | N2 | place | hamlet | 2 | + | N3 | place | quarter | 3 | + When importing + Then place_addressline contains + | object | address | fromarea | isaddress | + | N1 | N2 | False | True | + | N1 | N3 | False | True | + + + Scenario: place nodes too far away from a smaller ranked place nodes are marked non-address + Given the 0.002 grid + | 2 | | | 1 | | 3 | + And the named places + | osm | class | type | geometry | + | N1 | place | square | 1 | + | N2 | place | hamlet | 2 | + | N3 | place | quarter | 3 | + When importing + Then place_addressline contains + | object | address | fromarea | isaddress | + | N1 | N2 | False | True | + | N1 | N3 | False | False | + + + # github #121 + Scenario: Roads crossing boundaries should contain both states + Given the grid + | 1 | | | 2 | | 3 | + | | 7 | | | 8 | | + | 4 | | | 5 | | 6 | + And the named places + | osm | class | type | geometry | + | W1 | highway | road | 7, 8 | + And the named places + | osm | class | type | admin | geometry | + | W10 | boundary | administrative | 5 | (1, 2, 5, 4, 1) | + | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 2) | + When importing + Then place_addressline contains + | object | address | cached_rank_address | + | W1 | W10 | 10 | + | W1 | W11 | 10 | + + + Scenario: Roads following a boundary should contain both states + Given the grid + | 1 | | | 2 | | 3 | + | | | 8 | 7 | | | + | 4 | | | 5 | | 6 | + And the named places + | osm | class | type | geometry | + | W1 | highway | road | 2, 7, 8 | + And the named places + | osm | class | type | admin | geometry | + | W10 | boundary | administrative | 5 | (1, 2, 5, 4, 1) | + | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 2) | + When importing + Then place_addressline contains + | object | address | cached_rank_address | + | W1 | W10 | 10 | + | W1 | W11 | 10 | + + Scenario: Roads should not contain boundaries they touch in a end point + Given the grid + | 1 | | | 2 | | 3 | + | | 7 | | 8 | | | + | 4 | | | 5 | | 6 | + And the named places + | osm | class | type | geometry | + | W1 | highway | road | 7, 8 | + And the named places + | osm | class | type | admin | geometry | + | W10 | boundary | administrative | 5 | (1, 2, 8, 5, 4, 1) | + | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 8, 2) | + When importing + Then place_addressline contains exactly + | object | address | cached_rank_address | + | W1 | W10 | 10 | + + Scenario: Roads should not contain boundaries they touch in a middle point + Given the grid + | 1 | | | 2 | | 3 | + | | 7 | | 8 | | | + | 4 | | 9 | 5 | | 6 | + And the named places + | osm | class | type | geometry | + | W1 | highway | road | 7, 8, 9 | + And the named places + | osm | class | type | admin | geometry | + | W10 | boundary | administrative | 5 | (1, 2, 8, 5, 4, 1) | + | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 8, 2) | + When importing + Then place_addressline contains exactly + | object | address | cached_rank_address | + | W1 | W10 | 10 | + + Scenario: Locality points should contain all boundaries they touch + Given the 0.001 grid + | 1 | | | 2 | | 3 | + | | | | 8 | | | + | 4 | | | 5 | | 6 | + And the named places + | osm | class | type | geometry | + | N1 | place | locality | 8 | + And the named places + | osm | class | type | admin | geometry | + | W10 | boundary | administrative | 5 | (1, 2, 8, 5, 4, 1) | + | W11 | boundary | administrative | 5 | (2, 3, 6, 5, 8, 2) | + When importing + Then place_addressline contains + | object | address | cached_rank_address | + | N1 | W10 | 10 | + | N1 | W11 | 10 | + + Scenario: Areas should not contain boundaries they touch + Given the grid + | 1 | | | 2 | | 3 | + | | | | | | | + | 4 | | | 5 | | 6 | + And the named places + | osm | class | type | geometry | + | W1 | landuse | industrial | (1, 2, 5, 4, 1) | + And the named places + | osm | class | type | admin | geometry | + | W10 | boundary | administrative | 5 | (2, 3, 6, 5, 2) | + When importing + Then place_addressline contains exactly + | object | address | + + Scenario: buildings with only addr:postcodes do not appear in the address of a way + Given the grid with origin DE + | 1 | | | | | 8 | | 6 | | 2 | + | |10 |11 | | | | | | | | + | |13 |12 | | | | | | | | + | 20| | | 21| | | | | | | + | | | | | | | | | | | + | | | | | | 9 | | | | | + | 4 | | | | | | | 7 | | 3 | + And the named places + | osm | class | type | admin | addr+postcode | geometry | + | R1 | boundary | administrative | 6 | 10000 | (1,2,3,4,1)| + | R34 | boundary | administrative | 8 | 11200 | (1,6,7,4,1)| + | R4 | boundary | administrative | 10 | 11230 | (1,8,9,4,1)| + And the named places + | osm | class | type | geometry | + | W93 | highway | residential | 20,21 | + And the places + | osm | class | type | addr+postcode | geometry | + | W22 | place | postcode | 11234 | (10,11,12,13,10) | + When importing + Then place_addressline contains exactly + | object | address | + | R4 | R1 | + | R4 | R34 | + | R34 | R1 | + | W93 | R1 | + | W93 | R34 | + | W93 | R4 | + + Scenario: postcode boundaries do appear in the address of a way + Given the grid with origin DE + | 1 | | | | | 8 | | 6 | | 2 | + | |10 |11 | | | | | | | | + | |13 |12 | | | | | | | | + | 20| | | 21| | | | | | | + | | | | | | | | | | | + | | | | | | 9 | | | | | + | 4 | | | | | | | 7 | | 3 | + And the named places + | osm | class | type | admin | addr+postcode | geometry | + | R1 | boundary | administrative | 6 | 10000 | (1,2,3,4,1) | + | R34 | boundary | administrative | 8 | 11000 | (1,6,7,4,1) | + And the places + | osm | class | type | addr+postcode | geometry | + | R4 | boundary | postal_code | 11200 | (1,8,9,4,1) | + And the named places + | osm | class | type | geometry | + | W93 | highway | residential | 20,21 | + And the places + | osm | class | type | addr+postcode | geometry | + | W22 | place | postcode | 11234 | (10,11,12,13,10) | + When importing + Then place_addressline contains + | object | address | + | W93 | R4 | + + Scenario: squares do not appear in the address of a street + Given the grid + | | 1 | | 2 | | + | 8 | | | | 9 | + | | 4 | | 3 | | + And the named places + | osm | class | type | geometry | + | W1 | highway | residential | 8, 9 | + | W2 | place | square | (1, 2, 3 ,4, 1) | + When importing + Then place_addressline contains exactly + | object | address | + + Scenario: addr:* tags are honored even when a street is far away from the place + Given the grid + | 1 | | 2 | | | 5 | + | | | | 8 | 9 | | + | 4 | | 3 | | | 6 | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | + | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | + And the places + | osm | class | type | addr+city | geometry | + | W1 | highway | primary | Left | 8,9 | + | W2 | highway | primary | Right | 8,9 | + When importing + Then place_addressline contains exactly + | object | address | isaddress | + | W1 | R1 | True | + | W1 | R2 | False | + | W2 | R2 | True | + + + Scenario: addr:* tags are honored even when a POI is far away from the place + Given the grid + | 1 | | 2 | | | 5 | + | | | | 8 | 9 | | + | 4 | | 3 | | | 6 | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | + | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | + And the places + | osm | class | type | name | addr+city | geometry | + | W1 | highway | primary | Wonderway | Right | 8,9 | + | N1 | amenity | cafe | Bolder | Left | 9 | + When importing + Then place_addressline contains exactly + | object | address | isaddress | + | W1 | R2 | True | + | N1 | R1 | True | + When geocoding "Bolder" + Then the result set contains + | object | display_name | + | N1 | Bolder, Wonderway, Left | + + Scenario: addr:* tags do not produce addresslines when the parent has the address part + Given the grid + | 1 | | | 5 | + | | 8 | 9 | | + | 4 | | | 6 | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 8 | Outer | (1,5,6,4,1) | + And the places + | osm | class | type | name | addr+city | geometry | + | W1 | highway | primary | Wonderway | Outer | 8,9 | + | N1 | amenity | cafe | Bolder | Outer | 9 | + When importing + Then place_addressline contains exactly + | object | address | isaddress | + | W1 | R1 | True | + When geocoding "Bolder" + Then the result set contains + | object | display_name | + | N1 | Bolder, Wonderway, Outer | + + Scenario: addr:* tags on outside do not produce addresslines when the parent has the address part + Given the grid + | 1 | | 2 | | | 5 | + | | | | 8 | 9 | | + | 4 | | 3 | | | 6 | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | + | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | + And the places + | osm | class | type | name | addr+city | geometry | + | W1 | highway | primary | Wonderway | Left | 8,9 | + | N1 | amenity | cafe | Bolder | Left | 9 | + When importing + Then place_addressline contains exactly + | object | address | isaddress | + | W1 | R1 | True | + | W1 | R2 | False | + When geocoding "Bolder" + Then the result set contains + | object | display_name | + | N1 | Bolder, Wonderway, Left | + + Scenario: POIs can correct address parts on the fly + Given the grid + | 1 | | | | 2 | | 5 | + | | | | 9 | | 8 | | + | 4 | | | | 3 | | 6 | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | + | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | + And the places + | osm | class | type | name | geometry | + | W1 | highway | primary | Wonderway | 2,3 | + | N1 | amenity | cafe | Bolder | 9 | + | N2 | amenity | cafe | Leftside | 8 | + When importing + Then place_addressline contains exactly + | object | address | isaddress | + | W1 | R1 | False | + | W1 | R2 | True | + When geocoding "Bolder" + Then the result set contains + | object | display_name | + | N1 | Bolder, Wonderway, Left | + When geocoding "Leftside" + Then the result set contains + | object | display_name | + | N2 | Leftside, Wonderway, Right | + + + Scenario: POIs can correct address parts on the fly (with partial unmatching address) + Given the grid + | 1 | | | | 2 | | 5 | + | | | | 9 | | 8 | | + | | 10| 11| | | 12| | + | 4 | | | | 3 | | 6 | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | + | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | + And the places + | osm | class | type | name | geometry | + | W1 | highway | primary | Wonderway | 10,11,12 | + And the places + | osm | class | type | name | addr+suburb | geometry | + | N1 | amenity | cafe | Bolder | Boring | 9 | + | N2 | amenity | cafe | Leftside | Boring | 8 | + When importing + Then place_addressline contains exactly + | object | address | isaddress | + | W1 | R1 | True | + | W1 | R2 | False | + When geocoding "Bolder" + Then the result set contains + | object | display_name | + | N1 | Bolder, Wonderway, Left | + When geocoding "Leftside" + Then the result set contains + | object | display_name | + | N2 | Leftside, Wonderway, Right | + + + + Scenario: POIs can correct address parts on the fly (with partial matching address) + Given the grid + | 1 | | | | 2 | | 5 | + | | | | 9 | | 8 | | + | | 10| 11| | | 12| | + | 4 | | | | 3 | | 6 | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | + | R2 | boundary | administrative | 8 | Right | (2,3,6,5,2) | + And the places + | osm | class | type | name | geometry | + | W1 | highway | primary | Wonderway | 10,11,12 | + And the places + | osm | class | type | name | addr+state | geometry | + | N1 | amenity | cafe | Bolder | Left | 9 | + | N2 | amenity | cafe | Leftside | Left | 8 | + When importing + Then place_addressline contains exactly + | object | address | isaddress | + | W1 | R1 | True | + | W1 | R2 | False | + When geocoding "Bolder" + Then the result set contains + | object | display_name | + | N1 | Bolder, Wonderway, Left | + When geocoding "Leftside" + Then the result set contains + | object | display_name | + | N2 | Leftside, Wonderway, Left | + + + Scenario: addr:* tags always match the closer area + Given the grid + | 1 | | | | 2 | | 5 | + | | | | | | | | + | 4 | | | | 3 | | 6 | + | | 10| 11| | | | | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) | + | R2 | boundary | administrative | 8 | Left | (2,3,6,5,2) | + And the places + | osm | class | type | name | addr+city | geometry | + | W1 | highway | primary | Wonderway | Left | 10,11 | + When importing + Then place_addressline contains exactly + | object | address | + | W1 | R1 | + + Scenario: Full name is prefered for unlisted addr:place tags + Given the grid + | | 1 | 2 | | + | 8 | | | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | place | city | Away | (8,1,2,9,8) | + And the places + | osm | class | type | name | addr+city | geometry | + | W1 | highway | residential | Royal Terrace | Gardens | 8,9 | + And the places + | osm | class | type | housenr | addr+place | geometry | extra+foo | + | N1 | place | house | 1 | Royal Terrace Gardens | 1 | bar | + And the places + | osm | class | type | housenr | addr+street | geometry | + | N2 | place | house | 2 | Royal Terrace | 2 | + When importing + When geocoding "1, Royal Terrace Gardens" + Then result 0 contains + | object | + | N1 | diff --git a/test/bdd/features/db/import/country.feature b/test/bdd/features/db/import/country.feature new file mode 100644 index 00000000..90f40d05 --- /dev/null +++ b/test/bdd/features/db/import/country.feature @@ -0,0 +1,91 @@ +Feature: Country handling + Tests for import and use of country information + + Scenario: Country names from OSM country relations are added + Given the places + | osm | class | type | admin | name+name:xy | country | geometry | + | R1 | boundary | administrative | 2 | Loudou | de | (9 52, 9 53, 10 52, 9 52) | + Given the places + | osm | class | type | name | geometry | + | N1 | place | town | Wenig | country:de | + When importing + When geocoding "Wenig, Loudou" + Then the result set contains + | object | display_name | + | N1 | Wenig, Deutschland | + When geocoding "Wenig" + | accept-language | + | xy,en | + Then the result set contains + | object | display_name | + | N1 | Wenig, Loudou | + + Scenario: OSM country relations outside expected boundaries are ignored for naming + Given the grid + | 1 | | 2 | + | 4 | | 3 | + Given the places + | osm | class | type | admin | name+name:xy | country | geometry | + | R1 | boundary | administrative | 2 | Loudou | de | (1,2,3,4,1) | + Given the places + | osm | class | type | name | geometry | + | N1 | place | town | Wenig | country:de | + When importing + When geocoding "Wenig" + | accept-language | + | xy,en | + Then the result set contains + | object | display_name | + | N1 | Wenig, Germany | + + Scenario: Pre-defined country names are used + Given the grid with origin CH + | 1 | + Given the places + | osm | class | type | name | geometry | + | N1 | place | town | Ingb | 1 | + When importing + And geocoding "Ingb" + | accept-language | + | en,de | + Then the result set contains + | object | display_name | + | N1 | Ingb, Switzerland | + + Scenario: For overlapping countries, pre-defined countries are tie-breakers + Given the grid with origin US + | 1 | | 2 | | 5 | + | | 9 | | 8 | | + | 4 | | 3 | | 6 | + Given the named places + | osm | class | type | admin | country | geometry | + | R1 | boundary | administrative | 2 | de | (1,5,6,4,1) | + | R2 | boundary | administrative | 2 | us | (1,2,3,4,1) | + And the named places + | osm | class | type | geometry | + | N1 | place | town | 9 | + | N2 | place | town | 8 | + When importing + Then placex contains + | object | country_code | + | N1 | us | + | N2 | de | + + Scenario: For overlapping countries outside pre-define countries prefer smaller partition + Given the grid with origin US + | 1 | | 2 | | 5 | + | | 9 | | 8 | | + | 4 | | 3 | | 6 | + Given the named places + | osm | class | type | admin | country | geometry | + | R1 | boundary | administrative | 2 | ch | (1,5,6,4,1) | + | R2 | boundary | administrative | 2 | de | (1,2,3,4,1) | + And the named places + | osm | class | type | geometry | + | N1 | place | town | 9 | + | N2 | place | town | 8 | + When importing + Then placex contains + | object | country_code | + | N1 | de | + | N2 | ch | diff --git a/test/bdd/features/db/import/interpolation.feature b/test/bdd/features/db/import/interpolation.feature new file mode 100644 index 00000000..b1f31f8f --- /dev/null +++ b/test/bdd/features/db/import/interpolation.feature @@ -0,0 +1,616 @@ +Feature: Import of address interpolations + Tests that interpolated addresses are added correctly + + Scenario: Simple even interpolation line with two points and no street nearby + Given the grid with origin 1,1 + | 1 | | 9 | | 2 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,2 | + And the ways + | id | nodes | + | 1 | 1,2 | + When importing + Then W1 expands to no interpolation + + Scenario: Simple even interpolation line with two points + Given the grid with origin 1,1 + | 1 | | 9 | | 2 | + | 4 | | | | 5 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 4 | 9 | + + Scenario: Backwards even two point interpolation line + Given the grid with origin 1,1 + | 1 | 8 | 9 | 2 | + | 4 | | | 5 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 8 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 2,1 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 2,1 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 6 | 8,9 | + + Scenario: Simple odd two point interpolation + Given the grid with origin 1,1 + | 1 | 8 | | | 9 | 2 | + | 4 | | | | 5 | | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 1 | + | N2 | place | house | 11 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | odd | 1,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 3 | 9 | 8,9 | + + Scenario: Simple all two point interpolation + Given the grid with origin 1,1 + | 1 | 8 | 9 | 2 | + | 4 | | | 5 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 1 | + | N2 | place | house | 4 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | all | 1,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 2 | 3 | 8,9 | + + Scenario: Even two point interpolation line with intermediate empty node + Given the grid + | 1 | 8 | | 3 | 9 | 2 | + | 4 | | | | 5 | | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 12 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,3,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,3,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 10 | 8,3,9 | + + Scenario: Even two point interpolation line with intermediate duplicated empty node + Given the grid + | 4 | | | | 5 | + | 1 | 8 | 3 | 9 | 2 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 10 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,3,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,3,3,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 8 | 8,3,9 | + + Scenario: Simple even three point interpolation line + Given the grid + | 4 | | | | | | 5 | + | 1 | 8 | | 9 | 3 | 7 | 2 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 14 | + | N3 | place | house | 10 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,3,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,3,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 8 | 8,9 | + | 12 | 12 | 7 | + + Scenario: Simple even four point interpolation line + Given the grid + | 1 | 10 | | 11 | 3 | + | | | | | 12| + | | | 4 | 13 | 2 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 14 | + | N3 | place | house | 10 | + | N4 | place | house | 18 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,3,2,4 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 1,3,2,4 | + And the ways + | id | nodes | + | 1 | 1,3,2,4 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 8 | 10,11 | + | 12 | 12 | 12 | + | 16 | 16 | 13 | + + Scenario: Reverse simple even three point interpolation line + Given the grid + | 1 | 8 | | 9 | 3 | 7 | 2 | + | 4 | | | | | | 5 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 14 | + | N3 | place | house | 10 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 2,3,1 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 2,3,1 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 8 | 8,9 | + | 12 | 12 | 7 | + + Scenario: Even three point interpolation line with odd center point + Given the grid + | 1 | | 10 | | 11 | 3 | 2 | + | 4 | | | | | | 5 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 8 | + | N3 | place | house | 7 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,3,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,3,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 6 | 10,11 | + + Scenario: Interpolation line with self-intersecting way + Given the grid + | 1 | 9 | 2 | + | | | 8 | + | | | 3 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + | N3 | place | house | 10 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,2,3,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 1,2,3 | + And the ways + | id | nodes | + | 1 | 1,2,3,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 4 | 9 | + | 8 | 8 | 8 | + | 8 | 8 | 8 | + + Scenario: Interpolation line with self-intersecting way II + Given the grid + | 1 | 9 | 2 | + | | | 3 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,2,3,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 1,2,3 | + And the ways + | id | nodes | + | 1 | 1,2,3,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 4 | 9 | + + Scenario: addr:street on interpolation way + Given the grid + | | 1 | | 2 | | + | 10 | | | | 11 | + | 20 | | | | 21 | + And the places + | osm | class | type | housenr | geometry | + | N1 | place | house | 2 | 1 | + | N2 | place | house | 6 | 2 | + | N3 | place | house | 12 | 1 | + | N4 | place | house | 16 | 2 | + And the places + | osm | class | type | addr+interpolation | street | geometry | + | W10 | place | houses | even | | 1,2 | + | W11 | place | houses | even | Cloud Street | 1,2 | + And the places + | osm | class | type | name | geometry | + | W2 | highway | tertiary | Sun Way | 10,11 | + | W3 | highway | tertiary | Cloud Street | 20,21 | + And the ways + | id | nodes | + | 10 | 1,2 | + | 11 | 3,4 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + | N3 | W3 | + | N4 | W3 | + Then W10 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + Then W11 expands to interpolation + | parent_place_id | start | end | + | W3 | 14 | 14 | + When geocoding "16 Cloud Street" + Then result 0 contains + | object | + | N4 | + When geocoding "14 Cloud Street" + Then result 0 contains + | object | + | W11 | + + Scenario: addr:street on housenumber way + Given the grid + | | 1 | | 2 | | + | 10 | | | | 11 | + | 20 | | | | 21 | + And the places + | osm | class | type | housenr | street | geometry | + | N1 | place | house | 2 | | 1 | + | N2 | place | house | 6 | | 2 | + | N3 | place | house | 12 | Cloud Street | 1 | + | N4 | place | house | 16 | Cloud Street | 2 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W10 | place | houses | even | 1,2 | + | W11 | place | houses | even | 1,2 | + And the places + | osm | class | type | name | geometry | + | W2 | highway | tertiary | Sun Way | 10,11 | + | W3 | highway | tertiary | Cloud Street | 20,21 | + And the ways + | id | nodes | + | 10 | 1,2 | + | 11 | 3,4 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + | N3 | W3 | + | N4 | W3 | + Then W10 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + Then W11 expands to interpolation + | parent_place_id | start | end | + | W3 | 14 | 14 | + When geocoding "16 Cloud Street" + Then result 0 contains + | object | + | N4 | + When geocoding "14 Cloud Street" + Then result 0 contains + | object | + | W11 | + + Scenario: Geometry of points and way don't match (github #253) + Given the places + | osm | class | type | housenr | geometry | + | N1 | place | house | 10 | 144.9632341 -37.76163 | + | N2 | place | house | 6 | 144.9630541 -37.7628174 | + | N3 | shop | supermarket | 2 | 144.9629794 -37.7630755 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 144.9632341 -37.76163,144.9630541 -37.7628172,144.9629794 -37.7630755 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 144.9632341 -37.76163,144.9629794 -37.7630755 | + And the ways + | id | nodes | + | 1 | 1,2,3 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 4 | 144.96301672 -37.76294644 | + | 8 | 8 | 144.96314407 -37.762223692 | + + Scenario: Place with missing address information + Given the grid + | 1 | | 2 | | | 3 | + | 4 | | | | | 5 | + And the places + | osm | class | type | housenr | + | N1 | place | house | 23 | + | N2 | amenity | school | | + | N3 | place | house | 29 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | odd | 1,2,3 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,2,3 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 25 | 27 | 0.0000166 0,0.00002 0,0.0000333 0 | + + Scenario: Ways without node entries are ignored + Given the places + | osm | class | type | housenr | geometry | + | W1 | place | houses | even | 1 1, 1 1.001 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 1 1, 1 1.001 | + When importing + Then W1 expands to no interpolation + + Scenario: Ways with nodes without housenumbers are ignored + Given the grid + | 1 | | 2 | + | 4 | | 5 | + Given the places + | osm | class | type | + | N1 | place | house | + | N2 | place | house | + Given the places + | osm | class | type | housenr | geometry | + | W1 | place | houses | even | 1,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + When importing + Then W1 expands to no interpolation + + Scenario: Two point interpolation starting at 0 + Given the grid with origin 1,1 + | 1 | 10 | | | 11 | 2 | + | 4 | | | | | 5 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 0 | + | N2 | place | house | 10 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,2 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | residential | London Road |4,5 | + And the ways + | id | nodes | + | 1 | 1,2 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 2 | 8 | 10,11 | + When reverse geocoding 1,1 + Then the result contains + | object | type | display_name | + | N1 | house | 0, London Road | + + Scenario: Parenting of interpolation with additional tags + Given the grid + | 1 | | | | | | + | | | | | | | + | | 8 | | | 9 | | + | | | | | | | + | 2 | | | | | 3 | + Given the places + | osm | class | type | housenr | addr+street | + | N8 | place | house | 10 | Horiz St | + | N9 | place | house | 16 | Horiz St | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | Vert St | 1,2 | + | W2 | highway | residential | Horiz St | 2,3 | + And the places + | osm | class | type | addr+interpolation | addr+inclusion | geometry | + | W10 | place | houses | even | actual | 8,9 | + And the ways + | id | nodes | + | 10 | 8,9 | + When importing + Then placex contains + | object | parent_place_id | + | N8 | W2 | + | N9 | W2 | + And W10 expands to interpolation + | start | end | parent_place_id | + | 12 | 14 | W2 | + + + Scenario Outline: Bad interpolation values are ignored + Given the grid with origin 1,1 + | 1 | | 9 | | 2 | + | 4 | | | | 5 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | | 1,2 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,2 | + When importing + Then W1 expands to no interpolation + + Examples: + | value | + | foo | + | x | + | 12-2 | + + + Scenario: Interpolation line where points have been moved (Github #3022) + Given the 0.00001 grid + | 1 | | | | | | | | 2 | 3 | 9 | | | | | | | | 4 | + Given the places + | osm | class | type | housenr | geometry | + | N1 | place | house | 2 | 1 | + | N2 | place | house | 18 | 3 | + | N3 | place | house | 24 | 9 | + | N4 | place | house | 42 | 4 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,2,3,4 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 1,4 | + And the ways + | id | nodes | + | 1 | 1,2,3,4 | + When importing + Then W1 expands to interpolation + | start | end | + | 4 | 16 | + | 20 | 22 | + | 26 | 40 | + + + Scenario: Interpolation line with duplicated points + Given the grid + | 7 | 10 | 8 | 11 | 9 | + | 4 | | | | 5 | + Given the places + | osm | class | type | housenr | geometry | + | N1 | place | house | 2 | 7 | + | N2 | place | house | 6 | 8 | + | N3 | place | house | 10 | 8 | + | N4 | place | house | 14 | 9 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 7,8,8,9 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 4,5 | + And the ways + | id | nodes | + | 1 | 1,2,3,4 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 4 | 10 | + | 12 | 12 | 11 | + + + Scenario: Interpolaton line with broken way geometry (Github #2986) + Given the grid + | 1 | 8 | 10 | 11 | 9 | 2 | 3 | 4 | + Given the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 8 | + | N3 | place | house | 12 | + | N4 | place | house | 14 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 8,9 | + And the named places + | osm | class | type | geometry | + | W10 | highway | residential | 1,4 | + And the ways + | id | nodes | + | 1 | 1,8,9,2,3,4 | + When importing + Then W1 expands to interpolation + | start | end | geometry | + | 4 | 6 | 10,11 | diff --git a/test/bdd/features/db/import/linking.feature b/test/bdd/features/db/import/linking.feature new file mode 100644 index 00000000..22d5d48e --- /dev/null +++ b/test/bdd/features/db/import/linking.feature @@ -0,0 +1,332 @@ +Feature: Linking of places + Tests for correctly determining linked places + + Scenario: Only address-describing places can be linked + Given the grid + | 1 | | | | 2 | + | | | 9 | | | + | 4 | | | | 3 | + And the places + | osm | class | type | name | geometry | + | R13 | landuse | forest | Garbo | (1,2,3,4,1) | + | N256 | natural | peak | Garbo | 9 | + When importing + Then placex contains + | object | linked_place_id | + | R13 | - | + | N256 | - | + + Scenario: Postcode areas cannot be linked + Given the grid with origin US + | 1 | | 2 | + | | 9 | | + | 4 | | 3 | + And the named places + | osm | class | type | addr+postcode | extra+wikidata | geometry | + | R13 | boundary | postal_code | 12345 | Q87493 | (1,2,3,4,1) | + | N25 | place | suburb | 12345 | Q87493 | 9 | + When importing + Then placex contains + | object | linked_place_id | + | R13 | - | + | N25 | - | + + Scenario: Waterways are linked when in waterway relations + Given the grid + | 1 | | | | 3 | 4 | | | | 6 | + | | | 2 | | | 10 | | 5 | | | + | | | | | | 11 | | | | | + And the places + | osm | class | type | name | geometry | + | W1 | waterway | river | Rhein | 1,2,3 | + | W2 | waterway | river | Rhein | 3,4,5 | + | R13 | waterway | river | Rhein | 1,2,3,4,5,6 | + | R23 | waterway | river | Limmat| 4,10,11 | + And the relations + | id | members | tags+type | + | 13 | R23:tributary,W1,W2:main_stream | waterway | + When importing + Then placex contains + | object | linked_place_id | + | W1 | R13 | + | W2 | R13 | + | R13 | - | + | R23 | - | + When geocoding "rhein" + Then the result set contains + | object | + | R13 | + + Scenario: Relations are not linked when in waterway relations + Given the grid + | 1 | | | | 3 | 4 | | | | 6 | + | | | 2 | | | 10 | | 5 | | | + | | | | | | 11 | | | | | + And the places + | osm | class | type | name | geometry | + | W1 | waterway | stream | Rhein | 1,2,3,4 | + | W2 | waterway | river | Rhein | 4,5,6 | + | R1 | waterway | river | Rhein | 1,2,3,4 | + | R2 | waterway | river | Limmat| 4,10,11 | + And the relations + | id | members | tags+type | + | 1 | R2 | waterway | + When importing + Then placex contains + | object | linked_place_id | + | W1 | - | + | W2 | - | + | R1 | - | + | R2 | - | + When geocoding "rhein" + Then result 0 contains + | object | + | R1 | + And result 1 contains + | object | + | W2 | + + + Scenario: Empty waterway relations are handled correctly + Given the grid + | 1 | | | | 3 | + And the places + | osm | class | type | name | geometry | + | R1 | waterway | river | Rhein | 1,3 | + And the relations + | id | members | tags+type | + | 1 | | waterway | + When importing + Then placex contains + | object | linked_place_id | + | R1 | - | + + Scenario: Waterways are not linked when the way type is not a river feature + Given the grid + | 1 | | 2 | + | | | | + | 3 | | 4 | + And the places + | osm | class | type | name | geometry | + | W1 | waterway | lock | Rhein | 3,4 | + | R1 | landuse | meadow | Rhein | (3,1,2,4,3) | + And the relations + | id | members | tags+type | + | 1 | W1,W2 | multipolygon | + When importing + Then placex contains + | object | linked_place_id | + | W1 | - | + | R1 | - | + + Scenario: Side streams are linked only when they have the same name + Given the grid + | | | | | 8 | | | | + | 1 | | 2 | 3 | | 4 | 5 | 6| + | | | | | | 9 | | | + And the places + | osm | class | type | name | geometry | + | W1 | waterway | river | Rhein2 | 2,8,4 | + | W2 | waterway | river | Rhein | 3,9,5 | + | R1 | waterway | river | Rhein | 1,2,3,4,5,6 | + And the relations + | id | members | tags+type | + | 1 | W1:side_stream,W2:side_stream,W3 | waterway | + When importing + Then placex contains + | object | linked_place_id | + | W1 | - | + | W2 | R1 | + When geocoding "rhein2" + Then the result set contains + | object | + | W1 | + + # github #573 + Scenario: Boundaries should only be linked to places + Given the 0.05 grid + | 1 | | 2 | + | | 9 | | + | 4 | | 3 | + Given the named places + | osm | class | type | extra+wikidata | admin | geometry | + | R1 | boundary | administrative | 34 | 8 | (1,2,3,4,1) | + And the named places + | osm | class | type | + | N9 | natural | island | + | N9 | place | city | + And the relations + | id | members | + | 1 | N9:label | + When importing + Then placex contains + | object | linked_place_id | + | N9:natural | - | + | N9:place | R1 | + + Scenario: Nodes with 'role' label are always linked + Given the 0.05 grid + | 1 | | 2 | + | | 9 | | + | 4 | | 3 | + Given the places + | osm | class | type | admin | name | geometry | + | R13 | boundary | administrative | 6 | Garbo | (1,2,3,4,1) | + | N2 | place | hamlet | 15 | Vario | 9 | + And the relations + | id | members | tags+type | + | 13 | N2:label | boundary | + When importing + Then placex contains + | object | linked_place_id | + | N2 | R13 | + And placex contains + | object | centroid!wkt | name+name | extratags+linked_place | + | R13 | 9 | Garbo | hamlet | + + Scenario: Boundaries with place tags are linked against places with same type + Given the 0.01 grid + | 1 | | 2 | + | | 9 | | + | 4 | | 3 | + Given the places + | osm | class | type | admin | name | extra+place | geometry | + | R13 | boundary | administrative | 4 | Berlin | city | (1,2,3,4,1) | + And the places + | osm | class | type | name | geometry | + | N2 | place | city | Berlin | 9 | + When importing + Then placex contains + | object | linked_place_id | + | N2 | R13 | + And placex contains + | object | rank_address | + | R13 | 16 | + When geocoding "" + | city | + | Berlin | + Then result 0 contains + | object | + | R13 | + When geocoding "" + | state | + | Berlin | + Then result 0 contains + | object | + | R13 | + + + Scenario: Boundaries without place tags only link against same admin level + Given the 0.05 grid + | 1 | | 2 | + | | 9 | | + | 4 | | 3 | + Given the places + | osm | class | type | admin | name | geometry | + | R13 | boundary | administrative | 4 | Berlin | (1,2,3,4,1) | + And the places + | osm | class | type | name | geometry | + | N2 | place | city | Berlin | 9 | + When importing + Then placex contains + | object | linked_place_id | + | N2 | - | + And placex contains + | object | rank_address | + | R13 | 8 | + When geocoding "" + | state | + | Berlin | + Then result 0 contains + | object | + | R13 | + When geocoding "" + | city | + | Berlin | + Then result 0 contains + | object | + | N2 | + + # github #1352 + Scenario: Do not use linked centroid when it is outside the area + Given the 0.05 grid + | 1 | | 2 | | + | | | | 9 | + | 4 | | 3 | | + Given the named places + | osm | class | type | admin | geometry | + | R13 | boundary | administrative | 4 | (1,2,3,4,1) | + And the named places + | osm | class | type | geometry | + | N2 | place | city | 9 | + And the relations + | id | members | tags+type | + | 13 | N2:label | boundary | + When importing + Then placex contains + | object | linked_place_id | + | N2 | R13 | + And placex contains + | object | centroid!in_box | + | R13 | 0,0,0.1,0.1 | + + Scenario: Place nodes can only be linked once + Given the 0.02 grid + | 1 | | 2 | | 5 | + | | 9 | | | | + | 4 | | 3 | | 6 | + Given the named places + | osm | class | type | extra+wikidata | geometry | + | N2 | place | city | Q1234 | 9 | + And the named places + | osm | class | type | extra+wikidata | admin | geometry | + | R1 | boundary | administrative | Q1234 | 8 | (1,2,5,6,3,4,1) | + | R2 | boundary | administrative | Q1234 | 9 | (1,2,3,4,1) | + When importing + Then placex contains + | object | linked_place_id | + | N2 | R1 | + And placex contains + | object | extratags!dict | + | R1 | 'linked_place' : 'city', 'wikidata': 'Q1234' | + | R2 | 'wikidata': 'Q1234' | + + + Scenario: Boundaries without names inherit names from linked places + Given the 0.05 grid + | 1 | | 2 | + | | 9 | | + | 4 | | 3 | + Given the places + | osm | class | type | extra+wikidata | admin | geometry | + | R1 | boundary | administrative | 34 | 8 | (1,2,3,4,1) | + And the places + | osm | class | type | name+name | + | N9 | place | city | LabelPlace | + And the relations + | id | members | + | 1 | N9:label | + When importing + Then placex contains + | object | name+_place_name | + | R1 | LabelPlace | + + + @skip + Scenario: Linked places expand default language names + Given the grid + | 1 | | 2 | + | | 9 | | + | 4 | | 3 | + Given the places + | osm | class | type | name+name | geometry | + | N9 | place | city | Popayán | 9 | + | R1 | boundary | administrative | Perímetro Urbano Popayán | (1,2,3,4,1) | + And the relations + | id | members | + | 1 | N9:label | + When importing + Then placex contains + | object | name+_place_name | name+_place_name:es | + | R1 | Popayán | Popayán | + diff --git a/test/bdd/features/db/import/naming.feature b/test/bdd/features/db/import/naming.feature new file mode 100644 index 00000000..944c2de7 --- /dev/null +++ b/test/bdd/features/db/import/naming.feature @@ -0,0 +1,104 @@ +Feature: Import and search of names + Tests all naming related import issues + + Scenario: No copying name tag if only one name + Given the places + | osm | class | type | name+name | geometry | + | N1 | place | locality | german | country:de | + When importing + Then placex contains + | object | country_code | name+name | + | N1 | de | german | + + Scenario: Copying name tag to default language if it does not exist + Given the places + | osm | class | type | name+name | name+name:fi | geometry | + | N1 | place | locality | german | finnish | country:de | + When importing + Then placex contains + | object | country_code | name+name | name+name:fi | name+name:de | + | N1 | de | german | finnish | german | + + Scenario: Copying default language name tag to name if it does not exist + Given the places + | osm | class | type | name+name:de | name+name:fi | geometry | + | N1 | place | locality | german | finnish | country:de | + When importing + Then placex contains + | object | country_code | name+name | name+name:fi | name+name:de | + | N1 | de | german | finnish | german | + + Scenario: Do not overwrite default language with name tag + Given the places + | osm | class | type | name+name | name+name:fi | name+name:de | geometry | + | N1 | place | locality | german | finnish | local | country:de | + When importing + Then placex contains + | object | country_code | name+name | name+name:fi | name+name:de | + | N1 | de | german | finnish | local | + + Scenario Outline: Names in any script can be found + Given the places + | osm | class | type | name+name | + | N1 | place | hamlet | | + When importing + And geocoding "" + Then the result set contains + | object | + | N1 | + + Examples: + | name | + | Berlin | + | 北京 | + | Вологда | + | Αθήνα | + | القاهرة | + | រាជធានីភ្នំពេញ | + | 東京都 | + | ပုဗ္ဗသီရိ | + + + Scenario: German umlauts can be found when expanded + Given the places + | osm | class | type | name+name:de | + | N1 | place | city | Münster | + | N2 | place | city | Köln | + | N3 | place | city | Gräfenroda | + When importing + When geocoding "münster" + Then the result set contains + | object | + | N1 | + When geocoding "muenster" + Then the result set contains + | object | + | N1 | + When geocoding "munster" + Then the result set contains + | object | + | N1 | + When geocoding "Köln" + Then the result set contains + | object | + | N2 | + When geocoding "Koeln" + Then the result set contains + | object | + | N2 | + When geocoding "Koln" + Then the result set contains + | object | + | N2 | + When geocoding "gräfenroda" + Then the result set contains + | object | + | N3 | + When geocoding "graefenroda" + Then the result set contains + | object | + | N3 | + When geocoding "grafenroda" + Then the result set contains + | object | + | N3 | diff --git a/test/bdd/features/db/import/parenting.feature b/test/bdd/features/db/import/parenting.feature new file mode 100644 index 00000000..2cd09a8d --- /dev/null +++ b/test/bdd/features/db/import/parenting.feature @@ -0,0 +1,651 @@ +Feature: Parenting of objects + Tests that the correct parent is chosen + + Scenario: Address inherits postcode from its street unless it has a postcode + Given the grid with origin DE + | 10 | | | | | 11 | + | | | | | | | + | | 1 | | 2 | | | + And the places + | osm | class | type | housenr | + | N1 | place | house | 4 | + And the places + | osm | class | type | housenr | postcode | + | N2 | place | house | 5 | 99999 | + And the places + | osm | class | type | name | postcode | geometry | + | W1 | highway | residential | galoo | 12345 | 10,11 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + | N2 | W1 | + When geocoding "4 galoo" + Then result 0 contains + | object | display_name | + | N1 | 4, galoo, 12345, Deutschland | + When geocoding "5 galoo" + Then result 0 contains + | object | display_name | + | N2 | 5, galoo, 99999, Deutschland | + + Scenario: Address without tags, closest street + Given the grid + | 10 | | | | | 11 | + | | 1 | 2 | | | | + | | | | 3 | 4 | | + | 20 | | | | | 21 | + And the places + | osm | class | type | + | N1 | place | house | + | N2 | place | house | + | N3 | place | house | + | N4 | place | house | + And the named places + | osm | class | type | geometry | + | W1 | highway | residential | 10,11 | + | W2 | highway | residential | 20,21 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + | N2 | W1 | + | N3 | W2 | + | N4 | W2 | + + Scenario: Address without tags avoids unnamed streets + Given the grid + | 10 | | | | | 11 | + | | 1 | 2 | | | | + | | | | 3 | 4 | | + | 20 | | | | | 21 | + And the places + | osm | class | type | + | N1 | place | house | + | N2 | place | house | + | N3 | place | house | + | N4 | place | house | + And the places + | osm | class | type | geometry | + | W1 | highway | residential | 10,11 | + And the named places + | osm | class | type | geometry | + | W2 | highway | residential | 20,21 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + | N3 | W2 | + | N4 | W2 | + + Scenario: addr:street tag parents to appropriately named street + Given the grid + | 10 | | | | | 11 | + | | 1 | 2 | | | | + | | | | 3 | 4 | | + | 20 | | | | | 21 | + And the places + | osm | class | type | street| + | N1 | place | house | south | + | N2 | place | house | north | + | N3 | place | house | south | + | N4 | place | house | north | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | north | 10,11 | + | W2 | highway | residential | south | 20,21 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W1 | + | N3 | W2 | + | N4 | W1 | + + Scenario: addr:street tag parents to appropriately named street, locale names + Given the grid + | 10 | | | | | 11 | + | | 1 | 2 | | | | + | | | | 3 | 4 | | + | 20 | | | | | 21 | + And the places + | osm | class | type | street| addr+street:de | + | N1 | place | house | south | Süd | + | N2 | place | house | north | Nord | + | N3 | place | house | south | Süd | + | N4 | place | house | north | Nord | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | Nord | 10,11 | + | W2 | highway | residential | Süd | 20,21 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W1 | + | N3 | W2 | + | N4 | W1 | + + Scenario: addr:street tag parents to appropriately named street with abbreviation + Given the grid + | 10 | | | | | 11 | + | | 1 | 2 | | | | + | | | | 3 | 4 | | + | 20 | | | | | 21 | + And the places + | osm | class | type | street | + | N1 | place | house | south st | + | N2 | place | house | north st | + | N3 | place | house | south st | + | N4 | place | house | north st | + And the places + | osm | class | type | name+name:en | geometry | + | W1 | highway | residential | north street | 10,11 | + | W2 | highway | residential | south street | 20,21 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W1 | + | N3 | W2 | + | N4 | W1 | + + Scenario: addr:street tag parents to next named street + Given the grid + | 10 | | | | | 11 | + | | 1 | 2 | | | | + | | | | 3 | 4 | | + | 20 | | | | | 21 | + And the places + | osm | class | type | street | + | N1 | place | house | abcdef | + | N2 | place | house | abcdef | + | N3 | place | house | abcdef | + | N4 | place | house | abcdef | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | abcdef | 10,11 | + | W2 | highway | residential | abcdef | 20,21 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + | N2 | W1 | + | N3 | W2 | + | N4 | W2 | + + Scenario: addr:street tag without appropriately named street + Given the grid + | 10 | | | | | 11 | + | | 1 | | | | | + | | | | 3 | | | + | 20 | | | | | 21 | + And the places + | osm | class | type | street | + | N1 | place | house | abcdef | + | N3 | place | house | abcdef | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | abcde | 10,11 | + | W2 | highway | residential | abcde | 20,21 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + | N3 | W2 | + + Scenario: addr:place address + Given the grid + | 10 | | | | + | | 1 | | 2 | + | 11 | | | | + And the places + | osm | class | type | addr_place | + | N1 | place | house | myhamlet | + And the places + | osm | class | type | name | geometry | + | N2 | place | hamlet | myhamlet | 2 | + | W1 | highway | residential | myhamlet | 10,11 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | N2 | + + Scenario: addr:street is preferred over addr:place + Given the grid + | 10 | | | | + | | | 1 | 2 | + | 11 | | | | + And the places + | osm | class | type | addr_place | street | + | N1 | place | house | myhamlet | mystreet| + And the places + | osm | class | type | name | geometry | + | N2 | place | hamlet | myhamlet | 2 | + | W1 | highway | residential | mystreet | 10,11 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + + Scenario: Untagged address in simple associated street relation + Given the grid + | 10 | | | | | 11 | + | | 2 | | 3 | | | + | | | | | | | + | 12 | 1 | | | | | + And the places + | osm | class | type | + | N1 | place | house | + | N2 | place | house | + | N3 | place | house | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | foo | 10,11 | + | W2 | highway | service | bar | 10,12 | + And the relations + | id | members | tags+type | + | 1 | W1:street,N1,N2,N3 | associatedStreet | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + | N2 | W1 | + | N3 | W1 | + + Scenario: Avoid unnamed streets in simple associated street relation + Given the grid + | 10 | | | | | 11 | + | | 2 | | 3 | | | + | | | | | | | + | 12 | 1 | | | | | + And the places + | osm | class | type | + | N1 | place | house | + | N2 | place | house | + | N3 | place | house | + And the places + | osm | class | type | geometry | + | W2 | highway | residential | 10,12 | + And the named places + | osm | class | type | geometry | + | W1 | highway | residential | 10,11 | + And the relations + | id | members | tags+type | + | 1 | N1,N2,N3,W2:street,W1:street | associatedStreet | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + | N2 | W1 | + | N3 | W1 | + + Scenario: Associated street relation overrides addr:street + Given the grid + | 10 | | | | 11 | + | | | | | | + | | | 1 | | | + | | 20 | | 21 | | + And the places + | osm | class | type | street | + | N1 | place | house | bar | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | foo | 10,11 | + | W2 | highway | residential | bar | 20,21 | + And the relations + | id | members | tags+type | + | 1 | W1:street,N1 | associatedStreet | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + + Scenario: Building without tags, closest street from center point + Given the grid + | 10 | | | | 11 | + | | | 1 | 2 | | + | 12 | | 4 | 3 | | + And the named places + | osm | class | type | geometry | + | W1 | building | yes | (1,2,3,4,1) | + | W2 | highway | primary | 10,11 | + | W3 | highway | residential | 10,12 | + When importing + Then placex contains + | object | parent_place_id | + | W1 | W2 | + + Scenario: Building with addr:street tags + Given the grid + | 10 | | | | 11 | + | | | 1 | 2 | | + | 12 | | 4 | 3 | | + And the named places + | osm | class | type | street | geometry | + | W1 | building | yes | foo | (1,2,3,4,1) | + And the places + | osm | class | type | name | geometry | + | W2 | highway | primary | bar | 10,11 | + | W3 | highway | residential | foo | 10,12 | + When importing + Then placex contains + | object | parent_place_id | + | W1 | W3 | + + Scenario: Building with addr:place tags + Given the grid + | 10 | | | | | + | | 1 | 2 | | 9 | + | 11 | 4 | 3 | | | + And the places + | osm | class | type | name | geometry | + | N9 | place | village | bar | 9 | + | W2 | highway | primary | bar | 10,11 | + And the named places + | osm | class | type | addr_place | geometry | + | W1 | building | yes | bar | (1,2,3,4,1) | + When importing + Then placex contains + | object | parent_place_id | + | W1 | N9 | + + Scenario: Building in associated street relation + Given the grid + | 10 | | | | 11 | + | | | 1 | 2 | | + | 12 | | 4 | 3 | | + And the named places + | osm | class | type | geometry | + | W1 | building | yes | (1,2,3,4,1) | + And the places + | osm | class | type | name | geometry | + | W2 | highway | primary | bar | 10,11 | + | W3 | highway | residential | foo | 10,12 | + And the relations + | id | members | tags+type | + | 1 | W1:house,W3:street | associatedStreet | + When importing + Then placex contains + | object | parent_place_id | + | W1 | W3 | + + Scenario: Building in associated street relation overrides addr:street + Given the grid + | 10 | | | | 11 | + | | | 1 | 2 | | + | 12 | | 4 | 3 | | + And the named places + | osm | class | type | street | geometry | + | W1 | building | yes | foo | (1,2,3,4,1) | + And the places + | osm | class | type | name | geometry | + | W2 | highway | primary | bar | 10,11 | + | W3 | highway | residential | foo | 10,12 | + And the relations + | id | members | tags+type | + | 1 | W1:house,W2:street | associatedStreet | + When importing + Then placex contains + | object | parent_place_id | + | W1 | W2 | + + Scenario: Wrong member in associated street relation is ignored + Given the grid + | 10 | | | | | | | 11 | + | | 1 | | 3 | 4 | | | | + | | | | 6 | 5 | | | | + And the named places + | osm | class | type | geometry | + | N1 | place | house | 11 | + And the named places + | osm | class | type | street | geometry | + | W1 | building | yes | foo | (3,4,5,6,3) | + And the places + | osm | class | type | name | geometry | + | W3 | highway | residential | foo | 10,11 | + And the relations + | id | members | tags+type | + | 1 | N1:house,W1:street,W3:street | associatedStreet | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W3 | + + Scenario: street member in associatedStreet relation can be a relation + Given the grid + | 1 | | | 2 | + | 3 | | | 4 | + | | | | | + | | 9 | | | + | 5 | | | 6 | + And the places + | osm | class | type | housenr | geometry | + | N9 | place | house | 34 | 9 | + And the named places + | osm | class | type | name | geometry | + | R14 | highway | pedestrian | Right St | (1,2,4,3,1) | + | W14 | highway | pedestrian | Left St | 5,6 | + And the relations + | id | members | tags+type | + | 1 | N9:house,R14:street | associatedStreet | + When importing + Then placex contains + | object | parent_place_id | + | N9 | R14 | + + + Scenario: Choose closest street in associatedStreet relation + Given the grid + | 1 | | | | 3 | + | 10 | | 11 | | 12 | + And the places + | osm | class | type | housenr | geometry | + | N1 | place | house | 1 | 1 | + | N3 | place | house | 3 | 3 | + And the named places + | osm | class | type | geometry | + | W100 | highway | residential | 10,11 | + | W101 | highway | residential | 11,12 | + And the relations + | id | members | tags+type | + | 1 | N1:house,N3:house,W100:street,W101:street | associatedStreet | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W100 | + | N3 | W101 | + + + Scenario: POIs in building inherit address + Given the grid + | 10 | | | | | | 11 | + | | | 5 | 2 | 6 | | | + | | | 3 | 1 | | | | + | 12 | | 8 | | 7 | | | + And the named places + | osm | class | type | + | N1 | amenity | bank | + | N2 | shop | bakery | + | N3 | shop | supermarket| + And the places + | osm | class | type | street | housenr | geometry | + | W1 | building | yes | foo | 3 | (5,6,7,8,5) | + And the places + | osm | class | type | name | geometry | + | W2 | highway | primary | bar | 10,11 | + | W3 | highway | residential | foo | 10,12 | + When importing + Then placex contains + | object | parent_place_id | housenumber | + | W1 | W3 | 3 | + | N1 | W3 | 3 | + | N2 | W3 | 3 | + | N3 | W3 | 3 | + When geocoding "3, foo" + Then the result set contains + | address+house_number | + | 3 | + + Scenario: POIs don't inherit from streets + Given the grid + | 10 | | | | 11 | + | | 5 | 1 | 6 | | + | | 8 | | 7 | | + And the named places + | osm | class | type | + | N1 | amenity | bank | + And the places + | osm | class | type | name | street | housenr | geometry | + | W1 | highway | path | bar | foo | 3 | (5,6,7,8,5) | + And the places + | osm | class | type | name | geometry | + | W3 | highway | residential | foo | 10,11 | + When importing + Then placex contains + | object | parent_place_id | housenumber | + | N1 | W1 | None | + + Scenario: POIs with own address do not inherit building address + Given the grid + | 10 | | | | | | 11 | + | | | 6 | 2 | 7 | | | + | | | 3 | 1 | | 5 | 4 | + | 12 | | 9 | | 8 | | | + And the named places + | osm | class | type | street | + | N1 | amenity | bank | bar | + And the named places + | osm | class | type | housenr | + | N2 | shop | bakery | 4 | + And the named places + | osm | class | type | addr_place | + | N3 | shop | supermarket| nowhere | + And the places + | osm | class | type | name | + | N4 | place | isolated_dwelling | theplace | + | N5 | place | isolated_dwelling | nowhere | + And the places + | osm | class | type | addr_place | housenr | geometry | + | W1 | building | yes | theplace | 3 | (6,7,8,9,6) | + And the places + | osm | class | type | name | geometry | + | W2 | highway | primary | bar | 10,11 | + | W3 | highway | residential | foo | 10,12 | + When importing + Then placex contains + | object | parent_place_id | housenumber | + | W1 | N4 | 3 | + | N1 | W2 | None | + | N2 | W2 | 4 | + | N3 | N5 | None | + + Scenario: POIs parent a road if they are attached to it + Given the grid + | | 10 | | + | 20 | 1 | 21 | + | | 11 | | + And the named places + | osm | class | type | + | N1 | highway | bus_stop | + And the places + | osm | class | type | name | geometry | + | W1 | highway | secondary | North St | 10,11 | + | W2 | highway | unclassified | South St | 20,1,21 | + And the ways + | id | nodes | + | 1 | 10,11 | + | 2 | 20,1,21 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + + Scenario: POIs do not parent non-roads they are attached to + Given the grid + | 10 | | 1 | | 11 | | 30 | + | 14 | | | | 15 | | | + | 13 | | 2 | | 12 | | 31 | + And the named places + | osm | class | type | street | + | N1 | highway | bus_stop | North St | + | N2 | highway | bus_stop | South St | + And the places + | osm | class | type | name | geometry | + | W1 | landuse | residential | North St | (14,15,12,2,13,14) | + | W2 | waterway| river | South St | 10,1,11 | + | W3 | highway | residential | foo | 30,31 | + And the ways + | id | nodes | + | 1 | 10,11,12,2,13,10 | + | 2 | 10,1,11 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W3 | + | N2 | W3 | + + Scenario: POIs on building outlines inherit associated street relation + Given the grid + | 10 | | | | 11 | + | | 5 | 1 | 6 | | + | 12 | 8 | | 7 | | + And the named places + | osm | class | type | geometry | + | N1 | place | house | 1 | + | W1 | building | yes | (5,1,6,7,8,5)| + And the places + | osm | class | type | name | geometry | + | W2 | highway | primary | bar | 10,11 | + | W3 | highway | residential | foo | 10,12 | + And the relations + | id | members | tags+type | + | 1 | W1:house,W3:street | associatedStreet | + And the ways + | id | nodes | + | 1 | 5,1,6,7,8,5 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W3 | + + # github #1056 + Scenario: Full names should be preferably matched for nearest road + Given the grid + | 1 | | 2 | 5 | + | | | | | + | 3 | | | 4 | + | | 10| | | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Via Cavassico superiore | 1, 2 | + | W3 | highway | residential | Via Cavassico superiore | 2, 5 | + | W2 | highway | primary | Via Frazione Cavassico | 3, 4 | + And the named places + | osm | class | type | addr+street | + | N10 | shop | yes | Via Cavassico superiore | + When importing + Then placex contains + | object | parent_place_id | + | N10 | W1 | + + Scenario: place=square may be parented via addr:place + Given the grid + | | | 9 | | | + | | 5 | | 6 | | + | | 8 | | 7 | | + And the places + | osm | class | type | name+name | geometry | + | W2 | place | square | Foo pl | (5, 6, 7, 8, 5) | + And the places + | osm | class | type | name+name | housenr | addr_place | geometry | + | N10 | shop | grocery | le shop | 5 | Foo pl | 9 | + When importing + Then placex contains + | object | rank_address | + | W2 | 25 | + Then placex contains + | object | parent_place_id | + | N10 | W2 | + diff --git a/test/bdd/features/db/import/placex.feature b/test/bdd/features/db/import/placex.feature new file mode 100644 index 00000000..8c1e4a8a --- /dev/null +++ b/test/bdd/features/db/import/placex.feature @@ -0,0 +1,193 @@ +Feature: Import into placex + Tests that data in placex is completed correctly. + + Scenario: No country code tag is available + Given the named places + | osm | class | type | geometry | + | N1 | highway | primary | country:us | + When importing + Then placex contains + | object | address | country_code | + | N1 | - | us | + + Scenario: Location overwrites country code tag + Given the named places + | osm | class | type | country | geometry | + | N1 | highway | primary | de | country:us | + When importing + Then placex contains + | object | addr+country | country_code | + | N1 | de | us | + + Scenario: Country code tag overwrites location for countries + Given the named places + | osm | class | type | admin | country | geometry | + | R1 | boundary | administrative | 2 | de | (-100 40, -101 40, -101 41, -100 41, -100 40) | + When importing + Then placex contains + | object | rank_search| addr+country | country_code | + | R1 | 4 | de | de | + + Scenario: Illegal country code tag for countries is ignored + Given the named places + | osm | class | type | admin | country | geometry | + | R1 | boundary | administrative | 2 | xx | (-100 40, -101 40, -101 41, -100 41, -100 40) | + When importing + Then placex contains + | object | addr+country | country_code | + | R1 | xx | us | + + Scenario: admin level is copied over + Given the named places + | osm | class | type | admin | + | N1 | place | state | 3 | + When importing + Then placex contains + | object | admin_level | + | N1 | 3 | + + Scenario: postcode node without postcode is dropped + Given the places + | osm | class | type | name+ref | + | N1 | place | postcode | 12334 | + When importing + Then placex has no entry for N1 + + Scenario: postcode boundary without postcode is dropped + Given the 0.01 grid + | 1 | 2 | + | 3 | | + Given the places + | osm | class | type | name+ref | geometry | + | R1 | boundary | postal_code | 554476 | (1,2,3,1) | + When importing + Then placex has no entry for R1 + + Scenario: search and address ranks for boundaries are correctly assigned + Given the named places + | osm | class | type | + | N1 | boundary | administrative | + And the named places + | osm | class | type | geometry | + | W10 | boundary | administrative | 10 10, 11 11 | + And the named places + | osm | class | type | admin | geometry | + | R20 | boundary | administrative | 2 | (1 1, 2 2, 1 2, 1 1) | + | R21 | boundary | administrative | 32 | (3 3, 4 4, 3 4, 3 3) | + | R22 | boundary | nature_park | 6 | (0 0, 1 0, 0 1, 0 0) | + | R23 | boundary | natural_reserve| 10 | (0 0, 1 1, 1 0, 0 0) | + And the named places + | osm | class | type | geometry | + | R40 | place | country | (1 1, 2 2, 1 2, 1 1) | + | R41 | place | state | (3 3, 4 4, 3 4, 3 3) | + When importing + Then placex has no entry for N1 + And placex has no entry for W10 + And placex contains + | object | rank_search | rank_address | + | R20 | 4 | 4 | + | R21 | 25 | 0 | + | R22 | 25 | 0 | + | R23 | 25 | 0 | + | R40 | 4 | 0 | + | R41 | 8 | 0 | + + Scenario: search and address ranks for highways correctly assigned + Given the grid + | 10 | 1 | 11 | | 12 | | 13 | | 14 | | 15 | | 16 | + And the places + | osm | class | type | + | N1 | highway | bus_stop | + And the places + | osm | class | type | geometry | + | W1 | highway | primary | 10,11 | + | W2 | highway | secondary | 11,12 | + | W3 | highway | tertiary | 12,13 | + | W4 | highway | residential | 13,14 | + | W5 | highway | unclassified | 14,15 | + | W6 | highway | something | 15,16 | + When importing + Then placex contains + | object | rank_search | rank_address | + | N1 | 30 | 30 | + | W1 | 26 | 26 | + | W2 | 26 | 26 | + | W3 | 26 | 26 | + | W4 | 26 | 26 | + | W5 | 26 | 26 | + | W6 | 30 | 30 | + + Scenario: rank and inclusion of landuses + Given the 0.4 grid + | 1 | 2 | | | | | | 5 | + | 4 | 3 | | | | | | 6 | + Given the named places + | osm | class | type | + | N2 | landuse | residential | + And the named places + | osm | class | type | geometry | + | W2 | landuse | residential | 1,2,5 | + | W4 | landuse | residential | (1,4,3,1) | + | R2 | landuse | residential | (1,2,3,4,1) | + | R3 | landuse | forrest | (1,5,6,4,1) | + When importing + Then placex contains + | object | rank_search | rank_address | + | N2 | 30 | 30 | + | W2 | 30 | 30 | + | W4 | 22 | 22 | + | R2 | 22 | 22 | + | R3 | 22 | 0 | + + Scenario: rank and inclusion of naturals + Given the 0.4 grid + | 1 | 2 | | | | | | 5 | + | 4 | 3 | | | | | | 6 | + Given the named places + | osm | class | type | + | N2 | natural | peak | + | N4 | natural | volcano | + | N5 | natural | foobar | + And the named places + | osm | class | type | geometry | + | W2 | natural | mountain_range | 1,2,5 | + | W3 | natural | foobar | 2,3 | + | R3 | natural | volcano | (1,2,4,1) | + | R4 | natural | foobar | (1,2,3,4,1) | + | R5 | natural | sea | (1,2,5,6,3,4,1) | + | R6 | natural | sea | (2,3,4,2) | + When importing + Then placex contains + | object | rank_search | rank_address | + | N2 | 18 | 0 | + | N4 | 18 | 0 | + | N5 | 22 | 0 | + | W2 | 18 | 0 | + | R3 | 18 | 0 | + | R4 | 22 | 0 | + | R5 | 4 | 0 | + | R6 | 4 | 0 | + | W3 | 22 | 0 | + + Scenario: boundary ways for countries and states are ignored + Given the 0.3 grid + | 1 | 2 | + | 4 | 3 | + Given the named places + | osm | class | type | admin | geometry | + | W4 | boundary | administrative | 2 | (1,2,3,4,1) | + | R4 | boundary | administrative | 2 | (1,2,3,4,1) | + | W5 | boundary | administrative | 3 | (1,2,3,4,1) | + | R5 | boundary | administrative | 3 | (1,2,3,4,1) | + | W6 | boundary | administrative | 4 | (1,2,3,4,1) | + | R6 | boundary | administrative | 4 | (1,2,3,4,1) | + | W7 | boundary | administrative | 5 | (1,2,3,4,1) | + | R7 | boundary | administrative | 5 | (1,2,3,4,1) | + When importing + Then placex contains exactly + | object | + | R4 | + | R5 | + | R6 | + | W7 | + | R7 | diff --git a/test/bdd/features/db/import/postcodes.feature b/test/bdd/features/db/import/postcodes.feature new file mode 100644 index 00000000..7f69b1e1 --- /dev/null +++ b/test/bdd/features/db/import/postcodes.feature @@ -0,0 +1,208 @@ +Feature: Import of postcodes + Tests for postcode estimation + + Scenario: Postcodes on the object are preferred over those on the address + Given the grid with origin FR + | 1 | | | | 4 | | 6 | | 8 | + | | 10 | | 11 | | | | | | + | | | 22 | | | | | | | + | 2 | | | | 3 | | 5 | | 7 | + And the named places + | osm | class | type | admin | addr+postcode | geometry | + | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | + | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | + | R4 | boundary | administrative | 10 | 11200 | (1,4,3,2,1) | + And the named places + | osm | class | type | addr+postcode | geometry | + | W93 | highway | residential | 11250 | 10,11 | + | N22 | building | yes | 11254 | 22 | + When importing + Then placex contains + | object | postcode | + | N22 | 11254 | + | W93 | 11250 | + | R4 | 11200 | + | R34 | 11000 | + | R1 | 10000 | + + Scenario: Postcodes from a road are inherited by an attached building + Given the grid with origin DE + | 10 | | | | 11 | + | | 1 | 2 | | | + | | 4 | 3 | | | + And the named places + | osm | class | type | addr+postcode | geometry | + | W93 | highway | residential | 86034 | 10,11 | + And the named places + | osm | class | type | geometry | + | W22 | building | yes | (1,2,3,4,1) | + When importing + Then placex contains + | object | postcode | parent_place_id | + | W22 | 86034 | W93 | + + Scenario: Postcodes from the lowest admin area are inherited by ways + Given the grid with origin FR + | 1 | | | | 4 | | 6 | | 8 | + | | 10 | | 11 | | | | | | + | 2 | | | | 3 | | 5 | | 7 | + And the named places + | osm | class | type | admin | addr+postcode | geometry | + | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | + | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | + | R4 | boundary | administrative | 10 | 11200 | (1,4,3,2,1) | + And the named places + | osm | class | type | geometry | + | W93 | highway | residential | 10,11 | + When importing + Then placex contains + | object | postcode | + | W93 | 11200 | + + Scenario: Postcodes from the lowest admin area with postcode are inherited by ways + Given the grid with origin FR + | 1 | | | | 4 | | 6 | | 8 | + | | 10 | | 11 | | | | | | + | 2 | | | | 3 | | 5 | | 7 | + And the named places + | osm | class | type | admin | addr+postcode | geometry | + | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | + | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | + And the named places + | osm | class | type | admin | geometry | + | R4 | boundary | administrative | 10 | (1,4,3,2,1) | + And the named places + | osm | class | type | geometry | + | W93 | highway | residential | 10,11 | + When importing + Then placex contains + | object | postcode | parent_place_id | + | W93 | 11000 | R4 | + + Scenario: Postcodes from the lowest admin area are inherited by buildings + Given the grid with origin FR + | 1 | | | | 4 | | 6 | | 8 | + | | 10 | | 11 | | | | | | + | | 13 | | 12 | | | | | | + | 2 | | | | 3 | | 5 | | 7 | + And the named places + | osm | class | type | admin | addr+postcode | geometry | + | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | + | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | + | R4 | boundary | administrative | 10 | 11200 | (1,4,3,2,1) | + And the named places + | osm | class | type | geometry | + | W22 | building | yes | (10,11,12,13,10) | + When importing + Then placex contains + | object | postcode | + | W22 | 11200 | + + Scenario: Roads get postcodes from nearby named buildings without other info + Given the grid with origin US + | 10 | | | | 11 | + | | 1 | 2 | | | + | | 4 | 3 | | | + And the named places + | osm | class | type | geometry | + | W93 | highway | residential | 10,11 | + And the named places + | osm | class | type | addr+postcode | geometry | + | W22 | building | yes | 45023 | (1,2,3,4,1) | + When importing + Then placex contains + | object | postcode | + | W93 | 45023 | + + Scenario: Road areas get postcodes from nearby named buildings without other info + Given the grid with origin US + | 10 | | | | 11 | + | 13 | | | | 12 | + | | 1 | 2 | | | + | | 4 | 3 | | | + And the named places + | osm | class | type | geometry | + | W93 | highway | pedestriant | (10,11,12,13,10) | + And the named places + | osm | class | type | addr+postcode | geometry | + | W22 | building | yes | 45023 | (1,2,3,4,1) | + When importing + Then placex contains + | object | postcode | + | W93 | 45023 | + + Scenario: Roads get postcodes from nearby unnamed buildings without other info + Given the grid with origin US + | 10 | | | | 11 | + | | 1 | 2 | | | + | | 4 | 3 | | | + And the named places + | osm | class | type | geometry | + | W93 | highway | residential | 10,11 | + And the places + | osm | class | type | addr+postcode | geometry | + | W22 | place | postcode | 45023 | (1,2,3,4,1) | + When importing + Then placex contains + | object | postcode | + | W93 | 45023 | + + Scenario: Postcodes from admin boundaries are preferred over estimated postcodes + Given the grid with origin FR + | 1 | | | | 4 | | 6 | | 8 | + | | 10 | | 11 | | | | | | + | | | 22 | | | | | | | + | 2 | | | | 3 | | 5 | | 7 | + And the named places + | osm | class | type | admin | addr+postcode | geometry | + | R1 | boundary | administrative | 6 | 10000 | (1,8,7,2,1) | + | R34 | boundary | administrative | 8 | 11000 | (1,6,5,2,1) | + | R4 | boundary | administrative | 10 | 11200 | (1,4,3,2,1) | + And the named places + | osm | class | type | geometry | + | W93 | highway | residential | 10,1 | + And the named places + | osm | class | type | addr+postcode | + | N22 | building | yes | 45023 | + When importing + Then placex contains + | object | postcode | + | W93 | 11200 | + + Scenario: Postcodes are added to the postcode + Given the places + | osm | class | type | addr+postcode | addr+housenumber | geometry | + | N34 | place | house | 01982 | 111 |country:de | + When importing + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt | + | de | 01982 | country:de | + + @skip + Scenario: search and address ranks for GB post codes correctly assigned + Given the places + | osm | class | type | postcode | geometry | + | N1 | place | postcode | E45 2CD | country:gb | + | N2 | place | postcode | E45 2 | country:gb | + | N3 | place | postcode | Y45 | country:gb | + When importing + Then location_postcode contains exactly + | postcode | country_code | rank_search | rank_address | + | E45 2CD | gb | 25 | 5 | + | E45 2 | gb | 23 | 5 | + | Y45 | gb | 21 | 5 | + + Scenario: Postcodes outside all countries are not added to the postcode table + Given the places + | osm | class | type | addr+postcode | addr+housenumber | addr+place | geometry | + | N34 | place | house | 01982 | 111 | Null Island | 0 0.00001 | + And the places + | osm | class | type | name | geometry | + | N1 | place | hamlet | Null Island | 0 0 | + When importing + Then location_postcode contains exactly + | place_id | + When geocoding "111, 01982 Null Island" + Then the result set contains + | object | display_name | + | N34 | 111, Null Island, 01982 | diff --git a/test/bdd/features/db/import/rank_computation.feature b/test/bdd/features/db/import/rank_computation.feature new file mode 100644 index 00000000..de123a56 --- /dev/null +++ b/test/bdd/features/db/import/rank_computation.feature @@ -0,0 +1,295 @@ +Feature: Rank assignment + Tests for assignment of search and address ranks. + + Scenario: Ranks for place nodes are assigned according to their type + Given the named places + | osm | class | type | geometry | + | N1 | foo | bar | 0 0 | + | N11 | place | Continent | 0 0 | + | N12 | place | continent | 0 0 | + | N13 | place | sea | 0 0 | + | N14 | place | country | 0 0 | + | N15 | place | state | 0 0 | + | N16 | place | region | 0 0 | + | N17 | place | county | 0 0 | + | N18 | place | city | 0 0 | + | N19 | place | island | 0 0 | + | N36 | place | house | 0 0 | + And the named places + | osm | class | type | extra+capital | geometry | + | N101 | place | city | yes | 0 0 | + When importing + Then placex contains + | object | rank_search | rank_address | + | N1 | 30 | 30 | + | N11 | 22 | 0 | + | N12 | 2 | 0 | + | N13 | 2 | 0 | + | N14 | 4 | 0 | + | N15 | 8 | 0 | + | N16 | 18 | 0 | + | N17 | 12 | 12 | + | N18 | 16 | 16 | + | N19 | 17 | 0 | + | N101 | 15 | 16 | + | N36 | 30 | 30 | + + Scenario: Ranks for boundaries are assigned according to admin level + Given the named places + | osm | class | type | admin | geometry | + | R20 | boundary | administrative | 2 | (1 1, 2 2, 1 2, 1 1) | + | R21 | boundary | administrative | 32 | (3 3, 4 4, 3 4, 3 3) | + | R22 | boundary | administrative | 6 | (0 0, 1 0, 0 1, 0 0) | + | R23 | boundary | administrative | 10 | (0 0, 1 1, 1 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R20 | 4 | 4 | + | R21 | 25 | 0 | + | R22 | 12 | 12 | + | R23 | 20 | 20 | + + Scenario: Ranks for addressable boundaries with place assignment go with place address ranks if available + Given the named places + | osm | class | type | admin | extra+place | geometry | + | R20 | boundary | administrative | 3 | state | (1 1, 2 2, 1 2, 1 1) | + | R21 | boundary | administrative | 32 | suburb | (3 3, 4 4, 3 4, 3 3) | + | R22 | boundary | administrative | 6 | town | (0 0, 1 0, 0 1, 0 0) | + | R23 | boundary | administrative | 10 | village | (0 0, 1 1, 1 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R20 | 6 | 6 | + | R21 | 25 | 0 | + | R22 | 12 | 16 | + | R23 | 20 | 16 | + + Scenario: Place address ranks cannot overtake a parent address rank + Given the named places + | osm | class | type | admin | extra+place | geometry | + | R20 | boundary | administrative | 8 | town | (0 0, 0 2, 2 2, 2 0, 0 0) | + | R21 | boundary | administrative | 9 | municipality | (0 0, 0 1, 1 1, 1 0, 0 0) | + | R22 | boundary | administrative | 9 | suburb | (0 0, 0 1, 1 1, 1 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R20 | 16 | 16 | + | R21 | 18 | 18 | + | R22 | 18 | 20 | + Then place_addressline contains + | object | address | cached_rank_address | + | R21 | R20 | 16 | + | R22 | R20 | 16 | + + Scenario: Admin levels cannot overtake each other due to place address ranks + Given the named places + | osm | class | type | admin | extra+place | geometry | + | R20 | boundary | administrative | 6 | town | (0 0, 0 2, 2 2, 2 0, 0 0) | + | R21 | boundary | administrative | 8 | | (0 0, 0 1, 1 1, 1 0, 0 0) | + | R22 | boundary | administrative | 8 | suburb | (0 0, 0 1, 1 1, 1 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R20 | 12 | 16 | + | R21 | 16 | 18 | + | R22 | 16 | 20 | + Then place_addressline contains + | object | address | cached_rank_address | + | R21 | R20 | 16 | + | R22 | R20 | 16 | + + Scenario: Admin levels cannot overtake each other due to place address ranks even when slightly misaligned + Given the named places + | osm | class | type | admin | extra+place | geometry | + | R20 | boundary | administrative | 6 | town | (0 0, 0 2, 2 2, 2 0, 0 0) | + | R21 | boundary | administrative | 8 | | (0 0, -0.0001 1, 1 1, 1 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R20 | 12 | 16 | + | R21 | 16 | 18 | + Then place_addressline contains + | object | address | cached_rank_address | + | R21 | R20 | 16 | + + Scenario: Admin levels must not be larger than 25 + Given the named places + | osm | class | type | admin | extra+place | geometry | + | R20 | boundary | administrative | 6 | neighbourhood | (0 0, 0 2, 2 2, 2 0, 0 0) | + | R21 | boundary | administrative | 7 | | (0 0, 0 1, 1 1, 1 0, 0 0) | + | R22 | boundary | administrative | 8 | | (0 0, 0 0.5, 0.5 0.5, 0.5 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R20 | 12 | 22 | + | R21 | 14 | 24 | + | R22 | 16 | 25 | + + Scenario: admin levels contained in a place area must not overtake address ranks + Given the named places + | osm | class | type | admin | geometry | + | R10 | place | city | 15 | (0 0, 0 2, 2 0, 0 0) | + | R20 | boundary | administrative | 6 | (0 0, 0 1, 1 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R10 | 16 | 16 | + | R20 | 12 | 18 | + + Scenario: admin levels overlapping a place area are not demoted + Given the named places + | osm | class | type | admin | geometry | + | R10 | place | city | 15 | (0 0, 0 2, 2 0, 0 0) | + | R20 | boundary | administrative | 6 | (-1 0, 0 1, 1 0, -1 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R10 | 16 | 16 | + | R20 | 12 | 12 | + + Scenario: admin levels with equal area as a place area are not demoted + Given the named places + | osm | class | type | admin | geometry | + | R10 | place | city | 15 | (0 0, 0 2, 2 0, 0 0) | + | R20 | boundary | administrative | 6 | (0 0, 0 2, 2 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R10 | 16 | 16 | + | R20 | 12 | 12 | + + + Scenario: adjacent admin_levels are considered the same object when they have the same wikidata + Given the named places + | osm | class | type | admin | extra+wikidata | geometry | + | N20 | place | square | 15 | Q123 | 0.1 0.1 | + | R23 | boundary | administrative | 10 | Q444 | (0 0, 0 1, 1 1, 1 0, 0 0) | + | R21 | boundary | administrative | 9 | Q444 | (0 0, 0 1, 1 1, 1 0, 0 0) | + | R22 | boundary | administrative | 8 | Q444 | (0 0, 0 1, 1 1, 1 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R23 | 20 | 0 | + | R21 | 18 | 0 | + | R22 | 16 | 16 | + Then place_addressline contains exactly + | object | address | cached_rank_address | + | N20 | R22 | 16 | + + Scenario: adjacent admin_levels are considered different objects when they have different wikidata + Given the named places + | osm | class | type | admin | extra+wikidata | geometry | + | N20 | place | square | 15 | Q123 | 0.1 0.1 | + | R21 | boundary | administrative | 9 | Q4441 | (0 0, 0 1, 1 1, 1 0, 0 0) | + | R22 | boundary | administrative | 8 | Q444 | (0 0, 0 1, 1 1, 1 0, 0 0) | + When importing + Then placex contains + | object | rank_search | rank_address | + | R21 | 18 | 18 | + | R22 | 16 | 16 | + Then place_addressline contains + | object | address | cached_rank_address | + | N20 | R22 | 16 | + | N20 | R21 | 18 | + + Scenario: Mixes of admin boundaries and place areas I + Given the grid + | 1 | | 10 | | | 2 | + | | 9 | | | | | + | 20| | 21 | | | | + | 4 | | 11 | | | 3 | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 5 | Greater London | (1,2,3,4,1) | + | R2 | boundary | administrative | 8 | Kensington | (1,10,11,4,1) | + And the places + | osm | class | type | name | geometry | + | R10 | place | city | London | (1,2,3,4,1) | + | N9 | place | town | Fulham | 9 | + | W1 | highway | residential | Lots Grove | 20,21 | + When importing + Then placex contains + | object | rank_search | rank_address | + | R1 | 10 | 10 | + | R10 | 16 | 16 | + | R2 | 16 | 18 | + | N9 | 18 | 18 | + And place_addressline contains + | object | address | isaddress | cached_rank_address | + | W1 | R1 | True | 10 | + | W1 | R10 | True | 16 | + | W1 | R2 | True | 18 | + | W1 | N9 | False | 18 | + + + Scenario: Mixes of admin boundaries and place areas II + Given the grid + | 1 | | 10 | | 5 | 2 | + | | 9 | | | | | + | 20| | 21 | | | | + | 4 | | 11 | | 6 | 3 | + And the places + | osm | class | type | admin | name | geometry | + | R1 | boundary | administrative | 5 | Greater London | (1,2,3,4,1) | + | R2 | boundary | administrative | 8 | London | (1,5,6,4,1) | + And the places + | osm | class | type | name | geometry | + | R10 | place | city | Westminster | (1,10,11,4,1) | + | N9 | place | town | Fulham | 9 | + | W1 | highway | residential | Lots Grove | 20,21 | + When importing + Then placex contains + | object | rank_search | rank_address | + | R1 | 10 | 10 | + | R2 | 16 | 16 | + | R10 | 16 | 18 | + | N9 | 18 | 18 | + And place_addressline contains + | object | address | isaddress | cached_rank_address | + | W1 | R1 | True | 10 | + | W1 | R10 | True | 18 | + | W1 | R2 | True | 16 | + | W1 | N9 | False | 18 | + + + Scenario: POI nodes with place tags + Given the places + | osm | class | type | name | extratags | + | N23 | amenity | playground | AB | "place": "city" | + | N23 | place | city | AB | "amenity": "playground" | + When importing + Then placex contains exactly + | object | rank_search | rank_address | + | N23:amenity | 30 | 30 | + | N23:place | 16 | 16 | + + Scenario: Address rank 25 is only used for addr:place + Given the grid + | 10 | 33 | 34 | 11 | + Given the places + | osm | class | type | name | + | N10 | place | village | vil | + | N11 | place | farm | farm | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | RD | 33,11 | + And the places + | osm | class | type | name | addr+farm | geometry | + | W2 | highway | residential | RD2 | farm | 34,11 | + And the places + | osm | class | type | housenr | + | N33 | place | house | 23 | + And the places + | osm | class | type | housenr | addr+place | + | N34 | place | house | 23 | farm | + When importing + Then placex contains + | object | parent_place_id | + | N11 | N10 | + | N33 | W1 | + | N34 | N11 | + And place_addressline contains + | object | address | + | W1 | N10 | + | W2 | N10 | + | W2 | N11 | diff --git a/test/bdd/features/db/import/search_name.feature b/test/bdd/features/db/import/search_name.feature new file mode 100644 index 00000000..29b0f0bf --- /dev/null +++ b/test/bdd/features/db/import/search_name.feature @@ -0,0 +1,389 @@ +Feature: Creation of search terms + Tests that search_name table is filled correctly + + Scenario: Semicolon-separated names appear as separate full names + Given the places + | osm | class | type | name+alt_name | + | N1 | place | city | New York; Big Apple | + | N2 | place | town | New York Big Apple | + When importing + And geocoding "New York Big Apple" + Then result 0 contains + | object | + | N2 | + + Scenario: Comma-separated names appear as a single full name + Given the places + | osm | class | type | name+name | + | N1 | place | city | New York, Big Apple | + | N2 | place | town | New York Big Apple | + When importing + And geocoding "New York Big Apple" + Then result 0 contains + | object | + | N1 | + + Scenario: Name parts before brackets appear as full names + Given the places + | osm | class | type | name+name | + | N1 | place | city | Halle (Saale) | + | N2 | place | town | Halle | + When importing + And geocoding "Halle" + Then result 0 contains + | object | + | N1 | + When geocoding "Halle (Saale)" + Then the result set contains + | object | + | N1 | + + Scenario: Unknown addr: tags can be found for unnamed POIs + Given the grid + | | 1 | | | + | 10 | | | 11 | + And the places + | osm | class | type | housenr | addr+city | + | N1 | place | house | 23 | Walltown | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Rose Street | 10,11 | + When importing + When geocoding "23 Rose Street, Walltown" + Then the result set contains + | object | display_name | + | N1 | 23, Rose Street | + When geocoding "Walltown, Rose Street 23" + Then the result set contains + | object | display_name | + | N1 | 23, Rose Street | + When geocoding "Rose Street 23, Walltown" + Then the result set contains + | object | display_name | + | N1 | 23, Rose Street | + + Scenario: Searching for unknown addr: tags also works for multiple words + Given the grid + | | 1 | | | + | 10 | | | 11 | + And the places + | osm | class | type | housenr | addr+city | + | N1 | place | house | 23 | Little Big Town | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Rose Street | 10,11 | + When importing + When geocoding "23 Rose Street, Little Big Town" + Then the result set contains + | object | display_name | + | N1 | 23, Rose Street | + When geocoding "Rose Street 23, Little Big Town" + Then the result set contains + | object | display_name | + | N1 | 23, Rose Street | + When geocoding "Little big Town, Rose Street 23" + Then the result set contains + | object | display_name | + | N1 | 23, Rose Street | + + Scenario: Unnamed POI can be found when it has known addr: tags + Given the grid + | | 1 | | | + | 10 | | | 11 | + And the places + | osm | class | type | housenr | addr+city | + | N1 | place | house | 23 | Walltown | + And the places + | osm | class | type | name+name | addr+city | geometry | + | W1 | highway | residential | Rose Street | Walltown | 10,11 | + When importing + When geocoding "23 Rose Street, Walltown" + Then the result set contains + | object | display_name | + | N1 | 23, Rose Street | + + Scenario: Unnamed POIs inherit parent name when unknown addr:place is present + Given the grid + | 100 | | | | | 101 | + | | | 1 | | | | + | 103 | 10 | | | 11 | 102 | + And the places + | osm | class | type | housenr | addr+place | + | N1 | place | house | 23 | Walltown | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Rose Street | 10,11 | + | R1 | place | city | Strange Town | (100,101,102,103,100) | + When importing + Then placex contains + | object | parent_place_id | + | N1 | R1 | + When geocoding "23 Rose Street" + Then all results contain + | object | display_name | + | W1 | Rose Street, Strange Town | + When geocoding "23 Walltown, Strange Town" + Then the result set contains + | object | display_name | + | N1 | 23, Walltown, Strange Town | + When geocoding "Walltown 23, Strange Town" + Then the result set contains + | object | display_name | + | N1 | 23, Walltown, Strange Town | + When geocoding "Strange Town, Walltown 23" + Then the result set contains + | object | display_name | + | N1 | 23, Walltown, Strange Town | + + Scenario: Named POIs can be searched by housenumber when unknown addr:place is present + Given the grid + | 100 | | | | | 101 | + | | | 1 | | | | + | 103 | 10 | | | 11 | 102 | + And the places + | osm | class | type | name | housenr | addr+place | + | N1 | place | house | Blue house | 23 | Walltown | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Rose Street | 10,11 | + | R1 | place | city | Strange Town | (100,101,102,103,100) | + When importing + When geocoding "23 Walltown, Strange Town" + Then the result set contains + | object | display_name | + | N1 | Blue house, 23, Walltown, Strange Town | + When geocoding "Walltown 23, Strange Town" + Then the result set contains + | object | display_name | + | N1 | Blue house, 23, Walltown, Strange Town | + When geocoding "Strange Town, Walltown 23" + Then the result set contains + | object | display_name | + | N1 | Blue house, 23, Walltown, Strange Town | + When geocoding "Strange Town, Walltown 23, Blue house" + Then the result set contains + | object | display_name | + | N1 | Blue house, 23, Walltown, Strange Town | + When geocoding "Strange Town, Walltown, Blue house" + Then the result set contains + | object | display_name | + | N1 | Blue house, 23, Walltown, Strange Town | + + Scenario: Named POIs can be found when unknown multi-word addr:place is present + Given the grid + | 100 | | | | | 101 | + | | | 1 | | | | + | 103 | 10 | | | 11 | 102 | + And the places + | osm | class | type | name | housenr | addr+place | + | N1 | place | house | Blue house | 23 | Moon sun | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Rose Street | 10,11 | + | R1 | place | city | Strange Town | (100,101,102,103,100) | + When importing + When geocoding "23 Moon Sun, Strange Town" + Then the result set contains + | object | display_name | + | N1 | Blue house, 23, Moon sun, Strange Town | + When geocoding "Blue house, Moon Sun, Strange Town" + Then the result set contains + | object | display_name | + | N1 | Blue house, 23, Moon sun, Strange Town | + + Scenario: Unnamed POIs doesn't inherit parent name when addr:place is present only in parent address + Given the grid + | 100 | | | | | 101 | + | | | 1 | | | | + | 103 | 10 | | | 11 | 102 | + And the places + | osm | class | type | housenr | addr+place | + | N1 | place | house | 23 | Walltown | + And the places + | osm | class | type | name+name | addr+city | geometry | + | W1 | highway | residential | Rose Street | Walltown | 10,11 | + | R1 | place | suburb | Strange Town | Walltown | (100,101,102,103,100) | + When importing + When geocoding "23 Rose Street, Walltown" + Then all results contain + | object | display_name | + | W1 | Rose Street, Strange Town | + When geocoding "23 Walltown" + Then all results contain + | object | display_name | + | N1 | 23, Walltown, Strange Town | + + Scenario: Unnamed POIs does inherit parent name when unknown addr:place and addr:street is present + Given the grid + | | 1 | | | + | 10 | | | 11 | + And the places + | osm | class | type | housenr | addr+place | addr+street | + | N1 | place | house | 23 | Walltown | Lily Street | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Rose Street | 10,11 | + When importing + When geocoding "23 Rose Street" + Then the result set contains + | object | display_name | + | N1 | 23, Rose Street | + When geocoding "23 Lily Street" + Then exactly 0 results are returned + + Scenario: An unknown addr:street is ignored + Given the grid + | | 1 | | | + | 10 | | | 11 | + And the places + | osm | class | type | housenr | addr+street | + | N1 | place | house | 23 | Lily Street | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Rose Street | 10,11 | + When importing + When geocoding "23 Rose Street" + Then the result set contains + | object | display_name | + | N1 | 23, Rose Street | + When geocoding "23 Lily Street" + Then exactly 0 results are returned + + Scenario: Named POIs can be found through unknown address tags + Given the grid + | | 1 | | | + | 10 | | | 11 | + And the places + | osm | class | type | name+name | housenr | addr+city | + | N1 | place | house | Green Moss | 26 | Walltown | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Rose Street | 10,11 | + When importing + When geocoding "Green Moss, Rose Street, Walltown" + Then the result set contains + | object | display_name | + | N1 | Green Moss, 26, Rose Street | + When geocoding "Green Moss, 26, Rose Street, Walltown" + Then the result set contains + | object | display_name | + | N1 | Green Moss, 26, Rose Street | + When geocoding "26, Rose Street, Walltown" + Then the result set contains + | object | display_name | + | N1 | Green Moss, 26, Rose Street | + When geocoding "Rose Street 26, Walltown" + Then the result set contains + | object | display_name | + | N1 | Green Moss, 26, Rose Street | + When geocoding "Walltown, Rose Street 26" + Then the result set contains + | object | display_name | + | N1 | Green Moss, 26, Rose Street | + + Scenario: Named POI doesn't inherit parent name when addr:place is present only in parent address + Given the grid + | 100 | | | | | 101 | + | | | 1 | | | | + | 103 | 10 | | | 11 | 102 | + And the places + | osm | class | type | name+name | addr+place | + | N1 | place | house | Green Moss | Walltown | + And the places + | osm | class | type | name+name | geometry | + | W1 | highway | residential | Rose Street | 10,11 | + | R1 | place | suburb | Strange Town | (100,101,102,103,100) | + When importing + When geocoding "Green Moss, Rose Street, Walltown" + Then exactly 0 results are returned + When geocoding "Green Moss, Walltown" + Then the result set contains + | object | display_name | + | N1 | Green Moss, Walltown, Strange Town | + + Scenario: Named POIs inherit address from parent + Given the grid + | | 1 | | | + | 10 | | | 11 | + And the places + | osm | class | type | name | geometry | + | N1 | place | house | foo | 1 | + | W1 | highway | residential | the road | 10,11 | + When importing + When geocoding "foo, the road" + Then all results contain + | object | + | N1 | + + Scenario: Some addr: tags are added to address + Given the grid + | | 2 | 3 | | + | 10 | | | 11 | + And the places + | osm | class | type | name | + | N2 | place | city | bonn | + | N3 | place | suburb | smalltown| + And the places + | osm | class | type | name | addr+city | addr+municipality | addr+suburb | geometry | + | W1 | highway | service | the end | bonn | New York | Smalltown | 10,11 | + When importing + When geocoding "the end, new york, bonn, smalltown" + Then all results contain + | object | + | W1 | + + Scenario: A known addr:* tag is added even if the name is unknown + Given the grid + | 10 | | | | 11 | + And the places + | osm | class | type | name | addr+city | geometry | + | W1 | highway | residential | Road | Nandu | 10,11 | + When importing + And geocoding "Road, Nandu" + Then all results contain + | object | + | W1 | + + Scenario: a linked place does not show up in search name + Given the 0.01 grid + | 10 | | 11 | + | | 2 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | admin | geometry | + | R13 | boundary | administrative | Roma | 9 | (10,11,12,13,10) | + And the places + | osm | class | type | name | + | N2 | place | city | Cite | + And the relations + | id | members | tags+type | + | 13 | N2:label | boundary | + When importing + Then placex contains + | object | linked_place_id | + | N2 | R13 | + When geocoding "Cite" + Then all results contain + | object | + | R13 | + + Scenario: a linked waterway does not show up in search name + Given the grid + | 1 | | 2 | | 3 | + And the places + | osm | class | type | name | geometry | + | W1 | waterway | river | Rhein | 1,2 | + | W2 | waterway | river | Rhein | 2,3 | + | R13 | waterway | river | Rhein | 1,2,3 | + And the relations + | id | members | tags+type | + | 13 | W1,W2:main_stream | waterway | + When importing + Then placex contains + | object | linked_place_id | + | W1 | R13 | + | W2 | R13 | + When geocoding "Rhein" + Then all results contain + | object | + | R13 | diff --git a/test/bdd/features/db/query/housenumbers.feature b/test/bdd/features/db/query/housenumbers.feature new file mode 100644 index 00000000..6ed6284b --- /dev/null +++ b/test/bdd/features/db/query/housenumbers.feature @@ -0,0 +1,320 @@ +Feature: Searching of house numbers + Test for specialised treeatment of housenumbers + + Background: + Given the grid + | 1 | | 2 | | 3 | + | | 9 | | | | + | | | | | 4 | + + + Scenario: A simple ascii digit housenumber is found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | 45 | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | North Road | 1,2,3 | + When importing + And geocoding "45, North Road" + Then the result set contains + | object | + | N1 | + When geocoding "North Road 45" + Then the result set contains + | object | + | N1 | + + + Scenario Outline: Numeral housenumbers in any script are found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | North Road | 1,2,3 | + When importing + And geocoding "45, North Road" + Then the result set contains + | object | + | N1 | + When geocoding "North Road ④⑤" + Then the result set contains + | object | + | N1 | + When geocoding "North Road 𑁪𑁫" + Then the result set contains + | object | + | N1 | + + Examples: + | number | + | 45 | + | ④⑤ | + | 𑁪𑁫 | + + + Scenario Outline: Each housenumber in a list is found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | Multistr | 1,2,3 | + When importing + When geocoding "2 Multistr" + Then the result set contains + | object | + | N1 | + When geocoding "4 Multistr" + Then the result set contains + | object | + | N1 | + When geocoding "12 Multistr" + Then the result set contains + | object | + | N1 | + + Examples: + | hnrs | + | 2;4;12 | + | 2,4,12 | + | 2, 4, 12 | + + + Scenario Outline: Housenumber - letter combinations are found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | Multistr | 1,2,3 | + When importing + When geocoding "2A Multistr" + Then the result set contains + | object | + | N1 | + When geocoding "2 a Multistr" + Then the result set contains + | object | + | N1 | + When geocoding "2-A Multistr" + Then the result set contains + | object | + | N1 | + When geocoding "Multistr 2 A" + Then the result set contains + | object | + | N1 | + + Examples: + | hnr | + | 2a | + | 2 A | + | 2-a | + | 2/A | + + + Scenario Outline: Number - Number combinations as a housenumber are found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | Chester St | 1,2,3 | + When importing + When geocoding "34-10 Chester St" + Then the result set contains + | object | + | N1 | + When geocoding "34/10 Chester St" + Then the result set contains + | object | + | N1 | + When geocoding "34 10 Chester St" + Then the result set contains + | object | + | N1 | + When geocoding "3410 Chester St" + Then the result set contains + | object | + | W10 | + + Examples: + | hnr | + | 34-10 | + | 34 10 | + | 34/10 | + + + Scenario Outline: a bis housenumber is found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | Rue Paris | 1,2,3 | + When importing + When geocoding "Rue Paris 45bis" + Then the result set contains + | object | + | N1 | + When geocoding "Rue Paris 45 BIS" + Then the result set contains + | object | + | N1 | + When geocoding "Rue Paris 45BIS" + Then the result set contains + | object | + | N1 | + When geocoding "Rue Paris 45 bis" + Then the result set contains + | object | + | N1 | + + Examples: + | hnr | + | 45bis | + | 45BIS | + | 45 BIS | + | 45 bis | + + + Scenario Outline: a ter housenumber is found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | Rue du Berger | 1,2,3 | + When importing + When geocoding "Rue du Berger 45ter" + Then the result set contains + | object | + | N1 | + When geocoding "Rue du Berger 45 TER" + Then the result set contains + | object | + | N1 | + When geocoding "Rue du Berger 45TER" + Then the result set contains + | object | + | N1 | + When geocoding "Rue du Berger 45 ter" + Then the result set contains + | object | + | N1 | + + Examples: + | hnr | + | 45ter | + | 45TER | + | 45 ter | + | 45 TER | + + + Scenario Outline: a number - letter - number combination housenumber is found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | Herengracht | 1,2,3 | + When importing + When geocoding "501-H 1 Herengracht" + Then the result set contains + | object | + | N1 | + When geocoding "501H-1 Herengracht" + Then the result set contains + | object | + | N1 | + When geocoding "501H1 Herengracht" + Then the result set contains + | object | + | N1 | + When geocoding "501-H1 Herengracht" + Then the result set contains + | object | + | N1 | + + Examples: + | hnr | + | 501 H1 | + | 501H 1 | + | 501/H/1 | + | 501h1 | + + + Scenario Outline: Russian housenumbers are found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | Голубинская улица | 1,2,3 | + When importing + When geocoding "Голубинская улица 55к3" + Then the result set contains + | object | + | N1 | + When geocoding "Голубинская улица 55 k3" + Then the result set contains + | object | + | N1 | + When geocoding "Голубинская улица 55 к-3" + Then the result set contains + | object | + | N1 | + + Examples: + | hnr | + | 55к3 | + | 55 к3 | + + + Scenario: A name mapped as a housenumber is found + Given the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | Warring | 9 | + And the places + | osm | class | type | name | geometry | + | W10 | highway | path | Chester St | 1,2,3 | + When importing + When geocoding "Chester St Warring" + Then the result set contains + | object | + | N1 | + + + Scenario: Interpolations are found according to their type + Given the grid + | 10 | | 11 | + | 100 | | 101 | + | 20 | | 21 | + And the places + | osm | class | type | name | geometry | + | W100 | highway | residential | Ringstr | 100, 101 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W10 | place | houses | even | 10, 11 | + | W20 | place | houses | odd | 20, 21 | + And the places + | osm | class | type | housenr | geometry | + | N10 | place | house | 10 | 10 | + | N11 | place | house | 20 | 11 | + | N20 | place | house | 11 | 20 | + | N21 | place | house | 21 | 21 | + And the ways + | id | nodes | + | 10 | 10, 11 | + | 20 | 20, 21 | + When importing + When geocoding "Ringstr 12" + Then the result set contains + | object | + | W10 | + When geocoding "Ringstr 13" + Then the result set contains + | object | + | W20 | diff --git a/test/bdd/features/db/query/interpolation.feature b/test/bdd/features/db/query/interpolation.feature new file mode 100644 index 00000000..1746d37d --- /dev/null +++ b/test/bdd/features/db/query/interpolation.feature @@ -0,0 +1,57 @@ +Feature: Query of address interpolations + Tests that interpolated addresses can be queried correctly + + Background: + Given the grid + | 1 | | 2 | | 3 | + | 10 | | 12 | | 13 | + | 7 | | 8 | | 9 | + + Scenario: Find interpolations with single number + Given the places + | osm | class | type | name | geometry | + | W10 | highway | primary | Nickway | 10,12,13 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | odd | 1,3 | + And the places + | osm | class | type | housenr | geometry | + | N1 | place | house | 1 | 1 | + | N3 | place | house | 5 | 3 | + And the ways + | id | nodes | + | 1 | 1,3 | + When importing + When reverse geocoding at node 2 + Then the result contains + | display_name | + | 3, Nickway | + When geocoding "Nickway 3" + Then all results contain + | object | display_name | + | W1 | 3, Nickway | + + + Scenario: Find interpolations with multiple numbers + Given the places + | osm | class | type | name | geometry | + | W10 | highway | primary | Nickway | 10,12,13 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,3 | + And the places + | osm | class | type | housenr | geometry | + | N1 | place | house | 2 | 1 | + | N3 | place | house | 18 | 3 | + And the ways + | id | nodes | + | 1 | 1,3 | + When importing + When reverse geocoding at node 2 + Then the result contains + | display_name | centroid!wkt | + | 10, Nickway | 2 | + When geocoding "Nickway 10" + Then all results contain + | object | display_name | centroid!wkt | + | W1 | 10, Nickway | 2 | diff --git a/test/bdd/features/db/query/japanese.feature b/test/bdd/features/db/query/japanese.feature new file mode 100644 index 00000000..4fad118c --- /dev/null +++ b/test/bdd/features/db/query/japanese.feature @@ -0,0 +1,28 @@ +Feature: Searches in Japan + Test specifically for searches of Japanese addresses and in Japanese language. + Scenario: A block house-number is parented to the neighbourhood + Given the grid with origin JP + | 1 | | | | 2 | + | | 3 | | | | + | | | 9 | | | + | | | | 6 | | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | 雉子橋通り | 1,2 | + And the places + | osm | class | type | housenr | addr+block_number | addr+neighbourhood | geometry | + | N3 | amenity | restaurant | 2 | 6 | 2丁目 | 3 | + And the places + | osm | class | type | name | geometry | + | N9 | place | neighbourhood | 2丁目 | 9 | + And the places + | osm | class | type | name | geometry | + | N6 | place | quarter | 加瀬 | 6 | + When importing + Then placex contains + | object | parent_place_id | + | N3 | N9 | + When geocoding "2丁目 6-2" + Then all results contain + | object | + | N3 | diff --git a/test/bdd/features/db/query/linking.feature b/test/bdd/features/db/query/linking.feature new file mode 100644 index 00000000..8e6ab4d1 --- /dev/null +++ b/test/bdd/features/db/query/linking.feature @@ -0,0 +1,63 @@ +Feature: Searching linked places + Tests that information from linked places can be searched correctly + + Scenario: Additional names from linked places are searchable + Given the 0.1 grid + | 10 | | 11 | + | | 2 | | + | 13 | | 12 | + Given the places + | osm | class | type | admin | name | geometry | + | R13 | boundary | administrative | 6 | Garbo | (10,11,12,13,10) | + Given the places + | osm | class | type | admin | name+name:it | + | N2 | place | hamlet | 15 | Vario | + And the relations + | id | members | tags+type | + | 13 | N2:label | boundary | + When importing + Then placex contains + | object | linked_place_id | + | N2 | R13 | + When geocoding "Vario" + | namedetails | + | 1 | + Then all results contain + | object | display_name | namedetails!dict | + | R13 | Garbo | "name": "Garbo", "name:it": "Vario" | + When geocoding "Vario" + | accept-language | + | it | + Then all results contain + | object | display_name | + | R13 | Vario | + + + Scenario: Differing names from linked places are searchable + Given the 0.1 grid + | 10 | | 11 | + | | 2 | | + | 13 | | 12 | + Given the places + | osm | class | type | admin | name | geometry | + | R13 | boundary | administrative | 6 | Garbo | (10,11,12,13,10) | + Given the places + | osm | class | type | admin | name | + | N2 | place | hamlet | 15 | Vario | + And the relations + | id | members | tags+type | + | 13 | N2:label | boundary | + When importing + Then placex contains + | object | linked_place_id | + | N2 | R13 | + When geocoding "Vario" + | namedetails | + | 1 | + Then all results contain + | object | display_name | namedetails!dict | + | R13 | Garbo | "name": "Garbo", "_place_name": "Vario" | + When geocoding "Garbo" + Then all results contain + | object | display_name | + | R13 | Garbo | diff --git a/test/bdd/features/db/query/normalization.feature b/test/bdd/features/db/query/normalization.feature new file mode 100644 index 00000000..f884be6b --- /dev/null +++ b/test/bdd/features/db/query/normalization.feature @@ -0,0 +1,225 @@ +Feature: Import and search of names + Tests all naming related issues: normalisation, + abbreviations, internationalisation, etc. + + Scenario: non-latin scripts can be found + Given the places + | osm | class | type | name | + | N1 | place | locality | Речицкий район | + | N2 | place | locality | Refugio de montaña | + | N3 | place | locality | 高槻市| + | N4 | place | locality | الدوحة | + When importing + When geocoding "Речицкий район" + Then result 0 contains + | object | + | N1 | + When geocoding "Refugio de montaña" + Then result 0 contains + | object | + | N2 | + When geocoding "高槻市" + Then result 0 contains + | object | + | N3 | + When geocoding "الدوحة" + Then result 0 contains + | object | + | N4 | + + Scenario: Case-insensitivity of search + Given the places + | osm | class | type | name | + | N1 | place | locality | FooBar | + When importing + Then placex contains + | object | class | type | name+name | + | N1 | place | locality | FooBar | + When geocoding "FooBar" + Then result 0 contains + | object | + | N1 | + When geocoding "foobar" + Then result 0 contains + | object | + | N1 | + When geocoding "fOObar" + Then result 0 contains + | object | + | N1 | + When geocoding "FOOBAR" + Then result 0 contains + | object | + | N1 | + + Scenario: Multiple spaces in name + Given the places + | osm | class | type | name | + | N1 | place | locality | one two three | + When importing + When geocoding "one two three" + Then result 0 contains + | object | + | N1 | + When geocoding "one two three" + Then result 0 contains + | object | + | N1 | + When geocoding "one two three" + Then result 0 contains + | object | + | N1 | + When geocoding " one two three" + Then result 0 contains + | object | + | N1 | + + Scenario: Special characters in name + Given the places + | osm | class | type | name+name:de | + | N1 | place | locality | Jim-Knopf-Straße | + | N2 | place | locality | Smith/Weston | + | N3 | place | locality | space mountain | + | N4 | place | locality | space | + | N5 | place | locality | mountain | + When importing + When geocoding "Jim-Knopf-Str" + Then result 0 contains + | object | + | N1 | + When geocoding "Jim Knopf-Str" + Then result 0 contains + | object | + | N1 | + When geocoding "Jim Knopf Str" + Then result 0 contains + | object | + | N1 | + When geocoding "Jim/Knopf-Str" + Then result 0 contains + | object | + | N1 | + When geocoding "Jim-Knopfstr" + Then result 0 contains + | object | + | N1 | + When geocoding "Smith/Weston" + Then result 0 contains + | object | + | N2 | + When geocoding "Smith Weston" + Then result 0 contains + | object | + | N2 | + When geocoding "Smith-Weston" + Then result 0 contains + | object | + | N2 | + When geocoding "space mountain" + Then result 0 contains + | object | + | N3 | + When geocoding "space-mountain" + Then result 0 contains + | object | + | N3 | + When geocoding "space/mountain" + Then result 0 contains + | object | + | N3 | + When geocoding "space\mountain" + Then result 0 contains + | object | + | N3 | + When geocoding "space(mountain)" + Then result 0 contains + | object | + | N3 | + + Scenario: Landuse with name are found + Given the grid + | 1 | 2 | + | 3 | | + Given the places + | osm | class | type | name | geometry | + | R1 | natural | meadow | landuse1 | (1,2,3,1) | + | R2 | landuse | industrial | landuse2 | (2,3,1,2) | + When importing + When geocoding "landuse1" + Then result 0 contains + | object | + | R1 | + When geocoding "landuse2" + Then result 0 contains + | object | + | R2 | + + Scenario: Postcode boundaries without ref + Given the grid with origin FR + | | 2 | | + | 1 | | 3 | + Given the places + | osm | class | type | postcode | geometry | + | R1 | boundary | postal_code | 123-45 | (1,2,3,1) | + When importing + When geocoding "123-45" + Then result 0 contains + | object | + | R1 | + + Scenario Outline: Housenumbers with special characters are found + Given the grid + | 1 | | | | 2 | + | | | 9 | | | + And the places + | osm | class | type | name | geometry | + | W1 | highway | primary | Main St | 1,2 | + And the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + When importing + And geocoding "Main St " + Then result 0 contains + | object | display_name | + | N1 | , Main St | + + Examples: + | nr | + | 1 | + | 3456 | + | 1 a | + | 56b | + | 1 A | + | 2號 | + | 1Б | + | 1 к1 | + | 23-123 | + + Scenario Outline: Housenumbers in lists are found + Given the grid + | 1 | | | | 2 | + | | | 9 | | | + And the places + | osm | class | type | name | geometry | + | W1 | highway | primary | Main St | 1,2 | + And the places + | osm | class | type | housenr | geometry | + | N1 | building | yes | | 9 | + When importing + And geocoding "Main St " + Then result 0 contains + | object | display_name | + | N1 | , Main St | + + Examples: + | nr-list | nr | + | 1,2,3 | 1 | + | 1,2,3 | 2 | + | 1, 2, 3 | 3 | + | 45 ;67;3 | 45 | + | 45 ;67;3 | 67 | + | 1a;1k | 1a | + | 1a;1k | 1k | + | 34/678 | 34 | + | 34/678 | 678 | + | 34/678 | 34/678 | diff --git a/test/bdd/features/db/query/postcodes.feature b/test/bdd/features/db/query/postcodes.feature new file mode 100644 index 00000000..f5ffcd00 --- /dev/null +++ b/test/bdd/features/db/query/postcodes.feature @@ -0,0 +1,109 @@ +Feature: Querying fo postcode variants + + Scenario: Postcodes in Singapore (6-digit postcode) + Given the grid with origin SG + | 10 | | | | 11 | + And the places + | osm | class | type | name | addr+postcode | geometry | + | W1 | highway | path | Lorang | 399174 | 10,11 | + When importing + When geocoding "399174" + Then result 0 contains + | type | display_name | + | postcode | 399174, Singapore | + + + Scenario Outline: Postcodes in the Netherlands (mixed postcode with spaces) + Given the grid with origin NL + | 10 | | | | 11 | + And the places + | osm | class | type | name | addr+postcode | geometry | + | W1 | highway | path | De Weide | 3993 DX | 10,11 | + When importing + When geocoding "3993 DX" + Then result 0 contains + | type | display_name | + | postcode | 3993 DX, Nederland | + When geocoding "3993dx" + Then result 0 contains + | type | display_name | + | postcode | 3993 DX, Nederland | + + Examples: + | postcode | + | 3993 DX | + | 3993DX | + | 3993 dx | + + + Scenario: Postcodes in Singapore (6-digit postcode) + Given the grid with origin SG + | 10 | | | | 11 | + And the places + | osm | class | type | name | addr+postcode | geometry | + | W1 | highway | path | Lorang | 399174 | 10,11 | + When importing + When geocoding "399174" + Then result 0 contains + | type | display_name | + | postcode | 399174, Singapore | + + + Scenario Outline: Postcodes in Andorra (with country code) + Given the grid with origin AD + | 10 | | | | 11 | + And the places + | osm | class | type | name | addr+postcode | geometry | + | W1 | highway | path | Lorang | | 10,11 | + When importing + When geocoding "675" + Then result 0 contains + | type | display_name | + | postcode | AD675, Andorra | + When geocoding "AD675" + Then result 0 contains + | type | display_name | + | postcode | AD675, Andorra | + + Examples: + | postcode | + | 675 | + | AD 675 | + | AD675 | + + + Scenario: Different postcodes with the same normalization can both be found + Given the places + | osm | class | type | addr+postcode | addr+housenumber | geometry | + | N34 | place | house | EH4 7EA | 111 | country:gb | + | N35 | place | house | E4 7EA | 111 | country:gb | + When importing + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt | + | gb | EH4 7EA | country:gb | + | gb | E4 7EA | country:gb | + When geocoding "EH4 7EA" + Then result 0 contains + | type | display_name | + | postcode | EH4 7EA, United Kingdom | + When geocoding "E4 7EA" + Then result 0 contains + | type | display_name | + | postcode | E4 7EA, United Kingdom | + + + Scenario: Postcode areas are preferred over postcode points + Given the grid with origin DE + | 1 | 2 | + | 4 | 3 | + Given the places + | osm | class | type | postcode | geometry | + | R23 | boundary | postal_code | 12345 | (1,2,3,4,1) | + When importing + Then location_postcode contains exactly + | country_code | postcode | + | de | 12345 | + When geocoding "12345, de" + Then result 0 contains + | object | + | R23 | diff --git a/test/bdd/features/db/query/reverse.feature b/test/bdd/features/db/query/reverse.feature new file mode 100644 index 00000000..55c2162d --- /dev/null +++ b/test/bdd/features/db/query/reverse.feature @@ -0,0 +1,21 @@ +Feature: Reverse searches + Test results of reverse queries + + Scenario: POI in POI area + Given the 0.0001 grid with origin 1,1 + | 1 | | | | | | | | 2 | + | | 9 | | | | | | | | + | 4 | | | | | | | | 3 | + And the places + | osm | class | type | geometry | + | W1 | aeroway | terminal | (1,2,3,4,1) | + | N1 | amenity | restaurant | 9 | + When importing + And reverse geocoding 1.0001,1.0001 + Then the result contains + | object | + | N1 | + When reverse geocoding 1.0003,1.0001 + Then the result contains + | object | + | W1 | diff --git a/test/bdd/features/db/query/search_simple.feature b/test/bdd/features/db/query/search_simple.feature new file mode 100644 index 00000000..c46efec6 --- /dev/null +++ b/test/bdd/features/db/query/search_simple.feature @@ -0,0 +1,84 @@ +Feature: Searching of simple objects + Testing simple stuff + + Scenario: Search for place node + Given the places + | osm | class | type | name+name | geometry | + | N1 | place | village | Foo | 10.0 -10.0 | + When importing + And geocoding "Foo" + Then result 0 contains + | object | category | type | centroid!wkt | + | N1 | place | village | 10 -10 | + + # github #1763 + Scenario: Correct translation of highways under construction + Given the grid + | 1 | | | | 2 | + | | | 9 | | | + And the places + | osm | class | type | name | geometry | + | W1 | highway | construction | The build | 1,2 | + | N1 | amenity | cafe | Bean | 9 | + When importing + And geocoding "Bean" + Then result 0 contains in field address + | amenity | road | + | Bean | The build | + + Scenario: when missing housenumbers in search don't return a POI + Given the places + | osm | class | type | name | + | N3 | amenity | restaurant | Wood Street | + And the places + | osm | class | type | name | housenr | + | N20 | amenity | restaurant | Red Way | 34 | + When importing + And geocoding "Wood Street 45" + Then exactly 0 results are returned + When geocoding "Red Way 34" + Then all results contain + | object | + | N20 | + + Scenario: when the housenumber is missing the street is still returned + Given the grid + | 1 | | 2 | + Given the places + | osm | class | type | name | geometry | + | W1 | highway | residential | Wood Street | 1, 2 | + When importing + And geocoding "Wood Street" + Then all results contain + | object | + | W1 | + + Scenario Outline: Special cased american states will be found + Given the grid + | 1 | | 2 | + | | 10 | | + | 4 | | 3 | + Given the places + | osm | class | type | admin | name | name+ref | geometry | + | R1 | boundary | administrative | 4 | | | (1,2,3,4,1) | + Given the places + | osm | class | type | name | geometry | + | N2 | place | town | | 10 | + | N3 | place | city | | country:ca | + When importing + And geocoding ", " + Then all results contain + | object | + | N2 | + When geocoding ", " + | accept-language | + | en | + Then all results contain + | object | + | N2 | + + Examples: + | city | state | ref | + | Chicago | Illinois | IL | + | Auburn | Alabama | AL | + | New Orleans | Louisiana | LA | diff --git a/test/bdd/features/db/update/country.feature b/test/bdd/features/db/update/country.feature new file mode 100644 index 00000000..75e552c7 --- /dev/null +++ b/test/bdd/features/db/update/country.feature @@ -0,0 +1,108 @@ +Feature: Country handling + Tests for update of country information + + Background: + Given the 1.0 grid with origin DE + | 1 | | 2 | + | | 10 | | + | 4 | | 3 | + + Scenario: When country names are changed old ones are no longer searchable + Given the places + | osm | class | type | admin | name+name:xy | country | geometry | + | R1 | boundary | administrative | 2 | Loudou | de | (1,2,3,4,1) | + Given the places + | osm | class | type | name | + | N10 | place | town | Wenig | + When importing + When geocoding "Wenig, Loudou" + Then all results contain + | object | + | N10 | + When updating places + | osm | class | type | admin | name+name:xy | country | geometry | + | R1 | boundary | administrative | 2 | Germany | de | (1,2,3,4,1) | + When geocoding "Wenig, Loudou" + Then exactly 0 results are returned + + Scenario: When country names are deleted they are no longer searchable + Given the places + | osm | class | type | admin | name+name:xy | country | geometry | + | R1 | boundary | administrative | 2 | Loudou | de | (1,2,3,4,1) | + Given the places + | osm | class | type | name | + | N10 | place | town | Wenig | + When importing + When geocoding "Wenig, Loudou" + Then all results contain + | object | + | N10 | + When updating places + | osm | class | type | admin | name+name:en | country | geometry | + | R1 | boundary | administrative | 2 | Germany | de | (1,2,3,4,1) | + When geocoding "Wenig, Loudou" + Then exactly 0 results are returned + When geocoding "Wenig" + | accept-language | + | xy,en | + Then all results contain + | object | display_name | + | N10 | Wenig, Germany | + + + Scenario: Default country names are always searchable + Given the places + | osm | class | type | name | + | N10 | place | town | Wenig | + When importing + When geocoding "Wenig, Germany" + Then all results contain + | object | + | N10 | + When geocoding "Wenig, de" + Then all results contain + | object | + | N10 | + When updating places + | osm | class | type | admin | name+name:en | country | geometry | + | R1 | boundary | administrative | 2 | Lilly | de | (1,2,3,4,1) | + When geocoding "Wenig, Germany" + | accept-language | + | en,de | + Then all results contain + | object | display_name | + | N10 | Wenig, Lilly | + When geocoding "Wenig, de" + | accept-language | + | en,de | + Then all results contain + | object | display_name | + | N10 | Wenig, Lilly | + + + Scenario: When a localised name is deleted, the standard name takes over + Given the places + | osm | class | type | admin | name+name:de | country | geometry | + | R1 | boundary | administrative | 2 | Loudou | de | (1,2,3,4,1) | + Given the places + | osm | class | type | name | + | N10 | place | town | Wenig | + When importing + When geocoding "Wenig, Loudou" + | accept-language | + | de,en | + Then all results contain + | object | display_name | + | N10 | Wenig, Loudou | + When updating places + | osm | class | type | admin | name+name:en | country | geometry | + | R1 | boundary | administrative | 2 | Germany | de | (1,2,3,4,1) | + When geocoding "Wenig, Loudou" + Then exactly 0 results are returned + When geocoding "Wenig" + | accept-language | + | de,en | + Then all results contain + | object | display_name | + | N10 | Wenig, Deutschland | + diff --git a/test/bdd/features/db/update/interpolation.feature b/test/bdd/features/db/update/interpolation.feature new file mode 100644 index 00000000..e548862b --- /dev/null +++ b/test/bdd/features/db/update/interpolation.feature @@ -0,0 +1,418 @@ +Feature: Update of address interpolations + Test the interpolated address are updated correctly + + Scenario: new interpolation added to existing street + Given the grid + | 10 | | | | 11 | + | | 1 | 99 | 2 | | + | | | | | | + | 20 | | | | 21 | + And the places + | osm | class | type | name | geometry | + | W2 | highway | unclassified | Sun Way | 10,11 | + | W3 | highway | unclassified | Cloud Street | 20,21 | + And the ways + | id | nodes | + | 10 | 1,2 | + When importing + Then W10 expands to no interpolation + When updating places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And updating places + | osm | class | type | addr+interpolation | geometry | + | W10 | place | houses | even | 1,2 | + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + And W10 expands to interpolation + | parent_place_id | start | end | geometry | + | W2 | 4 | 4 | 99 | + + Scenario: addr:street added to interpolation + Given the grid + | 10 | | | | 11 | + | | 1 | | 2 | | + | | | | | | + | 20 | | | | 21 | + And the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W10 | place | houses | even | 1,2 | + And the places + | osm | class | type | name | geometry | + | W2 | highway | unclassified | Sun Way | 10,11 | + | W3 | highway | unclassified | Cloud Street | 20,21 | + And the ways + | id | nodes | + | 10 | 1,2 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + And W10 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + When updating places + | osm | class | type | addr+interpolation | street | geometry | + | W10 | place | houses | even | Cloud Street | 1,2 | + Then placex contains + | object | parent_place_id | + | N1 | W3 | + | N2 | W3 | + And W10 expands to interpolation + | parent_place_id | start | end | + | W3 | 4 | 4 | + + Scenario: addr:street added to housenumbers + Given the grid + | 10 | | | | 11 | + | | 1 | | 2 | | + | | | | | | + | 20 | | | | 21 | + And the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W10 | place | houses | even | 1,2 | + And the places + | osm | class | type | name | geometry | + | W2 | highway | unclassified | Sun Way | 10,11 | + | W3 | highway | unclassified | Cloud Street | 20,21 | + And the ways + | id | nodes | + | 10 | 1,2 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + And W10 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + When updating places + | osm | class | type | street | housenr | + | N1 | place | house | Cloud Street| 2 | + | N2 | place | house | Cloud Street| 6 | + Then placex contains + | object | parent_place_id | + | N1 | W3 | + | N2 | W3 | + And W10 expands to interpolation + | parent_place_id | start | end | + | W3 | 4 | 4 | + + Scenario: interpolation tag removed + Given the grid + | 10 | | | | 11 | + | | 1 | | 2 | | + | | | | | | + | 20 | | | | 21 | + And the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W10 | place | houses | even | 1,2 | + And the places + | osm | class | type | name | geometry | + | W2 | highway | unclassified | Sun Way | 10,11 | + | W3 | highway | unclassified | Cloud Street | 20,21 | + And the ways + | id | nodes | + | 10 | 1,2 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + And W10 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + When marking for delete W10 + Then W10 expands to no interpolation + And placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + + Scenario: referenced road added + Given the grid + | 10 | | | | 11 | + | | 1 | | 2 | | + | | | | | | + | 20 | | | | 21 | + And the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | addr+interpolation | street | geometry | + | W10 | place | houses | even | Cloud Street| 1,2 | + And the places + | osm | class | type | name | geometry | + | W2 | highway | unclassified | Sun Way | 10,11 | + And the ways + | id | nodes | + | 10 | 1,2 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + And W10 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + When updating places + | osm | class | type | name | geometry | + | W3 | highway | unclassified | Cloud Street | 20,21 | + Then placex contains + | object | parent_place_id | + | N1 | W3 | + | N2 | W3 | + And W10 expands to interpolation + | parent_place_id | start | end | + | W3 | 4 | 4 | + + Scenario: referenced road deleted + Given the grid + | 10 | | | | 11 | + | | 1 | | 2 | | + | | | | | | + | 20 | | | | 21 | + And the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | addr+interpolation | street | geometry | + | W10 | place | houses | even | Cloud Street| 1,2 | + And the places + | osm | class | type | name | geometry | + | W2 | highway | unclassified | Sun Way | 10,11 | + | W3 | highway | unclassified | Cloud Street | 20,21 | + And the ways + | id | nodes | + | 10 | 1,2 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W3 | + | N2 | W3 | + And W10 expands to interpolation + | parent_place_id | start | end | + | W3 | 4 | 4 | + When marking for delete W3 + Then placex contains + | object | parent_place_id | + | N1 | W2 | + | N2 | W2 | + And W10 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + + Scenario: building becomes interpolation + Given the grid + | 10 | | | | 11 | + | | 1 | | 2 | | + | | 4 | | 3 | | + And the places + | osm | class | type | housenr | geometry | + | W1 | place | house | 3 | (1,2,3,4,1) | + And the places + | osm | class | type | name | geometry | + | W2 | highway | unclassified | Cloud Street | 10,11 | + When importing + Then placex contains + | object | parent_place_id | + | W1 | W2 | + Given the ways + | id | nodes | + | 1 | 1,2 | + When updating places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And updating places + | osm | class | type | addr+interpolation | street | geometry | + | W1 | place | houses | even | Cloud Street| 1,2 | + Then placex has no entry for W1 + And W1 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + + Scenario: interpolation becomes building + Given the grid + | 10 | | | | 11 | + | | 1 | | 2 | | + | | 4 | | 3 | | + And the places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + And the places + | osm | class | type | name | geometry | + | W2 | highway | unclassified | Cloud Street | 10,11 | + And the ways + | id | nodes | + | 1 | 1,2 | + And the places + | osm | class | type | addr+interpolation | street | geometry | + | W1 | place | houses | even | Cloud Street| 1,2 | + When importing + Then placex has no entry for W1 + And W1 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + When updating places + | osm | class | type | housenr | geometry | + | W1 | place | house | 3 | (1,2,3,4,1) | + Then placex contains + | object | parent_place_id | + | W1 | W2 | + And W1 expands to no interpolation + + Scenario: housenumbers added to interpolation + Given the grid + | 10 | | | | 11 | + | | 1 | | 2 | | + And the places + | osm | class | type | name | geometry | + | W2 | highway | unclassified | Cloud Street | 10,11 | + And the ways + | id | nodes | + | 1 | 1,2 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W1 | place | houses | even | 1,2 | + When importing + Then W1 expands to no interpolation + When updating places + | osm | class | type | housenr | + | N1 | place | house | 2 | + | N2 | place | house | 6 | + Then W1 expands to interpolation + | parent_place_id | start | end | + | W2 | 4 | 4 | + + Scenario: housenumber added in middle of interpolation + Given the grid + | 1 | | | | | 2 | + | 3 | | | 4 | | 5 | + And the places + | osm | class | type | name | geometry | + | W1 | highway | unclassified | Cloud Street | 1, 2 | + And the ways + | id | nodes | + | 2 | 3,4,5 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W2 | place | houses | even | 3,4,5 | + And the places + | osm | class | type | housenr | + | N3 | place | house | 2 | + | N5 | place | house | 10 | + When importing + Then W2 expands to interpolation + | parent_place_id | start | end | + | W1 | 4 | 8 | + When updating places + | osm | class | type | housenr | + | N4 | place | house | 6 | + Then W2 expands to interpolation + | parent_place_id | start | end | + | W1 | 4 | 4 | + | W1 | 8 | 8 | + + @skip + Scenario: housenumber removed in middle of interpolation + Given the grid + | 1 | | | | | 2 | + | 3 | | | 4 | | 5 | + And the places + | osm | class | type | name | geometry | + | W1 | highway | unclassified | Cloud Street | 1, 2 | + And the ways + | id | nodes | + | 2 | 3,4,5 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W2 | place | houses | even | 3,4,5 | + And the places + | osm | class | type | housenr | + | N3 | place | house | 2 | + | N4 | place | house | 6 | + | N5 | place | house | 10 | + When importing + Then W2 expands to interpolation + | parent_place_id | start | end | + | W1 | 4 | 4 | + | W1 | 8 | 8 | + When marking for delete N4 + Then W2 expands to interpolation + | parent_place_id | start | end | + | W1 | 4 | 8 | + + Scenario: Change the start housenumber + Given the grid + | 1 | | 2 | + | 3 | | 4 | + And the places + | osm | class | type | name | geometry | + | W1 | highway | unclassified | Cloud Street | 1, 2 | + And the ways + | id | nodes | + | 2 | 3,4 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W2 | place | houses | even | 3,4 | + And the places + | osm | class | type | housenr | + | N3 | place | house | 2 | + | N4 | place | house | 6 | + When importing + Then W2 expands to interpolation + | parent_place_id | start | end | + | W1 | 4 | 4 | + When updating places + | osm | class | type | housenr | + | N4 | place | house | 8 | + Then W2 expands to interpolation + | parent_place_id | start | end | + | W1 | 4 | 6 | + + Scenario: Legal interpolation type changed to illegal one + Given the grid + | 1 | | 2 | + | 3 | | 4 | + And the places + | osm | class | type | name | geometry | + | W1 | highway | unclassified | Cloud Street | 1, 2 | + And the ways + | id | nodes | + | 2 | 3,4 | + And the places + | osm | class | type | addr+interpolation | geometry | + | W2 | place | houses | even | 3,4 | + And the places + | osm | class | type | housenr | + | N3 | place | house | 2 | + | N4 | place | house | 6 | + When importing + Then W2 expands to interpolation + | parent_place_id | start | end | + | W1 | 4 | 4 | + When updating places + | osm | class | type | addr+interpolation | geometry | + | W2 | place | houses | 12-2 | 3,4 | + Then W2 expands to no interpolation + diff --git a/test/bdd/features/db/update/linked_places.feature b/test/bdd/features/db/update/linked_places.feature new file mode 100644 index 00000000..d622cbfb --- /dev/null +++ b/test/bdd/features/db/update/linked_places.feature @@ -0,0 +1,340 @@ +Feature: Updates of linked places + Tests that linked places are correctly added and deleted. + + Scenario: Linking is kept when boundary is updated + Given the 0.1 grid + | 10 | | 11 | + | | 1 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | + | N1 | place | city | foo | + And the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | + When importing + Then placex contains + | object | linked_place_id | + | N1 | R1 | + When updating places + | osm | class | type | name | name+name:de | admin | geometry | + | R1 | boundary | administrative | foo | Dingens | 8 | (10,11,12,13,10) | + Then placex contains + | object | linked_place_id | + | N1 | R1 | + + + Scenario: Add linked place when linking relation is renamed + Given the 0.1 grid + | 10 | | 11 | + | | 1 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | + | N1 | place | city | foo | + And the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | + When importing + Then placex contains + | object | linked_place_id | + | N1 | R1 | + When geocoding "foo" + | dups | + | 1 | + Then all results contain + | object | + | R1 | + When updating places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foobar | 8 | (10,11,12,13,10) | + Then placex contains + | object | linked_place_id | + | N1 | - | + When geocoding "foo" + | dups | + | 1 | + Then all results contain + | object | + | N1 | + + Scenario: Add linked place when linking relation is removed + Given the 0.1 grid + | 10 | | 11 | + | | 1 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | + | N1 | place | city | foo | + And the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | + When importing + And geocoding "foo" + | dups | + | 1 | + Then all results contain + | object | + | R1 | + When marking for delete R1 + Then placex contains + | object | linked_place_id | + | N1 | - | + When geocoding "foo" + | dups | + | 1 | + Then all results contain + | object | + | N1 | + + Scenario: Remove linked place when linking relation is added + Given the 0.1 grid + | 10 | | 11 | + | | 1 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | + | N1 | place | city | foo | + When importing + And geocoding "foo" + | dups | + | 1 | + Then all results contain + | object | + | N1 | + When updating places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | + Then placex contains + | object | linked_place_id | + | N1 | R1 | + When geocoding "foo" + | dups | + | 1 | + Then all results contain + | object | + | R1 | + + Scenario: Remove linked place when linking relation is renamed + Given the 0.1 grid + | 10 | | 11 | + | | 1 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | + | N1 | place | city | foo | + And the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foobar | 8 | (10,11,12,13,10) | + When importing + And geocoding "foo" + | dups | + | 1 | + Then all results contain + | object | + | N1 | + When updating places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | + Then placex contains + | object | linked_place_id | + | N1 | R1 | + When geocoding "foo" + | dups | + | 1 | + Then all results contain + | object | + | R1 | + + Scenario: Update linking relation when linkee name is updated + Given the 0.1 grid + | 10 | | 11 | + | | 3 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | rel | 8 | (10,11,12,13,10) | + And the places + | osm | class | type | name+name:de | + | N3 | place | city | greeny | + And the relations + | id | members | + | 1 | N3:label | + When importing + Then placex contains + | object | linked_place_id | name+_place_name:de | + | R1 | - | greeny | + And placex contains + | object | linked_place_id | name+name:de | + | N3 | R1 | greeny | + When updating places + | osm | class | type | name+name:de | + | N3 | place | city | newname | + Then placex contains + | object | linked_place_id | name+name:de | + | N3 | R1 | newname | + And placex contains + | object | linked_place_id | name+_place_name:de | + | R1 | - | newname | + + Scenario: Update linking relation when linkee name is deleted + Given the 0.1 grid + | 10 | | 11 | + | | 3 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | rel | 8 | (10,11,12,13,10) | + And the places + | osm | class | type | name | + | N3 | place | city | greeny | + And the relations + | id | members | + | 1 | N3:label | + When importing + Then placex contains + | object | linked_place_id | name+_place_name | name+name | + | R1 | - | greeny | rel | + And placex contains + | object | linked_place_id | name+name | + | N3 | R1 | greeny | + When geocoding "greeny" + Then all results contain + | object | + | R1 | + When updating places + | osm | class | type | name+name:de | + | N3 | place | city | depnt | + Then placex contains + | object | linked_place_id | name+name:de | + | N3 | R1 | depnt | + And placex contains + | object | linked_place_id | name+_place_name:de | name+name | + | R1 | - | depnt | rel | + When geocoding "greeny" + Then exactly 0 results are returned + + Scenario: Updating linkee extratags keeps linker's extratags + Given the 0.1 grid + | 10 | | 11 | + | | 3 | | + | 13 | | 12 | + Given the named places + | osm | class | type | extra+wikidata | admin | geometry | + | R1 | boundary | administrative | 34 | 8 | (10,11,12,13,10) | + And the named places + | osm | class | type | + | N3 | place | city | + And the relations + | id | members | + | 1 | N3:label | + When importing + Then placex contains + | object | extratags!dict | + | R1 | 'wikidata' : '34', 'linked_place' : 'city' | + When updating places + | osm | class | type | name | extra+oneway | + | N3 | place | city | newname | yes | + Then placex contains + | object | extratags!dict | + | R1 | 'wikidata' : '34', 'oneway' : 'yes', 'linked_place' : 'city' | + + Scenario: Remove linked_place info when linkee is removed + Given the 0.1 grid + | 10 | | 11 | + | | 1 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | + | N1 | place | city | foo | + And the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | + When importing + Then placex contains + | object | extratags!dict | + | R1 | 'linked_place' : 'city' | + When marking for delete N1 + Then placex contains + | object | extratags | + | R1 | - | + + Scenario: Update linked_place info when linkee type changes + Given the 0.1 grid + | 10 | | 11 | + | | 1 | | + | 13 | | 12 | + Given the places + | osm | class | type | name | + | N1 | place | city | foo | + And the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foo | 8 | (10,11,12,13,10) | + When importing + Then placex contains + | object | extratags!dict | + | R1 | 'linked_place' : 'city' | + When updating places + | osm | class | type | name | + | N1 | place | town | foo | + Then placex contains + | object | extratags!dict | + | R1 | 'linked_place' : 'town' | + + + Scenario: Keep linking and ranks when place type changes + Given the grid + | 1 | | | 2 | + | | | 9 | | + | 4 | | | 3 | + And the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foo | 8 | (1,2,3,4,1) | + And the places + | osm | class | type | name | geometry | + | N1 | place | city | foo | 9 | + When importing + Then placex contains + | object | linked_place_id | rank_address | + | N1 | R1 | 16 | + | R1 | - | 16 | + + When updating places + | osm | class | type | name | geometry | + | N1 | place | town | foo | 9 | + Then placex contains + | object | linked_place_id | rank_address | + | N1 | R1 | 16 | + | R1 | - | 16 | + + + Scenario: Invalidate surrounding place nodes when place type changes + Given the grid + | 1 | | | 2 | + | | 8 | 9 | | + | 4 | | | 3 | + And the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | foo | 8 | (1,2,3,4,1) | + And the places + | osm | class | type | name | geometry | + | N1 | place | town | foo | 9 | + | N2 | place | city | bar | 8 | + And the relations + | id | members | + | 1 | N1:label | + When importing + Then placex contains + | object | linked_place_id | rank_address | + | N1 | R1 | 16 | + | R1 | - | 16 | + | N2 | - | 18 | + + When updating places + | osm | class | type | name | geometry | + | N1 | place | suburb | foo | 9 | + Then placex contains + | object | linked_place_id | rank_address | + | N1 | R1 | 20 | + | R1 | - | 20 | + | N2 | - | 16 | diff --git a/test/bdd/features/db/update/naming.feature b/test/bdd/features/db/update/naming.feature new file mode 100644 index 00000000..2912a7da --- /dev/null +++ b/test/bdd/features/db/update/naming.feature @@ -0,0 +1,20 @@ +Feature: Update of names in place objects + Test all naming related issues in updates + + Scenario: Delete postcode from postcode boundaries without ref + Given the grid with origin DE + | 1 | 2 | + | 4 | 3 | + Given the places + | osm | class | type | postcode | geometry | + | R1 | boundary | postal_code | 123-45 | (1,2,3,4,1) | + When importing + And geocoding "123-45" + Then result 0 contains + | object | + | R1 | + When updating places + | osm | class | type | geometry | + | R1 | boundary | postal_code | (1,2,3,4,1) | + Then placex has no entry for R1 + diff --git a/test/bdd/features/db/update/parenting.feature b/test/bdd/features/db/update/parenting.feature new file mode 100644 index 00000000..28f74cbe --- /dev/null +++ b/test/bdd/features/db/update/parenting.feature @@ -0,0 +1,163 @@ +Feature: Update parenting of objects + + Scenario: POI inside building inherits addr:street change + Given the grid + | 10 | | | | | | | 11 | + | | | 5 | | | 6 | | | + | | | | | | | | | + | | | | | 1 | | | | + | 12 | | 8 | | | 7 | | | + And the named places + | osm | class | type | + | N1 | amenity | bank | + And the places + | osm | class | type | street | housenr | geometry | + | W1 | building | yes | nowhere | 3 | (5,6,7,8,5) | + And the places + | osm | class | type | name | geometry | + | W2 | highway | primary | bar | 10,11 | + | W3 | highway | residential | foo | 10,12 | + When importing + Then placex contains + | object | parent_place_id | housenumber | + | W1 | W2 | 3 | + | N1 | W2 | 3 | + When updating places + | osm | class | type | street | addr_place | housenr | geometry | + | W1 | building | yes | foo | nowhere | 3 | (5,6,7,8,5) | + And updating places + | osm | class | type | name | + | N1 | amenity | bank | well | + Then placex contains + | object | parent_place_id | housenumber | + | W1 | W3 | 3 | + | N1 | W3 | 3 | + + + Scenario: Housenumber is reparented when street gets name matching addr:street + Given the grid + | 1 | | | 2 | + | | 10 | | | + | | | | | + | 3 | | | 4 | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | A street | 1,2 | + | W2 | highway | residential | B street | 3,4 | + And the places + | osm | class | type | housenr | street | geometry | + | N1 | building | yes | 3 | X street | 10 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + When updating places + | osm | class | type | name | geometry | + | W2 | highway | residential | X street | 3,4 | + Then placex contains + | object | parent_place_id | + | N1 | W2 | + + + Scenario: Housenumber is reparented when street looses name matching addr:street + Given the grid + | 1 | | | 2 | + | | 10 | | | + | | | | | + | 3 | | | 4 | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | A street | 1,2 | + | W2 | highway | residential | X street | 3,4 | + And the places + | osm | class | type | housenr | street | geometry | + | N1 | building | yes | 3 | X street | 10 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W2 | + When updating places + | osm | class | type | name | geometry | + | W2 | highway | residential | B street | 3,4 | + Then placex contains + | object | parent_place_id | + | N1 | W1 | + + + Scenario: Housenumber is reparented when street gets name matching addr:street + Given the grid + | 1 | | | 2 | + | | 10 | | | + | | | | | + | 3 | | | 4 | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | A street | 1,2 | + | W2 | highway | residential | B street | 3,4 | + And the places + | osm | class | type | housenr | street | geometry | + | N1 | building | yes | 3 | X street | 10 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | W1 | + When updating places + | osm | class | type | name | geometry | + | W2 | highway | residential | X street | 3,4 | + Then placex contains + | object | parent_place_id | + | N1 | W2 | + + + # Invalidation of geometries currently disabled for addr:place matches. + @skip + Scenario: Housenumber is reparented when place is renamed to matching addr:place + Given the grid + | 1 | | | 2 | + | | 10 | 4 | | + | | | | | + | | | 5 | | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | A street | 1,2 | + | N5 | place | village | Bdorf | 5 | + | N4 | place | village | Other | 4 | + And the places + | osm | class | type | housenr | addr_place | geometry | + | N1 | building | yes | 3 | Cdorf | 10 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | N4 | + When updating places + | osm | class | type | name | geometry | + | N5 | place | village | Cdorf | 5 | + Then placex contains + | object | parent_place_id | + | N1 | N5 | + + + Scenario: Housenumber is reparented when it looses a matching addr:place + Given the grid + | 1 | | | 2 | + | | 10 | 4 | | + | | | | | + | | | 5 | | + And the places + | osm | class | type | name | geometry | + | W1 | highway | residential | A street | 1,2 | + | N5 | place | village | Bdorf | 5 | + | N4 | place | village | Other | 4 | + And the places + | osm | class | type | housenr | addr_place | geometry | + | N1 | building | yes | 3 | Bdorf | 10 | + When importing + Then placex contains + | object | parent_place_id | + | N1 | N5 | + When updating places + | osm | class | type | name | geometry | + | N5 | place | village | Cdorf | 5 | + Then placex contains + | object | parent_place_id | + | N1 | N4 | diff --git a/test/bdd/features/db/update/postcode.feature b/test/bdd/features/db/update/postcode.feature new file mode 100644 index 00000000..d62953e7 --- /dev/null +++ b/test/bdd/features/db/update/postcode.feature @@ -0,0 +1,139 @@ +Feature: Update of postcode + Tests for updating of data related to postcodes + + Scenario: Updating postcode in postcode boundaries without ref + Given the grid + | 1 | 2 | + | 4 | 3 | + Given the places + | osm | class | type | postcode | geometry | + | R1 | boundary | postal_code | 12345 | (1,2,3,4,1) | + When importing + And geocoding "12345" + Then result 0 contains + | object | + | R1 | + When updating places + | osm | class | type | postcode | geometry | + | R1 | boundary | postal_code | 54321 | (1,2,3,4,1) | + And geocoding "12345" + Then exactly 0 results are returned + When geocoding "54321" + Then result 0 contains + | object | + | R1 | + + Scenario: A new postcode appears in the postcode table + Given the places + | osm | class | type | addr+postcode | addr+housenumber | geometry | + | N34 | place | house | 01982 | 111 | country:de | + When importing + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt | + | de | 01982 | country:de | + When updating places + | osm | class | type | addr+postcode | addr+housenumber | geometry | + | N35 | place | house | 4567 | 5 | country:ch | + And updating postcodes + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt | + | de | 01982 | country:de | + | ch | 4567 | country:ch | + + Scenario: When the last postcode is deleted, it is deleted from postcode + Given the places + | osm | class | type | addr+postcode | addr+housenumber | geometry | + | N34 | place | house | 01982 | 111 | country:de | + | N35 | place | house | 4567 | 5 | country:ch | + When importing + And marking for delete N34 + And updating postcodes + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt | + | ch | 4567 | country:ch | + + Scenario: A postcode is not deleted from postcode when it exist in another country + Given the places + | osm | class | type | addr+postcode | addr+housenumber | geometry | + | N34 | place | house | 01982 | 111 | country:de | + | N35 | place | house | 01982 | 5 | country:fr | + When importing + And marking for delete N34 + And updating postcodes + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt| + | fr | 01982 | country:fr | + + Scenario: Updating a postcode is reflected in postcode table + Given the places + | osm | class | type | addr+postcode | geometry | + | N34 | place | postcode | 01982 | country:de | + When importing + And updating places + | osm | class | type | addr+postcode | geometry | + | N34 | place | postcode | 20453 | country:de | + And updating postcodes + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt | + | de | 20453 | country:de | + + Scenario: When changing from a postcode type, the entry appears in placex + When importing + And updating places + | osm | class | type | addr+postcode | geometry | + | N34 | place | postcode | 01982 | country:de | + Then placex has no entry for N34 + When updating places + | osm | class | type | addr+postcode | housenr | geometry | + | N34 | place | house | 20453 | 1 | country:de | + Then placex contains + | object | addr+housenumber | geometry!wkt | + | N34 | 1 | country:de | + And place contains exactly + | osm_type | osm_id | class | type | + | N | 34 | place | house | + When updating postcodes + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt | + | de | 20453 | country:de | + + Scenario: When changing to a postcode type, the entry disappears from placex + When importing + And updating places + | osm | class | type | addr+postcode | housenr | geometry | + | N34 | place | house | 20453 | 1 | country:de | + Then placex contains + | object | addr+housenumber | geometry!wkt | + | N34 | 1 | country:de| + When updating places + | osm | class | type | addr+postcode | geometry | + | N34 | place | postcode | 01982 | country:de | + Then placex has no entry for N34 + And place contains exactly + | osm_type | osm_id | class | type | + | N | 34 | place | postcode | + When updating postcodes + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt | + | de | 01982 | country:de | + + Scenario: When a parent is deleted, the postcode gets a new parent + Given the grid with origin DE + | 1 | | 3 | 4 | + | | 9 | | | + | 2 | | 5 | 6 | + Given the places + | osm | class | type | name | admin | geometry | + | R1 | boundary | administrative | Big | 6 | (1,4,6,2,1) | + | R2 | boundary | administrative | Small | 6 | (1,3,5,2,1) | + Given the places + | osm | class | type | addr+postcode | geometry | + | N9 | place | postcode | 12345 | 9 | + When importing + Then location_postcode contains exactly + | postcode | geometry!wkt | parent_place_id | + | 12345 | 9 | R2 | + When marking for delete R2 + Then location_postcode contains exactly + | country_code | postcode | geometry!wkt | parent_place_id | + | de | 12345 | 9 | R1 | diff --git a/test/bdd/features/db/update/simple.feature b/test/bdd/features/db/update/simple.feature new file mode 100644 index 00000000..22165c2f --- /dev/null +++ b/test/bdd/features/db/update/simple.feature @@ -0,0 +1,117 @@ +Feature: Update of simple objects + Testing simple updating functionality + + Scenario: Do delete small boundary features + Given the 1.0 grid + | 1 | 2 | + | 4 | 3 | + Given the places + | osm | class | type | admin | geometry | + | R1 | boundary | administrative | 3 | (1,2,3,4,1) | + When importing + Then placex contains + | object | rank_search | + | R1 | 6 | + When marking for delete R1 + Then placex has no entry for R1 + + Scenario: Do not delete large boundary features + Given the 2.0 grid + | 1 | 2 | + | 4 | 3 | + Given the places + | osm | class | type | admin | geometry | + | R1 | boundary | administrative | 3 | (1,2,3,4,1) | + When importing + Then placex contains + | object | rank_search | + | R1 | 6 | + When marking for delete R1 + Then placex contains + | object | rank_search | + | R1 | 6 | + + Scenario: Do delete large features of low rank + Given the 2.0 grid + | 1 | 2 | + | 4 | 3 | + Given the named places + | osm | class | type | geometry | + | W1 | place | house | (1,2,3,4,1) | + | R1 | natural | wood | (1,2,3,4,1) | + | R2 | highway | residential | (1,2,3,4,1) | + When importing + Then placex contains + | object | rank_address | + | R1 | 0 | + | R2 | 26 | + | W1 | 30 | + When marking for delete R1 + And marking for delete R2 + And marking for delete W1 + Then placex has no entry for W1 + Then placex has no entry for R1 + Then placex has no entry for R2 + + Scenario: type mutation + Given the places + | osm | class | type | geometry | + | N3 | shop | toys | 1 -1 | + When importing + Then placex contains + | object | class | type | centroid!wkt | + | N3 | shop | toys | 1 -1 | + When updating places + | osm | class | type | geometry | + | N3 | shop | grocery | 1 -1 | + Then placex contains + | object | class | type | centroid!wkt | + | N3 | shop | grocery | 1 -1 | + + Scenario: remove postcode place when house number is added + Given the places + | osm | class | type | postcode | geometry | + | N3 | place | postcode | 12345 | country:de | + When importing + Then placex has no entry for N3 + When updating places + | osm | class | type | postcode | housenr | geometry | + | N3 | place | house | 12345 | 13 | country:de | + Then placex contains + | object | class | type | + | N3 | place | house | + + Scenario: remove boundary when changing from polygon to way + Given the grid + | 1 | 2 | + | 3 | 4 | + And the places + | osm | class | type | name | admin | geometry | + | W1 | boundary | administrative | Haha | 5 | (1, 2, 4, 3, 1) | + When importing + Then placex contains + | object | + | W1 | + When updating places + | osm | class | type | name | admin | geometry | + | W1 | boundary | administrative | Haha | 5 | 1, 2, 4, 3 | + Then placex has no entry for W1 + + #895 + Scenario: update rank when boundary is downgraded from admin to historic + Given the grid + | 1 | 2 | + | 3 | 4 | + And the places + | osm | class | type | name | admin | geometry | + | W1 | boundary | administrative | Haha | 5 | (1, 2, 4, 3, 1) | + When importing + Then placex contains + | object | rank_address | + | W1 | 10 | + When updating places + | osm | class | type | name | admin | geometry | + | W1 | boundary | historic | Haha | 5 | (1, 2, 4, 3, 1) | + Then placex contains + | object | rank_address | + | W1 | 0 | diff --git a/test/bdd/test_db.py b/test/bdd/test_db.py new file mode 100644 index 00000000..0e8d137d --- /dev/null +++ b/test/bdd/test_db.py @@ -0,0 +1,245 @@ +# 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 import acceptance tests. + +These tests check the Nominatim import chain after the osm2pgsql import. +""" +import asyncio +import re +from pathlib import Path + +import psycopg + +import pytest +from pytest_bdd import scenarios, when, then, given +from pytest_bdd.parsers import re as step_parse + +from utils.place_inserter import PlaceColumn +from utils.checks import check_table_content + +from nominatim_db.config import Configuration +from nominatim_db import cli +from nominatim_db.tools.database_import import load_data, create_table_triggers +from nominatim_db.tools.postcodes import update_postcodes +from nominatim_db.tokenizer import factory as tokenizer_factory + + +def _rewrite_placeid_field(field, new_field, datatable, place_ids): + try: + oidx = datatable[0].index(field) + datatable[0][oidx] = new_field + for line in datatable[1:]: + line[oidx] = None if line[oidx] == '-' else place_ids[line[oidx]] + except ValueError: + pass + + +def _collect_place_ids(conn): + pids = {} + with conn.cursor() as cur: + for row in cur.execute('SELECT place_id, osm_type, osm_id, class FROM placex'): + pids[f"{row[1]}{row[2]}"] = row[0] + pids[f"{row[1]}{row[2]}:{row[3]}"] = row[0] + + return pids + + +@pytest.fixture +def test_config_env(pytestconfig): + dbname = pytestconfig.getini('nominatim_test_db') + + config = Configuration(None).get_os_env() + config['NOMINATIM_DATABASE_DSN'] = f"pgsql:dbname={dbname}" + config['NOMINATIM_LANGUAGES'] = 'en,de,fr,ja' + config['NOMINATIM_USE_US_TIGER_DATA'] = 'yes' + if pytestconfig.option.NOMINATIM_TOKENIZER is not None: + config['NOMINATIM_TOKENIZER'] = pytestconfig.option.NOMINATIM_TOKENIZER + + return config + + +@pytest.fixture +def update_config(def_config): + """ Prepare the database for being updatable and return the config. + """ + cli.nominatim(['refresh', '--functions'], def_config.environ) + + return def_config + + +@given(step_parse('the (?Pnamed )?places'), target_fixture=None) +def import_places(db_conn, named, datatable, node_grid): + """ Insert todo rows into the place table. + When 'named' is given, then a random name will be generated for all + objects. + """ + with db_conn.cursor() as cur: + for row in datatable[1:]: + PlaceColumn(node_grid).add_row(datatable[0], row, named is not None).db_insert(cur) + + +@given('the ways', target_fixture=None) +def import_ways(db_conn, datatable): + """ Import raw ways into the osm2pgsql way middle table. + """ + with db_conn.cursor() as cur: + id_idx = datatable[0].index('id') + node_idx = datatable[0].index('nodes') + for line in datatable[1:]: + tags = psycopg.types.json.Json( + {k[5:]: v for k, v in zip(datatable[0], line) + if k.startswith("tags+")}) + nodes = [int(x) for x in line[node_idx].split(',')] + + cur.execute("INSERT INTO planet_osm_ways (id, nodes, tags) VALUES (%s, %s, %s)", + (line[id_idx], nodes, tags)) + + +@given('the relations', target_fixture=None) +def import_rels(db_conn, datatable): + """ Import raw relations into the osm2pgsql relation middle table. + """ + with db_conn.cursor() as cur: + id_idx = datatable[0].index('id') + memb_idx = datatable[0].index('members') + for line in datatable[1:]: + tags = psycopg.types.json.Json( + {k[5:]: v for k, v in zip(datatable[0], line) + if k.startswith("tags+")}) + members = [] + if line[memb_idx]: + for member in line[memb_idx].split(','): + m = re.fullmatch(r'\s*([RWN])(\d+)(?::(\S+))?\s*', member) + if not m: + raise ValueError(f'Illegal member {member}.') + members.append({'ref': int(m[2]), 'role': m[3] or '', 'type': m[1]}) + + cur.execute('INSERT INTO planet_osm_rels (id, tags, members) VALUES (%s, %s, %s)', + (int(line[id_idx]), tags, psycopg.types.json.Json(members))) + + +@when('importing', target_fixture='place_ids') +def do_import(db_conn, def_config): + """ Run a reduced version of the Nominatim import. + """ + create_table_triggers(db_conn, def_config) + asyncio.run(load_data(def_config.get_libpq_dsn(), 1)) + tokenizer = tokenizer_factory.get_tokenizer_for_db(def_config) + update_postcodes(def_config.get_libpq_dsn(), Path('/xxxx'), tokenizer) + cli.nominatim(['index', '-q'], def_config.environ) + + return _collect_place_ids(db_conn) + + +@when('updating places', target_fixture='place_ids') +def do_update(db_conn, update_config, node_grid, datatable): + """ Update the place table with the given data. Also runs all triggers + related to updates and reindexes the new data. + """ + with db_conn.cursor() as cur: + for row in datatable[1:]: + PlaceColumn(node_grid).add_row(datatable[0], row, False).db_insert(cur) + cur.execute('SELECT flush_deleted_places()') + db_conn.commit() + + cli.nominatim(['index', '-q'], update_config.environ) + + return _collect_place_ids(db_conn) + + +@when('updating postcodes') +def do_postcode_update(update_config): + """ Recompute the postcode centroids. + """ + cli.nominatim(['refresh', '--postcodes'], update_config.environ) + + +@when(step_parse(r'marking for delete (?P[NRW])(?P\d+)'), + converters={'oid': int}) +def do_delete_place(db_conn, update_config, node_grid, otype, oid): + """ Remove the given place from the database. + """ + with db_conn.cursor() as cur: + cur.execute('TRUNCATE place_to_be_deleted') + cur.execute('DELETE FROM place WHERE osm_type = %s and osm_id = %s', + (otype, oid)) + cur.execute('SELECT flush_deleted_places()') + db_conn.commit() + + cli.nominatim(['index', '-q'], update_config.environ) + + +@then(step_parse(r'(?P
\w+) contains(?P exactly)?')) +def then_check_table_content(db_conn, place_ids, datatable, node_grid, table, exact): + _rewrite_placeid_field('object', 'place_id', datatable, place_ids) + _rewrite_placeid_field('parent_place_id', 'parent_place_id', datatable, place_ids) + _rewrite_placeid_field('linked_place_id', 'linked_place_id', datatable, place_ids) + if table == 'place_addressline': + _rewrite_placeid_field('address', 'address_place_id', datatable, place_ids) + + for i, title in enumerate(datatable[0]): + if title.startswith('addr+'): + datatable[0][i] = f"address+{title[5:]}" + + check_table_content(db_conn, table, datatable, grid=node_grid, exact=bool(exact)) + + +@then(step_parse(r'(DISABLED?P
placex?) has no entry for (?P[NRW]\d+(?::\S+)?)')) +def then_check_place_missing_lines(db_conn, place_ids, table, oid): + assert oid in place_ids + + sql = pysql.SQL("""SELECT count(*) FROM {} + WHERE place_id = %s""").format(pysql.Identifier(tablename)) + + with conn.cursor(row_factory=tuple_row) as cur: + assert cur.execute(sql, [place_ids[oid]]).fetchone()[0] == 0 + + +@then(step_parse(r'W(?P\d+) expands to interpolation'), + converters={'oid': int}) +def then_check_interpolation_table(db_conn, node_grid, place_ids, oid, datatable): + with db_conn.cursor() as cur: + cur.execute('SELECT count(*) FROM location_property_osmline WHERE osm_id = %s', + [oid]) + assert cur.fetchone()[0] == len(datatable) - 1 + + converted = [['osm_id', 'startnumber', 'endnumber', 'linegeo!wkt']] + start_idx = datatable[0].index('start') if 'start' in datatable[0] else None + end_idx = datatable[0].index('end') if 'end' in datatable[0] else None + geom_idx = datatable[0].index('geometry') if 'geometry' in datatable[0] else None + converted = [['osm_id']] + for val, col in zip((start_idx, end_idx, geom_idx), + ('startnumber', 'endnumber', 'linegeo!wkt')): + if val is not None: + converted[0].append(col) + + for line in datatable[1:]: + convline = [oid] + for val in (start_idx, end_idx): + if val is not None: + convline.append(line[val]) + if geom_idx is not None: + convline.append(line[geom_idx]) + converted.append(convline) + + _rewrite_placeid_field('parent_place_id', 'parent_place_id', converted, place_ids) + + check_table_content(db_conn, 'location_property_osmline', converted, grid=node_grid) + + +@then(step_parse(r'W(?P\d+) expands to no interpolation'), + converters={'oid': int}) +def then_check_interpolation_table_negative(db_conn, oid): + with db_conn.cursor() as cur: + cur.execute("""SELECT count(*) FROM location_property_osmline + WHERE osm_id = %s and startnumber is not null""", + [oid]) + assert cur.fetchone()[0] == 0 + + +scenarios('features/db') diff --git a/test/bdd/test_osm2pgsql.py b/test/bdd/test_osm2pgsql.py index dccedc4d..a2214b08 100644 --- a/test/bdd/test_osm2pgsql.py +++ b/test/bdd/test_osm2pgsql.py @@ -11,13 +11,16 @@ import asyncio import random import pytest -from pytest_bdd import scenarios, when, given +from pytest_bdd import scenarios, when, then, given +from pytest_bdd.parsers import re as step_parse from nominatim_db import cli 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.checks import check_table_content + @pytest.fixture def osm2pgsql_options(def_config): @@ -103,11 +106,4 @@ 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
placex?) has no entry for ' - r'(?P[NRW])(?P\d+)(?::(?P\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') diff --git a/test/bdd/utils/checks.py b/test/bdd/utils/checks.py index b6b0214d..592dad69 100644 --- a/test/bdd/utils/checks.py +++ b/test/bdd/utils/checks.py @@ -12,9 +12,10 @@ import re import math from psycopg import sql as pysql -from psycopg.rows import dict_row, tuple_row +from psycopg.rows import dict_row from .geometry_alias import ALIASES + COMPARATOR_TERMS = { 'exactly': lambda exp, act: exp == act, 'more than': lambda exp, act: act > exp, @@ -26,11 +27,19 @@ def _pretty(obj): return json.dumps(obj, sort_keys=True, indent=2) +def _pt_close(p1, p2): + return math.isclose(p1[0], p2[0], abs_tol=1e-07) \ + and math.isclose(p1[1], p2[1], abs_tol=1e-07) + + def within_box(value, expect): coord = [float(x) for x in expect.split(',')] if isinstance(value, str): - value = value.split(',') + if value.startswith('POINT'): + value = value[6:-1].split(' ') + else: + value = value.split(',') value = list(map(float, value)) if len(value) == 2: @@ -98,10 +107,10 @@ class ResultAttr: self.subobj = self.subobj[sub] def __eq__(self, other): - if not isinstance(other, str): - raise NotImplementedError() - # work around bad quoting by pytest-bdd + if not isinstance(other, str): + return self.subobj == other + other = other.replace(r'\\', '\\') if self.fmt in COMPARISON_FUNCS: @@ -148,18 +157,16 @@ class ResultAttr: for pt in map(str.strip, m[2].split(','))] if expected.startswith('country:'): - ccode = geom[8:].upper() + ccode = expected[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])) + return m[1] == 'POINT' and _pt_close(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))) + return m[1] == 'POINT' and _pt_close(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 + all(_pt_close(p1, p2) for p1, p2 in zip(converted, (self.get_point(p) for p in expected.split(',')))) if m[1] != 'POLYGON': @@ -174,7 +181,7 @@ class ResultAttr: "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 + if all(_pt_close(p1, p2) for p1, p2 in zip(converted, line[i:] + line[:i])): return True @@ -199,7 +206,7 @@ def check_table_content(conn, tablename, data, grid=None, exact=False): cols.extend(('osm_id', 'osm_type')) elif '!' in col: name, fmt = col.rsplit('!', 1) - if fmt == 'wkt': + if fmt in ('wkt', 'in_box'): cols.append(f"ST_AsText({name}) as {name}") else: cols.append(name.split('+')[0]) @@ -215,7 +222,7 @@ def check_table_content(conn, tablename, data, grid=None, exact=False): 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: + if ResultAttr(row, col, grid=grid) != (None if value == '-' else value): break else: lines.remove(i) @@ -228,15 +235,3 @@ def check_table_content(conn, tablename, data, grid=None, exact=False): + '\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 diff --git a/test/bdd/utils/db.py b/test/bdd/utils/db.py index 766137c5..805b55b2 100644 --- a/test/bdd/utils/db.py +++ b/test/bdd/utils/db.py @@ -13,7 +13,7 @@ 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.data.country_info import setup_country_tables, create_country_names 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 @@ -98,4 +98,5 @@ class DBManager: create_functions(conn, config, enable_diff_updates=False) asyncio.run(create_search_indices(conn, config)) - tokenizer_factory.create_tokenizer(config) + tokenizer = tokenizer_factory.create_tokenizer(config) + create_country_names(conn, tokenizer) diff --git a/test/bdd/utils/grid.py b/test/bdd/utils/grid.py index 1a3d6ae8..50355a1b 100644 --- a/test/bdd/utils/grid.py +++ b/test/bdd/utils/grid.py @@ -38,7 +38,7 @@ class Grid: """ value = value.strip() if ' ' in value: - return [int(v) for v in value.split(' ', 1)] + return [float(v) for v in value.split(' ', 1)] return self.grid.get(value) diff --git a/test/bdd/utils/place_inserter.py b/test/bdd/utils/place_inserter.py new file mode 100644 index 00000000..a330c3ac --- /dev/null +++ b/test/bdd/utils/place_inserter.py @@ -0,0 +1,143 @@ +# 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. +""" +Helper classes for filling the place table. +""" +import random +import string + +from .geometry_alias import ALIASES + + +class PlaceColumn: + """ Helper class to collect contents from a BDD table row and + insert it into the place table. + """ + def __init__(self, grid=None): + self.columns = {'admin_level': 15} + self.grid = grid + self.geometry = None + + def add_row(self, headings, row, force_name): + """ Parse the content from the given behave row as place column data. + """ + for name, value in zip(headings, row): + self._add(name, value) + + assert 'osm_type' in self.columns, "osm column missing" + + if force_name and 'name' not in self.columns: + self._add_hstore( + 'name', + 'name', + ''.join(random.choices(string.printable, k=random.randrange(30))), + ) + + return self + + def _add(self, key, value): + if hasattr(self, '_set_key_' + key): + getattr(self, '_set_key_' + key)(value) + elif key.startswith('name+'): + self._add_hstore('name', key[5:], value) + elif key.startswith('extra+'): + self._add_hstore('extratags', key[6:], value) + elif key.startswith('addr+'): + self._add_hstore('address', key[5:], value) + elif key in ('name', 'address', 'extratags'): + self.columns[key] = eval('{' + value + '}') + else: + assert key in ('class', 'type'), "Unknown column '{}'.".format(key) + self.columns[key] = None if value == '' else value + + def _set_key_name(self, value): + self._add_hstore('name', 'name', value) + + def _set_key_osm(self, value): + assert value[0] in 'NRW' and value[1:].isdigit(), \ + "OSM id needs to be of format ." + + self.columns['osm_type'] = value[0] + self.columns['osm_id'] = int(value[1:]) + + def _set_key_admin(self, value): + self.columns['admin_level'] = int(value) + + def _set_key_housenr(self, value): + if value: + self._add_hstore('address', 'housenumber', value) + + def _set_key_postcode(self, value): + if value: + self._add_hstore('address', 'postcode', value) + + def _set_key_street(self, value): + if value: + self._add_hstore('address', 'street', value) + + def _set_key_addr_place(self, value): + if value: + self._add_hstore('address', 'place', value) + + def _set_key_country(self, value): + if value: + self._add_hstore('address', 'country', value) + + def _set_key_geometry(self, value): + if value.startswith('country:'): + ccode = value[8:].upper() + self.geometry = "ST_SetSRID(ST_Point({}, {}), 4326)".format(*ALIASES[ccode]) + elif ',' not in value: + if self.grid: + pt = self.grid.parse_point(value) + else: + pt = value.split(' ') + self.geometry = f"ST_SetSRID(ST_Point({pt[0]}, {pt[1]}), 4326)" + elif '(' not in value: + if self.grid: + coords = ','.join(' '.join(f"{p:.7f}" for p in pt) + for pt in self.grid.parse_line(value)) + else: + coords = value + self.geometry = f"'srid=4326;LINESTRING({coords})'::geometry" + else: + if self.grid: + coords = ','.join(' '.join(f"{p:.7f}" for p in pt) + for pt in self.grid.parse_line(value[1:-1])) + else: + coords = value[1:-1] + self.geometry = f"'srid=4326;POLYGON(({coords}))'::geometry" + + def _add_hstore(self, column, key, value): + if column in self.columns: + self.columns[column][key] = value + else: + self.columns[column] = {key: value} + + def db_delete(self, cursor): + """ Issue a delete for the given OSM object. + """ + cursor.execute('DELETE FROM place WHERE osm_type = %s and osm_id = %s', + (self.columns['osm_type'], self.columns['osm_id'])) + + def db_insert(self, cursor): + """ Insert the collected data into the database. + """ + if self.columns['osm_type'] == 'N' and self.geometry is None: + pt = self.grid.get(str(self.columns['osm_id'])) if self.grid else None + if pt is None: + pt = (random.uniform(-180, 180), random.uniform(-90, 90)) + + self.geometry = "ST_SetSRID(ST_Point({}, {}), 4326)".format(*pt) + else: + assert self.geometry is not None, "Geometry missing" + + query = 'INSERT INTO place ({}, geometry) values({}, {})'.format( + ','.join(self.columns.keys()), + ','.join(['%s' for x in range(len(self.columns))]), + self.geometry) + cursor.execute(query, list(self.columns.values()))