Compare commits

..

75 Commits

Author SHA1 Message Date
Sarah Hoffmann
f97a0a76f2 Merge pull request #3747 from anqixxx/fix-special-phrases-filtering
Special Phrases Filtering: Add Command Line Functionality
2025-06-06 21:37:17 +02:00
anqixxx
cf9b946eba Added skip for when min =0 2025-06-05 09:25:14 +08:00
anqixxx
7dc3924a3c Added default min = 0 argument for private functions
empty
2025-06-04 01:12:36 -07:00
anqixxx
20cf4b56b9 Refactored min and associated tests to follow greater than or equal to logic, so that min=0 accounted for no filtering
r
2025-06-04 00:53:52 -07:00
anqixxx
40d5b78eb8 Added command line (default 0) min argument for minimum filtering, updated args.py to reflect this 2025-06-04 00:53:52 -07:00
Sarah Hoffmann
8d0e767826 Merge pull request #3748 from lonvia/airports
Improve finding airports by their codes
2025-06-02 14:39:02 +02:00
Sarah Hoffmann
87a8c246a0 improve result cutting when a POI comes out with top importance 2025-06-01 12:00:36 +02:00
Sarah Hoffmann
90050de717 only rerank results if there is more than one
With one result order is obvious.
2025-06-01 11:55:27 +02:00
Sarah Hoffmann
10a7d1106d reduce influence of query rematching a little bit 2025-06-01 11:54:21 +02:00
Sarah Hoffmann
f2236f68f1 when rematching only distinguish between perfect, somewhat and bad match 2025-06-01 11:53:23 +02:00
Sarah Hoffmann
831fccdaee add FAA codes (US version of IATA codes) for airports 2025-06-01 11:49:55 +02:00
Sarah Hoffmann
d2e691b63f work around bogus type error in latest starlette 2025-05-31 09:43:48 +02:00
Sarah Hoffmann
2a508b6c99 fix missing optional return 2025-05-30 12:03:00 +02:00
Sarah Hoffmann
02c3a6fffa Merge pull request #3744 from lonvia/add-unnamed-cemetries
Include unnamed cemetaries in POIs
2025-05-28 11:51:23 +02:00
Sarah Hoffmann
26348764d4 add landuse=cemetery as POI even when unnamed 2025-05-28 09:48:08 +02:00
Sarah Hoffmann
f8a56ab6e6 Merge pull request #3742 from lonvia/korean-defaults
Remove English as default language for South Korea
2025-05-26 14:13:54 +02:00
Sarah Hoffmann
75b4c7e56b adapt to changed loop handling of pytest_asyncio 2025-05-26 11:51:20 +02:00
Sarah Hoffmann
9f1dfb1876 remove English as default language for South Korea 2025-05-26 10:28:14 +02:00
Sarah Hoffmann
730b4204f6 Merge pull request #3741 from dave-meyer/patch-1
docs: Added missing code span for search API parameter value
2025-05-26 09:21:40 +02:00
Dave Meyer
4898704b5a docs: Added missing code span for search API parameter value 2025-05-25 20:42:09 +02:00
Sarah Hoffmann
0cf470f863 Merge pull request #3710 from anqixxx/fix-special-phrases-filtering
Fix special phrases filtering
2025-05-21 21:34:28 +02:00
anqixxx
6220bde2d6 Added mypy ignore fix for logging.py (library change), as well as quick mac fix on mem.cached 2025-05-21 11:11:56 -07:00
Sarah Hoffmann
a4d3b57f37 Merge pull request #3709 from anqixxx/update-readme
Improve README formatting and add install steps
2025-05-21 19:49:12 +02:00
anqixxx
618fbc63d7 Added testing to test get classtype pairs in import special phrases 2025-05-21 10:39:51 -07:00
anqixxx
3f51cb3fd1 Made the limit configurable with an optional argument, updating the testing as well to reflect this. default is now 0, meaning that it will return everything that occurs more than once. Removed mock database test, and got rid of fetch all. Rebased all tests to monkeypatch 2025-05-21 10:38:34 -07:00
anqixxx
59a947c5f5 Removed class type pair getter that used style sheets from both spi_importer and the associated testing function 2025-05-21 10:38:08 -07:00
anqixxx
1952290359 Removed magic mocking, using monkeypatch instead, and using a placex table to simulate a 'real database' 2025-05-21 10:37:42 -07:00
anqixxx
1a323165f9 Filter special phrases by style and frequency to fix #235 2025-05-21 10:36:46 -07:00
anqixxx
9c2fdf5eae Improve README formatting and add install steps, adding a general cloning step before the virtual environment. This would have been helpful for me during Nominatim setup 2025-05-21 10:14:36 -07:00
Sarah Hoffmann
800c56642b tweak full count cut-off (as per deployment on osm.org) 2025-05-11 11:48:07 +02:00
Sarah Hoffmann
b51fed025c Merge pull request #3732 from lonvia/exclude-country-from-direction-penalty
Exclude address searches with country from direction penalty
2025-04-30 10:45:37 +02:00
Sarah Hoffmann
34b72591cc exclude address searches with country from direction penalty
Countries are not adequately represented by partial term counts.
2025-04-29 17:37:31 +02:00
Sarah Hoffmann
bc450d110c Merge pull request #3722 from emmanuel-ferdman/master
resolve datetime deprecation warnings
2025-04-22 14:21:05 +02:00
Sarah Hoffmann
388acf4727 Merge pull request #3726 from lonvia/revert-json-format-change
Revert accidental change in json output format
2025-04-18 14:43:51 +02:00
Sarah Hoffmann
3999977941 revert accidental change in json output format 2025-04-18 12:05:25 +02:00
Emmanuel Ferdman
df58870e3f resolve datetime deprecation warnings
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2025-04-17 11:15:16 -07:00
Sarah Hoffmann
478a8741db Merge pull request #3719 from lonvia/query-direction
Estimate query direction
2025-04-17 15:17:56 +02:00
Sarah Hoffmann
7f710d2394 add a comment about the precomputed denominator 2025-04-15 09:38:05 +02:00
Sarah Hoffmann
06e39e42d8 add direction penalties
Direction penalties are estimated by getting the name to address
ratio usage for each partial term in the query and computing the
linear regression of that ratio over the entire phrase. Or to put
it in ither words: we try to determine if the terms at the beginning
or the end of the query are more likely to constitute a name.

Direction penalties are currently used only in classic name queries.
2025-04-11 20:41:06 +02:00
Sarah Hoffmann
2ef0e20a3f reorganise token reranking
As the reranking is about changing penalties in presence of other
tokens, change the datastructure to have the other tokens readily
avilable.
2025-04-11 13:38:34 +02:00
Sarah Hoffmann
b680d81f0a ensure that bailout-check is done after each iteration 2025-04-11 11:02:11 +02:00
Sarah Hoffmann
e0e067b1d6 replace use of range when computing word list 2025-04-11 09:59:04 +02:00
Sarah Hoffmann
3980791cfd use iterator instead of list to go over partials 2025-04-11 09:38:24 +02:00
Sarah Hoffmann
497e27bb9a move partial token into a separate field in the query struct
There is exactly one token to be expected and the token is usually
present.
2025-04-11 08:57:34 +02:00
Sarah Hoffmann
1db717b886 Merge pull request #3716 from lonvia/github-cache-osm2pgsql-binary
Github actions: cache compiled osm2pgsql binary

For the tests on Ubunutu 22-04 we need to compile osm2pgsql because the version they ship is too old. This adds caching of the compiled binary, so that we don't need to recompile for each CI run. Together with the new BDD tests that shaves around 10 min off a CI run.
2025-04-10 17:20:32 +02:00
Sarah Hoffmann
b47c8ccfb1 actions: cache compiled osm2pgsql binary 2025-04-10 16:06:27 +02:00
Sarah Hoffmann
63b055283d Merge pull request #3714 from lonvia/postcode-update-without-project-dir
Change postcode update function to work without a project directory
2025-04-10 08:51:22 +02:00
Sarah Hoffmann
b80e6914e7 Merge pull request #3715 from lonvia/demote-tags-to-fallbacks
Demote historic and tourism=attraction to fallback tags
2025-04-10 08:51:06 +02:00
Sarah Hoffmann
9d00a137fe demote historic and tourism=attraction to fallback tags 2025-04-09 20:15:18 +02:00
Sarah Hoffmann
97d9e3c548 allow updating postcodes without a project directory
Postcodes will then be updated without looking for external postcodes.
2025-04-09 20:04:01 +02:00
Sarah Hoffmann
e4180936c1 Merge pull request #3713 from lonvia/bdd-pytest-db-test
Move BDD tests to pytest-bdd
2025-04-09 19:37:30 +02:00
Sarah Hoffmann
34e0ecb44f update documentation for BDD tests 2025-04-09 15:21:50 +02:00
Sarah Hoffmann
d95e9737da remove usage of behave 2025-04-09 14:57:39 +02:00
Sarah Hoffmann
b34991d85f add BDD tests for DB 2025-04-09 14:52:34 +02:00
Sarah Hoffmann
5f44aa2873 improve table comparison 2025-04-04 11:02:51 +02:00
Sarah Hoffmann
dae643c040 move database setup to generic conftest.py 2025-04-04 11:02:51 +02:00
Sarah Hoffmann
ee62d5e1cf remove old behave osm2pgsql BDD tests 2025-04-04 11:02:51 +02:00
Sarah Hoffmann
fb440f29a2 implement BDD osm2pgsql tests with pytest-bdd 2025-04-04 11:02:51 +02:00
Sarah Hoffmann
0f725b1880 enable python-bdd for github actions 2025-04-04 11:02:51 +02:00
Sarah Hoffmann
39f56ba4b8 restrict coordinate output to 7 digits 2025-04-04 11:02:51 +02:00
Sarah Hoffmann
6959577aa4 replace behave BDD API tests with pytest-bdd tests 2025-04-04 11:02:51 +02:00
Sarah Hoffmann
50d4b0a386 Merge pull request #3687 from asharmalik19/test-linked-places-language
test: linked places expand default language names
2025-04-04 10:58:53 +02:00
Ashar
9ff93bdb3d Update linked places name test
Clean up test scenario by removing extra language variations and
improving table readability.
2025-04-03 14:30:18 -04:00
Ashar
e0bf553aa5 test: linked places expand default language names
Add failing test for issue #2714 to verify default language expansion
2025-04-03 14:30:18 -04:00
Sarah Hoffmann
2ce2d031fa Merge pull request #3702 from lonvia/remove-tokenizer-dir
Remove automatic setup of tokenizer directory

So far the tokenizer factory would create a directory for private data for the tokenizer and then hand in the directory location to the tokenizer.

ICU tokenizer doesn't need any extra data anymore, so it doesn't make sense to create a directory which then remains empty. If a tokenizer needs such a directory in the future, it needs to create it on its own and make sure to handle the situation correctly where no project directory is used at all.
2025-04-03 09:04:48 +02:00
Sarah Hoffmann
186f562dd7 remove automatic setup of tokenizer directory
ICU tokenizer doesn't need any extra data anymore, so it doesn't
make sense to create a directory which then remains empty. If a
tokenizer needs such a directory in the future, it needs to create
it on its own and make sure to handle the situation correctly where
no project directory is used at all.
2025-04-02 20:20:04 +02:00
Sarah Hoffmann
c5bbeb626f Merge pull request #3700 from lonvia/ignore-inherited-addresses
Ignore POIs with inherited addresses for the address layer
2025-04-02 12:00:45 +02:00
Sarah Hoffmann
3bc77629c8 ignore POIs with inherited addresses for the address layer
We know that there is a building which describes the address as a
polygon and is therefore more suitable.
2025-04-02 10:30:45 +02:00
Sarah Hoffmann
6cf1287c4e Merge pull request #3686 from astridx/output_names
Output names as setting
2025-04-01 20:16:15 +02:00
Sarah Hoffmann
a49e8b9cf7 Merge pull request #3675 from TuringVerified/generic-preprocessors
Add generic preprocessors
2025-04-01 20:14:43 +02:00
TuringVerified
2eeec46040 Remove unnecessary assert statement, Fix regex_replace docstring and simplify regex_replace 2025-04-01 18:54:30 +05:30
TuringVerified
6d5a4a20c5 Update documentation, optimise regex_replace, add tests 2025-04-01 18:54:30 +05:30
TuringVerified
4665ea3e77 Add generic preprocessor 2025-04-01 18:54:30 +05:30
Sarah Hoffmann
9cf5eee5d4 add instructions for pip package upload 2025-04-01 11:59:03 +02:00
astridx
12ad95067d output names as setting 2025-03-31 16:55:05 +02:00
151 changed files with 6016 additions and 5880 deletions

View File

