processIsolation="false"
stopOnFailure="false"
syntaxCheck="true"
- bootstrap="tests-php/bootstrap.php"
+ bootstrap="test/php/bootstrap.php"
>
<php>
</php>
<testsuites>
<testsuite name="Nominatim PHP Test Suite">
- <directory>./tests-php/Nominatim</directory>
+ <directory>./test/php/Nominatim</directory>
</testsuite>
</testsuites>
<filter>
@define('CONST_Osm2pgsql_Binary', CONST_InstallPath.'/osm2pgsql/osm2pgsql');
@define('CONST_Osmosis_Binary', '/usr/bin/osmosis');
@define('CONST_Tiger_Data_Path', CONST_BasePath.'/data/tiger');
+@define('CONST_Wikipedia_Data_Path', CONST_BasePath.'/data');
// osm2pgsql settings
@define('CONST_Osm2pgsql_Flatnode_File', null);
--- /dev/null
+This directory contains functional and unit tests for the Nominatim API.
+
+Prerequisites
+=============
+
+ * Python 3 (https://www.python.org/)
+ * behave test framework >= 1.2.5 (https://github.com/behave/behave)
+ * nose (https://nose.readthedocs.org)
+ * pytidylib (http://countergram.com/open-source/pytidylib)
+ * psycopg2 (http://initd.org/psycopg/)
+
+To get the prerequisites on a a fresh Ubuntu LTS 16.04 run:
+
+ [sudo] apt-get install python3-dev python3-pip python3-psycopg2 python3-tidylib phpunit
+ pip3 install --user behave nose
+
+
+Overall structure
+=================
+
+There are two kind of tests in this test suite. There are functional tests
+which test the API interface using a BDD test framework and there are unit
+tests for specific PHP functions.
+
+This test directory is sturctured as follows:
+
+ -+- bdd Functional API tests
+ | \
+ | +- steps Step implementations for test descriptions
+ | +- osm2pgsql Tests for data import via osm2pgsql
+ | +- db Tests for internal data processing on import and update
+ | +- api Tests for API endpoints (search, reverse, etc.)
+ |
+ +- php PHP unit tests
+ +- scenes Geometry test data
+ +- testdb Base data for generating API test database
+
+
+PHP Unit Tests
+==============
+
+Unit tests can be found in the php/ directory and tests selected php functions.
+Very low coverage.
+
+To execute the test suite run
+
+ cd test/php
+ phpunit ../
+
+It will read phpunit.xml which points to the library, test path, bootstrap
+strip and set other parameters.
+
+
+BDD Functional Tests
+====================
+
+Functional tests are written as BDD instructions. For more information on
+the philosophy of BDD testing, see http://pythonhosted.org/behave/philosophy.html
+
+Usage
+-----
+
+To run the functional tests, do
+
+ cd test/bdd
+ behave
+
+The tests can be configured with a set of environment variables:
+
+ * `BUILD_DIR` - build directory of Nominatim installation to test
+ * `TEMPLATE_DB` - name of template database used as a skeleton for
+ the test databases (db tests)
+ * `TEST_DB` - name of test database (db tests)
+ * `ABI_TEST_DB` - name of the database containing the API test data (api tests)
+ * `TEST_SETTINGS_TEMPLATE` - file to write temporary Nominatim settings to
+ * `REMOVE_TEMPLATE` - if true, the template database will not be reused during
+ the next run. Reusing the base templates speeds up tests
+ considerably but might lead to outdated errors for some
+ changes in the database layout.
+ * `KEEP_TEST_DB` - if true, the test database will not be dropped after a test
+ is finished. Should only be used if one single scenario is
+ run, otherwise the result is undefined.
+
+Logging can be defined through command line parameters of behave itself. Check
+out `behave --help` for details. Also keep an eye out for the 'work-in-progress'
+feature of behave which comes in handy when writing new tests.
+
+Writing Tests
+-------------
+
+The following explanation assume that the reader is familiar with the BDD
+notations of features, scenarios and steps.
+
+All possible steps can be found in the `steps` directory and should ideally
+be documented.
+
+### API Tests (`test/bdd/api`)
+
+These tests are meant to test the different API endpoints and their parameters.
+They require a preimported test database, which consists of the import of a
+planet extract. The polygons defining the extract can be found in the test/testdb
+directory. There is also a reduced set of wikipedia data for this extract,
+which you need to import as well. For Tiger tests the data of South Dakota
+is required. Get the Tiger files `46*`.
+
+The official test dataset is derived from the 160725 planet. Newer
+planets are likely to work as well but you may see isolated test
+failures where the data has changed. To recreate the input data
+for the test database run:
+
+ wget http://free.nchc.org.tw/osm.planet/pbf/planet-160725.osm.pbf
+ osmconvert planet-160725.osm.pbf -B=test/testdb/testdb.polys -o=testdb.pbf
+
+Before importing make sure to add the following to your local settings:
+
+ @define('CONST_Database_DSN', 'pgsql://@/test_api_nominatim');
+ @define('CONST_Wikipedia_Data_Path', CONST_BasePath.'/test/testdb');
+
+### Indexing Tests (`test/bdd/db`)
+
+These tests check the import and update of the Nominatim database. They do not
+test the correctness of osm2pgsql. Each test will write some data into the `place`
+table (and optionally `the planet_osm_*` tables if required) and then run
+Nominatim's processing functions on that.
+
+These tests need to create their own test databases. By default they will be
+called `test_template_nominatim` and `test_nominatim`. Names can be changed with
+the environment variables `TEMPLATE_DB` and `TEST_DB`. The user running the tests
+needs superuser rights for postgres.
+
+### Import Tests (`test/bdd/osm2pgsql`)
+
+These tests check that data is imported correctly into the place table. They
+use the same template database as the Indexing tests, so the same remarks apply.
--- /dev/null
+@APIDB
+Feature: Object details
+ Check details page for correctness
+
+ Scenario Outline: Details via OSM id
+ When sending details query for <object>
+ Then the result is valid html
+
+ Examples:
+ | object |
+ | 492887 |
+ | N4267356889 |
+ | W230804120 |
+ | R123924 |
--- /dev/null
+@APIDB
+Feature: Places by osm_type and osm_id Tests
+ Simple tests for internal server errors and response format.
+
+ Scenario Outline: address lookup for existing node, way, relation
+ When sending <format> lookup query for N3284625766,W6065798,,R123924,X99,N0
+ Then the result is valid <format>
+ And exactly 3 results are returned
+
+ Examples:
+ | format |
+ | xml |
+ | json |
+
+ Scenario: address lookup for non-existing or invalid node, way, relation
+ When sending xml lookup query for X99,,N0,nN158845944,ABC,,W9
+ Then exactly 0 results are returned
--- /dev/null
+@APIDB
+Feature: Localization of reverse search results
+
+ Scenario: default language
+ When sending json reverse coordinates 18.1147,-15.95
+ Then result addresses contain
+ | ID | country |
+ | 0 | Mauritanie موريتانيا |
+
+ Scenario: accept-language parameter
+ When sending json reverse coordinates 18.1147,-15.95
+ | accept-language |
+ | en,fr |
+ Then result addresses contain
+ | ID | country |
+ | 0 | Mauritania |
+
+ Scenario: HTTP accept language header
+ Given the HTTP header
+ | accept-language |
+ | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3 |
+ When sending json reverse coordinates 18.1147,-15.95
+ Then result addresses contain
+ | ID | country |
+ | 0 | Mauritanie |
+
+ Scenario: accept-language parameter and HTTP header
+ Given the HTTP header
+ | accept-language |
+ | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3 |
+ When sending json reverse coordinates 18.1147,-15.95
+ | accept-language |
+ | en |
+ Then result addresses contain
+ | ID | country |
+ | 0 | Mauritania |
--- /dev/null
+@APIDB
+Feature: Parameters for Reverse API
+ Testing diferent parameter options for reverse API.
+
+ Scenario Outline: Reverse-geocoding without address
+ When sending <format> reverse coordinates 53.603,10.041
+ | addressdetails |
+ | 0 |
+ Then exactly 1 result is returned
+ And result has not attributes address
+
+ Examples:
+ | format |
+ | json |
+ | jsonv2 |
+ | xml |
+
+ Scenario Outline: Reverse Geocoding with extratags
+ When sending <format> reverse coordinates 10.776234290950017,106.70425325632095
+ | extratags |
+ | 1 |
+ Then result 0 has attributes extratags
+
+ Examples:
+ | format |
+ | xml |
+ | json |
+ | jsonv2 |
+
+ Scenario Outline: Reverse Geocoding with namedetails
+ When sending <format> reverse coordinates 10.776455623137625,106.70175343751907
+ | namedetails |
+ | 1 |
+ Then result 0 has attributes namedetails
+
+ Examples:
+ | format |
+ | xml |
+ | json |
+ | jsonv2 |
+
+ Scenario Outline: Reverse Geocoding contains TEXT geometry
+ When sending <format> reverse coordinates 47.165989816710066,9.515774846076965
+ | polygon_text |
+ | 1 |
+ Then result 0 has attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | geotext |
+ | json | geotext |
+ | jsonv2 | geotext |
+
+ Scenario Outline: Reverse Geocoding contains polygon-as-points geometry
+ When sending <format> reverse coordinates 47.165989816710066,9.515774846076965
+ | polygon |
+ | 1 |
+ Then result 0 has not attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | polygonpoints |
+ | json | polygonpoints |
+ | jsonv2 | polygonpoints |
+
+ Scenario Outline: Reverse Geocoding contains SVG geometry
+ When sending <format> reverse coordinates 47.165989816710066,9.515774846076965
+ | polygon_svg |
+ | 1 |
+ Then result 0 has attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | geosvg |
+ | json | svg |
+ | jsonv2 | svg |
+
+ Scenario Outline: Reverse Geocoding contains KML geometry
+ When sending <format> reverse coordinates 47.165989816710066,9.515774846076965
+ | polygon_kml |
+ | 1 |
+ Then result 0 has attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | geokml |
+ | json | geokml |
+ | jsonv2 | geokml |
+
+ Scenario Outline: Reverse Geocoding contains GEOJSON geometry
+ When sending <format> reverse coordinates 47.165989816710066,9.515774846076965
+ | polygon_geojson |
+ | 1 |
+ Then result 0 has attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | geojson |
+ | json | geojson |
+ | jsonv2 | geojson |
+
+
--- /dev/null
+@APIDB
+Feature: Reverse geocoding
+ Testing the reverse function
+
+ @Tiger
+ Scenario: TIGER house number
+ When sending jsonv2 reverse coordinates 45.3345,-97.5214
+ Then results contain
+ | osm_type | category | type |
+ | way | place | house |
+ And result addresses contain
+ | house_number | road | postcode | country_code |
+ | 906 | West 1st Street | 57274 | us |
+
+ @Tiger
+ Scenario: No TIGER house number for zoom < 18
+ When sending jsonv2 reverse coordinates 45.3345,-97.5214
+ | zoom |
+ | 17 |
+ Then results contain
+ | osm_type | category |
+ | way | highway |
+ And result addresses contain
+ | road | postcode | country_code |
+ | West 1st Street | 57274 | us |
--- /dev/null
+@APIDB
+Feature: Simple Reverse Tests
+ Simple tests for internal server errors and response format.
+
+ Scenario Outline: Simple reverse-geocoding
+ When sending reverse coordinates <lat>,<lon>
+ Then the result is valid xml
+ When sending xml reverse coordinates <lat>,<lon>
+ Then the result is valid xml
+ When sending json reverse coordinates <lat>,<lon>
+ Then the result is valid json
+ When sending jsonv2 reverse coordinates <lat>,<lon>
+ Then the result is valid json
+ When sending html reverse coordinates <lat>,<lon>
+ Then the result is valid html
+
+ Examples:
+ | lat | lon |
+ | 0.0 | 0.0 |
+ | -34.830 | -56.105 |
+ | 45.174 | -103.072 |
+ | 21.156 | -12.2744 |
+
+ Scenario Outline: Testing different parameters
+ When sending reverse coordinates 53.603,10.041
+ | param | value |
+ | <parameter> | <value> |
+ Then the result is valid xml
+ When sending html reverse coordinates 53.603,10.041
+ | param | value |
+ | <parameter> | <value> |
+ Then the result is valid html
+ When sending xml reverse coordinates 53.603,10.041
+ | param | value |
+ | <parameter> | <value> |
+ Then the result is valid xml
+ When sending json reverse coordinates 53.603,10.041
+ | param | value |
+ | <parameter> | <value> |
+ Then the result is valid json
+ When sending jsonv2 reverse coordinates 53.603,10.041
+ | param | value |
+ | <parameter> | <value> |
+ Then the result is valid json
+
+ Examples:
+ | parameter | value |
+ | polygon | 1 |
+ | polygon | 0 |
+ | polygon_text | 1 |
+ | polygon_text | 0 |
+ | polygon_kml | 1 |
+ | polygon_kml | 0 |
+ | polygon_geojson | 1 |
+ | polygon_geojson | 0 |
+ | polygon_svg | 1 |
+ | polygon_svg | 0 |
+
+ Scenario Outline: Wrapping of legal jsonp requests
+ When sending <format> reverse coordinates 67.3245,0.456
+ | json_callback |
+ | foo |
+ Then the result is valid json
+
+ Examples:
+ | format |
+ | json |
+ | jsonv2 |
+
+ @wip
+ Scenario Outline: Boundingbox is returned
+ When sending <format> reverse coordinates 14.62,108.1
+ | zoom |
+ | 4 |
+ Then result has bounding box in 9,20,102,113
+
+ Examples:
+ | format |
+ | json |
+ | jsonv2 |
+ | xml |
+
+ Scenario Outline: Reverse-geocoding with zoom
+ When sending <format> reverse coordinates 53.603,10.041
+ | zoom |
+ | 10 |
+ Then exactly 1 result is returned
+
+ Examples:
+ | format |
+ | json |
+ | jsonv2 |
+ | html |
+ | xml |
+
+ Scenario: Missing lon parameter
+ When sending reverse coordinates 52.52,
+ Then a HTTP 400 is returned
+
+ Scenario: Missing lat parameter
+ When sending reverse coordinates ,52.52
+ Then a HTTP 400 is returned
+
+ Scenario: Missing osm_id parameter
+ When sending reverse coordinates ,
+ | osm_type |
+ | N |
+ Then a HTTP 400 is returned
+
+ Scenario: Missing osm_type parameter
+ When sending reverse coordinates ,
+ | osm_id |
+ | 3498564 |
+ Then a HTTP 400 is returned
+
+ Scenario Outline: Bad format for lat or lon
+ When sending reverse coordinates ,
+ | lat | lon |
+ | <lat> | <lon> |
+ Then a HTTP 400 is returned
+
+ Examples:
+ | lat | lon |
+ | 48.9660 | 8,4482 |
+ | 48,9660 | 8.4482 |
+ | 48,9660 | 8,4482 |
+ | 48.966.0 | 8.4482 |
+ | 48.966 | 8.448.2 |
+ | Nan | 8.448 |
+ | 48.966 | Nan |
--- /dev/null
+@APIDB
+Feature: Localization of search results
+
+ Scenario: default language
+ When sending json search query "Vietnam"
+ Then results contain
+ | ID | display_name |
+ | 0 | Việt Nam |
+
+ Scenario: accept-language first
+ When sending json search query "Mauretanien"
+ | accept-language |
+ | en,de |
+ Then results contain
+ | ID | display_name |
+ | 0 | Mauritania |
+
+ Scenario: accept-language missing
+ When sending json search query "Mauretanien"
+ | accept-language |
+ | xx,fr,en,de |
+ Then results contain
+ | ID | display_name |
+ | 0 | Mauritanie |
+
+ Scenario: http accept language header first
+ Given the HTTP header
+ | accept-language |
+ | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3 |
+ When sending json search query "Mauretanien"
+ Then results contain
+ | ID | display_name |
+ | 0 | Mauritanie |
+
+ Scenario: http accept language header and accept-language
+ Given the HTTP header
+ | accept-language |
+ | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3 |
+ When sending json search query "Mauretanien"
+ | accept-language |
+ | de,en |
+ Then results contain
+ | ID | display_name |
+ | 0 | Mauretanien |
+
+ Scenario: http accept language header fallback
+ Given the HTTP header
+ | accept-language |
+ | fr-ca,en-ca;q=0.5 |
+ When sending json search query "Mauretanien"
+ Then results contain
+ | ID | display_name |
+ | 0 | Mauritanie |
+
+ Scenario: http accept language header fallback (upper case)
+ Given the HTTP header
+ | accept-language |
+ | fr-FR;q=0.8,en-ca;q=0.5 |
+ When sending json search query "Mauretanie"
+ Then results contain
+ | ID | display_name |
+ | 0 | Mauritanie |
--- /dev/null
+@APIDB
+Feature: Search queries
+ Testing different queries and parameters
+
+ Scenario: Simple XML search
+ When sending xml search query "Schaan"
+ Then result 0 has attributes place_id,osm_type,osm_id
+ And result 0 has attributes place_rank,boundingbox
+ And result 0 has attributes lat,lon,display_name
+ And result 0 has attributes class,type,importance,icon
+ And result 0 has not attributes address
+ And result 0 has bounding box in 46.5,47.5,9,10
+
+ Scenario: Simple JSON search
+ When sending json search query "Vaduz"
+ Then result 0 has attributes place_id,licence,icon,class,type
+ And result 0 has attributes osm_type,osm_id,boundingbox
+ And result 0 has attributes lat,lon,display_name,importance
+ And result 0 has not attributes address
+ And result 0 has bounding box in 46.5,47.5,9,10
+
+ Scenario: JSON search with addressdetails
+ When sending json search query "Montevideo" with address
+ Then address of result 0 is
+ | type | value |
+ | city | Montevideo |
+ | state | Montevideo |
+ | country | Uruguay |
+ | country_code | uy |
+
+ Scenario: XML search with addressdetails
+ When sending xml search query "Aleg" with address
+ | accept-language |
+ | en |
+ Then address of result 0 is
+ | type | value |
+ | city | Aleg |
+ | state | Brakna |
+ | country | Mauritania |
+ | country_code | mr |
+
+ Scenario: coordinate search with addressdetails
+ When sending json search query "14.271104294939,107.69828796387"
+ | accept-language |
+ | en |
+ Then results contain
+ | display_name |
+ | Plei Ya Rê, Kon Tum province, Vietnam |
+
+ Scenario: Address details with unknown class types
+ When sending json search query "Hundeauslauf, Hamburg" with address
+ Then results contain
+ | ID | class | type |
+ | 0 | leisure | dog_park |
+ And result addresses contain
+ | ID | address29 |
+ | 0 | Hundeauslauf |
+ And address of result 0 has no types leisure,dog_park
+
+ Scenario: Disabling deduplication
+ When sending json search query "Sievekingsallee, Hamburg"
+ Then there are no duplicates
+ When sending json search query "Sievekingsallee, Hamburg"
+ | dedupe |
+ | 0 |
+ Then there are duplicates
+
+ Scenario: Search with bounded viewbox in right area
+ When sending json search query "restaurant" with address
+ | bounded | viewbox |
+ | 1 | 9.93027,53.61634,10.10073,53.54500 |
+ Then result addresses contain
+ | state |
+ | Hamburg |
+
+ Scenario: Search with bounded viewboxlbrt in right area
+ When sending json search query "restaurant" with address
+ | bounded | viewboxlbrt |
+ | 1 | 9.93027,53.54500,10.10073,53.61634 |
+ Then result addresses contain
+ | state |
+ | Hamburg |
+
+ Scenario: No POI search with unbounded viewbox
+ When sending json search query "restaurant"
+ | viewbox |
+ | 9.93027,53.61634,10.10073,53.54500 |
+ Then results contain
+ | display_name |
+ | ^[^,]*[Rr]estaurant.* |
+
+ Scenario: bounded search remains within viewbox, even with no results
+ When sending json search query "restaurant"
+ | bounded | viewbox |
+ | 1 | 43.5403125,-5.6563282,43.54285,-5.662003 |
+ Then less than 1 result is returned
+
+ Scenario: bounded search remains within viewbox with results
+ When sending json search query "restaurant"
+ | bounded | viewbox |
+ | 1 | 9.93027,53.61634,10.10073,53.54500 |
+ Then result has bounding box in 53.54500,53.61634,9.93027,10.10073
+
+ Scenario: Prefer results within viewbox
+ When sending json search query "25 de Mayo" with address
+ | accept-language |
+ | en |
+ Then result addresses contain
+ | ID | state |
+ | 0 | Salto |
+ When sending json search query "25 de Mayo" with address
+ | accept-language | viewbox |
+ | en | -56.35879,-34.18330,-56.31618,-34.20815 |
+ Then result addresses contain
+ | ID | state |
+ | 0 | Florida |
+
+ Scenario: Overly large limit number for search results
+ When sending json search query "restaurant"
+ | limit |
+ | 1000 |
+ Then at most 50 results are returned
+
+ Scenario: Limit number of search results
+ When sending json search query "restaurant"
+ | limit |
+ | 4 |
+ Then exactly 4 results are returned
+
+ Scenario: Restrict to feature type country
+ When sending xml search query "Uruguay"
+ Then results contain
+ | ID | place_rank |
+ | 1 | 16 |
+ When sending xml search query "Uruguay"
+ | featureType |
+ | country |
+ Then results contain
+ | place_rank |
+ | 4 |
+
+ Scenario: Restrict to feature type state
+ When sending xml search query "Dakota"
+ Then results contain
+ | place_rank |
+ | 12 |
+ When sending xml search query "Dakota"
+ | featureType |
+ | state |
+ Then results contain
+ | place_rank |
+ | 8 |
+
+ Scenario: Restrict to feature type city
+ When sending xml search query "vaduz"
+ Then results contain
+ | ID | place_rank |
+ | 1 | 30 |
+ When sending xml search query "vaduz"
+ | featureType |
+ | city |
+ Then results contain
+ | place_rank |
+ | 16 |
+
+ Scenario: Restrict to feature type settlement
+ When sending json search query "Burg"
+ Then results contain
+ | ID | class |
+ | 1 | amenity |
+ When sending json search query "Burg"
+ | featureType |
+ | settlement |
+ Then results contain
+ | class | type |
+ | boundary | administrative |
+
+ Scenario Outline: Search with polygon threshold (json)
+ When sending json search query "switzerland"
+ | polygon_geojson | polygon_threshold |
+ | 1 | <th> |
+ Then at least 1 result is returned
+ And result 0 has attributes geojson
+
+ Examples:
+ | th |
+ | -1 |
+ | 0.0 |
+ | 0.5 |
+ | 999 |
+
+ Scenario Outline: Search with polygon threshold (xml)
+ When sending xml search query "switzerland"
+ | polygon_geojson | polygon_threshold |
+ | 1 | <th> |
+ Then at least 1 result is returned
+ And result 0 has attributes geojson
+
+ Examples:
+ | th |
+ | -1 |
+ | 0.0 |
+ | 0.5 |
+ | 999 |
+
+ Scenario Outline: Search with invalid polygon threshold (xml)
+ When sending xml search query "switzerland"
+ | polygon_geojson | polygon_threshold |
+ | 1 | <th> |
+ Then a HTTP 400 is returned
+
+ Examples:
+ | th |
+ | x |
+ | ;; |
+ | 1m |
+
+ Scenario Outline: Search with extratags
+ When sending <format> search query "Hauptstr"
+ | extratags |
+ | 1 |
+ Then result has attributes extratags
+
+ Examples:
+ | format |
+ | xml |
+ | json |
+ | jsonv2 |
+
+ Scenario Outline: Search with namedetails
+ When sending <format> search query "Hauptstr"
+ | namedetails |
+ | 1 |
+ Then result has attributes namedetails
+
+ Examples:
+ | format |
+ | xml |
+ | json |
+ | jsonv2 |
+
+ Scenario Outline: Search result with contains TEXT geometry
+ When sending <format> search query "Highmore"
+ | polygon_text |
+ | 1 |
+ Then result has attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | geotext |
+ | json | geotext |
+ | jsonv2 | geotext |
+
+ Scenario Outline: Search result contains polygon-as-points geometry
+ When sending <format> search query "Highmore"
+ | polygon |
+ | 1 |
+ Then result has attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | polygonpoints |
+ | json | polygonpoints |
+ | jsonv2 | polygonpoints |
+
+ Scenario Outline: Search result contains SVG geometry
+ When sending <format> search query "Highmore"
+ | polygon_svg |
+ | 1 |
+ Then result has attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | geosvg |
+ | json | svg |
+ | jsonv2 | svg |
+
+ Scenario Outline: Search result contains KML geometry
+ When sending <format> search query "Highmore"
+ | polygon_kml |
+ | 1 |
+ Then result has attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | geokml |
+ | json | geokml |
+ | jsonv2 | geokml |
+
+ Scenario Outline: Search result contains GEOJSON geometry
+ When sending <format> search query "Highmore"
+ | polygon_geojson |
+ | 1 |
+ Then result has attributes <response_attribute>
+
+ Examples:
+ | format | response_attribute |
+ | xml | geojson |
+ | json | geojson |
+ | jsonv2 | geojson |
--- /dev/null
+@APIDB
+Feature: Search queries
+ Generic search result correctness
+
+ Scenario: House number search for non-street address
+ When sending json search query "2 Steinwald, Austria" with address
+ | accept-language |
+ | en |
+ Then address of result 0 is
+ | type | value |
+ | house_number | 2 |
+ | hamlet | Steinwald |
+ | postcode | 6811 |
+ | country | Austria |
+ | country_code | at |
+
+ Scenario: House number interpolation even
+ When sending json search query "Schellingstr 86, Hamburg" with address
+ | accept-language |
+ | de |
+ Then address of result 0 is
+ | type | value |
+ | house_number | 86 |
+ | road | Schellingstraße |
+ | suburb | Eilbek |
+ | postcode | 22089 |
+ | city_district | Wandsbek |
+ | state | Hamburg |
+ | country | Deutschland |
+ | country_code | de |
+
+ Scenario: House number interpolation odd
+ When sending json search query "Schellingstr 73, Hamburg" with address
+ | accept-language |
+ | de |
+ Then address of result 0 is
+ | type | value |
+ | house_number | 73 |
+ | road | Schellingstraße |
+ | suburb | Eilbek |
+ | postcode | 22089 |
+ | city_district | Wandsbek |
+ | state | Hamburg |
+ | country | Deutschland |
+ | country_code | de |
+
+ @Tiger
+ Scenario: TIGER house number
+ When sending json search query "323 22nd Street Southwest, Huron"
+ Then results contain
+ | osm_type |
+ | way |
+
+ Scenario: Search with class-type feature
+ When sending jsonv2 search query "Hotel California"
+ Then results contain
+ | place_rank |
+ | 30 |
+
+ # https://trac.openstreetmap.org/ticket/5094
+ Scenario: housenumbers are ordered by complete match first
+ When sending json search query "6395 geminis, montevideo" with address
+ Then result addresses contain
+ | ID | house_number |
+ | 0 | 6395 |
+ | 1 | 6395 BIS |
+
--- /dev/null
+@APIDB
+Feature: Simple Tests
+ Simple tests for internal server errors and response format.
+
+ Scenario Outline: Testing different parameters
+ When sending search query "Hamburg"
+ | param | value |
+ | <parameter> | <value> |
+ Then at least 1 result is returned
+ When sending html search query "Hamburg"
+ | param | value |
+ | <parameter> | <value> |
+ Then at least 1 result is returned
+ When sending xml search query "Hamburg"
+ | param | value |
+ | <parameter> | <value> |
+ Then at least 1 result is returned
+ When sending json search query "Hamburg"
+ | param | value |
+ | <parameter> | <value> |
+ Then at least 1 result is returned
+ When sending jsonv2 search query "Hamburg"
+ | param | value |
+ | <parameter> | <value> |
+ Then at least 1 result is returned
+
+ Examples:
+ | parameter | value |
+ | addressdetails | 1 |
+ | addressdetails | 0 |
+ | polygon | 1 |
+ | polygon | 0 |
+ | polygon_text | 1 |
+ | polygon_text | 0 |
+ | polygon_kml | 1 |
+ | polygon_kml | 0 |
+ | polygon_geojson | 1 |
+ | polygon_geojson | 0 |
+ | polygon_svg | 1 |
+ | polygon_svg | 0 |
+ | accept-language | de,en |
+ | countrycodes | de |
+ | bounded | 1 |
+ | bounded | 0 |
+ | exclude_place_ids| 385252,1234515 |
+ | limit | 1000 |
+ | dedupe | 1 |
+ | dedupe | 0 |
+ | extratags | 1 |
+ | extratags | 0 |
+ | namedetails | 1 |
+ | namedetails | 0 |
+
+ Scenario: Search with invalid output format
+ When sending search query "Berlin"
+ | format |
+ | fd$# |
+ Then a HTTP 400 is returned
+
+ Scenario Outline: Simple Searches
+ When sending search query "<query>"
+ Then the result is valid html
+ When sending html search query "<query>"
+ Then the result is valid html
+ When sending xml search query "<query>"
+ Then the result is valid xml
+ When sending json search query "<query>"
+ Then the result is valid json
+ When sending jsonv2 search query "<query>"
+ Then the result is valid json
+
+ Examples:
+ | query |
+ | New York, New York |
+ | France |
+ | 12, Main Street, Houston |
+ | München |
+ | 東京都 |
+ | hotels in nantes |
+ | xywxkrf |
+ | gh; foo() |
+ | %#$@*&l;der#$! |
+ | 234 |
+ | 47.4,8.3 |
+
+ Scenario: Empty XML search
+ When sending xml search query "xnznxvcx"
+ Then result header contains
+ | attr | value |
+ | querystring | xnznxvcx |
+ | polygon | false |
+ | more_url | .*format=xml.*q=xnznxvcx.* |
+
+ Scenario: Empty XML search with special XML characters
+ When sending xml search query "xfdghn&zxn"xvbyx<vxx>cssdex"
+ Then result header contains
+ | attr | value |
+ | querystring | xfdghn&zxn"xvbyx<vxx>cssdex |
+ | polygon | false |
+ | more_url | .*format=xml.*q=xfdghn%26zxn%22xvbyx%3Cvxx%3Ecssdex.* |
+
+ Scenario: Empty XML search with viewbox
+ When sending xml search query "xnznxvcx"
+ | viewbox |
+ | 12,45.13,77,33 |
+ Then result header contains
+ | attr | value |
+ | querystring | xnznxvcx |
+ | polygon | false |
+ | viewbox | 12,45.13,77,33 |
+
+ Scenario: Empty XML search with viewboxlbrt
+ When sending xml search query "xnznxvcx"
+ | viewboxlbrt |
+ | 12,34.13,77,45 |
+ Then result header contains
+ | attr | value |
+ | querystring | xnznxvcx |
+ | polygon | false |
+ | viewbox | 12,45,77,34.13 |
+
+ Scenario: Empty XML search with viewboxlbrt and viewbox
+ When sending xml search query "pub"
+ | viewbox | viewboxblrt |
+ | 12,45.13,77,33 | 1,2,3,4 |
+ Then result header contains
+ | attr | value |
+ | querystring | pub |
+ | polygon | false |
+ | viewbox | 12,45.13,77,33 |
+
+ Scenario Outline: Empty XML search with polygon values
+ When sending xml search query "xnznxvcx"
+ | param | value |
+ | polygon | <polyval> |
+ Then result header contains
+ | attr | value |
+ | polygon | <result> |
+
+ Examples:
+ | result | polyval |
+ | false | 0 |
+ | true | 1 |
+ | true | True |
+ | true | true |
+ | true | false |
+ | true | FALSE |
+ | true | yes |
+ | true | no |
+ | true | '; delete from foobar; select ' |
+
+ Scenario: Empty XML search with exluded place ids
+ When sending xml search query "jghrleoxsbwjer"
+ | exclude_place_ids |
+ | 123,76,342565 |
+ Then result header contains
+ | attr | value |
+ | exclude_place_ids | 123,76,342565 |
+
+ Scenario: Empty XML search with bad exluded place ids
+ When sending xml search query "jghrleoxsbwjer"
+ | exclude_place_ids |
+ | , |
+ Then result header has not attributes exclude_place_ids
+
+ Scenario Outline: Wrapping of legal jsonp search requests
+ When sending json search query "Tokyo"
+ | param | value |
+ |json_callback | <data> |
+ Then result header contains
+ | attr | value |
+ | json_func | <result> |
+
+ Examples:
+ | data | result |
+ | foo | foo |
+ | FOO | FOO |
+ | __world | __world |
+ | $me | \$me |
+ | m1[4] | m1\[4\] |
+ | d_r[$d] | d_r\[\$d\] |
+
+ Scenario Outline: Wrapping of illegal jsonp search requests
+ When sending json search query "Tokyo"
+ | param | value |
+ |json_callback | <data> |
+ Then a HTTP 400 is returned
+
+ Examples:
+ | data |
+ | 1asd |
+ | bar(foo) |
+ | XXX['bad'] |
+ | foo; evil |
+
+ Scenario: Ignore jsonp parameter for anything but json
+ When sending json search query "Malibu"
+ | json_callback |
+ | 234 |
+ Then a HTTP 400 is returned
+ When sending xml search query "Malibu"
+ | json_callback |
+ | 234 |
+ Then the result is valid xml
+ When sending html search query "Malibu"
+ | json_callback |
+ | 234 |
+ Then the result is valid html
+
+ Scenario: Empty JSON search
+ When sending json search query "YHlERzzx"
+ Then exactly 0 results are returned
+
+ Scenario: Empty JSONv2 search
+ When sending jsonv2 search query "Flubb XdfESSaZx"
+ Then exactly 0 results are returned
+
+ Scenario: Search for non-existing coordinates
+ When sending json search query "-21.0,-33.0"
+ Then exactly 0 results are returned
+
--- /dev/null
+@APIDB
+Feature: Structured search queries
+ Testing correctness of results with
+ structured queries
+
+ Scenario: Country only
+ When sending json search query "" with address
+ | country |
+ | Liechtenstein |
+ Then address of result 0 is
+ | type | value |
+ | country | Liechtenstein |
+ | country_code | li |
+
+ Scenario: Postcode only
+ When sending json search query "" with address
+ | postalcode |
+ | 22547 |
+ Then results contain
+ | type |
+ | postcode |
+ And result addresses contain
+ | postcode |
+ | 22547 |
+
+ Scenario: Street, postcode and country
+ When sending xml search query "" with address
+ | street | postalcode | country |
+ | Old Palace Road | GU2 7UP | United Kingdom |
+ Then result header contains
+ | attr | value |
+ | querystring | Old Palace Road, GU2 7UP, United Kingdom |
+
+ Scenario: gihub #176
+ When sending json search query "" with address
+ | city |
+ | Mercedes |
+ Then at least 1 result is returned
--- /dev/null
+@DB
+Feature: Import of address interpolations
+ Tests that interpolated addresses are added correctly
+
+ Scenario: Simple even interpolation line with two points
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 1 1 |
+ | N2 | place | house | 6 | 1 1.001 |
+ | W1 | place | houses | even | 1 1, 1 1.001 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,2 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 6 | 1 1, 1 1.001 |
+
+ Scenario: Backwards even two point interpolation line
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 1 1 |
+ | N2 | place | house | 6 | 1 1.001 |
+ | W1 | place | houses | even | 1 1.001, 1 1 |
+ And the ways
+ | id | nodes |
+ | 1 | 2,1 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 6 | 1 1, 1 1.001 |
+
+ Scenario: Simple odd two point interpolation
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 1 | 1 1 |
+ | N2 | place | house | 11 | 1 1.001 |
+ | W1 | place | houses | odd | 1 1, 1 1.001 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,2 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 1 | 11 | 1 1, 1 1.001 |
+
+ Scenario: Simple all two point interpolation
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 1 | 1 1 |
+ | N2 | place | house | 3 | 1 1.001 |
+ | W1 | place | houses | all | 1 1, 1 1.001 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,2 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 1 | 3 | 1 1, 1 1.001 |
+
+ Scenario: Even two point interpolation line with intermediate empty node
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 1 1 |
+ | N2 | place | house | 10 | 1.001 1.001 |
+ | W1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,3,2 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 10 | 1 1, 1 1.001, 1.001 1.001 |
+
+ Scenario: Even two point interpolation line with intermediate duplicated empty node
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 1 1 |
+ | N2 | place | house | 10 | 1.001 1.001 |
+ | W1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,3,3,2 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 10 | 1 1, 1 1.001, 1.001 1.001 |
+
+ Scenario: Simple even three point interpolation line
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 1 1 |
+ | N2 | place | house | 14 | 1.001 1.001 |
+ | N3 | place | house | 10 | 1 1.001 |
+ | W1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,3,2 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 10 | 1 1, 1 1.001 |
+ | 10 | 14 | 1 1.001, 1.001 1.001 |
+
+ Scenario: Simple even four point interpolation line
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 1 1 |
+ | N2 | place | house | 14 | 1.001 1.001 |
+ | N3 | place | house | 10 | 1 1.001 |
+ | N4 | place | house | 18 | 1.001 1.002 |
+ | W1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001, 1.001 1.002 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,3,2,4 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 10 | 1 1, 1 1.001 |
+ | 10 | 14 | 1 1.001, 1.001 1.001 |
+ | 14 | 18 | 1.001 1.001, 1.001 1.002 |
+
+ Scenario: Reverse simple even three point interpolation line
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 1 1 |
+ | N2 | place | house | 14 | 1.001 1.001 |
+ | N3 | place | house | 10 | 1 1.001 |
+ | W1 | place | houses | even | 1.001 1.001, 1 1.001, 1 1 |
+ And the ways
+ | id | nodes |
+ | 1 | 2,3,1 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 10 | 1 1, 1 1.001 |
+ | 10 | 14 | 1 1.001, 1.001 1.001 |
+
+ Scenario: Even three point interpolation line with odd center point
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 1 1 |
+ | N2 | place | house | 8 | 1.001 1.001 |
+ | N3 | place | house | 7 | 1 1.001 |
+ | W1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,3,2 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 7 | 1 1, 1 1.001 |
+ | 7 | 8 | 1 1.001, 1.001 1.001 |
+
+ Scenario: Interpolation line with self-intersecting way
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 0 0 |
+ | N2 | place | house | 6 | 0 0.001 |
+ | N3 | place | house | 10 | 0 0.002 |
+ | W1 | place | houses | even | 0 0, 0 0.001, 0 0.002, 0 0.001 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,2,3,2 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 6 | 0 0, 0 0.001 |
+ | 6 | 10 | 0 0.001, 0 0.002 |
+ | 6 | 10 | 0 0.001, 0 0.002 |
+
+ Scenario: Interpolation line with self-intersecting way II
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | 0 0 |
+ | N2 | place | house | 6 | 0 0.001 |
+ | W1 | place | houses | even | 0 0, 0 0.001, 0 0.002, 0 0.001 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,2,3,2 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 6 | 0 0, 0 0.001 |
+
+ Scenario: addr:street on interpolation way
+ Given the scene parallel-road
+ And the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-middle-w |
+ | N2 | place | house | 6 | :n-middle-e |
+ | N3 | place | house | 12 | :n-middle-w |
+ | N4 | place | house | 16 | :n-middle-e |
+ And the places
+ | osm | class | type | housenr | street | geometry |
+ | W10 | place | houses | even | | :w-middle |
+ | W11 | place | houses | even | Cloud Street | :w-middle |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | tertiary | Sun Way | :w-north |
+ | W3 | highway | tertiary | Cloud Street | :w-south |
+ And the ways
+ | id | nodes |
+ | 10 | 1,100,101,102,2 |
+ | 11 | 3,200,201,202,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 | 2 | 6 |
+ Then W11 expands to interpolation
+ | parent_place_id | start | end |
+ | W3 | 12 | 16 |
+ When searching for "16 Cloud Street"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 4 |
+ When searching for "14 Cloud Street"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | W | 11 |
+ When searching for "18 Cloud Street"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | W | 3 |
+
+ Scenario: addr:street on housenumber way
+ Given the scene parallel-road
+ And the places
+ | osm | class | type | housenr | street | geometry |
+ | N1 | place | house | 2 | | :n-middle-w |
+ | N2 | place | house | 6 | | :n-middle-e |
+ | N3 | place | house | 12 | Cloud Street | :n-middle-w |
+ | N4 | place | house | 16 | Cloud Street | :n-middle-e |
+ And the places
+ | osm | class | type | housenr | geometry |
+ | W10 | place | houses | even | :w-middle |
+ | W11 | place | houses | even | :w-middle |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | tertiary | Sun Way | :w-north |
+ | W3 | highway | tertiary | Cloud Street | :w-south |
+ And the ways
+ | id | nodes |
+ | 10 | 1,100,101,102,2 |
+ | 11 | 3,200,201,202,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 | 2 | 6 |
+ Then W11 expands to interpolation
+ | parent_place_id | start | end |
+ | W3 | 12 | 16 |
+ When searching for "16 Cloud Street"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 4 |
+ When searching for "14 Cloud Street"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | W | 11 |
+
+ 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 |
+ | W1 | place | houses | even | 144.9632341 -37.76163,144.9630541 -37.7628172,144.9629794 -37.7630755 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,2,3 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 2 | 6 | 144.9629794 -37.7630755, 144.9630541 -37.7628174 |
+ | 6 | 10 | 144.9630541 -37.7628174, 144.9632341 -37.76163 |
+
+ Scenario: Place with missing address information
+ Given the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 23 | 0.0001 0.0001 |
+ | N2 | amenity | school | | 0.0001 0.0002 |
+ | N3 | place | house | 29 | 0.0001 0.0004 |
+ | W1 | place | houses | odd | 0.0001 0.0001,0.0001 0.0002,0.0001 0.0004 |
+ And the ways
+ | id | nodes |
+ | 1 | 1,2,3 |
+ When importing
+ Then W1 expands to interpolation
+ | start | end | geometry |
+ | 23 | 29 | 0.0001 0.0001, 0.0001 0.0002, 0.0001 0.0004 |
--- /dev/null
+@DB
+Feature: Linking of places
+ Tests for correctly determining linked places
+
+ Scenario: Only address-describing places can be linked
+ Given the scene way-area-with-center
+ And the places
+ | osm | class | type | name | geometry |
+ | R13 | landuse | forest | Garbo | :area |
+ | N256 | natural | peak | Garbo | :inner-C |
+ When importing
+ Then placex contains
+ | object | linked_place_id |
+ | R13 | - |
+ | N256 | - |
+
+ Scenario: Waterways are linked when in waterway relations
+ Given the scene split-road
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | waterway | river | Rhein | :w-2 |
+ | W2 | waterway | river | Rhein | :w-3 |
+ | R13 | waterway | river | Rhein | :w-1 + :w-2 + :w-3 |
+ | R23 | waterway | river | Limmat| :w-4a |
+ 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 searching for "rhein"
+ Then results contain
+ | osm_type |
+ | R |
+
+ Scenario: Relations are not linked when in waterway relations
+ Given the scene split-road
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | waterway | river | Rhein | :w-2 |
+ | W2 | waterway | river | Rhein | :w-3 |
+ | R1 | waterway | river | Rhein | :w-1 + :w-2 + :w-3 |
+ | R2 | waterway | river | Limmat| :w-4a |
+ And the relations
+ | id | members | tags+type |
+ | 1 | R2 | waterway |
+ When importing
+ Then placex contains
+ | object | linked_place_id |
+ | W1 | - |
+ | W2 | - |
+ | R1 | - |
+ | R2 | - |
+
+ Scenario: Empty waterway relations are handled correctly
+ Given the scene split-road
+ And the places
+ | osm | class | type | name | geometry |
+ | R1 | waterway | river | Rhein | :w-1 + :w-2 + :w-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 waterway types don't match
+ Given the scene split-road
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | waterway | drain | Rhein | :w-2 |
+ | R1 | waterway | river | Rhein | :w-1 + :w-2 + :w-3 |
+ And the relations
+ | id | members | tags+type |
+ | 1 | N23,N34,W1,R45 | multipolygon |
+ When importing
+ Then placex contains
+ | object | linked_place_id |
+ | W1 | - |
+ | R1 | - |
+ When searching for "rhein"
+ Then results contain
+ | ID | osm_type |
+ | 0 | R |
+ | 1 | W |
+
+ Scenario: Side streams are linked only when they have the same name
+ Given the scene split-road
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | waterway | river | Rhein2 | :w-2 |
+ | W2 | waterway | river | Rhein | :w-3 |
+ | R1 | waterway | river | Rhein | :w-1 + :w-2 + :w-3 |
+ And the relations
+ | id | members | tags+type |
+ | 1 | W1:side_stream,W2:side_stream | waterway |
+ When importing
+ Then placex contains
+ | object | linked_place_id |
+ | W1 | - |
+ | W2 | R1 |
+ When searching for "rhein2"
+ Then results contain
+ | osm_type |
+ | W |
--- /dev/null
+@DB
+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 | geometry |
+ | N1 | place | locality | german | country:de |
+ When importing
+ Then placex contains
+ | object | calculated_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:fi | geometry |
+ | N1 | place | locality | german | finnish | country:de |
+ When importing
+ Then placex contains
+ | object | calculated_country_code | 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 | calculated_country_code | 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:fi | name+name:de | geometry |
+ | N1 | place | locality | german | finnish | local | country:de |
+ When importing
+ Then placex contains
+ | object | calculated_country_code | name | name+name:fi | name+name:de |
+ | N1 | de | german | finnish | local |
--- /dev/null
+@DB
+Feature: Parenting of objects
+ Tests that the correct parent is choosen
+
+ Scenario: Address inherits postcode from its street unless it has a postcode
+ Given the scene roads-with-pois
+ And the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 4 | :p-N1 |
+ And the places
+ | osm | class | type | housenr | postcode | geometry |
+ | N2 | place | house | 5 | 99999 | :p-N1 |
+ And the places
+ | osm | class | type | name | postcode | geometry |
+ | W1 | highway | residential | galoo | 12345 | :w-north |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | W1 |
+ | N2 | W1 |
+ When searching for "4 galoo"
+ Then results contain
+ | ID | osm_type | osm_id | langaddress
+ | 0 | N | 1 | 4, galoo, 12345
+ When searching for "5 galoo"
+ Then results contain
+ | ID | osm_type | osm_id | langaddress
+ | 0 | N | 2 | 5, galoo, 99999
+
+ Scenario: Address without tags, closest street
+ Given the scene roads-with-pois
+ And the places
+ | osm | class | type | geometry |
+ | N1 | place | house | :p-N1 |
+ | N2 | place | house | :p-N2 |
+ | N3 | place | house | :p-S1 |
+ | N4 | place | house | :p-S2 |
+ And the named places
+ | osm | class | type | geometry |
+ | W1 | highway | residential | :w-north |
+ | W2 | highway | residential | :w-south |
+ 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 scene roads-with-pois
+ And the places
+ | osm | class | type | geometry |
+ | N1 | place | house | :p-N1 |
+ | N2 | place | house | :p-N2 |
+ | N3 | place | house | :p-S1 |
+ | N4 | place | house | :p-S2 |
+ | W1 | highway | residential | :w-north |
+ And the named places
+ | osm | class | type | geometry |
+ | W2 | highway | residential | :w-south |
+ 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 scene roads-with-pois
+ And the places
+ | osm | class | type | street| geometry |
+ | N1 | place | house | south | :p-N1 |
+ | N2 | place | house | north | :p-N2 |
+ | N3 | place | house | south | :p-S1 |
+ | N4 | place | house | north | :p-S2 |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | residential | north | :w-north |
+ | W2 | highway | residential | south | :w-south |
+ 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 scene roads-with-pois
+ And the places
+ | osm | class | type | street | geometry |
+ | N1 | place | house | abcdef | :p-N1 |
+ | N2 | place | house | abcdef | :p-N2 |
+ | N3 | place | house | abcdef | :p-S1 |
+ | N4 | place | house | abcdef | :p-S2 |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | residential | abcdef | :w-north |
+ | W2 | highway | residential | abcdef | :w-south |
+ 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 scene roads-with-pois
+ And the places
+ | osm | class | type | street | geometry |
+ | N1 | place | house | abcdef | :p-N1 |
+ | N2 | place | house | abcdef | :p-N2 |
+ | N3 | place | house | abcdef | :p-S1 |
+ | N4 | place | house | abcdef | :p-S2 |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | residential | abcde | :w-north |
+ | W2 | highway | residential | abcde | :w-south |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | W1 |
+ | N2 | W1 |
+ | N3 | W2 |
+ | N4 | W2 |
+
+ Scenario: addr:place address
+ Given the scene road-with-alley
+ And the places
+ | osm | class | type | addr_place | geometry |
+ | N1 | place | house | myhamlet | :n-alley |
+ And the places
+ | osm | class | type | name | geometry |
+ | N2 | place | hamlet | myhamlet | :n-main-west |
+ | W1 | highway | residential | myhamlet | :w-main |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | N2 |
+
+ Scenario: addr:street is preferred over addr:place
+ Given the scene road-with-alley
+ And the places
+ | osm | class | type | addr_place | street | geometry |
+ | N1 | place | house | myhamlet | mystreet| :n-alley |
+ And the places
+ | osm | class | type | name | geometry |
+ | N2 | place | hamlet | myhamlet | :n-main-west |
+ | W1 | highway | residential | mystreet | :w-main |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | W1 |
+
+ Scenario: Untagged address in simple associated street relation
+ Given the scene road-with-alley
+ And the places
+ | osm | class | type | geometry |
+ | N1 | place | house | :n-alley |
+ | N2 | place | house | :n-corner |
+ | N3 | place | house | :n-main-west |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | residential | foo | :w-main |
+ | W2 | highway | service | bar | :w-alley |
+ 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 scene road-with-alley
+ And the places
+ | osm | class | type | geometry |
+ | N1 | place | house | :n-alley |
+ | N2 | place | house | :n-corner |
+ | N3 | place | house | :n-main-west |
+ | W2 | highway | residential | :w-alley |
+ And the named places
+ | osm | class | type | geometry |
+ | W1 | highway | residential | :w-main |
+ 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 scene road-with-alley
+ And the places
+ | osm | class | type | street | geometry |
+ | N1 | place | house | bar | :n-alley |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | residential | foo | :w-main |
+ | W2 | highway | residential | bar | :w-alley |
+ 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 |
+
+ Scenario: Building without tags, closest street from center point
+ Given the scene building-on-street-corner
+ And the named places
+ | osm | class | type | geometry |
+ | W1 | building | yes | :w-building |
+ | W2 | highway | primary | :w-WE |
+ | W3 | highway | residential | :w-NS |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | W1 | W3 |
+
+ Scenario: Building with addr:street tags
+ Given the scene building-on-street-corner
+ And the named places
+ | osm | class | type | street | geometry |
+ | W1 | building | yes | bar | :w-building |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | primary | bar | :w-WE |
+ | W3 | highway | residential | foo | :w-NS |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | W1 | W2 |
+
+ Scenario: Building with addr:place tags
+ Given the scene building-on-street-corner
+ And the places
+ | osm | class | type | name | geometry |
+ | N1 | place | village | bar | :n-outer |
+ | W2 | highway | primary | bar | :w-WE |
+ | W3 | highway | residential | foo | :w-NS |
+ And the named places
+ | osm | class | type | addr_place | geometry |
+ | W1 | building | yes | bar | :w-building |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | W1 | N1 |
+
+ Scenario: Building in associated street relation
+ Given the scene building-on-street-corner
+ And the named places
+ | osm | class | type | geometry |
+ | W1 | building | yes | :w-building |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | primary | bar | :w-WE |
+ | W3 | highway | residential | foo | :w-NS |
+ 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: Building in associated street relation overrides addr:street
+ Given the scene building-on-street-corner
+ And the named places
+ | osm | class | type | street | geometry |
+ | W1 | building | yes | foo | :w-building |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | primary | bar | :w-WE |
+ | W3 | highway | residential | foo | :w-NS |
+ 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 scene building-on-street-corner
+ And the named places
+ | osm | class | type | geometry |
+ | N1 | place | house | :n-outer |
+ And the named places
+ | osm | class | type | street | geometry |
+ | W1 | building | yes | foo | :w-building |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | primary | bar | :w-WE |
+ | W3 | highway | residential | foo | :w-NS |
+ 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: POIs in building inherit address
+ Given the scene building-on-street-corner
+ And the named places
+ | osm | class | type | geometry |
+ | N1 | amenity | bank | :n-inner |
+ | N2 | shop | bakery | :n-edge-NS |
+ | N3 | shop | supermarket| :n-edge-WE |
+ And the places
+ | osm | class | type | street | addr_place | housenr | geometry |
+ | W1 | building | yes | foo | nowhere | 3 | :w-building |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | primary | bar | :w-WE |
+ | W3 | highway | residential | foo | :w-NS |
+ When importing
+ Then placex contains
+ | object | parent_place_id | street | addr_place | housenumber |
+ | W1 | W3 | foo | nowhere | 3 |
+ | N1 | W3 | foo | nowhere | 3 |
+ | N2 | W3 | foo | nowhere | 3 |
+ | N3 | W3 | foo | nowhere | 3 |
+
+ Scenario: POIs don't inherit from streets
+ Given the scene building-on-street-corner
+ And the named places
+ | osm | class | type | geometry |
+ | N1 | amenity | bank | :n-inner |
+ And the places
+ | osm | class | type | street | addr_place | housenr | geometry |
+ | W1 | highway | path | foo | nowhere | 3 | :w-building |
+ And the places
+ | osm | class | type | name | geometry |
+ | W3 | highway | residential | foo | :w-NS |
+ When importing
+ Then placex contains
+ | object | parent_place_id | street | addr_place | housenumber |
+ | N1 | W3 | None | None | None |
+
+ Scenario: POIs with own address do not inherit building address
+ Given the scene building-on-street-corner
+ And the named places
+ | osm | class | type | street | geometry |
+ | N1 | amenity | bank | bar | :n-inner |
+ And the named places
+ | osm | class | type | housenr | geometry |
+ | N2 | shop | bakery | 4 | :n-edge-NS |
+ And the named places
+ | osm | class | type | addr_place | geometry |
+ | N3 | shop | supermarket| nowhere | :n-edge-WE |
+ And the places
+ | osm | class | type | name | geometry |
+ | N4 | place | isolated_dwelling | theplace | :n-outer |
+ And the places
+ | osm | class | type | addr_place | housenr | geometry |
+ | W1 | building | yes | theplace | 3 | :w-building |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | primary | bar | :w-WE |
+ | W3 | highway | residential | foo | :w-NS |
+ When importing
+ Then placex contains
+ | object | parent_place_id | street | addr_place | housenumber |
+ | W1 | N4 | None | theplace | 3 |
+ | N1 | W2 | bar | None | None |
+ | N2 | W3 | None | None | 4 |
+ | N3 | W2 | None | nowhere | None |
+
+ Scenario: POIs parent a road if they are attached to it
+ Given the scene points-on-roads
+ And the named places
+ | osm | class | type | street | geometry |
+ | N1 | highway | bus_stop | North St | :n-SE |
+ | N2 | highway | bus_stop | South St | :n-NW |
+ | N3 | highway | bus_stop | North St | :n-S-unglued |
+ | N4 | highway | bus_stop | South St | :n-N-unglued |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | secondary | North St | :w-north |
+ | W2 | highway | unclassified | South St | :w-south |
+ And the ways
+ | id | nodes |
+ | 1 | 100,101,2,103,104 |
+ | 2 | 200,201,1,202,203 |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | W1 |
+ | N2 | W2 |
+ | N3 | W1 |
+ | N4 | W2 |
+
+ Scenario: POIs do not parent non-roads they are attached to
+ Given the scene points-on-roads
+ And the named places
+ | osm | class | type | street | geometry |
+ | N1 | highway | bus_stop | North St | :n-SE |
+ | N2 | highway | bus_stop | South St | :n-NW |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | landuse | residential | North St | :w-north |
+ | W2 | waterway| river | South St | :w-south |
+ And the ways
+ | id | nodes |
+ | 1 | 100,101,2,103,104 |
+ | 2 | 200,201,1,202,203 |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | 0 |
+ | N2 | 0 |
+
+ Scenario: POIs on building outlines inherit associated street relation
+ Given the scene building-on-street-corner
+ And the named places
+ | osm | class | type | geometry |
+ | N1 | place | house | :n-edge-NS |
+ | W1 | building | yes | :w-building |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | primary | bar | :w-WE |
+ | W3 | highway | residential | foo | :w-NS |
+ And the relations
+ | id | members | tags+type |
+ | 1 | W1:house,W2:street | associatedStreet |
+ And the ways
+ | id | nodes |
+ | 1 | 100,1,101,102,100 |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | W2 |
+
--- /dev/null
+@DB
+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 | country_code | calculated_country_code |
+ | N1 | None | 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 | country_code | calculated_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 | country_code | calculated_country_code |
+ | R1 | 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 | country_code | calculated_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: admin level is default 15
+ Given the named places
+ | osm | class | type |
+ | N1 | amenity | prison |
+ When importing
+ Then placex contains
+ | object | admin_level |
+ | N1 | 15 |
+
+ Scenario: admin level is never larger than 15
+ Given the named places
+ | osm | class | type | admin |
+ | N1 | amenity | prison | 16 |
+ When importing
+ Then placex contains
+ | object | admin_level |
+ | N1 | 15 |
+
+ 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 places
+ | osm | class | type | name+ref | geometry |
+ | R1 | boundary | postal_code | 554476 | poly-area:0.1 |
+ When importing
+ Then placex has no entry for R1
+
+ 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 placex contains
+ | object | postcode | calculated_country_code | rank_search | rank_address |
+ | N1 | E45 2CD | gb | 25 | 5 |
+ | N2 | E45 2 | gb | 23 | 5 |
+ | N3 | Y45 | gb | 21 | 5 |
+
+ Scenario: wrongly formatted GB postcodes are down-ranked
+ Given the places
+ | osm | class | type | postcode | geometry |
+ | N1 | place | postcode | EA452CD | country:gb |
+ | N2 | place | postcode | E45 23 | country:gb |
+ | N3 | place | postcode | y45 | country:gb |
+ When importing
+ Then placex contains
+ | object | calculated_country_code | rank_search | rank_address |
+ | N1 | gb | 30 | 30 |
+ | N2 | gb | 30 | 30 |
+ | N3 | gb | 30 | 30 |
+
+ Scenario: search and address rank for DE postcodes correctly assigned
+ Given the places
+ | osm | class | type | postcode | geometry |
+ | N1 | place | postcode | 56427 | country:de |
+ | N2 | place | postcode | 5642 | country:de |
+ | N3 | place | postcode | 5642A | country:de |
+ | N4 | place | postcode | 564276 | country:de |
+ When importing
+ Then placex contains
+ | object | calculated_country_code | rank_search | rank_address |
+ | N1 | de | 21 | 11 |
+ | N2 | de | 30 | 30 |
+ | N3 | de | 30 | 30 |
+ | N4 | de | 30 | 30 |
+
+ Scenario: search and address rank for other postcodes are correctly assigned
+ Given the places
+ | osm | class | type | postcode | geometry |
+ | N1 | place | postcode | 1 | country:ca |
+ | N2 | place | postcode | X3 | country:ca |
+ | N3 | place | postcode | 543 | country:ca |
+ | N4 | place | postcode | 54dc | country:ca |
+ | N5 | place | postcode | 12345 | country:ca |
+ | N6 | place | postcode | 55TT667 | country:ca |
+ | N7 | place | postcode | 123-65 | country:ca |
+ | N8 | place | postcode | 12 445 4 | country:ca |
+ | N9 | place | postcode | A1:bc10 | country:ca |
+ When importing
+ Then placex contains
+ | object | calculated_country_code | rank_search | rank_address |
+ | N1 | ca | 21 | 11 |
+ | N2 | ca | 21 | 11 |
+ | N3 | ca | 21 | 11 |
+ | N4 | ca | 21 | 11 |
+ | N5 | ca | 21 | 11 |
+ | N6 | ca | 21 | 11 |
+ | N7 | ca | 25 | 11 |
+ | N8 | ca | 25 | 11 |
+ | N9 | ca | 25 | 11 |
+
+ Scenario: search and address ranks for places are correctly assigned
+ Given the named places
+ | osm | class | type |
+ | N1 | foo | bar |
+ | N11 | place | Continent |
+ | N12 | place | continent |
+ | N13 | place | sea |
+ | N14 | place | country |
+ | N15 | place | state |
+ | N16 | place | region |
+ | N17 | place | county |
+ | N18 | place | city |
+ | N19 | place | island |
+ | N20 | place | town |
+ | N21 | place | village |
+ | N22 | place | hamlet |
+ | N23 | place | municipality |
+ | N24 | place | district |
+ | N25 | place | unincorporated_area |
+ | N26 | place | borough |
+ | N27 | place | suburb |
+ | N28 | place | croft |
+ | N29 | place | subdivision |
+ | N30 | place | isolated_dwelling |
+ | N31 | place | farm |
+ | N32 | place | locality |
+ | N33 | place | islet |
+ | N34 | place | mountain_pass |
+ | N35 | place | neighbourhood |
+ | N36 | place | house |
+ | N37 | place | building |
+ | N38 | place | houses |
+ And the named places
+ | osm | class | type | extra+locality |
+ | N100 | place | locality | townland |
+ And the named places
+ | osm | class | type | extra+capital |
+ | N101 | place | city | yes |
+ When importing
+ Then placex contains
+ | object | rank_search | rank_address |
+ | N1 | 30 | 30 |
+ | N11 | 30 | 30 |
+ | N12 | 2 | 2 |
+ | N13 | 2 | 0 |
+ | N14 | 4 | 4 |
+ | N15 | 8 | 8 |
+ | N16 | 18 | 0 |
+ | N17 | 12 | 12 |
+ | N18 | 16 | 16 |
+ | N19 | 17 | 0 |
+ | N20 | 18 | 16 |
+ | N21 | 19 | 16 |
+ | N22 | 19 | 16 |
+ | N23 | 19 | 16 |
+ | N24 | 19 | 16 |
+ | N25 | 19 | 16 |
+ | N26 | 19 | 16 |
+ | N27 | 20 | 20 |
+ | N28 | 20 | 20 |
+ | N29 | 20 | 20 |
+ | N30 | 20 | 20 |
+ | N31 | 20 | 0 |
+ | N32 | 20 | 0 |
+ | N33 | 20 | 0 |
+ | N34 | 20 | 0 |
+ | N100 | 20 | 20 |
+ | N101 | 15 | 16 |
+ | N35 | 22 | 22 |
+ | N36 | 30 | 30 |
+ | N37 | 30 | 30 |
+ | N38 | 28 | 0 |
+
+ 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) |
+ 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 | 30 | 30 |
+ | R22 | 12 | 0 |
+ | R23 | 20 | 0 |
+
+ Scenario: search and address ranks for highways correctly assigned
+ Given the scene roads-with-pois
+ And the places
+ | osm | class | type |
+ | N1 | highway | bus_stop |
+ And the places
+ | osm | class | type | geometry |
+ | W1 | highway | primary | :w-south |
+ | W2 | highway | secondary | :w-south |
+ | W3 | highway | tertiary | :w-south |
+ | W4 | highway | residential | :w-north |
+ | W5 | highway | unclassified | :w-north |
+ | W6 | highway | something | :w-north |
+ 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 | 26 | 26 |
+
+ Scenario: rank and inclusion of landuses
+ Given the named places
+ | osm | class | type |
+ | N2 | landuse | residential |
+ And the named places
+ | osm | class | type | geometry |
+ | W2 | landuse | residential | 1 1, 1 1.1 |
+ | W4 | landuse | residential | poly-area:0.1 |
+ | R2 | landuse | residential | poly-area:0.05 |
+ | R3 | landuse | forrest | poly-area:0.5 |
+ 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 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 | 12 12,11 11 |
+ | W3 | natural | foobar | 13 13,13.1 13 |
+ | R3 | natural | volcano | poly-area:0.1 |
+ | R4 | natural | foobar | poly-area:0.5 |
+ | R5 | natural | sea | poly-area:5.0 |
+ | R6 | natural | sea | poly-area:0.01 |
+ When importing
+ Then placex contains
+ | object | rank_search | rank_address |
+ | N2 | 18 | 0 |
+ | N4 | 18 | 0 |
+ | N5 | 30 | 30 |
+ | W2 | 18 | 0 |
+ | R3 | 18 | 0 |
+ | R4 | 22 | 0 |
+ | R5 | 4 | 4 |
+ | R6 | 4 | 4 |
+ | W3 | 30 | 30 |
+
--- /dev/null
+@DB
+Feature: Creation of search terms
+ Tests that search_name table is filled correctly
+
+ Scenario: POIs without a name have no search entry
+ Given the scene roads-with-pois
+ And the places
+ | osm | class | type | geometry |
+ | N1 | place | house | :p-N1 |
+ And the named places
+ | osm | class | type | geometry |
+ | W1 | highway | residential | :w-north |
+ When importing
+ Then search_name has no entry for N1
+
+ Scenario: Named POIs inherit address from parent
+ Given the scene roads-with-pois
+ And the places
+ | osm | class | type | name | geometry |
+ | N1 | place | house | foo | :p-N1 |
+ | W1 | highway | residential | the road | :w-north |
+ When importing
+ Then search_name contains
+ | object | name_vector | nameaddress_vector |
+ | N1 | foo | the road |
+
+ Scenario: Roads take over the postcode from attached houses
+ Given the scene roads-with-pois
+ And the places
+ | osm | class | type | housenr | postcode | street | geometry |
+ | N1 | place | house | 1 | 12345 | North St | :p-S1 |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | residential | North St | :w-north |
+ When importing
+ Then search_name contains
+ | object | nameaddress_vector |
+ | W1 | 12345 |
+
--- /dev/null
+@DB
+Feature: Import and search of names
+ Tests all naming related issues: normalisation,
+ abbreviations, internationalisation, etc.
+
+ 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 searching for "FooBar"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "foobar"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "fOObar"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "FOOBAR"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+
+ Scenario: Multiple spaces in name
+ Given the places
+ | osm | class | type | name |
+ | N1 | place | locality | one two three |
+ When importing
+ When searching for "one two three"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "one two three"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "one two three"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for " one two three"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+
+ Scenario: Special characters in name
+ Given the places
+ | osm | class | type | name |
+ | N1 | place | locality | Jim-Knopf-Str |
+ | N2 | place | locality | Smith/Weston |
+ | N3 | place | locality | space mountain |
+ | N4 | place | locality | space |
+ | N5 | place | locality | mountain |
+ When importing
+ When searching for "Jim-Knopf-Str"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "Jim Knopf-Str"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "Jim Knopf Str"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "Jim/Knopf-Str"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "Jim-Knopfstr"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 1 |
+ When searching for "Smith/Weston"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 2 |
+ When searching for "Smith Weston"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 2 |
+ When searching for "Smith-Weston"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 2 |
+ When searching for "space mountain"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 3 |
+ When searching for "space-mountain"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 3 |
+ When searching for "space/mountain"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 3 |
+ When searching for "space\mountain"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 3 |
+ When searching for "space(mountain)"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | N | 3 |
+
+ Scenario: Landuse with name are found
+ Given the places
+ | osm | class | type | name | geometry |
+ | R1 | natural | meadow | landuse1 | (0 0, 1 0, 1 1, 0 1, 0 0) |
+ | R2 | landuse | industrial | landuse2 | (0 0, -1 0, -1 -1, 0 -1, 0 0) |
+ When importing
+ When searching for "landuse1"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | R | 1 |
+ When searching for "landuse2"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | R | 2 |
+
+ @wip
+ Scenario: Postcode boundaries without ref
+ Given the places
+ | osm | class | type | postcode | geometry |
+ | R1 | boundary | postal_code | 12345 | (0 0, 1 0, 1 1, 0 1, 0 0) |
+ When importing
+ When searching for "12345"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | R | 1 |
--- /dev/null
+@DB
+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 searching for "Foo"
+ Then results contain
+ | ID | osm | class | type | centroid |
+ | 0 | N1 | place | village | 10 -10 |
+
+ Scenario: Updating postcode in postcode boundaries without ref
+ Given the places
+ | osm | class | type | postcode | geometry |
+ | R1 | boundary | postal_code | 12345 | poly-area:1.0 |
+ When importing
+ And searching for "12345"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | R | 1 |
+ When updating places
+ | osm | class | type | postcode | geometry |
+ | R1 | boundary | postal_code | 54321 | poly-area:1.0 |
+ And searching for "12345"
+ Then exactly 0 results are returned
+ When searching for "54321"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | R | 1 |
--- /dev/null
+@DB
+Feature: Update of address interpolations
+ Test the interpolated address are updated correctly
+
+ @wip
+ Scenario: new interpolation added to existing street
+ Given the scene parallel-road
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | unclassified | Sun Way | :w-north |
+ | W3 | highway | unclassified | Cloud Street | :w-south |
+ And the ways
+ | id | nodes |
+ | 10 | 1,100,101,102,2 |
+ When importing
+ Then W10 expands to no interpolation
+ When updating places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-middle-w |
+ | N2 | place | house | 6 | :n-middle-e |
+ | W10 | place | houses | even | :w-middle |
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | W2 |
+ | N2 | W2 |
+ And W10 expands to interpolation
+ | parent_place_id | start | end |
+ | W2 | 2 | 6 |
+
+ Scenario: addr:street added to interpolation
+ Given the scene parallel-road
+ And the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-middle-w |
+ | N2 | place | house | 6 | :n-middle-e |
+ | W10 | place | houses | even | :w-middle |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | unclassified | Sun Way | :w-north |
+ | W3 | highway | unclassified | Cloud Street | :w-south |
+ And the ways
+ | id | nodes |
+ | 10 | 1,100,101,102,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 | 2 | 6 |
+ When updating places
+ | osm | class | type | housenr | street | geometry |
+ | W10 | place | houses | even | Cloud Street | :w-middle |
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | W3 |
+ | N2 | W3 |
+ And W10 expands to interpolation
+ | parent_place_id | start | end |
+ | W3 | 2 | 6 |
+
+ Scenario: addr:street added to housenumbers
+ Given the scene parallel-road
+ And the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-middle-w |
+ | N2 | place | house | 6 | :n-middle-e |
+ | W10 | place | houses | even | :w-middle |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | unclassified | Sun Way | :w-north |
+ | W3 | highway | unclassified | Cloud Street | :w-south |
+ And the ways
+ | id | nodes |
+ | 10 | 1,100,101,102,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 | 2 | 6 |
+ When updating places
+ | osm | class | type | street | housenr | geometry |
+ | N1 | place | house | Cloud Street| 2 | :n-middle-w |
+ | N2 | place | house | Cloud Street| 6 | :n-middle-e |
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | W3 |
+ | N2 | W3 |
+ And W10 expands to interpolation
+ | parent_place_id | start | end |
+ | W3 | 2 | 6 |
+
+ Scenario: interpolation tag removed
+ Given the scene parallel-road
+ And the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-middle-w |
+ | N2 | place | house | 6 | :n-middle-e |
+ | W10 | place | houses | even | :w-middle |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | unclassified | Sun Way | :w-north |
+ | W3 | highway | unclassified | Cloud Street | :w-south |
+ And the ways
+ | id | nodes |
+ | 10 | 1,100,101,102,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 | 2 | 6 |
+ 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 scene parallel-road
+ And the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-middle-w |
+ | N2 | place | house | 6 | :n-middle-e |
+ And the places
+ | osm | class | type | housenr | street | geometry |
+ | W10 | place | houses | even | Cloud Street| :w-middle |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | unclassified | Sun Way | :w-north |
+ And the ways
+ | id | nodes |
+ | 10 | 1,100,101,102,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 | 2 | 6 |
+ When updating places
+ | osm | class | type | name | geometry |
+ | W3 | highway | unclassified | Cloud Street | :w-south |
+ Then placex contains
+ | object | parent_place_id |
+ | N1 | W3 |
+ | N2 | W3 |
+ And W10 expands to interpolation
+ | parent_place_id | start | end |
+ | W3 | 2 | 6 |
+
+ Scenario: referenced road deleted
+ Given the scene parallel-road
+ And the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-middle-w |
+ | N2 | place | house | 6 | :n-middle-e |
+ And the places
+ | osm | class | type | housenr | street | geometry |
+ | W10 | place | houses | even | Cloud Street| :w-middle |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | unclassified | Sun Way | :w-north |
+ | W3 | highway | unclassified | Cloud Street | :w-south |
+ And the ways
+ | id | nodes |
+ | 10 | 1,100,101,102,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 | 2 | 6 |
+ 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 | 2 | 6 |
+
+ Scenario: building becomes interpolation
+ Given the scene building-with-parallel-streets
+ And the places
+ | osm | class | type | housenr | geometry |
+ | W1 | place | house | 3 | :w-building |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | unclassified | Cloud Street | :w-south |
+ When importing
+ Then placex contains
+ | object | parent_place_id |
+ | W1 | W2 |
+ Given the ways
+ | id | nodes |
+ | 1 | 1,100,101,102,2 |
+ When updating places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-north-w |
+ | N2 | place | house | 6 | :n-north-e |
+ And updating places
+ | osm | class | type | housenr | street | geometry |
+ | W1 | place | houses | even | Cloud Street| :w-north |
+ Then placex has no entry for W1
+ And W1 expands to interpolation
+ | parent_place_id | start | end |
+ | W2 | 2 | 6 |
+
+ Scenario: interpolation becomes building
+ Given the scene building-with-parallel-streets
+ And the places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-north-w |
+ | N2 | place | house | 6 | :n-north-e |
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | unclassified | Cloud Street | :w-south |
+ And the ways
+ | id | nodes |
+ | 1 | 1,100,101,102,2 |
+ And the places
+ | osm | class | type | housenr | street | geometry |
+ | W1 | place | houses | even | Cloud Street| :w-north |
+ When importing
+ Then placex has no entry for W1
+ And W1 expands to interpolation
+ | parent_place_id | start | end |
+ | W2 | 2 | 6 |
+ When updating places
+ | osm | class | type | housenr | geometry |
+ | W1 | place | house | 3 | :w-building |
+ Then placex contains
+ | object | parent_place_id |
+ | W1 | W2 |
+
+ Scenario: housenumbers added to interpolation
+ Given the scene building-with-parallel-streets
+ And the places
+ | osm | class | type | name | geometry |
+ | W2 | highway | unclassified | Cloud Street | :w-south |
+ And the ways
+ | id | nodes |
+ | 1 | 1,100,101,102,2 |
+ And the places
+ | osm | class | type | housenr | geometry |
+ | W1 | place | houses | even | :w-north |
+ When importing
+ Then W1 expands to no interpolation
+ When updating places
+ | osm | class | type | housenr | geometry |
+ | N1 | place | house | 2 | :n-north-w |
+ | N2 | place | house | 6 | :n-north-e |
+ And updating places
+ | osm | class | type | housenr | street | geometry |
+ | W1 | place | houses | even | Cloud Street| :w-north |
+ Then W1 expands to interpolation
+ | parent_place_id | start | end |
+ | W2 | 2 | 6 |
--- /dev/null
+@DB
+Feature: Updates of linked places
+ Tests that linked places are correctly added and deleted.
+
+ Scenario: Add linked place when linking relation is renamed
+ Given the places
+ | osm | class | type | name | geometry |
+ | N1 | place | city | foo | 0 0 |
+ And the places
+ | osm | class | type | name | admin | geometry |
+ | R1 | boundary | administrative | foo | 8 | poly-area:0.1 |
+ When importing
+ And searching for "foo" with dups
+ Then results contain
+ | osm_type |
+ | R |
+ When updating places
+ | osm | class | type | name | admin | geometry |
+ | R1 | boundary | administrative | foobar | 8 | poly-area:0.1 |
+ Then placex contains
+ | object | linked_place_id |
+ | N1 | - |
+ When searching for "foo" with dups
+ Then results contain
+ | osm_type |
+ | N |
+
+ Scenario: Add linked place when linking relation is removed
+ Given the places
+ | osm | class | type | name | geometry |
+ | N1 | place | city | foo | 0 0 |
+ And the places
+ | osm | class | type | name | admin | geometry |
+ | R1 | boundary | administrative | foo | 8 | poly-area:0.1 |
+ When importing
+ And searching for "foo" with dups
+ Then results contain
+ | osm_type |
+ | R |
+ When marking for delete R1
+ Then placex contains
+ | object | linked_place_id |
+ | N1 | - |
+ When searching for "foo" with dups
+ Then results contain
+ | osm_type |
+ | N |
+
+ Scenario: Remove linked place when linking relation is added
+ Given the places
+ | osm | class | type | name | geometry |
+ | N1 | place | city | foo | 0 0 |
+ When importing
+ And searching for "foo" with dups
+ Then results contain
+ | osm_type |
+ | N |
+ When updating places
+ | osm | class | type | name | admin | geometry |
+ | R1 | boundary | administrative | foo | 8 | poly-area:0.1 |
+ Then placex contains
+ | object | linked_place_id |
+ | N1 | R1 |
+ When searching for "foo" with dups
+ Then results contain
+ | osm_type |
+ | R |
+
+ Scenario: Remove linked place when linking relation is renamed
+ Given the places
+ | osm | class | type | name | geometry |
+ | N1 | place | city | foo | 0 0 |
+ And the places
+ | osm | class | type | name | admin | geometry |
+ | R1 | boundary | administrative | foobar | 8 | poly-area:0.1 |
+ When importing
+ And searching for "foo" with dups
+ Then results contain
+ | osm_type |
+ | N |
+ When updating places
+ | osm | class | type | name | admin | geometry |
+ | R1 | boundary | administrative | foo | 8 | poly-area:0.1 |
+ Then placex contains
+ | object | linked_place_id |
+ | N1 | R1 |
+ When searching for "foo" with dups
+ Then results contain
+ | osm_type |
+ | R |
+
--- /dev/null
+@DB
+Feature: Update of names in place objects
+ Test all naming related issues in updates
+
+ Scenario: Delete postcode from postcode boundaries without ref
+ Given the places
+ | osm | class | type | postcode | geometry |
+ | R1 | boundary | postal_code | 12345 | poly-area:0.5 |
+ When importing
+ And searching for "12345"
+ Then results contain
+ | ID | osm_type | osm_id |
+ | 0 | R | 1 |
+ When updating places
+ | osm | class | type | geometry |
+ | R1 | boundary | postal_code | poly-area:0.5 |
+ Then placex has no entry for R1
+
--- /dev/null
+@DB
+Feature: Update of POI-inherited poscode
+ Test updates of postcodes on street which was inherited from a related POI
+
+ Background: Street and house with postcode
+ Given the scene roads-with-pois
+ And the places
+ | osm | class | type | housenr | postcode | street | geometry |
+ | N1 | place | house | 1 | 12345 | North St |:p-S1 |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | residential | North St | :w-north |
+ When importing
+ Then search_name contains
+ | object | nameaddress_vector |
+ | W1 | 12345 |
+
+ Scenario: POI-inherited postcode remains when way type is changed
+ When updating places
+ | osm | class | type | name | geometry |
+ | W1 | highway | unclassified | North St | :w-north |
+ Then search_name contains
+ | object | nameaddress_vector |
+ | W1 | 12345 |
+
+ Scenario: POI-inherited postcode remains when way name is changed
+ When updating places
+ | osm | class | type | name | geometry |
+ | W1 | highway | unclassified | South St | :w-north |
+ Then search_name contains
+ | object | nameaddress_vector |
+ | W1 | 12345 |
+
+ Scenario: POI-inherited postcode remains when way geometry is changed
+ When updating places
+ | osm | class | type | name | geometry |
+ | W1 | highway | unclassified | South St | :w-south |
+ Then search_name contains
+ | object | nameaddress_vector |
+ | W1 | 12345 |
+
+ Scenario: POI-inherited postcode is added when POI postcode changes
+ When updating places
+ | osm | class | type | housenr | postcode | street | geometry |
+ | N1 | place | house | 1 | 54321 | North St |:p-S1 |
+ Then search_name contains
+ | object | nameaddress_vector |
+ | W1 | 54321 |
+
+ Scenario: POI-inherited postcode remains when POI geometry changes
+ When updating places
+ | osm | class | type | housenr | postcode | street | geometry |
+ | N1 | place | house | 1 | 12345 | North St |:p-S2 |
+ Then search_name contains
+ | object | nameaddress_vector |
+ | W1 | 12345 |
+
--- /dev/null
+@DB
+Feature: Update of search terms
+ Tests that search_name table is updated correctly
+
+ Scenario: POI-inherited postcode remains when another POI is deleted
+ Given the scene roads-with-pois
+ And the places
+ | osm | class | type | housenr | postcode | street | geometry |
+ | N1 | place | house | 1 | 12345 | North St |:p-S1 |
+ | N2 | place | house | 2 | | North St |:p-S2 |
+ And the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | residential | North St | :w-north |
+ When importing
+ Then search_name contains
+ | object | nameaddress_vector |
+ | W1 | 12345 |
+ When marking for delete N2
+ Then search_name contains
+ | object | nameaddress_vector |
+ | W1 | 12345 |
--- /dev/null
+@DB
+Feature: Update of simple objects
+ Testing simple updating functionality
+
+ Scenario: Do delete small boundary features
+ Given the places
+ | osm | class | type | admin | geometry |
+ | R1 | boundary | administrative | 3 | poly-area:1.0 |
+ 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 places
+ | osm | class | type | admin | geometry |
+ | R1 | boundary | administrative | 3 | poly-area:5.0 |
+ 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 named places
+ | osm | class | type | geometry |
+ | W1 | place | house | poly-area:5.0 |
+ | R1 | boundary | national_park | poly-area:5.0 |
+ When importing
+ Then placex contains
+ | object | rank_address |
+ | R1 | 0 |
+ | W1 | 30 |
+ When marking for delete R1,W1
+ Then placex has no entry for W1
+ Then placex has no entry for R1
+
+ Scenario: type mutation
+ Given the places
+ | osm | class | type | geometry |
+ | N3 | shop | toys | 1 -1 |
+ When importing
+ Then placex contains
+ | object | class | type | centroid |
+ | N3 | shop | toys | 1 -1 |
+ When updating places
+ | osm | class | type | geometry |
+ | N3 | shop | grocery | 1 -1 |
+ Then placex contains
+ | object | class | type | centroid |
+ | 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 | 1 -1 |
+ When importing
+ Then placex contains
+ | object | class | type |
+ | N3 | place | postcode |
+ When updating places
+ | osm | class | type | postcode | housenr | geometry |
+ | N3 | place | house | 12345 | 13 | 1 -1 |
+ Then placex contains
+ | object | class | type |
+ | N3 | place | house |
--- /dev/null
+from behave import *
+import logging
+import os
+import psycopg2
+import psycopg2.extras
+import subprocess
+from nose.tools import * # for assert functions
+from sys import version_info as python_version
+
+logger = logging.getLogger(__name__)
+
+userconfig = {
+ 'BUILDDIR' : os.path.join(os.path.split(__file__)[0], "../../build"),
+ 'REMOVE_TEMPLATE' : False,
+ 'KEEP_TEST_DB' : False,
+ 'TEMPLATE_DB' : 'test_template_nominatim',
+ 'TEST_DB' : 'test_nominatim',
+ 'API_TEST_DB' : 'test_api_nominatim',
+ 'TEST_SETTINGS_FILE' : '/tmp/nominatim_settings.php'
+}
+
+use_step_matcher("re")
+
+class NominatimEnvironment(object):
+ """ Collects all functions for the execution of Nominatim functions.
+ """
+
+ def __init__(self, config):
+ self.build_dir = os.path.abspath(config['BUILDDIR'])
+ self.template_db = config['TEMPLATE_DB']
+ self.test_db = config['TEST_DB']
+ self.api_test_db = config['API_TEST_DB']
+ self.local_settings_file = config['TEST_SETTINGS_FILE']
+ self.reuse_template = not config['REMOVE_TEMPLATE']
+ self.keep_scenario_db = config['KEEP_TEST_DB']
+ os.environ['NOMINATIM_SETTINGS'] = self.local_settings_file
+
+ self.template_db_done = False
+
+ def write_nominatim_config(self, dbname):
+ f = open(self.local_settings_file, 'w')
+ f.write("<?php\n @define('CONST_Database_DSN', 'pgsql://@/%s');\n" % dbname)
+ f.close()
+
+ def cleanup(self):
+ try:
+ os.remove(self.local_settings_file)
+ except OSError:
+ pass # ignore missing file
+
+ def db_drop_database(self, name):
+ conn = psycopg2.connect(database='postgres')
+ conn.set_isolation_level(0)
+ cur = conn.cursor()
+ cur.execute('DROP DATABASE IF EXISTS %s' % (name, ))
+ conn.close()
+
+ def setup_template_db(self):
+ if self.template_db_done:
+ return
+
+ self.template_db_done = True
+
+ if self.reuse_template:
+ # check that the template is there
+ conn = psycopg2.connect(database='postgres')
+ cur = conn.cursor()
+ cur.execute('select count(*) from pg_database where datname = %s',
+ (self.template_db,))
+ if cur.fetchone()[0] == 1:
+ return
+ conn.close()
+ else:
+ # just in case... make sure a previous table has been dropped
+ self.db_drop_database(self.template_db)
+
+ # call the first part of database setup
+ self.write_nominatim_config(self.template_db)
+ self.run_setup_script('create-db', 'setup-db')
+ # remove external data to speed up indexing for tests
+ conn = psycopg2.connect(database=self.template_db)
+ cur = conn.cursor()
+ cur.execute("""select tablename from pg_tables
+ where tablename in ('gb_postcode', 'us_postcode')""")
+ for t in cur:
+ conn.cursor().execute('TRUNCATE TABLE %s' % (t[0],))
+ conn.commit()
+ conn.close()
+
+ # execute osm2pgsql on an empty file to get the right tables
+ osm2pgsql = os.path.join(self.build_dir, 'osm2pgsql', 'osm2pgsql')
+ proc = subprocess.Popen([osm2pgsql, '-lsc', '-r', 'xml',
+ '-O', 'gazetteer', '-d', self.template_db, '-'],
+ cwd=self.build_dir, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ [outstr, errstr] = proc.communicate(input=b'<osm version="0.6"></osm>')
+ logger.debug("running osm2pgsql for template: %s\n%s\n%s" % (osm2pgsql, outstr, errstr))
+ self.run_setup_script('create-functions', 'create-tables',
+ 'create-partition-tables', 'create-partition-functions',
+ 'load-data', 'create-search-indices')
+
+ def setup_api_db(self, context):
+ self.write_nominatim_config(self.api_test_db)
+
+ def setup_db(self, context):
+ self.setup_template_db()
+ self.write_nominatim_config(self.test_db)
+ conn = psycopg2.connect(database=self.template_db)
+ conn.set_isolation_level(0)
+ cur = conn.cursor()
+ cur.execute('DROP DATABASE IF EXISTS %s' % (self.test_db, ))
+ cur.execute('CREATE DATABASE %s TEMPLATE = %s' % (self.test_db, self.template_db))
+ conn.close()
+ context.db = psycopg2.connect(database=self.test_db)
+ if python_version[0] < 3:
+ psycopg2.extras.register_hstore(context.db, globally=False, unicode=True)
+ else:
+ psycopg2.extras.register_hstore(context.db, globally=False)
+
+ def teardown_db(self, context):
+ if 'db' in context:
+ context.db.close()
+
+ if not self.keep_scenario_db:
+ self.db_drop_database(self.test_db)
+
+ def run_setup_script(self, *args, **kwargs):
+ self.run_nominatim_script('setup', *args, **kwargs)
+
+ def run_update_script(self, *args, **kwargs):
+ self.run_nominatim_script('update', *args, **kwargs)
+
+ def run_nominatim_script(self, script, *args, **kwargs):
+ cmd = [os.path.join(self.build_dir, 'utils', '%s.php' % script)]
+ cmd.extend(['--%s' % x for x in args])
+ for k, v in kwargs.items():
+ cmd.extend(('--' + k.replace('_', '-'), str(v)))
+ proc = subprocess.Popen(cmd, cwd=self.build_dir,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (outp, outerr) = proc.communicate()
+ logger.debug("run_nominatim_script: %s\n%s\n%s" % (cmd, outp, outerr))
+ assert (proc.returncode == 0), "Script '%s' failed:\n%s\n%s\n" % (script, outp, outerr)
+
+
+class OSMDataFactory(object):
+
+ def __init__(self):
+ scriptpath = os.path.dirname(os.path.abspath(__file__))
+ self.scene_path = os.environ.get('SCENE_PATH',
+ os.path.join(scriptpath, '..', 'scenes', 'data'))
+ self.scene_cache = {}
+
+ def parse_geometry(self, geom, scene):
+ if geom.find(':') >= 0:
+ out = self.get_scene_geometry(scene, geom)
+ elif geom.find(',') < 0:
+ out = "'POINT(%s)'::geometry" % geom
+ elif geom.find('(') < 0:
+ out = "'LINESTRING(%s)'::geometry" % geom
+ else:
+ out = "'POLYGON(%s)'::geometry" % geom
+
+ return "ST_SetSRID(%s, 4326)" % out
+
+ def get_scene_geometry(self, default_scene, name):
+ geoms = []
+ for obj in name.split('+'):
+ oname = obj.strip()
+ if oname.startswith(':'):
+ assert_is_not_none(default_scene, "You need to set a scene")
+ defscene = self.load_scene(default_scene)
+ wkt = defscene[oname[1:]]
+ else:
+ scene, obj = oname.split(':', 2)
+ scene_geoms = self.load_scene(scene)
+ wkt = scene_geoms[obj]
+
+ geoms.append("'%s'::geometry" % wkt)
+
+ if len(geoms) == 1:
+ return geoms[0]
+ else:
+ return 'ST_LineMerge(ST_Collect(ARRAY[%s]))' % ','.join(geoms)
+
+ def load_scene(self, name):
+ if name in self.scene_cache:
+ return self.scene_cache[name]
+
+ scene = {}
+ with open(os.path.join(self.scene_path, "%s.wkt" % name), 'r') as fd:
+ for line in fd:
+ if line.strip():
+ obj, wkt = line.split('|', 2)
+ scene[obj.strip()] = wkt.strip()
+ self.scene_cache[name] = scene
+
+ return scene
+
+
+def before_all(context):
+ # logging setup
+ context.config.setup_logging()
+ # set up -D options
+ for k,v in userconfig.items():
+ context.config.userdata.setdefault(k, v)
+ logging.debug('User config: %s' %(str(context.config.userdata)))
+ # Nominatim test setup
+ context.nominatim = NominatimEnvironment(context.config.userdata)
+ context.osm = OSMDataFactory()
+
+def after_all(context):
+ context.nominatim.cleanup()
+
+
+def before_scenario(context, scenario):
+ if 'DB' in context.tags:
+ context.nominatim.setup_db(context)
+ elif 'APIDB' in context.tags:
+ context.nominatim.setup_api_db(context)
+ context.scene = None
+
+def after_scenario(context, scenario):
+ if 'DB' in context.tags:
+ context.nominatim.teardown_db(context)
+
--- /dev/null
+@DB
+Feature: Import of objects with broken geometries by osm2pgsql
+
+ Scenario: Import way with double nodes
+ When loading osm data
+ """
+ n100 x0 y0
+ n101 x0 y0.1
+ n102 x0.1 y0.2
+ w1 Thighway=primary Nn100,n101,n101,n102
+ """
+ Then place contains
+ | object | class | type | geometry |
+ | W1 | highway | primary | 0 0, 0 0.1, 0.1 0.2 |
+
+ @wip
+ Scenario: Import of ballon areas
+ When loading osm data
+ """
+ n1 x0 y0
+ n2 x0 y0.0001
+ n3 x0.00001 y0.0001
+ n4 x0.00001 y0
+ n5 x-0.00001 y0
+ w1 Thighway=unclassified Nn1,n2,n3,n4,n1,n5
+ w2 Thighway=unclassified Nn1,n2,n3,n4,n1
+ w3 Thighway=unclassified Nn1,n2,n3,n4,n3
+ """
+ Then place contains
+ | object | geometrytype |
+ | W1 | ST_LineString |
+ | W2 | ST_Polygon |
+ | W3 | ST_LineString |
--- /dev/null
+@DB
+Feature: Import of relations by osm2pgsql
+ Testing specific relation problems related to members.
+
+ Scenario: Don't import empty waterways
+ When loading osm data
+ """
+ n1 Tamenity=prison,name=foo
+ r1 Ttype=waterway,waterway=river,name=XZ Mn1@
+ """
+ Then place has no entry for R1
--- /dev/null
+@DB
+Feature: Import of simple objects by osm2pgsql
+ Testing basic tagging in osm2pgsql imports.
+
+ Scenario: Import simple objects
+ When loading osm data
+ """
+ n1 Tamenity=prison,name=foo x34.3 y-23
+ n100 x0 y0
+ n101 x0 y0.1
+ n102 x0.1 y0.2
+ n200 x0 y0
+ n201 x0 y1
+ n202 x1 y1
+ n203 x1 y0
+ w1 Tshop=toys,name=tata Nn100,n101,n102
+ w2 Tref=45 Nn200,n201,n202,n203,n200
+ r1 Ttype=multipolygon,tourism=hotel,name=XZ Mn1@,w2@
+ """
+ Then place contains exactly
+ | object | class | type | name | geometry |
+ | N1 | amenity | prison | 'name' : 'foo' | 34.3 -23 |
+ | W1 | shop | toys | 'name' : 'tata' | 0 0, 0 0.1, 0.1 0.2 |
+ | R1 | tourism | hotel | 'name' : 'XZ' | (0 0, 0 1, 1 1, 1 0, 0 0) |
+
+ Scenario: Import object with two main tags
+ When loading osm data
+ """
+ n1 Ttourism=hotel,amenity=restaurant,name=foo
+ """
+ Then place contains
+ | object | type | name |
+ | N1:tourism | hotel | 'name' : 'foo' |
+ | N1:amenity | restaurant | 'name' : 'foo' |
+
+ Scenario: Import stand-alone house number with postcode
+ When loading osm data
+ """
+ n1 Taddr:housenumber=4,addr:postcode=3345
+ """
+ Then place contains
+ | object | class | type |
+ | N1 | place | house |
+
+ Scenario: Landuses are only imported when named
+ When loading osm data
+ """
+ n100 x0 y0
+ n101 x0 y0.1
+ n102 x0.1 y0.1
+ n200 x0 y0
+ n202 x1 y1
+ n203 x1 y0
+ w1 Tlanduse=residential,name=rainbow Nn100,n101,n102,n100
+ w2 Tlanduse=residential Nn200,n202,n203,n200
+ """
+ Then place contains exactly
+ | object | class | type |
+ | W1 | landuse | residential |
--- /dev/null
+@DB
+Feature: Tag evaluation
+ Tests if tags are correctly imported into the place table
+
+ Scenario Outline: Name tags
+ When loading osm data
+ """
+ n1 Thighway=yes,<nametag>=Foo
+ """
+ Then place contains
+ | object | name |
+ | N1 | '<nametag>' : 'Foo' |
+
+ Examples:
+ | nametag |
+ | ref |
+ | int_ref |
+ | nat_ref |
+ | reg_ref |
+ | loc_ref |
+ | old_ref |
+ | iata |
+ | icao |
+ | pcode:1 |
+ | pcode:2 |
+ | pcode:3 |
+ | name |
+ | name:de |
+ | name:bt-BR |
+ | int_name |
+ | int_name:xxx |
+ | nat_name |
+ | nat_name:fr |
+ | reg_name |
+ | reg_name:1 |
+ | loc_name |
+ | loc_name:DE |
+ | old_name |
+ | old_name:v1 |
+ | alt_name |
+ | alt_name:dfe |
+ | alt_name_1 |
+ | official_name |
+ | short_name |
+ | short_name:CH |
+ | addr:housename |
+ | brand |
+
+ Scenario: operator only for shops and amenities
+ When loading osm data
+ """
+ n1 Thighway=yes,operator=Foo,name=null
+ n2 Tshop=grocery,operator=Foo
+ n3 Tamenity=hospital,operator=Foo
+ n4 Ttourism=hotel,operator=Foo
+ """
+ Then place contains
+ | object | name |
+ | N1 | 'name' : 'null' |
+ | N2 | 'operator' : 'Foo' |
+ | N3 | 'operator' : 'Foo' |
+ | N4 | 'operator' : 'Foo' |
+
+ Scenario Outline: Ignored name tags
+ When loading osm data
+ """
+ n1 Thighway=yes,<nametag>=Foo,name=real
+ """
+ Then place contains
+ | object | name |
+ | N1 | 'name' : 'real' |
+
+ Examples:
+ | nametag |
+ | name_de |
+ | Name |
+ | ref:de |
+ | ref_de |
+ | my:ref |
+ | br:name |
+ | name:prefix |
+ | name:source |
+
+ Scenario: Special character in name tag
+ When loading osm data
+ """
+ n1 Thighway=yes,name:%20%de=Foo,name=real1
+ n2 Thighway=yes,name:%a%de=Foo,name=real2
+ n3 Thighway=yes,name:%9%de=Foo,name:\\=real3
+ n4 Thighway=yes,name:%9%de=Foo,name=rea\l3
+ """
+ Then place contains
+ | object | name |
+ | N1 | 'name: de' : 'Foo', 'name' : 'real1' |
+ | N2 | 'name: de' : 'Foo', 'name' : 'real2' |
+ | N3 | 'name: de' : 'Foo', 'name:\\\\' : 'real3' |
+ | N4 | 'name: de' : 'Foo', 'name' : 'rea\\l3' |
+
+ Scenario Outline: Included places
+ When loading osm data
+ """
+ n1 T<key>=<value>,name=real
+ """
+ Then place contains
+ | object | name |
+ | N1 | 'name' : 'real' |
+
+ Examples:
+ | key | value |
+ | emergency | phone |
+ | tourism | information |
+ | historic | castle |
+ | military | barracks |
+ | natural | water |
+ | highway | residential |
+ | aerialway | station |
+ | aeroway | way |
+ | boundary | administrative |
+ | craft | butcher |
+ | leisure | playground |
+ | office | bookmaker |
+ | railway | rail |
+ | shop | bookshop |
+ | waterway | stream |
+ | landuse | cemetry |
+ | man_made | tower |
+ | mountain_pass | yes |
+
+ Scenario Outline: Bridges and Tunnels take special name tags
+ When loading osm data
+ """
+ n1 Thighway=road,<key>=yes,name=Rd,<key>:name=My
+ n2 Thighway=road,<key>=yes,name=Rd
+ """
+ Then place contains
+ | object | type | name |
+ | N1:highway | road | 'name' : 'Rd' |
+ | N1:<key> | yes | 'name' : 'My' |
+ | N2:highway | road | 'name' : 'Rd' |
+ And place has no entry for N2:<key>
+
+ Examples:
+ | key |
+ | bridge |
+ | tunnel |
+
+ Scenario Outline: Excluded places
+ When loading osm data
+ """
+ n1 T<key>=<value>,name=real
+ n2 Thighway=motorway,name=To%20%Hell
+ """
+ Then place has no entry for N1
+
+ Examples:
+ | key | value |
+ | emergency | yes |
+ | emergency | no |
+ | tourism | yes |
+ | tourism | no |
+ | historic | yes |
+ | historic | no |
+ | military | yes |
+ | military | no |
+ | natural | yes |
+ | natural | no |
+ | highway | no |
+ | highway | turning_circle |
+ | highway | mini_roundabout |
+ | highway | noexit |
+ | highway | crossing |
+ | aerialway | no |
+ | aerialway | pylon |
+ | man_made | survey_point |
+ | man_made | cutline |
+ | aeroway | no |
+ | amenity | no |
+ | bridge | no |
+ | craft | no |
+ | leisure | no |
+ | office | no |
+ | railway | no |
+ | railway | level_crossing |
+ | shop | no |
+ | tunnel | no |
+ | waterway | riverbank |
+
+ Scenario Outline: Some tags only are included when named
+ When loading osm data
+ """
+ n1 T<key>=<value>
+ n2 T<key>=<value>,name=To%20%Hell
+ n3 T<key>=<value>,ref=123
+ """
+ Then place contains exactly
+ | object | class | type |
+ | N2 | <key> | <value> |
+
+ Examples:
+ | key | value |
+ | landuse | residential |
+ | natural | meadow |
+ | highway | traffic_signals |
+ | highway | service |
+ | highway | cycleway |
+ | highway | path |
+ | highway | footway |
+ | highway | steps |
+ | highway | bridleway |
+ | highway | track |
+ | highway | byway |
+ | highway | motorway_link |
+ | highway | primary_link |
+ | highway | trunk_link |
+ | highway | secondary_link |
+ | highway | tertiary_link |
+ | railway | rail |
+ | boundary | administrative |
+ | waterway | stream |
+
+ Scenario: Footways are not included if they are sidewalks
+ When loading osm data
+ """
+ n2 Thighway=footway,name=To%20%Hell,footway=sidewalk
+ n23 Thighway=footway,name=x
+ """
+ Then place has no entry for N2
+
+ Scenario: named junctions are included if there is no other tag
+ When loading osm data
+ """
+ n1 Tjunction=yes
+ n2 Thighway=secondary,junction=roundabout,name=To-Hell
+ n3 Tjunction=yes,name=Le%20%Croix
+ """
+ Then place has no entry for N1
+ And place has no entry for N2:junction
+ And place contains
+ | object | class | type |
+ | N3 | junction | yes |
+
+ Scenario: Boundary with place tag
+ When loading osm data
+ """
+ n200 x0 y0
+ n201 x0 y1
+ n202 x1 y1
+ n203 x1 y0
+ w2 Tboundary=administrative,place=city,name=Foo Nn200,n201,n202,n203,n200
+ w4 Tboundary=administrative,place=island,name=Foo Nn200,n201,n202,n203,n200
+ w20 Tplace=city,name=ngng Nn200,n201,n202,n203,n200
+ w40 Tplace=city,boundary=statistical,name=BB Nn200,n201,n202,n203,n200
+ """
+ Then place contains
+ | object | class | extratags | type |
+ | W2 | boundary | 'place' : 'city' | administrative |
+ | W4:boundary | boundary | - | administrative |
+ | W4:place | place | - | island |
+ | W20 | place | - | city |
+ | W40:boundary | boundary | - | statistical |
+ | W40:place | place | - | city |
+ And place has no entry for W2:place
+
+ Scenario Outline: Tags that describe a house
+ When loading osm data
+ """
+ n100 T<key>=<value>
+ n999 Tamenity=prison,<key>=<value>
+ """
+ Then place contains exactly
+ | object | class | type |
+ | N100 | place | house |
+ | N999 | amenity | prison |
+
+ Examples:
+ | key | value |
+ | addr:housename | My%20%Mansion |
+ | addr:housenumber | 456 |
+ | addr:conscriptionnumber | 4 |
+ | addr:streetnumber | 4568765 |
+
+ Scenario: Only named with no other interesting tag
+ When loading osm data
+ """
+ n1 Tlanduse=meadow
+ n2 Tlanduse=residential,name=important
+ n3 Tlanduse=residential,name=important,place=hamlet
+ """
+ Then place contains
+ | object | class | type |
+ | N2 | landuse | residential |
+ | N3 | place | hamlet |
+ And place has no entry for N1
+ And place has no entry for N3:landuse
+
+ Scenario Outline: Import of postal codes
+ When loading osm data
+ """
+ n10 Thighway=secondary,<key>=<value>
+ n11 T<key>=<value>
+ """
+ Then place contains
+ | object | class | type | postcode |
+ | N10 | highway | secondary | <value> |
+ | N11 | place | postcode | <value> |
+ And place has no entry for N10:place
+
+ Examples:
+ | key | value |
+ | postal_code | 45736 |
+ | postcode | xxx |
+ | addr:postcode | 564 |
+ | tiger:zip_left | 00011 |
+ | tiger:zip_right | 09123 |
+
+ Scenario: Import of street and place
+ When loading osm data
+ """
+ n10 Tamenity=hospital,addr:street=Foo%20%St
+ n20 Tamenity=hospital,addr:place=Foo%20%Town
+ """
+ Then place contains
+ | object | class | type | street | addr_place |
+ | N10 | amenity | hospital | Foo St | None |
+ | N20 | amenity | hospital | - | Foo Town |
+
+
+ Scenario Outline: Import of country
+ When loading osm data
+ """
+ n10 Tplace=village,<key>=<value>
+ """
+ Then place contains
+ | object | class | type | country_code |
+ | N10 | place | village | <value> |
+
+ Examples:
+ | key | value |
+ | country_code | us |
+ | ISO3166-1 | XX |
+ | is_in:country_code | __ |
+ | addr:country | .. |
+ | addr:country_code | cv |
+
+ Scenario Outline: Ignore country codes with wrong length
+ When loading osm data
+ """
+ n10 Tplace=village,country_code=<value>
+ """
+ Then place contains
+ | object | class | type | country_code |
+ | N10 | place | village | - |
+
+ Examples:
+ | value |
+ | X |
+ | x |
+ | ger |
+ | dkeufr |
+ | d%20%e |
+
+ Scenario: Import of house numbers
+ When loading osm data
+ """
+ n10 Tbuilding=yes,addr:housenumber=4b
+ n11 Tbuilding=yes,addr:conscriptionnumber=003
+ n12 Tbuilding=yes,addr:streetnumber=2345
+ n13 Tbuilding=yes,addr:conscriptionnumber=3,addr:streetnumber=111
+ """
+ Then place contains
+ | object | class | type | housenumber |
+ | N10 | building | yes | 4b |
+ | N11 | building | yes | 003 |
+ | N12 | building | yes | 2345 |
+ | N13 | building | yes | 3/111 |
+
+ Scenario: Import of address interpolations
+ When loading osm data
+ """
+ n10 Taddr:interpolation=odd
+ n11 Taddr:housenumber=10,addr:interpolation=odd
+ n12 Taddr:interpolation=odd,addr:housenumber=23
+ """
+ Then place contains
+ | object | class | type | housenumber |
+ | N10 | place | houses | odd |
+ | N11 | place | houses | odd |
+ | N12 | place | houses | odd |
+
+ Scenario: Shorten tiger:county tags
+ When loading osm data
+ """
+ n10 Tplace=village,tiger:county=Feebourgh%2c%%20%AL
+ n11 Tplace=village,addr:state=Alabama,tiger:county=Feebourgh%2c%%20%AL
+ n12 Tplace=village,tiger:county=Feebourgh
+ """
+ Then place contains
+ | object | class | type | isin |
+ | N10 | place | village | Feebourgh county |
+ | N11 | place | village | Alabama,Feebourgh county |
+ | N12 | place | village | Feebourgh county |
+
+ Scenario Outline: Import of address tags
+ When loading osm data
+ """
+ n10 Tplace=village,<key>=<value>
+ """
+ Then place contains
+ | object | class | type | isin |
+ | N10 | place | village | <value> |
+
+ Examples:
+ | key | value |
+ | is_in:country | Xanadu |
+ | addr:suburb | hinein |
+ | addr:city | Sydney |
+ | addr:state | Jura |
+
+ Scenario: Import of isin tags with space
+ When loading osm data
+ """
+ n10 Tplace=village,is_in=Stockholm%2c%%20%Sweden
+ n11 Tplace=village,addr:county=le%20%havre
+ """
+ Then place contains
+ | object | class | type | isin |
+ | N10 | place | village | Stockholm, Sweden |
+ | N11 | place | village | le havre |
+
+ Scenario: Import of admin level
+ When loading osm data
+ """
+ n10 Tamenity=hospital,admin_level=3
+ n11 Tamenity=hospital,admin_level=b
+ n12 Tamenity=hospital
+ n13 Tamenity=hospital,admin_level=3.0
+ """
+ Then place contains
+ | object | class | type | admin_level |
+ | N10 | amenity | hospital | 3 |
+ | N11 | amenity | hospital | 100 |
+ | N12 | amenity | hospital | 100 |
+ | N13 | amenity | hospital | 3 |
+
+ Scenario Outline: Import of extra tags
+ When loading osm data
+ """
+ n10 Ttourism=hotel,<key>=foo
+ """
+ Then place contains
+ | object | class | type | extratags |
+ | N10 | tourism | hotel | '<key>' : 'foo' |
+
+ Examples:
+ | key |
+ | tracktype |
+ | traffic_calming |
+ | service |
+ | cuisine |
+ | capital |
+ | dispensing |
+ | religion |
+ | denomination |
+ | sport |
+ | internet_access |
+ | lanes |
+ | surface |
+ | smoothness |
+ | width |
+ | est_width |
+ | incline |
+ | opening_hours |
+ | collection_times |
+ | service_times |
+ | disused |
+ | wheelchair |
+ | sac_scale |
+ | trail_visibility |
+ | mtb:scale |
+ | mtb:description |
+ | wood |
+ | drive_in |
+ | access |
+ | vehicle |
+ | bicyle |
+ | foot |
+ | goods |
+ | hgv |
+ | motor_vehicle |
+ | motor_car |
+ | access:foot |
+ | contact:phone |
+ | drink:mate |
+ | oneway |
+ | date_on |
+ | date_off |
+ | day_on |
+ | day_off |
+ | hour_on |
+ | hour_off |
+ | maxweight |
+ | maxheight |
+ | maxspeed |
+ | disused |
+ | toll |
+ | charge |
+ | population |
+ | description |
+ | image |
+ | attribution |
+ | fax |
+ | email |
+ | url |
+ | website |
+ | phone |
+ | real_ale |
+ | smoking |
+ | food |
+ | camera |
+ | brewery |
+ | locality |
+ | wikipedia |
+ | wikipedia:de |
+ | wikidata |
+ | name:prefix |
+ | name:botanical |
+ | name:etymology:wikidata |
+
+ Scenario: buildings
+ When loading osm data
+ """
+ n10 Ttourism=hotel,building=yes
+ n11 Tbuilding=house
+ n12 Tbuilding=shed,addr:housenumber=1
+ n13 Tbuilding=yes,name=Das-Haus
+ n14 Tbuilding=yes,addr:postcode=12345
+ """
+ Then place contains
+ | object | class | type |
+ | N10 | tourism | hotel |
+ | N12 | building| yes |
+ | N13 | building| yes |
+ | N14 | building| yes |
+ And place has no entry for N10:building
+ And place has no entry for N11
+
+ Scenario: complete node entry
+ When loading osm data
+ """
+ n290393920 Taddr:city=Perpignan,addr:country=FR,addr:housenumber=43\,addr:postcode=66000,addr:street=Rue%20%Pierre%20%Constant%20%d`Ivry,source=cadastre-dgi-fr%20%source%20%:%20%Direction%20%Générale%20%des%20%Impôts%20%-%20%Cadastre%20%;%20%mise%20%à%20%jour%20%:2008
+ """
+ Then place contains
+ | object | class | type | housenumber |
+ | N290393920 | place | house| 43\ |
--- /dev/null
+@DB
+Feature: Update of relations by osm2pgsql
+ Testing relation update by osm2pgsql.
+
+ Scenario: Remove all members of a relation
+ When loading osm data
+ """
+ n1 Tamenity=prison,name=foo
+ n200 x0 y0
+ n201 x0 y0.0001
+ n202 x0.0001 y0.0001
+ n203 x0.0001 y0
+ w2 Tref=45' Nn200,n201,n202,n203,n200
+ r1 Ttype=multipolygon,tourism=hotel,name=XZ' Mw2@
+ """
+ Then place contains
+ | object | class | type | name
+ | R1 | tourism | hotel | 'name' : 'XZ'
+ When updating osm data
+ """
+ r1 Ttype=multipolygon,tourism=hotel,name=XZ Mn1@
+ """
+ Then place has no entry for R1
+
+
+ Scenario: Change type of a relation
+ When loading osm data
+ """
+ n200 x0 y0
+ n201 x0 y0.0001
+ n202 x0.0001 y0.0001
+ n203 x0.0001 y0
+ w2 Tref=45 Nn200,n201,n202,n203,n200
+ r1 Ttype=multipolygon,tourism=hotel,name=XZ Mw2@
+ """
+ Then place contains
+ | object | class | type | name
+ | R1 | tourism | hotel | 'name' : 'XZ'
+ When updating osm data
+ """
+ r1 Ttype=multipolygon,amenity=prison,name=XZ Mw2@
+ """
+ Then place has no entry for R1:tourism
+ And place contains
+ | object | class | type | name
+ | R1 | amenity | prison | 'name' : 'XZ'
+
+ Scenario: Change name of a relation
+ When loading osm data
+ """
+ n200 x0 y0
+ n201 x0 y0.0001
+ n202 x0.0001 y0.0001
+ n203 x0.0001 y0
+ w2 Tref=45 Nn200,n201,n202,n203,n200
+ r1 Ttype=multipolygon,tourism=hotel,name=AB Mw2@
+ """
+ Then place contains
+ | object | class | type | name
+ | R1 | tourism | hotel | 'name' : 'AB'
+ When updating osm data
+ """
+ r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
+ """
+ Then place contains
+ | object | class | type | name
+ | R1 | tourism | hotel | 'name' : 'XZ'
+
+ Scenario: Change type of a relation into something unknown
+ When loading osm data
+ """
+ n200 x0 y0
+ n201 x0 y0.0001
+ n202 x0.0001 y0.0001
+ n203 x0.0001 y0
+ w2 Tref=45 Nn200,n201,n202,n203,n200
+ r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
+ """
+ Then place contains
+ | object | class | type | name
+ | R1 | tourism | hotel | 'name' : 'XZ'
+ When updating osm data
+ """
+ r1 Ttype=multipolygon,amenities=prison,name=XY Mw2@
+ """
+ Then place has no entry for R1
+
+ Scenario: Type tag is removed
+ When loading osm data
+ """
+ n200 x0 y0
+ n201 x0 y0.0001
+ n202 x0.0001 y0.0001
+ n203 x0.0001 y0
+ w2 Tref=45 Nn200,n201,n202,n203,n200
+ r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
+ """
+ Then place contains
+ | object | class | type | name
+ | R1 | tourism | hotel | 'name' : 'XZ'
+ When updating osm data
+ """
+ r1 Ttourism=hotel,name=XY Mw2@
+ """
+ Then place has no entry for R1
+
+ @wip
+ Scenario: Type tag is renamed to something unknown
+ When loading osm data
+ """
+ n200 x0 y0
+ n201 x0 y0.0001
+ n202 x0.0001 y0.0001
+ n203 x0.0001 y0
+ w2 Tref=45 Nn200,n201,n202,n203,n200
+ r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
+ """
+ Then place contains
+ | object | class | type | name
+ | R1 | tourism | hotel | 'name' : 'XZ'
+ When updating osm data
+ """
+ r1 Ttype=multipolygonn,tourism=hotel,name=XY Mw2@
+ """
+ Then place has no entry for R1
+
--- /dev/null
+@DB
+Feature: Update of simple objects by osm2pgsql
+ Testing basic update functions of osm2pgsql.
+
+ Scenario: Import object with two main tags
+ When loading osm data
+ """
+ n1 Ttourism=hotel,amenity=restaurant,name=foo
+ n2 Tplace=locality,name=spotty
+ """
+ Then place contains
+ | object | type | name
+ | N1:tourism | hotel | 'name' : 'foo'
+ | N1:amenity | restaurant | 'name' : 'foo'
+ | N2:place | locality | 'name' : 'spotty'
+ When updating osm data
+ """
+ n1 dV Ttourism=hotel,name=foo
+ n2 dD
+ """
+ Then place has no entry for N1:amenity
+ And place has no entry for N2
+ And place contains
+ | object | class | type | name
+ | N1:tourism | tourism | hotel | 'name' : 'foo'
+
--- /dev/null
+import base64
+import random
+import string
+import re
+from nose.tools import * # for assert functions
+import psycopg2.extras
+
+class PlaceColumn:
+
+ def __init__(self, context, force_name):
+ self.columns = { 'admin_level' : 100}
+ self.force_name = force_name
+ self.context = context
+ self.geometry = None
+
+ 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)
+ else:
+ assert_in(key, ('class', 'type', 'street', 'addr_place',
+ 'isin', 'postcode'))
+ 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_in(value[0], 'NRW')
+ ok_(value[1:].isdigit())
+
+ 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):
+ self.columns['housenumber'] = None if value == '' else value
+
+ def set_key_country(self, value):
+ self.columns['country_code'] = None if value == '' else value
+
+ def set_key_geometry(self, value):
+ self.geometry = self.context.osm.parse_geometry(value, self.context.scene)
+ assert_is_not_none(self.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_insert(self, cursor):
+ assert_in('osm_type', self.columns)
+ if self.force_name and 'name' not in self.columns:
+ self.add_hstore('name', 'name', ''.join(random.choice(string.printable)
+ for _ in range(int(random.random()*30))))
+
+ if self.columns['osm_type'] == 'N' and self.geometry is None:
+ self.geometry = "ST_SetSRID(ST_Point(%f, %f), 4326)" % (
+ random.random()*360 - 180, random.random()*180 - 90)
+ else:
+ assert_is_not_none(self.geometry, "Geometry missing")
+ query = 'INSERT INTO place (%s, geometry) values(%s, %s)' % (
+ ','.join(self.columns.keys()),
+ ','.join(['%s' for x in range(len(self.columns))]),
+ self.geometry)
+ cursor.execute(query, list(self.columns.values()))
+
+class NominatimID:
+ """ Splits a unique identifier for places into its components.
+ As place_ids cannot be used for testing, we use a unique
+ identifier instead that is of the form <osmtype><osmid>[:<class>].
+ """
+
+ id_regex = re.compile(r"(?P<tp>[NRW])(?P<id>\d+)(:(?P<cls>\w+))?")
+
+ def __init__(self, oid):
+ self.typ = self.oid = self.cls = None
+
+ if oid is not None:
+ m = self.id_regex.fullmatch(oid)
+ assert_is_not_none(m, "ID '%s' not of form <osmtype><osmid>[:<class>]" % oid)
+
+ self.typ = m.group('tp')
+ self.oid = m.group('id')
+ self.cls = m.group('cls')
+
+ def table_select(self):
+ """ Return where clause and parameter list to select the object
+ from a Nominatim table.
+ """
+ where = 'osm_type = %s and osm_id = %s'
+ params = [self.typ, self. oid]
+
+ if self.cls is not None:
+ where += ' and class = %s'
+ params.append(self.cls)
+
+ return where, params
+
+ def get_place_id(self, cur):
+ where, params = self.table_select()
+ cur.execute("SELECT place_id FROM placex WHERE %s" % where, params)
+ eq_(1, cur.rowcount, "Expected exactly 1 entry in placex found %s" % cur.rowcount)
+
+ return cur.fetchone()[0]
+
+
+def assert_db_column(row, column, value, context):
+ if column == 'object':
+ return
+
+ if column.startswith('centroid'):
+ fac = float(column[9:]) if column.startswith('centroid*') else 1.0
+ x, y = value.split(' ')
+ assert_almost_equal(float(x) * fac, row['cx'], "Bad x coordinate")
+ assert_almost_equal(float(y) * fac, row['cy'], "Bad y coordinate")
+ elif column == 'geometry':
+ geom = context.osm.parse_geometry(value, context.scene)
+ cur = context.db.cursor()
+ query = "SELECT ST_Equals(ST_SnapToGrid(%s, 0.00001, 0.00001), ST_SnapToGrid(ST_SetSRID('%s'::geometry, 4326), 0.00001, 0.00001))" % (
+ geom, row['geomtxt'],)
+ cur.execute(query)
+ eq_(cur.fetchone()[0], True, "(Row %s failed: %s)" % (column, query))
+ elif value == '-':
+ assert_is_none(row[column], "Row %s" % column)
+ else:
+ eq_(value, str(row[column]),
+ "Row '%s': expected: %s, got: %s"
+ % (column, value, str(row[column])))
+
+
+################################ STEPS ##################################
+
+@given(u'the scene (?P<scene>.+)')
+def set_default_scene(context, scene):
+ context.scene = scene
+
+@given("the (?P<named>named )?places")
+def add_data_to_place_table(context, named):
+ cur = context.db.cursor()
+ cur.execute('ALTER TABLE place DISABLE TRIGGER place_before_insert')
+ for r in context.table:
+ col = PlaceColumn(context, named is not None)
+
+ for h in r.headings:
+ col.add(h, r[h])
+
+ col.db_insert(cur)
+ cur.execute('ALTER TABLE place ENABLE TRIGGER place_before_insert')
+ cur.close()
+ context.db.commit()
+
+@given("the relations")
+def add_data_to_planet_relations(context):
+ cur = context.db.cursor()
+ for r in context.table:
+ last_node = 0
+ last_way = 0
+ parts = []
+ if r['members']:
+ members = []
+ for m in r['members'].split(','):
+ mid = NominatimID(m)
+ if mid.typ == 'N':
+ parts.insert(last_node, int(mid.oid))
+ last_node += 1
+ last_way += 1
+ elif mid.typ == 'W':
+ parts.insert(last_way, int(mid.oid))
+ last_way += 1
+ else:
+ parts.append(int(mid.oid))
+
+ members.extend((mid.typ.lower() + mid.oid, mid.cls or ''))
+ else:
+ members = None
+
+ tags = []
+ for h in r.headings:
+ if h.startswith("tags+"):
+ tags.extend((h[5:], r[h]))
+
+ cur.execute("""INSERT INTO planet_osm_rels (id, way_off, rel_off, parts, members, tags)
+ VALUES (%s, %s, %s, %s, %s, %s)""",
+ (r['id'], last_node, last_way, parts, members, tags))
+ context.db.commit()
+
+@given("the ways")
+def add_data_to_planet_ways(context):
+ cur = context.db.cursor()
+ for r in context.table:
+ tags = []
+ for h in r.headings:
+ if h.startswith("tags+"):
+ tags.extend((h[5:], r[h]))
+
+ nodes = [ int(x.strip()) for x in r['nodes'].split(',') ]
+
+ cur.execute("INSERT INTO planet_osm_ways (id, nodes, tags) VALUES (%s, %s, %s)",
+ (r['id'], nodes, tags))
+ context.db.commit()
+
+@when("importing")
+def import_and_index_data_from_place_table(context):
+ context.nominatim.run_setup_script('create-functions', 'create-partition-functions')
+ cur = context.db.cursor()
+ cur.execute(
+ """insert into placex (osm_type, osm_id, class, type, name, admin_level,
+ housenumber, street, addr_place, isin, postcode, country_code, extratags,
+ geometry)
+ select * from place where not (class='place' and type='houses' and osm_type='W')""")
+ cur.execute(
+ """select insert_osmline (osm_id, housenumber, street, addr_place,
+ postcode, country_code, geometry)
+ from place where class='place' and type='houses' and osm_type='W'""")
+ context.db.commit()
+ context.nominatim.run_setup_script('index', 'index-noanalyse')
+
+@when("updating places")
+def update_place_table(context):
+ context.nominatim.run_setup_script(
+ 'create-functions', 'create-partition-functions', 'enable-diff-updates')
+ cur = context.db.cursor()
+ for r in context.table:
+ col = PlaceColumn(context, False)
+
+ for h in r.headings:
+ col.add(h, r[h])
+
+ col.db_insert(cur)
+
+ context.db.commit()
+ context.nominatim.run_update_script('index')
+
+@when("marking for delete (?P<oids>.*)")
+def delete_places(context, oids):
+ context.nominatim.run_setup_script(
+ 'create-functions', 'create-partition-functions', 'enable-diff-updates')
+ cur = context.db.cursor()
+ for oid in oids.split(','):
+ where, params = NominatimID(oid).table_select()
+ cur.execute("DELETE FROM place WHERE " + where, params)
+ context.db.commit()
+ context.nominatim.run_update_script('index')
+
+@then("placex contains(?P<exact> exactly)?")
+def check_placex_contents(context, exact):
+ cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
+
+ expected_content = set()
+ for row in context.table:
+ nid = NominatimID(row['object'])
+ where, params = nid.table_select()
+ cur.execute("""SELECT *, ST_AsText(geometry) as geomtxt,
+ ST_X(centroid) as cx, ST_Y(centroid) as cy
+ FROM placex where %s""" % where,
+ params)
+ assert_less(0, cur.rowcount, "No rows found for " + row['object'])
+
+ for res in cur:
+ if exact:
+ expected_content.add((res['osm_type'], res['osm_id'], res['class']))
+ for h in row.headings:
+ if h.startswith('name'):
+ name = h[5:] if h.startswith('name+') else 'name'
+ assert_in(name, res['name'])
+ eq_(res['name'][name], row[h])
+ elif h.startswith('extratags+'):
+ eq_(res['extratags'][h[10:]], row[h])
+ elif h in ('linked_place_id', 'parent_place_id'):
+ if row[h] == '0':
+ eq_(0, res[h])
+ elif row[h] == '-':
+ assert_is_none(res[h])
+ else:
+ eq_(NominatimID(row[h]).get_place_id(context.db.cursor()),
+ res[h])
+ else:
+ assert_db_column(res, h, row[h], context)
+
+ if exact:
+ cur.execute('SELECT osm_type, osm_id, class from placex')
+ eq_(expected_content, set([(r[0], r[1], r[2]) for r in cur]))
+
+ context.db.commit()
+
+@then("place contains(?P<exact> exactly)?")
+def check_placex_contents(context, exact):
+ cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
+
+ expected_content = set()
+ for row in context.table:
+ nid = NominatimID(row['object'])
+ where, params = nid.table_select()
+ cur.execute("""SELECT *, ST_AsText(geometry) as geomtxt,
+ ST_GeometryType(geometry) as geometrytype
+ FROM place where %s""" % where,
+ params)
+ assert_less(0, cur.rowcount, "No rows found for " + row['object'])
+
+ for res in cur:
+ if exact:
+ expected_content.add((res['osm_type'], res['osm_id'], res['class']))
+ for h in row.headings:
+ msg = "%s: %s" % (row['object'], h)
+ if h in ('name', 'extratags'):
+ if row[h] == '-':
+ assert_is_none(res[h], msg)
+ else:
+ vdict = eval('{' + row[h] + '}')
+ assert_equals(vdict, res[h], msg)
+ elif h.startswith('name+'):
+ assert_equals(res['name'][h[5:]], row[h], msg)
+ elif h.startswith('extratags+'):
+ assert_equals(res['extratags'][h[10:]], row[h], msg)
+ elif h in ('linked_place_id', 'parent_place_id'):
+ if row[h] == '0':
+ assert_equals(0, res[h], msg)
+ elif row[h] == '-':
+ assert_is_none(res[h], msg)
+ else:
+ assert_equals(NominatimID(row[h]).get_place_id(context.db.cursor()),
+ res[h], msg)
+ else:
+ assert_db_column(res, h, row[h], context)
+
+ if exact:
+ cur.execute('SELECT osm_type, osm_id, class from place')
+ eq_(expected_content, set([(r[0], r[1], r[2]) for r in cur]))
+
+ context.db.commit()
+
+@then("search_name contains")
+def check_search_name_contents(context):
+ cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
+
+ for row in context.table:
+ pid = NominatimID(row['object']).get_place_id(cur)
+ cur.execute("""SELECT *, ST_X(centroid) as cx, ST_Y(centroid) as cy
+ FROM search_name WHERE place_id = %s""", (pid, ))
+ assert_less(0, cur.rowcount, "No rows found for " + row['object'])
+
+ for res in cur:
+ for h in row.headings:
+ if h in ('name_vector', 'nameaddress_vector'):
+ terms = [x.strip().replace('#', ' ') for x in row[h].split(',')]
+ subcur = context.db.cursor()
+ subcur.execute("""SELECT word_id, word_token
+ FROM word, (SELECT unnest(%s) as term) t
+ WHERE word_token = make_standard_name(t.term)""",
+ (terms,))
+ ok_(subcur.rowcount >= len(terms))
+ for wid in subcur:
+ assert_in(wid[0], res[h],
+ "Missing term for %s/%s: %s" % (pid, h, wid[1]))
+ else:
+ assert_db_column(res, h, row[h], context)
+
+
+ context.db.commit()
+
+@then("(?P<oid>\w+) expands to(?P<neg> no)? interpolation")
+def check_location_property_osmline(context, oid, neg):
+ cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
+ nid = NominatimID(oid)
+
+ eq_('W', nid.typ, "interpolation must be a way")
+
+ cur.execute("""SELECT *, ST_AsText(linegeo) as geomtxt
+ FROM location_property_osmline WHERE osm_id = %s""",
+ (nid.oid, ))
+
+ if neg:
+ eq_(0, cur.rowcount)
+ return
+
+ todo = list(range(len(list(context.table))))
+ for res in cur:
+ for i in todo:
+ row = context.table[i]
+ if (int(row['start']) == res['startnumber']
+ and int(row['end']) == res['endnumber']):
+ todo.remove(i)
+ break
+ else:
+ assert False, "Unexpected row %s" % (str(res))
+
+ for h in row.headings:
+ if h in ('start', 'end'):
+ continue
+ elif h == 'parent_place_id':
+ if row[h] == '0':
+ eq_(0, res[h])
+ elif row[h] == '-':
+ assert_is_none(res[h])
+ else:
+ eq_(NominatimID(row[h]).get_place_id(context.db.cursor()),
+ res[h])
+ else:
+ assert_db_column(res, h, row[h], context)
+
+ eq_(todo, [])
+
+
+@then("(?P<table>placex|place) has no entry for (?P<oid>.*)")
+def check_placex_has_entry(context, table, oid):
+ cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
+ nid = NominatimID(oid)
+ where, params = nid.table_select()
+ cur.execute("SELECT * FROM %s where %s" % (table, where), params)
+ eq_(0, cur.rowcount)
+ context.db.commit()
+
+@then("search_name has no entry for (?P<oid>.*)")
+def check_search_name_has_entry(context, oid):
+ cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
+ pid = NominatimID(oid).get_place_id(cur)
+ cur.execute("SELECT * FROM search_name WHERE place_id = %s", (pid, ))
+ eq_(0, cur.rowcount)
+ context.db.commit()
--- /dev/null
+import subprocess
+import tempfile
+import random
+import os
+from nose.tools import * # for assert functions
+
+@when(u'loading osm data')
+def load_osm_file(context):
+
+ # create a OSM file in /tmp and import it
+ with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.opl', delete=False) as fd:
+ fname = fd.name
+ for line in context.text.splitlines():
+ if line.startswith('n') and line.find(' x') < 0:
+ line += " x%d y%d" % (random.random() * 360 - 180,
+ random.random() * 180 - 90)
+ fd.write(line.encode('utf-8'))
+ fd.write(b'\n')
+
+ context.nominatim.run_setup_script('import-data', osm_file=fname,
+ osm2pgsql_cache=300)
+
+ ### reintroduce the triggers/indexes we've lost by having osm2pgsql set up place again
+ cur = context.db.cursor()
+ cur.execute("""CREATE TRIGGER place_before_delete BEFORE DELETE ON place
+ FOR EACH ROW EXECUTE PROCEDURE place_delete()""")
+ cur.execute("""CREATE TRIGGER place_before_insert BEFORE INSERT ON place
+ FOR EACH ROW EXECUTE PROCEDURE place_insert()""")
+ cur.execute("""CREATE UNIQUE INDEX idx_place_osm_unique on place using btree(osm_id,osm_type,class,type)""")
+ context.db.commit()
+
+ os.remove(fname)
+
+@when(u'updating osm data')
+def update_from_osm_file(context):
+ context.nominatim.run_setup_script('create-functions', 'create-partition-functions')
+
+ cur = context.db.cursor()
+ cur.execute("""insert into placex (osm_type, osm_id, class, type, name,
+ admin_level, housenumber, street, addr_place, isin, postcode,
+ country_code, extratags, geometry) select * from place""")
+ context.db.commit()
+ context.nominatim.run_setup_script('index', 'index-noanalyse')
+ context.nominatim.run_setup_script('create-functions', 'create-partition-functions',
+ 'enable-diff-updates')
+
+ # create a OSM file in /tmp and import it
+ with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.opl', delete=False) as fd:
+ fname = fd.name
+ for line in context.text.splitlines():
+ if line.startswith('n') and line.find(' x') < 0:
+ line += " x%d y%d" % (random.random() * 360 - 180,
+ random.random() * 180 - 90)
+ fd.write(line.encode('utf-8'))
+ fd.write(b'\n')
+
+ context.nominatim.run_update_script(import_diff=fname)
+ os.remove(fname)
--- /dev/null
+""" Steps that run search queries.
+
+ Queries may either be run directly via PHP using the query script
+ or via the HTTP interface.
+"""
+
+import json
+import os
+import io
+import re
+from tidylib import tidy_document
+import xml.etree.ElementTree as ET
+import subprocess
+from urllib.parse import urlencode
+from collections import OrderedDict
+from nose.tools import * # for assert functions
+
+BASE_SERVER_ENV = {
+ 'HTTP_HOST' : 'localhost',
+ 'HTTP_USER_AGENT' : 'Mozilla/5.0 (X11; Linux x86_64; rv:51.0) Gecko/20100101 Firefox/51.0',
+ 'HTTP_ACCEPT' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+ 'HTTP_ACCEPT_ENCODING' : 'gzip, deflate',
+ 'HTTP_CONNECTION' : 'keep-alive',
+ 'SERVER_SIGNATURE' : '<address>Nominatim BDD Tests</address>',
+ 'SERVER_SOFTWARE' : 'Nominatim test',
+ 'SERVER_NAME' : 'localhost',
+ 'SERVER_ADDR' : '127.0.1.1',
+ 'SERVER_PORT' : '80',
+ 'REMOTE_ADDR' : '127.0.0.1',
+ 'DOCUMENT_ROOT' : '/var/www',
+ 'REQUEST_SCHEME' : 'http',
+ 'CONTEXT_PREFIX' : '/',
+ 'SERVER_ADMIN' : 'webmaster@localhost',
+ 'REMOTE_PORT' : '49319',
+ 'GATEWAY_INTERFACE' : 'CGI/1.1',
+ 'SERVER_PROTOCOL' : 'HTTP/1.1',
+ 'REQUEST_METHOD' : 'GET',
+ 'REDIRECT_STATUS' : 'CGI'
+}
+
+
+def compare(operator, op1, op2):
+ if operator == 'less than':
+ return op1 < op2
+ elif operator == 'more than':
+ return op1 > op2
+ elif operator == 'exactly':
+ return op1 == op2
+ elif operator == 'at least':
+ return op1 >= op2
+ elif operator == 'at most':
+ return op1 <= op2
+ else:
+ raise Exception("unknown operator '%s'" % operator)
+
+class GenericResponse(object):
+
+ def match_row(self, row):
+ if 'ID' in row.headings:
+ todo = [int(row['ID'])]
+ else:
+ todo = range(len(self.result))
+
+ for i in todo:
+ res = self.result[i]
+ for h in row.headings:
+ if h == 'ID':
+ pass
+ elif h == 'osm':
+ assert_equal(res['osm_type'], row[h][0])
+ assert_equal(res['osm_id'], row[h][1:])
+ elif h == 'centroid':
+ x, y = row[h].split(' ')
+ assert_almost_equal(float(y), float(res['lat']))
+ assert_almost_equal(float(x), float(res['lon']))
+ elif row[h].startswith("^"):
+ assert_in(h, res)
+ assert_is_not_none(re.fullmatch(row[h], res[h]),
+ "attribute '%s': expected: '%s', got '%s'"
+ % (h, row[h], res[h]))
+ else:
+ assert_in(h, res)
+ assert_equal(str(res[h]), str(row[h]))
+
+ def property_list(self, prop):
+ return [ x[prop] for x in self.result ]
+
+
+class SearchResponse(GenericResponse):
+
+ def __init__(self, page, fmt='json', errorcode=200):
+ self.page = page
+ self.format = fmt
+ self.errorcode = errorcode
+ self.result = []
+ self.header = dict()
+
+ if errorcode == 200:
+ getattr(self, 'parse_' + fmt)()
+
+ def parse_json(self):
+ m = re.fullmatch(r'([\w$][^(]*)\((.*)\)', self.page)
+ if m is None:
+ code = self.page
+ else:
+ code = m.group(2)
+ self.header['json_func'] = m.group(1)
+ self.result = json.JSONDecoder(object_pairs_hook=OrderedDict).decode(code)
+
+ def parse_html(self):
+ content, errors = tidy_document(self.page,
+ options={'char-encoding' : 'utf8'})
+ #eq_(len(errors), 0 , "Errors found in HTML document:\n%s" % errors)
+
+ b = content.find('nominatim_results =')
+ e = content.find('</script>')
+ content = content[b:e]
+ b = content.find('[')
+ e = content.rfind(']')
+
+ self.result = json.JSONDecoder(object_pairs_hook=OrderedDict).decode(content[b:e+1])
+
+ def parse_xml(self):
+ et = ET.fromstring(self.page)
+
+ self.header = dict(et.attrib)
+
+ for child in et:
+ assert_equal(child.tag, "place")
+ self.result.append(dict(child.attrib))
+
+ address = {}
+ for sub in child:
+ if sub.tag == 'extratags':
+ self.result[-1]['extratags'] = {}
+ for tag in sub:
+ self.result[-1]['extratags'][tag.attrib['key']] = tag.attrib['value']
+ elif sub.tag == 'namedetails':
+ self.result[-1]['namedetails'] = {}
+ for tag in sub:
+ self.result[-1]['namedetails'][tag.attrib['desc']] = tag.text
+ elif sub.tag in ('geokml'):
+ self.result[-1][sub.tag] = True
+ else:
+ address[sub.tag] = sub.text
+
+ if len(address) > 0:
+ self.result[-1]['address'] = address
+
+
+class ReverseResponse(GenericResponse):
+
+ def __init__(self, page, fmt='json', errorcode=200):
+ self.page = page
+ self.format = fmt
+ self.errorcode = errorcode
+ self.result = []
+ self.header = dict()
+
+ if errorcode == 200:
+ getattr(self, 'parse_' + fmt)()
+
+ def parse_html(self):
+ content, errors = tidy_document(self.page,
+ options={'char-encoding' : 'utf8'})
+ #eq_(len(errors), 0 , "Errors found in HTML document:\n%s" % errors)
+
+ b = content.find('nominatim_results =')
+ e = content.find('</script>')
+ content = content[b:e]
+ b = content.find('[')
+ e = content.rfind(']')
+
+ self.result = json.JSONDecoder(object_pairs_hook=OrderedDict).decode(content[b:e+1])
+
+ def parse_json(self):
+ m = re.fullmatch(r'([\w$][^(]*)\((.*)\)', self.page)
+ if m is None:
+ code = self.page
+ else:
+ code = m.group(2)
+ self.header['json_func'] = m.group(1)
+ self.result = [json.JSONDecoder(object_pairs_hook=OrderedDict).decode(code)]
+
+ def parse_xml(self):
+ et = ET.fromstring(self.page)
+
+ self.header = dict(et.attrib)
+ self.result = []
+
+ for child in et:
+ if child.tag == 'result':
+ eq_(0, len(self.result), "More than one result in reverse result")
+ self.result.append(dict(child.attrib))
+ elif child.tag == 'addressparts':
+ address = {}
+ for sub in child:
+ address[sub.tag] = sub.text
+ self.result[0]['address'] = address
+ elif child.tag == 'extratags':
+ self.result[0]['extratags'] = {}
+ for tag in child:
+ self.result[0]['extratags'][tag.attrib['key']] = tag.attrib['value']
+ elif child.tag == 'namedetails':
+ self.result[0]['namedetails'] = {}
+ for tag in child:
+ self.result[0]['namedetails'][tag.attrib['desc']] = tag.text
+ elif child.tag in ('geokml'):
+ self.result[0][child.tag] = True
+ else:
+ assert child.tag == 'error', \
+ "Unknown XML tag %s on page: %s" % (child.tag, self.page)
+
+
+class DetailsResponse(GenericResponse):
+
+ def __init__(self, page, fmt='json', errorcode=200):
+ self.page = page
+ self.format = fmt
+ self.errorcode = errorcode
+ self.result = []
+ self.header = dict()
+
+ if errorcode == 200:
+ getattr(self, 'parse_' + fmt)()
+
+ def parse_html(self):
+ content, errors = tidy_document(self.page,
+ options={'char-encoding' : 'utf8'})
+ self.result = {}
+
+@when(u'searching for "(?P<query>.*)"(?P<dups> with dups)?')
+def query_cmd(context, query, dups):
+ """ Query directly via PHP script.
+ """
+ cmd = [os.path.join(context.nominatim.build_dir, 'utils', 'query.php'),
+ '--search', query]
+ # add more parameters in table form
+ if context.table:
+ for h in context.table.headings:
+ value = context.table[0][h].strip()
+ if value:
+ cmd.extend(('--' + h, value))
+
+ if dups:
+ cmd.extend(('--dedupe', '0'))
+
+ proc = subprocess.Popen(cmd, cwd=context.nominatim.build_dir,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (outp, err) = proc.communicate()
+
+ assert_equals (0, proc.returncode, "query.php failed with message: %s\noutput: %s" % (err, outp))
+
+ context.response = SearchResponse(outp.decode('utf-8'), 'json')
+
+def send_api_query(endpoint, params, fmt, context):
+ if fmt is not None:
+ params['format'] = fmt.strip()
+ if context.table:
+ if context.table.headings[0] == 'param':
+ for line in context.table:
+ params[line['param']] = line['value']
+ else:
+ for h in context.table.headings:
+ params[h] = context.table[0][h]
+
+ env = dict(BASE_SERVER_ENV)
+ env['QUERY_STRING'] = urlencode(params)
+
+ env['SCRIPT_NAME'] = '/%s.php' % endpoint
+ env['REQUEST_URI'] = '%s?%s' % (env['SCRIPT_NAME'], env['QUERY_STRING'])
+ env['CONTEXT_DOCUMENT_ROOT'] = os.path.join(context.nominatim.build_dir, 'website')
+ env['SCRIPT_FILENAME'] = os.path.join(env['CONTEXT_DOCUMENT_ROOT'],
+ '%s.php' % endpoint)
+ env['NOMINATIM_SETTINGS'] = context.nominatim.local_settings_file
+
+ if hasattr(context, 'http_headers'):
+ env.update(context.http_headers)
+
+ cmd = ['/usr/bin/php-cgi', env['SCRIPT_FILENAME']]
+ for k,v in params.items():
+ cmd.append("%s=%s" % (k, v))
+
+ proc = subprocess.Popen(cmd, cwd=context.nominatim.build_dir, env=env,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ (outp, err) = proc.communicate()
+
+ assert_equals(0, proc.returncode,
+ "query.php failed with message: %s\noutput: %s" % (err, outp))
+
+ assert_equals(0, len(err), "Unexpected PHP error: %s" % (err))
+
+ outp = outp.decode('utf-8')
+
+ if outp.startswith('Status: '):
+ status = int(outp[8:11])
+ else:
+ status = 200
+
+ content_start = outp.find('\r\n\r\n')
+
+ return outp[content_start + 4:], status
+
+@given(u'the HTTP header')
+def add_http_header(context):
+ if not hasattr(context, 'http_headers'):
+ context.http_headers = {}
+
+ for h in context.table.headings:
+ envvar = 'HTTP_' + h.upper().replace('-', '_')
+ context.http_headers[envvar] = context.table[0][h]
+
+
+@when(u'sending (?P<fmt>\S+ )?search query "(?P<query>.*)"(?P<addr> with address)?')
+def website_search_request(context, fmt, query, addr):
+ params = {}
+ if query:
+ params['q'] = query
+ if addr is not None:
+ params['addressdetails'] = '1'
+
+ outp, status = send_api_query('search', params, fmt, context)
+
+ if fmt is None:
+ outfmt = 'html'
+ elif fmt == 'jsonv2 ':
+ outfmt = 'json'
+ else:
+ outfmt = fmt.strip()
+
+ context.response = SearchResponse(outp, outfmt, status)
+
+@when(u'sending (?P<fmt>\S+ )?reverse coordinates (?P<lat>[0-9.-]+)?,(?P<lon>[0-9.-]+)?')
+def website_reverse_request(context, fmt, lat, lon):
+ params = {}
+ if lat is not None:
+ params['lat'] = lat
+ if lon is not None:
+ params['lon'] = lon
+
+ outp, status = send_api_query('reverse', params, fmt, context)
+
+ if fmt is None:
+ outfmt = 'xml'
+ elif fmt == 'jsonv2 ':
+ outfmt = 'json'
+ else:
+ outfmt = fmt.strip()
+
+ context.response = ReverseResponse(outp, outfmt, status)
+
+@when(u'sending (?P<fmt>\S+ )?details query for (?P<query>.*)')
+def website_details_request(context, fmt, query):
+ params = {}
+ if query[0] in 'NWR':
+ params['osmtype'] = query[0]
+ params['osmid'] = query[1:]
+ else:
+ params['place_id'] = query
+ outp, status = send_api_query('details', params, fmt, context)
+
+ context.response = DetailsResponse(outp, 'html', status)
+
+@when(u'sending (?P<fmt>\S+ )?lookup query for (?P<query>.*)')
+def website_lookup_request(context, fmt, query):
+ params = { 'osm_ids' : query }
+ outp, status = send_api_query('lookup', params, fmt, context)
+
+ if fmt == 'json ':
+ outfmt = 'json'
+ else:
+ outfmt = 'xml'
+
+ context.response = SearchResponse(outp, outfmt, status)
+
+
+@step(u'(?P<operator>less than|more than|exactly|at least|at most) (?P<number>\d+) results? (?:is|are) returned')
+def validate_result_number(context, operator, number):
+ eq_(context.response.errorcode, 200)
+ numres = len(context.response.result)
+ ok_(compare(operator, numres, int(number)),
+ "Bad number of results: expected %s %s, got %d." % (operator, number, numres))
+
+@then(u'a HTTP (?P<status>\d+) is returned')
+def check_http_return_status(context, status):
+ eq_(context.response.errorcode, int(status))
+
+@then(u'the result is valid (?P<fmt>\w+)')
+def step_impl(context, fmt):
+ context.execute_steps("Then a HTTP 200 is returned")
+ eq_(context.response.format, fmt)
+
+@then(u'result header contains')
+def check_header_attr(context):
+ for line in context.table:
+ assert_is_not_none(re.fullmatch(line['value'], context.response.header[line['attr']]),
+ "attribute '%s': expected: '%s', got '%s'"
+ % (line['attr'], line['value'],
+ context.response.header[line['attr']]))
+
+@then(u'result header has (?P<neg>not )?attributes (?P<attrs>.*)')
+def check_header_no_attr(context, neg, attrs):
+ for attr in attrs.split(','):
+ if neg:
+ assert_not_in(attr, context.response.header)
+ else:
+ assert_in(attr, context.response.header)
+
+@then(u'results contain')
+def step_impl(context):
+ context.execute_steps("then at least 1 result is returned")
+
+ for line in context.table:
+ context.response.match_row(line)
+
+@then(u'result (?P<lid>\d+ )?has (?P<neg>not )?attributes (?P<attrs>.*)')
+def validate_attributes(context, lid, neg, attrs):
+ if lid is None:
+ idx = range(len(context.response.result))
+ context.execute_steps("then at least 1 result is returned")
+ else:
+ idx = [int(lid.strip())]
+ context.execute_steps("then more than %sresults are returned" % lid)
+
+ for i in idx:
+ for attr in attrs.split(','):
+ if neg:
+ assert_not_in(attr, context.response.result[i])
+ else:
+ assert_in(attr, context.response.result[i])
+
+@then(u'result addresses contain')
+def step_impl(context):
+ context.execute_steps("then at least 1 result is returned")
+
+ if 'ID' not in context.table.headings:
+ addr_parts = context.response.property_list('address')
+
+ for line in context.table:
+ if 'ID' in context.table.headings:
+ addr_parts = [dict(context.response.result[int(line['ID'])]['address'])]
+
+ for h in context.table.headings:
+ if h != 'ID':
+ for p in addr_parts:
+ assert_in(h, p)
+ assert_equal(p[h], line[h], "Bad address value for %s" % h)
+
+@then(u'address of result (?P<lid>\d+) has(?P<neg> no)? types (?P<attrs>.*)')
+def check_address(context, lid, neg, attrs):
+ context.execute_steps("then more than %s results are returned" % lid)
+
+ addr_parts = context.response.result[int(lid)]['address']
+
+ for attr in attrs.split(','):
+ if neg:
+ assert_not_in(attr, addr_parts)
+ else:
+ assert_in(attr, addr_parts)
+
+@then(u'address of result (?P<lid>\d+) is')
+def check_address(context, lid):
+ context.execute_steps("then more than %s results are returned" % lid)
+
+ addr_parts = dict(context.response.result[int(lid)]['address'])
+
+ for line in context.table:
+ assert_in(line['type'], addr_parts)
+ assert_equal(addr_parts[line['type']], line['value'],
+ "Bad address value for %s" % line['type'])
+ del addr_parts[line['type']]
+
+ eq_(0, len(addr_parts), "Additional address parts found: %s" % str(addr_parts))
+
+@then(u'result (?P<lid>\d+ )?has bounding box in (?P<coords>[\d,.-]+)')
+def step_impl(context, lid, coords):
+ if lid is None:
+ context.execute_steps("then at least 1 result is returned")
+ bboxes = context.response.property_list('boundingbox')
+ else:
+ context.execute_steps("then more than %sresults are returned" % lid)
+ bboxes = [ context.response.result[int(lid)]['boundingbox']]
+
+ coord = [ float(x) for x in coords.split(',') ]
+
+ for bbox in bboxes:
+ if isinstance(bbox, str):
+ bbox = bbox.split(',')
+ bbox = [ float(x) for x in bbox ]
+
+ assert_greater_equal(bbox[0], coord[0])
+ assert_less_equal(bbox[1], coord[1])
+ assert_greater_equal(bbox[2], coord[2])
+ assert_less_equal(bbox[3], coord[3])
+
+@then(u'there are(?P<neg> no)? duplicates')
+def check_for_duplicates(context, neg):
+ context.execute_steps("then at least 1 result is returned")
+
+ resarr = set()
+ has_dupe = False
+
+ for res in context.response.result:
+ dup = (res['osm_type'], res['class'], res['type'], res['display_name'])
+ if dup in resarr:
+ has_dupe = True
+ break
+ resarr.add(dup)
+
+ if neg:
+ assert not has_dupe, "Found duplicate for %s" % (dup, )
+ else:
+ assert has_dupe, "No duplicates found"
namespace Nominatim;
-require '../lib/lib.php';
+require '../../lib/lib.php';
class NominatimTest extends \PHPUnit_Framework_TestCase
{
--- /dev/null
+hamburg
+1
+ 9.5842804224817 53.5792118965693
+ 10.2155260812517 53.8246176085747
+ 10.475796519837 53.4477065812749
+ 9.86815657040402 53.3278566584492
+ 9.5842804224817 53.5792118965693
+END
+END
+liechtenstein
+1
+ 9.20853378041844 47.0559465458986
+ 9.29384606832709 47.3507444175206
+ 9.49848868129809 47.4492015884201
+ 9.89867967626406 47.130228397937
+ 9.58252408463202 46.8691824262863
+ 9.20853378041844 47.0559465458986
+END
+END
+mauretania
+1
+ -17.1644809253606 20.8842205115601
+ -16.9724694177095 21.4269590060279
+ -13.1021317602129 21.4296172232924
+ -13.1921945122782 22.933479308252
+ -12.6268357994672 23.3657053906301
+ -12.097953263953 23.5240373343518
+ -12.0829087151283 26.0504750040867
+ -8.76667736314239 26.0902806494621
+ -8.70696146546581 27.398150020094
+ -4.74082309576697 25.0335832288387
+ -4.81791817472112 24.9110447612753
+ -6.46188681458007 24.9097418021523
+ -5.52019664485951 16.5528424294381
+ -5.23944558032214 16.3345963721271
+ -5.46430959536222 15.4167603246987
+ -10.5098309475637 15.3543804758815
+ -10.8990110578091 15.0165664449548
+ -11.4524241105327 15.5284484048548
+ -11.6506044402694 15.4177129737211
+ -11.7217380574693 14.8443740855381
+ -12.0680385872662 14.631162290351
+ -12.9159650211166 15.1353675285383
+ -13.4682080737434 15.990978880421
+ -13.9034306609561 16.0335336430301
+ -14.4204259536969 16.5367120778098
+ -16.2084993845136 16.4236729998869
+ -16.4302891135388 15.983629954068
+ -16.7862990158802 16.0015581695182
+ -16.3398604501701 18.2277294784962
+ -16.8886326298423 19.403294102207
+ -16.585950103956 20.1812628878206
+ -17.0813413461875 20.5018960498623
+ -17.1644809253606 20.8842205115601
+END
+END
+southdakota
+1
+ -104.297439187793 45.5046231666747
+ -104.299553299189 45.9775616107873
+ -104.096382287788 46.1214554410027
+ -96.4569073278818 46.0989703969216
+ -96.3044767039811 45.9115675974812
+ -96.5247176872565 45.5862877816162
+ -96.1968327207445 45.3001425265336
+ -96.1895592635092 43.1140553398185
+ -96.3329810429579 42.753709571028
+ -96.2088431240585 42.4315154785281
+ -96.5131423452368 42.3018249286814
+ -97.3698668669773 42.6757095208851
+ -98.0672608450503 42.5864899816312
+ -98.6364176029847 42.8190548434256
+ -104.146030684593 42.8349704964079
+ -104.298744415682 43.0007971343175
+ -104.297439187793 45.5046231666747
+END
+END
+uruguay
+1
+ -58.7094503498495 -33.5893528935793
+ -58.5930297220504 -33.0935229194446
+ -58.329380679061 -32.9223715673938
+ -58.4160616358367 -31.8529190894557
+ -58.2162152055053 -31.6271374679322
+ -58.2872417410783 -31.4234579824293
+ -58.0141967102111 -30.7805399817192
+ -58.0801823804181 -30.438369563871
+ -57.7465673402929 -30.0366166581386
+ -57.3919054971047 -30.0920714480735
+ -57.0841245854315 -29.904689506793
+ -56.5203007925187 -30.0577138349604
+ -55.8148965965951 -30.7486942236281
+ -55.4992686810269 -30.6637735134172
+ -55.0823825399047 -31.0951827436534
+ -54.4609533378373 -31.3096231186724
+ -52.8647106347639 -32.7122837473293
+ -53.3056885052038 -33.2040687582016
+ -53.3095867684494 -33.547639206286
+ -52.9652990494926 -33.8719452856167
+ -53.3564833683333 -34.6077542513996
+ -55.6741509399751 -35.9609110600942
+ -58.0955146798429 -34.8078487405856
+ -58.1517292851949 -34.5120322638008
+ -58.490557396808 -34.2574246976253
+ -58.7094503498495 -33.5893528935793
+END
+END
+
+
+vietnam
+1
+ 111.737359200422 8.65966389848196
+ 112.121385535871 9.03974154821598
+ 112.431435613709 8.95854407291052
+ 112.38091038991 8.72869141993135
+ 112.09887919106 8.68752900955559
+ 111.97764097397 8.47027379584868
+ 111.737359200422 8.65966389848196))
+ ((101.952539778161 22.3744843276505
+ 102.42115782215 22.9512287654112
+ 103.011765362757 22.7267436381062
+ 103.309708309047 22.9942421815192
+ 103.88404879754 22.8346923138744
+ 104.62599830253 23.0355978361405
+ 104.77147320329 23.3199292571035
+ 105.357960240763 23.571602116825
+ 105.914016228782 23.1523756402279
+ 106.944000642084 22.9589139939517
+ 106.898848084023 22.1610135134167
+ 107.607567716302 21.7959107906373
+ 107.974793702983 21.8006347108588
+ 108.387669146529 21.2765369215022
+ 108.350851164743 20.968001228975
+ 107.807714207219 20.5205405897832
+ 108.126799006308 20.25069143158
+ 108.024397106408 19.8655870027683
+ 107.557799016272 19.7778131848293
+ 107.347203511678 19.9746481898397
+ 107.334187929597 20.2503746970115
+ 107.017188364275 20.2484351047036
+ 106.88168214676 19.9452753668929
+ 106.293211714996 19.5544614034104
+ 106.229050701227 19.084494263666
+ 106.339912028886 18.6544696395528
+ 107.02787120458 18.0908270105482
+ 106.992048357062 17.6610336194013
+ 107.61356106657 17.4455208732243
+ 109.497501740771 15.5635441996556
+ 109.851305717105 12.5942782065681
+ 109.388462041025 9.73419261849773
+ 106.766311197844 8.27477170182912
+ 104.780565299066 8.00956568214471
+ 103.236641212428 8.93273491992321
+ 102.869224286517 9.37238182036115
+ 102.909736286172 9.54505245725422
+ 103.717955146996 10.1271353150665
+ 103.686354089781 10.5210100495922
+ 104.015476398095 10.662190116791
+ 104.247688572369 10.5092223585766
+ 104.775438554966 10.7014491439673
+ 104.980441972174 11.1056591699101
+ 105.7024629092 11.2045497780933
+ 105.626753980109 11.6300119740348
+ 105.748149258633 11.8111016132902
+ 107.378741221451 12.5115515071093
+ 107.300732476862 12.958173736809
+ 107.435951728865 13.4799057185978
+ 107.150187488306 14.1107044150892
+ 107.411164618718 15.2667546971578
+ 107.034817435829 15.6854570535274
+ 107.112895173759 15.9819504632758
+ 106.538255811019 16.3296204491326
+ 106.173473133351 17.0787384431658
+ 105.048356842632 18.1992017641427
+ 104.927464569104 18.5590394564598
+ 103.69790542148 19.2392576346486
+ 103.937765477152 19.8144348780526
+ 104.649272403028 19.9254452865302
+ 104.202109979964 20.3691301778448
+ 104.304114148338 20.6519242141597
+ 103.979630359435 20.7152945884418
+ 103.65735416319 20.4809125958884
+ 103.000528825335 20.7510959123255
+ 102.627912181829 21.1973495084135
+ 102.683177661723 21.4733395191357
+ 101.952539778161 22.3744843276505
+END
+END
+++ /dev/null
-Basic unit tests of PHP code. Very low coverage. Doesn't cover interaction
-with the webserver/HTTP or database (yet).
-
-You need to have
-https://phpunit.de/manual/4.2/en/
-installed.
-
-To execute the test suite run
-$ cd tests-php
-$ phpunit ./
-
-It will read phpunit.xml which points to the library, test path, bootstrap
-strip and set other parameters.
-
+++ /dev/null
-This directory contains functional tests for the Nominatim API,
-for the import/update from osm files and for indexing.
-
-The tests use the lettuce framework (http://lettuce.it/) and
-nose (https://nose.readthedocs.org). API tests are meant to be run
-against a Nominatim installation with a complete planet-wide
-setup based on a fairly recent planet. If you only have an
-excerpt, some of the API tests may fail. Database tests can be
-run without having a database installed.
-
-Prerequisites
-=============
-
- * lettuce framework (http://lettuce.it/)
- * nose (https://nose.readthedocs.org)
- * pytidylib (http://countergram.com/open-source/pytidylib)
- * haversine (https://github.com/mapado/haversine)
- * shapely (https://github.com/Toblerity/Shapely)
-
-Usage
-=====
-
- * get prerequisites
-
- # on a fresh Ubuntu LTS 14.04 you'll also need these system-wide packages
- [sudo] apt-get install python-dev python-pip python-Levenshtein tidy
-
- [sudo] pip install lettuce nose pytidylib haversine psycopg2 shapely
-
- * run the tests
-
- NOMINATIM_SERVER=http://your.nominatim.instance/ lettuce features
-
-The tests can be configured with a set of environment variables:
-
- * `NOMINATIM_SERVER` - URL of the nominatim instance (API tests)
- * `NOMINATIM_DIR` - source directory of Nominatim (import tests)
- * `TEMPLATE_DB` - name of template database used as a skeleton for
- the test databases (db tests)
- * `TEST_DB` - name of test database (db tests)
- * `NOMINATIM_SETTINGS` - file to write temporary Nominatim settings to (db tests)
- * `NOMINATIM_REUSE_TEMPLATE` - if defined, the template database will not be
- deleted after the test runs and reused during
- the next run. This speeds up tests considerably
- but might lead to outdated errors for some
- changes in the database layout.
- * `NOMINATIM_KEEP_SCENARIO_DB` - if defined, the test database will not be
- dropped after a test is finished. Should
- only be used if one single scenario is run,
- otherwise the result is undefined.
- * `LOGLEVEL` - set to 'debug' to get more verbose output (only works properly
- when output to a logfile is configured)
- * `LOGFILE` - sends debug output to the given file
-
-Writing Tests
-=============
-
-The following explanation assume that the reader is familiar with the lettuce
-notations of features, scenarios and steps.
-
-All possible steps can be found in the `steps` directory and should ideally
-be documented.
-
-
-API Tests (`features/api`)
---------------------------
-
-These tests are meant to test the different API calls and their parameters.
-
-There are two kind of steps defined for these tests:
-request setup steps (see `steps/api_setup.py`)
-and steps for checking results (see `steps/api_result.py`).
-
-Each scenario follows this simple sequence of steps:
-
- 1. One or more steps to define parameters and HTTP headers of the request.
- These are cumulative, so you can use multiple steps.
- 2. A single step to call the API. This sends a HTTP request to the configured
- server and collects the answer. The cached parameters will be deleted,
- to ensure that the setup works properly with scenario outlines.
- 3. As many result checks as necessary. The result remains cached, so that
- multiple tests can be added here.
-
-Indexing Tests (`features/db`)
----------------------------------------------------
-
-These tests check the import and update of the Nominatim database. They do not
-test the correctness of osm2pgsql. Each test will write some data into the `place`
-table (and optionally `the planet_osm_*` tables if required) and then run
-Nominatim's processing functions on that.
-
-These tests need to create their own test databases. By default they will be
-called `test_template_nominatim` and `test_nominatim`. Names can be changed with
-the environment variables `TEMPLATE_DB` and `TEST_DB`. The user running the tests
-needs superuser rights for postgres.
-
-
-Import Tests (`features/osm2pgsql`)
------------------------------------
-
-These tests check that data is imported correctly into the place table. They
-use the same template database as the Indexing tests, so the same remarks apply.
+++ /dev/null
-Feature: Object details
- Check details page for correctness
-
- Scenario Outline: Details via OSM id
- When looking up details for <object>
- Then the result is valid
-
- Examples:
- | object
- | 1758375
- | N158845944
- | W72493656
- | R62422
+++ /dev/null
-Feature: Localization of search results
-
- Scenario: Search - default language
- When sending json search query "Germany"
- Then results contain
- | ID | display_name
- | 0 | Deutschland.*
-
- Scenario: Search - accept-language first
- Given the request parameters
- | accept-language
- | en,de
- When sending json search query "Deutschland"
- Then results contain
- | ID | display_name
- | 0 | Germany.*
-
- Scenario: Search - accept-language missing
- Given the request parameters
- | accept-language
- | xx,fr,en,de
- When sending json search query "Deutschland"
- Then results contain
- | ID | display_name
- | 0 | Allemagne.*
-
- Scenario: Search - http accept language header first
- Given the HTTP header
- | accept-language
- | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3
- When sending json search query "Deutschland"
- Then results contain
- | ID | display_name
- | 0 | Allemagne.*
-
- Scenario: Search - http accept language header and accept-language
- Given the request parameters
- | accept-language
- | de,en
- Given the HTTP header
- | accept-language
- | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3
- When sending json search query "Deutschland"
- Then results contain
- | ID | display_name
- | 0 | Deutschland.*
-
- Scenario: Search - http accept language header fallback
- Given the HTTP header
- | accept-language
- | fr-ca,en-ca;q=0.5
- When sending json search query "Deutschland"
- Then results contain
- | ID | display_name
- | 0 | Allemagne.*
-
- Scenario: Search - http accept language header fallback (upper case)
- Given the HTTP header
- | accept-language
- | fr-FR;q=0.8,en-ca;q=0.5
- When sending json search query "Deutschland"
- Then results contain
- | ID | display_name
- | 0 | Allemagne.*
-
- Scenario: Reverse - default language
- When looking up coordinates 48.13921,11.57328
- Then result addresses contain
- | ID | city
- | 0 | München
-
- Scenario: Reverse - accept-language parameter
- Given the request parameters
- | accept-language
- | en,fr
- When looking up coordinates 48.13921,11.57328
- Then result addresses contain
- | ID | city
- | 0 | Munich
-
- Scenario: Reverse - HTTP accept language header
- Given the HTTP header
- | accept-language
- | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3
- When looking up coordinates 48.13921,11.57328
- Then result addresses contain
- | ID | city
- | 0 | Munich
-
- Scenario: Reverse - accept-language parameter and HTTP header
- Given the request parameters
- | accept-language
- | it
- Given the HTTP header
- | accept-language
- | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3
- When looking up coordinates 48.13921,11.57328
- Then result addresses contain
- | ID | city
- | 0 | Monaco di Baviera
+++ /dev/null
-Feature: Places by osm_type and osm_id Tests
- Simple tests for internal server errors and response format.
-
- Scenario: address lookup for existing node, way, relation
- When looking up xml places N158845944,W72493656,,R62422,X99,N0
- Then the result is valid xml
- exactly 3 results are returned
- When looking up json places N158845944,W72493656,,R62422,X99,N0
- Then the result is valid json
- exactly 3 results are returned
-
- Scenario: address lookup for non-existing or invalid node, way, relation
- When looking up xml places X99,,N0,nN158845944,ABC,,W9
- Then the result is valid xml
- exactly 0 results are returned
\ No newline at end of file
+++ /dev/null
-Feature: API regression tests
- Tests error cases reported in tickets.
-
- Scenario: trac #2430
- When sending json search query "89 River Avenue, Hoddesdon, Hertfordshire, EN11 0JT"
- Then at least 1 result is returned
-
- Scenario: trac #2440
- When sending json search query "East Harvard Avenue, Denver"
- Then more than 2 results are returned
-
- Scenario: trac #2456
- When sending xml search query "Borlänge Kommun"
- Then results contain
- | ID | place_rank
- | 0 | 19
-
- Scenario: trac #2530
- When sending json search query "Lange Straße, Bamberg" with address
- Then result addresses contain
- | ID | town
- | 0 | Bamberg
-
- Scenario: trac #2541
- When sending json search query "pad, germany"
- Then results contain
- | ID | class | display_name
- | 0 | aeroway | Paderborn/Lippstadt,.*
-
- Scenario: trac #2579
- When sending json search query "Johnsons Close, hackbridge" with address
- Then result addresses contain
- | ID | postcode
- | 0 | SM5 2LU
-
- @Fail
- Scenario Outline: trac #2586
- When sending json search query "<query>" with address
- Then result addresses contain
- | ID | country_code
- | 0 | uk
-
- Examples:
- | query
- | DL7 0SN
- | DL70SN
-
- Scenario: trac #2628 (1)
- When sending json search query "Adam Kraft Str" with address
- Then result addresses contain
- | ID | road
- | 0 | Adam-Kraft-Straße
-
- Scenario: trac #2628 (2)
- When sending json search query "Maxfeldstr. 5, Nürnberg" with address
- Then result addresses contain
- | ID | house_number | road | city
- | 0 | 5 | Maxfeldstraße | Nürnberg
-
- Scenario: trac #2638
- When sending json search query "Nöthnitzer Str. 40, 01187 Dresden" with address
- Then result addresses contain
- | ID | house_number | road | city
- | 0 | 40 | Nöthnitzer Straße | Dresden
-
- Scenario Outline: trac #2667
- When sending json search query "<query>" with address
- Then result addresses contain
- | ID | house_number
- | 0 | <number>
-
- Examples:
- | number | query
- | 16 | 16 Woodpecker Way, Cambourne
- | 14906 | 14906, 114 Street Northwest, Edmonton, Alberta, Canada
- | 14904 | 14904, 114 Street Northwest, Edmonton, Alberta, Canada
- | 15022 | 15022, 114 Street Northwest, Edmonton, Alberta, Canada
- | 15024 | 15024, 114 Street Northwest, Edmonton, Alberta, Canada
-
- Scenario: trac #2681
- When sending json search query "kirchstraße troisdorf Germany"
- Then results contain
- | ID | display_name
- | 0 | .*, Troisdorf, .*
-
- Scenario: trac #2758
- When sending json search query "6а, полуботка, чернигов" with address
- Then result addresses contain
- | ID | house_number
- | 0 | 6а
-
- Scenario: trac #2790
- When looking up coordinates 49.0942079697809,8.27565898861822
- Then result addresses contain
- | ID | road | village | country
- | 0 | Daimlerstraße | Jockgrim | Deutschland
-
- Scenario: trac #2794
- When sending json search query "4008"
- Then results contain
- | ID | class | type
- | 0 | place | postcode
-
- Scenario: trac #2797
- When sending json search query "Philippstr.4, 52349 Düren" with address
- Then result addresses contain
- | ID | road | town
- | 0 | Philippstraße | Düren
-
- Scenario: trac #2830
- When sending json search query "207, Boardman Street, S0J 1L0, CA" with address
- Then result addresses contain
- | ID | house_number | road | postcode | country
- | 0 | 207 | Boardman Street | S0J 1L0 | Canada
-
- Scenario: trac #2830
- When sending json search query "S0J 1L0,CA"
- Then results contain
- | ID | class | type | display_name
- | 0 | place | postcode | .*, Canada
-
- Scenario: trac #2845
- When sending json search query "Leliestraat 31, Zwolle" with address
- Then result addresses contain
- | ID | city
- | 0 | Zwolle
-
- Scenario: trac #2852
- When sending json search query "berlinerstrasse, leipzig" with address
- Then result addresses contain
- | ID | road
- | 0 | Berliner Straße
-
- Scenario: trac #2871
- When looking up coordinates -33.906895553,150.99609375
- Then result addresses contain
- | ID | city | country
- | 0 | [^0-9]*$ | Australia
-
- Scenario: trac #2981
- When sending json search query "Ohmstraße 7, Berlin" with address
- Then at least 2 results are returned
- And result addresses contain
- | house_number | road | state
- | 7 | Ohmstraße | Berlin
-
- Scenario: trac #3049
- When sending json search query "Soccer City"
- Then results contain
- | ID | class | type | latlon
- | 0 | leisure | stadium | -26.2347261,27.982645 +-50m
-
- Scenario: trac #3130
- When sending json search query "Old Way, Frinton"
- Then results contain
- | ID | class | latlon
- | 0 | highway | 51.8324206,1.2447352 +-100m
-
- Scenario Outline: trac #5025
- When sending json search query "Kriegsstr <house_nr>, Karlsruhe" with address
- Then result addresses contain
- | house_number | road
- | <house_nr> | Kriegsstraße
-
- Examples:
- | house_nr
- | 5c
- | 25
- | 78
- | 80
- | 99
- | 130
- | 153
- | 196
- | 256
- | 294
-
- Scenario: trac #5238
- Given the request parameters
- | bounded | viewbox
- | 1 | -1,0,0,-1
- When sending json search query "sy"
- Then exactly 0 results are returned
-
- Scenario: trac #5274
- When sending json search query "Goedestraat 41-BS, Utrecht" with address
- Then result addresses contain
- | house_number | road | city
- | 41-BS | Goedestraat | Utrecht
-
- @poldi-only
- Scenario Outline: github #36
- When sending json search query "<query>" with address
- Then result addresses contain
- | ID | road | city
- | 0 | Seegasse | .*Wieselburg-Land
-
- Examples:
- | query
- | Seegasse, Gemeinde Wieselburg-Land
- | Seegasse, Wieselburg-Land
- | Seegasse, Wieselburg
-
- Scenario: github #190
- When looking up place N257363453
- Then the results contain
- | osm_type | osm_id | latlon
- | node | 257363453 | 35.8404121,128.5586643 +-100m
-
- Scenario: trac #5427
- Given the request parameters
- | countrycodes |
- | DE |
- When sending json search query "12345" with address
- Then result addresses contain
- | country_code |
- | de |
+++ /dev/null
-Feature: Reverse geocoding
- Testing the reverse function
-
- # Make sure country is not overwritten by the postcode
- Scenario: Country is returned
- Given the request parameters
- | accept-language
- | de
- When looking up coordinates 53.9788769,13.0830313
- Then result addresses contain
- | ID | country
- | 0 | Deutschland
-
-
- Scenario: Boundingbox is returned
- Given the request parameters
- | format | zoom
- | xml | 4
- When looking up coordinates 53.9788769,13.0830313
- And results contain valid boundingboxes
-
- Scenario: Reverse geocoding for odd interpolated housenumber
-
- Scenario: Reverse geocoding for even interpolated housenumber
-
- @Tiger
- Scenario: TIGER house number
- Given the request parameters
- | addressdetails
- | 1
- When looking up jsonv2 coordinates 40.6863624710666,-112.060005720023
- And exactly 1 result is returned
- And result addresses contain
- | ID | house_number | road | postcode | country_code
- | 0 | 709. | Kings Estate Drive | 84128 | us
- And results contain
- | osm_type | category | type
- | way | place | house
-
- @Tiger
- Scenario: No TIGER house number for zoom < 18
- Given the request parameters
- | addressdetails | zoom
- | 1 | 17
- When looking up coordinates 40.6863624710666,-112.060005720023
- And exactly 1 result is returned
- And result addresses contain
- | ID | road | postcode | country_code
- | 0 | Kings Estate Drive | 84128 | us
- And result 0 has attributes osm_id,osm_type
-
- Scenario Outline: Reverse Geocoding with extratags
- Given the request parameters
- | extratags
- | 1
- When looking up <format> coordinates 48.86093,2.2978
- Then result 0 has attributes extratags
-
- Examples:
- | format
- | xml
- | json
- | jsonv2
-
- Scenario Outline: Reverse Geocoding with namedetails
- Given the request parameters
- | namedetails
- | 1
- When looking up <format> coordinates 48.86093,2.2978
- Then result 0 has attributes namedetails
-
- Examples:
- | format
- | xml
- | json
- | jsonv2
-
-
- Scenario Outline: Reverse Geocoding contains TEXT geometry
- Given the request parameters
- | polygon_text
- | 1
- When looking up <format> coordinates 48.86093,2.2978
- Then result 0 has attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | geotext
- | json | geotext
- | jsonv2 | geotext
-
- Scenario Outline: Reverse Geocoding contains polygon-as-points geometry
- Given the request parameters
- | polygon
- | 1
- When looking up <format> coordinates 48.86093,2.2978
- Then result 0 has not attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | polygonpoints
- | json | polygonpoints
- | jsonv2 | polygonpoints
-
-
-
- Scenario Outline: Reverse Geocoding contains SVG geometry
- Given the request parameters
- | polygon_svg
- | 1
- When looking up <format> coordinates 48.86093,2.2978
- Then result 0 has attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | geosvg
- | json | svg
- | jsonv2 | svg
-
-
- Scenario Outline: Reverse Geocoding contains KML geometry
- Given the request parameters
- | polygon_kml
- | 1
- When looking up <format> coordinates 48.86093,2.2978
- Then result 0 has attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | geokml
- | json | geokml
- | jsonv2 | geokml
-
-
- Scenario Outline: Reverse Geocoding contains GEOJSON geometry
- Given the request parameters
- | polygon_geojson
- | 1
- When looking up <format> coordinates 48.86093,2.2978
- Then result 0 has attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | geojson
- | json | geojson
- | jsonv2 | geojson
-
-
+++ /dev/null
-Feature: Reverse lookup by ID
- Testing reverse geocoding via OSM ID
-
- # see github issue #269
- Scenario: Get address of linked places
- Given the request parameters
- | osm_type | osm_id
- | N | 151421301
- When sending an API call reverse
- Then exactly 1 result is returned
- And result addresses contain
- | county | state
- | Pratt County | Kansas
+++ /dev/null
-Feature: Simple Reverse Tests
- Simple tests for internal server errors and response format.
- These tests should pass on any Nominatim installation.
-
- Scenario Outline: Simple reverse-geocoding
- When looking up xml coordinates <lat>,<lon>
- Then the result is valid xml
- When looking up json coordinates <lat>,<lon>
- Then the result is valid json
- When looking up jsonv2 coordinates <lat>,<lon>
- Then the result is valid json
-
- Examples:
- | lat | lon
- | 0.0 | 0.0
- | 45.3 | 3.5
- | -79.34 | 23.5
- | 0.23 | -178.555
-
- Scenario Outline: Testing different parameters
- Given the request parameters
- | <parameter>
- | <value>
- When sending search query "Manchester"
- Then the result is valid html
- Given the request parameters
- | <parameter>
- | <value>
- When sending html search query "Manchester"
- Then the result is valid html
- Given the request parameters
- | <parameter>
- | <value>
- When sending xml search query "Manchester"
- Then the result is valid xml
- Given the request parameters
- | <parameter>
- | <value>
- When sending json search query "Manchester"
- Then the result is valid json
- Given the request parameters
- | <parameter>
- | <value>
- When sending jsonv2 search query "Manchester"
- Then the result is valid json
-
- Examples:
- | parameter | value
- | polygon | 1
- | polygon | 0
- | polygon_text | 1
- | polygon_text | 0
- | polygon_kml | 1
- | polygon_kml | 0
- | polygon_geojson | 1
- | polygon_geojson | 0
- | polygon_svg | 1
- | polygon_svg | 0
-
-
-
-
- Scenario Outline: Wrapping of legal jsonp requests
- Given the request parameters
- | json_callback
- | foo
- When looking up <format> coordinates 67.3245,0.456
- Then the result is valid json
-
- Examples:
- | format
- | json
- | jsonv2
-
- Scenario: Reverse-geocoding without address
- Given the request parameters
- | addressdetails
- | 0
- When looking up xml coordinates 36.791966,127.171726
- Then the result is valid xml
- When looking up json coordinates 36.791966,127.171726
- Then the result is valid json
- When looking up jsonv2 coordinates 36.791966,127.171726
- Then the result is valid json
-
- Scenario: Reverse-geocoding with zoom
- Given the request parameters
- | zoom
- | 10
- When looking up xml coordinates 36.791966,127.171726
- Then the result is valid xml
- When looking up json coordinates 36.791966,127.171726
- Then the result is valid json
- When looking up jsonv2 coordinates 36.791966,127.171726
- Then the result is valid json
-
- Scenario: Missing lon parameter
- Given the request parameters
- | lat
- | 51.51
- When sending an API call reverse
- Then a HTTP 400 is returned
-
- Scenario: Missing lat parameter
- Given the request parameters
- | lon
- | -79.39114
- When sending an API call reverse
- Then a HTTP 400 is returned
-
- Scenario: Missing osm_id parameter
- Given the request parameters
- | osm_type
- | N
- When sending an API call reverse
- Then a HTTP 400 is returned
-
- Scenario: Missing osm_type parameter
- Given the request parameters
- | osm_id
- | 3498564
- When sending an API call reverse
- Then a HTTP 400 is returned
-
- Scenario Outline: Bad format for lat or lon
- Given the request parameters
- | lat | lon |
- | <lat> | <lon> |
- When sending an API call reverse
- Then a HTTP 400 is returned
-
- Examples:
- | lat | lon
- | 48.9660 | 8,4482
- | 48,9660 | 8.4482
- | 48,9660 | 8,4482
- | 48.966.0 | 8.4482
- | 48.966 | 8.448.2
- | Nan | 8.448
- | 48.966 | Nan
+++ /dev/null
-Feature: Search queries
- Testing correctness of results
-
- Scenario: UK House number search
- When sending json search query "27 Thoresby Road, Broxtowe" with address
- Then address of result 0 contains
- | type | value
- | house_number | 27
- | road | Thoresby Road
- | city | Broxtowe
- | state | England
- | country | U.*K.*
- | country_code | gb
-
-
- Scenario: House number search for non-street address
- Given the request parameters
- | accept-language
- | en
- When sending json search query "4 Pomocnia, Pokrzywnica, Poland" with address
- Then address of result 0 contains
- | type | value
- | house_number | 4
- | county | gmina Pokrzywnica
- | state | Masovian Voivodeship
- | postcode | 06-121
- | country | Poland
- | country_code | pl
- Then address of result 0 does not contain road
-
- Scenario: House number interpolation even
- Given the request parameters
- | accept-language
- | en
- When sending json search query "140 rue Don Bosco, Saguenay" with address
- Then address of result 0 contains
- | type | value
- | house_number | 140
- | road | [Rr]ue Don Bosco
- | city | .*Saguenay
- | state | Quebec
- | country | Canada
- | country_code | ca
-
- Scenario: House number interpolation odd
- Given the request parameters
- | accept-language
- | en
- When sending json search query "141 rue Don Bosco, Saguenay" with address
- Then address of result 0 contains
- | type | value
- | house_number | 141
- | road | [rR]ue Don Bosco
- | city | .*Saguenay
- | state | Quebec
- | country | Canada
- | country_code | ca
-
- @Tiger
- Scenario: TIGER house number
- When sending json search query "3 West Victory Way, Craig"
- Then results contain
- | osm_type
- | way
-
- @Tiger
- Scenario: TIGER house number (road fallback)
- When sending json search query "3030 West Victory Way, Craig"
- Then results contain
- | osm_type
- | way
-
- Scenario: Expansion of Illinois
- Given the request parameters
- | accept-language
- | en
- When sending json search query "il, us"
- Then results contain
- | ID | display_name
- | 0 | Illinois.*
-
- Scenario: Search with class-type feature
- When sending jsonv2 search query "Hotel California"
- Then results contain
- | place_rank
- | 30
+++ /dev/null
-Feature: Result order for Geocoding
- Testing that importance ordering returns sensible results
-
- Scenario Outline: city order in street search
- Given the request parameters
- | limit
- | 100
- When sending json search query "<street>, <city>" with address
- Then address of result 0 contains
- | type | value
- | <type> | <city>
-
- Examples:
- | type | city | street
- | city | Zürich | Rigistr
- | city | Karlsruhe | Sophienstr
- | city | München | Karlstr
- | city | Praha | Dlouhá
-
- Scenario Outline: use more important city in street search
- When sending json search query "<street>, <city>" with address
- Then result addresses contain
- | ID | country_code
- | 0 | <country>
-
- Examples:
- | country | city | street
- | gb | London | Main St
- | gb | Manchester | Central Street
-
- # https://trac.openstreetmap.org/ticket/5094
- Scenario: housenumbers are ordered by complete match first
- When sending json search query "4 Докукина Москва" with address
- Then result addresses contain
- | ID | house_number
- | 0 | 4
+++ /dev/null
-Feature: Search queries
- Testing different queries and parameters
-
- Scenario: Simple XML search
- When sending xml search query "Schaan"
- Then result 0 has attributes place_id,osm_type,osm_id
- And result 0 has attributes place_rank,boundingbox
- And result 0 has attributes lat,lon,display_name
- And result 0 has attributes class,type,importance,icon
- And result 0 has not attributes address
- And results contain valid boundingboxes
-
- Scenario: Simple JSON search
- When sending json search query "Vaduz"
- And result 0 has attributes place_id,licence,icon,class,type
- And result 0 has attributes osm_type,osm_id,boundingbox
- And result 0 has attributes lat,lon,display_name,importance
- And result 0 has not attributes address
- And results contain valid boundingboxes
-
- Scenario: JSON search with addressdetails
- When sending json search query "Montevideo" with address
- Then address of result 0 is
- | type | value
- | city | Montevideo
- | state | Montevideo
- | country | Uruguay
- | country_code | uy
-
- Scenario: XML search with addressdetails
- When sending xml search query "Inuvik" with address
- Then address of result 0 contains
- | type | value
- | state | Northwest Territories
- | country | Canada
- | country_code | ca
-
- Scenario: coordinate search with addressdetails
- When sending json search query "51.193058013916,15.5245780944824" with address
- Then result addresses contain
- | village | country | country_code
- | Kraszowice | Polska | pl
-
- Scenario: Address details with unknown class types
- When sending json search query "foobar, Essen" with address
- Then results contain
- | ID | class | type
- | 0 | leisure | hackerspace
- And result addresses contain
- | ID | address29
- | 0 | Chaospott
- And address of result 0 does not contain leisure,hackerspace
-
- Scenario: Disabling deduplication
- When sending json search query "Oxford Street, London"
- Then there are no duplicates
- Given the request parameters
- | dedupe
- | 0
- When sending json search query "Oxford Street, London"
- Then there are duplicates
-
- Scenario: Search with bounded viewbox in right area
- Given the request parameters
- | bounded | viewbox
- | 1 | -87.7,41.9,-87.57,41.85
- When sending json search query "restaurant" with address
- Then result addresses contain
- | ID | city
- | 0 | Chicago
-
- Scenario: Search with bounded viewboxlbrt in right area
- Given the request parameters
- | bounded | viewboxlbrt
- | 1 | -87.7,41.85,-87.57,41.9
- When sending json search query "restaurant" with address
- Then result addresses contain
- | ID | city
- | 0 | Chicago
-
- Scenario: No POI search with unbounded viewbox
- Given the request parameters
- | viewbox
- | -87.7,41.9,-87.57,41.85
- When sending json search query "restaurant"
- Then results contain
- | display_name
- | [^,]*(?i)restaurant.*
-
- Scenario: bounded search remains within viewbox, even with no results
- Given the request parameters
- | bounded | viewbox
- | 1 | 43.5403125,-5.6563282,43.54285,-5.662003
- When sending json search query "restaurant"
- Then less than 1 result is returned
-
- Scenario: bounded search remains within viewbox with results
- Given the request parameters
- | bounded | viewbox
- | 1 | -5.662003,43.55,-5.6563282,43.5403125
- When sending json search query "restaurant"
- | lon | lat
- | >= -5.662003 | >= 43.5403125
- | <= -5.6563282| <= 43.55
-
- Scenario: Prefer results within viewbox
- Given the request parameters
- | accept-language
- | en
- When sending json search query "royan" with address
- Then result addresses contain
- | ID | country
- | 0 | France
- Given the request parameters
- | accept-language | viewbox
- | en | 51.94,36.59,51.99,36.56
- When sending json search query "royan" with address
- Then result addresses contain
- | ID | country
- | 0 | Iran
-
- Scenario: Overly large limit number for search results
- Given the request parameters
- | limit
- | 1000
- When sending json search query "Neustadt"
- Then at most 50 results are returned
-
- Scenario: Limit number of search results
- Given the request parameters
- | limit
- | 4
- When sending json search query "Neustadt"
- Then exactly 4 results are returned
-
- Scenario: Restrict to feature type country
- Given the request parameters
- | featureType
- | country
- When sending xml search query "Monaco"
- Then results contain
- | place_rank
- | 4
-
- Scenario: Restrict to feature type state
- When sending xml search query "Berlin"
- Then results contain
- | ID | place_rank
- | 0 | 1[56]
- Given the request parameters
- | featureType
- | state
- When sending xml search query "Berlin"
- Then results contain
- | place_rank
- | [78]
-
- Scenario: Restrict to feature type city
- Given the request parameters
- | featureType
- | city
- When sending xml search query "Monaco"
- Then results contain
- | place_rank
- | 1[56789]
-
-
- Scenario: Restrict to feature type settlement
- When sending json search query "Everest"
- Then results contain
- | ID | display_name
- | 0 | Mount Everest.*
- Given the request parameters
- | featureType
- | settlement
- When sending json search query "Everest"
- Then results contain
- | ID | display_name
- | 0 | Everest.*
-
- Scenario Outline: Search with polygon threshold (json)
- Given the request parameters
- | polygon_geojson | polygon_threshold
- | 1 | <th>
- When sending json search query "switzerland"
- Then at least 1 result is returned
- And result 0 has attributes geojson
-
- Examples:
- | th
- | -1
- | 0.0
- | 0.5
- | 999
-
- Scenario Outline: Search with polygon threshold (xml)
- Given the request parameters
- | polygon_geojson | polygon_threshold
- | 1 | <th>
- When sending xml search query "switzerland"
- Then at least 1 result is returned
- And result 0 has attributes geojson
-
- Examples:
- | th
- | -1
- | 0.0
- | 0.5
- | 999
-
- Scenario Outline: Search with invalid polygon threshold (xml)
- Given the request parameters
- | polygon_geojson | polygon_threshold
- | 1 | <th>
- When sending xml search query "switzerland"
- Then a HTTP 400 is returned
-
-
- Scenario Outline: Search with extratags
- Given the request parameters
- | extratags
- | 1
- When sending <format> search query "Hauptstr"
- Then result 0 has attributes extratags
- And result 1 has attributes extratags
-
- Examples:
- | format
- | xml
- | json
- | jsonv2
-
- Scenario Outline: Search with namedetails
- Given the request parameters
- | namedetails
- | 1
- When sending <format> search query "Hauptstr"
- Then result 0 has attributes namedetails
- And result 1 has attributes namedetails
-
- Examples:
- | format
- | xml
- | json
- | jsonv2
-
-
- Scenario Outline: Search result with contains TEXT geometry
- Given the request parameters
- | polygon_text
- | 1
- When sending <format> search query "switzerland"
- Then result 0 has attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | geotext
- | json | geotext
- | jsonv2 | geotext
-
- Scenario Outline: Search result contains polygon-as-points geometry
- Given the request parameters
- | polygon
- | 1
- When sending <format> search query "switzerland"
- Then result 0 has attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | polygonpoints
- | json | polygonpoints
- | jsonv2 | polygonpoints
-
-
-
- Scenario Outline: Search result contains SVG geometry
- Given the request parameters
- | polygon_svg
- | 1
- When sending <format> search query "switzerland"
- Then result 0 has attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | geosvg
- | json | svg
- | jsonv2 | svg
-
-
- Scenario Outline: Search result contains KML geometry
- Given the request parameters
- | polygon_kml
- | 1
- When sending <format> search query "switzerland"
- Then result 0 has attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | geokml
- | json | geokml
- | jsonv2 | geokml
-
-
- Scenario Outline: Search result contains GEOJSON geometry
- Given the request parameters
- | polygon_geojson
- | 1
- When sending <format> search query "switzerland"
- Then result 0 has attributes <response_attribute>
-
- Examples:
- | format | response_attribute
- | xml | geojson
- | json | geojson
- | jsonv2 | geojson
+++ /dev/null
-Feature: Simple Tests
- Simple tests for internal server errors and response format.
- These tests should pass on any Nominatim installation.
-
- Scenario Outline: Testing different parameters
- Given the request parameters
- | <parameter>
- | <value>
- When sending search query "Manchester"
- Then the result is valid html
- Given the request parameters
- | <parameter>
- | <value>
- When sending html search query "Manchester"
- Then the result is valid html
- Given the request parameters
- | <parameter>
- | <value>
- When sending xml search query "Manchester"
- Then the result is valid xml
- Given the request parameters
- | <parameter>
- | <value>
- When sending json search query "Manchester"
- Then the result is valid json
- Given the request parameters
- | <parameter>
- | <value>
- When sending jsonv2 search query "Manchester"
- Then the result is valid json
-
- Examples:
- | parameter | value
- | addressdetails | 1
- | addressdetails | 0
- | polygon | 1
- | polygon | 0
- | polygon_text | 1
- | polygon_text | 0
- | polygon_kml | 1
- | polygon_kml | 0
- | polygon_geojson | 1
- | polygon_geojson | 0
- | polygon_svg | 1
- | polygon_svg | 0
- | accept-language | de,en
- | countrycodes | uk,ir
- | bounded | 1
- | bounded | 0
- | exclude_place_ids| 385252,1234515
- | limit | 1000
- | dedupe | 1
- | dedupe | 0
- | extratags | 1
- | extratags | 0
- | namedetails | 1
- | namedetails | 0
-
- Scenario: Search with invalid output format
- Given the request parameters
- | format
- | fd$#
- When sending search query "Berlin"
- Then a HTTP 400 is returned
-
- Scenario Outline: Simple Searches
- When sending search query "<query>"
- Then the result is valid html
- When sending html search query "<query>"
- Then the result is valid html
- When sending xml search query "<query>"
- Then the result is valid xml
- When sending json search query "<query>"
- Then the result is valid json
- When sending jsonv2 search query "<query>"
- Then the result is valid json
-
- Examples:
- | query
- | New York, New York
- | France
- | 12, Main Street, Houston
- | München
- | 東京都
- | hotels in nantes
- | xywxkrf
- | gh; foo()
- | %#$@*&l;der#$!
- | 234
- | 47.4,8.3
-
- Scenario: Empty XML search
- When sending xml search query "xnznxvcx"
- Then result header contains
- | attr | value
- | querystring | xnznxvcx
- | polygon | false
- | more_url | .*format=xml.*q=xnznxvcx.*
-
- Scenario: Empty XML search with special XML characters
- When sending xml search query "xfdghn&zxn"xvbyx<vxx>cssdex"
- Then result header contains
- | attr | value
- | querystring | xfdghn&zxn"xvbyx<vxx>cssdex
- | polygon | false
- | more_url | .*format=xml.*q=xfdghn&zxn"xvbyx<vxx>cssdex.*
-
- Scenario: Empty XML search with viewbox
- Given the request parameters
- | viewbox
- | 12,45.13,77,33
- When sending xml search query "xnznxvcx"
- Then result header contains
- | attr | value
- | querystring | xnznxvcx
- | polygon | false
- | viewbox | 12,45.13,77,33
-
- Scenario: Empty XML search with viewboxlbrt
- Given the request parameters
- | viewboxlbrt
- | 12,34.13,77,45
- When sending xml search query "xnznxvcx"
- Then result header contains
- | attr | value
- | querystring | xnznxvcx
- | polygon | false
- | viewbox | 12,45.13,77,33
-
- Scenario: Empty XML search with viewboxlbrt and viewbox
- Given the request parameters
- | viewbox | viewboxblrt
- | 12,45.13,77,33 | 1,2,3,4
- When sending xml search query "pub"
- Then result header contains
- | attr | value
- | querystring | pub
- | polygon | false
- | viewbox | 12,45.13,77,33
-
-
- Scenario Outline: Empty XML search with polygon values
- Given the request parameters
- | polygon
- | <polyval>
- When sending xml search query "xnznxvcx"
- Then result header contains
- | attr | value
- | polygon | <result>
-
- Examples:
- | result | polyval
- | false | 0
- | true | 1
- | true | True
- | true | true
- | true | false
- | true | FALSE
- | true | yes
- | true | no
- | true | '; delete from foobar; select '
-
-
- Scenario: Empty XML search with exluded place ids
- Given the request parameters
- | exclude_place_ids
- | 123,76,342565
- When sending xml search query "jghrleoxsbwjer"
- Then result header contains
- | attr | value
- | exclude_place_ids | 123,76,342565
-
- Scenario: Empty XML search with bad exluded place ids
- Given the request parameters
- | exclude_place_ids
- | ,
- When sending xml search query "jghrleoxsbwjer"
- Then result header has no attribute exclude_place_ids
-
- Scenario Outline: Wrapping of legal jsonp search requests
- Given the request parameters
- | json_callback
- | <data>
- When sending json search query "Tokyo"
- Then there is a json wrapper "<data>"
-
- Examples:
- | data
- | foo
- | FOO
- | __world
- | $me
- | m1[4]
- | d_r[$d]
-
- Scenario Outline: Wrapping of illegal jsonp search requests
- Given the request parameters
- | json_callback
- | <data>
- When sending json search query "Tokyo"
- Then a HTTP 400 is returned
-
- Examples:
- | data
- | 1asd
- | bar(foo)
- | XXX['bad']
- | foo; evil
-
- Scenario Outline: Ignore jsonp parameter for anything but json
- Given the request parameters
- | json_callback
- | 234
- When sending json search query "Malibu"
- Then a HTTP 400 is returned
- Given the request parameters
- | json_callback
- | 234
- When sending xml search query "Malibu"
- Then the result is valid xml
- Given the request parameters
- | json_callback
- | 234
- When sending html search query "Malibu"
- Then the result is valid html
-
- Scenario: Empty JSON search
- When sending json search query "YHlERzzx"
- Then exactly 0 results are returned
-
- Scenario: Empty JSONv2 search
- When sending jsonv2 search query "Flubb XdfESSaZx"
- Then exactly 0 results are returned
-
- Scenario: Search for non-existing coordinates
- When sending json search query "-21.0,-33.0"
- Then exactly 0 results are returned
-
+++ /dev/null
-Feature: Structured search queries
- Testing correctness of results with
- structured queries
-
- Scenario: Country only
- When sending json structured query with address
- | country
- | Canada
- Then address of result 0 is
- | type | value
- | country | Canada
- | country_code | ca
-
- Scenario: Postcode only
- When sending json structured query with address
- | postalcode
- | 22547
- Then at least 1 result is returned
- And results contain
- | type
- | post(al_)?code
- And result addresses contain
- | postcode
- | 22547
-
-
- Scenario: Street, postcode and country
- When sending xml structured query with address
- | street | postalcode | country
- | Old Palace Road | GU2 7UP | United Kingdom
- Then at least 1 result is returned
- Then result header contains
- | attr | value
- | querystring | Old Palace Road, GU2 7UP, United Kingdom
-
-
- Scenario: gihub #176
- When sending json structured query with address
- | city
- | Washington
- Then at least 1 result is returned
+++ /dev/null
-@DB
-Feature: Import of address interpolations
- Tests that interpolated addresses are added correctly
-
- Scenario: Simple even interpolation line with two points
- Given the place nodes
- | osm_id | osm_type | class | type | housenumber | geometry
- | 1 | N | place | house | 2 | 1 1
- | 2 | N | place | house | 6 | 1 1.001
- And the place ways
- | osm_id | osm_type | class | type | housenumber | geometry
- | 1 | W | place | houses | even | 1 1, 1 1.001
- And the ways
- | id | nodes
- | 1 | 1,2
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 6 | 1 1, 1 1.001
-
- Scenario: Backwards even two point interpolation line
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | 1 1
- | 2 | place | house | 6 | 1 1.001
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 1 1.001, 1 1
- And the ways
- | id | nodes
- | 1 | 2,1
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 6 | 1 1, 1 1.001
-
- Scenario: Simple odd two point interpolation
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 1 | 1 1
- | 2 | place | house | 11 | 1 1.001
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | odd | 1 1, 1 1.001
- And the ways
- | id | nodes
- | 1 | 1,2
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 1 | 11 | 1 1, 1 1.001
-
- Scenario: Simple all two point interpolation
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 1 | 1 1
- | 2 | place | house | 3 | 1 1.001
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | all | 1 1, 1 1.001
- And the ways
- | id | nodes
- | 1 | 1,2
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 1 | 3 | 1 1, 1 1.001
-
- Scenario: Even two point interpolation line with intermediate empty node
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | 1 1
- | 2 | place | house | 10 | 1.001 1.001
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001
- And the ways
- | id | nodes
- | 1 | 1,3,2
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 10 | 1 1, 1 1.001, 1.001 1.001
-
- Scenario: Even two point interpolation line with intermediate duplicated empty node
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | 1 1
- | 2 | place | house | 10 | 1.001 1.001
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001
- And the ways
- | id | nodes
- | 1 | 1,3,3,2
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 10 | 1 1, 1 1.001, 1.001 1.001
-
- Scenario: Simple even three point interpolation line
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | 1 1
- | 2 | place | house | 14 | 1.001 1.001
- | 3 | place | house | 10 | 1 1.001
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001
- And the ways
- | id | nodes
- | 1 | 1,3,2
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 10 | 1 1, 1 1.001
- | 10 | 14 | 1 1.001, 1.001 1.001
-
- Scenario: Simple even four point interpolation line
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | 1 1
- | 2 | place | house | 14 | 1.001 1.001
- | 3 | place | house | 10 | 1 1.001
- | 4 | place | house | 18 | 1.001 1.002
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001, 1.001 1.002
- And the ways
- | id | nodes
- | 1 | 1,3,2,4
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 10 | 1 1, 1 1.001
- | 10 | 14 | 1 1.001, 1.001 1.001
- | 14 | 18 | 1.001 1.001, 1.001 1.002
-
- Scenario: Reverse simple even three point interpolation line
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | 1 1
- | 2 | place | house | 14 | 1.001 1.001
- | 3 | place | house | 10 | 1 1.001
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 1.001 1.001, 1 1.001, 1 1
- And the ways
- | id | nodes
- | 1 | 2,3,1
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 10 | 1 1, 1 1.001
- | 10 | 14 | 1 1.001, 1.001 1.001
-
- Scenario: Even three point interpolation line with odd center point
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | 1 1
- | 2 | place | house | 8 | 1.001 1.001
- | 3 | place | house | 7 | 1 1.001
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 1 1, 1 1.001, 1.001 1.001
- And the ways
- | id | nodes
- | 1 | 1,3,2
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 7 | 1 1, 1 1.001
- | 7 | 8 | 1 1.001, 1.001 1.001
-
- Scenario: Interpolation line with self-intersecting way
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | 0 0
- | 2 | place | house | 6 | 0 0.001
- | 3 | place | house | 10 | 0 0.002
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 0 0, 0 0.001, 0 0.002, 0 0.001
- And the ways
- | id | nodes
- | 1 | 1,2,3,2
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 6 | 0 0, 0 0.001
- | 6 | 10 | 0 0.001, 0 0.002
- | 6 | 10 | 0 0.001, 0 0.002
-
- Scenario: Interpolation line with self-intersecting way II
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | 0 0
- | 2 | place | house | 6 | 0 0.001
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 0 0, 0 0.001, 0 0.002, 0 0.001
- And the ways
- | id | nodes
- | 1 | 1,2,3,2
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 6 | 0 0, 0 0.001
-
- Scenario: addr:street on interpolation way
- Given the scene parallel-road
- And the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | :n-middle-w
- | 2 | place | house | 6 | :n-middle-e
- | 3 | place | house | 12 | :n-middle-w
- | 4 | place | house | 16 | :n-middle-e
- And the place ways
- | osm_id | class | type | housenumber | street | geometry
- | 10 | place | houses | even | | :w-middle
- | 11 | place | houses | even | Cloud Street | :w-middle
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | tertiary | 'name' : 'Sun Way' | :w-north
- | 3 | highway | tertiary | 'name' : 'Cloud Street' | :w-south
- And the ways
- | id | nodes
- | 10 | 1,100,101,102,2
- | 11 | 3,200,201,202,4
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W2
- | N2 | W2
- | N3 | W3
- | N4 | W3
- Then table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W2 | 2 | 6
- | W11 | W3 | 12 | 16
- When sending query "16 Cloud Street"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 4
- When sending query "14 Cloud Street"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | W | 11
- When sending query "18 Cloud Street"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | W | 3
-
- Scenario: addr:street on housenumber way
- Given the scene parallel-road
- And the place nodes
- | osm_id | class | type | housenumber | street | geometry
- | 1 | place | house | 2 | | :n-middle-w
- | 2 | place | house | 6 | | :n-middle-e
- | 3 | place | house | 12 | Cloud Street | :n-middle-w
- | 4 | place | house | 16 | Cloud Street | :n-middle-e
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 10 | place | houses | even | :w-middle
- | 11 | place | houses | even | :w-middle
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | tertiary | 'name' : 'Sun Way' | :w-north
- | 3 | highway | tertiary | 'name' : 'Cloud Street' | :w-south
- And the ways
- | id | nodes
- | 10 | 1,100,101,102,2
- | 11 | 3,200,201,202,4
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W2
- | N2 | W2
- | N3 | W3
- | N4 | W3
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W2 | 2 | 6
- | W11 | W3 | 12 | 16
- When sending query "16 Cloud Street"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 4
- When sending query "14 Cloud Street"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | W | 11
-
- Scenario: Geometry of points and way don't match (github #253)
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 10 | 144.9632341 -37.76163
- | 2 | place | house | 6 | 144.9630541 -37.7628174
- | 3 | shop | supermarket | 2 | 144.9629794 -37.7630755
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | 144.9632341 -37.76163,144.9630541 -37.7628172,144.9629794 -37.7630755
- And the ways
- | id | nodes
- | 1 | 1,2,3
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 2 | 6 | 144.9629794 -37.7630755, 144.9630541 -37.7628174
- | 6 | 10 | 144.9630541 -37.7628174, 144.9632341 -37.76163
-
- Scenario: Place with missing address information
- Given the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 23 | 0.0001 0.0001
- | 2 | amenity | school | | 0.0001 0.0002
- | 3 | place | house | 29 | 0.0001 0.0004
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | odd | 0.0001 0.0001,0.0001 0.0002,0.0001 0.0004
- And the ways
- | id | nodes
- | 1 | 1,2,3
- When importing
- Then way 1 expands to lines
- | startnumber | endnumber | geometry
- | 23 | 29 | 0.0001 0.0001, 0.0001 0.0002, 0.0001 0.0004
+++ /dev/null
-@DB
-Feature: Linking of places
- Tests for correctly determining linked places
-
- Scenario: Only address-describing places can be linked
- Given the scene way-area-with-center
- And the place areas
- | osm_type | osm_id | class | type | name | geometry
- | R | 13 | landuse | forest | Garbo | :area
- And the place nodes
- | osm_id | class | type | name | geometry
- | 256 | natural | peak | Garbo | :inner-C
- When importing
- Then table placex contains
- | object | linked_place_id
- | R13 | None
- | N256 | None
-
- Scenario: Waterways are linked when in waterway relations
- Given the scene split-road
- And the place ways
- | osm_type | osm_id | class | type | name | geometry
- | W | 1 | waterway | river | Rhein | :w-2
- | W | 2 | waterway | river | Rhein | :w-3
- | R | 13 | waterway | river | Rhein | :w-1 + :w-2 + :w-3
- | R | 23 | waterway | river | Limmat| :w-4a
- And the relations
- | id | members | tags
- | 13 | R23:tributary,W1,W2:main_stream | 'type' : 'waterway'
- When importing
- Then table placex contains
- | object | linked_place_id
- | W1 | R13
- | W2 | R13
- | R13 | None
- | R23 | None
- When sending query "rhein"
- Then results contain
- | osm_type
- | R
-
- Scenario: Relations are not linked when in waterway relations
- Given the scene split-road
- And the place ways
- | osm_type | osm_id | class | type | name | geometry
- | W | 1 | waterway | river | Rhein | :w-2
- | W | 2 | waterway | river | Rhein | :w-3
- | R | 1 | waterway | river | Rhein | :w-1 + :w-2 + :w-3
- | R | 2 | waterway | river | Limmat| :w-4a
- And the relations
- | id | members | tags
- | 1 | R2 | 'type' : 'waterway'
- When importing
- Then table placex contains
- | object | linked_place_id
- | W1 | None
- | W2 | None
- | R1 | None
- | R2 | None
-
- Scenario: Empty waterway relations are handled correctly
- Given the scene split-road
- And the place ways
- | osm_type | osm_id | class | type | name | geometry
- | R | 1 | waterway | river | Rhein | :w-1 + :w-2 + :w-3
- And the relations
- | id | members | tags
- | 1 | | 'type' : 'waterway'
- When importing
- Then table placex contains
- | object | linked_place_id
- | R1 | None
-
- Scenario: Waterways are not linked when waterway types don't match
- Given the scene split-road
- And the place ways
- | osm_type | osm_id | class | type | name | geometry
- | W | 1 | waterway | drain | Rhein | :w-2
- | R | 1 | waterway | river | Rhein | :w-1 + :w-2 + :w-3
- And the relations
- | id | members | tags
- | 1 | N23,N34,W1,R45 | 'type' : 'multipolygon'
- When importing
- Then table placex contains
- | object | linked_place_id
- | W1 | None
- | R1 | None
- When sending query "rhein"
- Then results contain
- | ID | osm_type
- | 0 | R
- | 1 | W
-
- Scenario: Side streams are linked only when they have the same name
- Given the scene split-road
- And the place ways
- | osm_type | osm_id | class | type | name | geometry
- | W | 1 | waterway | river | Rhein2 | :w-2
- | W | 2 | waterway | river | Rhein | :w-3
- | R | 1 | waterway | river | Rhein | :w-1 + :w-2 + :w-3
- And the relations
- | id | members | tags
- | 1 | W1:side_stream,W2:side_stream | 'type' : 'waterway'
- When importing
- Then table placex contains
- | object | linked_place_id
- | W1 | None
- | W2 | R1
- When sending query "rhein2"
- Then results contain
- | osm_type
- | W
+++ /dev/null
-@DB
-Feature: Import and search of names
- Tests all naming related issues: normalisation,
- abbreviations, internationalisation, etc.
-
-
- Scenario: Case-insensitivity of search
- Given the place nodes
- | osm_id | class | type | name
- | 1 | place | locality | 'name' : 'FooBar'
- When importing
- Then table placex contains
- | object | class | type | name
- | N1 | place | locality | 'name' : 'FooBar'
- When sending query "FooBar"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "foobar"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "fOObar"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "FOOBAR"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
-
- Scenario: Multiple spaces in name
- Given the place nodes
- | osm_id | class | type | name
- | 1 | place | locality | 'name' : 'one two three'
- When importing
- When sending query "one two three"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "one two three"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "one two three"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query " one two three"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
-
- Scenario: Special characters in name
- Given the place nodes
- | osm_id | class | type | name
- | 1 | place | locality | 'name' : 'Jim-Knopf-Str'
- | 2 | place | locality | 'name' : 'Smith/Weston'
- | 3 | place | locality | 'name' : 'space mountain'
- | 4 | place | locality | 'name' : 'space'
- | 5 | place | locality | 'name' : 'mountain'
- When importing
- When sending query "Jim-Knopf-Str"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "Jim Knopf-Str"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "Jim Knopf Str"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "Jim/Knopf-Str"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "Jim-Knopfstr"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
- When sending query "Smith/Weston"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 2
- When sending query "Smith Weston"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 2
- When sending query "Smith-Weston"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 2
- When sending query "space mountain"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 3
- When sending query "space-mountain"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 3
- When sending query "space/mountain"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 3
- When sending query "space\mountain"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 3
- When sending query "space(mountain)"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 3
-
- Scenario: No copying name tag if only one name
- Given the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | locality | 'name' : 'german' | country:de
- When importing
- Then table placex contains
- | object | calculated_country_code |
- | N1 | de
- And table placex contains as names for N1
- | object | k | v
- | N1 | name | german
-
- Scenario: Copying name tag to default language if it does not exist
- Given the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | locality | 'name' : 'german', 'name:fi' : 'finnish' | country:de
- When importing
- Then table placex contains
- | object | calculated_country_code |
- | N1 | de
- And table placex contains as names for N1
- | k | v
- | name | german
- | name:fi | finnish
- | name:de | german
-
- Scenario: Copying default language name tag to name if it does not exist
- Given the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | locality | 'name:de' : 'german', 'name:fi' : 'finnish' | country:de
- When importing
- Then table placex contains
- | object | calculated_country_code |
- | N1 | de
- And table placex contains as names for N1
- | k | v
- | name | german
- | name:fi | finnish
- | name:de | german
-
- Scenario: Do not overwrite default language with name tag
- Given the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | locality | 'name' : 'german', 'name:fi' : 'finnish', 'name:de' : 'local' | country:de
- When importing
- Then table placex contains
- | object | calculated_country_code |
- | N1 | de
- And table placex contains as names for N1
- | k | v
- | name | german
- | name:fi | finnish
- | name:de | local
-
- Scenario: Landuse with name are found
- Given the place areas
- | osm_type | osm_id | class | type | name | geometry
- | R | 1 | natural | meadow | 'name' : 'landuse1' | (0 0, 1 0, 1 1, 0 1, 0 0)
- | R | 2 | landuse | industrial | 'name' : 'landuse2' | (0 0, -1 0, -1 -1, 0 -1, 0 0)
- When importing
- When sending query "landuse1"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | R | 1
- When sending query "landuse2"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | R | 2
-
- Scenario: Postcode boundaries without ref
- Given the place areas
- | osm_type | osm_id | class | type | postcode | geometry
- | R | 1 | boundary | postal_code | 12345 | (0 0, 1 0, 1 1, 0 1, 0 0)
- When importing
- When sending query "12345"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | R | 1
+++ /dev/null
-@DB
-Feature: Parenting of objects
- Tests that the correct parent is choosen
-
- Scenario: Address inherits postcode from its street unless it has a postcode
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 4 | :p-N1
- And the place nodes
- | osm_id | class | type | housenumber | postcode | geometry
- | 2 | place | house | 5 | 99999 | :p-N1
- And the place ways
- | osm_id | class | type | name | postcode | geometry
- | 1 | highway | residential | galoo | 12345 | :w-north
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W1
- | N2 | W1
- When sending query "4 galoo"
- Then results contain
- | ID | osm_type | osm_id | langaddress
- | 0 | N | 1 | 4, galoo, 12345
- When sending query "5 galoo"
- Then results contain
- | ID | osm_type | osm_id | langaddress
- | 0 | N | 2 | 5, galoo, 99999
-
-
- Scenario: Address without tags, closest street
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | geometry
- | 1 | place | house | :p-N1
- | 2 | place | house | :p-N2
- | 3 | place | house | :p-S1
- | 4 | place | house | :p-S2
- And the named place ways
- | osm_id | class | type | geometry
- | 1 | highway | residential | :w-north
- | 2 | highway | residential | :w-south
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W1
- | N2 | W1
- | N3 | W2
- | N4 | W2
-
- Scenario: Address without tags avoids unnamed streets
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | geometry
- | 1 | place | house | :p-N1
- | 2 | place | house | :p-N2
- | 3 | place | house | :p-S1
- | 4 | place | house | :p-S2
- And the place ways
- | osm_id | class | type | geometry
- | 1 | highway | residential | :w-north
- And the named place ways
- | osm_id | class | type | geometry
- | 2 | highway | residential | :w-south
- When importing
- Then table 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 scene roads-with-pois
- And the place nodes
- | osm_id | class | type | street| geometry
- | 1 | place | house | south | :p-N1
- | 2 | place | house | north | :p-N2
- | 3 | place | house | south | :p-S1
- | 4 | place | house | north | :p-S2
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | north | :w-north
- | 2 | highway | residential | south | :w-south
- When importing
- Then table 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 scene roads-with-pois
- And the place nodes
- | osm_id | class | type | street | geometry
- | 1 | place | house | abcdef | :p-N1
- | 2 | place | house | abcdef | :p-N2
- | 3 | place | house | abcdef | :p-S1
- | 4 | place | house | abcdef | :p-S2
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | abcdef | :w-north
- | 2 | highway | residential | abcdef | :w-south
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W1
- | N2 | W1
- | N3 | W2
- | N4 | W2
-
- Scenario: addr:street tag without appropriately named street
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | street | geometry
- | 1 | place | house | abcdef | :p-N1
- | 2 | place | house | abcdef | :p-N2
- | 3 | place | house | abcdef | :p-S1
- | 4 | place | house | abcdef | :p-S2
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | abcde | :w-north
- | 2 | highway | residential | abcde | :w-south
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W1
- | N2 | W1
- | N3 | W2
- | N4 | W2
-
- Scenario: addr:place address
- Given the scene road-with-alley
- And the place nodes
- | osm_id | class | type | addr_place | geometry
- | 1 | place | house | myhamlet | :n-alley
- And the place nodes
- | osm_id | class | type | name | geometry
- | 2 | place | hamlet | myhamlet | :n-main-west
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | myhamlet | :w-main
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | N2
-
- Scenario: addr:street is preferred over addr:place
- Given the scene road-with-alley
- And the place nodes
- | osm_id | class | type | addr_place | street | geometry
- | 1 | place | house | myhamlet | mystreet| :n-alley
- And the place nodes
- | osm_id | class | type | name | geometry
- | 2 | place | hamlet | myhamlet | :n-main-west
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | mystreet | :w-main
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W1
-
- Scenario: Untagged address in simple associated street relation
- Given the scene road-with-alley
- And the place nodes
- | osm_id | class | type | geometry
- | 1 | place | house | :n-alley
- | 2 | place | house | :n-corner
- | 3 | place | house | :n-main-west
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | foo | :w-main
- | 2 | highway | service | bar | :w-alley
- And the relations
- | id | members | tags
- | 1 | W1:street,N1,N2,N3 | 'type' : 'associatedStreet'
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W1
- | N2 | W1
- | N3 | W1
-
- Scenario: Avoid unnamed streets in simple associated street relation
- Given the scene road-with-alley
- And the place nodes
- | osm_id | class | type | geometry
- | 1 | place | house | :n-alley
- | 2 | place | house | :n-corner
- | 3 | place | house | :n-main-west
- And the named place ways
- | osm_id | class | type | geometry
- | 1 | highway | residential | :w-main
- And the place ways
- | osm_id | class | type | geometry
- | 2 | highway | residential | :w-alley
- And the relations
- | id | members | tags
- | 1 | N1,N2,N3,W2:street,W1:street | 'type' : 'associatedStreet'
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W1
- | N2 | W1
- | N3 | W1
-
- ### Scenario 10
- Scenario: Associated street relation overrides addr:street
- Given the scene road-with-alley
- And the place nodes
- | osm_id | class | type | street | geometry
- | 1 | place | house | bar | :n-alley
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | foo | :w-main
- | 2 | highway | residential | bar | :w-alley
- And the relations
- | id | members | tags
- | 1 | W1:street,N1,N2,N3 | 'type' : 'associatedStreet'
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W1
-
- Scenario: Building without tags, closest street from center point
- Given the scene building-on-street-corner
- And the named place ways
- | osm_id | class | type | geometry
- | 1 | building | yes | :w-building
- | 2 | highway | primary | :w-WE
- | 3 | highway | residential | :w-NS
- When importing
- Then table placex contains
- | object | parent_place_id
- | W1 | W3
-
- Scenario: Building with addr:street tags
- Given the scene building-on-street-corner
- And the named place ways
- | osm_id | class | type | street | geometry
- | 1 | building | yes | bar | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | primary | bar | :w-WE
- | 3 | highway | residential | foo | :w-NS
- When importing
- Then table placex contains
- | object | parent_place_id
- | W1 | W2
-
- Scenario: Building with addr:place tags
- Given the scene building-on-street-corner
- And the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | village | bar | :n-outer
- And the named place ways
- | osm_id | class | type | addr_place | geometry
- | 1 | building | yes | bar | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | primary | bar | :w-WE
- | 3 | highway | residential | foo | :w-NS
- When importing
- Then table placex contains
- | object | parent_place_id
- | W1 | N1
-
- Scenario: Building in associated street relation
- Given the scene building-on-street-corner
- And the named place ways
- | osm_id | class | type | geometry
- | 1 | building | yes | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | primary | bar | :w-WE
- | 3 | highway | residential | foo | :w-NS
- And the relations
- | id | members | tags
- | 1 | W1:house,W2:street | 'type' : 'associatedStreet'
- When importing
- Then table placex contains
- | object | parent_place_id
- | W1 | W2
-
- Scenario: Building in associated street relation overrides addr:street
- Given the scene building-on-street-corner
- And the named place ways
- | osm_id | class | type | street | geometry
- | 1 | building | yes | foo | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | primary | bar | :w-WE
- | 3 | highway | residential | foo | :w-NS
- And the relations
- | id | members | tags
- | 1 | W1:house,W2:street | 'type' : 'associatedStreet'
- When importing
- Then table placex contains
- | object | parent_place_id
- | W1 | W2
-
- Scenario: Wrong member in associated street relation is ignored
- Given the scene building-on-street-corner
- And the named place nodes
- | osm_id | class | type | geometry
- | 1 | place | house | :n-outer
- And the named place ways
- | osm_id | class | type | street | geometry
- | 1 | building | yes | foo | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | primary | bar | :w-WE
- | 3 | highway | residential | foo | :w-NS
- And the relations
- | id | members | tags
- | 1 | N1:house,W1:street,W3:street | 'type' : 'associatedStreet'
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W3
-
- Scenario: POIs in building inherit address
- Given the scene building-on-street-corner
- And the named place nodes
- | osm_id | class | type | geometry
- | 1 | amenity | bank | :n-inner
- | 2 | shop | bakery | :n-edge-NS
- | 3 | shop | supermarket| :n-edge-WE
- And the place ways
- | osm_id | class | type | street | addr_place | housenumber | geometry
- | 1 | building | yes | foo | nowhere | 3 | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | primary | bar | :w-WE
- | 3 | highway | residential | foo | :w-NS
- When importing
- Then table placex contains
- | object | parent_place_id | street | addr_place | housenumber
- | W1 | W3 | foo | nowhere | 3
- | N1 | W3 | foo | nowhere | 3
- | N2 | W3 | foo | nowhere | 3
- | N3 | W3 | foo | nowhere | 3
-
- Scenario: POIs don't inherit from streets
- Given the scene building-on-street-corner
- And the named place nodes
- | osm_id | class | type | geometry
- | 1 | amenity | bank | :n-inner
- And the place ways
- | osm_id | class | type | street | addr_place | housenumber | geometry
- | 1 | highway | path | foo | nowhere | 3 | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 3 | highway | residential | foo | :w-NS
- When importing
- Then table placex contains
- | object | parent_place_id | street | addr_place | housenumber
- | N1 | W3 | None | None | None
-
- Scenario: POIs with own address do not inherit building address
- Given the scene building-on-street-corner
- And the named place nodes
- | osm_id | class | type | street | geometry
- | 1 | amenity | bank | bar | :n-inner
- And the named place nodes
- | osm_id | class | type | housenumber | geometry
- | 2 | shop | bakery | 4 | :n-edge-NS
- And the named place nodes
- | osm_id | class | type | addr_place | geometry
- | 3 | shop | supermarket| nowhere | :n-edge-WE
- And the place nodes
- | osm_id | class | type | name | geometry
- | 4 | place | isolated_dwelling | theplace | :n-outer
- And the place ways
- | osm_id | class | type | addr_place | housenumber | geometry
- | 1 | building | yes | theplace | 3 | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | primary | bar | :w-WE
- | 3 | highway | residential | foo | :w-NS
- When importing
- Then table placex contains
- | object | parent_place_id | street | addr_place | housenumber
- | W1 | N4 | None | theplace | 3
- | N1 | W2 | bar | None | None
- | N2 | W3 | None | None | 4
- | N3 | W2 | None | nowhere | None
-
- ### Scenario 20
- Scenario: POIs parent a road if they are attached to it
- Given the scene points-on-roads
- And the named place nodes
- | osm_id | class | type | street | geometry
- | 1 | highway | bus_stop | North St | :n-SE
- | 2 | highway | bus_stop | South St | :n-NW
- | 3 | highway | bus_stop | North St | :n-S-unglued
- | 4 | highway | bus_stop | South St | :n-N-unglued
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | secondary | North St | :w-north
- | 2 | highway | unclassified | South St | :w-south
- And the ways
- | id | nodes
- | 1 | 100,101,2,103,104
- | 2 | 200,201,1,202,203
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W1
- | N2 | W2
- | N3 | W1
- | N4 | W2
-
- Scenario: POIs do not parent non-roads they are attached to
- Given the scene points-on-roads
- And the named place nodes
- | osm_id | class | type | street | geometry
- | 1 | highway | bus_stop | North St | :n-SE
- | 2 | highway | bus_stop | South St | :n-NW
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | landuse | residential | North St | :w-north
- | 2 | waterway| river | South St | :w-south
- And the ways
- | id | nodes
- | 1 | 100,101,2,103,104
- | 2 | 200,201,1,202,203
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | 0
- | N2 | 0
-
- Scenario: POIs on building outlines inherit associated street relation
- Given the scene building-on-street-corner
- And the named place nodes
- | osm_id | class | type | geometry
- | 1 | place | house | :n-edge-NS
- And the named place ways
- | osm_id | class | type | geometry
- | 1 | building | yes | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | primary | bar | :w-WE
- | 3 | highway | residential | foo | :w-NS
- And the relations
- | id | members | tags
- | 1 | W1:house,W2:street | 'type' : 'associatedStreet'
- And the ways
- | id | nodes
- | 1 | 100,1,101,102,100
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W2
-
+++ /dev/null
-@DB
-Feature: Import into placex
- Tests that data in placex is completed correctly.
-
- Scenario: No country code tag is available
- Given the place nodes
- | osm_id | class | type | name | geometry
- | 1 | highway | primary | 'name' : 'A1' | country:us
- When importing
- Then table placex contains
- | object | country_code | calculated_country_code |
- | N1 | None | us |
-
- Scenario: Location overwrites country code tag
- Given the scene country
- And the place nodes
- | osm_id | class | type | name | country_code | geometry
- | 1 | highway | primary | 'name' : 'A1' | de | :us
- When importing
- Then table placex contains
- | object | country_code | calculated_country_code |
- | N1 | de | us |
-
- Scenario: Country code tag overwrites location for countries
- Given the place areas
- | osm_type | osm_id | class | type | admin_level | name | country_code | geometry
- | R | 1 | boundary | administrative | 2 | 'name' : 'foo' | de | (-100 40, -101 40, -101 41, -100 41, -100 40)
- When importing
- Then table placex contains
- | object | country_code | calculated_country_code |
- | R1 | de | de |
-
- Scenario: Illegal country code tag for countries is ignored
- And the place areas
- | osm_type | osm_id | class | type | admin_level | name | country_code | geometry
- | R | 1 | boundary | administrative | 2 | 'name' : 'foo' | xx | (-100 40, -101 40, -101 41, -100 41, -100 40)
- When importing
- Then table placex contains
- | object | country_code | calculated_country_code |
- | R1 | xx | us |
-
- Scenario: admin level is copied over
- Given the place nodes
- | osm_id | class | type | admin_level | name
- | 1 | place | state | 3 | 'name' : 'foo'
- When importing
- Then table placex contains
- | object | admin_level |
- | N1 | 3 |
-
- Scenario: admin level is default 15
- Given the place nodes
- | osm_id | class | type | name
- | 1 | amenity | prison | 'name' : 'foo'
- When importing
- Then table placex contains
- | object | admin_level |
- | N1 | 15 |
-
- Scenario: admin level is never larger than 15
- Given the place nodes
- | osm_id | class | type | name | admin_level
- | 1 | amenity | prison | 'name' : 'foo' | 16
- When importing
- Then table placex contains
- | object | admin_level |
- | N1 | 15 |
-
-
- Scenario: postcode node without postcode is dropped
- Given the place nodes
- | osm_id | class | type
- | 1 | place | postcode
- When importing
- Then table placex has no entry for N1
-
- Scenario: postcode boundary without postcode is dropped
- Given the place areas
- | osm_type | osm_id | class | type | geometry
- | R | 1 | boundary | postal_code | poly-area:0.1
- When importing
- Then table placex has no entry for R1
-
- Scenario: search and address ranks for GB post codes correctly assigned
- Given the place nodes
- | osm_id | class | type | postcode | geometry
- | 1 | place | postcode | E45 2CD | country:gb
- | 2 | place | postcode | E45 2 | country:gb
- | 3 | place | postcode | Y45 | country:gb
- When importing
- Then table placex contains
- | object | postcode | calculated_country_code | rank_search | rank_address
- | N1 | E45 2CD | gb | 25 | 5
- | N2 | E45 2 | gb | 23 | 5
- | N3 | Y45 | gb | 21 | 5
-
- Scenario: wrongly formatted GB postcodes are down-ranked
- Given the place nodes
- | osm_id | class | type | postcode | geometry
- | 1 | place | postcode | EA452CD | country:gb
- | 2 | place | postcode | E45 23 | country:gb
- | 3 | place | postcode | y45 | country:gb
- When importing
- Then table placex contains
- | object | calculated_country_code | rank_search | rank_address
- | N1 | gb | 30 | 30
- | N2 | gb | 30 | 30
- | N3 | gb | 30 | 30
-
- Scenario: search and address rank for DE postcodes correctly assigned
- Given the place nodes
- | osm_id | class | type | postcode | geometry
- | 1 | place | postcode | 56427 | country:de
- | 2 | place | postcode | 5642 | country:de
- | 3 | place | postcode | 5642A | country:de
- | 4 | place | postcode | 564276 | country:de
- When importing
- Then table placex contains
- | object | calculated_country_code | rank_search | rank_address
- | N1 | de | 21 | 11
- | N2 | de | 30 | 30
- | N3 | de | 30 | 30
- | N4 | de | 30 | 30
-
- Scenario: search and address rank for other postcodes are correctly assigned
- Given the place nodes
- | osm_id | class | type | postcode | geometry
- | 1 | place | postcode | 1 | country:ca
- | 2 | place | postcode | X3 | country:ca
- | 3 | place | postcode | 543 | country:ca
- | 4 | place | postcode | 54dc | country:ca
- | 5 | place | postcode | 12345 | country:ca
- | 6 | place | postcode | 55TT667 | country:ca
- | 7 | place | postcode | 123-65 | country:ca
- | 8 | place | postcode | 12 445 4 | country:ca
- | 9 | place | postcode | A1:bc10 | country:ca
- When importing
- Then table placex contains
- | object | calculated_country_code | rank_search | rank_address
- | N1 | ca | 21 | 11
- | N2 | ca | 21 | 11
- | N3 | ca | 21 | 11
- | N4 | ca | 21 | 11
- | N5 | ca | 21 | 11
- | N6 | ca | 21 | 11
- | N7 | ca | 25 | 11
- | N8 | ca | 25 | 11
- | N9 | ca | 25 | 11
-
-
- Scenario: search and address ranks for places are correctly assigned
- Given the named place nodes
- | osm_id | class | type |
- | 1 | foo | bar |
- | 11 | place | Continent |
- | 12 | place | continent |
- | 13 | place | sea |
- | 14 | place | country |
- | 15 | place | state |
- | 16 | place | region |
- | 17 | place | county |
- | 18 | place | city |
- | 19 | place | island |
- | 20 | place | town |
- | 21 | place | village |
- | 22 | place | hamlet |
- | 23 | place | municipality |
- | 24 | place | district |
- | 25 | place | unincorporated_area |
- | 26 | place | borough |
- | 27 | place | suburb |
- | 28 | place | croft |
- | 29 | place | subdivision |
- | 30 | place | isolated_dwelling |
- | 31 | place | farm |
- | 32 | place | locality |
- | 33 | place | islet |
- | 34 | place | mountain_pass |
- | 35 | place | neighbourhood |
- | 36 | place | house |
- | 37 | place | building |
- | 38 | place | houses |
- And the named place nodes
- | osm_id | class | type | extratags
- | 100 | place | locality | 'locality' : 'townland'
- | 101 | place | city | 'capital' : 'yes'
- When importing
- Then table placex contains
- | object | rank_search | rank_address |
- | N1 | 30 | 30 |
- | N11 | 30 | 30 |
- | N12 | 2 | 2 |
- | N13 | 2 | 0 |
- | N14 | 4 | 4 |
- | N15 | 8 | 8 |
- | N16 | 18 | 0 |
- | N17 | 12 | 12 |
- | N18 | 16 | 16 |
- | N19 | 17 | 0 |
- | N20 | 18 | 16 |
- | N21 | 19 | 16 |
- | N22 | 19 | 16 |
- | N23 | 19 | 16 |
- | N24 | 19 | 16 |
- | N25 | 19 | 16 |
- | N26 | 19 | 16 |
- | N27 | 20 | 20 |
- | N28 | 20 | 20 |
- | N29 | 20 | 20 |
- | N30 | 20 | 20 |
- | N31 | 20 | 0 |
- | N32 | 20 | 0 |
- | N33 | 20 | 0 |
- | N34 | 20 | 0 |
- | N100 | 20 | 20 |
- | N101 | 15 | 16 |
- | N35 | 22 | 22 |
- | N36 | 30 | 30 |
- | N37 | 30 | 30 |
- | N38 | 28 | 0 |
-
- Scenario: search and address ranks for boundaries are correctly assigned
- Given the named place nodes
- | osm_id | class | type
- | 1 | boundary | administrative
- And the named place ways
- | osm_id | class | type | geometry
- | 10 | boundary | administrative | 10 10, 11 11
- And the named place areas
- | osm_type | osm_id | class | type | admin_level | geometry
- | R | 20 | boundary | administrative | 2 | (1 1, 2 2, 1 2, 1 1)
- | R | 21 | boundary | administrative | 32 | (3 3, 4 4, 3 4, 3 3)
- | R | 22 | boundary | nature_park | 6 | (0 0, 1 0, 0 1, 0 0)
- | R | 23 | boundary | natural_reserve| 10 | (0 0, 1 1, 1 0, 0 0)
- When importing
- Then table placex has no entry for N1
- And table placex has no entry for W10
- And table placex contains
- | object | rank_search | rank_address
- | R20 | 4 | 4
- | R21 | 30 | 30
- | R22 | 12 | 0
- | R23 | 20 | 0
-
- Scenario: search and address ranks for highways correctly assigned
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type
- | 1 | highway | bus_stop
- And the place ways
- | osm_id | class | type | geometry
- | 1 | highway | primary | :w-south
- | 2 | highway | secondary | :w-south
- | 3 | highway | tertiary | :w-south
- | 4 | highway | residential | :w-north
- | 5 | highway | unclassified | :w-north
- | 6 | highway | something | :w-north
- When importing
- Then table 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 | 26 | 26
-
- Scenario: rank and inclusion of landuses
- And the named place nodes
- | osm_id | class | type
- | 2 | landuse | residential
- And the named place ways
- | osm_id | class | type | geometry
- | 2 | landuse | residential | 1 1, 1 1.1
- And the named place areas
- | osm_type | osm_id | class | type | geometry
- | W | 4 | landuse | residential | poly-area:0.1
- | R | 2 | landuse | residential | poly-area:0.05
- | R | 3 | landuse | forrest | poly-area:0.5
- When importing
- And table 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
- And the named place nodes
- | osm_id | class | type
- | 2 | natural | peak
- | 4 | natural | volcano
- | 5 | natural | foobar
- And the named place ways
- | osm_id | class | type | geometry
- | 2 | natural | mountain_range | 12 12,11 11
- | 3 | natural | foobar | 13 13,13.1 13
- And the named place areas
- | osm_type | osm_id | class | type | geometry
- | R | 3 | natural | volcano | poly-area:0.1
- | R | 4 | natural | foobar | poly-area:0.5
- | R | 5 | natural | sea | poly-area:5.0
- | R | 6 | natural | sea | poly-area:0.01
- When importing
- And table placex contains
- | object | rank_search | rank_address
- | N2 | 18 | 0
- | N4 | 18 | 0
- | N5 | 30 | 30
- | W2 | 18 | 0
- | R3 | 18 | 0
- | R4 | 22 | 0
- | R5 | 4 | 4
- | R6 | 4 | 4
- | W3 | 30 | 30
-
+++ /dev/null
-@DB
-Feature: Creation of search terms
- Tests that search_name table is filled correctly
-
- Scenario: POIs without a name have no search entry
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | geometry
- | 1 | place | house | :p-N1
- And the place ways
- | osm_id | class | type | geometry
- | 1 | highway | residential | :w-north
- When importing
- Then table search_name has no entry for N1
-
-
- Scenario: Named POIs inherit address from parent
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | house | foo | :p-N1
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | the road | :w-north
- When importing
- Then search_name table contains
- | place_id | name_vector | nameaddress_vector
- | N1 | foo | the road
-
- Scenario: Roads take over the postcode from attached houses
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | housenumber | postcode | street | geometry
- | 1 | place | house | 1 | 12345 | North St |:p-S1
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | North St | :w-north
- When importing
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
-
+++ /dev/null
-@DB
-Feature: Import of simple objects
- Testing simple stuff
-
- Scenario: Import place node
- Given the place nodes:
- | osm_id | class | type | name | geometry
- | 1 | place | village | 'name' : 'Foo' | 10.0 -10.0
- When importing
- Then table placex contains
- | object | class | type | name | centroid
- | N1 | place | village | 'name' : 'Foo' | 10.0,-10.0 +- 1m
- When sending query "Foo"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | N | 1
-
+++ /dev/null
-@DB
-Feature: Update of address interpolations
- Test the interpolated address are updated correctly
-
- Scenario: addr:street added to interpolation
- Given the scene parallel-road
- And the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | :n-middle-w
- | 2 | place | house | 6 | :n-middle-e
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 10 | place | houses | even | :w-middle
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | unclassified | 'name' : 'Sun Way' | :w-north
- | 3 | highway | unclassified | 'name' : 'Cloud Street' | :w-south
- And the ways
- | id | nodes
- | 10 | 1,100,101,102,2
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W2
- | N2 | W2
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W2 | 2 | 6
- When updating place ways
- | osm_id | class | type | housenumber | street | geometry
- | 10 | place | houses | even | Cloud Street | :w-middle
- Then table placex contains
- | object | parent_place_id
- | N1 | W3
- | N2 | W3
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W3 | 2 | 6
-
- Scenario: addr:street added to housenumbers
- Given the scene parallel-road
- And the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | :n-middle-w
- | 2 | place | house | 6 | :n-middle-e
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 10 | place | houses | even | :w-middle
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | unclassified | 'name' : 'Sun Way' | :w-north
- | 3 | highway | unclassified | 'name' : 'Cloud Street' | :w-south
- And the ways
- | id | nodes
- | 10 | 1,100,101,102,2
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W2
- | N2 | W2
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W2 | 2 | 6
- When updating place nodes
- | osm_id | class | type | street | housenumber | geometry
- | 1 | place | house | Cloud Street| 2 | :n-middle-w
- | 2 | place | house | Cloud Street| 6 | :n-middle-e
- Then table placex contains
- | object | parent_place_id
- | N1 | W3
- | N2 | W3
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W3 | 2 | 6
-
-
- Scenario: interpolation tag removed
- Given the scene parallel-road
- And the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | :n-middle-w
- | 2 | place | house | 6 | :n-middle-e
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 10 | place | houses | even | :w-middle
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | unclassified | 'name' : 'Sun Way' | :w-north
- | 3 | highway | unclassified | 'name' : 'Cloud Street' | :w-south
- And the ways
- | id | nodes
- | 10 | 1,100,101,102,2
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W2
- | N2 | W2
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W2 | 2 | 6
- When marking for delete W10
- Then table location_property_osmline has no entry for W10
- And table placex contains
- | object | parent_place_id
- | N1 | W2
- | N2 | W2
-
-
- Scenario: referenced road added
- Given the scene parallel-road
- And the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | :n-middle-w
- | 2 | place | house | 6 | :n-middle-e
- And the place ways
- | osm_id | class | type | housenumber | street | geometry
- | 10 | place | houses | even | Cloud Street| :w-middle
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | unclassified | 'name' : 'Sun Way' | :w-north
- And the ways
- | id | nodes
- | 10 | 1,100,101,102,2
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W2
- | N2 | W2
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W2 | 2 | 6
- When updating place ways
- | osm_id | class | type | name | geometry
- | 3 | highway | unclassified | 'name' : 'Cloud Street' | :w-south
- Then table placex contains
- | object | parent_place_id
- | N1 | W3
- | N2 | W3
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W3 | 2 | 6
-
-
- Scenario: referenced road deleted
- Given the scene parallel-road
- And the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | :n-middle-w
- | 2 | place | house | 6 | :n-middle-e
- And the place ways
- | osm_id | class | type | housenumber | street | geometry
- | 10 | place | houses | even | Cloud Street| :w-middle
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | unclassified | 'name' : 'Sun Way' | :w-north
- | 3 | highway | unclassified | 'name' : 'Cloud Street' | :w-south
- And the ways
- | id | nodes
- | 10 | 1,100,101,102,2
- When importing
- Then table placex contains
- | object | parent_place_id
- | N1 | W3
- | N2 | W3
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W3 | 2 | 6
- When marking for delete W3
- Then table placex contains
- | object | parent_place_id
- | N1 | W2
- | N2 | W2
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W10 | W2 | 2 | 6
-
- Scenario: building becomes interpolation
- Given the scene building-with-parallel-streets
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 3 | :w-building
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | unclassified | 'name' : 'Cloud Street' | :w-south
- When importing
- Then table placex contains
- | object | parent_place_id
- | W1 | W2
- When updating place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | :n-north-w
- | 2 | place | house | 6 | :n-north-e
- And the ways
- | id | nodes
- | 1 | 1,100,101,102,2
- And updating place ways
- | osm_id | class | type | housenumber | street | geometry
- | 1 | place | houses | even | Cloud Street| :w-north
- Then table placex has no entry for W1
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W1 | W2 | 2 | 6
-
-
-
- Scenario: interpolation becomes building
- Given the scene building-with-parallel-streets
- And the place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | :n-north-w
- | 2 | place | house | 6 | :n-north-e
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | unclassified | 'name' : 'Cloud Street' | :w-south
- And the ways
- | id | nodes
- | 1 | 1,100,101,102,2
- And the place ways
- | osm_id | class | type | housenumber | street | geometry
- | 1 | place | houses | even | Cloud Street| :w-north
- When importing
- Then table placex has no entry for W1
- And table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W1 | W2 | 2 | 6
- When updating place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 3 | :w-building
- Then table placex contains
- | object | parent_place_id
- | W1 | W2
-
- Scenario: housenumbers added to interpolation
- Given the scene building-with-parallel-streets
- And the place ways
- | osm_id | class | type | name | geometry
- | 2 | highway | unclassified | 'name' : 'Cloud Street' | :w-south
- And the ways
- | id | nodes
- | 1 | 1,100,101,102,2
- And the place ways
- | osm_id | class | type | housenumber | geometry
- | 1 | place | houses | even | :w-north
- When importing
- Then table location_property_osmline has no entry for W1
- When updating place nodes
- | osm_id | class | type | housenumber | geometry
- | 1 | place | house | 2 | :n-north-w
- | 2 | place | house | 6 | :n-north-e
- And updating place ways
- | osm_id | class | type | housenumber | street | geometry
- | 1 | place | houses | even | Cloud Street| :w-north
- Then table location_property_osmline contains
- | object | parent_place_id | startnumber | endnumber
- | W1 | W2 | 2 | 6
-
-
-
+++ /dev/null
-@DB
-Feature: Updates of linked places
- Tests that linked places are correctly added and deleted.
-
-
- Scenario: Add linked place when linking relation is renamed
- Given the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | city | foo | 0 0
- And the place areas
- | osm_type | osm_id | class | type | name | admin_level | geometry
- | R | 1 | boundary | administrative | foo | 8 | poly-area:0.1
- When importing
- And sending query "foo" with dups
- Then results contain
- | osm_type
- | R
- When updating place areas
- | osm_type | osm_id | class | type | name | admin_level | geometry
- | R | 1 | boundary | administrative | foobar | 8 | poly-area:0.1
- Then table placex contains
- | object | linked_place_id
- | N1 | None
- When updating place areas
- | osm_type | osm_id | class | type | name | admin_level | geometry
- When sending query "foo" with dups
- Then results contain
- | osm_type
- | N
-
- Scenario: Add linked place when linking relation is removed
- Given the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | city | foo | 0 0
- And the place areas
- | osm_type | osm_id | class | type | name | admin_level | geometry
- | R | 1 | boundary | administrative | foo | 8 | poly-area:0.1
- When importing
- And sending query "foo" with dups
- Then results contain
- | osm_type
- | R
- When marking for delete R1
- Then table placex contains
- | object | linked_place_id
- | N1 | None
- When updating place areas
- | osm_type | osm_id | class | type | name | admin_level | geometry
- And sending query "foo" with dups
- Then results contain
- | osm_type
- | N
-
- Scenario: Remove linked place when linking relation is added
- Given the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | city | foo | 0 0
- When importing
- And sending query "foo" with dups
- Then results contain
- | osm_type
- | N
- When updating place areas
- | osm_type | osm_id | class | type | name | admin_level | geometry
- | R | 1 | boundary | administrative | foo | 8 | poly-area:0.1
- Then table placex contains
- | object | linked_place_id
- | N1 | R1
- When sending query "foo" with dups
- Then results contain
- | osm_type
- | R
-
- Scenario: Remove linked place when linking relation is renamed
- Given the place nodes
- | osm_id | class | type | name | geometry
- | 1 | place | city | foo | 0 0
- And the place areas
- | osm_type | osm_id | class | type | name | admin_level | geometry
- | R | 1 | boundary | administrative | foobar | 8 | poly-area:0.1
- When importing
- And sending query "foo" with dups
- Then results contain
- | osm_type
- | N
- When updating place areas
- | osm_type | osm_id | class | type | name | admin_level | geometry
- | R | 1 | boundary | administrative | foo | 8 | poly-area:0.1
- Then table placex contains
- | object | linked_place_id
- | N1 | R1
- When sending query "foo" with dups
- Then results contain
- | osm_type
- | R
-
+++ /dev/null
-@DB
-Feature: Update of names in place objects
- Test all naming related issues in updates
-
-
- Scenario: Updating postcode in postcode boundaries without ref
- Given the place areas
- | osm_type | osm_id | class | type | postcode | geometry
- | R | 1 | boundary | postal_code | 12345 | (0 0, 1 0, 1 1, 0 1, 0 0)
- When importing
- And sending query "12345"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | R | 1
- When updating place areas
- | osm_type | osm_id | class | type | postcode | geometry
- | R | 1 | boundary | postal_code | 54321 | (0 0, 1 0, 1 1, 0 1, 0 0)
- And sending query "12345"
- Then exactly 0 results are returned
- When sending query "54321"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | R | 1
-
-
- Scenario: Delete postcode from postcode boundaries without ref
- Given the place areas
- | osm_type | osm_id | class | type | postcode | geometry
- | R | 1 | boundary | postal_code | 12345 | (0 0, 1 0, 1 1, 0 1, 0 0)
- When importing
- And sending query "12345"
- Then results contain
- | ID | osm_type | osm_id
- | 0 | R | 1
- When updating place areas
- | osm_type | osm_id | class | type | geometry
- | R | 1 | boundary | postal_code | (0 0, 1 0, 1 1, 0 1, 0 0)
- Then table placex has no entry for R1
-
+++ /dev/null
-@DB
-Feature: Update of search terms
- Tests that search_name table is filled correctly
-
- Scenario: POI-inherited postcode remains when way type is changed
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | housenumber | postcode | street | geometry
- | 1 | place | house | 1 | 12345 | North St |:p-S1
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | North St | :w-north
- When importing
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
- When updating place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | unclassified | North St | :w-north
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
-
- Scenario: POI-inherited postcode remains when way name is changed
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | housenumber | postcode | street | geometry
- | 1 | place | house | 1 | 12345 | North St |:p-S1
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | North St | :w-north
- When importing
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
- When updating place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | unclassified | South St | :w-north
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
-
- Scenario: POI-inherited postcode remains when way geometry is changed
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | housenumber | postcode | street | geometry
- | 1 | place | house | 1 | 12345 | North St |:p-S1
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | North St | :w-north
- When importing
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
- When updating place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | unclassified | South St | :w-south
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
-
- Scenario: POI-inherited postcode is added when POI postcode changes
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | housenumber | postcode | street | geometry
- | 1 | place | house | 1 | 12345 | North St |:p-S1
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | North St | :w-north
- When importing
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
- When updating place nodes
- | osm_id | class | type | housenumber | postcode | street | geometry
- | 1 | place | house | 1 | 54321 | North St |:p-S1
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 54321
-
- Scenario: POI-inherited postcode remains when POI geometry changes
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | housenumber | postcode | street | geometry
- | 1 | place | house | 1 | 12345 | North St |:p-S1
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | North St | :w-north
- When importing
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
- When updating place nodes
- | osm_id | class | type | housenumber | postcode | street | geometry
- | 1 | place | house | 1 | 12345 | North St |:p-S2
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
-
-
- Scenario: POI-inherited postcode remains when another POI is deleted
- Given the scene roads-with-pois
- And the place nodes
- | osm_id | class | type | housenumber | postcode | street | geometry
- | 1 | place | house | 1 | 12345 | North St |:p-S1
- | 2 | place | house | 2 | | North St |:p-S2
- And the place ways
- | osm_id | class | type | name | geometry
- | 1 | highway | residential | North St | :w-north
- When importing
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
- When marking for delete N2
- Then search_name table contains
- | place_id | nameaddress_vector
- | W1 | 12345
+++ /dev/null
-@DB
-Feature: Update of simple objects
- Testing simple stuff
-
- Scenario: Do delete small boundary features
- Given the place areas
- | osm_type | osm_id | class | type | admin_level | geometry
- | R | 1 | boundary | administrative | 3 | (0 0, 1 0, 1 1, 0 1, 0 0)
- When importing
- Then table placex contains
- | object | rank_search
- | R1 | 6
- When marking for delete R1
- Then table placex has no entry for R1
-
- Scenario: Do not delete large boundary features
- Given the place areas
- | osm_type | osm_id | class | type | admin_level | geometry
- | R | 1 | boundary | administrative | 3 | (0 0, 2 0, 2 2.1, 0 2, 0 0)
- When importing
- Then table placex contains
- | object | rank_search
- | R1 | 6
- When marking for delete R1
- Then table placex contains
- | object | rank_search
- | R1 | 6
-
- Scenario: Do delete large features of low rank
- Given the named place areas
- | osm_type | osm_id | class | type | geometry
- | W | 1 | place | house | (0 0, 2 0, 2 2.1, 0 2, 0 0)
- | R | 1 | boundary | national_park | (0 0, 2 0, 2 2.1, 0 2, 0 0)
- When importing
- Then table placex contains
- | object | rank_address
- | R1 | 0
- | W1 | 30
- When marking for delete R1,W1
- Then table placex has no entry for W1
- Then table placex has no entry for R1
-
-
- Scenario: type mutation
- Given the place nodes
- | osm_id | class | type | geometry
- | 3 | shop | toys | 1 -1
- When importing
- Then table placex contains
- | object | class | type
- | N3 | shop | toys
- When updating place nodes
- | osm_id | class | type | geometry
- | 3 | shop | grocery | 1 -1
- Then table placex contains
- | object | class | type
- | N3 | shop | grocery
-
-
- Scenario: remove postcode place when house number is added
- Given the place nodes
- | osm_id | class | type | postcode | geometry
- | 3 | place | postcode | 12345 | 1 -1
- When importing
- Then table placex contains
- | object | class | type
- | N3 | place | postcode
- When updating place nodes
- | osm_id | class | type | postcode | housenumber | geometry
- | 3 | place | house | 12345 | 13 | 1 -1
- Then table placex contains
- | object | class | type
- | N3 | place | house
+++ /dev/null
-@DB
-Feature: Import of objects with broken geometries by osm2pgsql
-
- @Fail
- Scenario: Import way with double nodes
- Given the osm nodes:
- | id | geometry
- | 100 | 0 0
- | 101 | 0 0.1
- | 102 | 0.1 0.2
- And the osm ways:
- | id | tags | nodes
- | 1 | 'highway' : 'primary' | 100 101 101 102
- When loading osm data
- Then table place contains
- | object | class | type | geometry
- | W1 | highway | primary | (0 0, 0 0.1, 0.1 0.2)
-
- Scenario: Import of ballon areas
- Given the osm nodes:
- | id | geometry
- | 1 | 0 0
- | 2 | 0 0.0001
- | 3 | 0.00001 0.0001
- | 4 | 0.00001 0
- | 5 | -0.00001 0
- And the osm ways:
- | id | tags | nodes
- | 1 | 'highway' : 'unclassified' | 1 2 3 4 1 5
- | 2 | 'highway' : 'unclassified' | 1 2 3 4 1
- | 3 | 'highway' : 'unclassified' | 1 2 3 4 3
- When loading osm data
- Then table place contains
- | object | geometrytype
- | W1 | ST_LineString
- | W2 | ST_Polygon
- | W3 | ST_LineString
+++ /dev/null
-@DB
-Feature: Import of relations by osm2pgsql
- Testing specific relation problems related to members.
-
- Scenario: Don't import empty waterways
- Given the osm nodes:
- | id | tags
- | 1 | 'amenity' : 'prison', 'name' : 'foo'
- And the osm relations:
- | id | tags | members
- | 1 | 'type' : 'waterway', 'waterway' : 'river', 'name' : 'XZ' | N1
- When loading osm data
- Then table place has no entry for R1
+++ /dev/null
-@DB
-Feature: Import of simple objects by osm2pgsql
- Testing basic tagging in osm2pgsql imports.
-
- Scenario: Import simple objects
- Given the osm nodes:
- | id | tags
- | 1 | 'amenity' : 'prison', 'name' : 'foo'
- Given the osm nodes:
- | id | geometry
- | 100 | 0 0
- | 101 | 0 0.1
- | 102 | 0.1 0.2
- | 200 | 0 0
- | 201 | 0 1
- | 202 | 1 1
- | 203 | 1 0
- And the osm ways:
- | id | tags | nodes
- | 1 | 'shop' : 'toys', 'name' : 'tata' | 100 101 102
- | 2 | 'ref' : '45' | 200 201 202 203 200
- And the osm relations:
- | id | tags | members
- | 1 | 'type' : 'multipolygon', 'tourism' : 'hotel', 'name' : 'XZ' | N1,W2
- When loading osm data
- Then table place contains
- | object | class | type | name
- | N1 | amenity | prison | 'name' : 'foo'
- | W1 | shop | toys | 'name' : 'tata'
- | R1 | tourism | hotel | 'name' : 'XZ'
-
- Scenario: Import object with two main tags
- Given the osm nodes:
- | id | tags
- | 1 | 'tourism' : 'hotel', 'amenity' : 'restaurant', 'name' : 'foo'
- When loading osm data
- Then table place contains
- | object | class | type | name
- | N1:tourism | tourism | hotel | 'name' : 'foo'
- | N1:amenity | amenity | restaurant | 'name' : 'foo'
-
- Scenario: Import stand-alone house number with postcode
- Given the osm nodes:
- | id | tags
- | 1 | 'addr:housenumber' : '4', 'addr:postcode' : '3345'
- When loading osm data
- Then table place contains
- | object | class | type
- | N1 | place | house
-
- Scenario: Landuses are only imported when named
- Given the osm nodes:
- | id | geometry
- | 100 | 0 0
- | 101 | 0 0.1
- | 102 | 0.1 0.1
- | 200 | 0 0
- | 202 | 1 1
- | 203 | 1 0
- And the osm ways:
- | id | tags | nodes
- | 1 | 'landuse' : 'residential', 'name' : 'rainbow' | 100 101 102 100
- | 2 | 'landuse' : 'residential' | 200 202 203 200
- When loading osm data
- Then table place contains
- | object | class | type
- | W1 | landuse | residential
- And table place has no entry for W2
+++ /dev/null
-@DB
-Feature: Tag evaluation
- Tests if tags are correctly imported into the place table
-
- Scenario Outline: Name tags
- Given the osm nodes:
- | id | tags
- | 1 | 'highway' : 'yes', '<nametag>' : 'Foo'
- When loading osm data
- Then table place contains
- | object | name
- | N1 | '<nametag>' : 'Foo'
-
- Examples:
- | nametag
- | ref
- | int_ref
- | nat_ref
- | reg_ref
- | loc_ref
- | old_ref
- | iata
- | icao
- | pcode:1
- | pcode:2
- | pcode:3
- | name
- | name:de
- | name:bt-BR
- | int_name
- | int_name:xxx
- | nat_name
- | nat_name:fr
- | reg_name
- | reg_name:1
- | loc_name
- | loc_name:DE
- | old_name
- | old_name:v1
- | alt_name
- | alt_name:dfe
- | alt_name_1
- | official_name
- | short_name
- | short_name:CH
- | addr:housename
- | brand
-
- Scenario Outline: operator only for shops and amenities
- Given the osm nodes:
- | id | tags
- | 1 | 'highway' : 'yes', 'operator' : 'Foo', 'name' : 'null'
- | 2 | 'shop' : 'grocery', 'operator' : 'Foo'
- | 3 | 'amenity' : 'hospital', 'operator' : 'Foo'
- | 4 | 'tourism' : 'hotel', 'operator' : 'Foo'
- When loading osm data
- Then table place contains
- | object | name
- | N1 | 'name' : 'null'
- | N2 | 'operator' : 'Foo'
- | N3 | 'operator' : 'Foo'
- | N4 | 'operator' : 'Foo'
-
- Scenario Outline: Ignored name tags
- Given the osm nodes:
- | id | tags
- | 1 | 'highway' : 'yes', '<nametag>' : 'Foo', 'name' : 'real'
- When loading osm data
- Then table place contains
- | object | name
- | N1 | 'name' : 'real'
-
- Examples:
- | nametag
- | name_de
- | Name
- | ref:de
- | ref_de
- | my:ref
- | br:name
- | name:prefix
- | name:source
-
- Scenario: Special character in name tag
- Given the osm nodes:
- | id | tags
- | 1 | 'highway' : 'yes', 'name: de' : 'Foo', 'name' : 'real1'
- | 2 | 'highway' : 'yes', 'name:
de' : 'Foo', 'name' : 'real2'
- | 3 | 'highway' : 'yes', 'name:	de' : 'Foo', 'name:\\' : 'real3'
- When loading osm data
- Then table place contains
- | object | name
- | N1 | 'name: de' : 'Foo', 'name' : 'real1'
- | N2 | 'name: de' : 'Foo', 'name' : 'real2'
- | N3 | 'name: de' : 'Foo', 'name:\\' : 'real3'
-
- Scenario Outline: Included places
- Given the osm nodes:
- | id | tags
- | 1 | '<key>' : '<value>', 'name' : 'real'
- When loading osm data
- Then table place contains
- | object | name
- | N1 | 'name' : 'real'
-
- Examples:
- | key | value
- | emergency | phone
- | tourism | information
- | historic | castle
- | military | barracks
- | natural | water
- | highway | residential
- | aerialway | station
- | aeroway | way
- | boundary | administrative
- | craft | butcher
- | leisure | playground
- | office | bookmaker
- | railway | rail
- | shop | bookshop
- | waterway | stream
- | landuse | cemetry
- | man_made | tower
- | mountain_pass | yes
-
- Scenario Outline: Bridges and Tunnels take special name tags
- Given the osm nodes:
- | id | tags
- | 1 | 'highway' : 'road', '<key>' : 'yes', 'name' : 'Rd', '<key>:name' : 'My'
- | 2 | 'highway' : 'road', '<key>' : 'yes', 'name' : 'Rd'
- When loading osm data
- Then table place contains
- | object | class | type | name
- | N1:highway | highway | road | 'name' : 'Rd'
- | N1:<key> | <key> | yes | 'name' : 'My'
- | N2:highway | highway | road | 'name' : 'Rd'
- And table place has no entry for N2:<key>
-
- Examples:
- | key
- | bridge
- | tunnel
-
- Scenario Outline: Excluded places
- Given the osm nodes:
- | id | tags
- | 1 | '<key>' : '<value>', 'name' : 'real'
- | 2 | 'highway' : 'motorway', 'name' : 'To Hell'
- When loading osm data
- Then table place has no entry for N1
-
- Examples:
- | key | value
- | emergency | yes
- | emergency | no
- | tourism | yes
- | tourism | no
- | historic | yes
- | historic | no
- | military | yes
- | military | no
- | natural | yes
- | natural | no
- | highway | no
- | highway | turning_circle
- | highway | mini_roundabout
- | highway | noexit
- | highway | crossing
- | aerialway | no
- | aerialway | pylon
- | man_made | survey_point
- | man_made | cutline
- | aeroway | no
- | amenity | no
- | bridge | no
- | craft | no
- | leisure | no
- | office | no
- | railway | no
- | railway | level_crossing
- | shop | no
- | tunnel | no
- | waterway | riverbank
-
- Scenario: Some tags only are included when named
- Given the osm nodes:
- | id | tags
- | 1 | '<key>' : '<value>'
- | 2 | '<key>' : '<value>', 'name' : 'To Hell'
- | 3 | '<key>' : '<value>', 'ref' : '123'
- When loading osm data
- Then table place has no entry for N1
- And table place has no entry for N3
- And table place contains
- | object | class | type
- | N2 | <key> | <value>
-
- Examples:
- | key | value
- | landuse | residential
- | natural | meadow
- | highway | traffic_signals
- | highway | service
- | highway | cycleway
- | highway | path
- | highway | footway
- | highway | steps
- | highway | bridleway
- | highway | track
- | highway | byway
- | highway | motorway_link
- | highway | primary_link
- | highway | trunk_link
- | highway | secondary_link
- | highway | tertiary_link
- | railway | rail
- | boundary | administrative
- | waterway | stream
-
- Scenario: Footways are not included if they are sidewalks
- Given the osm nodes:
- | id | tags
- | 2 | 'highway' : 'footway', 'name' : 'To Hell', 'footway' : 'sidewalk'
- | 23 | 'highway' : 'footway', 'name' : 'x'
- When loading osm data
- Then table place has no entry for N2
-
- Scenario: named junctions are included if there is no other tag
- Given the osm nodes:
- | id | tags
- | 1 | 'junction' : 'yes'
- | 2 | 'highway' : 'secondary', 'junction' : 'roundabout', 'name' : 'To Hell'
- | 3 | 'junction' : 'yes', 'name' : 'Le Croix'
- When loading osm data
- Then table place has no entry for N1
- And table place has no entry for N2:junction
- And table place contains
- | object | class | type
- | N3 | junction | yes
-
- Scenario: Boundary with place tag
- Given the osm nodes:
- | id | geometry
- | 200 | 0 0
- | 201 | 0 1
- | 202 | 1 1
- | 203 | 1 0
- And the osm ways:
- | id | tags | nodes
- | 2 | 'boundary' : 'administrative', 'place' : 'city', 'name' : 'Foo' | 200 201 202 203 200
- | 4 | 'boundary' : 'administrative', 'place' : 'island','name' : 'Foo' | 200 201 202 203 200
- | 20 | 'place' : 'city', 'name' : 'ngng' | 200 201 202 203 200
- | 40 | 'place' : 'city', 'boundary' : 'statistical', 'name' : 'BB' | 200 201 202 203 200
- When loading osm data
- Then table place contains
- | object | class | extratags | type
- | W2 | boundary | 'place' : 'city' | administrative
- | W4:boundary | boundary | None | administrative
- | W4:place | place | None | island
- | W20 | place | None | city
- | W40:boundary | boundary | None | statistical
- | W40:place | place | None | city
- And table place has no entry for W2:place
-
- Scenario Outline: Tags that describe a house
- Given the osm nodes:
- | id | tags
- | 100 | '<key>' : '<value>'
- | 999 | 'amenity' : 'prison', '<key>' : '<value>'
- When loading osm data
- Then table place contains
- | object | class | type
- | N100 | place | house
- | N999 | amenity | prison
- And table place has no entry for N100:<key>
- And table place has no entry for N999:<key>
- And table place has no entry for N999:place
-
- Examples:
- | key | value
- | addr:housename | My Mansion
- | addr:housenumber | 456
- | addr:conscriptionnumber | 4
- | addr:streetnumber | 4568765
-
- Scenario: Only named with no other interesting tag
- Given the osm nodes:
- | id | tags
- | 1 | 'landuse' : 'meadow'
- | 2 | 'landuse' : 'residential', 'name' : 'important'
- | 3 | 'landuse' : 'residential', 'name' : 'important', 'place' : 'hamlet'
- When loading osm data
- Then table place contains
- | object | class | type
- | N2 | landuse | residential
- | N3 | place | hamlet
- And table place has no entry for N1
- And table place has no entry for N3:landuse
-
- Scenario Outline: Import of postal codes
- Given the osm nodes:
- | id | tags
- | 10 | 'highway' : 'secondary', '<key>' : '<value>'
- | 11 | '<key>' : '<value>'
- When loading osm data
- Then table place contains
- | object | class | type | postcode
- | N10 | highway | secondary | <value>
- | N11 | place | postcode | <value>
- And table place has no entry for N10:place
-
- Examples:
- | key | value
- | postal_code | 45736
- | postcode | xxx
- | addr:postcode | 564
- | tiger:zip_left | 00011
- | tiger:zip_right | 09123
-
- Scenario: Import of street and place
- Given the osm nodes:
- | id | tags
- | 10 | 'amenity' : 'hospital', 'addr:street' : 'Foo St'
- | 20 | 'amenity' : 'hospital', 'addr:place' : 'Foo Town'
- When loading osm data
- Then table place contains
- | object | class | type | street | addr_place
- | N10 | amenity | hospital | Foo St | None
- | N20 | amenity | hospital | None | Foo Town
-
-
- Scenario Outline: Import of country
- Given the osm nodes:
- | id | tags
- | 10 | 'place' : 'village', '<key>' : '<value>'
- When loading osm data
- Then table place contains
- | object | class | type | country_code
- | N10 | place | village | <value>
-
- Examples:
- | key | value
- | country_code | us
- | ISO3166-1 | XX
- | is_in:country_code | __
- | addr:country | ..
- | addr:country_code | cv
-
- Scenario Outline: Ignore country codes with wrong length
- Given the osm nodes:
- | id | tags
- | 10 | 'place' : 'village', 'country_code' : '<value>'
- When loading osm data
- Then table place contains
- | object | class | type | country_code
- | N10 | place | village | None
-
- Examples:
- | value
- | X
- | x
- | ger
- | dkeufr
- | d e
-
- Scenario: Import of house numbers
- Given the osm nodes:
- | id | tags
- | 10 | 'building' : 'yes', 'addr:housenumber' : '4b'
- | 11 | 'building' : 'yes', 'addr:conscriptionnumber' : '003'
- | 12 | 'building' : 'yes', 'addr:streetnumber' : '2345'
- | 13 | 'building' : 'yes', 'addr:conscriptionnumber' : '3', 'addr:streetnumber' : '111'
- When loading osm data
- Then table place contains
- | object | class | type | housenumber
- | N10 | building | yes | 4b
- | N11 | building | yes | 003
- | N12 | building | yes | 2345
- | N13 | building | yes | 3/111
-
- Scenario: Import of address interpolations
- Given the osm nodes:
- | id | tags
- | 10 | 'addr:interpolation' : 'odd'
- | 11 | 'addr:housenumber' : '10', 'addr:interpolation' : 'odd'
- | 12 | 'addr:interpolation' : 'odd', 'addr:housenumber' : '23'
- When loading osm data
- Then table place contains
- | object | class | type | housenumber
- | N10 | place | houses | odd
- | N11 | place | houses | odd
- | N12 | place | houses | odd
-
- Scenario: Shorten tiger:county tags
- Given the osm nodes:
- | id | tags
- | 10 | 'place' : 'village', 'tiger:county' : 'Feebourgh, AL'
- | 11 | 'place' : 'village', 'addr:state' : 'Alabama', 'tiger:county' : 'Feebourgh, AL'
- | 12 | 'place' : 'village', 'tiger:county' : 'Feebourgh'
- When loading osm data
- Then table place contains
- | object | class | type | isin
- | N10 | place | village | Feebourgh county
- | N11 | place | village | Feebourgh county,Alabama
- | N12 | place | village | Feebourgh county
-
- Scenario Outline: Import of address tags
- Given the osm nodes:
- | id | tags
- | 10 | 'place' : 'village', '<key>' : '<value>'
- When loading osm data
- Then table place contains
- | object | class | type | isin
- | N10 | place | village | <value>
-
- Examples:
- | key | value
- | is_in | Stockholm, Sweden
- | is_in:country | Xanadu
- | addr:suburb | hinein
- | addr:county | le havre
- | addr:city | Sydney
- | addr:state | Jura
-
- Scenario: Import of admin level
- Given the osm nodes:
- | id | tags
- | 10 | 'amenity' : 'hospital', 'admin_level' : '3'
- | 11 | 'amenity' : 'hospital', 'admin_level' : 'b'
- | 12 | 'amenity' : 'hospital'
- | 13 | 'amenity' : 'hospital', 'admin_level' : '3.0'
- When loading osm data
- Then table place contains
- | object | class | type | admin_level
- | N10 | amenity | hospital | 3
- | N11 | amenity | hospital | 100
- | N12 | amenity | hospital | 100
- | N13 | amenity | hospital | 3
-
- Scenario: Import of extra tags
- Given the osm nodes:
- | id | tags
- | 10 | 'tourism' : 'hotel', '<key>' : 'foo'
- When loading osm data
- Then table place contains
- | object | class | type | extratags
- | N10 | tourism | hotel | '<key>' : 'foo'
-
- Examples:
- | key
- | tracktype
- | traffic_calming
- | service
- | cuisine
- | capital
- | dispensing
- | religion
- | denomination
- | sport
- | internet_access
- | lanes
- | surface
- | smoothness
- | width
- | est_width
- | incline
- | opening_hours
- | collection_times
- | service_times
- | disused
- | wheelchair
- | sac_scale
- | trail_visibility
- | mtb:scale
- | mtb:description
- | wood
- | drive_in
- | access
- | vehicle
- | bicyle
- | foot
- | goods
- | hgv
- | motor_vehicle
- | motor_car
- | access:foot
- | contact:phone
- | drink:mate
- | oneway
- | date_on
- | date_off
- | day_on
- | day_off
- | hour_on
- | hour_off
- | maxweight
- | maxheight
- | maxspeed
- | disused
- | toll
- | charge
- | population
- | description
- | image
- | attribution
- | fax
- | email
- | url
- | website
- | phone
- | real_ale
- | smoking
- | food
- | camera
- | brewery
- | locality
- | wikipedia
- | wikipedia:de
- | wikidata
- | name:prefix
- | name:botanical
- | name:etymology:wikidata
-
- Scenario: buildings
- Given the osm nodes:
- | id | tags
- | 10 | 'tourism' : 'hotel', 'building' : 'yes'
- | 11 | 'building' : 'house'
- | 12 | 'building' : 'shed', 'addr:housenumber' : '1'
- | 13 | 'building' : 'yes', 'name' : 'Das Haus'
- | 14 | 'building' : 'yes', 'addr:postcode' : '12345'
- When loading osm data
- Then table place contains
- | object | class | type
- | N10 | tourism | hotel
- | N12 | building| yes
- | N13 | building| yes
- | N14 | building| yes
- And table place has no entry for N10:building
- And table place has no entry for N11
-
- Scenario: complete node entry
- Given the osm nodes:
- | id | tags
- | 290393920 | 'addr:city':'Perpignan','addr:country':'FR','addr:housenumber':'43\\','addr:postcode':'66000','addr:street':'Rue Pierre Constant d`Ivry','source':'cadastre-dgi-fr source : Direction Générale des Impôts - Cadastre ; mise à jour :2008'
- When loading osm data
- Then table place contains
- | object | class | type | housenumber
- | N290393920 | place | house| 43\
+++ /dev/null
-@DB
-Feature: Update of relations by osm2pgsql
- Testing relation update by osm2pgsql.
-
-Scenario: Remove all members of a relation
- Given the osm nodes:
- | id | tags
- | 1 | 'amenity' : 'prison', 'name' : 'foo'
- Given the osm nodes:
- | id | geometry
- | 200 | 0 0
- | 201 | 0 0.0001
- | 202 | 0.0001 0.0001
- | 203 | 0.0001 0
- Given the osm ways:
- | id | tags | nodes
- | 2 | 'ref' : '45' | 200 201 202 203 200
- Given the osm relations:
- | id | tags | members
- | 1 | 'type' : 'multipolygon', 'tourism' : 'hotel', 'name' : 'XZ' | W2
- When loading osm data
- Then table place contains
- | object | class | type | name
- | R1 | tourism | hotel | 'name' : 'XZ'
- Given the osm relations:
- | action | id | tags | members
- | M | 1 | 'type' : 'multipolygon', 'tourism' : 'hotel', 'name' : 'XZ' | N1
- When updating osm data
- Then table place has no entry for R1
-
-
-Scenario: Change type of a relation
- Given the osm nodes:
- | id | geometry
- | 200 | 0 0
- | 201 | 0 0.0001
- | 202 | 0.0001 0.0001
- | 203 | 0.0001 0
- Given the osm ways:
- | id | tags | nodes
- | 2 | 'ref' : '45' | 200 201 202 203 200
- Given the osm relations:
- | id | tags | members
- | 1 | 'type' : 'multipolygon', 'tourism' : 'hotel', 'name' : 'XZ' | W2
- When loading osm data
- Then table place contains
- | object | class | type | name
- | R1 | tourism | hotel | 'name' : 'XZ'
- Given the osm relations:
- | action | id | tags | members
- | M | 1 | 'type' : 'multipolygon', 'amenity' : 'prison', 'name' : 'XZ' | W2
- When updating osm data
- Then table place has no entry for R1:tourism
- And table place contains
- | object | class | type | name
- | R1 | amenity | prison | 'name' : 'XZ'
-
-Scenario: Change name of a relation
- Given the osm nodes:
- | id | geometry
- | 200 | 0 0
- | 201 | 0 0.0001
- | 202 | 0.0001 0.0001
- | 203 | 0.0001 0
- Given the osm ways:
- | id | tags | nodes
- | 2 | 'ref' : '45' | 200 201 202 203 200
- Given the osm relations:
- | id | tags | members
- | 1 | 'type' : 'multipolygon', 'tourism' : 'hotel', 'name' : 'AB' | W2
- When loading osm data
- Then table place contains
- | object | class | type | name
- | R1 | tourism | hotel | 'name' : 'AB'
- Given the osm relations:
- | action | id | tags | members
- | M | 1 | 'type' : 'multipolygon', 'tourism' : 'hotel', 'name' : 'XZ' | W2
- When updating osm data
- Then table place contains
- | object | class | type | name
- | R1 | tourism | hotel | 'name' : 'XZ'
-
-
-Scenario: Change type of a relation into something unknown
- Given the osm nodes:
- | id | geometry
- | 200 | 0 0
- | 201 | 0 0.0001
- | 202 | 0.0001 0.0001
- | 203 | 0.0001 0
- Given the osm ways:
- | id | tags | nodes
- | 2 | 'ref' : '45' | 200 201 202 203 200
- Given the osm relations:
- | id | tags | members
- | 1 | 'type' : 'multipolygon', 'tourism' : 'hotel', 'name' : 'XZ' | W2
- When loading osm data
- Then table place contains
- | object | class | type | name
- | R1 | tourism | hotel | 'name' : 'XZ'
- Given the osm relations:
- | action | id | tags | members
- | M | 1 | 'type' : 'multipolygon', 'amenities' : 'prison', 'name' : 'XZ' | W2
- When updating osm data
- Then table place has no entry for R1
-
-Scenario: Type tag is removed
- Given the osm nodes:
- | id | geometry
- | 200 | 0 0
- | 201 | 0 0.0001
- | 202 | 0.0001 0.0001
- | 203 | 0.0001 0
- Given the osm ways:
- | id | tags | nodes
- | 2 | 'ref' : '45' | 200 201 202 203 200
- Given the osm relations:
- | id | tags | members
- | 1 | 'type' : 'multipolygon', 'tourism' : 'hotel', 'name' : 'XZ' | W2
- When loading osm data
- Then table place contains
- | object | class | type | name
- | R1 | tourism | hotel | 'name' : 'XZ'
- Given the osm relations:
- | action | id | tags | members
- | M | 1 | 'tourism' : 'hotel', 'name' : 'XZ' | W2
- When updating osm data
- Then table place has no entry for R1
-
-Scenario: Type tag is renamed to something unknown
- Given the osm nodes:
- | id | geometry
- | 200 | 0 0
- | 201 | 0 0.0001
- | 202 | 0.0001 0.0001
- | 203 | 0.0001 0
- Given the osm ways:
- | id | tags | nodes
- | 2 | 'ref' : '45' | 200 201 202 203 200
- Given the osm relations:
- | id | tags | members
- | 1 | 'type' : 'multipolygon', 'tourism' : 'hotel', 'name' : 'XZ' | W2
- When loading osm data
- Then table place contains
- | object | class | type | name
- | R1 | tourism | hotel | 'name' : 'XZ'
- Given the osm relations:
- | action | id | tags | members
- | M | 1 | 'type' : 'multipolygonn', 'tourism' : 'hotel', 'name' : 'XZ' | W2
- When updating osm data
- Then table place has no entry for R1
-
+++ /dev/null
-@DB
-Feature: Update of simple objects by osm2pgsql
- Testing basic update functions of osm2pgsql.
-
- Scenario: Import object with two main tags
- Given the osm nodes:
- | id | tags
- | 1 | 'tourism' : 'hotel', 'amenity' : 'restaurant', 'name' : 'foo'
- When loading osm data
- Then table place contains
- | object | class | type | name
- | N1:tourism | tourism | hotel | 'name' : 'foo'
- | N1:amenity | amenity | restaurant | 'name' : 'foo'
- Given the osm nodes:
- | action | id | tags
- | M | 1 | 'tourism' : 'hotel', 'name' : 'foo'
- When updating osm data
- Then table place has no entry for N1:amenity
- And table place contains
- | object | class | type | name
- | N1:tourism | tourism | hotel | 'name' : 'foo'
-
+++ /dev/null
-""" Steps for checking the results of queries.
-"""
-
-from nose.tools import *
-from lettuce import *
-from tidylib import tidy_document
-from collections import OrderedDict
-import json
-import logging
-import re
-from xml.dom.minidom import parseString
-
-logger = logging.getLogger(__name__)
-
-def _parse_xml():
- """ Puts the DOM structure into more convenient python
- with a similar structure as the json document, so
- that the same the semantics can be used. It does not
- check if the content is valid (or at least not more than
- necessary to transform it into a dict structure).
- """
- page = parseString(world.page).documentElement
-
- # header info
- world.result_header = OrderedDict(page.attributes.items())
- logger.debug('Result header: %r' % (world.result_header))
- world.results = []
-
- # results
- if page.nodeName == 'searchresults' or page.nodeName == 'lookupresults':
- for node in page.childNodes:
- if node.nodeName != "#text":
- assert_equals(node.nodeName, 'place', msg="Unexpected element '%s'" % node.nodeName)
- newresult = OrderedDict(node.attributes.items())
- assert_not_in('address', newresult)
- assert_not_in('geokml', newresult)
- assert_not_in('extratags', newresult)
- assert_not_in('namedetails', newresult)
- address = OrderedDict()
- for sub in node.childNodes:
- if sub.nodeName == 'geokml':
- newresult['geokml'] = sub.childNodes[0].toxml()
- elif sub.nodeName == 'extratags':
- newresult['extratags'] = {}
- for tag in sub.childNodes:
- assert_equals(tag.nodeName, 'tag')
- attrs = dict(tag.attributes.items())
- assert_in('key', attrs)
- assert_in('value', attrs)
- newresult['extratags'][attrs['key']] = attrs['value']
- elif sub.nodeName == 'namedetails':
- newresult['namedetails'] = {}
- for tag in sub.childNodes:
- assert_equals(tag.nodeName, 'name')
- attrs = dict(tag.attributes.items())
- assert_in('desc', attrs)
- newresult['namedetails'][attrs['desc']] = tag.firstChild.nodeValue.strip()
-
- elif sub.nodeName == '#text':
- pass
- else:
- address[sub.nodeName] = sub.firstChild.nodeValue.strip()
- if address:
- newresult['address'] = address
- world.results.append(newresult)
- elif page.nodeName == 'reversegeocode':
- haserror = False
- address = {}
- for node in page.childNodes:
- if node.nodeName == 'result':
- assert_equals(len(world.results), 0)
- assert (not haserror)
- world.results.append(OrderedDict(node.attributes.items()))
- assert_not_in('display_name', world.results[0])
- assert_not_in('address', world.results[0])
- world.results[0]['display_name'] = node.firstChild.nodeValue.strip()
- elif node.nodeName == 'error':
- assert_equals(len(world.results), 0)
- haserror = True
- elif node.nodeName == 'addressparts':
- assert (not haserror)
- address = OrderedDict()
- for sub in node.childNodes:
- address[sub.nodeName] = sub.firstChild.nodeValue.strip()
- world.results[0]['address'] = address
- elif node.nodeName == 'extratags':
- world.results[0]['extratags'] = {}
- for tag in node.childNodes:
- assert_equals(tag.nodeName, 'tag')
- attrs = dict(tag.attributes.items())
- assert_in('key', attrs)
- assert_in('value', attrs)
- world.results[0]['extratags'][attrs['key']] = attrs['value']
- elif node.nodeName == 'namedetails':
- world.results[0]['namedetails'] = {}
- for tag in node.childNodes:
- assert_equals(tag.nodeName, 'name')
- attrs = dict(tag.attributes.items())
- assert_in('desc', attrs)
- world.results[0]['namedetails'][attrs['desc']] = tag.firstChild.nodeValue.strip()
- elif node.nodeName == "geokml":
- world.results[0]['geokml'] = node
- elif node.nodeName == "#text":
- pass
- else:
- assert False, "Unknown content '%s' in XML" % node.nodeName
- else:
- assert False, "Unknown document node name %s in XML" % page.nodeName
-
- logger.debug("The following was parsed out of XML:")
- logger.debug(world.results)
-
-@step(u'a HTTP (\d+) is returned')
-def api_result_http_error(step, error):
- assert_equals(world.returncode, int(error))
-
-@step(u'the result is valid( \w+)?')
-def api_result_is_valid(step, fmt):
- assert_equals(world.returncode, 200)
-
- if world.response_format == 'html':
- document, errors = tidy_document(world.page,
- options={'char-encoding' : 'utf8'})
- # assert(len(errors) == 0), "Errors found in HTML document:\n%s" % errors
- world.results = document
- elif world.response_format == 'xml':
- _parse_xml()
- elif world.response_format == 'json':
- world.results = json.JSONDecoder(object_pairs_hook=OrderedDict).decode(world.page)
- if world.request_type == 'reverse':
- world.results = (world.results,)
- else:
- assert False, "Unknown page format: %s" % (world.response_format)
-
- if fmt:
- assert_equals (fmt.strip(), world.response_format)
-
-
-def compare(operator, op1, op2):
- if operator == 'less than':
- return op1 < op2
- elif operator == 'more than':
- return op1 > op2
- elif operator == 'exactly':
- return op1 == op2
- elif operator == 'at least':
- return op1 >= op2
- elif operator == 'at most':
- return op1 <= op2
- else:
- raise Exception("unknown operator '%s'" % operator)
-
-@step(u'(less than|more than|exactly|at least|at most) (\d+) results? (?:is|are) returned')
-def validate_result_number(step, operator, number):
- step.given('the result is valid')
- numres = len(world.results)
- assert compare(operator, numres, int(number)), \
- "Bad number of results: expected %s %s, got %d." % (operator, number, numres)
-
-@step(u'result (\d+) has( not)? attributes (\S+)')
-def search_check_for_result_attribute(step, num, invalid, attrs):
- num = int(num)
- step.given('at least %d results are returned' % (num + 1))
- res = world.results[num]
- for attr in attrs.split(','):
- if invalid:
- assert_not_in(attr.strip(), res)
- else:
- assert_in(attr.strip(),res)
-
-@step(u'there is a json wrapper "([^"]*)"')
-def api_result_check_json_wrapper(step, wrapper):
- step.given('the result is valid json')
- assert_equals(world.json_callback, wrapper)
-
-@step(u'result header contains')
-def api_result_header_contains(step):
- step.given('the result is valid')
- for line in step.hashes:
- assert_in(line['attr'], world.result_header)
- m = re.match("%s$" % (line['value'],), world.result_header[line['attr']])
-
-@step(u'result header has no attribute (.*)')
-def api_result_header_contains_not(step, attr):
- step.given('the result is valid')
- assert_not_in(attr, world.result_header)
-
-@step(u'results contain$')
-def api_result_contains(step):
- step.given('at least 1 result is returned')
- for line in step.hashes:
- if 'ID' in line:
- reslist = (world.results[int(line['ID'])],)
- else:
- reslist = world.results
- for k,v in line.iteritems():
- if k == 'latlon':
- for curres in reslist:
- world.match_geometry((float(curres['lat']), float(curres['lon'])), v)
- elif k != 'ID':
- for curres in reslist:
- assert_in(k, curres)
- if v[0] in '<>=':
- # mathematical operation
- evalexp = '%s %s' % (curres[k], v)
- res = eval(evalexp)
- logger.debug('Evaluating: %s = %s' % (res, evalexp))
- assert_true(res, "Evaluation failed: %s" % (evalexp, ))
- else:
- # regex match
- m = re.match("%s$" % (v,), curres[k])
- assert_is_not_none(m, msg="field %s does not match: %s$ != %s." % (k, v, curres[k]))
-
-@step(u'results contain valid boundingboxes$')
-def api_result_address_contains(step):
- step.given('the result is valid')
- for curres in world.results:
- bb = curres['boundingbox']
- if world.response_format == 'json':
- bb = ','.join(bb)
- m = re.match('^(-?\d+\.\d+),(-?\d+\.\d+),(-?\d+\.\d+),(-?\d+\.\d+)$', bb)
- assert_is_not_none(m, msg="invalid boundingbox: %s." % (curres['boundingbox']))
-
-@step(u'result addresses contain$')
-def api_result_address_contains(step):
- step.given('the result is valid')
- for line in step.hashes:
- if 'ID' in line:
- reslist = (world.results[int(line['ID'])],)
- else:
- reslist = world.results
- for k,v in line.iteritems():
- if k != 'ID':
- for res in reslist:
- curres = res['address']
- assert_in(k, curres)
- m = re.match("%s$" % (v,), curres[k])
- assert_is_not_none(m, msg="field %s does not match: %s$ != %s." % (k, v, curres[k]))
-
-
-@step(u'address of result (\d+) contains')
-def api_result_address_exact(step, resid):
- resid = int(resid)
- step.given('at least %d results are returned' % (resid + 1))
- addr = world.results[resid]['address']
- for line in step.hashes:
- assert_in(line['type'], addr)
- m = re.match("%s$" % line['value'], addr[line['type']])
- assert_is_not_none(m, msg="field %s does not match: %s$ != %s." % (
- line['type'], line['value'], addr[line['type']]))
- #assert_equals(line['value'], addr[line['type']])
-
-@step(u'address of result (\d+) does not contain (.*)')
-def api_result_address_details_missing(step, resid, types):
- resid = int(resid)
- step.given('at least %d results are returned' % (resid + 1))
- addr = world.results[resid]['address']
- for t in types.split(','):
- assert_not_in(t.strip(), addr)
-
-
-@step(u'address of result (\d+) is')
-def api_result_address_exact(step, resid):
- resid = int(resid)
- step.given('at least %d results are returned' % (resid + 1))
- result = world.results[resid]
- linenr = 0
- assert_equals(len(step.hashes), len(result['address']))
- for k,v in result['address'].iteritems():
- assert_equals(step.hashes[linenr]['type'], k)
- assert_equals(step.hashes[linenr]['value'], v)
- linenr += 1
-
-
-@step('there are( no)? duplicates')
-def api_result_check_for_duplicates(step, nodups=None):
- step.given('at least 1 result is returned')
- resarr = []
- for res in world.results:
- resarr.append((res['osm_type'], res['class'],
- res['type'], res['display_name']))
-
- if nodups is None:
- assert len(resarr) > len(set(resarr))
- else:
- assert_equal(len(resarr), len(set(resarr)))
+++ /dev/null
-""" Steps for setting up and sending API requests.
-"""
-
-from nose.tools import *
-from lettuce import *
-import urllib
-import urllib2
-import logging
-
-logger = logging.getLogger(__name__)
-
-def api_call(requesttype):
- world.request_type = requesttype
- world.json_callback = None
- data = urllib.urlencode(world.params)
- url = "%s/%s?%s" % (world.config.base_url, requesttype, data)
- req = urllib2.Request(url=url, headers=world.header)
- try:
- fd = urllib2.urlopen(req)
- world.page = fd.read()
- world.returncode = 200
- except urllib2.HTTPError, ex:
- world.returncode = ex.code
- world.page = None
- return
-
- pageinfo = fd.info()
- assert_equal('utf-8', pageinfo.getparam('charset').lower())
- pagetype = pageinfo.gettype()
-
- fmt = world.params.get('format')
- if fmt == 'html':
- assert_equals('text/html', pagetype)
- world.response_format = fmt
- elif fmt == 'xml':
- assert_equals('text/xml', pagetype)
- world.response_format = fmt
- elif fmt in ('json', 'jsonv2'):
- if 'json_callback' in world.params:
- world.json_callback = world.params['json_callback'].encode('utf8')
- assert world.page.startswith(world.json_callback + '(')
- assert world.page.endswith(')')
- world.page = world.page[(len(world.json_callback)+1):-1]
- assert_equals('application/javascript', pagetype)
- else:
- assert_equals('application/json', pagetype)
- world.response_format = 'json'
- else:
- if requesttype == 'reverse':
- assert_equals('text/xml', pagetype)
- world.response_format = 'xml'
- else:
- assert_equals('text/html', pagetype)
- world.response_format = 'html'
- logger.debug("Page received (%s):" % world.response_format)
- logger.debug(world.page)
-
- api_setup_prepare_params(None)
-
-@before.each_scenario
-def api_setup_prepare_params(scenario):
- world.results = []
- world.params = {}
- world.header = {}
-
-@step(u'the request parameters$')
-def api_setup_parameters(step):
- """Define the parameters of the request as a hash.
- Resets parameter list.
- """
- world.params = step.hashes[0]
-
-@step(u'the HTTP header$')
-def api_setup_parameters(step):
- """Define additional HTTP header parameters as a hash.
- Resets parameter list.
- """
- world.header = step.hashes[0]
-
-
-@step(u'sending( \w+)? search query "([^"]*)"( with address)?')
-def api_setup_search(step, fmt, query, doaddr):
- world.params['q'] = query.encode('utf8')
- if doaddr:
- world.params['addressdetails'] = 1
- if fmt:
- world.params['format'] = fmt.strip()
- api_call('search')
-
-@step(u'sending( \w+)? structured query( with address)?$')
-def api_setup_structured_search(step, fmt, doaddr):
- world.params.update(step.hashes[0])
- if doaddr:
- world.params['addressdetails'] = 1
- if fmt:
- world.params['format'] = fmt.strip()
- api_call('search')
-
-@step(u'looking up (\w+ )?coordinates ([-\d.]+),([-\d.]+)')
-def api_setup_reverse(step, fmt, lat, lon):
- world.params['lat'] = lat
- world.params['lon'] = lon
- if fmt and fmt.strip():
- world.params['format'] = fmt.strip()
- api_call('reverse')
-
-@step(u'looking up place ([NRW]?\d+)')
-def api_setup_details_reverse(step, obj):
- if obj[0] in ('N', 'R', 'W'):
- # an osm id
- world.params['osm_type'] = obj[0]
- world.params['osm_id'] = obj[1:]
- else:
- world.params['place_id'] = obj
- api_call('reverse')
-
-@step(u'looking up details for ([NRW]?\d+)')
-def api_setup_details(step, obj):
- if obj[0] in ('N', 'R', 'W'):
- # an osm id
- world.params['osmtype'] = obj[0]
- world.params['osmid'] = obj[1:]
- else:
- world.params['place_id'] = obj
- api_call('details')
-
-@step(u'looking up (\w+) places ((?:[a-z]\d+,*)+)')
-def api_setup_lookup(step, fmt, ids):
- world.params['osm_ids'] = ids
- if fmt and fmt.strip():
- world.params['format'] = fmt.strip()
- api_call('lookup')
-
-@step(u'sending an API call (\w+)')
-def api_general_call(step, call):
- api_call(call)
+++ /dev/null
-""" Steps for checking the DB after import and update tests.
-
- There are two groups of test here. The first group tests
- the contents of db tables directly, the second checks
- query results by using the command line query tool.
-"""
-
-from nose.tools import *
-from lettuce import *
-import psycopg2
-import psycopg2.extensions
-import psycopg2.extras
-import os
-import random
-import json
-import re
-import logging
-from collections import OrderedDict
-
-logger = logging.getLogger(__name__)
-
-@step(u'table placex contains as names for (N|R|W)(\d+)')
-def check_placex_names(step, osmtyp, osmid):
- """ Check for the exact content of the name hstore in placex.
- """
- cur = world.conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
- cur.execute('SELECT name FROM placex where osm_type = %s and osm_id =%s', (osmtyp, int(osmid)))
- for line in cur:
- names = dict(line['name'])
- for name in step.hashes:
- assert_in(name['k'], names)
- assert_equals(names[name['k']], name['v'])
- del names[name['k']]
- assert_equals(len(names), 0)
-
-
-
-
-@step(u'table ([a-z_]+) contains$')
-def check_placex_content(step, tablename):
- """ check that the given lines are in the given table
- Entries are searched by osm_type/osm_id and then all
- given columns are tested. If there is more than one
- line for an OSM object, they must match in these columns.
- """
- try:
- cur = world.conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
- for line in step.hashes:
- osmtype, osmid, cls = world.split_id(line['object'])
- q = 'SELECT *'
- if tablename == 'placex':
- q = q + ", ST_X(centroid) as clat, ST_Y(centroid) as clon"
- if tablename == 'location_property_osmline':
- q = q + ' FROM %s where osm_id = %%s' % (tablename,)
- else:
- q = q + ", ST_GeometryType(geometry) as geometrytype"
- q = q + ' FROM %s where osm_type = %%s and osm_id = %%s' % (tablename,)
- if cls is None:
- if tablename == 'location_property_osmline':
- params = (osmid,)
- else:
- params = (osmtype, osmid)
- else:
- q = q + ' and class = %s'
- if tablename == 'location_property_osmline':
- params = (osmid, cls)
- else:
- params = (osmtype, osmid, cls)
- cur.execute(q, params)
- assert(cur.rowcount > 0)
- for res in cur:
- for k,v in line.iteritems():
- if not k == 'object':
- assert_in(k, res)
- if type(res[k]) is dict:
- val = world.make_hash(v)
- assert_equals(res[k], val)
- elif k in ('parent_place_id', 'linked_place_id'):
- pid = world.get_placeid(v)
- assert_equals(pid, res[k], "Results for '%s'/'%s' differ: '%s' != '%s'" % (line['object'], k, pid, res[k]))
- elif k == 'centroid':
- world.match_geometry((res['clat'], res['clon']), v)
- else:
- assert_equals(str(res[k]), v, "Results for '%s'/'%s' differ: '%s' != '%s'" % (line['object'], k, str(res[k]), v))
- finally:
- cur.close()
- world.conn.commit()
-
-@step(u'table (placex?) has no entry for (N|R|W)(\d+)(:\w+)?')
-def check_placex_missing(step, tablename, osmtyp, osmid, placeclass):
- cur = world.conn.cursor()
- try:
- q = 'SELECT count(*) FROM %s where osm_type = %%s and osm_id = %%s' % (tablename, )
- args = [osmtyp, int(osmid)]
- if placeclass is not None:
- q = q + ' and class = %s'
- args.append(placeclass[1:])
- cur.execute(q, args)
- numres = cur.fetchone()[0]
- assert_equals (numres, 0)
- finally:
- cur.close()
- world.conn.commit()
-
-@step(u'table location_property_osmline has no entry for W(\d+)?')
-def check_osmline_missing(step, osmid):
- cur = world.conn.cursor()
- try:
- q = 'SELECT count(*) FROM location_property_osmline where osm_id = %s' % (osmid, )
- cur.execute(q)
- numres = cur.fetchone()[0]
- assert_equals (numres, 0)
- finally:
- cur.close()
- world.conn.commit()
-
-@step(u'search_name table contains$')
-def check_search_name_content(step):
- cur = world.conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
- for line in step.hashes:
- placeid = world.get_placeid(line['place_id'])
- cur.execute('SELECT * FROM search_name WHERE place_id = %s', (placeid,))
- assert(cur.rowcount > 0)
- for res in cur:
- for k,v in line.iteritems():
- if k in ('search_rank', 'address_rank'):
- assert_equals(int(v), res[k], "Results for '%s'/'%s' differ: '%s' != '%d'" % (line['place_id'], k, v, res[k]))
- elif k in ('importance'):
- assert_equals(float(v), res[k], "Results for '%s'/'%s' differ: '%s' != '%d'" % (line['place_id'], k, v, res[k]))
- elif k in ('name_vector', 'nameaddress_vector'):
- terms = [x.strip().replace('#', ' ') for x in v.split(',')]
- cur.execute('SELECT word_id, word_token FROM word, (SELECT unnest(%s) as term) t WHERE word_token = make_standard_name(t.term)', (terms,))
- assert cur.rowcount >= len(terms)
- for wid in cur:
- assert_in(wid['word_id'], res[k], "Missing term for %s/%s: %s" % (line['place_id'], k, wid['word_token']))
- elif k in ('country_code'):
- assert_equals(v, res[k], "Results for '%s'/'%s' differ: '%s' != '%d'" % (line['place_id'], k, v, res[k]))
- elif k == 'place_id':
- pass
- else:
- raise Exception("Cannot handle field %s in search_name table" % (k, ))
-
-@step(u'way (\d+) expands to lines')
-def check_interpolation_lines(step, wayid):
- """Check that the correct interpolation line has been entered in
- location_property_osmline for the given source line/nodes.
- Expected are three columns:
- startnumber, endnumber and linegeo
- """
- lines = []
- for line in step.hashes:
- lines.append((line["startnumber"], line["endnumber"], line["geometry"]))
- cur = world.conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
- cur.execute("""SELECT startnumber::text, endnumber::text, st_astext(linegeo) as geometry
- FROM location_property_osmline WHERE osm_id = %s""",
- (int(wayid),))
- assert_equals(len(lines), cur.rowcount)
- for r in cur:
- linegeo = str(str(r["geometry"].split('(')[1]).split(')')[0]).replace(',', ', ')
- exp = (r["startnumber"], r["endnumber"], linegeo)
- assert_in(exp, lines)
- lines.remove(exp)
-
-@step(u'way (\d+) expands exactly to housenumbers ([0-9,]*)')
-def check_interpolated_housenumber_list(step, nodeid, numberlist):
- """ Checks that the interpolated house numbers corresponds
- to the given list.
- """
- expected = numberlist.split(',');
- cur = world.conn.cursor()
- cur.execute("""SELECT housenumber FROM placex
- WHERE osm_type = 'W' and osm_id = %s
- and class = 'place' and type = 'address'""", (int(nodeid),))
- for r in cur:
- assert_in(r[0], expected, "Unexpected house number %s for node %s." % (r[0], nodeid))
- expected.remove(r[0])
- assert_equals(0, len(expected), "Missing house numbers for way %s: %s" % (nodeid, expected))
-
-@step(u'way (\d+) expands to no housenumbers')
-def check_no_interpolated_housenumber_list(step, nodeid):
- """ Checks that the interpolated house numbers corresponds
- to the given list.
- """
- cur = world.conn.cursor()
- cur.execute("""SELECT housenumber FROM placex
- WHERE osm_type = 'W' and osm_id = %s
- and class = 'place' and type = 'address'""", (int(nodeid),))
- res = [r[0] for r in cur]
- assert_equals(0, len(res), "Unexpected house numbers for way %s: %s" % (nodeid, res))
-
-@step(u'table search_name has no entry for (.*)')
-def check_placex_missing(step, osmid):
- """ Checks if there is an entry in the search index for the
- given place object.
- """
- cur = world.conn.cursor()
- placeid = world.get_placeid(osmid)
- cur.execute('SELECT count(*) FROM search_name WHERE place_id =%s', (placeid,))
- numres = cur.fetchone()[0]
- assert_equals (numres, 0)
-
+++ /dev/null
-""" Steps for setting up a test database with imports and updates.
-
- There are two ways to state geometries for test data: with coordinates
- and via scenes.
-
- Coordinates should be given as a wkt without the enclosing type name.
-
- Scenes are prepared geometries which can be found in the scenes/data/
- directory. Each scene is saved in a .wkt file with its name, which
- contains a list of id/wkt pairs. A scene can be set globally
- for a scene by using the step `the scene <scene name>`. Then each
- object should be refered to as `:<object id>`. A geometry can also
- be referred to without loading the scene by explicitly stating the
- scene: `<scene name>:<object id>`.
-"""
-
-from nose.tools import *
-from lettuce import *
-import psycopg2
-import psycopg2.extensions
-import psycopg2.extras
-import os
-import subprocess
-import random
-import base64
-import sys
-
-psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
-
-@before.each_scenario
-def setup_test_database(scenario):
- """ Creates a new test database from the template database
- that was set up earlier in terrain.py. Will be done only
- for scenarios whose feature is tagged with 'DB'.
- """
- if scenario.feature.tags is not None and 'DB' in scenario.feature.tags:
- world.db_template_setup()
- world.write_nominatim_config(world.config.test_db)
- conn = psycopg2.connect(database=world.config.template_db)
- conn.set_isolation_level(0)
- cur = conn.cursor()
- cur.execute('DROP DATABASE IF EXISTS %s' % (world.config.test_db, ))
- cur.execute('CREATE DATABASE %s TEMPLATE = %s' % (world.config.test_db, world.config.template_db))
- conn.close()
- world.conn = psycopg2.connect(database=world.config.test_db)
- psycopg2.extras.register_hstore(world.conn, globally=False, unicode=True)
-
-@step('a wiped database')
-def db_setup_wipe_db(step):
- """Explicit DB scenario setup only needed
- to work around a bug where scenario outlines don't call
- before_each_scenario correctly.
- """
- if hasattr(world, 'conn'):
- world.conn.close()
- conn = psycopg2.connect(database=world.config.template_db)
- conn.set_isolation_level(0)
- cur = conn.cursor()
- cur.execute('DROP DATABASE IF EXISTS %s' % (world.config.test_db, ))
- cur.execute('CREATE DATABASE %s TEMPLATE = %s' % (world.config.test_db, world.config.template_db))
- conn.close()
- world.conn = psycopg2.connect(database=world.config.test_db)
- psycopg2.extras.register_hstore(world.conn, globally=False, unicode=True)
-
-
-@after.each_scenario
-def tear_down_test_database(scenario):
- """ Drops any previously created test database.
- """
- if hasattr(world, 'conn'):
- world.conn.close()
- if scenario.feature.tags is not None and 'DB' in scenario.feature.tags and not world.config.keep_scenario_db:
- conn = psycopg2.connect(database=world.config.template_db)
- conn.set_isolation_level(0)
- cur = conn.cursor()
- cur.execute('DROP DATABASE %s' % (world.config.test_db,))
- conn.close()
-
-
-def _format_placex_cols(cols, geomtype, force_name):
- if 'name' in cols:
- if cols['name'].startswith("'"):
- cols['name'] = world.make_hash(cols['name'])
- else:
- cols['name'] = { 'name' : cols['name'] }
- elif force_name:
- cols['name'] = { 'name' : base64.urlsafe_b64encode(os.urandom(int(random.random()*30))) }
- if 'extratags' in cols:
- cols['extratags'] = world.make_hash(cols['extratags'])
- if 'admin_level' not in cols:
- cols['admin_level'] = 100
- if 'geometry' in cols:
- coords = world.get_scene_geometry(cols['geometry'])
- if coords is None:
- coords = "'%s(%s)'::geometry" % (geomtype, cols['geometry'])
- else:
- coords = "'%s'::geometry" % coords.wkt
- cols['geometry'] = coords
- for k in cols:
- if not cols[k]:
- cols[k] = None
-
-
-def _insert_place_table_nodes(places, force_name):
- cur = world.conn.cursor()
- for line in places:
- cols = dict(line)
- cols['osm_type'] = 'N'
- _format_placex_cols(cols, 'POINT', force_name)
- if 'geometry' in cols:
- coords = cols.pop('geometry')
- else:
- coords = "ST_Point(%f, %f)" % (random.random()*360 - 180, random.random()*180 - 90)
-
- query = 'INSERT INTO place (%s,geometry) values(%s, ST_SetSRID(%s, 4326))' % (
- ','.join(cols.iterkeys()),
- ','.join(['%s' for x in range(len(cols))]),
- coords
- )
- cur.execute(query, cols.values())
- world.conn.commit()
-
-
-def _insert_place_table_objects(places, geomtype, force_name):
- cur = world.conn.cursor()
- for line in places:
- cols = dict(line)
- if 'osm_type' not in cols:
- cols['osm_type'] = 'W'
- _format_placex_cols(cols, geomtype, force_name)
- coords = cols.pop('geometry')
-
- query = 'INSERT INTO place (%s, geometry) values(%s, ST_SetSRID(%s, 4326))' % (
- ','.join(cols.iterkeys()),
- ','.join(['%s' for x in range(len(cols))]),
- coords
- )
- cur.execute(query, cols.values())
- world.conn.commit()
-
-@step(u'the scene (.*)')
-def import_set_scene(step, scene):
- world.load_scene(scene)
-
-@step(u'the (named )?place (node|way|area)s')
-def import_place_table_nodes(step, named, osmtype):
- """Insert a list of nodes into the place table.
- Expects a table where columns are named in the same way as place.
- """
- cur = world.conn.cursor()
- cur.execute('ALTER TABLE place DISABLE TRIGGER place_before_insert')
- if osmtype == 'node':
- _insert_place_table_nodes(step.hashes, named is not None)
- elif osmtype == 'way' :
- _insert_place_table_objects(step.hashes, 'LINESTRING', named is not None)
- elif osmtype == 'area' :
- _insert_place_table_objects(step.hashes, 'POLYGON', named is not None)
- cur.execute('ALTER TABLE place ENABLE TRIGGER place_before_insert')
- cur.close()
- world.conn.commit()
-
-
-@step(u'the relations')
-def import_fill_planet_osm_rels(step):
- """Adds a raw relation to the osm2pgsql table.
- Three columns need to be suplied: id, tags, members.
- """
- cur = world.conn.cursor()
- for line in step.hashes:
- members = []
- parts = { 'n' : [], 'w' : [], 'r' : [] }
- if line['members'].strip():
- for mem in line['members'].split(','):
- memparts = mem.strip().split(':', 2)
- memid = memparts[0].lower()
- parts[memid[0]].append(int(memid[1:]))
- members.append(memid)
- if len(memparts) == 2:
- members.append(memparts[1])
- else:
- members.append('')
- tags = []
- for k,v in world.make_hash(line['tags']).iteritems():
- tags.extend((k,v))
- if not members:
- members = None
-
- cur.execute("""INSERT INTO planet_osm_rels
- (id, way_off, rel_off, parts, members, tags)
- VALUES (%s, %s, %s, %s, %s, %s)""",
- (line['id'], len(parts['n']), len(parts['n']) + len(parts['w']),
- parts['n'] + parts['w'] + parts['r'], members, tags))
- world.conn.commit()
-
-
-@step(u'the ways')
-def import_fill_planet_osm_ways(step):
- cur = world.conn.cursor()
- for line in step.hashes:
- if 'tags' in line:
- tags = world.make_hash(line['tags'])
- else:
- tags = None
- nodes = [int(x.strip()) for x in line['nodes'].split(',')]
-
- cur.execute("""INSERT INTO planet_osm_ways (id, nodes, tags)
- VALUES (%s, %s, %s)""",
- (line['id'], nodes, tags))
- world.conn.commit()
-
-############### import and update steps #######################################
-
-@step(u'importing')
-def import_database(step):
- """ Runs the actual indexing. """
- world.run_nominatim_script('setup', 'create-functions', 'create-partition-functions')
- cur = world.conn.cursor()
- #world.db_dump_table('place')
- cur.execute("""insert into placex (osm_type, osm_id, class, type, name, admin_level,
- housenumber, street, addr_place, isin, postcode, country_code, extratags,
- geometry) select * from place where not (class='place' and type='houses' and osm_type='W')""")
- cur.execute("""select insert_osmline (osm_id, housenumber, street, addr_place, postcode, country_code, geometry) from place where class='place' and type='houses' and osm_type='W'""")
- world.conn.commit()
- world.run_nominatim_script('setup', 'index', 'index-noanalyse')
- #world.db_dump_table('placex')
- #world.db_dump_table('location_property_osmline')
-
-@step(u'updating place (node|way|area)s')
-def update_place_table_nodes(step, osmtype):
- """ Replace a geometry in place by reinsertion and reindex database."""
- world.run_nominatim_script('setup', 'create-functions', 'create-partition-functions', 'enable-diff-updates')
- if osmtype == 'node':
- _insert_place_table_nodes(step.hashes, False)
- elif osmtype == 'way':
- _insert_place_table_objects(step.hashes, 'LINESTRING', False)
- elif osmtype == 'area':
- _insert_place_table_objects(step.hashes, 'POLYGON', False)
- world.run_nominatim_script('update', 'index')
-
-@step(u'marking for delete (.*)')
-def update_delete_places(step, places):
- """ Remove an entry from place and reindex database.
- """
- world.run_nominatim_script('setup', 'create-functions', 'create-partition-functions', 'enable-diff-updates')
- cur = world.conn.cursor()
- for place in places.split(','):
- osmtype, osmid, cls = world.split_id(place)
- if cls is None:
- q = "delete from place where osm_type = %s and osm_id = %s"
- params = (osmtype, osmid)
- else:
- q = "delete from place where osm_type = %s and osm_id = %s and class = %s"
- params = (osmtype, osmid, cls)
- cur.execute(q, params)
- world.conn.commit()
- #world.db_dump_table('placex')
- world.run_nominatim_script('update', 'index')
-
-
-
-@step(u'sending query "(.*)"( with dups)?$')
-def query_cmd(step, query, with_dups):
- """ Results in standard query output. The same tests as for API queries
- can be used.
- """
- cmd = [os.path.join(world.config.source_dir, 'utils', 'query.php'),
- '--search', query]
- if with_dups is not None:
- cmd.append('--nodedupe')
- proc = subprocess.Popen(cmd, cwd=world.config.source_dir,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (outp, err) = proc.communicate()
- assert (proc.returncode == 0), "query.php failed with message: %s" % err
- world.page = outp
- world.response_format = 'json'
- world.request_type = 'search'
- world.returncode = 200
-
+++ /dev/null
-""" Steps for setting up a test database for osm2pgsql import.
-
- Note that osm2pgsql features need a database and therefore need
- to be tagged with @DB.
-"""
-
-from nose.tools import *
-from lettuce import *
-
-import logging
-import random
-import tempfile
-import os
-import subprocess
-
-logger = logging.getLogger(__name__)
-
-@before.each_scenario
-def osm2pgsql_setup_test(scenario):
- world.osm2pgsql = []
-
-@step(u'the osm nodes:')
-def osm2pgsql_import_nodes(step):
- """ Define a list of OSM nodes to be imported, given as a table.
- Each line describes one node with all its attributes.
- 'id' is mendatory, all other fields are filled with random values
- when not given. If 'tags' is missing an empty tag list is assumed.
- For updates, a mandatory 'action' column needs to contain 'A' (add),
- 'M' (modify), 'D' (delete).
- """
- for line in step.hashes:
- node = { 'type' : 'N', 'version' : '1', 'timestamp': "2012-05-01T15:06:20Z",
- 'changeset' : "11470653", 'uid' : "122294", 'user' : "foo"
- }
- node.update(line)
- node['id'] = int(node['id'])
- if 'geometry' in node:
- lat, lon = node['geometry'].split(' ')
- node['lat'] = float(lat)
- node['lon'] = float(lon)
- else:
- node['lon'] = random.random()*360 - 180
- node['lat'] = random.random()*180 - 90
- if 'tags' in node:
- node['tags'] = world.make_hash(line['tags'])
- else:
- node['tags'] = {}
-
- world.osm2pgsql.append(node)
-
-
-@step(u'the osm ways:')
-def osm2pgsql_import_ways(step):
- """ Define a list of OSM ways to be imported.
- """
- for line in step.hashes:
- way = { 'type' : 'W', 'version' : '1', 'timestamp': "2012-05-01T15:06:20Z",
- 'changeset' : "11470653", 'uid' : "122294", 'user' : "foo"
- }
- way.update(line)
-
- way['id'] = int(way['id'])
- if 'tags' in way:
- way['tags'] = world.make_hash(line['tags'])
- else:
- way['tags'] = None
- way['nodes'] = way['nodes'].strip().split()
-
- world.osm2pgsql.append(way)
-
-membertype = { 'N' : 'node', 'W' : 'way', 'R' : 'relation' }
-
-@step(u'the osm relations:')
-def osm2pgsql_import_rels(step):
- """ Define a list of OSM relation to be imported.
- """
- for line in step.hashes:
- rel = { 'type' : 'R', 'version' : '1', 'timestamp': "2012-05-01T15:06:20Z",
- 'changeset' : "11470653", 'uid' : "122294", 'user' : "foo"
- }
- rel.update(line)
-
- rel['id'] = int(rel['id'])
- if 'tags' in rel:
- rel['tags'] = world.make_hash(line['tags'])
- else:
- rel['tags'] = {}
- members = []
- if rel['members'].strip():
- for mem in line['members'].split(','):
- memparts = mem.strip().split(':', 2)
- memid = memparts[0].upper()
- members.append((membertype[memid[0]],
- memid[1:],
- memparts[1] if len(memparts) == 2 else ''
- ))
- rel['members'] = members
-
- world.osm2pgsql.append(rel)
-
-
-
-def _sort_xml_entries(x, y):
- if x['type'] == y['type']:
- return cmp(x['id'], y['id'])
- else:
- return cmp('NWR'.find(x['type']), 'NWR'.find(y['type']))
-
-def write_osm_obj(fd, obj):
- if obj['type'] == 'N':
- fd.write('<node id="%(id)d" lat="%(lat).8f" lon="%(lon).8f" version="%(version)s" timestamp="%(timestamp)s" changeset="%(changeset)s" uid="%(uid)s" user="%(user)s"'% obj)
- if obj['tags'] is None:
- fd.write('/>\n')
- else:
- fd.write('>\n')
- for k,v in obj['tags'].iteritems():
- fd.write(' <tag k="%s" v="%s"/>\n' % (k, v))
- fd.write('</node>\n')
- elif obj['type'] == 'W':
- fd.write('<way id="%(id)d" version="%(version)s" changeset="%(changeset)s" timestamp="%(timestamp)s" user="%(user)s" uid="%(uid)s">\n' % obj)
- for nd in obj['nodes']:
- fd.write('<nd ref="%s" />\n' % (nd,))
- for k,v in obj['tags'].iteritems():
- fd.write(' <tag k="%s" v="%s"/>\n' % (k, v))
- fd.write('</way>\n')
- elif obj['type'] == 'R':
- fd.write('<relation id="%(id)d" version="%(version)s" changeset="%(changeset)s" timestamp="%(timestamp)s" user="%(user)s" uid="%(uid)s">\n' % obj)
- for mem in obj['members']:
- fd.write(' <member type="%s" ref="%s" role="%s"/>\n' % mem)
- for k,v in obj['tags'].iteritems():
- fd.write(' <tag k="%s" v="%s"/>\n' % (k, v))
- fd.write('</relation>\n')
-
-@step(u'loading osm data')
-def osm2pgsql_load_place(step):
- """Imports the previously defined OSM data into a fresh copy of a
- Nominatim test database.
- """
-
- world.osm2pgsql.sort(cmp=_sort_xml_entries)
-
- # create a OSM file in /tmp
- with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.osm', delete=False) as fd:
- fname = fd.name
- fd.write("<?xml version='1.0' encoding='UTF-8'?>\n")
- fd.write('<osm version="0.6" generator="test-nominatim" timestamp="2014-08-26T20:22:02Z">\n')
- fd.write('\t<bounds minlat="43.72335" minlon="7.409205" maxlat="43.75169" maxlon="7.448637"/>\n')
-
- for obj in world.osm2pgsql:
- write_osm_obj(fd, obj)
-
- fd.write('</osm>\n')
-
- logger.debug( "Filename: %s" % fname)
-
- cmd = [os.path.join(world.config.source_dir, 'utils', 'setup.php')]
- cmd.extend(['--osm-file', fname, '--import-data','--osm2pgsql-cache', '300'])
- proc = subprocess.Popen(cmd, cwd=world.config.source_dir,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (outp, outerr) = proc.communicate()
- assert (proc.returncode == 0), "OSM data import failed:\n%s\n%s\n" % (outp, outerr)
-
- ### reintroduce the triggers/indexes we've lost by having osm2pgsql set up place again
- cur = world.conn.cursor()
- cur.execute("""CREATE TRIGGER place_before_delete BEFORE DELETE ON place
- FOR EACH ROW EXECUTE PROCEDURE place_delete()""")
- cur.execute("""CREATE TRIGGER place_before_insert BEFORE INSERT ON place
- FOR EACH ROW EXECUTE PROCEDURE place_insert()""")
- cur.execute("""CREATE UNIQUE INDEX idx_place_osm_unique on place using btree(osm_id,osm_type,class,type)""")
- world.conn.commit()
-
-
- os.remove(fname)
- world.osm2pgsql = []
-
-actiontypes = { 'C' : 'create', 'M' : 'modify', 'D' : 'delete' }
-
-@step(u'updating osm data')
-def osm2pgsql_update_place(step):
- """Creates an osc file from the previously defined data and imports it
- into the database.
- """
- world.run_nominatim_script('setup', 'create-functions', 'create-partition-functions')
- cur = world.conn.cursor()
- cur.execute("""insert into placex (osm_type, osm_id, class, type, name, admin_level,
- housenumber, street, addr_place, isin, postcode, country_code, extratags,
- geometry) select * from place""")
- world.conn.commit()
- world.run_nominatim_script('setup', 'index', 'index-noanalyse')
- world.run_nominatim_script('setup', 'create-functions', 'create-partition-functions', 'enable-diff-updates')
-
- with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.osc', delete=False) as fd:
- fname = fd.name
- fd.write("<?xml version='1.0' encoding='UTF-8'?>\n")
- fd.write('<osmChange version="0.6" generator="Osmosis 0.43.1">\n')
-
- for obj in world.osm2pgsql:
- fd.write('<%s>\n' % (actiontypes[obj['action']], ))
- write_osm_obj(fd, obj)
- fd.write('</%s>\n' % (actiontypes[obj['action']], ))
-
- fd.write('</osmChange>\n')
-
- logger.debug( "Filename: %s" % fname)
-
- cmd = [os.path.join(world.config.source_dir, 'utils', 'update.php')]
- cmd.extend(['--import-diff', fname])
- proc = subprocess.Popen(cmd, cwd=world.config.source_dir,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (outp, outerr) = proc.communicate()
- assert (proc.returncode == 0), "OSM data update failed:\n%s\n%s\n" % (outp, outerr)
-
- os.remove(fname)
- world.osm2pgsql = []
+++ /dev/null
-from lettuce import *
-from nose.tools import *
-import logging
-import os
-import subprocess
-import psycopg2
-import re
-from haversine import haversine
-from shapely.wkt import loads as wkt_load
-from shapely.ops import linemerge
-
-logger = logging.getLogger(__name__)
-
-class NominatimConfig:
-
- def __init__(self):
- # logging setup
- loglevel = getattr(logging, os.environ.get('LOGLEVEL','info').upper())
- if 'LOGFILE' in os.environ:
- logging.basicConfig(filename=os.environ.get('LOGFILE','run.log'),
- level=loglevel)
- else:
- logging.basicConfig(level=loglevel)
- # Nominatim test setup
- self.base_url = os.environ.get('NOMINATIM_SERVER', 'http://localhost/nominatim')
- self.source_dir = os.path.abspath(os.environ.get('NOMINATIM_DIR', '../build'))
- self.template_db = os.environ.get('TEMPLATE_DB', 'test_template_nominatim')
- self.test_db = os.environ.get('TEST_DB', 'test_nominatim')
- self.local_settings_file = os.environ.get('NOMINATIM_SETTINGS', '/tmp/nominatim_settings.php')
- self.reuse_template = 'NOMINATIM_REMOVE_TEMPLATE' not in os.environ
- self.keep_scenario_db = 'NOMINATIM_KEEP_SCENARIO_DB' in os.environ
- os.environ['NOMINATIM_SETTINGS'] = '/tmp/nominatim_settings.php'
-
- scriptpath = os.path.dirname(os.path.abspath(__file__))
- self.scene_path = os.environ.get('SCENE_PATH',
- os.path.join(scriptpath, '..', 'scenes', 'data'))
-
-
- def __str__(self):
- return 'Server URL: %s\nSource dir: %s\n' % (self.base_url, self.source_dir)
-
-world.config = NominatimConfig()
-
-@world.absorb
-def write_nominatim_config(dbname):
- f = open(world.config.local_settings_file, 'w')
- f.write("<?php\n @define('CONST_Database_DSN', 'pgsql://@/%s');\n" % dbname)
- f.close()
-
-
-@world.absorb
-def run_nominatim_script(script, *args):
- cmd = [os.path.join(world.config.source_dir, 'utils', '%s.php' % script)]
- cmd.extend(['--%s' % x for x in args])
- proc = subprocess.Popen(cmd, cwd=world.config.source_dir,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (outp, outerr) = proc.communicate()
- logger.debug("run_nominatim_script: %s\n%s\n%s" % (cmd, outp, outerr))
- assert (proc.returncode == 0), "Script '%s' failed:\n%s\n%s\n" % (script, outp, outerr)
-
-@world.absorb
-def make_hash(inp):
- return eval('{' + inp + '}')
-
-@world.absorb
-def split_id(oid):
- """ Splits a unique identifier for places into its components.
- As place_ids cannot be used for testing, we use a unique
- identifier instead that is of the form <osmtype><osmid>[:class].
- """
- oid = oid.strip()
- if oid == 'None':
- return None, None, None
- osmtype = oid[0]
- assert_in(osmtype, ('R','N','W'))
- if ':' in oid:
- osmid, cls = oid[1:].split(':')
- return (osmtype, int(osmid), cls)
- else:
- return (osmtype, int(oid[1:]), None)
-
-@world.absorb
-def get_placeid(oid):
- """ Tries to retrive the place_id for a unique identifier. """
- if oid[0].isdigit():
- return int(oid)
-
- osmtype, osmid, cls = world.split_id(oid)
- if osmtype is None:
- return None
- cur = world.conn.cursor()
- if cls is None:
- q = 'SELECT place_id FROM placex where osm_type = %s and osm_id = %s'
- params = (osmtype, osmid)
- else:
- q = 'SELECT place_id FROM placex where osm_type = %s and osm_id = %s and class = %s'
- params = (osmtype, osmid, cls)
- cur.execute(q, params)
- assert_equals(cur.rowcount, 1, "%d rows found for place %s" % (cur.rowcount, oid))
- return cur.fetchone()[0]
-
-
-@world.absorb
-def match_geometry(coord, matchstring):
- m = re.match(r'([-0-9.]+),\s*([-0-9.]+)\s*(?:\+-([0-9.]+)([a-z]+)?)?', matchstring)
- assert_is_not_none(m, "Invalid match string")
-
- logger.debug("Distmatch: %s/%s %s %s" % (m.group(1), m.group(2), m.group(3), m.group(4) ))
- dist = haversine(coord, (float(m.group(1)), float(m.group(2))))
-
- if m.group(3) is not None:
- expdist = float(m.group(3))
- if m.group(4) is not None:
- if m.group(4) == 'm':
- expdist = expdist/1000
- elif m.group(4) == 'km':
- pass
- else:
- raise Exception("Unknown unit '%s' in geometry match" % (m.group(4), ))
- else:
- expdist = 0
-
- logger.debug("Distances expected: %f, got: %f" % (expdist, dist))
- assert dist <= expdist, "Geometry too far away, expected: %f, got: %f" % (expdist, dist)
-
-@world.absorb
-def print_statement(element):
- print '\n\n\n'+str(element)+'\n\n\n'
-
-
-@world.absorb
-def db_dump_table(table):
- cur = world.conn.cursor()
- cur.execute('SELECT * FROM %s' % table)
- print '\n\n\n<<<<<<< BEGIN OF TABLE DUMP %s' % table
- for res in cur:
- print res
- print '<<<<<<< END OF TABLE DUMP %s\n\n\n' % table
-
-@world.absorb
-def db_drop_database(name):
- conn = psycopg2.connect(database='postgres')
- conn.set_isolation_level(0)
- cur = conn.cursor()
- cur.execute('DROP DATABASE IF EXISTS %s' % (name, ))
- conn.close()
-
-
-world.is_template_set_up = False
-
-@world.absorb
-def db_template_setup():
- """ Set up a template database, containing all tables
- but not yet any functions.
- """
- if world.is_template_set_up:
- return
-
- world.is_template_set_up = True
- world.write_nominatim_config(world.config.template_db)
- if world.config.reuse_template:
- # check that the template is there
- conn = psycopg2.connect(database='postgres')
- cur = conn.cursor()
- cur.execute('select count(*) from pg_database where datname = %s',
- (world.config.template_db,))
- if cur.fetchone()[0] == 1:
- return
- else:
- # just in case... make sure a previous table has been dropped
- world.db_drop_database(world.config.template_db)
- # call the first part of database setup
- world.run_nominatim_script('setup', 'create-db', 'setup-db')
- # remove external data to speed up indexing for tests
- conn = psycopg2.connect(database=world.config.template_db)
- psycopg2.extras.register_hstore(conn, globally=False, unicode=True)
- cur = conn.cursor()
- for table in ('gb_postcode', 'us_postcode'):
- cur.execute("select * from pg_tables where tablename = '%s'" % (table, ))
- if cur.rowcount > 0:
- cur.execute('TRUNCATE TABLE %s' % (table,))
- conn.commit()
- conn.close()
- # execute osm2pgsql on an empty file to get the right tables
- osm2pgsql = os.path.join(world.config.source_dir, 'osm2pgsql', 'osm2pgsql')
- proc = subprocess.Popen([osm2pgsql, '-lsc', '-r', 'xml', '-O', 'gazetteer', '-d', world.config.template_db, '-'],
- cwd=world.config.source_dir, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- [outstr, errstr] = proc.communicate(input='<osm version="0.6"></osm>')
- logger.debug("running osm2pgsql for template: %s\n%s\n%s" % (osm2pgsql, outstr, errstr))
- world.run_nominatim_script('setup', 'create-functions', 'create-tables', 'create-partition-tables', 'create-partition-functions', 'load-data', 'create-search-indices')
-
-
-# Leave the table around so it can be reused again after a non-reuse test round.
-#@after.all
-def db_template_teardown(total):
- """ Set up a template database, containing all tables
- but not yet any functions.
- """
- if world.is_template_set_up:
- # remove template DB
- if not world.config.reuse_template:
- world.db_drop_database(world.config.template_db)
- try:
- os.remove(world.config.local_settings_file)
- except OSError:
- pass # ignore missing file
-
-
-##########################################################################
-#
-# Data scene handling
-#
-
-world.scenes = {}
-world.current_scene = None
-
-@world.absorb
-def load_scene(name):
- if name in world.scenes:
- world.current_scene = world.scenes[name]
- else:
- with open(os.path.join(world.config.scene_path, "%s.wkt" % name), 'r') as fd:
- scene = {}
- for line in fd:
- if line.strip():
- obj, wkt = line.split('|', 2)
- wkt = wkt.strip()
- scene[obj.strip()] = wkt_load(wkt)
- world.scenes[name] = scene
- world.current_scene = scene
-
-@world.absorb
-def get_scene_geometry(name):
- if not ':' in name:
- # Not a scene description
- return None
-
- geoms = []
- for obj in name.split('+'):
- oname = obj.strip()
- if oname.startswith(':'):
- geoms.append(world.current_scene[oname[1:]])
- else:
- scene, obj = oname.split(':', 2)
- oldscene = world.current_scene
- world.load_scene(scene)
- wkt = world.current_scene[obj]
- world.current_scene = oldscene
- geoms.append(wkt)
-
- if len(geoms) == 1:
- return geoms[0]
- else:
- return linemerge(geoms)
if ($aCMDResult['import-wikipedia-articles'] || $aCMDResult['all']) {
$bDidSomething = true;
- $sWikiArticlesFile = CONST_BasePath.'/data/wikipedia_article.sql.bin';
- $sWikiRedirectsFile = CONST_BasePath.'/data/wikipedia_redirect.sql.bin';
+ $sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
+ $sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
if (file_exists($sWikiArticlesFile)) {
echo "Importing wikipedia articles...";
pgsqlRunDropAndRestore($sWikiArticlesFile);