@@ -7,5 +7,5 @@ extend-ignore =
per-file-ignores =
__init__.py: F401
test/python/utils/test_json_writer.py: E131
test/python/conftest.py: E402
**/conftest.py: E402
test/bdd/*: F821

View File

@@ -68,26 +68,34 @@ jobs:
with:
dependencies: ${{ matrix.dependencies }}
- uses: actions/cache@v4
with:
path: |
/usr/local/bin/osm2pgsql
key: osm2pgsql-bin-22-1
if: matrix.ubuntu == '22'
- name: Compile osm2pgsql
run: |
sudo apt-get install -y -qq libboost-system-dev libboost-filesystem-dev libexpat1-dev zlib1g-dev libbz2-dev libpq-dev libproj-dev libicu-dev liblua${LUA_VERSION}-dev lua-dkjson nlohmann-json3-dev
mkdir osm2pgsql-build
cd osm2pgsql-build
git clone https://github.com/osm2pgsql-dev/osm2pgsql
mkdir build
cd build
cmake ../osm2pgsql
make
sudo make install
cd ../..
rm -rf osm2pgsql-build
if [ ! -f /usr/local/bin/osm2pgsql ]; then
sudo apt-get install -y -qq libboost-system-dev libboost-filesystem-dev libexpat1-dev zlib1g-dev libbz2-dev libpq-dev libproj-dev libicu-dev liblua${LUA_VERSION}-dev lua-dkjson nlohmann-json3-dev
mkdir osm2pgsql-build
cd osm2pgsql-build
git clone https://github.com/osm2pgsql-dev/osm2pgsql
mkdir build
cd build
cmake ../osm2pgsql
make
sudo make install
cd ../..
rm -rf osm2pgsql-build
else
sudo apt-get install -y -qq libexpat1 liblua${LUA_VERSION}
fi
if: matrix.ubuntu == '22'
env:
LUA_VERSION: ${{ matrix.lua }}
- name: Install test prerequisites
run: ./venv/bin/pip install behave==1.2.6
- name: Install test prerequisites (apt)
run: sudo apt-get install -y -qq python3-pytest python3-pytest-asyncio uvicorn python3-falcon python3-aiosqlite python3-pyosmium
if: matrix.dependencies == 'apt'
@@ -96,6 +104,9 @@ jobs:
run: ./venv/bin/pip install pytest-asyncio falcon starlette asgi_lifespan aiosqlite osmium uvicorn
if: matrix.dependencies == 'pip'
- name: Install test prerequisites
run: ./venv/bin/pip install pytest-bdd
- name: Install latest flake8
run: ./venv/bin/pip install -U flake8
@@ -118,8 +129,8 @@ jobs:
- name: BDD tests
run: |
../../../venv/bin/python -m behave -DREMOVE_TEMPLATE=1 --format=progress3
working-directory: Nominatim/test/bdd
../venv/bin/python -m pytest test/bdd --nominatim-purge
working-directory: Nominatim
install:
runs-on: ubuntu-latest

View File

@@ -113,3 +113,5 @@ Checklist for releases:
* run `nominatim --version` to confirm correct version
* [ ] tag new release and add a release on github.com
* [ ] build pip packages and upload to pypi
* `make build`
* `twine upload dist/*`

View File

@@ -27,7 +27,7 @@ lint:
flake8 src test/python test/bdd
bdd:
cd test/bdd; behave -DREMOVE_TEMPLATE=1
pytest test/bdd --nominatim-purge
# Documentation

View File

@@ -27,18 +27,25 @@ can be found at nominatim.org as well.
A quick summary of the necessary steps:
1. Create a Python virtualenv and install the packages:
1. Clone this git repository and download the country grid
git clone https://github.com/osm-search/Nominatim.git
wget -O Nominatim/data/country_osm_grid.sql.gz https://nominatim.org/data/country_grid.sql.gz
2. Create a Python virtualenv and install the packages:
python3 -m venv nominatim-venv
./nominatim-venv/bin/pip install packaging/nominatim-{api,db}
2. Create a project directory, get OSM data and import:
3. Create a project directory, get OSM data and import:
mkdir nominatim-project
cd nominatim-project
../nominatim-venv/bin/nominatim import --osm-file <your planet file>
../nominatim-venv/bin/nominatim import --osm-file <your planet file> 2>&1 | tee setup.log
3. Start the webserver:
4. Start the webserver:
./nominatim-venv/bin/pip install uvicorn falcon
../nominatim-venv/bin/nominatim serve

View File

@@ -212,7 +212,7 @@ other layers.
The featureType allows to have a more fine-grained selection for places
from the address layer. Results can be restricted to places that make up
the 'state', 'country' or 'city' part of an address. A featureType of
settlement selects any human inhabited feature from 'state' down to
`settlement` selects any human inhabited feature from 'state' down to
'neighbourhood'.
When featureType is set, then results are automatically restricted

View File

@@ -602,6 +602,43 @@ results gathered so far.
Note that under high load you may observe that users receive different results
than usual without seeing an error. This may cause some confusion.
#### NOMINATIM_OUTPUT_NAMES
| Summary | |
| -------------- | --------------------------------------------------- |
| **Description:** | Specifies order of name tags |
| **Format:** | string: comma-separated list of tag names |
| **Default:** | name:XX,name,brand,official_name:XX,short_name:XX,official_name,short_name,ref |
Specifies the order in which different name tags are used.
The values in this list determine the preferred order of name variants,
including language-specific names (in OSM: the name tag with and without any language suffix).
Comma-separated list, where :XX stands for language suffix
(e.g. name:en) and no :XX stands for general tags (e.g. name).
See also [NOMINATIM_DEFAULT_LANGUAGE](#nominatim_default_language).
!!! note
If NOMINATIM_OUTPUT_NAMES = `name:XX,name,short_name:XX,short_name` the search follows
```
'name', 'short_name'
```
if we have no preferred language order for showing search results.
For languages ['en', 'es'] the search follows
```
'name:en', 'name:es',
'name',
'short_name:en', 'short_name:es',
'short_name'
```
For those familiar with the internal implementation, the `_place_*` expansion is added, but to simplify, it is not included in this example.
### Logging Settings
#### NOMINATIM_LOG_DB

View File

@@ -67,7 +67,13 @@ Here is an example configuration file:
``` yaml
query-preprocessing:
- normalize
- step: split_japanese_phrases
- step: regex_replace
replacements:
- pattern: https?://[^\s]* # Filter URLs starting with http or https
replace: ''
- step: normalize
normalization:
- ":: lower ()"
- "ß > 'ss'" # German szet is unambiguously equal to double ss
@@ -88,8 +94,8 @@ token-analysis:
replacements: ['ä', 'ae']
```
The configuration file contains four sections:
`normalization`, `transliteration`, `sanitizers` and `token-analysis`.
The configuration file contains five sections:
`query-preprocessing`, `normalization`, `transliteration`, `sanitizers` and `token-analysis`.
#### Query preprocessing
@@ -106,6 +112,19 @@ The following is a list of preprocessors that are shipped with Nominatim.
heading_level: 6
docstring_section_style: spacy
##### regex-replace
::: nominatim_api.query_preprocessing.regex_replace
options:
members: False
heading_level: 6
docstring_section_style: spacy
description:
This option runs any given regex pattern on the input and replaces values accordingly
replacements:
- pattern: regex pattern
replace: string to replace with
#### Normalization and Transliteration

View File

@@ -25,15 +25,15 @@ following packages should get you started:
## Prerequisites for testing and documentation
The Nominatim test suite consists of behavioural tests (using behave) and
The Nominatim test suite consists of behavioural tests (using pytest-bdd) and
unit tests (using pytest). It has the following additional requirements:
* [behave test framework](https://behave.readthedocs.io) >= 1.2.6
* [flake8](https://flake8.pycqa.org/en/stable/) (CI always runs the latest version from pip)
* [mypy](http://mypy-lang.org/) (plus typing information for external libs)
* [Python Typing Extensions](https://github.com/python/typing_extensions) (for Python < 3.9)
* [pytest](https://pytest.org)
* [pytest-asyncio](https://pytest-asyncio.readthedocs.io)
* [pytest-bdd](https://pytest-bdd.readthedocs.io)
For testing the Python search frontend, you need to install extra dependencies
depending on your choice of webserver framework:
@@ -48,9 +48,6 @@ The documentation is built with mkdocs:
* [mkdocs-material](https://squidfunk.github.io/mkdocs-material/)
* [mkdocs-gen-files](https://oprypin.github.io/mkdocs-gen-files/)
Please be aware that tests always run against the globally installed
osm2pgsql, so you need to have this set up. If you want to test against
the vendored version of osm2pgsql, you need to set the PATH accordingly.
### Installing prerequisites on Ubuntu/Debian
@@ -70,8 +67,9 @@ To set up the virtual environment with all necessary packages run:
virtualenv ~/nominatim-dev-venv
~/nominatim-dev-venv/bin/pip install\
psutil 'psycopg[binary]' PyICU SQLAlchemy \
python-dotenv jinja2 pyYAML behave \
mkdocs 'mkdocstrings[python]' mkdocs-gen-files pytest pytest-asyncio flake8 \
python-dotenv jinja2 pyYAML \
mkdocs 'mkdocstrings[python]' mkdocs-gen-files \
pytest pytest-asyncio pytest-bdd flake8 \
types-jinja2 types-markupsafe types-psutil types-psycopg2 \
types-pygments types-pyyaml types-requests types-ujson \
types-urllib3 typing-extensions unicorn falcon starlette \

View File

@@ -43,53 +43,53 @@ The name of the pytest binary depends on your installation.
## BDD Functional Tests (`test/bdd`)
Functional tests are written as BDD instructions. For more information on
the philosophy of BDD testing, see the
[Behave manual](http://pythonhosted.org/behave/philosophy.html).
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.
the philosophy of BDD testing, read the Wikipedia article on
[Behaviour-driven development](https://en.wikipedia.org/wiki/Behavior-driven_development).
### General Usage
To run the functional tests, do
cd test/bdd
behave
pytest test/bdd
The tests can be configured with a set of environment variables (`behave -D key=val`):
The BDD tests create databases for the tests. You can set name of the databases
through configuration variables in your `pytest.ini`:
* `TEMPLATE_DB` - name of template database used as a skeleton for
the test databases (db tests)
* `TEST_DB` - name of test database (db tests)
* `API_TEST_DB` - name of the database containing the API test data (api tests)
* `API_TEST_FILE` - OSM file to be imported into the API test database (api tests)
* `API_ENGINE` - webframe to use for running search queries, same values as
`nominatim serve --engine` parameter
* `DB_HOST` - (optional) hostname of database host
* `DB_PORT` - (optional) port of database on host
* `DB_USER` - (optional) username of database login
* `DB_PASS` - (optional) password for database login
* `REMOVE_TEMPLATE` - if true, the template and API 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.
* `nominatim_test_db` defines the name of the temporary database created for
a single test (default: `test_nominatim`)
* `nominatim_api_test_db` defines the name of the database containing
the API test data, see also below (default: `test_api_nominatim`)
* `nominatim_template_db` defines the name of the template database used
for creating the temporary test databases. It contains some static setup
which usually doesn't change between imports of OSM data
(default: `test_template_nominatim`)
To change other connection parameters for the PostgreSQL database, use
the [libpq enivronment variables](https://www.postgresql.org/docs/current/libpq-envars.html).
Never set a password through these variables. Use a
[password file](https://www.postgresql.org/docs/current/libpq-pgpass.html) instead.
The API test database and the template database are only created once and then
left untouched. This is usually what you want because it speeds up subsequent
runs of BDD tests. If you do change code that has an influence on the content
of these databases, you can run pytest with the `--nominatim-purge` parameter
and the databases will be dropped and recreated from scratch.
When running the BDD tests with make (using `make tests` or `make bdd`), then
the databases will always be purged.
The temporary test database is usually dropped directly after the test, so
it does not take up unnecessary space. If you want to keep the database around,
for example while debugging a specific BDD test, use the parameter
`--nominatim-keep-db`.
Logging can be defined through command line parameters of behave itself. Check
out `behave --help` for details. Also have a look at the 'work-in-progress'
feature of behave which comes in handy when writing new tests.
### API Tests (`test/bdd/api`)
These tests are meant to test the different API endpoints and their parameters.
They require to import several datasets into a test database. This is normally
done automatically during setup of the test. The API test database is then
kept around and reused in subsequent runs of behave. Use `behave -DREMOVE_TEMPLATE`
kept around and reused in subsequent runs of behave. Use `--nominatim-purge`
to force a reimport of the database.
The official test dataset is saved in the file `test/testdb/apidb-test-data.pbf`
@@ -109,12 +109,12 @@ test the correctness of osm2pgsql. Each test will write some data into the `plac
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.
These tests use the template database and create temporary test databases for
each test.
### 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 DB Creation tests, so the same remarks apply.
These tests check that data is imported correctly into the place table.
These tests also use the template database and create temporary test databases
for each test.

View File

@@ -187,7 +187,7 @@ module.MAIN_TAGS_POIS = function (group)
passing_place = group,
street_lamp = 'named',
traffic_signals = 'named'},
historic = {'always',
historic = {'fallback',
yes = group,
no = group},
information = {include_when_tag_present('tourism', 'information'),
@@ -196,6 +196,7 @@ module.MAIN_TAGS_POIS = function (group)
trail_blaze = 'never'},
junction = {'fallback',
no = group},
landuse = {cemetery = 'always'},
leisure = {'always',
nature_reserve = 'fallback',
swimming_pool = 'named',
@@ -229,6 +230,7 @@ module.MAIN_TAGS_POIS = function (group)
shop = {'always',
no = group},
tourism = {'always',
attraction = 'fallback',
no = group,
yes = group,
information = exclude_when_key_present('information')},
@@ -330,7 +332,7 @@ module.NAME_TAGS.core = {main = {'name', 'name:*',
}
module.NAME_TAGS.address = {house = {'addr:housename'}}
module.NAME_TAGS.poi = group_merge({main = {'brand'},
extra = {'iata', 'icao'}},
extra = {'iata', 'icao', 'faa'}},
module.NAME_TAGS.core)
-- Address tagging

View File

@@ -944,7 +944,7 @@ kp:
# South Korea (대한민국)
kr:
partition: 49
languages: ko, en
languages: ko
names: !include country-names/kr.yaml
postcode:
pattern: "ddddd"

View File

@@ -192,6 +192,13 @@ NOMINATIM_REQUEST_TIMEOUT=60
# to geocode" instead.
NOMINATIM_SEARCH_WITHIN_COUNTRIES=False
# Specifies the order in which different name tags are used.
# The values in this list determine the preferred order of name variants,
# including language-specific names.
# Comma-separated list, where :XX stands for language-specific tags
# (e.g. name:en) and no :XX stands for general tags (e.g. name).
NOMINATIM_OUTPUT_NAMES=name:XX,name,brand,official_name:XX,short_name:XX,official_name,short_name,ref
### Log settings
#
# The following options allow to enable logging of API requests.

View File

@@ -8,6 +8,7 @@
Helper functions for localizing names of results.
"""
from typing import Mapping, List, Optional
from .config import Configuration
import re
@@ -20,14 +21,18 @@ class Locales:
"""
def __init__(self, langs: Optional[List[str]] = None):
self.config = Configuration(None)
self.languages = langs or []
self.name_tags: List[str] = []
# Build the list of supported tags. It is currently hard-coded.
self._add_lang_tags('name')
self._add_tags('name', 'brand')
self._add_lang_tags('official_name', 'short_name')
self._add_tags('official_name', 'short_name', 'ref')
parts = self.config.OUTPUT_NAMES.split(',')
for part in parts:
part = part.strip()
if part.endswith(":XX"):
self._add_lang_tags(part[:-3])
else:
self._add_tags(part)
def __bool__(self) -> bool:
return len(self.languages) > 0

View File

@@ -342,7 +342,8 @@ HTML_HEADER: str = """<!DOCTYPE html>
<title>Nominatim - Debug</title>
<style>
""" + \
(HtmlFormatter(nobackground=True).get_style_defs('.highlight') if CODE_HIGHLIGHT else '') + \
(HtmlFormatter(nobackground=True).get_style_defs('.highlight') # type: ignore[no-untyped-call]
if CODE_HIGHLIGHT else '') + \
"""
h2 { font-size: x-large }

View File

@@ -0,0 +1,52 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
This preprocessor replaces values in a given input based on pre-defined regex rules.
Arguments:
pattern: Regex pattern to be applied on the input
replace: The string that it is to be replaced with
"""
from typing import List
import re
from .config import QueryConfig
from .base import QueryProcessingFunc
from ..search.query import Phrase
class _GenericPreprocessing:
"""Perform replacements to input phrases using custom regex patterns."""
def __init__(self, config: QueryConfig) -> None:
"""Initialise the _GenericPreprocessing class with patterns from the ICU config file."""
self.config = config
match_patterns = self.config.get('replacements', 'Key not found')
self.compiled_patterns = [
(re.compile(item['pattern']), item['replace']) for item in match_patterns
]
def split_phrase(self, phrase: Phrase) -> Phrase:
"""This function performs replacements on the given text using regex patterns."""
for item in self.compiled_patterns:
phrase.text = item[0].sub(item[1], phrase.text)
return phrase
def __call__(self, phrases: List[Phrase]) -> List[Phrase]:
"""
Return the final Phrase list.
Returns an empty list if there is nothing left after split_phrase.
"""
result = [p for p in map(self.split_phrase, phrases) if p.text.strip()]
return result
def create(config: QueryConfig) -> QueryProcessingFunc:
""" Create a function for generic preprocessing."""
return _GenericPreprocessing(config)

View File

@@ -2,7 +2,7 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Conversion from token assignment to an abstract DB search.
@@ -146,7 +146,7 @@ class SearchBuilder:
if address:
sdata.lookups = [dbf.FieldLookup('nameaddress_vector',
[t.token for r in address
for t in self.query.get_partials_list(r)],
for t in self.query.iter_partials(r)],
lookups.Restrict)]
yield dbs.PostcodeSearch(penalty, sdata)
@@ -159,7 +159,7 @@ class SearchBuilder:
expected_count = sum(t.count for t in hnrs)
partials = {t.token: t.addr_count for trange in address
for t in self.query.get_partials_list(trange)}
for t in self.query.iter_partials(trange)}
if not partials:
# can happen when none of the partials is indexed
@@ -203,9 +203,9 @@ class SearchBuilder:
are and tries to find a lookup that optimizes index use.
"""
penalty = 0.0 # extra penalty
name_partials = {t.token: t for t in self.query.get_partials_list(name)}
name_partials = {t.token: t for t in self.query.iter_partials(name)}
addr_partials = [t for r in address for t in self.query.get_partials_list(r)]
addr_partials = [t for r in address for t in self.query.iter_partials(r)]
addr_tokens = list({t.token for t in addr_partials})
exp_count = min(t.count for t in name_partials.values()) / (3**(len(name_partials) - 1))
@@ -220,7 +220,7 @@ class SearchBuilder:
if name_fulls:
fulls_count = sum(t.count for t in name_fulls)
if fulls_count < 50000 or addr_count < 50000:
if fulls_count < 80000 or addr_count < 50000:
yield penalty, fulls_count / (2**len(addr_tokens)), \
self.get_full_name_ranking(name_fulls, addr_partials,
fulls_count > 30000 / max(1, len(addr_tokens)))
@@ -282,8 +282,7 @@ class SearchBuilder:
ranks = [dbf.RankedTokens(t.penalty, [t.token]) for t in name_fulls]
ranks.sort(key=lambda r: r.penalty)
# Fallback, sum of penalty for partials
name_partials = self.query.get_partials_list(trange)
default = sum(t.penalty for t in name_partials) + 0.2
default = sum(t.penalty for t in self.query.iter_partials(trange)) + 0.2
return dbf.FieldRanking(db_field, default, ranks)
def get_addr_ranking(self, trange: qmod.TokenRange) -> dbf.FieldRanking:
@@ -296,35 +295,35 @@ class SearchBuilder:
while todo:
neglen, pos, rank = heapq.heappop(todo)
# partial node
partial = self.query.nodes[pos].partial
if partial is not None:
if pos + 1 < trange.end:
penalty = rank.penalty + partial.penalty \
+ PENALTY_WORDCHANGE[self.query.nodes[pos + 1].btype]
heapq.heappush(todo, (neglen - 1, pos + 1,
dbf.RankedTokens(penalty, rank.tokens)))
else:
ranks.append(dbf.RankedTokens(rank.penalty + partial.penalty,
rank.tokens))
# full words
for tlist in self.query.nodes[pos].starting:
if tlist.ttype in (qmod.TOKEN_PARTIAL, qmod.TOKEN_WORD):
if tlist.ttype == qmod.TOKEN_WORD:
if tlist.end < trange.end:
chgpenalty = PENALTY_WORDCHANGE[self.query.nodes[tlist.end].btype]
if tlist.ttype == qmod.TOKEN_PARTIAL:
penalty = rank.penalty + chgpenalty \
+ max(t.penalty for t in tlist.tokens)
for t in tlist.tokens:
heapq.heappush(todo, (neglen - 1, tlist.end,
dbf.RankedTokens(penalty, rank.tokens)))
else:
for t in tlist.tokens:
heapq.heappush(todo, (neglen - 1, tlist.end,
rank.with_token(t, chgpenalty)))
rank.with_token(t, chgpenalty)))
elif tlist.end == trange.end:
if tlist.ttype == qmod.TOKEN_PARTIAL:
ranks.append(dbf.RankedTokens(rank.penalty
+ max(t.penalty for t in tlist.tokens),
rank.tokens))
else:
ranks.extend(rank.with_token(t, 0.0) for t in tlist.tokens)
if len(ranks) >= 10:
# Too many variants, bail out and only add
# Worst-case Fallback: sum of penalty of partials
name_partials = self.query.get_partials_list(trange)
default = sum(t.penalty for t in name_partials) + 0.2
ranks.append(dbf.RankedTokens(rank.penalty + default, []))
# Bail out of outer loop
todo.clear()
break
ranks.extend(rank.with_token(t, 0.0) for t in tlist.tokens)
if len(ranks) >= 10:
# Too many variants, bail out and only add
# Worst-case Fallback: sum of penalty of partials
default = sum(t.penalty for t in self.query.iter_partials(trange)) + 0.2
ranks.append(dbf.RankedTokens(rank.penalty + default, []))
# Bail out of outer loop
break
ranks.sort(key=lambda r: len(r.tokens))
default = ranks[0].penalty + 0.3

View File

@@ -2,7 +2,7 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Public interface to the search code.
@@ -50,6 +50,9 @@ class ForwardGeocoder:
self.query_analyzer = await make_query_analyzer(self.conn)
query = await self.query_analyzer.analyze_query(phrases)
query.compute_direction_penalty()
log().var_dump('Query direction penalty',
lambda: f"[{'LR' if query.dir_penalty < 0 else 'RL'}] {query.dir_penalty}")
searches: List[AbstractSearch] = []
if query.num_token_slots() > 0:
@@ -115,17 +118,20 @@ class ForwardGeocoder:
""" Remove badly matching results, sort by ranking and
limit to the configured number of results.
"""
if results:
results.sort(key=lambda r: (r.ranking, 0 if r.bbox is None else -r.bbox.area))
min_rank = results[0].rank_search
min_ranking = results[0].ranking
results = SearchResults(r for r in results
if (r.ranking + 0.03 * (r.rank_search - min_rank)
< min_ranking + 0.5))
results.sort(key=lambda r: (r.ranking, 0 if r.bbox is None else -r.bbox.area))
results = SearchResults(results[:self.limit])
final = SearchResults()
min_rank = results[0].rank_search
min_ranking = results[0].ranking
return results
for r in results:
if r.ranking + 0.03 * (r.rank_search - min_rank) < min_ranking + 0.5:
final.append(r)
min_rank = min(r.rank_search, min_rank)
if len(final) == self.limit:
break
return final
def rerank_by_query(self, query: QueryStruct, results: SearchResults) -> None:
""" Adjust the accuracy of the localized result according to how well
@@ -150,17 +156,16 @@ class ForwardGeocoder:
if not words:
continue
for qword in qwords:
wdist = max(difflib.SequenceMatcher(a=qword, b=w).quick_ratio() for w in words)
if wdist < 0.5:
distance += len(qword)
else:
distance += (1.0 - wdist) * len(qword)
# only add distance penalty if there is no perfect match
if qword not in words:
wdist = max(difflib.SequenceMatcher(a=qword, b=w).quick_ratio() for w in words)
distance += len(qword) if wdist < 0.4 else 1
# Compensate for the fact that country names do not get a
# match penalty yet by the tokenizer.
# Temporary hack that needs to be removed!
if result.rank_address == 4:
distance *= 2
result.accuracy += distance * 0.4 / sum(len(w) for w in qwords)
result.accuracy += distance * 0.3 / sum(len(w) for w in qwords)
async def lookup_pois(self, categories: List[Tuple[str, str]],
phrases: List[Phrase]) -> SearchResults:
@@ -208,9 +213,10 @@ class ForwardGeocoder:
results = self.pre_filter_results(results)
await add_result_details(self.conn, results, self.params)
log().result_dump('Preliminary Results', ((r.accuracy, r) for r in results))
self.rerank_by_query(query, results)
log().result_dump('Results after reranking', ((r.accuracy, r) for r in results))
results = self.sort_and_cut_results(results)
if len(results) > 1:
self.rerank_by_query(query, results)
log().result_dump('Results after reranking', ((r.accuracy, r) for r in results))
results = self.sort_and_cut_results(results)
log().result_dump('Final Results', ((r.accuracy, r) for r in results))
return results

View File

@@ -2,7 +2,7 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Implementation of query analysis for the ICU tokenizer.
@@ -267,32 +267,47 @@ class ICUQueryAnalyzer(AbstractQueryAnalyzer):
def rerank_tokens(self, query: qmod.QueryStruct) -> None:
""" Add penalties to tokens that depend on presence of other token.
"""
for i, node, tlist in query.iter_token_lists():
if tlist.ttype == qmod.TOKEN_POSTCODE:
tlen = len(cast(ICUToken, tlist.tokens[0]).word_token)
for repl in node.starting:
if repl.end == tlist.end and repl.ttype != qmod.TOKEN_POSTCODE \
and (repl.ttype != qmod.TOKEN_HOUSENUMBER or tlen > 4):
repl.add_penalty(0.39)
elif (tlist.ttype == qmod.TOKEN_HOUSENUMBER
and len(tlist.tokens[0].lookup_word) <= 3):
if any(c.isdigit() for c in tlist.tokens[0].lookup_word):
for repl in node.starting:
if repl.end == tlist.end and repl.ttype != qmod.TOKEN_HOUSENUMBER:
repl.add_penalty(0.5 - tlist.tokens[0].penalty)
elif tlist.ttype not in (qmod.TOKEN_COUNTRY, qmod.TOKEN_PARTIAL):
norm = ' '.join(n.term_normalized for n in query.nodes[i + 1:tlist.end + 1]
if n.btype != qmod.BREAK_TOKEN)
if not norm:
# Can happen when the token only covers a partial term
norm = query.nodes[i + 1].term_normalized
for token in tlist.tokens:
cast(ICUToken, token).rematch(norm)
for start, end, tlist in query.iter_tokens_by_edge():
if len(tlist) > 1:
# If it looks like a Postcode, give preference.
if qmod.TOKEN_POSTCODE in tlist:
for ttype, tokens in tlist.items():
if ttype != qmod.TOKEN_POSTCODE and \
(ttype != qmod.TOKEN_HOUSENUMBER or
start + 1 > end or
len(query.nodes[end].term_lookup) > 4):
for token in tokens:
token.penalty += 0.39
# If it looks like a simple housenumber, prefer that.
if qmod.TOKEN_HOUSENUMBER in tlist:
hnr_lookup = tlist[qmod.TOKEN_HOUSENUMBER][0].lookup_word
if len(hnr_lookup) <= 3 and any(c.isdigit() for c in hnr_lookup):
penalty = 0.5 - tlist[qmod.TOKEN_HOUSENUMBER][0].penalty
for ttype, tokens in tlist.items():
if ttype != qmod.TOKEN_HOUSENUMBER:
for token in tokens:
token.penalty += penalty
# rerank tokens against the normalized form
norm = ' '.join(n.term_normalized for n in query.nodes[start + 1:end + 1]
if n.btype != qmod.BREAK_TOKEN)
if not norm:
# Can happen when the token only covers a partial term
norm = query.nodes[start + 1].term_normalized
for ttype, tokens in tlist.items():
if ttype != qmod.TOKEN_COUNTRY:
for token in tokens:
cast(ICUToken, token).rematch(norm)
def _dump_word_tokens(query: qmod.QueryStruct) -> Iterator[List[Any]]:
yield ['type', 'from', 'to', 'token', 'word_token', 'lookup_word', 'penalty', 'count', 'info']
for i, node in enumerate(query.nodes):
if node.partial is not None:
t = cast(ICUToken, node.partial)
yield [qmod.TOKEN_PARTIAL, str(i), str(i + 1), t.token,
t.word_token, t.lookup_word, t.penalty, t.count, t.info]
for tlist in node.starting:
for token in tlist.tokens:
t = cast(ICUToken, token)

View File

@@ -2,7 +2,7 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Datastructures for a tokenized query.
@@ -12,6 +12,17 @@ from abc import ABC, abstractmethod
from collections import defaultdict
import dataclasses
# Precomputed denominator for the computation of the linear regression slope
# used to determine the query direction.
# The x value for the regression computation will be the position of the
# token in the query. Thus we know the x values will be [0, query length).
# As the denominator only depends on the x values, we can pre-compute here
# the denominatior to use for a given query length.
# Note that query length of two or less is special cased and will not use
# the values from this array. Thus it is not a problem that they are 0.
LINFAC = [i * (sum(si * si for si in range(i)) - (i - 1) * i * (i - 1) / 4)
for i in range(50)]
BreakType = str
""" Type of break between tokens.
@@ -183,15 +194,32 @@ class QueryNode:
""" Penalty for the break at this node.
"""
term_lookup: str
""" Transliterated term following this node.
""" Transliterated term ending at this node.
"""
term_normalized: str
""" Normalised form of term following this node.
""" Normalised form of term ending at this node.
When the token resulted from a split during transliteration,
then this string contains the complete source term.
"""
starting: List[TokenList] = dataclasses.field(default_factory=list)
""" List of all full tokens starting at this node.
"""
partial: Optional[Token] = None
""" Base token going to the next node.
May be None when the query has parts for which no words are known.
Note that the query may still be parsable when there are other
types of tokens spanning over the gap.
"""
def name_address_ratio(self) -> float:
""" Return the propability that the partial token belonging to
this node forms part of a name (as opposed of part of the address).
"""
if self.partial is None:
return 0.5
return self.partial.count / (self.partial.count + self.partial.addr_count)
def adjust_break(self, btype: BreakType, penalty: float) -> None:
""" Change the break type and penalty for this node.
@@ -234,12 +262,20 @@ class QueryStruct:
need to be direct neighbours. Thus the query is represented as a
directed acyclic graph.
A query also has a direction penalty 'dir_penalty'. This describes
the likelyhood if the query should be read from left-to-right or
vice versa. A negative 'dir_penalty' should be read as a penalty on
right-to-left reading, while a positive value represents a penalty
for left-to-right reading. The default value is 0, which is equivalent
to having no information about the reading.
When created, a query contains a single node: the start of the
query. Further nodes can be added by appending to 'nodes'.
"""
def __init__(self, source: List[Phrase]) -> None:
self.source = source
self.dir_penalty = 0.0
self.nodes: List[QueryNode] = \
[QueryNode(BREAK_START, source[0].ptype if source else PHRASE_ANY,
0.0, '', '')]
@@ -269,37 +305,63 @@ class QueryStruct:
be added to, then the token is silently dropped.
"""
snode = self.nodes[trange.start]
full_phrase = snode.btype in (BREAK_START, BREAK_PHRASE)\
and self.nodes[trange.end].btype in (BREAK_PHRASE, BREAK_END)
if _phrase_compatible_with(snode.ptype, ttype, full_phrase):
tlist = snode.get_tokens(trange.end, ttype)
if tlist is None:
snode.starting.append(TokenList(trange.end, ttype, [token]))
else:
tlist.append(token)
if ttype == TOKEN_PARTIAL:
assert snode.partial is None
if _phrase_compatible_with(snode.ptype, TOKEN_PARTIAL, False):
snode.partial = token
else:
full_phrase = snode.btype in (BREAK_START, BREAK_PHRASE)\
and self.nodes[trange.end].btype in (BREAK_PHRASE, BREAK_END)
if _phrase_compatible_with(snode.ptype, ttype, full_phrase):
tlist = snode.get_tokens(trange.end, ttype)
if tlist is None:
snode.starting.append(TokenList(trange.end, ttype, [token]))
else:
tlist.append(token)
def compute_direction_penalty(self) -> None:
""" Recompute the direction probability from the partial tokens
of each node.
"""
n = len(self.nodes) - 1
if n == 1 or n >= 50:
self.dir_penalty = 0
elif n == 2:
self.dir_penalty = (self.nodes[1].name_address_ratio()
- self.nodes[0].name_address_ratio()) / 3
else:
ratios = [n.name_address_ratio() for n in self.nodes[:-1]]
self.dir_penalty = (n * sum(i * r for i, r in enumerate(ratios))
- sum(ratios) * n * (n - 1) / 2) / LINFAC[n]
def get_tokens(self, trange: TokenRange, ttype: TokenType) -> List[Token]:
""" Get the list of tokens of a given type, spanning the given
nodes. The nodes must exist. If no tokens exist, an
empty list is returned.
Cannot be used to get the partial token.
"""
assert ttype != TOKEN_PARTIAL
return self.nodes[trange.start].get_tokens(trange.end, ttype) or []
def get_partials_list(self, trange: TokenRange) -> List[Token]:
""" Create a list of partial tokens between the given nodes.
The list is composed of the first token of type PARTIAL
going to the subsequent node. Such PARTIAL tokens are
assumed to exist.
def iter_partials(self, trange: TokenRange) -> Iterator[Token]:
""" Iterate over the partial tokens between the given nodes.
Missing partials are ignored.
"""
return [next(iter(self.get_tokens(TokenRange(i, i+1), TOKEN_PARTIAL)))
for i in range(trange.start, trange.end)]
return (n.partial for n in self.nodes[trange.start:trange.end] if n.partial is not None)
def iter_token_lists(self) -> Iterator[Tuple[int, QueryNode, TokenList]]:
""" Iterator over all token lists in the query.
def iter_tokens_by_edge(self) -> Iterator[Tuple[int, int, Dict[TokenType, List[Token]]]]:
""" Iterator over all tokens except partial ones grouped by edge.
Returns the start and end node indexes and a dictionary
of list of tokens by token type.
"""
for i, node in enumerate(self.nodes):
by_end: Dict[int, Dict[TokenType, List[Token]]] = defaultdict(dict)
for tlist in node.starting:
yield i, node, tlist
by_end[tlist.end][tlist.ttype] = tlist.tokens
for end, endlist in by_end.items():
yield i, end, endlist
def find_lookup_word_by_id(self, token: int) -> str:
""" Find the first token with the given token ID and return
@@ -308,6 +370,8 @@ class QueryStruct:
debugging.
"""
for node in self.nodes:
if node.partial is not None and node.partial.token == token:
return f"[P]{node.partial.lookup_word}"
for tlist in node.starting:
for t in tlist.tokens:
if t.token == token:
@@ -339,16 +403,18 @@ class QueryStruct:
words: Dict[str, List[TokenRange]] = defaultdict(list)
for first in range(start, endpos - 1):
word = self.nodes[first + 1].term_lookup
for first, first_node in enumerate(self.nodes[start + 1:endpos], start):
word = first_node.term_lookup
penalty = base_penalty
words[word].append(TokenRange(first, first + 1, penalty=penalty))
if self.nodes[first + 1].btype != BREAK_PHRASE:
for last in range(first + 2, min(first + 20, endpos)):
word = ' '.join((word, self.nodes[last].term_lookup))
penalty += self.nodes[last - 1].penalty
if first_node.btype != BREAK_PHRASE:
penalty += first_node.penalty
max_last = min(first + 20, endpos)
for last, last_node in enumerate(self.nodes[first + 2:max_last], first + 2):
word = ' '.join((word, last_node.term_lookup))
words[word].append(TokenRange(first, last, penalty=penalty))
if self.nodes[last].btype == BREAK_PHRASE:
if last_node.btype == BREAK_PHRASE:
break
penalty += last_node.penalty
return words

View File

@@ -286,8 +286,12 @@ class _TokenSequence:
log().var_dump('skip forward', (base.postcode, first))
return
penalty = self.penalty
if not base.country and self.direction == 1 and query.dir_penalty > 0:
penalty += query.dir_penalty
log().comment('first word = name')
yield dataclasses.replace(base, penalty=self.penalty,
yield dataclasses.replace(base, penalty=penalty,
name=first, address=base.address[1:])
# To paraphrase:
@@ -300,14 +304,15 @@ class _TokenSequence:
or (query.nodes[first.start].ptype != qmod.PHRASE_ANY):
return
penalty = self.penalty
# Penalty for:
# * <name>, <street>, <housenumber> , ...
# * queries that are comma-separated
if (base.housenumber and base.housenumber > first) or len(query.source) > 1:
penalty += 0.25
if self.direction == 0 and query.dir_penalty > 0:
penalty += query.dir_penalty
for i in range(first.start + 1, first.end):
name, addr = first.split(i)
log().comment(f'split first word = name ({i - first.start})')
@@ -326,9 +331,13 @@ class _TokenSequence:
log().var_dump('skip backward', (base.postcode, last))
return
penalty = self.penalty
if not base.country and self.direction == -1 and query.dir_penalty < 0:
penalty -= query.dir_penalty
if self.direction == -1 or len(base.address) > 1 or base.postcode:
log().comment('last word = name')
yield dataclasses.replace(base, penalty=self.penalty,
yield dataclasses.replace(base, penalty=penalty,
name=last, address=base.address[:-1])
# To paraphrase:
@@ -341,12 +350,14 @@ class _TokenSequence:
or (query.nodes[last.start].ptype != qmod.PHRASE_ANY):
return
penalty = self.penalty
if base.housenumber and base.housenumber < last:
penalty += 0.4
if len(query.source) > 1:
penalty += 0.25
if self.direction == 0 and query.dir_penalty < 0:
penalty -= query.dir_penalty
for i in range(last.start + 1, last.end):
addr, name = last.split(i)
log().comment(f'split last word = name ({i - last.start})')
@@ -379,11 +390,11 @@ class _TokenSequence:
if base.postcode and base.postcode.start == 0:
self.penalty += 0.1
# Right-to-left reading of the address
# Left-to-right reading of the address
if self.direction != -1:
yield from self._get_assignments_address_forward(base, query)
# Left-to-right reading of the address
# Right-to-left reading of the address
if self.direction != 1:
yield from self._get_assignments_address_backward(base, query)
@@ -409,11 +420,22 @@ def yield_token_assignments(query: qmod.QueryStruct) -> Iterator[TokenAssignment
node = query.nodes[state.end_pos]
for tlist in node.starting:
newstate = state.advance(tlist.ttype, tlist.end, node.btype)
if newstate is not None:
if newstate.end_pos == query.num_token_slots():
if newstate.recheck_sequence():
log().var_dump('Assignment', newstate)
yield from newstate.get_assignments(query)
elif not newstate.is_final():
todo.append(newstate)
yield from _append_state_to_todo(
query, todo,
state.advance(tlist.ttype, tlist.end, node.btype))
if node.partial is not None:
yield from _append_state_to_todo(
query, todo,
state.advance(qmod.TOKEN_PARTIAL, state.end_pos + 1, node.btype))
def _append_state_to_todo(query: qmod.QueryStruct, todo: List[_TokenSequence],
newstate: Optional[_TokenSequence]) -> Iterator[TokenAssignment]:
if newstate is not None:
if newstate.end_pos == query.num_token_slots():
if newstate.recheck_sequence():
log().var_dump('Assignment', newstate)
yield from newstate.get_assignments(query)
elif not newstate.is_final():
todo.append(newstate)

View File

@@ -143,7 +143,7 @@ def get_application(project_dir: Path,
log_file = config.LOG_FILE
if log_file:
middleware.append(Middleware(FileLoggingMiddleware, file_name=log_file))
middleware.append(Middleware(FileLoggingMiddleware, file_name=log_file)) # type: ignore
exceptions: Dict[Any, Callable[[Request, Exception], Awaitable[Response]]] = {
TimeoutError: timeout_error,

View File

@@ -122,15 +122,18 @@ class IsAddressPoint(sa.sql.functions.GenericFunction[Any]):
def __init__(self, table: sa.Table) -> None:
super().__init__(table.c.rank_address,
table.c.housenumber, table.c.name)
table.c.housenumber, table.c.name, table.c.address)
@compiles(IsAddressPoint)
def default_is_address_point(element: IsAddressPoint,
compiler: 'sa.Compiled', **kw: Any) -> str:
rank, hnr, name = list(element.clauses)
return "(%s = 30 AND (%s IS NOT NULL OR %s ? 'addr:housename'))" % (
rank, hnr, name, address = list(element.clauses)
return "(%s = 30 AND (%s IS NULL OR NOT %s ? '_inherited')" \
" AND (%s IS NOT NULL OR %s ? 'addr:housename'))" % (
compiler.process(rank, **kw),
compiler.process(address, **kw),
compiler.process(address, **kw),
compiler.process(hnr, **kw),
compiler.process(name, **kw))
@@ -138,9 +141,11 @@ def default_is_address_point(element: IsAddressPoint,
@compiles(IsAddressPoint, 'sqlite')
def sqlite_is_address_point(element: IsAddressPoint,
compiler: 'sa.Compiled', **kw: Any) -> str:
rank, hnr, name = list(element.clauses)
return "(%s = 30 AND coalesce(%s, json_extract(%s, '$.addr:housename')) IS NOT NULL)" % (
rank, hnr, name, address = list(element.clauses)
return "(%s = 30 AND json_extract(%s, '$._inherited') IS NULL" \
" AND coalesce(%s, json_extract(%s, '$.addr:housename')) IS NOT NULL)" % (
compiler.process(rank, **kw),
compiler.process(address, **kw),
compiler.process(hnr, **kw),
compiler.process(name, **kw))

View File

@@ -84,8 +84,9 @@ def format_base_json(results: Union[ReverseResults, SearchResults],
_write_osm_id(out, result.osm_object)
out.keyval('lat', f"{result.centroid.lat}")\
.keyval('lon', f"{result.centroid.lon}")\
# lat and lon must be string values
out.keyval('lat', f"{result.centroid.lat:0.7f}")\
.keyval('lon', f"{result.centroid.lon:0.7f}")\
.keyval(class_label, result.category[0])\
.keyval('type', result.category[1])\
.keyval('place_rank', result.rank_search)\
@@ -112,6 +113,7 @@ def format_base_json(results: Union[ReverseResults, SearchResults],
if options.get('namedetails', False):
out.keyval('namedetails', result.names)
# must be string values
bbox = cl.bbox_from_result(result)
out.key('boundingbox').start_array()\
.value(f"{bbox.minlat:0.7f}").next()\

View File

@@ -90,7 +90,7 @@ def format_base_xml(results: Union[ReverseResults, SearchResults],
result will be output, otherwise a list.
"""
root = ET.Element(xml_root_tag)
root.set('timestamp', dt.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S +00:00'))
root.set('timestamp', dt.datetime.now(dt.timezone.utc).strftime('%a, %d %b %Y %H:%M:%S +00:00'))
root.set('attribution', cl.OSM_ATTRIBUTION)
for k, v in xml_extra_info.items():
root.set(k, v)

View File

@@ -136,6 +136,7 @@ class NominatimArgs:
import_from_wiki: bool
import_from_csv: Optional[str]
no_replace: bool
min: int
# Arguments to all query functions
format: str

View File

@@ -58,6 +58,8 @@ class ImportSpecialPhrases:
help='Import special phrases from a CSV file')
group.add_argument('--no-replace', action='store_true',
help='Keep the old phrases and only add the new ones')
group.add_argument('--min', type=int, default=0,
help='Restrict special phrases by minimum occurance')
def run(self, args: NominatimArgs) -> int:
@@ -82,7 +84,9 @@ class ImportSpecialPhrases:
tokenizer = tokenizer_factory.get_tokenizer_for_db(args.config)
should_replace = not args.no_replace
min = args.min
with connect(args.config.get_libpq_dsn()) as db_connection:
SPImporter(
args.config, db_connection, loader
).import_phrases(tokenizer, should_replace)
).import_phrases(tokenizer, should_replace, min)

View File

@@ -2,7 +2,7 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Abstract class definitions for tokenizers. These base classes are here
@@ -10,7 +10,6 @@ mainly for documentation purposes.
"""
from abc import ABC, abstractmethod
from typing import List, Tuple, Dict, Any, Optional, Iterable
from pathlib import Path
from ..typing import Protocol
from ..config import Configuration
@@ -38,7 +37,7 @@ class AbstractAnalyzer(ABC):
"""
@abstractmethod
def get_word_token_info(self, words: List[str]) -> List[Tuple[str, str, int]]:
def get_word_token_info(self, words: List[str]) -> List[Tuple[str, str, Optional[int]]]:
""" Return token information for the given list of words.
The function is used for testing and debugging only
@@ -232,6 +231,6 @@ class TokenizerModule(Protocol):
own tokenizer.
"""
def create(self, dsn: str, data_dir: Path) -> AbstractTokenizer:
def create(self, dsn: str) -> AbstractTokenizer:
""" Factory for new tokenizers.
"""

View File

@@ -2,7 +2,7 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for creating a tokenizer or initialising the right one for an
@@ -52,19 +52,10 @@ def create_tokenizer(config: Configuration, init_db: bool = True,
if module_name is None:
module_name = config.TOKENIZER
# Create the directory for the tokenizer data
assert config.project_dir is not None
basedir = config.project_dir / 'tokenizer'
if not basedir.exists():
basedir.mkdir()
elif not basedir.is_dir():
LOG.fatal("Tokenizer directory '%s' cannot be created.", basedir)
raise UsageError("Tokenizer setup failed.")
# Import and initialize the tokenizer.
tokenizer_module = _import_tokenizer(module_name)
tokenizer = tokenizer_module.create(config.get_libpq_dsn(), basedir)
tokenizer = tokenizer_module.create(config.get_libpq_dsn())
tokenizer.init_new_db(config, init_db=init_db)
with connect(config.get_libpq_dsn()) as conn:
@@ -79,12 +70,6 @@ def get_tokenizer_for_db(config: Configuration) -> AbstractTokenizer:
The function looks up the appropriate tokenizer in the database
and initialises it.
"""
assert config.project_dir is not None
basedir = config.project_dir / 'tokenizer'
if not basedir.is_dir():
# Directory will be repopulated by tokenizer below.
basedir.mkdir()
with connect(config.get_libpq_dsn()) as conn:
name = properties.get_property(conn, 'tokenizer')
@@ -94,7 +79,7 @@ def get_tokenizer_for_db(config: Configuration) -> AbstractTokenizer:
tokenizer_module = _import_tokenizer(name)
tokenizer = tokenizer_module.create(config.get_libpq_dsn(), basedir)
tokenizer = tokenizer_module.create(config.get_libpq_dsn())
tokenizer.init_from_project(config)
return tokenizer

View File

@@ -2,7 +2,7 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Tokenizer implementing normalisation as used before Nominatim 4 but using
@@ -12,7 +12,6 @@ from typing import Optional, Sequence, List, Tuple, Mapping, Any, cast, \
Dict, Set, Iterable
import itertools
import logging
from pathlib import Path
from psycopg.types.json import Jsonb
from psycopg import sql as pysql
@@ -38,10 +37,10 @@ WORD_TYPES = (('country_names', 'C'),
('housenumbers', 'H'))
def create(dsn: str, data_dir: Path) -> 'ICUTokenizer':
def create(dsn: str) -> 'ICUTokenizer':
""" Create a new instance of the tokenizer provided by this module.
"""
return ICUTokenizer(dsn, data_dir)
return ICUTokenizer(dsn)
class ICUTokenizer(AbstractTokenizer):
@@ -50,9 +49,8 @@ class ICUTokenizer(AbstractTokenizer):
normalization routines in Nominatim 3.
"""
def __init__(self, dsn: str, data_dir: Path) -> None:
def __init__(self, dsn: str) -> None:
self.dsn = dsn
self.data_dir = data_dir
self.loader: Optional[ICURuleLoader] = None
def init_new_db(self, config: Configuration, init_db: bool = True) -> None:
@@ -340,7 +338,7 @@ class ICUNameAnalyzer(AbstractAnalyzer):
"""
return cast(str, self.token_analysis.normalizer.transliterate(name)).strip()
def get_word_token_info(self, words: Sequence[str]) -> List[Tuple[str, str, int]]:
def get_word_token_info(self, words: Sequence[str]) -> List[Tuple[str, str, Optional[int]]]:
""" Return token information for the given list of words.
If a word starts with # it is assumed to be a full name
otherwise is a partial name.
@@ -364,11 +362,11 @@ class ICUNameAnalyzer(AbstractAnalyzer):
cur.execute("""SELECT word_token, word_id
FROM word WHERE word_token = ANY(%s) and type = 'W'
""", (list(full_tokens.values()),))
full_ids = {r[0]: r[1] for r in cur}
full_ids = {r[0]: cast(int, r[1]) for r in cur}
cur.execute("""SELECT word_token, word_id
FROM word WHERE word_token = ANY(%s) and type = 'w'""",
(list(partial_tokens.values()),))
part_ids = {r[0]: r[1] for r in cur}
part_ids = {r[0]: cast(int, r[1]) for r in cur}
return [(k, v, full_ids.get(v, None)) for k, v in full_tokens.items()] \
+ [(k, v, part_ids.get(v, None)) for k, v in partial_tokens.items()]

View File

@@ -127,7 +127,7 @@ def import_osm_data(osm_files: Union[Path, Sequence[Path]],
fsize += os.stat(str(fname)).st_size
else:
fsize = os.stat(str(osm_files)).st_size
options['osm2pgsql_cache'] = int(min((mem.available + mem.cached) * 0.75,
options['osm2pgsql_cache'] = int(min((mem.available + getattr(mem, 'cached', 0)) * 0.75,
fsize * 2) / 1024 / 1024) + 1
run_osm2pgsql(options)

View File

@@ -2,7 +2,7 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Functions for importing, updating and otherwise maintaining the table
@@ -64,11 +64,15 @@ class _PostcodeCollector:
if normalized:
self.collected[normalized] += (x, y)
def commit(self, conn: Connection, analyzer: AbstractAnalyzer, project_dir: Path) -> None:
""" Update postcodes for the country from the postcodes selected so far
as well as any externally supplied postcodes.
def commit(self, conn: Connection, analyzer: AbstractAnalyzer,
project_dir: Optional[Path]) -> None:
""" Update postcodes for the country from the postcodes selected so far.
When 'project_dir' is set, then any postcode files found in this
directory are taken into account as well.
"""
self._update_from_external(analyzer, project_dir)
if project_dir is not None:
self._update_from_external(analyzer, project_dir)
to_add, to_delete, to_update = self._compute_changes(conn)
LOG.info("Processing country '%s' (%s added, %s deleted, %s updated).",
@@ -170,7 +174,7 @@ class _PostcodeCollector:
return None
def update_postcodes(dsn: str, project_dir: Path, tokenizer: AbstractTokenizer) -> None:
def update_postcodes(dsn: str, project_dir: Optional[Path], tokenizer: AbstractTokenizer) -> None:
""" Update the table of artificial postcodes.
Computes artificial postcode centroids from the placex table,

View File

@@ -16,7 +16,6 @@
from typing import Iterable, Tuple, Mapping, Sequence, Optional, Set
import logging
import re
from psycopg.sql import Identifier, SQL
from ...typing import Protocol
@@ -65,7 +64,32 @@ class SPImporter():
# special phrases class/type on the wiki.
self.table_phrases_to_delete: Set[str] = set()
def import_phrases(self, tokenizer: AbstractTokenizer, should_replace: bool) -> None:
def get_classtype_pairs(self, min: int = 0) -> Set[Tuple[str, str]]:
"""
Returns list of allowed special phrases from the database,
restricting to a list of combinations of classes and types
which occur equal to or more than a specified amount of times.
Default value for this is 0, which allows everything in database.
"""
db_combinations = set()
query = f"""
SELECT class AS CLS, type AS typ
FROM placex
GROUP BY class, type
HAVING COUNT(*) >= {min}
"""
with self.db_connection.cursor() as db_cursor:
db_cursor.execute(SQL(query))
for row in db_cursor:
db_combinations.add((row[0], row[1]))
return db_combinations
def import_phrases(self, tokenizer: AbstractTokenizer, should_replace: bool,
min: int = 0) -> None:
"""
Iterate through all SpecialPhrases extracted from the
loader and import them into the database.
@@ -85,9 +109,10 @@ class SPImporter():
if result:
class_type_pairs.add(result)
self._create_classtype_table_and_indexes(class_type_pairs)
self._create_classtype_table_and_indexes(class_type_pairs, min)
if should_replace:
self._remove_non_existent_tables_from_db()
self.db_connection.commit()
with tokenizer.name_analyzer() as analyzer:
@@ -163,7 +188,8 @@ class SPImporter():
return (phrase.p_class, phrase.p_type)
def _create_classtype_table_and_indexes(self,
class_type_pairs: Iterable[Tuple[str, str]]) -> None:
class_type_pairs: Iterable[Tuple[str, str]],
min: int = 0) -> None:
"""
Create table place_classtype for each given pair.
Also create indexes on place_id and centroid.
@@ -177,10 +203,19 @@ class SPImporter():
with self.db_connection.cursor() as db_cursor:
db_cursor.execute("CREATE INDEX idx_placex_classtype ON placex (class, type)")
if min:
allowed_special_phrases = self.get_classtype_pairs(min)
for pair in class_type_pairs:
phrase_class = pair[0]
phrase_type = pair[1]
# Will only filter if min is not 0
if min and (phrase_class, phrase_type) not in allowed_special_phrases:
LOG.warning("Skipping phrase %s=%s: not in allowed special phrases",
phrase_class, phrase_type)
continue
table_name = _classtype_table(phrase_class, phrase_type)
if table_name in self.table_phrases_to_delete:

View File

@@ -1,10 +0,0 @@
all: bdd python
bdd:
cd bdd && behave -DREMOVE_TEMPLATE=1
python:
pytest python
.PHONY: bdd python

View File

@@ -1,3 +0,0 @@
[behave]
show_skipped=False
default_tags=~@Fail

View File

@@ -1,63 +0,0 @@
@SQLITE
@APIDB
Feature: Localization of search results
Scenario: default language
When sending details query for R1155955
Then results contain
| ID | localname |
| 0 | Liechtenstein |
Scenario: accept-language first
When sending details query for R1155955
| accept-language |
| zh,de |
Then results contain
| ID | localname |
| 0 | |
Scenario: accept-language missing
When sending details query for R1155955
| accept-language |
| xx,fr,en,de |
Then results contain
| ID | localname |
| 0 | Liechtenstein |
Scenario: http accept language header first
Given the HTTP header
| accept-language |
| fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
When sending details query for R1155955
Then results contain
| ID | localname |
| 0 | Liktinstein |
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 details query for R1155955
| accept-language |
| fo,en |
Then results contain
| ID | localname |
| 0 | Liktinstein |
Scenario: http accept language header fallback
Given the HTTP header
| accept-language |
| fo-ca,en-ca;q=0.5 |
When sending details query for R1155955
Then results contain
| ID | localname |
| 0 | Liktinstein |
Scenario: http accept language header fallback (upper case)
Given the HTTP header
| accept-language |
| fo-FR;q=0.8,en-ca;q=0.5 |
When sending details query for R1155955
Then results contain
| ID | localname |
| 0 | Liktinstein |

View File

@@ -1,96 +0,0 @@
@APIDB
Feature: Object details
Testing different parameter options for details API.
@SQLITE
Scenario: JSON Details
When sending json details query for W297699560
Then the result is valid json
And result has attributes geometry
And result has not attributes keywords,address,linked_places,parentof
And results contain in field geometry
| type |
| Point |
@SQLITE
Scenario: JSON Details with pretty printing
When sending json details query for W297699560
| pretty |
| 1 |
Then the result is valid json
And result has attributes geometry
And result has not attributes keywords,address,linked_places,parentof
@SQLITE
Scenario: JSON Details with addressdetails
When sending json details query for W297699560
| addressdetails |
| 1 |
Then the result is valid json
And result has attributes address
@SQLITE
Scenario: JSON Details with linkedplaces
When sending json details query for R123924
| linkedplaces |
| 1 |
Then the result is valid json
And result has attributes linked_places
@SQLITE
Scenario: JSON Details with hierarchy
When sending json details query for W297699560
| hierarchy |
| 1 |
Then the result is valid json
And result has attributes hierarchy
@SQLITE
Scenario: JSON Details with grouped hierarchy
When sending json details query for W297699560
| hierarchy | group_hierarchy |
| 1 | 1 |
Then the result is valid json
And result has attributes hierarchy
Scenario Outline: JSON Details with keywords
When sending json details query for <osmid>
| keywords |
| 1 |
Then the result is valid json
And result has attributes keywords
Examples:
| osmid |
| W297699560 |
| W243055645 |
| W243055716 |
| W43327921 |
# ticket #1343
Scenario: Details of a country with keywords
When sending details query for R1155955
| keywords |
| 1 |
Then the result is valid json
And result has attributes keywords
@SQLITE
Scenario Outline: JSON details with full geometry
When sending json details query for <osmid>
| polygon_geojson |
| 1 |
Then the result is valid json
And result has attributes geometry
And results contain in field geometry
| type |
| <geometry> |
Examples:
| osmid | geometry |
| W297699560 | LineString |
| W243055645 | Polygon |
| W243055716 | Polygon |
| W43327921 | LineString |

View File

@@ -1,81 +0,0 @@
@SQLITE
@APIDB
Feature: Object details
Check details page for correctness
Scenario Outline: Details via OSM id
When sending details query for <type><id>
Then the result is valid json
And results contain
| osm_type | osm_id |
| <type> | <id> |
Examples:
| type | id |
| N | 5484325405 |
| W | 43327921 |
| R | 123924 |
Scenario Outline: Details for different class types for the same OSM id
When sending details query for N300209696:<class>
Then the result is valid json
And results contain
| osm_type | osm_id | category |
| N | 300209696 | <class> |
Examples:
| class |
| tourism |
| mountain_pass |
Scenario Outline: Details via unknown OSM id
When sending details query for <object>
Then a HTTP 404 is returned
Examples:
| object |
| 1 |
| R1 |
| N300209696:highway |
Scenario: Details for interpolation way return the interpolation
When sending details query for W1
Then the result is valid json
And results contain
| category | type | osm_type | osm_id | admin_level |
| place | houses | W | 1 | 15 |
@Fail
Scenario: Details for interpolation way return the interpolation
When sending details query for 112871
Then the result is valid json
And results contain
| category | type | admin_level |
| place | houses | 15 |
And result has not attributes osm_type,osm_id
@Fail
Scenario: Details for interpolation way return the interpolation
When sending details query for 112820
Then the result is valid json
And results contain
| category | type | admin_level |
| place | postcode | 15 |
And result has not attributes osm_type,osm_id
Scenario Outline: Details debug output returns no errors
When sending debug details query for <feature>
Then the result is valid html
Examples:
| feature |
| N5484325405 |
| W1 |
| 112820 |
| 112871 |

View File

@@ -1,14 +0,0 @@
@SQLITE
@APIDB
Feature: Places by osm_type and osm_id Tests
Simple tests for errors in various response formats.
Scenario Outline: Force error by providing too many ids
When sending <format> lookup query for N1,N2,N3,N4,N5,N6,N7,N8,N9,N10,N11,N12,N13,N14,N15,N16,N17,N18,N19,N20,N21,N22,N23,N24,N25,N26,N27,N28,N29,N30,N31,N32,N33,N34,N35,N36,N37,N38,N39,N40,N41,N42,N43,N44,N45,N46,N47,N48,N49,N50,N51
Then a <format> user error is returned
Examples:
| format |
| xml |
| json |
| geojson |

View File

@@ -1,42 +0,0 @@
@SQLITE
@APIDB
Feature: Places by osm_type and osm_id Tests
Simple tests for response format.
Scenario Outline: address lookup for existing node, way, relation
When sending <format> lookup query for N5484325405,W43327921,,R123924,X99,N0
Then the result is valid <outformat>
And exactly 3 results are returned
Examples:
| format | outformat |
| xml | xml |
| json | json |
| jsonv2 | json |
| geojson | geojson |
| geocodejson | geocodejson |
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
Scenario Outline: Boundingbox is returned
When sending <format> lookup query for N5484325405,W43327921
Then exactly 2 results are returned
And result 0 has bounding box in 47.135,47.14,9.52,9.525
And result 1 has bounding box in 47.07,47.08,9.50,9.52
Examples:
| format |
| json |
| jsonv2 |
| geojson |
| xml |
Scenario: Lookup of a linked place
When sending geocodejson lookup query for N1932181216
Then exactly 1 result is returned
And results contain
| name |
| Vaduz |

View File

@@ -1,45 +0,0 @@
@SQLITE
@APIDB
Feature: Geometries for reverse geocoding
Tests for returning geometries with reverse
Scenario: Polygons are returned fully by default
When sending v1/reverse at 47.13803,9.52264
| polygon_text |
| 1 |
Then results contain
| geotext |
| ^POLYGON\(\(9.5225302 47.138066, ?9.5225348 47.1379282, ?9.5226142 47.1379294, ?9.5226143 47.1379257, ?9.522615 47.137917, ?9.5226225 47.1379098, ?9.5226334 47.1379052, ?9.5226461 47.1379037, ?9.5226588 47.1379056, ?9.5226693 47.1379107, ?9.5226762 47.1379181, ?9.5226762 47.1379268, ?9.5226761 47.1379308, ?9.5227366 47.1379317, ?9.5227352 47.1379753, ?9.5227608 47.1379757, ?9.5227595 47.1380148, ?9.5227355 47.1380145, ?9.5227337 47.1380692, ?9.5225302 47.138066\)\) |
Scenario: Polygons can be slightly simplified
When sending v1/reverse at 47.13803,9.52264
| polygon_text | polygon_threshold |
| 1 | 0.00001 |
Then results contain
| geotext |
| ^POLYGON\(\(9.5225302 47.138066, ?9.5225348 47.1379282, ?9.5226142 47.1379294, ?9.5226225 47.1379098, ?9.5226588 47.1379056, ?9.5226761 47.1379308, ?9.5227366 47.1379317, ?9.5227352 47.1379753, ?9.5227608 47.1379757, ?9.5227595 47.1380148, ?9.5227355 47.1380145, ?9.5227337 47.1380692, ?9.5225302 47.138066\)\) |
Scenario: Polygons can be much simplified
When sending v1/reverse at 47.13803,9.52264
| polygon_text | polygon_threshold |
| 1 | 0.9 |
Then results contain
| geotext |
| ^POLYGON\(\([0-9. ]+, ?[0-9. ]+, ?[0-9. ]+, ?[0-9. ]+(, ?[0-9. ]+)?\)\) |
Scenario: For polygons return the centroid as center point
When sending v1/reverse at 47.13836,9.52304
Then results contain
| centroid |
| 9.52271080 47.13818045 |
Scenario: For streets return the closest point as center point
When sending v1/reverse at 47.13368,9.52942
Then results contain
| centroid |
| 9.529431527 47.13368172 |

View File

@@ -1,37 +0,0 @@
@SQLITE
@APIDB
Feature: Localization of reverse search results
Scenario: default language
When sending v1/reverse at 47.14,9.55
Then result addresses contain
| ID | country |
| 0 | Liechtenstein |
Scenario: accept-language parameter
When sending v1/reverse at 47.14,9.55
| accept-language |
| ja,en |
Then result addresses contain
| ID | country |
| 0 | |
Scenario: HTTP accept language header
Given the HTTP header
| accept-language |
| fo-ca,fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
When sending v1/reverse at 47.14,9.55
Then result addresses contain
| ID | country |
| 0 | Liktinstein |
Scenario: accept-language parameter and HTTP header
Given the HTTP header
| accept-language |
| fo-ca,fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
When sending v1/reverse at 47.14,9.55
| accept-language |
| en |
Then result addresses contain
| ID | country |
| 0 | Liechtenstein |

View File

@@ -1,117 +0,0 @@
@SQLITE
@APIDB
Feature: Reverse geocoding
Testing the reverse function
Scenario Outline: Simple reverse-geocoding with no results
When sending v1/reverse at <lat>,<lon>
Then exactly 0 results are returned
Examples:
| lat | lon |
| 0.0 | 0.0 |
| 91.3 | 0.4 |
| -700 | 0.4 |
| 0.2 | 324.44 |
| 0.2 | -180.4 |
Scenario: Unknown countries fall back to default country grid
When sending v1/reverse at 45.174,-103.072
Then results contain
| category | type | display_name |
| place | country | United States |
@Tiger
Scenario: TIGER house number
When sending v1/reverse at 32.4752389363,-86.4810198619
Then results contain
| category | type |
| place | house |
And result addresses contain
| house_number | road | postcode | country_code |
| 707 | Upper Kingston Road | 36067 | us |
@Tiger
Scenario: No TIGER house number for zoom < 18
When sending v1/reverse at 32.4752389363,-86.4810198619
| zoom |
| 17 |
Then results contain
| osm_type | category |
| way | highway |
And result addresses contain
| road | postcode | country_code |
| Upper Kingston Road | 36067 | us |
Scenario: Interpolated house number
When sending v1/reverse at 47.118533,9.57056562
Then results contain
| osm_type | category | type |
| way | place | house |
And result addresses contain
| house_number | road |
| 1019 | Grosssteg |
Scenario: Address with non-numerical house number
When sending v1/reverse at 47.107465,9.52838521614
Then result addresses contain
| house_number | road |
| 39A/B | Dorfstrasse |
Scenario: Address with numerical house number
When sending v1/reverse at 47.168440329479594,9.511551699184338
Then result addresses contain
| house_number | road |
| 6 | Schmedgässle |
Scenario Outline: Zoom levels below 5 result in country
When sending v1/reverse at 47.16,9.51
| zoom |
| <zoom> |
Then results contain
| display_name |
| Liechtenstein |
Examples:
| zoom |
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
Scenario: When on a street, the closest interpolation is shown
When sending v1/reverse at 47.118457166193245,9.570678289621355
| zoom |
| 18 |
Then results contain
| display_name |
| 1021, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
# github 2214
Scenario: Interpolations do not override house numbers when they are closer
When sending v1/reverse at 47.11778,9.57255
| zoom |
| 18 |
Then results contain
| display_name |
| 5, Grosssteg, Steg, Triesenberg, Oberland, 9497, Liechtenstein |
Scenario: Interpolations do not override house numbers when they are closer (2)
When sending v1/reverse at 47.11834,9.57167
| zoom |
| 18 |
Then results contain
| display_name |
| 3, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
Scenario: When on a street with zoom 18, the closest housenumber is returned
When sending v1/reverse at 47.11755503977281,9.572722250405036
| zoom |
| 18 |
Then result addresses contain
| house_number |
| 7 |

View File

@@ -1,107 +0,0 @@
@SQLITE
@APIDB
Feature: Geocodejson for Reverse API
Testing correctness of geocodejson output (API version v1).
Scenario Outline: Simple OSM result
When sending v1/reverse at 47.066,9.504 with format geocodejson
| addressdetails |
| <has_address> |
Then result has attributes place_id, accuracy
And result has <attributes> country,postcode,county,city,district,street,housenumber, admin
Then results contain
| osm_type | osm_id | osm_key | osm_value | type |
| node | 6522627624 | shop | bakery | house |
And results contain
| name | label |
| Dorfbäckerei Herrmann | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
And results contain in field geojson
| type | coordinates |
| Point | [9.5036065, 47.0660892] |
And results contain in field __geocoding
| version | licence | attribution |
| 0.1.0 | ODbL | ^Data © OpenStreetMap contributors, ODbL 1.0. https?://osm.org/copyright$ |
Examples:
| has_address | attributes |
| 1 | attributes |
| 0 | not attributes |
Scenario: City housenumber-level address with street
When sending v1/reverse at 47.1068011,9.52810091 with format geocodejson
Then results contain
| housenumber | street | postcode | city | country |
| 8 | Im Winkel | 9495 | Triesen | Liechtenstein |
And results contain in field admin
| level6 | level8 |
| Oberland | Triesen |
Scenario: Town street-level address with street
When sending v1/reverse at 47.066,9.504 with format geocodejson
| zoom |
| 16 |
Then results contain
| name | city | postcode | country |
| Gnetsch | Balzers | 9496 | Liechtenstein |
Scenario: Poi street-level address with footway
When sending v1/reverse at 47.06515,9.50083 with format geocodejson
Then results contain
| street | city | postcode | country |
| Burgweg | Balzers | 9496 | Liechtenstein |
Scenario: City address with suburb
When sending v1/reverse at 47.146861,9.511771 with format geocodejson
Then results contain
| housenumber | street | district | city | postcode | country |
| 5 | Lochgass | Ebenholz | Vaduz | 9490 | Liechtenstein |
@Tiger
Scenario: Tiger address
When sending v1/reverse at 32.4752389363,-86.4810198619 with format geocodejson
Then results contain
| osm_type | osm_id | osm_key | osm_value | type |
| way | 396009653 | place | house | house |
And results contain
| housenumber | street | city | county | postcode | country |
| 707 | Upper Kingston Road | Prattville | Autauga County | 36067 | United States |
Scenario: Interpolation address
When sending v1/reverse at 47.118533,9.57056562 with format geocodejson
Then results contain
| osm_type | osm_id | osm_key | osm_value | type |
| way | 1 | place | house | house |
And results contain
| label |
| 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
And result has not attributes name
Scenario: Line geometry output is supported
When sending v1/reverse at 47.06597,9.50467 with format geocodejson
| param | value |
| polygon_geojson | 1 |
Then results contain in field geojson
| type |
| LineString |
Scenario Outline: Only geojson polygons are supported
When sending v1/reverse at 47.06597,9.50467 with format geocodejson
| param | value |
| <param> | 1 |
Then results contain in field geojson
| type |
| Point |
Examples:
| param |
| polygon_text |
| polygon_svg |
| polygon_kml |

View File

@@ -1,73 +0,0 @@
@SQLITE
@APIDB
Feature: Geojson for Reverse API
Testing correctness of geojson output (API version v1).
Scenario Outline: Simple OSM result
When sending v1/reverse at 47.066,9.504 with format geojson
| addressdetails |
| <has_address> |
Then result has attributes place_id, importance, __licence
And result has <attributes> address
And results contain
| osm_type | osm_id | place_rank | category | type | addresstype |
| node | 6522627624 | 30 | shop | bakery | shop |
And results contain
| name | display_name |
| Dorfbäckerei Herrmann | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
And results contain
| boundingbox |
| [47.0660392, 47.0661392, 9.5035565, 9.5036565] |
And results contain in field geojson
| type | coordinates |
| Point | [9.5036065, 47.0660892] |
Examples:
| has_address | attributes |
| 1 | attributes |
| 0 | not attributes |
@Tiger
Scenario: Tiger address
When sending v1/reverse at 32.4752389363,-86.4810198619 with format geojson
Then results contain
| osm_type | osm_id | category | type | addresstype | place_rank |
| way | 396009653 | place | house | place | 30 |
Scenario: Interpolation address
When sending v1/reverse at 47.118533,9.57056562 with format geojson
Then results contain
| osm_type | osm_id | place_rank | category | type | addresstype |
| way | 1 | 30 | place | house | place |
And results contain
| boundingbox |
| ^\[47.118495\d*, 47.118595\d*, 9.570496\d*, 9.570596\d*\] |
And results contain
| display_name |
| 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
Scenario: Line geometry output is supported
When sending v1/reverse at 47.06597,9.50467 with format geojson
| param | value |
| polygon_geojson | 1 |
Then results contain in field geojson
| type |
| LineString |
Scenario Outline: Only geojson polygons are supported
When sending v1/reverse at 47.06597,9.50467 with format geojson
| param | value |
| <param> | 1 |
Then results contain in field geojson
| type |
| Point |
Examples:
| param |
| polygon_text |
| polygon_svg |
| polygon_kml |

View File

@@ -1,130 +0,0 @@
@SQLITE
@APIDB
Feature: Json output for Reverse API
Testing correctness of json and jsonv2 output (API version v1).
Scenario Outline: OSM result with and without addresses
When sending v1/reverse at 47.066,9.504 with format json
| addressdetails |
| <has_address> |
Then result has <attributes> address
When sending v1/reverse at 47.066,9.504 with format jsonv2
| addressdetails |
| <has_address> |
Then result has <attributes> address
Examples:
| has_address | attributes |
| 1 | attributes |
| 0 | not attributes |
Scenario Outline: Simple OSM result
When sending v1/reverse at 47.066,9.504 with format <format>
Then result has attributes place_id
And results contain
| licence |
| ^Data © OpenStreetMap contributors, ODbL 1.0. https?://osm.org/copyright$ |
And results contain
| osm_type | osm_id |
| node | 6522627624 |
And results contain
| centroid | boundingbox |
| 9.5036065 47.0660892 | ['47.0660392', '47.0661392', '9.5035565', '9.5036565'] |
And results contain
| display_name |
| Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
And result has not attributes namedetails,extratags
Examples:
| format |
| json |
| jsonv2 |
Scenario: Extra attributes of jsonv2 result
When sending v1/reverse at 47.066,9.504 with format jsonv2
Then result has attributes importance
Then results contain
| category | type | name | place_rank | addresstype |
| shop | bakery | Dorfbäckerei Herrmann | 30 | shop |
@Tiger
Scenario: Tiger address
When sending v1/reverse at 32.4752389363,-86.4810198619 with format jsonv2
Then results contain
| osm_type | osm_id | category | type | addresstype |
| way | 396009653 | place | house | place |
Scenario Outline: Interpolation address
When sending v1/reverse at 47.118533,9.57056562 with format <format>
Then results contain
| osm_type | osm_id |
| way | 1 |
And results contain
| centroid | boundingbox |
| 9.57054676 47.118545392 | ^\['47.118495\d*', '47.118595\d*', '9.570496\d*', '9.570596\d*'\] |
And results contain
| display_name |
| 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
Examples:
| format |
| json |
| jsonv2 |
Scenario Outline: Output of geojson
When sending v1/reverse at 47.06597,9.50467 with format <format>
| param | value |
| polygon_geojson | 1 |
Then results contain in field geojson
| type | coordinates |
| LineString | [[9.5039353, 47.0657546], [9.5040437, 47.0657781], [9.5040808, 47.065787], [9.5054298, 47.0661407]] |
Examples:
| format |
| json |
| jsonv2 |
Scenario Outline: Output of WKT
When sending v1/reverse at 47.06597,9.50467 with format <format>
| param | value |
| polygon_text | 1 |
Then results contain
| geotext |
| ^LINESTRING\(9.5039353 47.0657546, ?9.5040437 47.0657781, ?9.5040808 47.065787, ?9.5054298 47.0661407\) |
Examples:
| format |
| json |
| jsonv2 |
Scenario Outline: Output of SVG
When sending v1/reverse at 47.06597,9.50467 with format <format>
| param | value |
| polygon_svg | 1 |
Then results contain
| svg |
| M 9.5039353 -47.0657546 L 9.5040437 -47.0657781 9.5040808 -47.065787 9.5054298 -47.0661407 |
Examples:
| format |
| json |
| jsonv2 |
Scenario Outline: Output of KML
When sending v1/reverse at 47.06597,9.50467 with format <format>
| param | value |
| polygon_kml | 1 |
Then results contain
| geokml |
| ^<LineString><coordinates>9.5039\d*,47.0657\d* 9.5040\d*,47.0657\d* 9.5040\d*,47.065\d* 9.5054\d*,47.0661\d*</coordinates></LineString> |
Examples:
| format |
| json |
| jsonv2 |

View File

@@ -1,206 +0,0 @@
@SQLITE
@APIDB
Feature: v1/reverse Parameter Tests
Tests for parameter inputs for the v1 reverse endpoint.
This file contains mostly bad parameter input. Valid parameters
are tested in the format tests.
Scenario: Bad format
When sending v1/reverse at 47.14122383,9.52169581334 with format sdf
Then a HTTP 400 is returned
Scenario: Missing lon parameter
When sending v1/reverse at 52.52,
Then a HTTP 400 is returned
Scenario: Missing lat parameter
When sending v1/reverse at ,52.52
Then a HTTP 400 is returned
Scenario Outline: Bad format for lat or lon
When sending v1/reverse at ,
| 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 |
| Inf | 5.6 |
| 5.6 | -Inf |
| <script></script> | 3.4 |
| 3.4 | <script></script> |
| -45.3 | ; |
| gkjd | 50 |
Scenario: Non-numerical zoom levels return an error
When sending v1/reverse at 47.14122383,9.52169581334
| zoom |
| adfe |
Then a HTTP 400 is returned
Scenario Outline: Truthy values for boolean parameters
When sending v1/reverse at 47.14122383,9.52169581334
| addressdetails |
| <value> |
Then exactly 1 result is returned
And result has attributes address
When sending v1/reverse at 47.14122383,9.52169581334
| extratags |
| <value> |
Then exactly 1 result is returned
And result has attributes extratags
When sending v1/reverse at 47.14122383,9.52169581334
| namedetails |
| <value> |
Then exactly 1 result is returned
And result has attributes namedetails
When sending v1/reverse at 47.14122383,9.52169581334
| polygon_geojson |
| <value> |
Then exactly 1 result is returned
And result has attributes geojson
When sending v1/reverse at 47.14122383,9.52169581334
| polygon_kml |
| <value> |
Then exactly 1 result is returned
And result has attributes geokml
When sending v1/reverse at 47.14122383,9.52169581334
| polygon_svg |
| <value> |
Then exactly 1 result is returned
And result has attributes svg
When sending v1/reverse at 47.14122383,9.52169581334
| polygon_text |
| <value> |
Then exactly 1 result is returned
And result has attributes geotext
Examples:
| value |
| yes |
| no |
| -1 |
| 100 |
| false |
| 00 |
Scenario: Only one geometry can be requested
When sending v1/reverse at 47.165989816710066,9.515774846076965
| polygon_text | polygon_svg |
| 1 | 1 |
Then a HTTP 400 is returned
Scenario Outline: Wrapping of legal jsonp requests
When sending v1/reverse at 67.3245,0.456 with format <format>
| json_callback |
| foo |
Then the result is valid <outformat>
Examples:
| format | outformat |
| json | json |
| jsonv2 | json |
| geojson | geojson |
| geocodejson | geocodejson |
Scenario Outline: Illegal jsonp are not allowed
When sending v1/reverse at 47.165989816710066,9.515774846076965
| param | value |
|json_callback | <data> |
Then a HTTP 400 is returned
Examples:
| data |
| 1asd |
| bar(foo) |
| XXX['bad'] |
| foo; evil |
Scenario Outline: Reverse debug mode produces valid HTML
When sending v1/reverse at , with format debug
| lat | lon |
| <lat> | <lon> |
Then the result is valid html
Examples:
| lat | lon |
| 0.0 | 0.0 |
| 47.06645 | 9.56601 |
| 47.14081 | 9.52267 |
Scenario Outline: Full address display for city housenumber-level address with street
When sending v1/reverse at 47.1068011,9.52810091 with format <format>
Then address of result 0 is
| type | value |
| house_number | 8 |
| road | Im Winkel |
| neighbourhood | Oberdorf |
| village | Triesen |
| ISO3166-2-lvl8 | LI-09 |
| county | Oberland |
| postcode | 9495 |
| country | Liechtenstein |
| country_code | li |
Examples:
| format |
| json |
| jsonv2 |
| geojson |
| xml |
Scenario Outline: Results with name details
When sending v1/reverse at 47.14052,9.52202 with format <format>
| zoom | namedetails |
| 14 | 1 |
Then results contain in field namedetails
| name |
| Ebenholz |
Examples:
| format |
| json |
| jsonv2 |
| xml |
| geojson |
Scenario Outline: Results with extratags
When sending v1/reverse at 47.14052,9.52202 with format <format>
| zoom | extratags |
| 14 | 1 |
Then results contain in field extratags
| wikidata |
| Q4529531 |
Examples:
| format |
| json |
| jsonv2 |
| xml |
| geojson |

View File

@@ -1,88 +0,0 @@
@SQLITE
@APIDB
Feature: XML output for Reverse API
Testing correctness of xml output (API version v1).
Scenario Outline: OSM result with and without addresses
When sending v1/reverse at 47.066,9.504 with format xml
| addressdetails |
| <has_address> |
Then result has attributes place_id
Then result has <attributes> address
And results contain
| osm_type | osm_id | place_rank | address_rank |
| node | 6522627624 | 30 | 30 |
And results contain
| centroid | boundingbox |
| 9.5036065 47.0660892 | 47.0660392,47.0661392,9.5035565,9.5036565 |
And results contain
| ref | display_name |
| Dorfbäckerei Herrmann | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
Examples:
| has_address | attributes |
| 1 | attributes |
| 0 | not attributes |
@Tiger
Scenario: Tiger address
When sending v1/reverse at 32.4752389363,-86.4810198619 with format xml
Then results contain
| osm_type | osm_id | place_rank | address_rank |
| way | 396009653 | 30 | 30 |
And results contain
| centroid | boundingbox |
| -86.4808553 32.4753580 | ^32.4753080\d*,32.4754080\d*,-86.4809053\d*,-86.4808053\d* |
And results contain
| display_name |
| 707, Upper Kingston Road, Upper Kingston, Prattville, Autauga County, 36067, United States |
Scenario: Interpolation address
When sending v1/reverse at 47.118533,9.57056562 with format xml
Then results contain
| osm_type | osm_id | place_rank | address_rank |
| way | 1 | 30 | 30 |
And results contain
| centroid | boundingbox |
| 9.57054676 47.118545392 | ^47.118495\d*,47.118595\d*,9.570496\d*,9.570596\d* |
And results contain
| display_name |
| 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
Scenario: Output of geojson
When sending v1/reverse at 47.06597,9.50467 with format xml
| param | value |
| polygon_geojson | 1 |
Then results contain
| geojson |
| {"type":"LineString","coordinates":[[9.5039353,47.0657546],[9.5040437,47.0657781],[9.5040808,47.065787],[9.5054298,47.0661407]]} |
Scenario: Output of WKT
When sending v1/reverse at 47.06597,9.50467 with format xml
| param | value |
| polygon_text | 1 |
Then results contain
| geotext |
| ^LINESTRING\(9.5039353 47.0657546, ?9.5040437 47.0657781, ?9.5040808 47.065787, ?9.5054298 47.0661407\) |
Scenario: Output of SVG
When sending v1/reverse at 47.06597,9.50467 with format xml
| param | value |
| polygon_svg | 1 |
Then results contain
| geosvg |
| M 9.5039353 -47.0657546 L 9.5040437 -47.0657781 9.5040808 -47.065787 9.5054298 -47.0661407 |
Scenario: Output of KML
When sending v1/reverse at 47.06597,9.50467 with format xml
| param | value |
| polygon_kml | 1 |
Then results contain
| geokml |
| ^<geokml><LineString><coordinates>9.5039\d*,47.0657\d* 9.5040\d*,47.0657\d* 9.5040\d*,47.065\d* 9.5054\d*,47.0661\d*</coordinates></LineString></geokml> |

View File

@@ -1,28 +0,0 @@
@SQLITE
@APIDB
Feature: Parameters for Search API
Testing correctness of geocodejson output.
Scenario: City housenumber-level address with street
When sending geocodejson search query "Im Winkel 8, Triesen" with address
Then results contain
| housenumber | street | postcode | city | country |
| 8 | Im Winkel | 9495 | Triesen | Liechtenstein |
Scenario: Town street-level address with street
When sending geocodejson search query "Gnetsch, Balzers" with address
Then results contain
| name | city | postcode | country |
| Gnetsch | Balzers | 9496 | Liechtenstein |
Scenario: Town street-level address with footway
When sending geocodejson search query "burg gutenberg 6000 jahre geschichte" with address
Then results contain
| street | city | postcode | country |
| Burgweg | Balzers | 9496 | Liechtenstein |
Scenario: City address with suburb
When sending geocodejson search query "Lochgass 5, Ebenholz, Vaduz" with address
Then results contain
| housenumber | street | district | city | postcode | country |
| 5 | Lochgass | Ebenholz | Vaduz | 9490 | Liechtenstein |

View File

@@ -1,63 +0,0 @@
@SQLITE
@APIDB
Feature: Localization of search results
Scenario: default language
When sending json search query "Liechtenstein"
Then results contain
| ID | display_name |
| 0 | Liechtenstein |
Scenario: accept-language first
When sending json search query "Liechtenstein"
| accept-language |
| zh,de |
Then results contain
| ID | display_name |
| 0 | |
Scenario: accept-language missing
When sending json search query "Liechtenstein"
| accept-language |
| xx,fr,en,de |
Then results contain
| ID | display_name |
| 0 | Liechtenstein |
Scenario: http accept language header first
Given the HTTP header
| accept-language |
| fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
When sending json search query "Liechtenstein"
Then results contain
| ID | display_name |
| 0 | Liktinstein |
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 "Liechtenstein"
| accept-language |
| fo,en |
Then results contain
| ID | display_name |
| 0 | Liktinstein |
Scenario: http accept language header fallback
Given the HTTP header
| accept-language |
| fo-ca,en-ca;q=0.5 |
When sending json search query "Liechtenstein"
Then results contain
| ID | display_name |
| 0 | Liktinstein |
Scenario: http accept language header fallback (upper case)
Given the HTTP header
| accept-language |
| fo-FR;q=0.8,en-ca;q=0.5 |
When sending json search query "Liechtenstein"
Then results contain
| ID | display_name |
| 0 | Liktinstein |

View File

@@ -1,362 +0,0 @@
@SQLITE
@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
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,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: Unknown formats returns a user error
When sending search query "Vaduz"
| format |
| x45 |
Then a HTTP 400 is returned
Scenario Outline: Search with addressdetails
When sending <format> search query "Triesen" with address
Then address of result 0 is
| type | value |
| village | Triesen |
| county | Oberland |
| postcode | 9495 |
| country | Liechtenstein |
| country_code | li |
| ISO3166-2-lvl8 | LI-09 |
Examples:
| format |
| json |
| jsonv2 |
| geojson |
| xml |
Scenario: Coordinate search with addressdetails
When sending json search query "47.12400621,9.6047552"
| accept-language |
| en |
Then results contain
| display_name |
| Guschg, Valorschstrasse, Balzers, Oberland, 9497, Liechtenstein |
Scenario: Address details with unknown class types
When sending json search query "Kloster St. Elisabeth" with address
Then results contain
| ID | class | type |
| 0 | amenity | monastery |
And result addresses contain
| ID | amenity |
| 0 | Kloster St. Elisabeth |
Scenario: Disabling deduplication
When sending json search query "Malbunstr"
Then there are no duplicates
When sending json search query "Malbunstr"
| dedupe |
| 0 |
Then there are duplicates
Scenario: Search with bounded viewbox in right area
When sending json search query "post" with address
| bounded | viewbox |
| 1 | 9,47,10,48 |
Then result addresses contain
| ID | town |
| 0 | Vaduz |
When sending json search query "post" with address
| bounded | viewbox |
| 1 | 9.49712,47.17122,9.52605,47.16242 |
Then result addresses contain
| town |
| Schaan |
Scenario: Country search with bounded viewbox remain in the area
When sending json search query "" with address
| bounded | viewbox | country |
| 1 | 9.49712,47.17122,9.52605,47.16242 | de |
Then less than 1 result is returned
Scenario: Search with bounded viewboxlbrt in right area
When sending json search query "bar" with address
| bounded | viewboxlbrt |
| 1 | 9.49712,47.16242,9.52605,47.17122 |
Then result addresses contain
| town |
| Schaan |
@Fail
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.49712,47.17122,9.52605,47.16242 |
Then result has centroid in 9.49712,47.16242,9.52605,47.17122
Scenario: Prefer results within viewbox
When sending json search query "Gässle" with address
| accept-language | viewbox |
| en | 9.52413,47.10759,9.53140,47.10539 |
Then result addresses contain
| ID | village |
| 0 | Triesen |
When sending json search query "Gässle" with address
| accept-language | viewbox |
| en | 9.45949,47.08421,9.54094,47.05466 |
Then result addresses contain
| ID | town |
| 0 | Balzers |
Scenario: viewboxes cannot be points
When sending json search query "foo"
| viewbox |
| 1.01,34.6,1.01,34.6 |
Then a HTTP 400 is returned
Scenario Outline: viewbox must have four coordinate numbers
When sending json search query "foo"
| viewbox |
| <viewbox> |
Then a HTTP 400 is returned
Examples:
| viewbox |
| 34 |
| 0.003,-84.4 |
| 5.2,4.5542,12.4 |
| 23.1,-6,0.11,44.2,9.1 |
Scenario Outline: viewboxlbrt must have four coordinate numbers
When sending json search query "foo"
| viewboxlbrt |
| <viewbox> |
Then a HTTP 400 is returned
Examples:
| viewbox |
| 34 |
| 0.003,-84.4 |
| 5.2,4.5542,12.4 |
| 23.1,-6,0.11,44.2,9.1 |
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 "landstr"
| dedupe |
| 0 |
Then more than 4 results are returned
When sending json search query "landstr"
| limit | dedupe |
| 4 | 0 |
Then exactly 4 results are returned
Scenario: Limit parameter must be a number
When sending search query "Blue Laguna"
| limit |
| ); |
Then a HTTP 400 is returned
Scenario: Restrict to feature type country
When sending xml search query "fürstentum"
| featureType |
| country |
Then results contain
| place_rank |
| 4 |
Scenario: Restrict to feature type state
When sending xml search query "Wangerberg"
Then at least 1 result is returned
When sending xml search query "Wangerberg"
| featureType |
| state |
Then exactly 0 results are returned
Scenario: Restrict to feature type city
When sending xml search query "vaduz"
Then at least 1 result is returned
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 "Malbun"
Then results contain
| ID | class |
| 1 | landuse |
When sending json search query "Malbun"
| featureType |
| settlement |
Then results contain
| class | type |
| place | village |
Scenario Outline: Search with polygon threshold (json)
When sending json search query "triesenberg"
| 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 "triesenberg"
| 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 "triesenberg"
| 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 "Landstr"
| extratags |
| 1 |
Then result has attributes extratags
Examples:
| format |
| xml |
| json |
| jsonv2 |
| geojson |
Scenario Outline: Search with namedetails
When sending <format> search query "Landstr"
| namedetails |
| 1 |
Then result has attributes namedetails
Examples:
| format |
| xml |
| json |
| jsonv2 |
| geojson |
Scenario Outline: Search result with contains TEXT geometry
When sending <format> search query "triesenberg"
| polygon_text |
| 1 |
Then result has attributes <response_attribute>
Examples:
| format | response_attribute |
| xml | geotext |
| json | geotext |
| jsonv2 | geotext |
Scenario Outline: Search result contains SVG geometry
When sending <format> search query "triesenberg"
| 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 "triesenberg"
| 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 "triesenberg"
| polygon_geojson |
| 1 |
Then result has attributes <response_attribute>
Examples:
| format | response_attribute |
| xml | geojson |
| json | geojson |
| jsonv2 | geojson |
| geojson | geojson |
Scenario Outline: Search result in geojson format contains no non-geojson geometry
When sending geojson search query "triesenberg"
| polygon_text | polygon_svg | polygon_geokml |
| 1 | 1 | 1 |
Then result 0 has not attributes <response_attribute>
Examples:
| response_attribute |
| geotext |
| polygonpoints |
| svg |
| geokml |
Scenario: Array parameters are ignored
When sending json search query "Vaduz" with address
| countrycodes[] | polygon_svg[] | limit[] | polygon_threshold[] |
| IT | 1 | 3 | 3.4 |
Then result addresses contain
| ID | country_code |
| 0 | li |

View File

@@ -1,221 +0,0 @@
@SQLITE
@APIDB
Feature: Search queries
Generic search result correctness
Scenario: Search for natural object
When sending json search query "Samina"
| accept-language |
| en |
Then results contain
| ID | class | type | display_name |
| 0 | waterway | river | Samina, Austria |
Scenario: House number search for non-street address
When sending json search query "6 Silum, Liechtenstein" with address
| accept-language |
| en |
Then address of result 0 is
| type | value |
| house_number | 6 |
| village | Silum |
| town | Triesenberg |
| county | Oberland |
| postcode | 9497 |
| country | Liechtenstein |
| country_code | li |
| ISO3166-2-lvl8 | LI-10 |
Scenario: House number interpolation
When sending json search query "Grosssteg 1023, Triesenberg" with address
| accept-language |
| de |
Then address of result 0 contains
| type | value |
| house_number | 1023 |
| road | Grosssteg |
| village | Sücka |
| postcode | 9497 |
| town | Triesenberg |
| country | Liechtenstein |
| country_code | li |
Scenario: With missing housenumber search falls back to road
When sending json search query "Bündaweg 555" with address
Then address of result 0 is
| type | value |
| road | Bündaweg |
| village | Silum |
| postcode | 9497 |
| county | Oberland |
| town | Triesenberg |
| country | Liechtenstein |
| country_code | li |
| ISO3166-2-lvl8 | LI-10 |
Scenario Outline: Housenumber 0 can be found
When sending <format> search query "Gnalpstrasse 0" with address
Then results contain
| display_name |
| ^0,.* |
And result addresses contain
| house_number |
| 0 |
Examples:
| format |
| xml |
| json |
| jsonv2 |
| geojson |
@Tiger
Scenario: TIGER house number
When sending json search query "697 Upper Kingston Road"
Then results contain
| osm_type | display_name |
| way | ^697,.* |
Scenario: Search with class-type feature
When sending jsonv2 search query "bars in ebenholz"
Then results contain
| place_rank |
| 30 |
Scenario: Search with specific amenity
When sending json search query "[restaurant] Vaduz" with address
Then result addresses contain
| country |
| Liechtenstein |
And results contain
| class | type |
| amenity | restaurant |
Scenario: Search with specific amenity also work in country
When sending json search query "restaurants in liechtenstein" with address
Then result addresses contain
| country |
| Liechtenstein |
And results contain
| class | type |
| amenity | restaurant |
Scenario: Search with key-value amenity
When sending json search query "[club=scout] Vaduz"
Then results contain
| class | type |
| club | scout |
Scenario: POI search near given coordinate
When sending json search query "restaurant near 47.16712,9.51100"
Then results contain
| class | type |
| amenity | restaurant |
Scenario: Arbitrary key/value search near given coordinate
When sending json search query "[leisure=firepit] 47.150° N 9.5340493° E"
Then results contain
| class | type |
| leisure | firepit |
Scenario: POI search in a bounded viewbox
When sending json search query "restaurants"
| viewbox | bounded |
| 9.50830,47.15253,9.52043,47.14866 | 1 |
Then results contain
| class | type |
| amenity | restaurant |
Scenario Outline: Key/value search near given coordinate can be restricted to country
When sending json search query "[natural=peak] 47.06512,9.53965" with address
| countrycodes |
| <cc> |
Then result addresses contain
| country_code |
| <cc> |
Examples:
| cc |
| li |
| ch |
Scenario: Name search near given coordinate
When sending json search query "sporry" with address
Then result addresses contain
| ID | town |
| 0 | Vaduz |
When sending json search query "sporry, 47.10791,9.52676" with address
Then result addresses contain
| ID | village |
| 0 | Triesen |
Scenario: Name search near given coordinate without result
When sending json search query "sporry, N 47 15 7 W 9 61 26"
Then exactly 0 results are returned
Scenario: Arbitrary key/value search near a road
When sending json search query "[amenity=drinking_water] Wissfläckaweg"
Then results contain
| class | type |
| amenity | drinking_water |
Scenario: Ignore other country codes in structured search with country
When sending json search query ""
| city | country |
| li | de |
Then exactly 0 results are returned
Scenario: Ignore country searches when query is restricted to countries
When sending json search query "fr"
| countrycodes |
| li |
Then exactly 0 results are returned
Scenario: Country searches only return results for the given country
When sending search query "Ans Trail" with address
| countrycodes |
| li |
Then result addresses contain
| country_code |
| li |
# https://trac.openstreetmap.org/ticket/5094
Scenario: housenumbers are ordered by complete match first
When sending json search query "Austrasse 11, Vaduz" with address
Then result addresses contain
| ID | house_number |
| 0 | 11 |
Scenario Outline: Coordinate searches with white spaces
When sending json search query "<data>"
Then exactly 1 result is returned
And results contain
| class |
| water |
Examples:
| data |
| sporry weiher, N 47.10791° E 9.52676° |
| sporry weiher, N 47.10791° E 9.52676° |
| sporry weiher , N 47.10791° E 9.52676° |
| sporry weiher, N 47.10791° E 9.52676° |
| sporry weiher , N 47.10791° E 9.52676° |
Scenario: Searches with white spaces
When sending json search query "52 Bodastr , Triesenberg"
Then results contain
| class | type |
| highway | residential |
# github #1949
Scenario: Addressdetails always return the place type
When sending json search query "Vaduz" with address
Then result addresses contain
| ID | town |
| 0 | Vaduz |
Scenario: Search can handle complex query word sets
When sending search query "aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus"
Then a HTTP 200 is returned

View File

@@ -1,208 +0,0 @@
@SQLITE
@APIDB
Feature: Simple Tests
Simple tests for internal server errors and response format.
Scenario Outline: Testing different parameters
When sending search query "Vaduz"
| param | value |
| <parameter> | <value> |
Then at least 1 result is returned
When sending xml search query "Vaduz"
| param | value |
| <parameter> | <value> |
Then at least 1 result is returned
When sending json search query "Vaduz"
| param | value |
| <parameter> | <value> |
Then at least 1 result is returned
When sending jsonv2 search query "Vaduz"
| param | value |
| <parameter> | <value> |
Then at least 1 result is returned
When sending geojson search query "Vaduz"
| param | value |
| <parameter> | <value> |
Then at least 1 result is returned
When sending geocodejson search query "Vaduz"
| param | value |
| <parameter> | <value> |
Then at least 1 result is returned
Examples:
| parameter | value |
| addressdetails | 0 |
| polygon_text | 0 |
| polygon_kml | 0 |
| polygon_geojson | 0 |
| polygon_svg | 0 |
| accept-language | de,en |
| countrycodes | li |
| bounded | 1 |
| bounded | 0 |
| exclude_place_ids| 385252,1234515 |
| limit | 1000 |
| dedupe | 1 |
| dedupe | 0 |
| extratags | 0 |
| 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 json
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
When sending geojson search query "<query>"
Then the result is valid geojson
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 |
| more_url | .*q=xnznxvcx.*format=xml |
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 |
| more_url | .*q=xfdghn%26zxn%22xvbyx%3Cvxx%3Ecssdex.*format=xml |
Scenario: Empty XML search with viewbox
When sending xml search query "xnznxvcx"
| viewbox |
| 12,33,77,45.13 |
Then result header contains
| attr | value |
| querystring | xnznxvcx |
| viewbox | 12,33,77,45.13 |
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 |
| viewbox | 12,34.13,77,45 |
Scenario: Empty XML search with viewboxlbrt and viewbox
When sending xml search query "pub"
| viewbox | viewboxblrt |
| 12,33,77,45.13 | 1,2,3,4 |
Then result header contains
| attr | value |
| querystring | pub |
| viewbox | 12,33,77,45.13 |
Scenario: Empty XML search with excluded 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 excluded 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 |
Scenario Outline: Wrapping of illegal jsonp search requests
When sending json search query "Tokyo"
| param | value |
|json_callback | <data> |
Then a json user error 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
Scenario Outline: Empty search
When sending <format> search query "YHlERzzx"
Then exactly 0 results are returned
Examples:
| format |
| json |
| jsonv2 |
| geojson |
| geocodejson |
Scenario: Search for non-existing coordinates
When sending json search query "-21.0,-33.0"
Then exactly 0 results are returned
Scenario: Country code selection is retained in more URL (#596)
When sending xml search query "Vaduz"
| countrycodes |
| pl,1,,invalid,undefined,%3Cb%3E,bo,, |
Then result header contains
| attr | value |
| more_url | .*&countrycodes=pl%2Cbo&.* |
Scenario Outline: Search debug output does not return errors
When sending debug search query "<query>"
Then a HTTP 200 is returned
Examples:
| query |
| Liechtenstein |
| Triesen |
| Pfarrkirche |
| Landstr 27 Steinort, Triesenberg, 9495 |
| 9497 |
| restaurant in triesen |

View File

@@ -1,79 +0,0 @@
@SQLITE
@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 |
| 9495 |
Then results contain
| type |
| ^post(al_)?code |
And result addresses contain
| postcode |
| 9495 |
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: Street with housenumber, city and postcode
When sending xml search query "" with address
| street | city | postalcode |
| 19 Am schrägen Weg | Vaduz | 9490 |
Then result addresses contain
| house_number | road |
| 19 | Am Schrägen Weg |
Scenario: Street with housenumber, city and bad postcode
When sending xml search query "" with address
| street | city | postalcode |
| 19 Am schrägen Weg | Vaduz | 9491 |
Then result addresses contain
| house_number | road |
| 19 | Am Schrägen Weg |
Scenario: Amenity, city
When sending json search query "" with address
| city | amenity |
| Vaduz | bar |
Then result addresses contain
| country |
| Liechtenstein |
And results contain
| class | type |
| amenity | ^(pub)\|(bar)\|(restaurant) |
#176
Scenario: Structured search restricts rank
When sending json search query "" with address
| city |
| Vaduz |
Then result addresses contain
| town |
| Vaduz |
#3651
Scenario: Structured search with surrounding extra characters
When sending xml search query "" with address
| street | city | postalcode |
| "19 Am schrägen Weg" | "Vaduz" | "9491" |
Then result addresses contain
| house_number | road |
| 19 | Am Schrägen Weg |

View File

@@ -1,17 +0,0 @@
@UNKNOWNDB
Feature: Status queries against unknown database
Testing status query
Scenario: Failed status as text
When sending text status query
Then a HTTP 500 is returned
And the page contents equals "ERROR: Database connection failed"
Scenario: Failed status as json
When sending json status query
Then a HTTP 200 is returned
And the result is valid json
And results contain
| status | message |
| 700 | Database connection failed |
And result has not attributes data_updated

View File

@@ -1,17 +0,0 @@
@SQLITE
@APIDB
Feature: Status queries
Testing status query
Scenario: Status as text
When sending status query
Then a HTTP 200 is returned
And the page contents equals "OK"
Scenario: Status as json
When sending json status query
Then the result is valid json
And results contain
| status | message |
| 0 | OK |
And result has attributes data_updated

358
test/bdd/conftest.py Normal file
View File

@@ -0,0 +1,358 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Fixtures for BDD test steps
"""
import sys
import json
from pathlib import Path
import psycopg
from psycopg import sql as pysql
# always test against the source
SRC_DIR = (Path(__file__) / '..' / '..' / '..').resolve()
sys.path.insert(0, str(SRC_DIR / 'src'))
import pytest
from pytest_bdd.parsers import re as step_parse
from pytest_bdd import given, when, then
pytest.register_assert_rewrite('utils')
from utils.api_runner import APIRunner
from utils.api_result import APIResult
from utils.checks import ResultAttr, COMPARATOR_TERMS
from utils.geometry_alias import ALIASES
from utils.grid import Grid
from utils.db import DBManager
from nominatim_db.config import Configuration
from nominatim_db.data.country_info import setup_country_config
def _strlist(inp):
return [s.strip() for s in inp.split(',')]
def _pretty_json(inp):
return json.dumps(inp, indent=2)
def pytest_addoption(parser, pluginmanager):
parser.addoption('--nominatim-purge', dest='NOMINATIM_PURGE', action='store_true',
help='Force recreation of test databases from scratch.')
parser.addoption('--nominatim-keep-db', dest='NOMINATIM_KEEP_DB', action='store_true',
help='Do not drop the database after tests are finished.')
parser.addoption('--nominatim-api-engine', dest='NOMINATIM_API_ENGINE',
default='falcon',
help='Chose the API engine to use when sending requests.')
parser.addoption('--nominatim-tokenizer', dest='NOMINATIM_TOKENIZER',
metavar='TOKENIZER',
help='Use the specified tokenizer for importing data into '
'a Nominatim database.')
parser.addini('nominatim_test_db', default='test_nominatim',
help='Name of the database used for running a single test.')
parser.addini('nominatim_api_test_db', default='test_api_nominatim',
help='Name of the database for storing API test data.')
parser.addini('nominatim_template_db', default='test_template_nominatim',
help='Name of database used as a template for test databases.')
@pytest.fixture
def datatable():
""" Default fixture for datatables, so that their presence can be optional.
"""
return None
@pytest.fixture
def node_grid():
""" Default fixture for node grids. Nothing set.
"""
return Grid([[]], None, None)
@pytest.fixture(scope='session', autouse=True)
def setup_country_info():
setup_country_config(Configuration(None))
@pytest.fixture(scope='session')
def template_db(pytestconfig):
""" Create a template database containing the extensions and base data
needed by Nominatim. Using the template instead of doing the full
setup can speed up the tests.
The template database will only be created if it does not exist yet
or a purge has been explicitly requested.
"""
dbm = DBManager(purge=pytestconfig.option.NOMINATIM_PURGE)
template_db = pytestconfig.getini('nominatim_template_db')
template_config = Configuration(
None, environ={'NOMINATIM_DATABASE_DSN': f"pgsql:dbname={template_db}"})
dbm.setup_template_db(template_config)
return template_db
@pytest.fixture
def def_config(pytestconfig):
dbname = pytestconfig.getini('nominatim_test_db')
return Configuration(None,
environ={'NOMINATIM_DATABASE_DSN': f"pgsql:dbname={dbname}"})
@pytest.fixture
def db(template_db, pytestconfig):
""" Set up an empty database for use with osm2pgsql.
"""
dbm = DBManager(purge=pytestconfig.option.NOMINATIM_PURGE)
dbname = pytestconfig.getini('nominatim_test_db')
dbm.create_db_from_template(dbname, template_db)
yield dbname
if not pytestconfig.option.NOMINATIM_KEEP_DB:
dbm.drop_db(dbname)
@pytest.fixture
def db_conn(db, def_config):
with psycopg.connect(def_config.get_libpq_dsn()) as conn:
info = psycopg.types.TypeInfo.fetch(conn, "hstore")
psycopg.types.hstore.register_hstore(info, conn)
yield conn
@when(step_parse(r'reverse geocoding (?P<lat>[\d.-]*),(?P<lon>[\d.-]*)'),
target_fixture='nominatim_result')
def reverse_geocode_via_api(test_config_env, pytestconfig, datatable, lat, lon):
runner = APIRunner(test_config_env, pytestconfig.option.NOMINATIM_API_ENGINE)
api_response = runner.run_step('reverse',
{'lat': float(lat), 'lon': float(lon)},
datatable, 'jsonv2', {})
assert api_response.status == 200
assert api_response.headers['content-type'] == 'application/json; charset=utf-8'
result = APIResult('json', 'reverse', api_response.body)
assert result.is_simple()
assert isinstance(result.result['lat'], str)
assert isinstance(result.result['lon'], str)
result.result['centroid'] = f"POINT({result.result['lon']} {result.result['lat']})"
return result
@when(step_parse(r'reverse geocoding at node (?P<node>[\d]+)'),
target_fixture='nominatim_result')
def reverse_geocode_via_api_and_grid(test_config_env, pytestconfig, node_grid, datatable, node):
coords = node_grid.get(node)
if coords is None:
raise ValueError('Unknown node id')
return reverse_geocode_via_api(test_config_env, pytestconfig, datatable, coords[1], coords[0])
@when(step_parse(r'geocoding(?: "(?P<query>.*)")?'),
target_fixture='nominatim_result')
def forward_geocode_via_api(test_config_env, pytestconfig, datatable, query):
runner = APIRunner(test_config_env, pytestconfig.option.NOMINATIM_API_ENGINE)
params = {'addressdetails': '1'}
if query:
params['q'] = query
api_response = runner.run_step('search', params, datatable, 'jsonv2', {})
assert api_response.status == 200
assert api_response.headers['content-type'] == 'application/json; charset=utf-8'
result = APIResult('json', 'search', api_response.body)
assert not result.is_simple()
for res in result.result:
assert isinstance(res['lat'], str)
assert isinstance(res['lon'], str)
res['centroid'] = f"POINT({res['lon']} {res['lat']})"
return result
@then(step_parse(r'(?P<op>[a-z ]+) (?P<num>\d+) results? (?:are|is) returned'),
converters={'num': int})
def check_number_of_results(nominatim_result, op, num):
assert not nominatim_result.is_simple()
assert COMPARATOR_TERMS[op](num, len(nominatim_result))
@then(step_parse('the result metadata contains'))
def check_metadata_for_fields(nominatim_result, datatable):
if datatable[0] == ['param', 'value']:
pairs = datatable[1:]
else:
pairs = zip(datatable[0], datatable[1])
for k, v in pairs:
assert ResultAttr(nominatim_result.meta, k) == v
@then(step_parse('the result metadata has no attributes (?P<attributes>.*)'),
converters={'attributes': _strlist})
def check_metadata_for_field_presence(nominatim_result, attributes):
assert all(a not in nominatim_result.meta for a in attributes), \
f"Unexpectedly have one of the attributes '{attributes}' in\n" \
f"{_pretty_json(nominatim_result.meta)}"
@then(step_parse(r'the result contains(?: in field (?P<field>\S+))?'))
def check_result_for_fields(nominatim_result, datatable, node_grid, field):
assert nominatim_result.is_simple()
if datatable[0] == ['param', 'value']:
pairs = datatable[1:]
else:
pairs = zip(datatable[0], datatable[1])
prefix = field + '+' if field else ''
for k, v in pairs:
assert ResultAttr(nominatim_result.result, prefix + k, grid=node_grid) == v
@then(step_parse('the result has attributes (?P<attributes>.*)'),
converters={'attributes': _strlist})
def check_result_for_field_presence(nominatim_result, attributes):
assert nominatim_result.is_simple()
assert all(a in nominatim_result.result for a in attributes)
@then(step_parse('the result has no attributes (?P<attributes>.*)'),
converters={'attributes': _strlist})
def check_result_for_field_absence(nominatim_result, attributes):
assert nominatim_result.is_simple()
assert all(a not in nominatim_result.result for a in attributes)
@then(step_parse('the result set contains(?P<exact> exactly)?'))
def check_result_list_match(nominatim_result, datatable, exact):
assert not nominatim_result.is_simple()
result_set = set(range(len(nominatim_result.result)))
for row in datatable[1:]:
for idx in result_set:
for key, value in zip(datatable[0], row):
if ResultAttr(nominatim_result.result[idx], key) != value:
break
else:
# found a match
result_set.remove(idx)
break
else:
assert False, f"Missing data row {row}. Full response:\n{nominatim_result}"
if exact:
assert not [nominatim_result.result[i] for i in result_set]
@then(step_parse('all results have attributes (?P<attributes>.*)'),
converters={'attributes': _strlist})
def check_all_results_for_field_presence(nominatim_result, attributes):
assert not nominatim_result.is_simple()
assert len(nominatim_result) > 0
for res in nominatim_result.result:
assert all(a in res for a in attributes), \
f"Missing one of the attributes '{attributes}' in\n{_pretty_json(res)}"
@then(step_parse('all results have no attributes (?P<attributes>.*)'),
converters={'attributes': _strlist})
def check_all_result_for_field_absence(nominatim_result, attributes):
assert not nominatim_result.is_simple()
assert len(nominatim_result) > 0
for res in nominatim_result.result:
assert all(a not in res for a in attributes), \
f"Unexpectedly have one of the attributes '{attributes}' in\n{_pretty_json(res)}"
@then(step_parse(r'all results contain(?: in field (?P<field>\S+))?'))
def check_all_results_contain(nominatim_result, datatable, node_grid, field):
assert not nominatim_result.is_simple()
assert len(nominatim_result) > 0
if datatable[0] == ['param', 'value']:
pairs = datatable[1:]
else:
pairs = zip(datatable[0], datatable[1])
prefix = field + '+' if field else ''
for k, v in pairs:
for r in nominatim_result.result:
assert ResultAttr(r, prefix + k, grid=node_grid) == v
@then(step_parse(r'result (?P<num>\d+) contains(?: in field (?P<field>\S+))?'),
converters={'num': int})
def check_specific_result_for_fields(nominatim_result, datatable, num, field):
assert not nominatim_result.is_simple()
assert len(nominatim_result) > num
if datatable[0] == ['param', 'value']:
pairs = datatable[1:]
else:
pairs = zip(datatable[0], datatable[1])
prefix = field + '+' if field else ''
for k, v in pairs:
assert ResultAttr(nominatim_result.result[num], prefix + k) == v
@given(step_parse(r'the (?P<step>[0-9.]+ )?grid(?: with origin (?P<origin>.*))?'),
target_fixture='node_grid')
def set_node_grid(datatable, step, origin):
if step is not None:
step = float(step)
if origin:
if ',' in origin:
coords = origin.split(',')
if len(coords) != 2:
raise RuntimeError('Grid origin expects origin with x,y coordinates.')
origin = list(map(float, coords))
elif origin in ALIASES:
origin = ALIASES[origin]
else:
raise RuntimeError('Grid origin must be either coordinate or alias.')
return Grid(datatable, step, origin)
@then(step_parse('(?P<table>placex?) has no entry for '
r'(?P<osm_type>[NRW])(?P<osm_id>\d+)(?::(?P<osm_class>\S+))?'),
converters={'osm_id': int})
def check_place_missing_lines(db_conn, table, osm_type, osm_id, osm_class):
sql = pysql.SQL("""SELECT count(*) FROM {}
WHERE osm_type = %s and osm_id = %s""").format(pysql.Identifier(table))
params = [osm_type, int(osm_id)]
if osm_class:
sql += pysql.SQL(' AND class = %s')
params.append(osm_class)
with db_conn.cursor() as cur:
assert cur.execute(sql, params).fetchone()[0] == 0

View File

@@ -1,105 +0,0 @@
@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 | 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 | 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 | 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 | country_code | name | name+name:fi | name+name:de |
| N1 | de | german | finnish | local |
Scenario Outline: Names in any script can be found
Given the places
| osm | class | type | name |
| N1 | place | hamlet | <name> |
When importing
And sending search query "<name>"
Then results contain
| osm |
| N1 |
Examples:
| name |
| Berlin |
| |
| Вологда |
| Αθήνα |
| القاهرة |
| |
| |
| |
Scenario: German umlauts can be found when expanded
Given the places
| osm | class | type | name+name:de |
| N1 | place | city | Münster |
| N2 | place | city | Köln |
| N3 | place | city | Gräfenroda |
When importing
When sending search query "münster"
Then results contain
| osm |
| N1 |
When sending search query "muenster"
Then results contain
| osm |
| N1 |
When sending search query "munster"
Then results contain
| osm |
| N1 |
When sending search query "Köln"
Then results contain
| osm |
| N2 |
When sending search query "Koeln"
Then results contain
| osm |
| N2 |
When sending search query "Koln"
Then results contain
| osm |
| N2 |
When sending search query "gräfenroda"
Then results contain
| osm |
| N3 |
When sending search query "graefenroda"
Then results contain
| osm |
| N3 |
When sending search query "grafenroda"
Then results contain
| osm |
| N3 |

View File

@@ -1,226 +0,0 @@
@DB
Feature: Import and search of names
Tests all naming related issues: normalisation,
abbreviations, internationalisation, etc.
Scenario: non-latin scripts can be found
Given the places
| osm | class | type | name |
| N1 | place | locality | Речицкий район |
| N2 | place | locality | Refugio de montaña |
| N3 | place | locality | |
| N4 | place | locality | الدوحة |
When importing
When sending search query "Речицкий район"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "Refugio de montaña"
Then results contain
| ID | osm |
| 0 | N2 |
When sending search query ""
Then results contain
| ID | osm |
| 0 | N3 |
When sending search query "الدوحة"
Then results contain
| ID | osm |
| 0 | N4 |
Scenario: Case-insensitivity of search
Given the places
| osm | class | type | name |
| N1 | place | locality | FooBar |
When importing
Then placex contains
| object | class | type | name+name |
| N1 | place | locality | FooBar |
When sending search query "FooBar"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "foobar"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "fOObar"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "FOOBAR"
Then results contain
| ID | osm |
| 0 | N1 |
Scenario: Multiple spaces in name
Given the places
| osm | class | type | name |
| N1 | place | locality | one two three |
When importing
When sending search query "one two three"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "one two three"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "one two three"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query " one two three"
Then results contain
| ID | osm |
| 0 | N1 |
Scenario: Special characters in name
Given the places
| osm | class | type | name+name:de |
| N1 | place | locality | Jim-Knopf-Straße |
| N2 | place | locality | Smith/Weston |
| N3 | place | locality | space mountain |
| N4 | place | locality | space |
| N5 | place | locality | mountain |
When importing
When sending search query "Jim-Knopf-Str"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "Jim Knopf-Str"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "Jim Knopf Str"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "Jim/Knopf-Str"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "Jim-Knopfstr"
Then results contain
| ID | osm |
| 0 | N1 |
When sending search query "Smith/Weston"
Then results contain
| ID | osm |
| 0 | N2 |
When sending search query "Smith Weston"
Then results contain
| ID | osm |
| 0 | N2 |
When sending search query "Smith-Weston"
Then results contain
| ID | osm |
| 0 | N2 |
When sending search query "space mountain"
Then results contain
| ID | osm |
| 0 | N3 |
When sending search query "space-mountain"
Then results contain
| ID | osm |
| 0 | N3 |
When sending search query "space/mountain"
Then results contain
| ID | osm |
| 0 | N3 |
When sending search query "space\mountain"
Then results contain
| ID | osm |
| 0 | N3 |
When sending search query "space(mountain)"
Then results contain
| ID | osm |
| 0 | N3 |
Scenario: Landuse with name are found
Given the grid
| 1 | 2 |
| 3 | |
Given the places
| osm | class | type | name | geometry |
| R1 | natural | meadow | landuse1 | (1,2,3,1) |
| R2 | landuse | industrial | landuse2 | (2,3,1,2) |
When importing
When sending search query "landuse1"
Then results contain
| ID | osm |
| 0 | R1 |
When sending search query "landuse2"
Then results contain
| ID | osm |
| 0 | R2 |
Scenario: Postcode boundaries without ref
Given the grid with origin FR
| | 2 | |
| 1 | | 3 |
Given the places
| osm | class | type | postcode | geometry |
| R1 | boundary | postal_code | 123-45 | (1,2,3,1) |
When importing
When sending search query "123-45"
Then results contain
| ID | osm |
| 0 | R1 |
Scenario Outline: Housenumbers with special characters are found
Given the grid
| 1 | | | | 2 |
| | | 9 | | |
And the places
| osm | class | type | name | geometry |
| W1 | highway | primary | Main St | 1,2 |
And the places
| osm | class | type | housenr | geometry |
| N1 | building | yes | <nr> | 9 |
When importing
And sending search query "Main St <nr>"
Then results contain
| osm | display_name |
| N1 | <nr>, Main St |
Examples:
| nr |
| 1 |
| 3456 |
| 1 a |
| 56b |
| 1 A |
| 2 |
| 1Б |
| 1 к1 |
| 23-123 |
Scenario Outline: Housenumbers in lists are found
Given the grid
| 1 | | | | 2 |
| | | 9 | | |
And the places
| osm | class | type | name | geometry |
| W1 | highway | primary | Main St | 1,2 |
And the places
| osm | class | type | housenr | geometry |
| N1 | building | yes | <nr-list> | 9 |
When importing
And sending search query "Main St <nr>"
Then results contain
| ID | osm | display_name |
| 0 | N1 | <nr-list>, Main St |
Examples:
| nr-list | nr |
| 1,2,3 | 1 |
| 1,2,3 | 2 |
| 1, 2, 3 | 3 |
| 45 ;67;3 | 45 |
| 45 ;67;3 | 67 |
| 1a;1k | 1a |
| 1a;1k | 1k |
| 34/678 | 34 |
| 34/678 | 678 |
| 34/678 | 34/678 |

View File

@@ -1,64 +0,0 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
from pathlib import Path
import sys
from behave import * # noqa
sys.path.insert(1, str(Path(__file__, '..', '..', '..', 'src').resolve()))
from steps.geometry_factory import GeometryFactory # noqa: E402
from steps.nominatim_environment import NominatimEnvironment # noqa: E402
TEST_BASE_DIR = Path(__file__, '..', '..').resolve()
userconfig = {
'REMOVE_TEMPLATE': False,
'KEEP_TEST_DB': False,
'DB_HOST': None,
'DB_PORT': None,
'DB_USER': None,
'DB_PASS': None,
'TEMPLATE_DB': 'test_template_nominatim',
'TEST_DB': 'test_nominatim',
'API_TEST_DB': 'test_api_nominatim',
'API_TEST_FILE': TEST_BASE_DIR / 'testdb' / 'apidb-test-data.pbf',
'TOKENIZER': None, # Test with a custom tokenizer
'STYLE': 'extratags',
'API_ENGINE': 'falcon'
}
use_step_matcher("re") # noqa: F405
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)
# Nominatim test setup
context.nominatim = NominatimEnvironment(context.config.userdata)
context.osm = GeometryFactory()
def before_scenario(context, scenario):
if 'SQLITE' not in context.tags \
and context.config.userdata['API_TEST_DB'].startswith('sqlite:'):
context.scenario.skip("Not usable with Sqlite database.")
elif 'DB' in context.tags:
context.nominatim.setup_db(context)
elif 'APIDB' in context.tags:
context.nominatim.setup_api_db()
elif 'UNKNOWNDB' in context.tags:
context.nominatim.setup_unknown_db()
def after_scenario(context, scenario):
if 'DB' in context.tags:
context.nominatim.teardown_db(context)

View File

@@ -0,0 +1,83 @@
Feature: Localization of search results
Scenario: default language
When sending v1/details
| osmtype | osmid |
| R | 1155955 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| localname |
| Liechtenstein |
Scenario: accept-language first
When sending v1/details
| osmtype | osmid | accept-language |
| R | 1155955 | zh,de |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| localname |
| |
Scenario: accept-language missing
When sending v1/details
| osmtype | osmid | accept-language |
| R | 1155955 | xx,fr,en,de |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| localname |
| Liechtenstein |
Scenario: http accept language header first
Given the HTTP header
| accept-language |
| fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
When sending v1/details
| osmtype | osmid |
| R | 1155955 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| localname |
| Liktinstein |
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 v1/details
| osmtype | osmid | accept-language |
| R | 1155955 | fo,en |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| localname |
| Liktinstein |
Scenario: http accept language header fallback
Given the HTTP header
| accept-language |
| fo-ca,en-ca;q=0.5 |
When sending v1/details
| osmtype | osmid |
| R | 1155955 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| localname |
| Liktinstein |
Scenario: http accept language header fallback (upper case)
Given the HTTP header
| accept-language |
| fo-FR;q=0.8,en-ca;q=0.5 |
When sending v1/details
| osmtype | osmid |
| R | 1155955 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| localname |
| Liktinstein |

View File

@@ -0,0 +1,99 @@
Feature: Object details
Testing different parameter options for details API.
Scenario: Basic details
When sending v1/details
| osmtype | osmid |
| W | 297699560 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes geometry
And the result has no attributes keywords,address,linked_places,parentof
And the result contains
| geometry+type |
| Point |
Scenario: Basic details with pretty printing
When sending v1/details
| osmtype | osmid | pretty |
| W | 297699560 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes geometry
And the result has no attributes keywords,address,linked_places,parentof
Scenario: Details with addressdetails
When sending v1/details
| osmtype | osmid | addressdetails |
| W | 297699560 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes address
Scenario: Details with linkedplaces
When sending v1/details
| osmtype | osmid | linkedplaces |
| R | 123924 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes linked_places
Scenario: Details with hierarchy
When sending v1/details
| osmtype | osmid | hierarchy |
| W | 297699560 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes hierarchy
Scenario: Details with grouped hierarchy
When sending v1/details
| osmtype | osmid | hierarchy | group_hierarchy |
| W | 297699560 | 1 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes hierarchy
Scenario Outline: Details with keywords
When sending v1/details
| osmtype | osmid | keywords |
| <type> | <id> | 1 |
Then a HTTP 200 is returned
Then the result is valid json
And the result has attributes keywords
Examples:
| type | id |
| W | 297699560 |
| W | 243055645 |
| W | 243055716 |
| W | 43327921 |
# ticket #1343
Scenario: Details of a country with keywords
When sending v1/details
| osmtype | osmid | keywords |
| R | 1155955 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes keywords
Scenario Outline: Details with full geometry
When sending v1/details
| osmtype | osmid | polygon_geojson |
| <type> | <id> | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes geometry
And the result contains
| geometry+type |
| <geometry> |
Examples:
| type | id | geometry |
| W | 297699560 | LineString |
| W | 243055645 | Polygon |
| W | 243055716 | Polygon |
| W | 43327921 | LineString |

View File

@@ -0,0 +1,99 @@
Feature: Object details
Check details page for correctness
Scenario Outline: Details request with OSM id
When sending v1/details
| osmtype | osmid |
| <type> | <id> |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| osm_type | osm_id |
| <type> | <id> |
Examples:
| type | id |
| N | 5484325405 |
| W | 43327921 |
| R | 123924 |
Scenario Outline: Details request with different class types for the same OSM id
When sending v1/details
| osmtype | osmid | class |
| N | 300209696 | <class> |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| osm_type | osm_id | category |
| N | 300209696 | <class> |
Examples:
| class |
| tourism |
| mountain_pass |
Scenario: Details request without osmtype
When sending v1/details
| osmid |
| <id> |
Then a HTTP 400 is returned
And the result is valid json
Scenario: Details request with unknown OSM id
When sending v1/details
| osmtype | osmid |
| R | 1 |
Then a HTTP 404 is returned
And the result is valid json
Scenario: Details request with unknown class
When sending v1/details
| osmtype | osmid | class |
| N | 300209696 | highway |
Then a HTTP 404 is returned
And the result is valid json
Scenario: Details for interpolation way return the interpolation
When sending v1/details
| osmtype | osmid |
| W | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| category | type | osm_type | osm_id | admin_level |
| place | houses | W | 1 | 15 |
@skip
Scenario: Details for interpolation way return the interpolation
When sending details query for 112871
Then the result is valid json
And the result contains
| category | type | admin_level |
| place | houses | 15 |
And result has not attributes osm_type,osm_id
@skip
Scenario: Details for postcode
When sending details query for 112820
Then the result is valid json
And the result contains
| category | type | admin_level |
| place | postcode | 15 |
And result has not attributes osm_type,osm_id
Scenario Outline: Details debug output returns no errors
When sending v1/details
| osmtype | osmid | debug |
| <type> | <id> | 1 |
Then a HTTP 200 is returned
And the result is valid html
Examples:
| type | id |
| N | 5484325405 |
| W | 43327921 |
| R | 123924 |

View File

@@ -0,0 +1,71 @@
Feature: Tests for finding places by osm_type and osm_id
Simple tests for response format.
Scenario Outline: Address lookup for existing object
When sending v1/lookup with format <format>
| osm_ids |
| N5484325405,W43327921,,R123924,X99,N0 |
Then a HTTP 200 is returned
And the result is valid <outformat>
And exactly 3 results are returned
Examples:
| format | outformat |
| xml | xml |
| json | json |
| jsonv2 | json |
| geojson | geojson |
| geocodejson | geocodejson |
Scenario: Address lookup for non-existing or invalid object
When sending v1/lookup
| osm_ids |
| X99,,N0,nN158845944,ABC,,W9 |
Then a HTTP 200 is returned
And the result is valid xml
And exactly 0 results are returned
Scenario Outline: Boundingbox is returned
When sending v1/lookup with format <format>
| osm_ids |
| N5484325405,W43327921 |
Then the result is valid <outformat>
And the result set contains exactly
| object | boundingbox!in_box |
| N5484325405 | 47.135,47.14,9.52,9.525 |
| W43327921 | 47.07,47.08,9.50,9.52 |
Examples:
| format | outformat |
| xml | xml |
| json | json |
| jsonv2 | json |
| geojson | geojson |
Scenario: Linked places return information from the linkee
When sending v1/lookup with format geocodejson
| osm_ids |
| N1932181216 |
Then the result is valid geocodejson
And exactly 1 result is returned
And all results contain
| name |
| Vaduz |
Scenario Outline: Force error by providing too many ids
When sending v1/lookup with format <format>
| osm_ids |
| N1,N2,N3,N4,N5,N6,N7,N8,N9,N10,N11,N12,N13,N14,N15,N16,N17,N18,N19,N20,N21,N22,N23,N24,N25,N26,N27,N28,N29,N30,N31,N32,N33,N34,N35,N36,N37,N38,N39,N40,N41,N42,N43,N44,N45,N46,N47,N48,N49,N50,N51 |
Then a HTTP 400 is returned
And the result is valid <outformat>
And the result contains
| error+code | error+message |
| 400 | Too many object IDs. |
Examples:
| format | outformat |
| xml | xml |
| json | json |
| jsonv2 | json |
| geojson | json |
| geocodejson | json |

View File

@@ -0,0 +1,56 @@
Feature: Geometries for reverse geocoding
Tests for returning geometries with reverse
Scenario: Reverse - polygons are returned fully by default
When sending v1/reverse
| lat | lon | polygon_text |
| 47.13803 | 9.52264 | 1 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| geotext!fm |
| POLYGON\(\(9.5225302 47.138066, ?9.5225348 47.1379282, ?9.5226142 47.1379294, ?9.5226143 47.1379257, ?9.522615 47.137917, ?9.5226225 47.1379098, ?9.5226334 47.1379052, ?9.5226461 47.1379037, ?9.5226588 47.1379056, ?9.5226693 47.1379107, ?9.5226762 47.1379181, ?9.5226762 47.1379268, ?9.5226761 47.1379308, ?9.5227366 47.1379317, ?9.5227352 47.1379753, ?9.5227608 47.1379757, ?9.5227595 47.1380148, ?9.5227355 47.1380145, ?9.5227337 47.1380692, ?9.5225302 47.138066\)\) |
Scenario: Reverse - polygons can be slightly simplified
When sending v1/reverse
| lat | lon | polygon_text | polygon_threshold |
| 47.13803 | 9.52264 | 1 | 0.00001 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| geotext!fm |
| POLYGON\(\(9.5225302 47.138066, ?9.5225348 47.1379282, ?9.5226142 47.1379294, ?9.5226225 47.1379098, ?9.5226588 47.1379056, ?9.5226761 47.1379308, ?9.5227366 47.1379317, ?9.5227352 47.1379753, ?9.5227608 47.1379757, ?9.5227595 47.1380148, ?9.5227355 47.1380145, ?9.5227337 47.1380692, ?9.5225302 47.138066\)\) |
Scenario: Reverse - polygons can be much simplified
When sending v1/reverse
| lat | lon | polygon_text | polygon_threshold |
| 47.13803 | 9.52264 | 1 | 0.9 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| geotext!fm |
| POLYGON\(\([0-9. ]+, ?[0-9. ]+, ?[0-9. ]+, ?[0-9. ]+(, ?[0-9. ]+)?\)\) |
Scenario: Reverse - for polygons return the centroid as center point
When sending v1/reverse
| lat | lon |
| 47.13836 | 9.52304 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| lon | lat |
| 9.5227108 | 47.1381805 |
Scenario: Reverse - for streets return the closest point as center point
When sending v1/reverse
| lat | lon |
| 47.13368 | 9.52942 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| lon | lat |
| 9.5294315 | 47.1336817 |

View File

@@ -0,0 +1,47 @@
Feature: Localization of reverse search results
Scenario: Reverse - default language
When sending v1/reverse with format jsonv2
| lat | lon |
| 47.14 | 9.55 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| address+country |
| Liechtenstein |
Scenario: Reverse - accept-language parameter
When sending v1/reverse with format jsonv2
| lat | lon | accept-language |
| 47.14 | 9.55 | ja,en |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| address+country |
| |
Scenario: Reverse - HTTP accept language header
Given the HTTP header
| accept-language |
| fo-ca,fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
When sending v1/reverse with format jsonv2
| lat | lon |
| 47.14 | 9.55 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| address+country |
| Liktinstein |
Scenario: Reverse - accept-language parameter and HTTP header
Given the HTTP header
| accept-language |
| fo-ca,fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
When sending v1/reverse with format jsonv2
| lat | lon | accept-language |
| 47.14 | 9.55 | en |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| address+country |
| Liechtenstein |

View File

@@ -1,24 +1,20 @@
@SQLITE
@APIDB
Feature: Layer parameter in reverse geocoding
Testing correct function of layer selection while reverse geocoding
Scenario: POIs are selected by default
When sending v1/reverse at 47.14077,9.52414
Then results contain
When reverse geocoding 47.14077,9.52414
Then the result contains
| category | type |
| tourism | viewpoint |
Scenario Outline: Same address level POI with different layers
When sending v1/reverse at 47.14077,9.52414
When reverse geocoding 47.14077,9.52414
| layer |
| <layer> |
Then results contain
Then the result contains
| category |
| <category> |
Examples:
| layer | category |
| address | highway |
@@ -28,12 +24,11 @@ Feature: Layer parameter in reverse geocoding
| address,natural | highway |
| natural,poi | tourism |
Scenario Outline: POIs are not selected without housenumber for address layer
When sending v1/reverse at 47.13816,9.52168
When reverse geocoding 47.13816,9.52168
| layer |
| <layer> |
Then results contain
Then the result contains
| category | type |
| <category> | <type> |
@@ -42,21 +37,19 @@ Feature: Layer parameter in reverse geocoding
| address,poi | highway | bus_stop |
| address | amenity | parking |
Scenario: Between natural and low-zoom address prefer natural
When sending v1/reverse at 47.13636,9.52094
When reverse geocoding 47.13636,9.52094
| layer | zoom |
| natural,address | 15 |
Then results contain
Then the result contains
| category |
| waterway |
Scenario Outline: Search for mountain peaks begins at level 12
When sending v1/reverse at 47.08293,9.57109
When reverse geocoding 47.08293,9.57109
| layer | zoom |
| natural | <zoom> |
Then results contain
Then the result contains
| category | type |
| <category> | <type> |
@@ -65,12 +58,11 @@ Feature: Layer parameter in reverse geocoding
| 12 | natural | peak |
| 13 | waterway | river |
Scenario Outline: Reverse search with manmade layers
When sending v1/reverse at 32.46904,-86.44439
When reverse geocoding 32.46904,-86.44439
| layer |
| <layer> |
Then results contain
Then the result contains
| category | type |
| <category> | <type> |

View File

@@ -0,0 +1,80 @@
Feature: Reverse geocoding
Testing the reverse function
Scenario: Reverse - Unknown countries fall back to default country grid
When reverse geocoding 45.174,-103.072
Then the result contains
| category | type | display_name |
| place | country | United States |
Scenario: Reverse - No TIGER house number for zoom < 18
When reverse geocoding 32.4752389363,-86.4810198619
| zoom |
| 17 |
Then the result contains
| osm_type | category |
| way | highway |
And the result contains in field address
| road | postcode | country_code |
| Upper Kingston Road | 36067 | us |
Scenario: Reverse - Address with non-numerical house number
When reverse geocoding 47.107465,9.52838521614
Then the result contains in field address
| house_number | road |
| 39A/B | Dorfstrasse |
Scenario: Reverse - Address with numerical house number
When reverse geocoding 47.168440329479594,9.511551699184338
Then the result contains in field address
| house_number | road |
| 6 | Schmedgässle |
Scenario Outline: Reverse - Zoom levels below 5 result in country
When reverse geocoding 47.16,9.51
| zoom |
| <zoom> |
Then the result contains
| display_name |
| Liechtenstein |
Examples:
| zoom |
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
Scenario: Reverse - When on a street, the closest interpolation is shown
When reverse geocoding 47.118457166193245,9.570678289621355
| zoom |
| 18 |
Then the result contains
| display_name |
| 1021, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
# github 2214
Scenario: Reverse - Interpolations do not override house numbers when they are closer
When reverse geocoding 47.11778,9.57255
| zoom |
| 18 |
Then the result contains
| display_name |
| 5, Grosssteg, Steg, Triesenberg, Oberland, 9497, Liechtenstein |
Scenario: Reverse - Interpolations do not override house numbers when they are closer (2)
When reverse geocoding 47.11834,9.57167
| zoom |
| 18 |
Then the result contains
| display_name |
| 3, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
Scenario: Reverse - When on a street with zoom 18, the closest housenumber is returned
When reverse geocoding 47.11755503977281,9.572722250405036
| zoom |
| 18 |
Then the result contains in field address
| house_number |
| 7 |

View File

@@ -0,0 +1,143 @@
Feature: Geocodejson for Reverse API
Testing correctness of geocodejson output (API version v1).
Scenario Outline: Reverse geocodejson - Simple with no results
When sending v1/reverse with format geocodejson
| lat | lon |
| <lat> | <lon> |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| error |
| Unable to geocode |
Examples:
| lat | lon |
| 0.0 | 0.0 |
| 91.3 | 0.4 |
| -700 | 0.4 |
| 0.2 | 324.44 |
| 0.2 | -180.4 |
Scenario Outline: Reverse geocodejson - Simple OSM result
When sending v1/reverse with format geocodejson
| lat | lon | addressdetails |
| 47.066 | 9.504 | <has_address> |
Then a HTTP 200 is returned
And the result is valid geocodejson with 1 result
And the result metadata contains
| version | licence | attribution!fm |
| 0.1.0 | ODbL | Data © OpenStreetMap contributors, ODbL 1.0. https?://osm.org/copyright |
And all results have <attributes> country,postcode,county,city,district,street,housenumber,admin
And all results contain
| param | value |
| osm_type | node |
| osm_id | 6522627624 |
| osm_key | shop |
| osm_value | bakery |
| type | house |
| name | Dorfbäckerei Herrmann |
| label | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
| geojson+type | Point |
| geojson+coordinates | [9.5036065, 47.0660892] |
Examples:
| has_address | attributes |
| 1 | attributes |
| 0 | no attributes |
Scenario: Reverse geocodejson - City housenumber-level address with street
When sending v1/reverse with format geocodejson
| lat | lon |
| 47.1068011 | 9.52810091 |
Then a HTTP 200 is returned
And the result is valid geocodejson with 1 result
And all results contain
| housenumber | street | postcode | city | country |
| 8 | Im Winkel | 9495 | Triesen | Liechtenstein |
And all results contain
| admin+level6 | admin+level8 |
| Oberland | Triesen |
Scenario: Reverse geocodejson - Town street-level address with street
When sending v1/reverse with format geocodejson
| lat | lon | zoom |
| 47.066 | 9.504 | 16 |
Then a HTTP 200 is returned
And the result is valid geocodejson with 1 result
And all results contain
| name | city | postcode | country |
| Gnetsch | Balzers | 9496 | Liechtenstein |
Scenario: Reverse geocodejson - Poi street-level address with footway
When sending v1/reverse with format geocodejson
| lat | lon |
| 47.06515 | 9.50083 |
Then a HTTP 200 is returned
And the result is valid geocodejson with 1 result
And all results contain
| street | city | postcode | country |
| Burgweg | Balzers | 9496 | Liechtenstein |
Scenario: Reverse geocodejson - City address with suburb
When sending v1/reverse with format geocodejson
| lat | lon |
| 47.146861 | 9.511771 |
Then a HTTP 200 is returned
And the result is valid geocodejson with 1 result
And all results contain
| housenumber | street | district | city | postcode | country |
| 5 | Lochgass | Ebenholz | Vaduz | 9490 | Liechtenstein |
Scenario: Reverse geocodejson - Tiger address
When sending v1/reverse with format geocodejson
| lat | lon |
| 32.4752389363 | -86.4810198619 |
Then a HTTP 200 is returned
And the result is valid geocodejson with 1 result
And all results contain
| osm_type | osm_id | osm_key | osm_value | type |
| way | 396009653 | place | house | house |
And all results contain
| housenumber | street | city | county | postcode | country |
| 707 | Upper Kingston Road | Prattville | Autauga County | 36067 | United States |
Scenario: Reverse geocodejson - Interpolation address
When sending v1/reverse with format geocodejson
| lat | lon |
| 47.118533 | 9.57056562 |
Then a HTTP 200 is returned
And the result is valid geocodejson with 1 result
And all results contain
| osm_type | osm_id | osm_key | osm_value | type |
| way | 1 | place | house | house |
And all results contain
| label |
| 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
And all results have no attributes name
Scenario: Reverse geocodejson - Line geometry output is supported
When sending v1/reverse with format geocodejson
| lat | lon | polygon_geojson |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid geocodejson with 1 result
And all results contain
| geojson+type |
| LineString |
Scenario Outline: Reverse geocodejson - Only geojson polygons are supported
When sending v1/reverse with format geocodejson
| lat | lon | <param> |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid geocodejson with 1 result
And all results contain
| geojson+type |
| Point |
Examples:
| param |
| polygon_text |
| polygon_svg |
| polygon_kml |

View File

@@ -0,0 +1,102 @@
Feature: Geojson for Reverse API
Testing correctness of geojson output (API version v1).
Scenario Outline: Reverse geojson - Simple with no results
When sending v1/reverse with format geojson
| lat | lon |
| <lat> | <lon> |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| error |
| Unable to geocode |
Examples:
| lat | lon |
| 0.0 | 0.0 |
| 91.3 | 0.4 |
| -700 | 0.4 |
| 0.2 | 324.44 |
| 0.2 | -180.4 |
Scenario Outline: Reverse geojson - Simple OSM result
When sending v1/reverse with format geojson
| lat | lon | addressdetails |
| 47.066 | 9.504 | <has_address> |
Then a HTTP 200 is returned
And the result is valid geojson with 1 result
And the result metadata contains
| licence!fm |
| Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright |
And all results have attributes place_id, importance
And all results have <attributes> address
And all results contain
| param | value |
| osm_type | node |
| osm_id | 6522627624 |
| place_rank | 30 |
| category | shop |
| type | bakery |
| addresstype | shop |
| name | Dorfbäckerei Herrmann |
| display_name | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
| boundingbox | [47.0660392, 47.0661392, 9.5035565, 9.5036565] |
| geojson+type | Point |
| geojson+coordinates | [9.5036065, 47.0660892] |
Examples:
| has_address | attributes |
| 1 | attributes |
| 0 | no attributes |
Scenario: Reverse geojson - Tiger address
When sending v1/reverse with format geojson
| lat | lon |
| 32.4752389363 | -86.4810198619 |
Then a HTTP 200 is returned
And the result is valid geojson with 1 result
And all results contain
| osm_type | osm_id | category | type | addresstype | place_rank |
| way | 396009653 | place | house | place | 30 |
Scenario: Reverse geojson - Interpolation address
When sending v1/reverse with format geojson
| lat | lon |
| 47.118533 | 9.57056562 |
Then a HTTP 200 is returned
And the result is valid geojson with 1 result
And all results contain
| osm_type | osm_id | place_rank | category | type | addresstype |
| way | 1 | 30 | place | house | place |
And all results contain
| boundingbox!in_box |
| 47.118494, 47.118596, 9.570495, 9.570597 |
And all results contain
| display_name |
| 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
Scenario: Reverse geojson - Line geometry output is supported
When sending v1/reverse with format geojson
| lat | lon | polygon_geojson |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid geojson with 1 result
And all results contain
| geojson+type |
| LineString |
Scenario Outline: Reverse geojson - Only geojson polygons are supported
When sending v1/reverse with format geojson
| lat | lon | <param> |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid geojson with 1 result
And all results contain
| geojson+type |
| Point |
Examples:
| param |
| polygon_text |
| polygon_svg |
| polygon_kml |

View File

@@ -0,0 +1,175 @@
Feature: Json output for Reverse API
Testing correctness of json and jsonv2 output (API version v1).
Scenario Outline: Reverse json - Simple with no results
When sending v1/reverse with format json
| lat | lon |
| <lat> | <lon> |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| error |
| Unable to geocode |
When sending v1/reverse with format jsonv2
| lat | lon |
| <lat> | <lon> |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| error |
| Unable to geocode |
Examples:
| lat | lon |
| 0.0 | 0.0 |
| 91.3 | 0.4 |
| -700 | 0.4 |
| 0.2 | 324.44 |
| 0.2 | -180.4 |
Scenario Outline: Reverse json - OSM result with and without addresses
When sending v1/reverse with format json
| lat | lon | addressdetails |
| 47.066 | 9.504 | <has_address> |
Then a HTTP 200 is returned
And the result is valid json
And the result has <attributes> address
When sending v1/reverse with format jsonv2
| lat | lon | addressdetails |
| 47.066 | 9.504 | <has_address> |
Then a HTTP 200 is returned
And the result is valid json
And the result has <attributes> address
Examples:
| has_address | attributes |
| 1 | attributes |
| 0 | no attributes |
Scenario Outline: Reverse json - Simple OSM result
When sending v1/reverse with format <format>
| lat | lon |
| 47.066 | 9.504 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes place_id
And the result contains
| licence!fm |
| Data © OpenStreetMap contributors, ODbL 1.0. https?://osm.org/copyright |
And the result contains
| osm_type | osm_id |
| node | 6522627624 |
And the result contains
| lon | lat | boundingbox!in_box |
| 9.5036065 | 47.0660892 | 47.0660391, 47.0661393, 9.5035564, 9.5036566 |
And the result contains
| display_name |
| Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
And the result has no attributes namedetails,extratags
Examples:
| format |
| json |
| jsonv2 |
Scenario: Reverse json - Extra attributes of jsonv2 result
When sending v1/reverse with format jsonv2
| lat | lon |
| 47.066 | 9.504 |
Then a HTTP 200 is returned
And the result is valid json
And the result has attributes importance
And the result contains
| category | type | name | place_rank | addresstype |
| shop | bakery | Dorfbäckerei Herrmann | 30 | shop |
Scenario: Reverse json - Tiger address
When sending v1/reverse with format jsonv2
| lat | lon |
| 32.4752389363 | -86.4810198619 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| osm_type | osm_id | category | type | addresstype |
| way | 396009653 | place | house | place |
Scenario Outline: Reverse json - Interpolation address
When sending v1/reverse with format <format>
| lat | lon |
| 47.118533 | 9.57056562 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| osm_type | osm_id |
| way | 1 |
And the result contains
| lon | lat | boundingbox!in_box |
| 9.5705468 | 47.1185454 | 47.118494, 47.118596, 9.570495, 9.570597 |
And the result contains
| display_name |
| 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
Examples:
| format |
| json |
| jsonv2 |
Scenario Outline: Reverse json - Output of geojson
When sending v1/reverse with format <format>
| lat | lon | polygon_geojson |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| geojson+type | geojson+coordinates |
| LineString | [[9.5039353, 47.0657546], [9.5040437, 47.0657781], [9.5040808, 47.065787], [9.5054298, 47.0661407]] |
Examples:
| format |
| json |
| jsonv2 |
Scenario Outline: Reverse json - Output of WKT
When sending v1/reverse with format <format>
| lat | lon | polygon_text |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| geotext!wkt |
| 9.5039353 47.0657546, 9.5040437 47.0657781, 9.5040808 47.065787, 9.5054298 47.0661407 |
Examples:
| format |
| json |
| jsonv2 |
Scenario Outline: Reverse json - Output of SVG
When sending v1/reverse with format <format>
| lat | lon | polygon_svg |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| svg |
| M 9.5039353 -47.0657546 L 9.5040437 -47.0657781 9.5040808 -47.065787 9.5054298 -47.0661407 |
Examples:
| format |
| json |
| jsonv2 |
Scenario Outline: Reverse json - Output of KML
When sending v1/reverse with format <format>
| lat | lon | polygon_kml |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| geokml!fm |
| <LineString><coordinates>9.5039\d*,47.0657\d* 9.5040\d*,47.0657\d* 9.5040\d*,47.065\d* 9.5054\d*,47.0661\d*</coordinates></LineString> |
Examples:
| format |
| json |
| jsonv2 |

View File

@@ -0,0 +1,169 @@
Feature: v1/reverse Parameter Tests
Tests for parameter inputs for the v1 reverse endpoint.
This file contains mostly bad parameter input. Valid parameters
are tested in the format tests.
Scenario: Bad format
When sending v1/reverse
| lat | lon | format |
| 47.14122383 | 9.52169581334 | sdf |
Then a HTTP 400 is returned
Scenario: Missing lon parameter
When sending v1/reverse
| lat |
| 52.52 |
Then a HTTP 400 is returned
Scenario: Missing lat parameter
When sending v1/reverse
| lon |
| 52.52 |
Then a HTTP 400 is returned
Scenario Outline: Bad format for lat or lon
When sending v1/reverse
| 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 |
| Inf | 5.6 |
| 5.6 | -Inf |
| <script></script> | 3.4 |
| 3.4 | <script></script> |
| -45.3 | ; |
| gkjd | 50 |
Scenario: Non-numerical zoom levels return an error
When sending v1/reverse
| lat | lon | zoom |
| 47.14122383 | 9.52169581334 | adfe |
Then a HTTP 400 is returned
Scenario Outline: Truthy values for boolean parameters
When sending v1/reverse
| lat | lon | addressdetails |
| 47.14122383 | 9.52169581334 | <value> |
Then a HTTP 200 is returned
And the result is valid xml
And the result has attributes address
When sending v1/reverse
| lat | lon | extratags |
| 47.14122383 | 9.52169581334 | <value> |
Then a HTTP 200 is returned
And the result is valid xml
And the result has attributes extratags
When sending v1/reverse
| lat | lon | namedetails |
| 47.14122383 | 9.52169581334 | <value> |
Then a HTTP 200 is returned
And the result is valid xml
And the result has attributes namedetails
Examples:
| value |
| yes |
| no |
| -1 |
| 100 |
| false |
| 00 |
Scenario: Only one geometry can be requested
When sending v1/reverse
| lat | lon | polygon_text | polygon_svg |
| 47.14122383 | 9.52169581334 | 1 | 1 |
Then a HTTP 400 is returned
Scenario Outline: Illegal jsonp are not allowed
When sending v1/reverse with format json
| lat | lon | json_callback |
| 47.14122383 | 9.52169581334 | <data> |
Then a HTTP 400 is returned
Examples:
| data |
| 1asd |
| bar(foo) |
| XXX['bad'] |
| foo; evil |
Scenario Outline: Reverse debug mode produces valid HTML
When sending v1/reverse
| lat | lon | debug |
| <lat> | <lon> | 1 |
Then a HTTP 200 is returned
And the result is valid html
Examples:
| lat | lon |
| 0.0 | 0.0 |
| 47.06645 | 9.56601 |
| 47.14081 | 9.52267 |
Scenario Outline: Full address display for city housenumber-level address with street
When sending v1/reverse with format <format>
| lat | lon |
| 47.1068011 | 9.52810091 |
Then a HTTP 200 is returned
And the result is valid <outformat>
And the result contains in field address
| param | value |
| house_number | 8 |
| road | Im Winkel |
| neighbourhood | Oberdorf |
| village | Triesen |
| ISO3166-2-lvl8 | LI-09 |
| county | Oberland |
| postcode | 9495 |
| country | Liechtenstein |
| country_code | li |
Examples:
| format | outformat |
| json | json |
| jsonv2 | json |
| xml | xml |
Scenario Outline: Results with name details
When sending v1/reverse with format <format>
| lat | lon | zoom | namedetails |
| 47.14052 | 9.52202 | 14 | 1 |
Then a HTTP 200 is returned
And the result is valid <outformat>
And the result contains in field namedetails
| name |
| Ebenholz |
Examples:
| format | outformat |
| json | json |
| jsonv2 | json |
| xml | xml |
Scenario Outline: Results with extratags
When sending v1/reverse with format <format>
| lat | lon | zoom | extratags |
| 47.14052 | 9.52202 | 14 | 1 |
Then a HTTP 200 is returned
And the result is valid <outformat>
And the result contains in field extratags
| wikidata |
| Q4529531 |
Examples:
| format | outformat |
| json | json |
| jsonv2 | json |
| xml | xml |

View File

@@ -0,0 +1,116 @@
Feature: XML output for Reverse API
Testing correctness of xml output (API version v1).
Scenario Outline: Reverse XML - Simple reverse-geocoding with no results
When sending v1/reverse
| lat | lon |
| <lat> | <lon> |
Then a HTTP 200 is returned
And the result is valid xml
And the result has no attributes osm_type, address, extratags
And the result contains
| error |
| Unable to geocode |
Examples:
| lat | lon |
| 0.0 | 0.0 |
| 91.3 | 0.4 |
| -700 | 0.4 |
| 0.2 | 324.44 |
| 0.2 | -180.4 |
Scenario Outline: Reverse XML - OSM result with and without addresses
When sending v1/reverse with format xml
| lat | lon | addressdetails |
| 47.066 | 9.504 | <has_address> |
Then a HTTP 200 is returned
And the result is valid xml
And the result has attributes place_id
And the result has <attributes> address
And the result contains
| osm_type | osm_id | place_rank | address_rank |
| node | 6522627624 | 30 | 30 |
And the result contains
| lon | lat | boundingbox |
| 9.5036065 | 47.0660892 | 47.0660392,47.0661392,9.5035565,9.5036565 |
And the result contains
| ref | display_name |
| Dorfbäckerei Herrmann | Dorfbäckerei Herrmann, 29, Gnetsch, Mäls, Balzers, Oberland, 9496, Liechtenstein |
Examples:
| has_address | attributes |
| 1 | attributes |
| 0 | no attributes |
Scenario: Reverse XML - Tiger address
When sending v1/reverse with format xml
| lat | lon |
| 32.4752389363 | -86.4810198619 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| osm_type | osm_id | place_rank | address_rank |
| way | 396009653 | 30 | 30 |
And the result contains
| lon | lat | boundingbox |
| -86.4808553 | 32.4753580 | 32.4753080,32.4754080,-86.4809053,-86.4808053 |
And the result contains
| display_name |
| 707, Upper Kingston Road, Upper Kingston, Prattville, Autauga County, 36067, United States |
Scenario: Reverse XML - Interpolation address
When sending v1/reverse with format xml
| lat | lon |
| 47.118533 | 9.57056562 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| osm_type | osm_id | place_rank | address_rank |
| way | 1 | 30 | 30 |
And the result contains
| lon | lat | boundingbox |
| 9.5705468 | 47.1185454 | 47.1184954,47.1185954,9.5704968,9.5705968 |
And the result contains
| display_name |
| 1019, Grosssteg, Sücka, Triesenberg, Oberland, 9497, Liechtenstein |
Scenario: Reverse XML - Output of geojson
When sending v1/reverse with format xml
| lat | lon | polygon_geojson |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| geojson |
| {"type":"LineString","coordinates":[[9.5039353,47.0657546],[9.5040437,47.0657781],[9.5040808,47.065787],[9.5054298,47.0661407]]} |
Scenario: Reverse XML - Output of WKT
When sending v1/reverse with format xml
| lat | lon | polygon_text |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| geotext!wkt |
| 9.5039353 47.0657546, 9.5040437 47.0657781, 9.5040808 47.065787, 9.5054298 47.0661407 |
Scenario: Reverse XML - Output of SVG
When sending v1/reverse with format xml
| lat | lon | polygon_svg |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| geosvg |
| M 9.5039353 -47.0657546 L 9.5040437 -47.0657781 9.5040808 -47.065787 9.5054298 -47.0661407 |
Scenario: Reverse XML - Output of KML
When sending v1/reverse with format xml
| lat | lon | polygon_kml |
| 47.06597 | 9.50467 | 1 |
Then a HTTP 200 is returned
And the result is valid xml
And the result contains
| geokml!fm |
| <geokml><LineString><coordinates>9.5039\d*,47.0657\d* 9.5040\d*,47.0657\d* 9.5040\d*,47.065\d* 9.5054\d*,47.0661\d*</coordinates></LineString></geokml> |

View File

@@ -0,0 +1,83 @@
Feature: Localization of search results
Scenario: Search - default language
When sending v1/search
| q |
| Liechtenstein |
Then a HTTP 200 is returned
And the result is valid json
And result 0 contains
| display_name |
| Liechtenstein |
Scenario: Search - accept-language first
When sending v1/search
| q | accept-language |
| Liechtenstein | zh,de |
Then a HTTP 200 is returned
And the result is valid json
And result 0 contains
| display_name |
| |
Scenario: Search - accept-language missing
When sending v1/search
| q | accept-language |
| Liechtenstein | xx,fr,en,de |
Then a HTTP 200 is returned
And the result is valid json
And result 0 contains
| display_name |
| Liechtenstein |
Scenario: Search - http accept language header first
Given the HTTP header
| accept-language |
| fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
When sending v1/search
| q |
| Liechtenstein |
Then a HTTP 200 is returned
And the result is valid json
And result 0 contains
| display_name |
| Liktinstein |
Scenario: Search - 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 v1/search
| q | accept-language |
| Liechtenstein | fo,en |
Then a HTTP 200 is returned
And the result is valid json
And result 0 contains
| display_name |
| Liktinstein |
Scenario: Search - http accept language header fallback
Given the HTTP header
| accept-language |
| fo-ca,en-ca;q=0.5 |
When sending v1/search
| q |
| Liechtenstein |
Then a HTTP 200 is returned
And the result is valid json
And result 0 contains
| display_name |
| Liktinstein |
Scenario: Search - http accept language header fallback (upper case)
Given the HTTP header
| accept-language |
| fo-FR;q=0.8,en-ca;q=0.5 |
When sending v1/search
| q |
| Liechtenstein |
Then a HTTP 200 is returned
And the result is valid json
And result 0 contains
| display_name |
| Liktinstein |

View File

@@ -0,0 +1,361 @@
Feature: Search queries
Testing different queries and parameters
Scenario: Simple XML search
When sending v1/search with format xml
| q |
| Schaan |
Then a HTTP 200 is returned
And the result is valid xml
And all results have attributes place_id,osm_type,osm_id
And all results have attributes place_rank,boundingbox
And all results have attributes lat,lon,display_name
And all results have attributes class,type,importance
And all results have no attributes address
And all results contain
| boundingbox!in_box |
| 46.5,47.5,9,10 |
Scenario Outline: Simple JSON search
When sending v1/search with format <format>
| q |
| Vaduz |
Then a HTTP 200 is returned
And the result is valid json
And all results have attributes place_id,licence,<cname>,type
And all results have attributes osm_type,osm_id,boundingbox
And all results have attributes lat,lon,display_name,importance
And all results have no attributes address
And all results contain
| boundingbox!in_box |
| 46.5,47.5,9,10 |
Examples:
| format | cname |
| json | class |
| jsonv2 | category |
Scenario: Unknown formats returns a user error
When sending v1/search with format x45
| q |
| Vaduz |
Then a HTTP 400 is returned
Scenario Outline: Search with addressdetails
When sending v1/search with format <format>
| q | addressdetails |
| Triesen | 1 |
Then a HTTP 200 is returned
And the result is valid <outformat>
And result 0 contains in field address
| param | value |
| village | Triesen |
| county | Oberland |
| postcode | 9495 |
| country | Liechtenstein |
| country_code | li |
| ISO3166-2-lvl8 | LI-09 |
Examples:
| format | outformat |
| json | json |
| jsonv2 | json |
| geojson | geojson |
| xml | xml |
Scenario: Coordinate search with addressdetails
When geocoding "47.12400621,9.6047552"
| accept-language |
| en |
Then all results contain
| display_name |
| Guschg, Valorschstrasse, Balzers, Oberland, 9497, Liechtenstein |
Scenario: Address details with unknown class types
When geocoding "Kloster St. Elisabeth"
Then result 0 contains
| category | type | address+amenity |
| amenity | monastery | Kloster St. Elisabeth |
Scenario: Disabling deduplication
When geocoding "Malbunstr, Schaan"
Then exactly 1 result is returned
When geocoding "Malbunstr, Schaan"
| dedupe |
| 0 |
Then exactly 4 results are returned
Scenario: Search with bounded viewbox in right area
When geocoding "post"
| bounded | viewbox |
| 1 | 9,47,10,48 |
Then result 0 contains
| address+town |
| Vaduz |
When geocoding "post"
| bounded | viewbox |
| 1 | 9.49712,47.17122,9.52605,47.16242 |
Then result 0 contains
| address+town |
| Schaan |
Scenario: Country search with bounded viewbox remain in the area
When geocoding
| bounded | viewbox | country |
| 1 | 9.49712,47.17122,9.52605,47.16242 | de |
Then exactly 0 results are returned
Scenario: Search with bounded viewboxlbrt in right area
When geocoding "bar"
| bounded | viewboxlbrt |
| 1 | 9.49712,47.16242,9.52605,47.17122 |
Then all results contain
| address+town |
| Schaan |
Scenario: No POI search with unbounded viewbox
When geocoding "restaurant"
| viewbox |
| 9.93027,53.61634,10.10073,53.54500 |
Then all results contain
| display_name!fm |
| .*[Rr]estaurant.* |
Scenario: bounded search remains within viewbox, even with no results
When geocoding "[restaurant]"
| bounded | viewbox |
| 1 | 43.5403125,-5.6563282,43.54285,-5.662003 |
Then exactly 0 results are returned
Scenario: bounded search remains within viewbox with results
When geocoding "restaurant"
| bounded | viewbox |
| 1 | 9.49712,47.17122,9.52605,47.16242 |
Then all results contain
| boundingbox!in_box |
| 47.16242,47.17122,9.49712,9.52605 |
Scenario: Prefer results within viewbox
When geocoding "Gässle"
| accept-language | viewbox |
| en | 9.52413,47.10759,9.53140,47.10539 |
Then result 0 contains
| address+village |
| Triesen |
When geocoding "Gässle"
| accept-language | viewbox |
| en | 9.45949,47.08421,9.54094,47.05466 |
Then result 0 contains
| address+town |
| Balzers |
Scenario: viewboxes cannot be points
When sending v1/search
| q | viewbox |
| foo | 1.01,34.6,1.01,34.6 |
Then a HTTP 400 is returned
Scenario Outline: viewbox must have four coordinate numbers
When sending v1/search
| q | viewbox |
| foo | <viewbox> |
Then a HTTP 400 is returned
Examples:
| viewbox |
| 34 |
| 0.003,-84.4 |
| 5.2,4.5542,12.4 |
| 23.1,-6,0.11,44.2,9.1 |
Scenario Outline: viewboxlbrt must have four coordinate numbers
When sending v1/search
| q | viewboxlbrt |
| foo | <viewbox> |
Then a HTTP 400 is returned
Examples:
| viewbox |
| 34 |
| 0.003,-84.4 |
| 5.2,4.5542,12.4 |
| 23.1,-6,0.11,44.2,9.1 |
Scenario: Overly large limit number for search results
When geocoding "restaurant"
| limit |
| 1000 |
Then exactly 35 results are returned
Scenario: Limit number of non-duplicated search results
When geocoding "landstr"
| dedupe |
| 0 |
Then exactly 10 results are returned
When geocoding "landstr"
| limit | dedupe |
| 4 | 0 |
Then exactly 4 results are returned
Scenario: Limit parameter must be a number
When sending v1/search
| q | limit |
| Blue Laguna | ); |
Then a HTTP 400 is returned
Scenario: Restrict to feature type country
When geocoding "fürstentum"
| featureType |
| country |
Then all results contain
| place_rank |
| 4 |
Scenario: Restrict to feature type state
When geocoding "Wangerberg"
Then more than 0 results are returned
When geocoding "Wangerberg"
| featureType |
| state |
Then exactly 0 results are returned
Scenario: Restrict to feature type city
When geocoding "vaduz"
| featureType |
| state |
Then exactly 0 results are returned
When geocoding "vaduz"
| featureType |
| city |
Then more than 0 results are returned
Then all results contain
| place_rank |
| 16 |
Scenario: Restrict to feature type settlement
When geocoding "Malbun"
Then result 1 contains
| category |
| landuse |
When geocoding "Malbun"
| featureType |
| settlement |
Then all results contain
| category | type |
| place | village |
Scenario Outline: Search with polygon threshold (json)
When sending v1/search with format json
| q | polygon_geojson | polygon_threshold |
| Triesenberg | 1 | <th> |
Then a HTTP 200 is returned
And the result is valid json
And more than 0 results are returned
And all results have attributes geojson
Examples:
| th |
| -1 |
| 0.0 |
| 0.5 |
| 999 |
Scenario Outline: Search with polygon threshold (xml)
When sending v1/search with format xml
| q | polygon_geojson | polygon_threshold |
| Triesenberg | 1 | <th> |
Then a HTTP 200 is returned
And the result is valid xml
And more than 0 results are returned
And all results have attributes geojson
Examples:
| th |
| -1 |
| 0.0 |
| 0.5 |
| 999 |
Scenario Outline: Search with invalid polygon threshold (xml)
When sending v1/search with format xml
| q | polygon_geojson | polygon_threshold |
| Triesenberg | 1 | <th> |
Then a HTTP 400 is returned
Examples:
| th |
| x |
| ;; |
| 1m |
Scenario Outline: Search with extratags
When sending v1/search with format <format>
| q | extratags |
| Landstr | 1 |
Then a HTTP 200 is returned
And the result is valid <outformat>
And more than 0 results are returned
Then all results have attributes extratags
Examples:
| format | outformat |
| xml | xml |
| json | json |
| jsonv2 | json |
| geojson | geojson |
Scenario Outline: Search with namedetails
When sending v1/search with format <format>
| q | namedetails |
| Landstr | 1 |
Then a HTTP 200 is returned
And the result is valid <outformat>
And more than 0 results are returned
Then all results have attributes namedetails
Examples:
| format | outformat |
| xml | xml |
| json | json |
| jsonv2 | json |
| geojson | geojson |
Scenario Outline: Search result with contains formatted geometry
When sending v1/search with format <format>
| q | <param> |
| Triesenberg | 1 |
Then a HTTP 200 is returned
And the result is valid <outformat>
And more than 0 results are returned
And all results have attributes <response_attribute>
Examples:
| format | outformat | param | response_attribute |
| xml | xml | polygon_text | geotext |
| json | json | polygon_text | geotext |
| jsonv2 | json | polygon_text | geotext |
| xml | xml | polygon_svg | geosvg |
| json | json | polygon_svg | svg |
| jsonv2 | json | polygon_svg | svg |
| xml | xml | polygon_kml | geokml |
| json | json | polygon_kml | geokml |
| jsonv2 | json | polygon_kml | geokml |
| xml | xml | polygon_geojson | geojson |
| json | json | polygon_geojson | geojson |
| jsonv2 | json | polygon_geojson | geojson |
| geojson | geojson | polygon_geojson | geojson |
Scenario Outline: Search result in geojson format contains no non-geojson geometry
When sending v1/search with format geojson
| q | <param> |
| Triesenberg | 1 |
Then a HTTP 200 is returned
And the result is valid geojson
And more than 0 results are returned
And all results have no attributes <response_attribute>
Examples:
| param | response_attribute |
| polygon_text | geotext |
| polygon_svg | svg |
| polygon_kml | geokml |

View File

@@ -1,51 +1,51 @@
@SQLITE
@APIDB
Feature: Searches with postcodes
Various searches involving postcodes
Scenario: US 5+4 ZIP codes are shortened to 5 ZIP codes if not found
When sending json search query "36067-1111, us" with address
Then result addresses contain
When geocoding "36067-1111, us"
Then all results contain in field address
| postcode |
| 36067 |
And results contain
And all results contain
| type |
| postcode |
Scenario: Postcode search with address
When sending json search query "9486, mauren"
Then at least 1 result is returned
When geocoding "9486, mauren"
Then result 0 contains
| type |
| postcode |
Scenario: Postcode search with country
When sending json search query "9486, li" with address
Then result addresses contain
When geocoding "9486, li"
Then all results contain in field address
| country_code |
| li |
Scenario: Postcode search with country code restriction
When sending json search query "9490" with address
When geocoding "9490"
| countrycodes |
| li |
Then result addresses contain
Then all results contain in field address
| country_code |
| li |
Scenario: Postcode search with bounded viewbox restriction
When sending json search query "9486" with address
When geocoding "9486"
| bounded | viewbox |
| 1 | 9.55,47.20,9.58,47.22 |
Then result addresses contain
Then all results contain in field address
| postcode |
| 9486 |
When sending json search query "9486" with address
When geocoding "9486"
| bounded | viewbox |
| 1 | 5.00,20.00,6.00,21.00 |
Then exactly 0 results are returned
Then exactly 0 result is returned
Scenario: Postcode search with structured query
When sending json search query "" with address
When geocoding ""
| postalcode | country |
| 9490 | li |
Then result addresses contain
Then all results contain in field address
| country_code | postcode |
| li | 9490 |

View File

@@ -0,0 +1,212 @@
Feature: Search queries
Generic search result correctness
Scenario: Search for natural object
When geocoding "Samina"
| accept-language |
| en |
Then result 0 contains
| category | type | display_name |
| waterway | river | Samina, Austria |
Scenario: House number search for non-street address
When geocoding "6 Silum, Liechtenstein"
| accept-language |
| en |
Then result 0 contains in field address
| param | value |
| house_number | 6 |
| village | Silum |
| town | Triesenberg |
| county | Oberland |
| postcode | 9497 |
| country | Liechtenstein |
| country_code | li |
| ISO3166-2-lvl8 | LI-10 |
Scenario: Search for house number interpolation
When geocoding "Grosssteg 1023, Triesenberg"
| accept-language |
| de |
Then result 0 contains in field address
| param | value |
| house_number | 1023 |
| road | Grosssteg |
| village | Sücka |
| postcode | 9497 |
| town | Triesenberg |
| country | Liechtenstein |
| country_code | li |
Scenario: With missing housenumber search falls back to road
When geocoding "Bündaweg 555"
Then result 0 contains in field address
| param | value |
| road | Bündaweg |
| village | Silum |
| postcode | 9497 |
| county | Oberland |
| town | Triesenberg |
| country | Liechtenstein |
| country_code | li |
| ISO3166-2-lvl8 | LI-10 |
And all results have no attributes address+house_number
Scenario Outline: Housenumber 0 can be found
When sending v1/search with format <format>
| q | addressdetails |
| Gnalpstrasse 0 | 1 |
Then a HTTP 200 is returned
And the result is valid <outformat>
And all results contain
| display_name!fm | address+house_number |
| 0,.* | 0 |
Examples:
| format | outformat |
| xml | xml |
| json | json |
| jsonv2 | json |
| geojson | geojson |
Scenario: TIGER house number
When geocoding "697 Upper Kingston Road"
Then all results contain
| osm_type | display_name!fm | address+house_number |
| way | 697,.* | 697 |
Scenario: Search with class-type feature
When geocoding "bars in ebenholz"
Then all results contain
| place_rank |
| 30 |
Scenario: Search with specific amenity
When geocoding "[restaurant] Vaduz"
Then all results contain
| category | type | address+country |
| amenity | restaurant | Liechtenstein |
Scenario: Search with specific amenity also work in country
When geocoding "restaurants in liechtenstein"
Then all results contain
| category | type | address+country |
| amenity | restaurant | Liechtenstein |
Scenario: Search with key-value amenity
When geocoding "[club=scout] Vaduz"
Then all results contain
| category | type |
| club | scout |
Scenario: POI search near given coordinate
When geocoding "restaurant near 47.16712,9.51100"
Then all results contain
| category | type |
| amenity | restaurant |
Scenario: Arbitrary key/value search near given coordinate
When geocoding "[leisure=firepit] 47.150° N 9.5340493° E"
Then all results contain
| category | type |
| leisure | firepit |
Scenario: POI search in a bounded viewbox
When geocoding "restaurants"
| viewbox | bounded |
| 9.50830,47.15253,9.52043,47.14866 | 1 |
Then all results contain
| category | type |
| amenity | restaurant |
Scenario Outline: Key/value search near given coordinate can be restricted to country
When geocoding "[natural=peak] 47.06512,9.53965"
| countrycodes |
| <cc> |
Then all results contain
| address+country_code |
| <cc> |
Examples:
| cc |
| li |
| ch |
Scenario: Name search near given coordinate
When geocoding "sporry"
Then result 0 contains
| address+town |
| Vaduz |
When geocoding "sporry, 47.10791,9.52676"
Then result 0 contains
| address+village |
| Triesen |
Scenario: Name search near given coordinate without result
When geocoding "sporry, N 47 15 7 W 9 61 26"
Then exactly 0 results are returned
Scenario: Arbitrary key/value search near a road
When geocoding "[amenity=drinking_water] Wissfläckaweg"
Then all results contain
| category | type |
| amenity | drinking_water |
Scenario: Ignore other country codes in structured search with country
When geocoding
| countrycodes | country |
| li | de |
Then exactly 0 results are returned
Scenario: Ignore country searches when query is restricted to countries
When geocoding "fr"
Then all results contain
| name |
| France |
When geocoding "fr"
| countrycodes |
| li |
Then exactly 0 results are returned
Scenario: Country searches only return results for the given country
When geocoding "Ans Trail"
| countrycodes |
| li |
Then all results contain
| address+country_code |
| li |
# https://trac.openstreetmap.org/ticket/5094
Scenario: housenumbers are ordered by complete match first
When geocoding "Austrasse 11, Vaduz"
Then result 0 contains
| address+house_number |
| 11 |
Scenario Outline: Coordinate searches with white spaces
When geocoding "<data>"
Then the result set contains exactly
| category |
| water |
Examples:
| data |
| sporry weiher, N 47.10791° E 9.52676° |
| sporry weiher, N 47.10791° E 9.52676° |
| sporry weiher , N 47.10791° E 9.52676° |
| sporry weiher, N 47.10791° E 9.52676° |
| sporry weiher , N 47.10791° E 9.52676° |
Scenario: Searches with white spaces
When geocoding "52 Bodastr , Triesenberg"
Then all results contain
| category | type |
| highway | residential |
# github #1949
Scenario: Addressdetails always return the place type
When geocoding "Vaduz"
Then result 0 contains
| address+town |
| Vaduz |

View File

@@ -0,0 +1,166 @@
Feature: Simple Tests
Simple tests for internal server errors and response format.
Scenario Outline: Garbage Searches
When sending v1/search
| q |
| <query> |
Then a HTTP 200 is returned
And the result is valid json
And exactly 0 results are returned
Examples:
| query |
| New York, New York |
| 12, Main Street, Houston |
| München |
| |
| hotels in sdfewf |
| xywxkrf |
| gh; foo() |
| %#$@*&l;der#$! |
| 234.23.14.5 |
| aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus |
Scenario: Empty XML search
When sending v1/search with format xml
| q |
| xnznxvcx |
Then a HTTP 200 is returned
And the result is valid xml
Then the result metadata contains
| param | value |
| querystring | xnznxvcx |
| more_url!fm | .*q=xnznxvcx.*format=xml |
Scenario: Empty XML search with special XML characters
When sending v1/search with format xml
| q |
| xfdghn&zxn"xvbyx<vxx>cssdex |
Then a HTTP 200 is returned
And the result is valid xml
Then the result metadata contains
| param | value |
| querystring | xfdghn&zxn"xvbyx<vxx>cssdex |
| more_url!fm | .*q=xfdghn%26zxn%22xvbyx%3Cvxx%3Ecssdex.*format=xml |
Scenario: Empty XML search with viewbox
When sending v1/search with format xml
| q | viewbox |
| xnznxvcx | 12,33,77,45.13 |
Then a HTTP 200 is returned
And the result is valid xml
And the result metadata contains
| param | value |
| querystring | xnznxvcx |
| viewbox | 12,33,77,45.13 |
Scenario: Empty XML search with viewboxlbrt
When sending v1/search with format xml
| q | viewboxlbrt |
| xnznxvcx | 12,34.13,77,45 |
Then a HTTP 200 is returned
And the result is valid xml
And the result metadata contains
| param | value |
| querystring | xnznxvcx |
| viewbox | 12,34.13,77,45 |
Scenario: Empty XML search with viewboxlbrt and viewbox
When sending v1/search with format xml
| q | viewbox | viewboxblrt |
| pub | 12,33,77,45.13 | 1,2,3,4 |
Then a HTTP 200 is returned
And the result is valid xml
And the result metadata contains
| param | value |
| querystring | pub |
| viewbox | 12,33,77,45.13 |
Scenario: Empty XML search with excluded place ids
When sending v1/search with format xml
| q | exclude_place_ids |
| jghrleoxsbwjer | 123,76,342565 |
Then a HTTP 200 is returned
And the result is valid xml
And the result metadata contains
| param | value |
| exclude_place_ids | 123,76,342565 |
Scenario: Empty XML search with bad excluded place ids
When sending v1/search with format xml
| q | exclude_place_ids |
| jghrleoxsbwjer | , |
Then a HTTP 200 is returned
And the result is valid xml
And the result metadata has no attributes exclude_place_ids
Scenario Outline: Wrapping of illegal jsonp search requests
When sending v1/search with format json
| q | json_callback |
| Tokyo | <data> |
Then a HTTP 400 is returned
And the result is valid json
And the result contains
| error+code | error+message |
| 400 | Invalid json_callback value |
Examples:
| data |
| 1asd |
| bar(foo) |
| XXX['bad'] |
| foo; evil |
| 234 |
Scenario: Ignore jsonp parameter for anything but json
When sending v1/search with format xml
| q | json_callback |
| Tokyo | 234 |
Then a HTTP 200 is returned
Then the result is valid xml
Scenario Outline: Empty search for json like
When sending v1/search with format <format>
| q |
| YHlERzzx |
Then a HTTP 200 is returned
And the result is valid <outformat>
And exactly 0 results are returned
Examples:
| format | outformat |
| json | json |
| jsonv2 | json |
| geojson | geojson |
| geocodejson | geocodejson |
Scenario: Search for non-existing coordinates
When geocoding "-21.0,-33.0"
Then exactly 0 results are returned
Scenario: Country code selection is retained in more URL (#596)
When sending v1/search with format xml
| q | countrycodes |
| Vaduz | pl,1,,invalid,undefined,%3Cb%3E,bo,, |
Then a HTTP 200 is returned
And the result is valid xml
And the result metadata contains
| more_url!fm |
| .*&countrycodes=pl%2Cbo&.* |
Scenario Outline: Search debug output does not return errors
When sending v1/search
| q | debug |
| <query> | 1 |
Then a HTTP 200 is returned
And the result is valid html
Examples:
| query |
| Liechtenstein |
| Triesen |
| Pfarrkirche |
| Landstr 27 Steinort, Triesenberg, 9495 |
| 9497 |
| restaurant in triesen |

View File

@@ -0,0 +1,72 @@
Feature: Structured search queries
Testing correctness of results with
structured queries
Scenario: Structured search for country only
When geocoding
| country |
| Liechtenstein |
Then all results contain in field address
| country_code | country |
| li | Liechtenstein |
Scenario: Structured search for postcode only
When geocoding
| postalcode |
| 9495 |
Then all results contain
| type!fm | address+postcode |
| ^post(al_)?code | 9495 |
Scenario: Structured search for street, postcode and country
When sending v1/search with format xml
| street | postalcode | country |
| Old Palace Road | GU2 7UP | United Kingdom |
Then a HTTP 200 is returned
And the result is valid xml
And the result metadata contains
| querystring |
| Old Palace Road, GU2 7UP, United Kingdom |
Scenario: Structured search for street with housenumber, city and postcode
When geocoding
| street | city | postalcode |
| 19 Am schrägen Weg | Vaduz | 9490 |
Then all results contain in field address
| house_number | road |
| 19 | Am Schrägen Weg |
Scenario: Structured search for street with housenumber, city and bad postcode
When geocoding
| street | city | postalcode |
| 19 Am schrägen Weg | Vaduz | 9491 |
Then all results contain in field address
| house_number | road |
| 19 | Am Schrägen Weg |
Scenario: Structured search for amenity, city
When geocoding
| city | amenity |
| Vaduz | bar |
Then all results contain
| address+country | category | type!fm |
| Liechtenstein | amenity | (pub)\|(bar)\|(restaurant) |
#176
Scenario: Structured search restricts rank
When geocoding
| city |
| Steg |
Then all results contain
| addresstype |
| village |
#3651
Scenario: Structured search with surrounding extra characters
When geocoding
| street | city | postalcode |
| "19 Am schrägen Weg" | "Vaduz" | "9491" |
Then all results contain in field address
| house_number | road |
| 19 | Am Schrägen Weg |

View File

@@ -0,0 +1,42 @@
Feature: Search API geocodejson output
Testing correctness of geocodejson output.
Scenario: Search geocodejson - City housenumber-level address with street
When sending v1/search with format geocodejson
| q | addressdetails |
| Im Winkel 8, Triesen | 1 |
Then a HTTP 200 is returned
And the result is valid geocodejson
And all results contain
| housenumber | street | postcode | city | country |
| 8 | Im Winkel | 9495 | Triesen | Liechtenstein |
Scenario: Search geocodejson - Town street-level address with street
When sending v1/search with format geocodejson
| q | addressdetails |
| Gnetsch, Balzers | 1 |
Then a HTTP 200 is returned
And the result is valid geocodejson
And all results contain
| name | city | postcode | country |
| Gnetsch | Balzers | 9496 | Liechtenstein |
Scenario: Search geocodejson - Town street-level address with footway
When sending v1/search with format geocodejson
| q | addressdetails |
| burg gutenberg 6000 jahre geschichte | 1 |
Then a HTTP 200 is returned
And the result is valid geocodejson
And all results contain
| street | city | postcode | country |
| Burgweg | Balzers | 9496 | Liechtenstein |
Scenario: Search geocodejson - City address with suburb
When sending v1/search with format geocodejson
| q | addressdetails |
| Lochgass 5, Ebenholz, Vaduz | 1 |
Then a HTTP 200 is returned
And the result is valid geocodejson
And all results contain
| housenumber | street | district | city | postcode | country |
| 5 | Lochgass | Ebenholz | Vaduz | 9490 | Liechtenstein |

View File

@@ -0,0 +1,19 @@
Feature: Status queries against unknown database
Testing status query
Background:
Given an unknown database
Scenario: Failed status as text
When sending v1/status
Then a HTTP 500 is returned
And the page content equals "ERROR: Database connection failed"
Scenario: Failed status as json
When sending v1/status with format json
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| status!:d | message |
| 700 | Database connection failed |
And the result has no attributes data_updated

View File

@@ -0,0 +1,15 @@
Feature: Status queries
Testing status query
Scenario: Status as text
When sending v1/status
Then a HTTP 200 is returned
And the page content equals "OK"
Scenario: Status as json
When sending v1/status with format json
Then a HTTP 200 is returned
And the result is valid json
And the result contains
| status!:d | message | data_updated!fm |
| 0 | OK | ....-..-..T..:..:...00:00 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Address computation
Tests for filling of place_addressline
@@ -11,16 +10,13 @@ Feature: Address computation
| N2 | place | hamlet | West Farm | 2 |
| N3 | place | hamlet | East Farm | 3 |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | fromarea |
| N1 | N3 | False |
Then place_addressline doesn't contain
| object | address |
| N1 | N2 |
When sending search query "Square"
Then results contain
| osm | display_name |
| N1 | Square, East Farm |
When geocoding "Square"
Then the result set contains
| object | display_name |
| N1 | Square, East Farm |
Scenario: given two place nodes, the closer one wins for the address
Given the grid
@@ -102,12 +98,9 @@ Feature: Address computation
| N2 | place | city | 15 | 9 |
| R1 | place | city | 8 | (1,2,3,4,1) |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | isaddress | cached_rank_address |
| N1 | R1 | True | 16 |
And place_addressline doesn't contain
| object | address |
| N1 | N2 |
Scenario: place nodes close enough to smaller ranked place nodes are included
@@ -191,12 +184,9 @@ Feature: Address computation
| W10 | boundary | administrative | 5 | (1, 2, 8, 5, 4, 1) |
| W11 | boundary | administrative | 5 | (2, 3, 6, 5, 8, 2) |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | cached_rank_address |
| W1 | W10 | 10 |
Then place_addressline doesn't contain
| object | address |
| W1 | W11 |
Scenario: Roads should not contain boundaries they touch in a middle point
Given the grid
@@ -211,12 +201,9 @@ Feature: Address computation
| W10 | boundary | administrative | 5 | (1, 2, 8, 5, 4, 1) |
| W11 | boundary | administrative | 5 | (2, 3, 6, 5, 8, 2) |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | cached_rank_address |
| W1 | W10 | 10 |
Then place_addressline doesn't contain
| object | address |
| W1 | W11 |
Scenario: Locality points should contain all boundaries they touch
Given the 0.001 grid
@@ -248,9 +235,8 @@ Feature: Address computation
| osm | class | type | admin | geometry |
| W10 | boundary | administrative | 5 | (2, 3, 6, 5, 2) |
When importing
Then place_addressline doesn't contain
Then place_addressline contains exactly
| object | address |
| W1 | W10 |
Scenario: buildings with only addr:postcodes do not appear in the address of a way
Given the grid with origin DE
@@ -273,9 +259,14 @@ Feature: Address computation
| osm | class | type | addr+postcode | geometry |
| W22 | place | postcode | 11234 | (10,11,12,13,10) |
When importing
Then place_addressline doesn't contain
Then place_addressline contains exactly
| object | address |
| W93 | W22 |
| R4 | R1 |
| R4 | R34 |
| R34 | R1 |
| W93 | R1 |
| W93 | R34 |
| W93 | R4 |
Scenario: postcode boundaries do appear in the address of a way
Given the grid with origin DE
@@ -314,9 +305,8 @@ Feature: Address computation
| W1 | highway | residential | 8, 9 |
| W2 | place | square | (1, 2, 3 ,4, 1) |
When importing
Then place_addressline doesn't contain
Then place_addressline contains exactly
| object | address |
| W1 | W2 |
Scenario: addr:* tags are honored even when a street is far away from the place
Given the grid
@@ -332,14 +322,11 @@ Feature: Address computation
| W1 | highway | primary | Left | 8,9 |
| W2 | highway | primary | Right | 8,9 |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | isaddress |
| W1 | R1 | True |
| W1 | R2 | False |
| W2 | R2 | True |
And place_addressline doesn't contain
| object | address |
| W2 | R1 |
Scenario: addr:* tags are honored even when a POI is far away from the place
@@ -356,17 +343,14 @@ Feature: Address computation
| W1 | highway | primary | Wonderway | Right | 8,9 |
| N1 | amenity | cafe | Bolder | Left | 9 |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | isaddress |
| W1 | R2 | True |
| N1 | R1 | True |
And place_addressline doesn't contain
| object | address |
| W1 | R1 |
When sending search query "Bolder"
Then results contain
| osm | display_name |
| N1 | Bolder, Wonderway, Left |
When geocoding "Bolder"
Then the result set contains
| object | display_name |
| N1 | Bolder, Wonderway, Left |
Scenario: addr:* tags do not produce addresslines when the parent has the address part
Given the grid
@@ -381,16 +365,13 @@ Feature: Address computation
| W1 | highway | primary | Wonderway | Outer | 8,9 |
| N1 | amenity | cafe | Bolder | Outer | 9 |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | isaddress |
| W1 | R1 | True |
And place_addressline doesn't contain
| object | address |
| N1 | R1 |
When sending search query "Bolder"
Then results contain
| osm | display_name |
| N1 | Bolder, Wonderway, Outer |
When geocoding "Bolder"
Then the result set contains
| object | display_name |
| N1 | Bolder, Wonderway, Outer |
Scenario: addr:* tags on outside do not produce addresslines when the parent has the address part
Given the grid
@@ -406,17 +387,14 @@ Feature: Address computation
| W1 | highway | primary | Wonderway | Left | 8,9 |
| N1 | amenity | cafe | Bolder | Left | 9 |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | isaddress |
| W1 | R1 | True |
| W1 | R2 | False |
And place_addressline doesn't contain
| object | address |
| N1 | R1 |
When sending search query "Bolder"
Then results contain
| osm | display_name |
| N1 | Bolder, Wonderway, Left |
When geocoding "Bolder"
Then the result set contains
| object | display_name |
| N1 | Bolder, Wonderway, Left |
Scenario: POIs can correct address parts on the fly
Given the grid
@@ -433,22 +411,18 @@ Feature: Address computation
| N1 | amenity | cafe | Bolder | 9 |
| N2 | amenity | cafe | Leftside | 8 |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | isaddress |
| W1 | R1 | False |
| W1 | R2 | True |
And place_addressline doesn't contain
| object | address |
| N1 | R1 |
| N2 | R2 |
When sending search query "Bolder"
Then results contain
| osm | display_name |
| N1 | Bolder, Wonderway, Left |
When sending search query "Leftside"
Then results contain
| osm | display_name |
| N2 | Leftside, Wonderway, Right |
When geocoding "Bolder"
Then the result set contains
| object | display_name |
| N1 | Bolder, Wonderway, Left |
When geocoding "Leftside"
Then the result set contains
| object | display_name |
| N2 | Leftside, Wonderway, Right |
Scenario: POIs can correct address parts on the fly (with partial unmatching address)
@@ -469,22 +443,18 @@ Feature: Address computation
| N1 | amenity | cafe | Bolder | Boring | 9 |
| N2 | amenity | cafe | Leftside | Boring | 8 |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | isaddress |
| W1 | R1 | True |
| W1 | R2 | False |
And place_addressline doesn't contain
| object | address |
| N1 | R1 |
| N2 | R2 |
When sending search query "Bolder"
Then results contain
| osm | display_name |
| N1 | Bolder, Wonderway, Left |
When sending search query "Leftside"
Then results contain
| osm | display_name |
| N2 | Leftside, Wonderway, Right |
When geocoding "Bolder"
Then the result set contains
| object | display_name |
| N1 | Bolder, Wonderway, Left |
When geocoding "Leftside"
Then the result set contains
| object | display_name |
| N2 | Leftside, Wonderway, Right |
@@ -506,30 +476,26 @@ Feature: Address computation
| N1 | amenity | cafe | Bolder | Left | 9 |
| N2 | amenity | cafe | Leftside | Left | 8 |
When importing
Then place_addressline contains
Then place_addressline contains exactly
| object | address | isaddress |
| W1 | R1 | True |
| W1 | R2 | False |
And place_addressline doesn't contain
| object | address |
| N1 | R1 |
| N2 | R2 |
When sending search query "Bolder"
Then results contain
| osm | display_name |
| N1 | Bolder, Wonderway, Left |
When sending search query "Leftside"
Then results contain
| osm | display_name |
| N2 | Leftside, Wonderway, Left |
When geocoding "Bolder"
Then the result set contains
| object | display_name |
| N1 | Bolder, Wonderway, Left |
When geocoding "Leftside"
Then the result set contains
| object | display_name |
| N2 | Leftside, Wonderway, Left |
Scenario: addr:* tags always match the closer area
Given the grid
| 1 | | | | 2 | | 5 |
| | | | | | | |
| | 10| 11| | | | |
| 4 | | | | 3 | | 6 |
| | 10| 11| | | | |
And the places
| osm | class | type | admin | name | geometry |
| R1 | boundary | administrative | 8 | Left | (1,2,3,4,1) |
@@ -538,9 +504,9 @@ Feature: Address computation
| osm | class | type | name | addr+city | geometry |
| W1 | highway | primary | Wonderway | Left | 10,11 |
When importing
Then place_addressline doesn't contain
Then place_addressline contains exactly
| object | address |
| W1 | R2 |
| W1 | R1 |
Scenario: Full name is prefered for unlisted addr:place tags
Given the grid
@@ -559,7 +525,7 @@ Feature: Address computation
| osm | class | type | housenr | addr+street | geometry |
| N2 | place | house | 2 | Royal Terrace | 2 |
When importing
When sending search query "1, Royal Terrace Gardens"
Then results contain
| ID | osm |
| 0 | N1 |
When geocoding "1, Royal Terrace Gardens"
Then result 0 contains
| object |
| N1 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Country handling
Tests for import and use of country information
@@ -10,16 +9,16 @@ Feature: Country handling
| osm | class | type | name | geometry |
| N1 | place | town | Wenig | country:de |
When importing
When sending search query "Wenig, Loudou"
Then results contain
| osm | display_name |
| N1 | Wenig, Deutschland |
When sending search query "Wenig"
When geocoding "Wenig, Loudou"
Then the result set contains
| object | display_name |
| N1 | Wenig, Deutschland |
When geocoding "Wenig"
| accept-language |
| xy,en |
Then results contain
| osm | display_name |
| N1 | Wenig, Loudou |
Then the result set contains
| object | display_name |
| N1 | Wenig, Loudou |
Scenario: OSM country relations outside expected boundaries are ignored for naming
Given the grid
@@ -32,12 +31,12 @@ Feature: Country handling
| osm | class | type | name | geometry |
| N1 | place | town | Wenig | country:de |
When importing
When sending search query "Wenig"
When geocoding "Wenig"
| accept-language |
| xy,en |
Then results contain
| osm | display_name |
| N1 | Wenig, Germany |
Then the result set contains
| object | display_name |
| N1 | Wenig, Germany |
Scenario: Pre-defined country names are used
Given the grid with origin CH
@@ -46,12 +45,12 @@ Feature: Country handling
| osm | class | type | name | geometry |
| N1 | place | town | Ingb | 1 |
When importing
And sending search query "Ingb"
And geocoding "Ingb"
| accept-language |
| en,de |
Then results contain
| osm | display_name |
| N1 | Ingb, Switzerland |
Then the result set contains
| object | display_name |
| N1 | Ingb, Switzerland |
Scenario: For overlapping countries, pre-defined countries are tie-breakers
Given the grid with origin US

View File

@@ -1,4 +1,3 @@
@DB
Feature: Import of address interpolations
Tests that interpolated addresses are added correctly
@@ -60,7 +59,7 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 4 | 6 | 9,8 |
| 4 | 6 | 8,9 |
Scenario: Simple odd two point interpolation
Given the grid with origin 1,1
@@ -227,8 +226,8 @@ Feature: Import of address interpolations
Scenario: Even three point interpolation line with odd center point
Given the grid
| 1 | | 10 | | | 11 | 3 | 2 |
| 4 | | | | | | | 5 |
| 1 | | 10 | | 11 | 3 | 2 |
| 4 | | | | | | 5 |
Given the places
| osm | class | type | housenr |
| N1 | place | house | 2 |
@@ -332,14 +331,14 @@ Feature: Import of address interpolations
Then W11 expands to interpolation
| parent_place_id | start | end |
| W3 | 14 | 14 |
When sending search query "16 Cloud Street"
Then results contain
| ID | osm |
| 0 | N4 |
When sending search query "14 Cloud Street"
Then results contain
| ID | osm |
| 0 | W11 |
When geocoding "16 Cloud Street"
Then result 0 contains
| object |
| N4 |
When geocoding "14 Cloud Street"
Then result 0 contains
| object |
| W11 |
Scenario: addr:street on housenumber way
Given the grid
@@ -377,14 +376,14 @@ Feature: Import of address interpolations
Then W11 expands to interpolation
| parent_place_id | start | end |
| W3 | 14 | 14 |
When sending search query "16 Cloud Street"
Then results contain
| ID | osm |
| 0 | N4 |
When sending search query "14 Cloud Street"
Then results contain
| ID | osm |
| 0 | W11 |
When geocoding "16 Cloud Street"
Then result 0 contains
| object |
| N4 |
When geocoding "14 Cloud Street"
Then result 0 contains
| object |
| W11 |
Scenario: Geometry of points and way don't match (github #253)
Given the places
@@ -404,7 +403,7 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 4 | 4 | 144.963016 -37.762946 |
| 4 | 4 | 144.96301672 -37.76294644 |
| 8 | 8 | 144.96314407 -37.762223692 |
Scenario: Place with missing address information
@@ -428,7 +427,7 @@ Feature: Import of address interpolations
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 25 | 27 | 0.000016 0,0.00002 0,0.000033 0 |
| 25 | 27 | 0.0000166 0,0.00002 0,0.0000333 0 |
Scenario: Ways without node entries are ignored
Given the places
@@ -478,10 +477,10 @@ Feature: Import of address interpolations
Then W1 expands to interpolation
| start | end | geometry |
| 2 | 8 | 10,11 |
When sending v1/reverse at 1,1
Then results contain
| ID | osm | type | display_name |
| 0 | N1 | house | 0, London Road |
When reverse geocoding 1,1
Then the result contains
| object | type | display_name |
| N1 | house | 0, London Road |
Scenario: Parenting of interpolation with additional tags
Given the grid

View File

@@ -1,4 +1,3 @@
@DB
Feature: Linking of places
Tests for correctly determining linked places
@@ -53,10 +52,10 @@ Feature: Linking of places
| W2 | R13 |
| R13 | - |
| R23 | - |
When sending search query "rhein"
Then results contain
| osm |
| R13 |
When geocoding "rhein"
Then the result set contains
| object |
| R13 |
Scenario: Relations are not linked when in waterway relations
Given the grid
@@ -79,11 +78,13 @@ Feature: Linking of places
| W2 | - |
| R1 | - |
| R2 | - |
When sending search query "rhein"
Then results contain
| ID | osm |
| 0 | R1 |
| 1 | W2 |
When geocoding "rhein"
Then result 0 contains
| object |
| R1 |
And result 1 contains
| object |
| W2 |
Scenario: Empty waterway relations are handled correctly
@@ -136,9 +137,9 @@ Feature: Linking of places
| object | linked_place_id |
| W1 | - |
| W2 | R1 |
When sending search query "rhein2"
Then results contain
| osm |
When geocoding "rhein2"
Then the result set contains
| object |
| W1 |
# github #573
@@ -180,8 +181,8 @@ Feature: Linking of places
| object | linked_place_id |
| N2 | R13 |
And placex contains
| object | centroid | name+name | extratags+linked_place |
| R13 | 9 | Garbo | hamlet |
| object | centroid!wkt | name+name | extratags+linked_place |
| R13 | 9 | Garbo | hamlet |
Scenario: Boundaries with place tags are linked against places with same type
Given the 0.01 grid
@@ -201,18 +202,18 @@ Feature: Linking of places
And placex contains
| object | rank_address |
| R13 | 16 |
When sending search query ""
When geocoding ""
| city |
| Berlin |
Then results contain
| ID | osm |
| 0 | R13 |
When sending search query ""
Then result 0 contains
| object |
| R13 |
When geocoding ""
| state |
| Berlin |
Then results contain
| ID | osm |
| 0 | R13 |
Then result 0 contains
| object |
| R13 |
Scenario: Boundaries without place tags only link against same admin level
@@ -233,18 +234,18 @@ Feature: Linking of places
And placex contains
| object | rank_address |
| R13 | 8 |
When sending search query ""
When geocoding ""
| state |
| Berlin |
Then results contain
| ID | osm |
| 0 | R13 |
When sending search query ""
Then result 0 contains
| object |
| R13 |
When geocoding ""
| city |
| Berlin |
Then results contain
| ID | osm |
| 0 | N2 |
Then result 0 contains
| object |
| N2 |
# github #1352
Scenario: Do not use linked centroid when it is outside the area
@@ -266,8 +267,8 @@ Feature: Linking of places
| object | linked_place_id |
| N2 | R13 |
And placex contains
| object | centroid |
| R13 | in geometry |
| object | centroid!in_box |
| R13 | 0,0,0.1,0.1 |
Scenario: Place nodes can only be linked once
Given the 0.02 grid
@@ -286,7 +287,7 @@ Feature: Linking of places
| object | linked_place_id |
| N2 | R1 |
And placex contains
| object | extratags |
| object | extratags!dict |
| R1 | 'linked_place' : 'city', 'wikidata': 'Q1234' |
| R2 | 'wikidata': 'Q1234' |
@@ -310,3 +311,22 @@ Feature: Linking of places
| object | name+_place_name |
| R1 | LabelPlace |
@skip
Scenario: Linked places expand default language names
Given the grid
| 1 | | 2 |
| | 9 | |
| 4 | | 3 |
Given the places
| osm | class | type | name+name | geometry |
| N9 | place | city | Popayán | 9 |
| R1 | boundary | administrative | Perímetro Urbano Popayán | (1,2,3,4,1) |
And the relations
| id | members |
| 1 | N9:label |
When importing
Then placex contains
| object | name+_place_name | name+_place_name:es |
| R1 | Popayán | Popayán |

View File

@@ -0,0 +1,104 @@
Feature: Import and search of names
Tests all naming related import issues
Scenario: No copying name tag if only one name
Given the places
| osm | class | type | name+name | geometry |
| N1 | place | locality | german | country:de |
When importing
Then placex contains
| object | country_code | name+name |
| N1 | de | german |
Scenario: Copying name tag to default language if it does not exist
Given the places
| osm | class | type | name+name | name+name:fi | geometry |
| N1 | place | locality | german | finnish | country:de |
When importing
Then placex contains
| object | country_code | name+name | name+name:fi | name+name:de |
| N1 | de | german | finnish | german |
Scenario: Copying default language name tag to name if it does not exist
Given the places
| osm | class | type | name+name:de | name+name:fi | geometry |
| N1 | place | locality | german | finnish | country:de |
When importing
Then placex contains
| object | country_code | name+name | name+name:fi | name+name:de |
| N1 | de | german | finnish | german |
Scenario: Do not overwrite default language with name tag
Given the places
| osm | class | type | name+name | name+name:fi | name+name:de | geometry |
| N1 | place | locality | german | finnish | local | country:de |
When importing
Then placex contains
| object | country_code | name+name | name+name:fi | name+name:de |
| N1 | de | german | finnish | local |
Scenario Outline: Names in any script can be found
Given the places
| osm | class | type | name+name |
| N1 | place | hamlet | <name> |
When importing
And geocoding "<name>"
Then the result set contains
| object |
| N1 |
Examples:
| name |
| Berlin |
| |
| Вологда |
| Αθήνα |
| القاهرة |
| |
| |
| |
Scenario: German umlauts can be found when expanded
Given the places
| osm | class | type | name+name:de |
| N1 | place | city | Münster |
| N2 | place | city | Köln |
| N3 | place | city | Gräfenroda |
When importing
When geocoding "münster"
Then the result set contains
| object |
| N1 |
When geocoding "muenster"
Then the result set contains
| object |
| N1 |
When geocoding "munster"
Then the result set contains
| object |
| N1 |
When geocoding "Köln"
Then the result set contains
| object |
| N2 |
When geocoding "Koeln"
Then the result set contains
| object |
| N2 |
When geocoding "Koln"
Then the result set contains
| object |
| N2 |
When geocoding "gräfenroda"
Then the result set contains
| object |
| N3 |
When geocoding "graefenroda"
Then the result set contains
| object |
| N3 |
When geocoding "grafenroda"
Then the result set contains
| object |
| N3 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Parenting of objects
Tests that the correct parent is chosen
@@ -21,14 +20,14 @@ Feature: Parenting of objects
| object | parent_place_id |
| N1 | W1 |
| N2 | W1 |
When sending search query "4 galoo"
Then results contain
| ID | osm | display_name |
| 0 | N1 | 4, galoo, 12345, Deutschland |
When sending search query "5 galoo"
Then results contain
| ID | osm | display_name |
| 0 | N2 | 5, galoo, 99999, Deutschland |
When geocoding "4 galoo"
Then result 0 contains
| object | display_name |
| N1 | 4, galoo, 12345, Deutschland |
When geocoding "5 galoo"
Then result 0 contains
| object | display_name |
| N2 | 5, galoo, 99999, Deutschland |
Scenario: Address without tags, closest street
Given the grid
@@ -484,9 +483,9 @@ Feature: Parenting of objects
| N1 | W3 | 3 |
| N2 | W3 | 3 |
| N3 | W3 | 3 |
When sending geocodejson search query "3, foo" with address
Then results contain
| housenumber |
When geocoding "3, foo"
Then the result set contains
| address+house_number |
| 3 |
Scenario: POIs don't inherit from streets

View File

@@ -1,4 +1,3 @@
@DB
Feature: Import into placex
Tests that data in placex is completed correctly.
@@ -8,8 +7,8 @@ Feature: Import into placex
| N1 | highway | primary | country:us |
When importing
Then placex contains
| object | addr+country | country_code |
| N1 | - | us |
| object | address | country_code |
| N1 | - | us |
Scenario: Location overwrites country code tag
Given the named places

View File

@@ -1,4 +1,3 @@
@DB
Feature: Import of postcodes
Tests for postcode estimation
@@ -176,11 +175,10 @@ Feature: Import of postcodes
| N34 | place | house | 01982 | 111 |country:de |
When importing
Then location_postcode contains exactly
| country | postcode | geometry |
| de | 01982 | country:de |
| country_code | postcode | geometry!wkt |
| de | 01982 | country:de |
@Fail
@skip
Scenario: search and address ranks for GB post codes correctly assigned
Given the places
| osm | class | type | postcode | geometry |
@@ -189,10 +187,10 @@ Feature: Import of postcodes
| N3 | place | postcode | Y45 | country:gb |
When importing
Then location_postcode contains exactly
| postcode | country | rank_search | rank_address |
| E45 2CD | gb | 25 | 5 |
| E45 2 | gb | 23 | 5 |
| Y45 | gb | 21 | 5 |
| postcode | country_code | rank_search | rank_address |
| E45 2CD | gb | 25 | 5 |
| E45 2 | gb | 23 | 5 |
| Y45 | gb | 21 | 5 |
Scenario: Postcodes outside all countries are not added to the postcode table
Given the places
@@ -203,8 +201,8 @@ Feature: Import of postcodes
| N1 | place | hamlet | Null Island | 0 0 |
When importing
Then location_postcode contains exactly
| country | postcode | geometry |
When sending search query "111, 01982 Null Island"
Then results contain
| osm | display_name |
| N34 | 111, Null Island, 01982 |
| place_id |
When geocoding "111, 01982 Null Island"
Then the result set contains
| object | display_name |
| N34 | 111, Null Island, 01982 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Rank assignment
Tests for assignment of search and address ranks.
@@ -173,13 +172,9 @@ Feature: Rank assignment
| R23 | 20 | 0 |
| R21 | 18 | 0 |
| R22 | 16 | 16 |
Then place_addressline contains
Then place_addressline contains exactly
| object | address | cached_rank_address |
| N20 | R22 | 16 |
Then place_addressline doesn't contain
| object | address |
| N20 | R21 |
| N20 | R23 |
Scenario: adjacent admin_levels are considered different objects when they have different wikidata
Given the named places

View File

@@ -1,4 +1,3 @@
@DB
Feature: Creation of search terms
Tests that search_name table is filled correctly
@@ -6,43 +5,40 @@ Feature: Creation of search terms
Given the places
| osm | class | type | name+alt_name |
| N1 | place | city | New York; Big Apple |
| N2 | place | town | New York Big Apple |
When importing
Then search_name contains
| object | name_vector |
| N1 | #New York, #Big Apple |
And geocoding "New York Big Apple"
Then result 0 contains
| object |
| N2 |
Scenario: Comma-separated names appear as a single full name
Given the places
| osm | class | type | name+alt_name |
| osm | class | type | name+name |
| N1 | place | city | New York, Big Apple |
| N2 | place | town | New York Big Apple |
When importing
Then search_name contains
| object | name_vector |
| N1 | #New York Big Apple |
And geocoding "New York Big Apple"
Then result 0 contains
| object |
| N1 |
Scenario: Name parts before brackets appear as full names
Given the places
| osm | class | type | name+name |
| N1 | place | city | Halle (Saale) |
| N2 | place | town | Halle |
When importing
Then search_name contains
| object | name_vector |
| N1 | #Halle Saale, #Halle |
And geocoding "Halle"
Then result 0 contains
| object |
| N1 |
When geocoding "Halle (Saale)"
Then the result set contains
| object |
| N1 |
Scenario: Unnamed POIs have no search entry
Given the grid
| | 1 | | |
| 10 | | | 11 |
And the places
| osm | class | type |
| N1 | place | house |
And the named places
| osm | class | type | geometry |
| W1 | highway | residential | 10,11 |
When importing
Then search_name has no entry for N1
Scenario: Unnamed POI has a search entry when it has unknown addr: tags
Scenario: Unknown addr: tags can be found for unnamed POIs
Given the grid
| | 1 | | |
| 10 | | | 11 |
@@ -53,21 +49,18 @@ Feature: Creation of search terms
| osm | class | type | name+name | geometry |
| W1 | highway | residential | Rose Street | 10,11 |
When importing
Then search_name contains
| object | nameaddress_vector |
| N1 | #Rose Street, Walltown |
When sending search query "23 Rose Street, Walltown"
Then results contain
| osm | display_name |
| N1 | 23, Rose Street |
When sending search query "Walltown, Rose Street 23"
Then results contain
| osm | display_name |
| N1 | 23, Rose Street |
When sending search query "Rose Street 23, Walltown"
Then results contain
| osm | display_name |
| N1 | 23, Rose Street |
When geocoding "23 Rose Street, Walltown"
Then the result set contains
| object | display_name |
| N1 | 23, Rose Street |
When geocoding "Walltown, Rose Street 23"
Then the result set contains
| object | display_name |
| N1 | 23, Rose Street |
When geocoding "Rose Street 23, Walltown"
Then the result set contains
| object | display_name |
| N1 | 23, Rose Street |
Scenario: Searching for unknown addr: tags also works for multiple words
Given the grid
@@ -80,23 +73,20 @@ Feature: Creation of search terms
| osm | class | type | name+name | geometry |
| W1 | highway | residential | Rose Street | 10,11 |
When importing
Then search_name contains
| object | nameaddress_vector |
| N1 | #Rose Street, rose, Little, Big, Town |
When sending search query "23 Rose Street, Little Big Town"
Then results contain
| osm | display_name |
| N1 | 23, Rose Street |
When sending search query "Rose Street 23, Little Big Town"
Then results contain
| osm | display_name |
| N1 | 23, Rose Street |
When sending search query "Little big Town, Rose Street 23"
Then results contain
| osm | display_name |
| N1 | 23, Rose Street |
When geocoding "23 Rose Street, Little Big Town"
Then the result set contains
| object | display_name |
| N1 | 23, Rose Street |
When geocoding "Rose Street 23, Little Big Town"
Then the result set contains
| object | display_name |
| N1 | 23, Rose Street |
When geocoding "Little big Town, Rose Street 23"
Then the result set contains
| object | display_name |
| N1 | 23, Rose Street |
Scenario: Unnamed POI has no search entry when it has known addr: tags
Scenario: Unnamed POI can be found when it has known addr: tags
Given the grid
| | 1 | | |
| 10 | | | 11 |
@@ -107,24 +97,10 @@ Feature: Creation of search terms
| osm | class | type | name+name | addr+city | geometry |
| W1 | highway | residential | Rose Street | Walltown | 10,11 |
When importing
Then search_name has no entry for N1
When sending search query "23 Rose Street, Walltown"
Then results contain
| osm | display_name |
| N1 | 23, Rose Street |
Scenario: Unnamed POI must have a house number to get a search entry
Given the grid
| | 1 | | |
| 10 | | | 11 |
And the places
| osm | class | type | addr+city |
| N1 | place | house | Walltown |
And the places
| osm | class | type | name+name | geometry |
| W1 | highway | residential | Rose Street | 10,11 |
When importing
Then search_name has no entry for N1
When geocoding "23 Rose Street, Walltown"
Then the result set contains
| object | display_name |
| N1 | 23, Rose Street |
Scenario: Unnamed POIs inherit parent name when unknown addr:place is present
Given the grid
@@ -142,23 +118,22 @@ Feature: Creation of search terms
Then placex contains
| object | parent_place_id |
| N1 | R1 |
When sending search query "23 Rose Street"
Then exactly 1 results are returned
And results contain
| osm | display_name |
| W1 | Rose Street, Strange Town |
When sending search query "23 Walltown, Strange Town"
Then results contain
| osm | display_name |
| N1 | 23, Walltown, Strange Town |
When sending search query "Walltown 23, Strange Town"
Then results contain
| osm | display_name |
| N1 | 23, Walltown, Strange Town |
When sending search query "Strange Town, Walltown 23"
Then results contain
| osm | display_name |
| N1 | 23, Walltown, Strange Town |
When geocoding "23 Rose Street"
Then all results contain
| object | display_name |
| W1 | Rose Street, Strange Town |
When geocoding "23 Walltown, Strange Town"
Then the result set contains
| object | display_name |
| N1 | 23, Walltown, Strange Town |
When geocoding "Walltown 23, Strange Town"
Then the result set contains
| object | display_name |
| N1 | 23, Walltown, Strange Town |
When geocoding "Strange Town, Walltown 23"
Then the result set contains
| object | display_name |
| N1 | 23, Walltown, Strange Town |
Scenario: Named POIs can be searched by housenumber when unknown addr:place is present
Given the grid
@@ -173,26 +148,26 @@ Feature: Creation of search terms
| W1 | highway | residential | Rose Street | 10,11 |
| R1 | place | city | Strange Town | (100,101,102,103,100) |
When importing
When sending search query "23 Walltown, Strange Town"
Then results contain
| osm | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
When sending search query "Walltown 23, Strange Town"
Then results contain
| osm | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
When sending search query "Strange Town, Walltown 23"
Then results contain
| osm | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
When sending search query "Strange Town, Walltown 23, Blue house"
Then results contain
| osm | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
When sending search query "Strange Town, Walltown, Blue house"
Then results contain
| osm | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
When geocoding "23 Walltown, Strange Town"
Then the result set contains
| object | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
When geocoding "Walltown 23, Strange Town"
Then the result set contains
| object | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
When geocoding "Strange Town, Walltown 23"
Then the result set contains
| object | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
When geocoding "Strange Town, Walltown 23, Blue house"
Then the result set contains
| object | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
When geocoding "Strange Town, Walltown, Blue house"
Then the result set contains
| object | display_name |
| N1 | Blue house, 23, Walltown, Strange Town |
Scenario: Named POIs can be found when unknown multi-word addr:place is present
Given the grid
@@ -207,14 +182,14 @@ Feature: Creation of search terms
| W1 | highway | residential | Rose Street | 10,11 |
| R1 | place | city | Strange Town | (100,101,102,103,100) |
When importing
When sending search query "23 Moon Sun, Strange Town"
Then results contain
| osm | display_name |
| N1 | Blue house, 23, Moon sun, Strange Town |
When sending search query "Blue house, Moon Sun, Strange Town"
Then results contain
| osm | display_name |
| N1 | Blue house, 23, Moon sun, Strange Town |
When geocoding "23 Moon Sun, Strange Town"
Then the result set contains
| object | display_name |
| N1 | Blue house, 23, Moon sun, Strange Town |
When geocoding "Blue house, Moon Sun, Strange Town"
Then the result set contains
| object | display_name |
| N1 | Blue house, 23, Moon sun, Strange Town |
Scenario: Unnamed POIs doesn't inherit parent name when addr:place is present only in parent address
Given the grid
@@ -229,16 +204,14 @@ Feature: Creation of search terms
| W1 | highway | residential | Rose Street | Walltown | 10,11 |
| R1 | place | suburb | Strange Town | Walltown | (100,101,102,103,100) |
When importing
When sending search query "23 Rose Street, Walltown"
Then exactly 1 result is returned
And results contain
| osm | display_name |
| W1 | Rose Street, Strange Town |
When sending search query "23 Walltown"
Then exactly 1 result is returned
And results contain
| osm | display_name |
| N1 | 23, Walltown, Strange Town |
When geocoding "23 Rose Street, Walltown"
Then all results contain
| object | display_name |
| W1 | Rose Street, Strange Town |
When geocoding "23 Walltown"
Then all results contain
| object | display_name |
| N1 | 23, Walltown, Strange Town |
Scenario: Unnamed POIs does inherit parent name when unknown addr:place and addr:street is present
Given the grid
@@ -251,12 +224,11 @@ Feature: Creation of search terms
| osm | class | type | name+name | geometry |
| W1 | highway | residential | Rose Street | 10,11 |
When importing
Then search_name has no entry for N1
When sending search query "23 Rose Street"
Then results contain
| osm | display_name |
| N1 | 23, Rose Street |
When sending search query "23 Lily Street"
When geocoding "23 Rose Street"
Then the result set contains
| object | display_name |
| N1 | 23, Rose Street |
When geocoding "23 Lily Street"
Then exactly 0 results are returned
Scenario: An unknown addr:street is ignored
@@ -270,15 +242,14 @@ Feature: Creation of search terms
| osm | class | type | name+name | geometry |
| W1 | highway | residential | Rose Street | 10,11 |
When importing
Then search_name has no entry for N1
When sending search query "23 Rose Street"
Then results contain
| osm | display_name |
| N1 | 23, Rose Street |
When sending search query "23 Lily Street"
When geocoding "23 Rose Street"
Then the result set contains
| object | display_name |
| N1 | 23, Rose Street |
When geocoding "23 Lily Street"
Then exactly 0 results are returned
Scenario: Named POIs get unknown address tags added in the search_name table
Scenario: Named POIs can be found through unknown address tags
Given the grid
| | 1 | | |
| 10 | | | 11 |
@@ -289,29 +260,26 @@ Feature: Creation of search terms
| osm | class | type | name+name | geometry |
| W1 | highway | residential | Rose Street | 10,11 |
When importing
Then search_name contains
| object | name_vector | nameaddress_vector |
| N1 | #Green Moss | #Rose Street, Walltown |
When sending search query "Green Moss, Rose Street, Walltown"
Then results contain
| osm | display_name |
| N1 | Green Moss, 26, Rose Street |
When sending search query "Green Moss, 26, Rose Street, Walltown"
Then results contain
| osm | display_name |
| N1 | Green Moss, 26, Rose Street |
When sending search query "26, Rose Street, Walltown"
Then results contain
| osm | display_name |
| N1 | Green Moss, 26, Rose Street |
When sending search query "Rose Street 26, Walltown"
Then results contain
| osm | display_name |
| N1 | Green Moss, 26, Rose Street |
When sending search query "Walltown, Rose Street 26"
Then results contain
| osm | display_name |
| N1 | Green Moss, 26, Rose Street |
When geocoding "Green Moss, Rose Street, Walltown"
Then the result set contains
| object | display_name |
| N1 | Green Moss, 26, Rose Street |
When geocoding "Green Moss, 26, Rose Street, Walltown"
Then the result set contains
| object | display_name |
| N1 | Green Moss, 26, Rose Street |
When geocoding "26, Rose Street, Walltown"
Then the result set contains
| object | display_name |
| N1 | Green Moss, 26, Rose Street |
When geocoding "Rose Street 26, Walltown"
Then the result set contains
| object | display_name |
| N1 | Green Moss, 26, Rose Street |
When geocoding "Walltown, Rose Street 26"
Then the result set contains
| object | display_name |
| N1 | Green Moss, 26, Rose Street |
Scenario: Named POI doesn't inherit parent name when addr:place is present only in parent address
Given the grid
@@ -326,12 +294,12 @@ Feature: Creation of search terms
| W1 | highway | residential | Rose Street | 10,11 |
| R1 | place | suburb | Strange Town | (100,101,102,103,100) |
When importing
When sending search query "Green Moss, Rose Street, Walltown"
Then exactly 0 result is returned
When sending search query "Green Moss, Walltown"
Then results contain
| osm | display_name |
| N1 | Green Moss, Walltown, Strange Town |
When geocoding "Green Moss, Rose Street, Walltown"
Then exactly 0 results are returned
When geocoding "Green Moss, Walltown"
Then the result set contains
| object | display_name |
| N1 | Green Moss, Walltown, Strange Town |
Scenario: Named POIs inherit address from parent
Given the grid
@@ -342,9 +310,10 @@ Feature: Creation of search terms
| N1 | place | house | foo | 1 |
| W1 | highway | residential | the road | 10,11 |
When importing
Then search_name contains
| object | name_vector | nameaddress_vector |
| N1 | foo | #the road |
When geocoding "foo, the road"
Then all results contain
| object |
| N1 |
Scenario: Some addr: tags are added to address
Given the grid
@@ -354,13 +323,14 @@ Feature: Creation of search terms
| osm | class | type | name |
| N2 | place | city | bonn |
| N3 | place | suburb | smalltown|
And the named places
| osm | class | type | addr+city | addr+municipality | addr+suburb | geometry |
| W1 | highway | service | bonn | New York | Smalltown | 10,11 |
And the places
| osm | class | type | name | addr+city | addr+municipality | addr+suburb | geometry |
| W1 | highway | service | the end | bonn | New York | Smalltown | 10,11 |
When importing
Then search_name contains
| object | nameaddress_vector |
| W1 | bonn, new, york, smalltown |
When geocoding "the end, new york, bonn, smalltown"
Then all results contain
| object |
| W1 |
Scenario: A known addr:* tag is added even if the name is unknown
Given the grid
@@ -369,36 +339,22 @@ Feature: Creation of search terms
| osm | class | type | name | addr+city | geometry |
| W1 | highway | residential | Road | Nandu | 10,11 |
When importing
Then search_name contains
| object | nameaddress_vector |
| W1 | nandu |
Scenario: addr:postcode is not added to the address terms
Given the grid with origin DE
| | 1 | | |
| 10 | | | 11 |
And the places
| osm | class | type | name+ref |
| N1 | place | state | 12345 |
And the named places
| osm | class | type | addr+postcode | geometry |
| W1 | highway | residential | 12345 | 10,11 |
When importing
Then search_name contains not
| object | nameaddress_vector |
| W1 | 12345 |
And geocoding "Road, Nandu"
Then all results contain
| object |
| W1 |
Scenario: a linked place does not show up in search name
Given the 0.01 grid
| 10 | | 11 |
| | 2 | |
| 13 | | 12 |
Given the named places
| osm | class | type | admin | geometry |
| R13 | boundary | administrative | 9 | (10,11,12,13,10) |
And the named places
| osm | class | type |
| N2 | place | city |
Given the places
| osm | class | type | name | admin | geometry |
| R13 | boundary | administrative | Roma | 9 | (10,11,12,13,10) |
And the places
| osm | class | type | name |
| N2 | place | city | Cite |
And the relations
| id | members | tags+type |
| 13 | N2:label | boundary |
@@ -406,7 +362,10 @@ Feature: Creation of search terms
Then placex contains
| object | linked_place_id |
| N2 | R13 |
And search_name has no entry for N2
When geocoding "Cite"
Then all results contain
| object |
| R13 |
Scenario: a linked waterway does not show up in search name
Given the grid
@@ -424,5 +383,7 @@ Feature: Creation of search terms
| object | linked_place_id |
| W1 | R13 |
| W2 | R13 |
And search_name has no entry for W1
And search_name has no entry for W2
When geocoding "Rhein"
Then all results contain
| object |
| R13 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Searching of house numbers
Test for specialised treeatment of housenumbers
@@ -17,13 +16,13 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | North Road | 1,2,3 |
When importing
And sending search query "45, North Road"
Then results contain
| osm |
And geocoding "45, North Road"
Then the result set contains
| object |
| N1 |
When sending search query "North Road 45"
Then results contain
| osm |
When geocoding "North Road 45"
Then the result set contains
| object |
| N1 |
@@ -35,17 +34,17 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | North Road | 1,2,3 |
When importing
And sending search query "45, North Road"
Then results contain
| osm |
And geocoding "45, North Road"
Then the result set contains
| object |
| N1 |
When sending search query "North Road "
Then results contain
| osm |
When geocoding "North Road "
Then the result set contains
| object |
| N1 |
When sending search query "North Road 𑁪𑁫"
Then results contain
| osm |
When geocoding "North Road 𑁪𑁫"
Then the result set contains
| object |
| N1 |
Examples:
@@ -63,17 +62,17 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | Multistr | 1,2,3 |
When importing
When sending search query "2 Multistr"
Then results contain
| osm |
When geocoding "2 Multistr"
Then the result set contains
| object |
| N1 |
When sending search query "4 Multistr"
Then results contain
| osm |
When geocoding "4 Multistr"
Then the result set contains
| object |
| N1 |
When sending search query "12 Multistr"
Then results contain
| osm |
When geocoding "12 Multistr"
Then the result set contains
| object |
| N1 |
Examples:
@@ -91,21 +90,21 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | Multistr | 1,2,3 |
When importing
When sending search query "2A Multistr"
Then results contain
| osm |
When geocoding "2A Multistr"
Then the result set contains
| object |
| N1 |
When sending search query "2 a Multistr"
Then results contain
| osm |
When geocoding "2 a Multistr"
Then the result set contains
| object |
| N1 |
When sending search query "2-A Multistr"
Then results contain
| osm |
When geocoding "2-A Multistr"
Then the result set contains
| object |
| N1 |
When sending search query "Multistr 2 A"
Then results contain
| osm |
When geocoding "Multistr 2 A"
Then the result set contains
| object |
| N1 |
Examples:
@@ -124,21 +123,21 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | Chester St | 1,2,3 |
When importing
When sending search query "34-10 Chester St"
Then results contain
| osm |
When geocoding "34-10 Chester St"
Then the result set contains
| object |
| N1 |
When sending search query "34/10 Chester St"
Then results contain
| osm |
When geocoding "34/10 Chester St"
Then the result set contains
| object |
| N1 |
When sending search query "34 10 Chester St"
Then results contain
| osm |
When geocoding "34 10 Chester St"
Then the result set contains
| object |
| N1 |
When sending search query "3410 Chester St"
Then results contain
| osm |
When geocoding "3410 Chester St"
Then the result set contains
| object |
| W10 |
Examples:
@@ -156,21 +155,21 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | Rue Paris | 1,2,3 |
When importing
When sending search query "Rue Paris 45bis"
Then results contain
| osm |
When geocoding "Rue Paris 45bis"
Then the result set contains
| object |
| N1 |
When sending search query "Rue Paris 45 BIS"
Then results contain
| osm |
When geocoding "Rue Paris 45 BIS"
Then the result set contains
| object |
| N1 |
When sending search query "Rue Paris 45BIS"
Then results contain
| osm |
When geocoding "Rue Paris 45BIS"
Then the result set contains
| object |
| N1 |
When sending search query "Rue Paris 45 bis"
Then results contain
| osm |
When geocoding "Rue Paris 45 bis"
Then the result set contains
| object |
| N1 |
Examples:
@@ -189,21 +188,21 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | Rue du Berger | 1,2,3 |
When importing
When sending search query "Rue du Berger 45ter"
Then results contain
| osm |
When geocoding "Rue du Berger 45ter"
Then the result set contains
| object |
| N1 |
When sending search query "Rue du Berger 45 TER"
Then results contain
| osm |
When geocoding "Rue du Berger 45 TER"
Then the result set contains
| object |
| N1 |
When sending search query "Rue du Berger 45TER"
Then results contain
| osm |
When geocoding "Rue du Berger 45TER"
Then the result set contains
| object |
| N1 |
When sending search query "Rue du Berger 45 ter"
Then results contain
| osm |
When geocoding "Rue du Berger 45 ter"
Then the result set contains
| object |
| N1 |
Examples:
@@ -222,21 +221,21 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | Herengracht | 1,2,3 |
When importing
When sending search query "501-H 1 Herengracht"
Then results contain
| osm |
When geocoding "501-H 1 Herengracht"
Then the result set contains
| object |
| N1 |
When sending search query "501H-1 Herengracht"
Then results contain
| osm |
When geocoding "501H-1 Herengracht"
Then the result set contains
| object |
| N1 |
When sending search query "501H1 Herengracht"
Then results contain
| osm |
When geocoding "501H1 Herengracht"
Then the result set contains
| object |
| N1 |
When sending search query "501-H1 Herengracht"
Then results contain
| osm |
When geocoding "501-H1 Herengracht"
Then the result set contains
| object |
| N1 |
Examples:
@@ -255,17 +254,17 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | Голубинская улица | 1,2,3 |
When importing
When sending search query "Голубинская улица 55к3"
Then results contain
| osm |
When geocoding "Голубинская улица 55к3"
Then the result set contains
| object |
| N1 |
When sending search query "Голубинская улица 55 k3"
Then results contain
| osm |
When geocoding "Голубинская улица 55 k3"
Then the result set contains
| object |
| N1 |
When sending search query "Голубинская улица 55 к-3"
Then results contain
| osm |
When geocoding "Голубинская улица 55 к-3"
Then the result set contains
| object |
| N1 |
Examples:
@@ -282,9 +281,9 @@ Feature: Searching of house numbers
| osm | class | type | name | geometry |
| W10 | highway | path | Chester St | 1,2,3 |
When importing
When sending search query "Chester St Warring"
Then results contain
| osm |
When geocoding "Chester St Warring"
Then the result set contains
| object |
| N1 |
@@ -311,11 +310,11 @@ Feature: Searching of house numbers
| 10 | 10, 11 |
| 20 | 20, 21 |
When importing
When sending search query "Ringstr 12"
Then results contain
| osm |
When geocoding "Ringstr 12"
Then the result set contains
| object |
| W10 |
When sending search query "Ringstr 13"
Then results contain
| osm |
When geocoding "Ringstr 13"
Then the result set contains
| object |
| W20 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Query of address interpolations
Tests that interpolated addresses can be queried correctly
@@ -23,14 +22,14 @@ Feature: Query of address interpolations
| id | nodes |
| 1 | 1,3 |
When importing
When sending v1/reverse N2
Then results contain
| ID | display_name |
| 0 | 3, Nickway |
When sending search query "Nickway 3"
Then results contain
| osm | display_name |
| W1 | 3, Nickway |
When reverse geocoding at node 2
Then the result contains
| display_name |
| 3, Nickway |
When geocoding "Nickway 3"
Then all results contain
| object | display_name |
| W1 | 3, Nickway |
Scenario: Find interpolations with multiple numbers
@@ -48,11 +47,11 @@ Feature: Query of address interpolations
| id | nodes |
| 1 | 1,3 |
When importing
When sending v1/reverse N2
Then results contain
| ID | display_name | centroid |
| 0 | 10, Nickway | 2 |
When sending search query "Nickway 10"
Then results contain
| osm | display_name | centroid |
| W1 | 10, Nickway | 2 |
When reverse geocoding at node 2
Then the result contains
| display_name | centroid!wkt |
| 10, Nickway | 2 |
When geocoding "Nickway 10"
Then all results contain
| object | display_name | centroid!wkt |
| W1 | 10, Nickway | 2 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Searches in Japan
Test specifically for searches of Japanese addresses and in Japanese language.
Scenario: A block house-number is parented to the neighbourhood
@@ -23,7 +22,7 @@ Feature: Searches in Japan
Then placex contains
| object | parent_place_id |
| N3 | N9 |
When sending search query "2 6-2"
Then results contain
| osm |
When geocoding "2 6-2"
Then all results contain
| object |
| N3 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Searching linked places
Tests that information from linked places can be searched correctly
@@ -20,18 +19,18 @@ Feature: Searching linked places
Then placex contains
| object | linked_place_id |
| N2 | R13 |
When sending search query "Vario"
When geocoding "Vario"
| namedetails |
| 1 |
Then results contain
| osm | display_name | namedetails |
| R13 | Garbo | "name": "Garbo", "name:it": "Vario" |
When sending search query "Vario"
Then all results contain
| object | display_name | namedetails!dict |
| R13 | Garbo | "name": "Garbo", "name:it": "Vario" |
When geocoding "Vario"
| accept-language |
| it |
Then results contain
| osm | display_name |
| R13 | Vario |
Then all results contain
| object | display_name |
| R13 | Vario |
Scenario: Differing names from linked places are searchable
@@ -52,13 +51,13 @@ Feature: Searching linked places
Then placex contains
| object | linked_place_id |
| N2 | R13 |
When sending search query "Vario"
When geocoding "Vario"
| namedetails |
| 1 |
Then results contain
| osm | display_name | namedetails |
| R13 | Garbo | "name": "Garbo", "_place_name": "Vario" |
When sending search query "Garbo"
Then results contain
| osm | display_name |
| R13 | Garbo |
Then all results contain
| object | display_name | namedetails!dict |
| R13 | Garbo | "name": "Garbo", "_place_name": "Vario" |
When geocoding "Garbo"
Then all results contain
| object | display_name |
| R13 | Garbo |

View File

@@ -0,0 +1,225 @@
Feature: Import and search of names
Tests all naming related issues: normalisation,
abbreviations, internationalisation, etc.
Scenario: non-latin scripts can be found
Given the places
| osm | class | type | name |
| N1 | place | locality | Речицкий район |
| N2 | place | locality | Refugio de montaña |
| N3 | place | locality | |
| N4 | place | locality | الدوحة |
When importing
When geocoding "Речицкий район"
Then result 0 contains
| object |
| N1 |
When geocoding "Refugio de montaña"
Then result 0 contains
| object |
| N2 |
When geocoding ""
Then result 0 contains
| object |
| N3 |
When geocoding "الدوحة"
Then result 0 contains
| object |
| N4 |
Scenario: Case-insensitivity of search
Given the places
| osm | class | type | name |
| N1 | place | locality | FooBar |
When importing
Then placex contains
| object | class | type | name+name |
| N1 | place | locality | FooBar |
When geocoding "FooBar"
Then result 0 contains
| object |
| N1 |
When geocoding "foobar"
Then result 0 contains
| object |
| N1 |
When geocoding "fOObar"
Then result 0 contains
| object |
| N1 |
When geocoding "FOOBAR"
Then result 0 contains
| object |
| N1 |
Scenario: Multiple spaces in name
Given the places
| osm | class | type | name |
| N1 | place | locality | one two three |
When importing
When geocoding "one two three"
Then result 0 contains
| object |
| N1 |
When geocoding "one two three"
Then result 0 contains
| object |
| N1 |
When geocoding "one two three"
Then result 0 contains
| object |
| N1 |
When geocoding " one two three"
Then result 0 contains
| object |
| N1 |
Scenario: Special characters in name
Given the places
| osm | class | type | name+name:de |
| N1 | place | locality | Jim-Knopf-Straße |
| N2 | place | locality | Smith/Weston |
| N3 | place | locality | space mountain |
| N4 | place | locality | space |
| N5 | place | locality | mountain |
When importing
When geocoding "Jim-Knopf-Str"
Then result 0 contains
| object |
| N1 |
When geocoding "Jim Knopf-Str"
Then result 0 contains
| object |
| N1 |
When geocoding "Jim Knopf Str"
Then result 0 contains
| object |
| N1 |
When geocoding "Jim/Knopf-Str"
Then result 0 contains
| object |
| N1 |
When geocoding "Jim-Knopfstr"
Then result 0 contains
| object |
| N1 |
When geocoding "Smith/Weston"
Then result 0 contains
| object |
| N2 |
When geocoding "Smith Weston"
Then result 0 contains
| object |
| N2 |
When geocoding "Smith-Weston"
Then result 0 contains
| object |
| N2 |
When geocoding "space mountain"
Then result 0 contains
| object |
| N3 |
When geocoding "space-mountain"
Then result 0 contains
| object |
| N3 |
When geocoding "space/mountain"
Then result 0 contains
| object |
| N3 |
When geocoding "space\mountain"
Then result 0 contains
| object |
| N3 |
When geocoding "space(mountain)"
Then result 0 contains
| object |
| N3 |
Scenario: Landuse with name are found
Given the grid
| 1 | 2 |
| 3 | |
Given the places
| osm | class | type | name | geometry |
| R1 | natural | meadow | landuse1 | (1,2,3,1) |
| R2 | landuse | industrial | landuse2 | (2,3,1,2) |
When importing
When geocoding "landuse1"
Then result 0 contains
| object |
| R1 |
When geocoding "landuse2"
Then result 0 contains
| object |
| R2 |
Scenario: Postcode boundaries without ref
Given the grid with origin FR
| | 2 | |
| 1 | | 3 |
Given the places
| osm | class | type | postcode | geometry |
| R1 | boundary | postal_code | 123-45 | (1,2,3,1) |
When importing
When geocoding "123-45"
Then result 0 contains
| object |
| R1 |
Scenario Outline: Housenumbers with special characters are found
Given the grid
| 1 | | | | 2 |
| | | 9 | | |
And the places
| osm | class | type | name | geometry |
| W1 | highway | primary | Main St | 1,2 |
And the places
| osm | class | type | housenr | geometry |
| N1 | building | yes | <nr> | 9 |
When importing
And geocoding "Main St <nr>"
Then result 0 contains
| object | display_name |
| N1 | <nr>, Main St |
Examples:
| nr |
| 1 |
| 3456 |
| 1 a |
| 56b |
| 1 A |
| 2 |
| 1Б |
| 1 к1 |
| 23-123 |
Scenario Outline: Housenumbers in lists are found
Given the grid
| 1 | | | | 2 |
| | | 9 | | |
And the places
| osm | class | type | name | geometry |
| W1 | highway | primary | Main St | 1,2 |
And the places
| osm | class | type | housenr | geometry |
| N1 | building | yes | <nr-list> | 9 |
When importing
And geocoding "Main St <nr>"
Then result 0 contains
| object | display_name |
| N1 | <nr-list>, Main St |
Examples:
| nr-list | nr |
| 1,2,3 | 1 |
| 1,2,3 | 2 |
| 1, 2, 3 | 3 |
| 45 ;67;3 | 45 |
| 45 ;67;3 | 67 |
| 1a;1k | 1a |
| 1a;1k | 1k |
| 34/678 | 34 |
| 34/678 | 678 |
| 34/678 | 34/678 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Querying fo postcode variants
Scenario: Postcodes in Singapore (6-digit postcode)
@@ -8,10 +7,10 @@ Feature: Querying fo postcode variants
| osm | class | type | name | addr+postcode | geometry |
| W1 | highway | path | Lorang | 399174 | 10,11 |
When importing
When sending search query "399174"
Then results contain
| ID | type | display_name |
| 0 | postcode | 399174, Singapore |
When geocoding "399174"
Then result 0 contains
| type | display_name |
| postcode | 399174, Singapore |
Scenario Outline: Postcodes in the Netherlands (mixed postcode with spaces)
@@ -21,14 +20,14 @@ Feature: Querying fo postcode variants
| osm | class | type | name | addr+postcode | geometry |
| W1 | highway | path | De Weide | 3993 DX | 10,11 |
When importing
When sending search query "3993 DX"
Then results contain
| ID | type | display_name |
| 0 | postcode | 3993 DX, Nederland |
When sending search query "3993dx"
Then results contain
| ID | type | display_name |
| 0 | postcode | 3993 DX, Nederland |
When geocoding "3993 DX"
Then result 0 contains
| type | display_name |
| postcode | 3993 DX, Nederland |
When geocoding "3993dx"
Then result 0 contains
| type | display_name |
| postcode | 3993 DX, Nederland |
Examples:
| postcode |
@@ -44,10 +43,10 @@ Feature: Querying fo postcode variants
| osm | class | type | name | addr+postcode | geometry |
| W1 | highway | path | Lorang | 399174 | 10,11 |
When importing
When sending search query "399174"
Then results contain
| ID | type | display_name |
| 0 | postcode | 399174, Singapore |
When geocoding "399174"
Then result 0 contains
| type | display_name |
| postcode | 399174, Singapore |
Scenario Outline: Postcodes in Andorra (with country code)
@@ -57,14 +56,14 @@ Feature: Querying fo postcode variants
| osm | class | type | name | addr+postcode | geometry |
| W1 | highway | path | Lorang | <postcode> | 10,11 |
When importing
When sending search query "675"
Then results contain
| ID | type | display_name |
| 0 | postcode | AD675, Andorra |
When sending search query "AD675"
Then results contain
| ID | type | display_name |
| 0 | postcode | AD675, Andorra |
When geocoding "675"
Then result 0 contains
| type | display_name |
| postcode | AD675, Andorra |
When geocoding "AD675"
Then result 0 contains
| type | display_name |
| postcode | AD675, Andorra |
Examples:
| postcode |
@@ -80,15 +79,15 @@ Feature: Querying fo postcode variants
| N35 | place | house | E4 7EA | 111 | country:gb |
When importing
Then location_postcode contains exactly
| country | postcode | geometry |
| gb | EH4 7EA | country:gb |
| gb | E4 7EA | country:gb |
When sending search query "EH4 7EA"
Then results contain
| country_code | postcode | geometry!wkt |
| gb | EH4 7EA | country:gb |
| gb | E4 7EA | country:gb |
When geocoding "EH4 7EA"
Then result 0 contains
| type | display_name |
| postcode | EH4 7EA, United Kingdom |
When sending search query "E4 7EA"
Then results contain
When geocoding "E4 7EA"
Then result 0 contains
| type | display_name |
| postcode | E4 7EA, United Kingdom |
@@ -102,9 +101,9 @@ Feature: Querying fo postcode variants
| R23 | boundary | postal_code | 12345 | (1,2,3,4,1) |
When importing
Then location_postcode contains exactly
| country | postcode |
| de | 12345 |
When sending search query "12345, de"
Then results contain
| osm |
| country_code | postcode |
| de | 12345 |
When geocoding "12345, de"
Then result 0 contains
| object |
| R23 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Reverse searches
Test results of reverse queries
@@ -12,11 +11,11 @@ Feature: Reverse searches
| W1 | aeroway | terminal | (1,2,3,4,1) |
| N1 | amenity | restaurant | 9 |
When importing
And sending v1/reverse at 1.0001,1.0001
Then results contain
| osm |
And reverse geocoding 1.0001,1.0001
Then the result contains
| object |
| N1 |
When sending v1/reverse at 1.0003,1.0001
Then results contain
| osm |
When reverse geocoding 1.0003,1.0001
Then the result contains
| object |
| W1 |

View File

@@ -1,4 +1,3 @@
@DB
Feature: Searching of simple objects
Testing simple stuff
@@ -7,32 +6,10 @@ Feature: Searching of simple objects
| osm | class | type | name+name | geometry |
| N1 | place | village | Foo | 10.0 -10.0 |
When importing
And sending search query "Foo"
Then results contain
| ID | osm | category | type | centroid |
| 0 | N1 | place | village | 10 -10 |
Scenario: Updating postcode in postcode boundaries without ref
Given the grid
| 1 | 2 |
| 4 | 3 |
Given the places
| osm | class | type | postcode | geometry |
| R1 | boundary | postal_code | 12345 | (1,2,3,4,1) |
When importing
And sending search query "12345"
Then results contain
| ID | osm |
| 0 | R1 |
When updating places
| osm | class | type | postcode | geometry |
| R1 | boundary | postal_code | 54321 | (1,2,3,4,1) |
And sending search query "12345"
Then exactly 0 results are returned
When sending search query "54321"
Then results contain
| ID | osm |
| 0 | R1 |
And geocoding "Foo"
Then result 0 contains
| object | category | type | centroid!wkt |
| N1 | place | village | 10 -10 |
# github #1763
Scenario: Correct translation of highways under construction
@@ -44,8 +21,8 @@ Feature: Searching of simple objects
| W1 | highway | construction | The build | 1,2 |
| N1 | amenity | cafe | Bean | 9 |
When importing
And sending json search query "Bean" with address
Then result addresses contain
And geocoding "Bean"
Then result 0 contains in field address
| amenity | road |
| Bean | The build |
@@ -57,11 +34,11 @@ Feature: Searching of simple objects
| osm | class | type | name | housenr |
| N20 | amenity | restaurant | Red Way | 34 |
When importing
And sending search query "Wood Street 45"
And geocoding "Wood Street 45"
Then exactly 0 results are returned
When sending search query "Red Way 34"
Then results contain
| osm |
When geocoding "Red Way 34"
Then all results contain
| object |
| N20 |
Scenario: when the housenumber is missing the street is still returned
@@ -71,12 +48,11 @@ Feature: Searching of simple objects
| osm | class | type | name | geometry |
| W1 | highway | residential | Wood Street | 1, 2 |
When importing
And sending search query "Wood Street"
Then results contain
| osm |
And geocoding "Wood Street"
Then all results contain
| object |
| W1 |
Scenario Outline: Special cased american states will be found
Given the grid
| 1 | | 2 |
@@ -90,15 +66,15 @@ Feature: Searching of simple objects
| N2 | place | town | <city> | 10 |
| N3 | place | city | <city> | country:ca |
When importing
And sending search query "<city>, <state>"
Then results contain
| osm |
And geocoding "<city>, <state>"
Then all results contain
| object |
| N2 |
When sending search query "<city>, <ref>"
When geocoding "<city>, <ref>"
| accept-language |
| en |
Then results contain
| osm |
Then all results contain
| object |
| N2 |
Examples:

Some files were not shown because too many files have changed in this diff Show More