forked from hans/Nominatim
Compare commits
265 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
627a487fcf | ||
|
|
b0d0d046e7 | ||
|
|
e09c42a78a | ||
|
|
28d7e11e4f | ||
|
|
a5d35d6e84 | ||
|
|
e8982068b6 | ||
|
|
6e3670bce5 | ||
|
|
e7b738fe35 | ||
|
|
1ee636461c | ||
|
|
c7e7d5e980 | ||
|
|
57bf76a0e1 | ||
|
|
c80c80200c | ||
|
|
5859f9a3cb | ||
|
|
a825414558 | ||
|
|
6577be3744 | ||
|
|
e936041713 | ||
|
|
a8b31090a1 | ||
|
|
14e708f366 | ||
|
|
3502ff837f | ||
|
|
84cfe5db53 | ||
|
|
26bd8304c6 | ||
|
|
ee3973f507 | ||
|
|
55fa051d3a | ||
|
|
4d38833170 | ||
|
|
e47646ddba | ||
|
|
7081e2ab66 | ||
|
|
e286536959 | ||
|
|
88374c2522 | ||
|
|
3fcfab9772 | ||
|
|
de8888ec58 | ||
|
|
3af8dc9580 | ||
|
|
71ae7f10f7 | ||
|
|
38304136d3 | ||
|
|
ff4b1758e1 | ||
|
|
a9b4894b07 | ||
|
|
28ba5fc8a4 | ||
|
|
c7a4c2c88a | ||
|
|
0617768ee2 | ||
|
|
62b60c70e6 | ||
|
|
0394f32438 | ||
|
|
569184a5b0 | ||
|
|
41c4b51be5 | ||
|
|
513bf485f2 | ||
|
|
263240919c | ||
|
|
d2b9493d72 | ||
|
|
60cea6c8bf | ||
|
|
9b7f0627ea | ||
|
|
2e9cebf025 | ||
|
|
48d4ea5542 | ||
|
|
9bdbbec0c8 | ||
|
|
8f0b3cb00f | ||
|
|
646fa53b44 | ||
|
|
7f10264fb6 | ||
|
|
7e0fdf5928 | ||
|
|
4a28d28c08 | ||
|
|
bfea79f1e4 | ||
|
|
87d78e87d2 | ||
|
|
c712c6e55e | ||
|
|
510054492c | ||
|
|
12db7a9af2 | ||
|
|
fcab682231 | ||
|
|
0981a6403d | ||
|
|
37bd4dfd83 | ||
|
|
057d77fcd4 | ||
|
|
713ea080d2 | ||
|
|
bf5063de9a | ||
|
|
13efedde34 | ||
|
|
c1beefd543 | ||
|
|
1d81c17335 | ||
|
|
b8b87716db | ||
|
|
1108bf7d86 | ||
|
|
62747c934d | ||
|
|
81b90c9f15 | ||
|
|
5a772a5770 | ||
|
|
3433ec306e | ||
|
|
03039ebfa8 | ||
|
|
271c23f459 | ||
|
|
0892eab1d3 | ||
|
|
d68996127d | ||
|
|
83270557a7 | ||
|
|
0e4f80bf1b | ||
|
|
b7abc8566e | ||
|
|
92f86de938 | ||
|
|
b17019a21c | ||
|
|
be58b929f2 | ||
|
|
25baaf530d | ||
|
|
320d488627 | ||
|
|
879f818d81 | ||
|
|
80a6751c51 | ||
|
|
05bef92f0f | ||
|
|
01d5ecb86b | ||
|
|
f50f46c1ce | ||
|
|
adae49b184 | ||
|
|
2bd7c75a35 | ||
|
|
9955155ce0 | ||
|
|
b0e0f7ae76 | ||
|
|
0315b87664 | ||
|
|
ac29f8bc91 | ||
|
|
ec6a427e0a | ||
|
|
09b59bd56a | ||
|
|
d0548caa76 | ||
|
|
96d2a331a1 | ||
|
|
1d7ed6737e | ||
|
|
97b6656182 | ||
|
|
f108eac527 | ||
|
|
144c3b3eb2 | ||
|
|
426e108b34 | ||
|
|
229ad01042 | ||
|
|
71e3d8b1de | ||
|
|
a5750a6ef6 | ||
|
|
d5e39260b3 | ||
|
|
0996fdfb55 | ||
|
|
07eb108a6d | ||
|
|
41249377d2 | ||
|
|
be091b17d9 | ||
|
|
398467b2f4 | ||
|
|
796f069d74 | ||
|
|
82b6245aaa | ||
|
|
f0e5cf53b8 | ||
|
|
7585d37818 | ||
|
|
ee54ebfe77 | ||
|
|
43ee4a8faf | ||
|
|
ab5bcd6d2f | ||
|
|
6b8c99a275 | ||
|
|
073221d321 | ||
|
|
dfb9579a73 | ||
|
|
26bc83c984 | ||
|
|
8139a079f8 | ||
|
|
0d341c256b | ||
|
|
5a17bfc9c9 | ||
|
|
1d981b3171 | ||
|
|
743ec43460 | ||
|
|
87ee3a6f58 | ||
|
|
10897787af | ||
|
|
4d073b0350 | ||
|
|
625018b654 | ||
|
|
5f2410119d | ||
|
|
424c0d0ebb | ||
|
|
71c9adfdba | ||
|
|
723bb4d0b9 | ||
|
|
cb76635da7 | ||
|
|
237e31b3ce | ||
|
|
d0741f21b1 | ||
|
|
a376608344 | ||
|
|
7a964efb3a | ||
|
|
1d0da944a6 | ||
|
|
d0880694eb | ||
|
|
1f689bdaae | ||
|
|
6a0361d0c6 | ||
|
|
f29c7bf910 | ||
|
|
2cc4c73b64 | ||
|
|
8841a328c8 | ||
|
|
c555b60b36 | ||
|
|
b30e2ab5dc | ||
|
|
115792d1db | ||
|
|
7075a5828e | ||
|
|
bd04ce62e0 | ||
|
|
3bb6ecdc3b | ||
|
|
a885e7309a | ||
|
|
6706a23fb5 | ||
|
|
c7faab4d7c | ||
|
|
080ba00956 | ||
|
|
2613ebfa01 | ||
|
|
53c526c01d | ||
|
|
dc371618ba | ||
|
|
59288417f0 | ||
|
|
1dd401b570 | ||
|
|
ee194ab369 | ||
|
|
b8113abd93 | ||
|
|
5182da9f45 | ||
|
|
14c25717ab | ||
|
|
3087ac1145 | ||
|
|
cdbde5b88d | ||
|
|
7a1ee99345 | ||
|
|
7a31a3d106 | ||
|
|
fe3dba3fd7 | ||
|
|
31c7f25541 | ||
|
|
45aef06d00 | ||
|
|
0eb71cdce8 | ||
|
|
45bc511955 | ||
|
|
dedf56b5f8 | ||
|
|
1e28f2478c | ||
|
|
3cdbcbff8f | ||
|
|
9a3bc9cc1e | ||
|
|
6bd905ff43 | ||
|
|
ae83ceab5e | ||
|
|
28ee59dd64 | ||
|
|
b4ef3d91ab | ||
|
|
efac4a135a | ||
|
|
984e91e519 | ||
|
|
49dc62201c | ||
|
|
4791fc341e | ||
|
|
4743a5e166 | ||
|
|
908c66ca84 | ||
|
|
62719f58c9 | ||
|
|
d183cd3c78 | ||
|
|
ac5a901daf | ||
|
|
aaee03d502 | ||
|
|
329948e685 | ||
|
|
f6a76ebcd5 | ||
|
|
1cb87164d9 | ||
|
|
64fa70ac0a | ||
|
|
2c42bda9ce | ||
|
|
1787892d32 | ||
|
|
7cc8f63125 | ||
|
|
2b6515e704 | ||
|
|
27bc8d4f7b | ||
|
|
90d531c640 | ||
|
|
2c24b9da5a | ||
|
|
d79a2bb17e | ||
|
|
4ac1bf2d47 | ||
|
|
8b4b647d77 | ||
|
|
6495717036 | ||
|
|
d23fa84471 | ||
|
|
937c8ba231 | ||
|
|
88beeb7916 | ||
|
|
34a27c7cab | ||
|
|
c04541b4da | ||
|
|
8d4a86635f | ||
|
|
2dc6ee7e1c | ||
|
|
ccab565a4a | ||
|
|
5c8fbe8186 | ||
|
|
7fd46dcee9 | ||
|
|
3ef4c4fbe7 | ||
|
|
47258f40ea | ||
|
|
123a3c0347 | ||
|
|
d60a2e693c | ||
|
|
db524a35c6 | ||
|
|
f23a860b33 | ||
|
|
df008d99f5 | ||
|
|
fd920fba9b | ||
|
|
d9cd8c6fff | ||
|
|
b303c785e9 | ||
|
|
36fa21d7ce | ||
|
|
f1a388700f | ||
|
|
2b66a7a39a | ||
|
|
4daa584b7d | ||
|
|
de9507bc63 | ||
|
|
2b48d09286 | ||
|
|
97741aaf1c | ||
|
|
146779340c | ||
|
|
d1f6fab68a | ||
|
|
c835918123 | ||
|
|
fd9345cda3 | ||
|
|
8a615ad969 | ||
|
|
eaaa4a7b31 | ||
|
|
c3e5654113 | ||
|
|
ff2a40b109 | ||
|
|
7e00a6e2ff | ||
|
|
8ee36fb78c | ||
|
|
c3483747eb | ||
|
|
3fda792929 | ||
|
|
ba57a9ba07 | ||
|
|
a489ac07cd | ||
|
|
3505417e3f | ||
|
|
29e78780e5 | ||
|
|
868caeaf1b | ||
|
|
7f72c7b5fc | ||
|
|
e428019170 | ||
|
|
e9407cd48d | ||
|
|
5042be1b72 | ||
|
|
315713ff9a | ||
|
|
7b3fb23216 | ||
|
|
1d6861667b | ||
|
|
ae1df044e2 |
11
.travis.yml
11
.travis.yml
@@ -16,16 +16,17 @@ install:
|
|||||||
before_script:
|
before_script:
|
||||||
- psql -U postgres -c "create extension postgis"
|
- psql -U postgres -c "create extension postgis"
|
||||||
script:
|
script:
|
||||||
- cd $TRAVIS_BUILD_DIR/build
|
- cd $TRAVIS_BUILD_DIR/
|
||||||
- if [[ $TEST_SUITE == "monaco" ]]; then wget --no-verbose --output-document=../data/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf; fi
|
- if [[ $TEST_SUITE == "tests" ]]; then phpcs --report-width=120 . ; fi
|
||||||
- if [[ $TEST_SUITE == "monaco" ]]; then ./utils/setup.php --osm-file ../data/monaco.osm.pbf --osm2pgsql-cache 1000 --all 2>&1 | grep -v 'ETA (seconds)'; fi
|
|
||||||
- if [[ $TEST_SUITE == "monaco" ]]; then ./utils/specialphrases.php --wiki-import | psql -d test_api_nominatim >/dev/null; fi
|
|
||||||
- cd $TRAVIS_BUILD_DIR/test/php
|
- cd $TRAVIS_BUILD_DIR/test/php
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then phpunit ./ ; fi
|
- if [[ $TEST_SUITE == "tests" ]]; then phpunit ./ ; fi
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then phpcs --report-width=120 */**.php ; fi
|
|
||||||
- cd $TRAVIS_BUILD_DIR/test/bdd
|
- cd $TRAVIS_BUILD_DIR/test/bdd
|
||||||
- # behave --format=progress3 api
|
- # behave --format=progress3 api
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then behave --format=progress3 db ; fi
|
- if [[ $TEST_SUITE == "tests" ]]; then behave --format=progress3 db ; fi
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then behave --format=progress3 osm2pgsql ; fi
|
- if [[ $TEST_SUITE == "tests" ]]; then behave --format=progress3 osm2pgsql ; fi
|
||||||
|
- cd $TRAVIS_BUILD_DIR/build
|
||||||
|
- if [[ $TEST_SUITE == "monaco" ]]; then wget --no-verbose --output-document=../data/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf; fi
|
||||||
|
- if [[ $TEST_SUITE == "monaco" ]]; then /usr/bin/env php ./utils/setup.php --osm-file ../data/monaco.osm.pbf --osm2pgsql-cache 1000 --all 2>&1 | grep -v 'ETA (seconds)'; fi
|
||||||
|
- if [[ $TEST_SUITE == "monaco" ]]; then /usr/bin/env php ./utils/specialphrases.php --wiki-import | psql -d test_api_nominatim >/dev/null; fi
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|||||||
17
AUTHORS
17
AUTHORS
@@ -3,16 +3,13 @@ Nominatim was written by:
|
|||||||
Brian Quinion
|
Brian Quinion
|
||||||
Sarah Hoffmann
|
Sarah Hoffmann
|
||||||
Marc Tobias Metten
|
Marc Tobias Metten
|
||||||
|
|
||||||
markigail
|
markigail
|
||||||
|
gemo1011
|
||||||
IrlJidel
|
IrlJidel
|
||||||
Frederik Ramm
|
Frederik Ramm
|
||||||
Michael Spreng
|
|
||||||
Daniele Forsi
|
and many more.
|
||||||
mfn
|
|
||||||
Grant Slater
|
For a full list of contributors see
|
||||||
Andree Klattenhoff
|
https://github.com/openstreetmap/Nominatim/graphs/contributors
|
||||||
appelflap
|
|
||||||
b3nn0
|
|
||||||
Spin0us
|
|
||||||
Kurt Roeckx
|
|
||||||
Rodolphe Quiédeville
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
|||||||
project(nominatim)
|
project(nominatim)
|
||||||
|
|
||||||
set(NOMINATIM_VERSION_MAJOR 3)
|
set(NOMINATIM_VERSION_MAJOR 3)
|
||||||
set(NOMINATIM_VERSION_MINOR 1)
|
set(NOMINATIM_VERSION_MINOR 2)
|
||||||
set(NOMINATIM_VERSION_PATCH 0)
|
set(NOMINATIM_VERSION_PATCH 0)
|
||||||
|
|
||||||
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
|
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
|
||||||
@@ -53,7 +53,7 @@ link_directories(${PostgreSQL_LIBRARY_DIRS})
|
|||||||
|
|
||||||
find_program(PYOSMIUM pyosmium-get-changes)
|
find_program(PYOSMIUM pyosmium-get-changes)
|
||||||
if (NOT EXISTS "${PYOSMIUM}")
|
if (NOT EXISTS "${PYOSMIUM}")
|
||||||
set(PYOSMIUM_PATH "/nonexistent")
|
set(PYOSMIUM_PATH "")
|
||||||
message(WARNING "pyosmium-get-changes not found (required for updates)")
|
message(WARNING "pyosmium-get-changes not found (required for updates)")
|
||||||
else()
|
else()
|
||||||
set(PYOSMIUM_PATH "${PYOSMIUM}")
|
set(PYOSMIUM_PATH "${PYOSMIUM}")
|
||||||
@@ -77,6 +77,16 @@ find_package(BZip2 REQUIRED)
|
|||||||
find_package(LibXml2 REQUIRED)
|
find_package(LibXml2 REQUIRED)
|
||||||
include_directories(${LIBXML2_INCLUDE_DIR})
|
include_directories(${LIBXML2_INCLUDE_DIR})
|
||||||
|
|
||||||
|
# Setting PHP binary variable as to command line (prevailing) or auto detect
|
||||||
|
if (NOT PHP_BIN)
|
||||||
|
find_program (PHP_BIN php)
|
||||||
|
endif()
|
||||||
|
# sanity check if PHP binary exists
|
||||||
|
if (NOT EXISTS ${PHP_BIN})
|
||||||
|
message(FATAL_ERROR "PHP binary not found. Install php or provide location with -DPHP_BIN=/path/php ")
|
||||||
|
endif()
|
||||||
|
message (STATUS "Using PHP binary " ${PHP_BIN})
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Setup settings and paths
|
# Setup settings and paths
|
||||||
@@ -97,6 +107,7 @@ set(CUSTOMFILES
|
|||||||
utils/country_languages.php
|
utils/country_languages.php
|
||||||
utils/imports.php
|
utils/imports.php
|
||||||
utils/importWikipedia.php
|
utils/importWikipedia.php
|
||||||
|
utils/export.php
|
||||||
utils/query.php
|
utils/query.php
|
||||||
utils/server_compare.php
|
utils/server_compare.php
|
||||||
utils/setup.php
|
utils/setup.php
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ Please add the following information to your issue:
|
|||||||
|
|
||||||
## Workflow for Pull Requests
|
## Workflow for Pull Requests
|
||||||
|
|
||||||
We love to get pull reuqests from you. We operate the "Fork & Pull" model
|
We love to get pull requests from you. We operate the "Fork & Pull" model
|
||||||
explained at
|
explained at
|
||||||
|
|
||||||
https://help.github.com/articles/using-pull-requests
|
https://help.github.com/articles/using-pull-requests
|
||||||
@@ -65,7 +65,7 @@ that duplicate work can be avoided.
|
|||||||
## Coding style
|
## Coding style
|
||||||
|
|
||||||
Nominatim historically hasn't followed a particular coding style but we
|
Nominatim historically hasn't followed a particular coding style but we
|
||||||
are in process of consolodating the style. The following rules apply:
|
are in process of consolidating the style. The following rules apply:
|
||||||
|
|
||||||
* Python code uses the official Python style
|
* Python code uses the official Python style
|
||||||
* indention
|
* indention
|
||||||
@@ -78,11 +78,13 @@ are in process of consolodating the style. The following rules apply:
|
|||||||
* no spaces after opening and before closing bracket
|
* no spaces after opening and before closing bracket
|
||||||
* leave out space between a function name and bracket
|
* leave out space between a function name and bracket
|
||||||
but add one between control statement(if, while, etc.) and bracket
|
but add one between control statement(if, while, etc.) and bracket
|
||||||
|
* for PHP variables use CamelCase with a prefixing letter indicating the type
|
||||||
|
(i - integer, f - float, a - array, s - string, o - object)
|
||||||
|
|
||||||
The coding style is enforced with PHPCS and can be tested with:
|
The coding style is enforced with PHPCS and can be tested with:
|
||||||
|
|
||||||
```
|
```
|
||||||
phpcs --report-width=120 --colors */**.php
|
phpcs --report-width=120 --colors .
|
||||||
```
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|||||||
26
ChangeLog
26
ChangeLog
@@ -1,3 +1,29 @@
|
|||||||
|
3.2.0
|
||||||
|
|
||||||
|
* complete rewrite of reverse search algorithm
|
||||||
|
* add new geojson and geocodejson output formats
|
||||||
|
* add simple export script to exprot addresses to CSV
|
||||||
|
* remove is_in terms from address computation
|
||||||
|
* remove unused search_name_country tables
|
||||||
|
* various smaller fixes to query parsing
|
||||||
|
* convert Tokens and token types to class types
|
||||||
|
* correctly handle update when boundary object type is changed
|
||||||
|
* improve debug output for /search endpoint
|
||||||
|
* update to latest osm2pgsql and leaflet.js
|
||||||
|
* overhaul of /details endpoint:
|
||||||
|
* new class parameter when using osmtype/osmid parameters
|
||||||
|
* permalink to instance-independent osmtype/osmid parameter format
|
||||||
|
* new json output format
|
||||||
|
* update CentOS vagrant machine to use SELinux
|
||||||
|
* add vagrant scripts for Ubuntu 18.04
|
||||||
|
* fix build process for BSD
|
||||||
|
* enable running the database on a different host than the setup scripts
|
||||||
|
* allow to configure use of custom PHP binaries (PHP_BIN)
|
||||||
|
* extensive coding style improvements to PHP code
|
||||||
|
* more PHP unit tests for new classes
|
||||||
|
* increase coverage for API tests
|
||||||
|
* add documentation for API
|
||||||
|
|
||||||
3.1.0
|
3.1.0
|
||||||
|
|
||||||
* rework postcode handling and introduce location_postcode table
|
* rework postcode handling and introduce location_postcode table
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -7,25 +7,24 @@ Nominatim (from the Latin, 'by name') is a tool to search OpenStreetMap data
|
|||||||
by name and address (geocoding) and to generate synthetic addresses of
|
by name and address (geocoding) and to generate synthetic addresses of
|
||||||
OSM points (reverse geocoding). An instance with up-to-date data can be found
|
OSM points (reverse geocoding). An instance with up-to-date data can be found
|
||||||
at https://nominatim.openstreetmap.org. Nominatim is also used as one of the
|
at https://nominatim.openstreetmap.org. Nominatim is also used as one of the
|
||||||
sources for the Search box on the OpenStreetMap home page and powers the search
|
sources for the Search box on the OpenStreetMap home page.
|
||||||
on the MapQuest Open Initiative websites.
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
=============
|
=============
|
||||||
|
|
||||||
More information about Nominatim, including usage and installation instructions,
|
The documentation of the latest development version is in the
|
||||||
can be found in the docs/ subdirectory and in the OSM wiki at:
|
`docs/` subdirectory. A HTML version can be found at
|
||||||
|
https://nominatim.org/release-docs/develop/ .
|
||||||
https://nominatim.org
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
The latest stable release can be downloaded from https://nominatim.org.
|
The latest stable release can be downloaded from https://nominatim.org.
|
||||||
There you can also find [installation instructions for the release](https://nominatim.org/release-docs/latest/Installation).
|
There you can also find [installation instructions for the release](https://nominatim.org/release-docs/latest/admin/Installation).
|
||||||
|
|
||||||
Detailed installation instructions for the development version can be
|
Detailed installation instructions for the development version can be
|
||||||
found in the `/docs` directory, see [docs/Installation.md](docs/Installation.md).
|
found at [nominatim.org](https://nominatim.org/release-docs/develop/admin/Installation)
|
||||||
|
as well.
|
||||||
|
|
||||||
A quick summary of the necessary steps:
|
A quick summary of the necessary steps:
|
||||||
|
|
||||||
|
|||||||
103
VAGRANT.md
103
VAGRANT.md
@@ -1,11 +1,11 @@
|
|||||||
# Install Nominatim in a virtual machine for development and testing
|
# Install Nominatim in a virtual machine for development and testing
|
||||||
|
|
||||||
This document describes how you can install Nominatim inside a Ubuntu 14
|
This document describes how you can install Nominatim inside a Ubuntu 16
|
||||||
virtual machine on your desktop/laptop (host machine). The goal is to give
|
virtual machine on your desktop/laptop (host machine). The goal is to give
|
||||||
you a development environment to easily edit code and run the test suite
|
you a development environment to easily edit code and run the test suite
|
||||||
without affecting the rest of your system.
|
without affecting the rest of your system.
|
||||||
|
|
||||||
The installation can run largely unsupervised. You should expect 1-2h from
|
The installation can run largely unsupervised. You should expect 1h from
|
||||||
start to finish depending on how fast your computer and download speed
|
start to finish depending on how fast your computer and download speed
|
||||||
is.
|
is.
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ is.
|
|||||||
|
|
||||||
git clone --recursive https://github.com/openstreetmap/Nominatim.git
|
git clone --recursive https://github.com/openstreetmap/Nominatim.git
|
||||||
|
|
||||||
If you haven't used `--recursive`, then you can load the submodules using
|
If you forgot `--recursive`, it you can later load the submodules using
|
||||||
|
|
||||||
git submodule init
|
git submodule init
|
||||||
git submodule update
|
git submodule update
|
||||||
@@ -37,18 +37,14 @@ is.
|
|||||||
vagrant ssh ubuntu
|
vagrant ssh ubuntu
|
||||||
|
|
||||||
3. Import a small country (Monaco)
|
3. Import a small country (Monaco)
|
||||||
|
|
||||||
You need to give the virtual machine more memory (2GB) for an import,
|
|
||||||
see `Vagrantfile`. Otherwise 1GB is enough.
|
|
||||||
|
|
||||||
See the FAQ how to skip this step and point Nominatim to an existing database.
|
See the FAQ how to skip this step and point Nominatim to an existing database.
|
||||||
|
|
||||||
```
|
```
|
||||||
# inside the virtual machine:
|
# inside the virtual machine:
|
||||||
mkdir data
|
|
||||||
cd build
|
cd build
|
||||||
wget --no-verbose --output-document=../data/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf
|
wget --no-verbose --output-document=/tmp/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf
|
||||||
./utils/setup.php --osm-file ../data/monaco.osm.pbf --osm2pgsql-cache 1000 --all 2>&1 | tee monaco.$$.log
|
./utils/setup.php --osm-file /tmp/monaco.osm.pbf --osm2pgsql-cache 1000 --all 2>&1 | tee monaco.$$.log
|
||||||
```
|
```
|
||||||
|
|
||||||
To repeat an import you'd need to delete the database first
|
To repeat an import you'd need to delete the database first
|
||||||
@@ -65,47 +61,64 @@ see Nominatim in action on [locahost:8089](http://localhost:8089/nominatim/).
|
|||||||
You edit code on your host machine in any editor you like. There is no need to
|
You edit code on your host machine in any editor you like. There is no need to
|
||||||
restart any software: just refresh your browser window.
|
restart any software: just refresh your browser window.
|
||||||
|
|
||||||
|
Note that the webserver uses files from the /build directory. If you change
|
||||||
|
files in Nominatim/website or Nominatim/utils for example you first need to
|
||||||
|
copy them into the /build directory by running the `cmake` step from the
|
||||||
|
installation.
|
||||||
|
|
||||||
PHP errors are written to `/var/log/apache2/error.log`.
|
PHP errors are written to `/var/log/apache2/error.log`.
|
||||||
|
|
||||||
With `echo` and `var_dump()` you write into the output (HTML/XML/JSON) when
|
With `echo` and `var_dump()` you write into the output (HTML/XML/JSON) when
|
||||||
you either add `&debug=1` to the URL (preferred) or set
|
you either add `&debug=1` to the URL (preferred) or set
|
||||||
`@define('CONST_Debug', true);` in `settings/local.php`.
|
`@define('CONST_Debug', true);` in `settings/local.php`.
|
||||||
|
|
||||||
|
In the Python BDD test you can use `logger.info()` for temporary debug
|
||||||
|
statements.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Running functional tests
|
|
||||||
|
|
||||||
Tests in `/features/db` and `/features/osm2pgsql` have to pass 100%. Other
|
|
||||||
tests might require full planet-wide data. Sadly even if you have your own
|
|
||||||
planet-wide data there will be enough differences to the openstreetmap.org
|
|
||||||
installation to cause false positives in the other tests (see FAQ).
|
|
||||||
|
|
||||||
To run the full test suite
|
|
||||||
|
|
||||||
cd ~/Nominatim/tests
|
|
||||||
NOMINATIM_SERVER=http://localhost:8089/nominatim lettuce features
|
|
||||||
|
|
||||||
To run a single file
|
|
||||||
|
|
||||||
NOMINATIM_SERVER=http://localhost:8089/nominatim lettuce features/api/reverse.feature
|
|
||||||
|
|
||||||
To run specific tests you can add tags just before the `Scenario line`, e.g.
|
|
||||||
|
|
||||||
@bug-34
|
|
||||||
Scenario: address lookup for non-existing or invalid node, way, relation
|
|
||||||
|
|
||||||
and then
|
|
||||||
|
|
||||||
NOMINATIM_SERVER=http://localhost:8089/nominatim lettuce -t bug-34
|
|
||||||
|
|
||||||
|
|
||||||
## Running unit tests
|
## Running unit tests
|
||||||
|
|
||||||
cd ~/Nominatim/tests/php
|
cd ~/Nominatim/tests/php
|
||||||
phpunit ./
|
phpunit ./
|
||||||
|
|
||||||
|
|
||||||
|
## Running PHP code style tests
|
||||||
|
|
||||||
|
cd ~/Nominatim
|
||||||
|
phpcs --colors .
|
||||||
|
|
||||||
|
|
||||||
|
## Running functional tests
|
||||||
|
|
||||||
|
Tests in `test/bdd/db` and `test/bdd/osm2pgsql` have to pass 100%. Other
|
||||||
|
tests might require full planet-wide data. Sadly even if you have your own
|
||||||
|
planet-wide data there will be enough differences to the openstreetmap.org
|
||||||
|
installation to cause false positives in the other tests (see FAQ).
|
||||||
|
|
||||||
|
To run the full test suite
|
||||||
|
|
||||||
|
cd ~/Nominatim/test/bdd
|
||||||
|
behave -DBUILDDIR=/home/vagrant/build/ db osm2pgsql
|
||||||
|
|
||||||
|
To run a single file
|
||||||
|
|
||||||
|
behave -DBUILDDIR=/home/vagrant/build/ api/lookup/simple.feature
|
||||||
|
|
||||||
|
Or a single test by line number
|
||||||
|
|
||||||
|
behave -DBUILDDIR=/home/vagrant/build/ api/lookup/simple.feature:34
|
||||||
|
|
||||||
|
To run specific groups of tests you can add tags just before the `Scenario line`, e.g.
|
||||||
|
|
||||||
|
@bug-34
|
||||||
|
Scenario: address lookup for non-existing or invalid node, way, relation
|
||||||
|
|
||||||
|
and then
|
||||||
|
|
||||||
|
behave -DBUILDDIR=/home/vagrant/build/ --tags @bug-34
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -130,17 +143,18 @@ bug fixes) get added since those usually only get applied to new/changed data.
|
|||||||
Also this document skips the optional Wikipedia data import which affects ranking
|
Also this document skips the optional Wikipedia data import which affects ranking
|
||||||
of search results. See [Nominatim installation](http://nominatim.org/release-docs/latest/Installation) for details.
|
of search results. See [Nominatim installation](http://nominatim.org/release-docs/latest/Installation) for details.
|
||||||
|
|
||||||
##### Why Ubuntu and CentOS, can I test CentOS/CoreOS/FreeBSD?
|
##### Why Ubuntu? Can I test CentOS/Fedora/CoreOS/FreeBSD?
|
||||||
|
|
||||||
There is a Vagrant script for CentOS available. Simply start your box
|
There is a Vagrant script for CentOS available, but the Nominatim directory
|
||||||
with `vagrant up centos` and then log in with `vagrant ssh centos`.
|
isn't symlinked/mounted to the host which makes development trickier. We used
|
||||||
In general Nominatim will also run in the other environments. The installation steps
|
it mainly for debugging installation with SELinux.
|
||||||
|
|
||||||
|
In general Nominatim will run in the other environments. The installation steps
|
||||||
are slightly different, e.g. the name of the package manager, Apache2 package
|
are slightly different, e.g. the name of the package manager, Apache2 package
|
||||||
name, location of files. We chose Ubuntu because that is closest to the
|
name, location of files. We chose Ubuntu because that is closest to the
|
||||||
nominatim.openstreetmap.org production environment.
|
nominatim.openstreetmap.org production environment.
|
||||||
|
|
||||||
You can configure/download other Vagrant boxes from [vagrantbox.es](http://www.vagrantbox.es/).
|
You can configure/download other Vagrant boxes from [https://app.vagrantup.com/boxes/search](https://app.vagrantup.com/boxes/search).
|
||||||
|
|
||||||
|
|
||||||
##### How can I connect to an existing database?
|
##### How can I connect to an existing database?
|
||||||
|
|
||||||
@@ -148,7 +162,7 @@ Let's say you have a Postgres database named `nominatim_it` on server `your-serv
|
|||||||
|
|
||||||
pgsql://postgres@your-server.com:5432/nominatim_it
|
pgsql://postgres@your-server.com:5432/nominatim_it
|
||||||
|
|
||||||
No data import necessary, no restarting necessary.
|
No data import necessary or restarting necessary.
|
||||||
|
|
||||||
If the Postgres installation is behind a firewall, you can try
|
If the Postgres installation is behind a firewall, you can try
|
||||||
|
|
||||||
@@ -157,14 +171,15 @@ If the Postgres installation is behind a firewall, you can try
|
|||||||
inside the virtual machine. It will map the port to `localhost:9999` and then
|
inside the virtual machine. It will map the port to `localhost:9999` and then
|
||||||
you edit `settings/local.php` with
|
you edit `settings/local.php` with
|
||||||
|
|
||||||
pgsql://postgres@localhost:9999/nominatim_it
|
@define('CONST_Database_DSN', 'pgsql://postgres@localhost:9999/nominatim_it');
|
||||||
|
|
||||||
To access postgres directly remember to specify the hostname, e.g. `psql --host localhost --port 9999 nominatim_it`
|
To access postgres directly remember to specify the hostname, e.g. `psql --host localhost --port 9999 nominatim_it`
|
||||||
|
|
||||||
|
|
||||||
##### My computer is slow and the import takes too long. Can I start the virtual machine "in the cloud"?
|
##### My computer is slow and the import takes too long. Can I start the virtual machine "in the cloud"?
|
||||||
|
|
||||||
Yes. It's possible to start the virtual machine on [Amazon AWS (plugin)](https://github.com/mitchellh/vagrant-aws) or [DigitalOcean (plugin)](https://github.com/smdahlen/vagrant-digitalocean).
|
Yes. It's possible to start the virtual machine on [Amazon AWS (plugin)](https://github.com/mitchellh/vagrant-aws)
|
||||||
|
or [DigitalOcean (plugin)](https://github.com/smdahlen/vagrant-digitalocean).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
40
Vagrantfile
vendored
40
Vagrantfile
vendored
@@ -15,6 +15,15 @@ Vagrant.configure("2") do |config|
|
|||||||
end
|
end
|
||||||
|
|
||||||
config.vm.define "ubuntu", primary: true do |sub|
|
config.vm.define "ubuntu", primary: true do |sub|
|
||||||
|
sub.vm.box = "bento/ubuntu-18.04"
|
||||||
|
sub.vm.provision :shell do |s|
|
||||||
|
s.path = "vagrant/Install-on-Ubuntu-18.sh"
|
||||||
|
s.privileged = false
|
||||||
|
s.args = [checkout]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.define "ubuntu16" do |sub|
|
||||||
sub.vm.box = "bento/ubuntu-16.04"
|
sub.vm.box = "bento/ubuntu-16.04"
|
||||||
sub.vm.provision :shell do |s|
|
sub.vm.provision :shell do |s|
|
||||||
s.path = "vagrant/Install-on-Ubuntu-16.sh"
|
s.path = "vagrant/Install-on-Ubuntu-16.sh"
|
||||||
@@ -32,38 +41,21 @@ Vagrant.configure("2") do |config|
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
config.vm.define "centos" do |sub|
|
config.vm.define "centos" do |sub|
|
||||||
sub.vm.box = "bento/centos-7.2"
|
sub.vm.box = "centos/7"
|
||||||
sub.vm.provision :shell do |s|
|
sub.vm.provision :shell do |s|
|
||||||
s.path = "vagrant/Install-on-Centos-7.sh"
|
s.path = "vagrant/Install-on-Centos-7.sh"
|
||||||
s.privileged = false
|
s.privileged = false
|
||||||
s.args = [checkout]
|
s.args = "yes"
|
||||||
end
|
end
|
||||||
|
sub.vm.synced_folder ".", "/home/vagrant/Nominatim", disabled: true
|
||||||
|
sub.vm.synced_folder ".", "/vagrant", disabled: true
|
||||||
end
|
end
|
||||||
|
|
||||||
# configure shared package cache if possible
|
|
||||||
#if Vagrant.has_plugin?("vagrant-cachier")
|
|
||||||
# config.cache.enable :apt
|
|
||||||
# config.cache.scope = :box
|
|
||||||
#end
|
|
||||||
|
|
||||||
|
|
||||||
config.vm.provider "virtualbox" do |vb|
|
config.vm.provider "virtualbox" do |vb|
|
||||||
vb.gui = false
|
vb.gui = false
|
||||||
vb.customize ["modifyvm", :id, "--memory", "2048"]
|
vb.memory = 2048
|
||||||
|
vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate//vagrant","0"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# config.vm.provider :digital_ocean do |provider, override|
|
|
||||||
# override.ssh.private_key_path = '~/.ssh/id_rsa'
|
|
||||||
# override.vm.box = 'digital_ocean'
|
|
||||||
# override.vm.box_url = "https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box"
|
|
||||||
|
|
||||||
# provider.token = ''
|
|
||||||
# # provider.token = 'YOUR TOKEN'
|
|
||||||
# provider.image = 'ubuntu-14-04-x64'
|
|
||||||
# provider.region = 'nyc2'
|
|
||||||
# provider.size = '512mb'
|
|
||||||
# end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -18,12 +18,13 @@ SET default_with_oids = false;
|
|||||||
-- Name: word_frequencies; Type: TABLE; Schema: public; Owner: -; Tablespace:
|
-- Name: word_frequencies; Type: TABLE; Schema: public; Owner: -; Tablespace:
|
||||||
--
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS word_frequencies;
|
||||||
|
|
||||||
CREATE TABLE word_frequencies (
|
CREATE TABLE word_frequencies (
|
||||||
word_token text,
|
word_token text,
|
||||||
count bigint
|
count bigint
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: word_frequencies; Type: TABLE DATA; Schema: public; Owner: -
|
-- Data for Name: word_frequencies; Type: TABLE DATA; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/appendix)
|
|||||||
|
|
||||||
ADD_CUSTOM_TARGET(doc
|
ADD_CUSTOM_TARGET(doc
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/admin ${CMAKE_CURRENT_BINARY_DIR}/admin
|
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/admin ${CMAKE_CURRENT_BINARY_DIR}/admin
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/develop ${CMAKE_CURRENT_BINARY_DIR}/develop
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/api ${CMAKE_CURRENT_BINARY_DIR}/api
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/index.md ${CMAKE_CURRENT_BINARY_DIR}/index.md
|
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/index.md ${CMAKE_CURRENT_BINARY_DIR}/index.md
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/extra.css ${CMAKE_CURRENT_BINARY_DIR}/extra.css
|
||||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Centos-7.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Centos-7.md
|
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Centos-7.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Centos-7.md
|
||||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Ubuntu-16.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Ubuntu-16.md
|
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Ubuntu-16.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Ubuntu-16.md
|
||||||
|
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Ubuntu-18.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Ubuntu-18.md
|
||||||
COMMAND mkdocs build -d ${CMAKE_CURRENT_BINARY_DIR}/../site-html -f ${CMAKE_CURRENT_BINARY_DIR}/../mkdocs.yml
|
COMMAND mkdocs build -d ${CMAKE_CURRENT_BINARY_DIR}/../site-html -f ${CMAKE_CURRENT_BINARY_DIR}/../mkdocs.yml
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,10 @@
|
|||||||
# Running Your Own Instance
|
# Troubleshooting Nominatim Installations
|
||||||
|
|
||||||
### Can I import only a few countries and also keep them up to date?
|
## Installation Issues
|
||||||
|
|
||||||
You should use the extracts and updates from https://download.geofabrik.de.
|
### Can a stopped/killed import process be resumed?
|
||||||
For the intial import, download the countries you need and merge them.
|
|
||||||
See [OSM Help](https://help.openstreetmap.org/questions/48843/merging-two-or-more-geographical-areas-to-import-two-or-more-osm-files-in-nominatim)
|
|
||||||
for examples how to do that. Use the resulting single osm file when
|
|
||||||
running `setup.php`.
|
|
||||||
|
|
||||||
For updates you need to download the change files for each country
|
"I accidentally killed the import process after it has been running for many hours. Can it be resumed?"
|
||||||
once per day and apply them **separately** using
|
|
||||||
|
|
||||||
./utils/update.php --import-diff <filename> --index
|
|
||||||
|
|
||||||
See [this issue](https://github.com/openstreetmap/Nominatim/issues/60#issuecomment-18679446)
|
|
||||||
for a script that runs the updates using osmosis.
|
|
||||||
|
|
||||||
### My website shows: `XML Parsing Error: XML or text declaration not at start of entity Location</code>.`
|
|
||||||
|
|
||||||
Make sure there are no spaces at the beginning of your `settings/local.php` file.
|
|
||||||
|
|
||||||
|
|
||||||
# Installation
|
|
||||||
|
|
||||||
### I accidentally killed the import process after it has been running for many hours. Can it be resumed?
|
|
||||||
|
|
||||||
It is possible if the import already got to the indexing stage.
|
It is possible if the import already got to the indexing stage.
|
||||||
Check the last line of output that was logged before the process
|
Check the last line of output that was logged before the process
|
||||||
@@ -41,11 +22,12 @@ then you can resume with the following command:
|
|||||||
If the reported rank is 26 or higher, you can also safely add `--index-noanalyse`.
|
If the reported rank is 26 or higher, you can also safely add `--index-noanalyse`.
|
||||||
|
|
||||||
|
|
||||||
### When running the setup.php script I get a warning:
|
### PHP "open_basedir restriction in effect" warnings
|
||||||
|
|
||||||
`PHP Warning: file_get_contents(): open_basedir restriction in effect.`
|
`PHP Warning: file_get_contents(): open_basedir restriction in effect.`
|
||||||
|
|
||||||
You need to adjust the [open_basedir](http://www.php.net/manual/en/ini.core.php#ini.open-basedir) setting
|
You need to adjust the [open_basedir](http://www.php.net/manual/en/ini.core.php#ini.open-basedir) setting
|
||||||
in your PHP configuration (php.ini file). By default this setting may look like this:
|
in your PHP configuration (`php.ini file`). By default this setting may look like this:
|
||||||
|
|
||||||
open_basedir = /srv/http/:/home/:/tmp/:/usr/share/pear/
|
open_basedir = /srv/http/:/home/:/tmp/:/usr/share/pear/
|
||||||
|
|
||||||
@@ -54,7 +36,9 @@ dding ";" at the beginning of the line. Don't forget to enable this setting agai
|
|||||||
once you are done with the PHP command line operations.
|
once you are done with the PHP command line operations.
|
||||||
|
|
||||||
|
|
||||||
### The Apache log contains lots of PHP warnings like this:
|
### PHP timzeone warnings
|
||||||
|
|
||||||
|
The Apache log may contain lots of PHP warnings like this:
|
||||||
`PHP Warning: date_default_timezone_set() function.`
|
`PHP Warning: date_default_timezone_set() function.`
|
||||||
|
|
||||||
You should set the default time zone as instructed in the warning in
|
You should set the default time zone as instructed in the warning in
|
||||||
@@ -71,28 +55,32 @@ Or
|
|||||||
echo "date.timezone = 'America/Denver'" > /etc/php.d/timezone.ini
|
echo "date.timezone = 'America/Denver'" > /etc/php.d/timezone.ini
|
||||||
```
|
```
|
||||||
|
|
||||||
### When running the import I get a version mismatch:
|
### nominatim.so version mismatch
|
||||||
`COPY_END for place failed: ERROR: incompatible library "/opt/Nominatim/module/nominatim.so": version mismatch`
|
|
||||||
|
When running the import you may get a version mismatch:
|
||||||
|
`COPY_END for place failed: ERROR: incompatible library "/srv/Nominatim/nominatim/build/module/nominatim.so": version mismatch`
|
||||||
|
|
||||||
pg_config seems to use bad includes sometimes when multiple versions
|
pg_config seems to use bad includes sometimes when multiple versions
|
||||||
of PostgreSQL are available in the system. Make sure you remove the
|
of PostgreSQL are available in the system. Make sure you remove the
|
||||||
server development libraries (`postgresql-server-dev-9.1` on Ubuntu)
|
server development libraries (`postgresql-server-dev-9.5` on Ubuntu)
|
||||||
and recompile (`cmake .. && make`).
|
and recompile (`cmake .. && make`).
|
||||||
|
|
||||||
|
|
||||||
### I see the error: `function transliteration(text) does not exist`
|
### I see the error: "function transliteration(text) does not exist"
|
||||||
|
|
||||||
Reinstall the nominatim functions with `setup.php --create--functions`
|
Reinstall the nominatim functions with `setup.php --create--functions`
|
||||||
and check for any errors, e.g. a missing `nominatim.so` file.
|
and check for any errors, e.g. a missing `nominatim.so` file.
|
||||||
|
|
||||||
|
|
||||||
### The website shows: `Could not get word tokens`
|
### The website shows: "Could not get word tokens"
|
||||||
|
|
||||||
The server cannot access your database. Add `&debug=1` to your URL
|
The server cannot access your database. Add `&debug=1` to your URL
|
||||||
to get the full error message.
|
to get the full error message.
|
||||||
|
|
||||||
|
|
||||||
### On CentOS the website shows `could not connect to server: No such file or directory`
|
### On CentOS the website shows "Could not connect to server"
|
||||||
|
|
||||||
|
`could not connect to server: No such file or directory`
|
||||||
|
|
||||||
On CentOS v7 the PostgreSQL server is started with `systemd`.
|
On CentOS v7 the PostgreSQL server is started with `systemd`.
|
||||||
Check if `/usr/lib/systemd/system/httpd.service` contains a line `PrivateTmp=true`.
|
Check if `/usr/lib/systemd/system/httpd.service` contains a line `PrivateTmp=true`.
|
||||||
@@ -105,22 +93,93 @@ However, you can solve this the quick and dirty way by commenting out that line
|
|||||||
sudo systemctl restart httpd
|
sudo systemctl restart httpd
|
||||||
|
|
||||||
|
|
||||||
### Setup.php fails with the message: `DB Error: extension not found`
|
### Website reports "DB Error: insufficient permissions"
|
||||||
|
|
||||||
|
The user the webserver, e.g. Apache, runs under needs to have access to the Nominatim database. You can find the user like [this](https://serverfault.com/questions/125865/finding-out-what-user-apache-is-running-as), for default Ubuntu operating system for example it's `www-data`.
|
||||||
|
|
||||||
|
1. Repeat the `createuser` step of the installation instructions.
|
||||||
|
|
||||||
|
2. Give the user permission to existing tables
|
||||||
|
|
||||||
|
```
|
||||||
|
GRANT usage ON SCHEMA public TO "www-data";
|
||||||
|
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "www-data";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Website reports "Could not load library "nominatim.so"
|
||||||
|
|
||||||
|
Example error message
|
||||||
|
|
||||||
|
```
|
||||||
|
SELECT make_standard_name('3039 E MEADOWLARK LN') [nativecode=ERROR: could not
|
||||||
|
load library "/srv/nominatim/Nominatim-3.1.0/build/module/nominatim.so":
|
||||||
|
/srv/nominatim/Nominatim-3.1.0/build/module/nominatim.so: cannot open shared
|
||||||
|
object file: Permission denied
|
||||||
|
CONTEXT: PL/pgSQL function make_standard_name(text) line 5 at assignment]
|
||||||
|
```
|
||||||
|
|
||||||
|
The Postgresql database, i.e. user postgres, needs to have access to that file.
|
||||||
|
|
||||||
|
The permission need to be read & executable by everybody, e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
-rwxr-xr-x 1 nominatim nominatim 297984 build/module/nominatim.so
|
||||||
|
```
|
||||||
|
|
||||||
|
Try `chmod a+r nominatim.so; chmod a+x nominatim.so`.
|
||||||
|
|
||||||
|
When running SELinux, make sure that the
|
||||||
|
[context is set up correctly](../appendix/Install-on-Centos-7/#adding-selinux-security-settings).
|
||||||
|
|
||||||
|
### Setup.php fails with "DB Error: extension not found"
|
||||||
|
|
||||||
Make sure you have the Postgres extensions hstore and postgis installed.
|
Make sure you have the Postgres extensions hstore and postgis installed.
|
||||||
See the installation instruction for a full list of required packages.
|
See the installation instruction for a full list of required packages.
|
||||||
|
|
||||||
### When running the setup.php script I get a error:
|
|
||||||
`Cannot redeclare getDB() (previously declared in /your/path/Nominatim/lib/db.php:4)`
|
### Setup.php reports "Cannot redeclare getDB()"
|
||||||
|
|
||||||
|
`Cannot redeclare getDB() (previously declared in /your/path/Nominatim/lib/db.php:4)`
|
||||||
|
|
||||||
The message is a bit misleading as PHP needs to load the file `DB.php` and
|
The message is a bit misleading as PHP needs to load the file `DB.php` and
|
||||||
instead re-loads Nominatim's `db.php`. To solve this make sure you
|
instead re-loads Nominatim's `db.php`. To solve this make sure you
|
||||||
have the [http://pear.php.net/package/DB/ Pear module 'DB'] installed.
|
have the [Pear module 'DB'](http://pear.php.net/package/DB/) installed.
|
||||||
|
|
||||||
sudo pear install DB
|
sudo pear install DB
|
||||||
|
|
||||||
### I forgot to delete the flatnodes file before starting an import.
|
### I forgot to delete the flatnodes file before starting an import.
|
||||||
|
|
||||||
That's fine. For each import the flatnodes file get overwritten.
|
That's fine. For each import the flatnodes file get overwritten.
|
||||||
See https://help.openstreetmap.org/questions/52419/nominatim-flatnode-storage
|
See [https://help.openstreetmap.org/questions/52419/nominatim-flatnode-storage]()
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
|
|
||||||
|
## Running your own instance
|
||||||
|
|
||||||
|
### Can I import multiple countries and keep them up to date?
|
||||||
|
|
||||||
|
You should use the extracts and updates from https://download.geofabrik.de.
|
||||||
|
For the initial import, download the countries you need and merge them.
|
||||||
|
See [OSM Help](https://help.openstreetmap.org/questions/48843/merging-two-or-more-geographical-areas-to-import-two-or-more-osm-files-in-nominatim)
|
||||||
|
for examples how to do that. Use the resulting single osm file when
|
||||||
|
running `setup.php`.
|
||||||
|
|
||||||
|
For updates you need to download the change files for each country
|
||||||
|
once per day and apply them **separately** using
|
||||||
|
|
||||||
|
./utils/update.php --import-diff <filename> --index
|
||||||
|
|
||||||
|
See [this issue](https://github.com/openstreetmap/Nominatim/issues/60#issuecomment-18679446)
|
||||||
|
for a script that runs the updates using osmosis.
|
||||||
|
|
||||||
|
### Can I import negative OSM ids into Nominatim?
|
||||||
|
|
||||||
|
See [this question of Stackoverflow](https://help.openstreetmap.org/questions/64662/nominatim-flatnode-with-negative-id).
|
||||||
|
|
||||||
|
### Missing XML or text declaration
|
||||||
|
|
||||||
|
The website might show: `XML Parsing Error: XML or text declaration not at start of entity Location.`
|
||||||
|
|
||||||
|
Make sure there are no spaces at the beginning of your `settings/local.php` file.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
# Importing and Updating the Database
|
||||||
|
|
||||||
The following instructions explain how to create a Nominatim database
|
The following instructions explain how to create a Nominatim database
|
||||||
from an OSM planet file and how to keep the database up to date. It
|
from an OSM planet file and how to keep the database up to date. It
|
||||||
is assumed that you have already successfully installed the Nominatim
|
is assumed that you have already successfully installed the Nominatim
|
||||||
software itself, if not return to the [installation page](Installation.md).
|
software itself, if not return to the [installation page](Installation.md).
|
||||||
|
|
||||||
# Configuration setup in settings/local.php
|
## Configuration setup in settings/local.php
|
||||||
|
|
||||||
The Nominatim server can be customized via the file `settings/local.php`
|
The Nominatim server can be customized via the file `settings/local.php`
|
||||||
in the build directory. Note that this is a PHP file, so it must always
|
in the build directory. Note that this is a PHP file, so it must always
|
||||||
@@ -16,7 +18,7 @@ without any leading spaces.
|
|||||||
There are lots of configuration settings you can tweak. Have a look
|
There are lots of configuration settings you can tweak. Have a look
|
||||||
at `settings/default.php` for a full list. Most should have a sensible default.
|
at `settings/default.php` for a full list. Most should have a sensible default.
|
||||||
|
|
||||||
### Flatnode files
|
#### Flatnode files
|
||||||
|
|
||||||
If you plan to import a large dataset (e.g. Europe, North America, planet),
|
If you plan to import a large dataset (e.g. Europe, North America, planet),
|
||||||
you should also enable flatnode storage of node locations. With this
|
you should also enable flatnode storage of node locations. With this
|
||||||
@@ -29,9 +31,9 @@ Add to your `settings/local.php`:
|
|||||||
Replace the second part with a suitable path on your system and make sure
|
Replace the second part with a suitable path on your system and make sure
|
||||||
the directory exists. There should be at least 40GB of free space.
|
the directory exists. There should be at least 40GB of free space.
|
||||||
|
|
||||||
# Downloading additional data
|
## Downloading additional data
|
||||||
|
|
||||||
## Wikipedia rankings
|
### Wikipedia rankings
|
||||||
|
|
||||||
Wikipedia can be used as an optional auxiliary data source to help indicate
|
Wikipedia can be used as an optional auxiliary data source to help indicate
|
||||||
the importance of osm features. Nominatim will work without this information
|
the importance of osm features. Nominatim will work without this information
|
||||||
@@ -49,7 +51,7 @@ size of nominatim. They also increase the install time by an hour or so.
|
|||||||
the initial import of the data if you want the rankings applied to the
|
the initial import of the data if you want the rankings applied to the
|
||||||
loaded data.
|
loaded data.
|
||||||
|
|
||||||
## UK postcodes
|
### UK postcodes
|
||||||
|
|
||||||
Nominatim can use postcodes from an external source to improve searches that involve a UK postcode. This data can be optionally downloaded:
|
Nominatim can use postcodes from an external source to improve searches that involve a UK postcode. This data can be optionally downloaded:
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ Nominatim can use postcodes from an external source to improve searches that inv
|
|||||||
wget https://www.nominatim.org/data/gb_postcode_data.sql.gz
|
wget https://www.nominatim.org/data/gb_postcode_data.sql.gz
|
||||||
|
|
||||||
|
|
||||||
# Initial import of the data
|
## Initial import of the data
|
||||||
|
|
||||||
**Important:** first try the import with a small excerpt, for example from
|
**Important:** first try the import with a small excerpt, for example from
|
||||||
[Geofabrik](https://download.geofabrik.de).
|
[Geofabrik](https://download.geofabrik.de).
|
||||||
@@ -97,7 +99,7 @@ you also need to enable these key phrases like this:
|
|||||||
Note that this command downloads the phrases from the wiki link above.
|
Note that this command downloads the phrases from the wiki link above.
|
||||||
|
|
||||||
|
|
||||||
# Installing Tiger housenumber data for the US
|
## Installing Tiger housenumber data for the US
|
||||||
|
|
||||||
Nominatim is able to use the official TIGER address set to complement the
|
Nominatim is able to use the official TIGER address set to complement the
|
||||||
OSM house number data in the US. You can add TIGER data to your own Nominatim
|
OSM house number data in the US. You can add TIGER data to your own Nominatim
|
||||||
@@ -108,7 +110,7 @@ instance by following these steps:
|
|||||||
* Ubuntu: `sudo apt-get install python-gdal unzip`
|
* Ubuntu: `sudo apt-get install python-gdal unzip`
|
||||||
* CentOS: `sudo yum install gdal-python unzip`
|
* CentOS: `sudo yum install gdal-python unzip`
|
||||||
|
|
||||||
2. Get preprocessed TIGER 2015 data and unpack it into the
|
2. Get preprocessed TIGER 2017 data and unpack it into the
|
||||||
data directory in your Nominatim sources:
|
data directory in your Nominatim sources:
|
||||||
|
|
||||||
cd Nominatim/data
|
cd Nominatim/data
|
||||||
@@ -135,13 +137,9 @@ You can also process the data from the original TIGER data to create the
|
|||||||
SQL files, Nominatim needs for the import:
|
SQL files, Nominatim needs for the import:
|
||||||
|
|
||||||
1. Get the TIGER 2017 data. You will need the EDGES files
|
1. Get the TIGER 2017 data. You will need the EDGES files
|
||||||
(3,234 zip files, 11GB total). Choose one of the two sources:
|
(3,234 zip files, 11GB total).
|
||||||
|
|
||||||
wget -r ftp://ftp2.census.gov/geo/tiger/TIGER2017/EDGES/
|
wget -r ftp://ftp2.census.gov/geo/tiger/TIGER2017/EDGES/
|
||||||
wget -r ftp://mirror1.shellbot.com/census/geo/tiger/TIGER2017/EDGES/
|
|
||||||
|
|
||||||
The first one is the original source, the second a considerably faster
|
|
||||||
mirror.
|
|
||||||
|
|
||||||
2. Convert the data into SQL statements:
|
2. Convert the data into SQL statements:
|
||||||
|
|
||||||
@@ -150,13 +148,13 @@ SQL files, Nominatim needs for the import:
|
|||||||
Be warned that this can take quite a long time. After this process is finished,
|
Be warned that this can take quite a long time. After this process is finished,
|
||||||
the same preprocessed files as above are available in `data/tiger`.
|
the same preprocessed files as above are available in `data/tiger`.
|
||||||
|
|
||||||
# Updates
|
## Updates
|
||||||
|
|
||||||
There are many different possibilities to update your Nominatim database.
|
There are many different possibilities to update your Nominatim database.
|
||||||
The following section describes how to keep it up-to-date with Pyosmium.
|
The following section describes how to keep it up-to-date with Pyosmium.
|
||||||
For a list of other methods see the output of `./utils/update.php --help`.
|
For a list of other methods see the output of `./utils/update.php --help`.
|
||||||
|
|
||||||
### Installing the newest version of Pyosmium
|
#### Installing the newest version of Pyosmium
|
||||||
|
|
||||||
It is recommended to install Pyosmium via pip. Run (as the same user who
|
It is recommended to install Pyosmium via pip. Run (as the same user who
|
||||||
will later run the updates):
|
will later run the updates):
|
||||||
@@ -165,7 +163,7 @@ will later run the updates):
|
|||||||
pip install --user osmium
|
pip install --user osmium
|
||||||
```
|
```
|
||||||
|
|
||||||
Nominatim needs a tool called `pyosmium-get-updates` that comes with
|
Nominatim needs a tool called `pyosmium-get-updates`, which comes with
|
||||||
Pyosmium. You need to tell Nominatim where to find it. Add the
|
Pyosmium. You need to tell Nominatim where to find it. Add the
|
||||||
following line to your `settings/local.php`:
|
following line to your `settings/local.php`:
|
||||||
|
|
||||||
@@ -174,7 +172,7 @@ following line to your `settings/local.php`:
|
|||||||
The path above is fine if you used the `--user` parameter with pip.
|
The path above is fine if you used the `--user` parameter with pip.
|
||||||
Replace `user` with your user name.
|
Replace `user` with your user name.
|
||||||
|
|
||||||
### Setting up the update process
|
#### Setting up the update process
|
||||||
|
|
||||||
Next the update needs to be initialised. By default Nominatim is configured
|
Next the update needs to be initialised. By default Nominatim is configured
|
||||||
to update using the global minutely diffs.
|
to update using the global minutely diffs.
|
||||||
@@ -200,7 +198,7 @@ what you expect.
|
|||||||
The --init-updates command needs to be rerun whenever the replication service
|
The --init-updates command needs to be rerun whenever the replication service
|
||||||
is changed.
|
is changed.
|
||||||
|
|
||||||
### Updating Nominatim
|
#### Updating Nominatim
|
||||||
|
|
||||||
The following command will keep your database constantly up to date:
|
The following command will keep your database constantly up to date:
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,26 @@
|
|||||||
|
# Basic Installation
|
||||||
|
|
||||||
This page contains generic installation instructions for Nominatim and its
|
This page contains generic installation instructions for Nominatim and its
|
||||||
prerequisites. There are also step-by-step instructions available for
|
prerequisites. There are also step-by-step instructions available for
|
||||||
the following operating systems:
|
the following operating systems:
|
||||||
|
|
||||||
|
* [Ubuntu 18.04](../appendix/Install-on-Ubuntu-18.md)
|
||||||
* [Ubuntu 16.04](../appendix/Install-on-Ubuntu-16.md)
|
* [Ubuntu 16.04](../appendix/Install-on-Ubuntu-16.md)
|
||||||
* [CentOS 7.2](../appendix/Install-on-Centos-7.md)
|
* [CentOS 7.2](../appendix/Install-on-Centos-7.md)
|
||||||
|
|
||||||
These OS-specific instructions can also be found in executable form
|
These OS-specific instructions can also be found in executable form
|
||||||
in the `vagrant/` directory.
|
in the `vagrant/` directory.
|
||||||
|
|
||||||
# Prerequisites
|
Users have created instructions for other frameworks. We haven't tested those
|
||||||
|
and can't offer support.
|
||||||
|
|
||||||
## Software
|
* [Docker](https://github.com/mediagis/nominatim-docker)
|
||||||
|
* [Docker on Kubernetes](https://github.com/peter-evans/nominatim-k8s)
|
||||||
|
* [Ansible](https://github.com/synthesio/infra-ansible-nominatim)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### Software
|
||||||
|
|
||||||
For compiling:
|
For compiling:
|
||||||
|
|
||||||
@@ -42,22 +52,22 @@ For running continuous updates:
|
|||||||
|
|
||||||
* [pyosmium](http://osmcode.org/pyosmium/)
|
* [pyosmium](http://osmcode.org/pyosmium/)
|
||||||
|
|
||||||
## Hardware
|
### Hardware
|
||||||
|
|
||||||
A minimum of 2GB of RAM is required or installation will fail. For a full
|
A minimum of 2GB of RAM is required or installation will fail. For a full
|
||||||
planet import 32GB of RAM or more strongly are recommended.
|
planet import 32GB of RAM or more strongly are recommended.
|
||||||
|
|
||||||
For a full planet install you will need about 600GB of hard disk space (as of
|
For a full planet install you will need at least 700GB of hard disk space
|
||||||
January 2017, take into account that the OSM database is growing fast). SSD disks
|
(take into account that the OSM database is growing fast). SSD disks
|
||||||
will help considerably to speed up import and queries.
|
will help considerably to speed up import and queries.
|
||||||
|
|
||||||
On a 6-core machine with 32GB RAM and SSDs the import of a full planet takes
|
On a 6-core machine with 32GB RAM and SSDs the import of a full planet takes
|
||||||
a bit more than 2 days. Without SSDs 7-8 days are more realistic.
|
a bit more than 2 days. Without SSDs 7-8 days are more realistic.
|
||||||
|
|
||||||
|
|
||||||
# Setup of the server
|
## Setup of the server
|
||||||
|
|
||||||
## PostgreSQL tuning
|
### PostgreSQL tuning
|
||||||
|
|
||||||
You might want to tune your PostgreSQL installation so that the later steps
|
You might want to tune your PostgreSQL installation so that the later steps
|
||||||
make best use of your hardware. You should tune the following parameters in
|
make best use of your hardware. You should tune the following parameters in
|
||||||
@@ -84,13 +94,13 @@ Don't forget to reenable them after the initial import or you risk database
|
|||||||
corruption. Autovacuum must not be switched off because it ensures that the
|
corruption. Autovacuum must not be switched off because it ensures that the
|
||||||
tables are frequently analysed.
|
tables are frequently analysed.
|
||||||
|
|
||||||
## Webserver setup
|
### Webserver setup
|
||||||
|
|
||||||
The `website/` directory in the build directory contains the configured
|
The `website/` directory in the build directory contains the configured
|
||||||
website. Include the directory into your webbrowser to serve php files
|
website. Include the directory into your webbrowser to serve php files
|
||||||
from there.
|
from there.
|
||||||
|
|
||||||
### Configure for use with Apache
|
#### Configure for use with Apache
|
||||||
|
|
||||||
Make sure your Apache configuration contains the required permissions for the
|
Make sure your Apache configuration contains the required permissions for the
|
||||||
directory and create an alias:
|
directory and create an alias:
|
||||||
@@ -109,7 +119,7 @@ build directory.
|
|||||||
After making changes in the apache config you need to restart apache.
|
After making changes in the apache config you need to restart apache.
|
||||||
The website should now be available on http://localhost/nominatim.
|
The website should now be available on http://localhost/nominatim.
|
||||||
|
|
||||||
### Configure for use with Nginx
|
#### Configure for use with Nginx
|
||||||
|
|
||||||
Use php-fpm as a deamon for serving PHP cgi. Install php-fpm together with nginx.
|
Use php-fpm as a deamon for serving PHP cgi. Install php-fpm together with nginx.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
Database Migrations
|
# Database Migrations
|
||||||
===================
|
|
||||||
|
|
||||||
This page describes database migrations necessary to update existing databases
|
This page describes database migrations necessary to update existing databases
|
||||||
to newer versions of Nominatim.
|
to newer versions of Nominatim.
|
||||||
@@ -7,7 +6,46 @@ to newer versions of Nominatim.
|
|||||||
SQL statements should be executed from the postgres commandline. Execute
|
SQL statements should be executed from the postgres commandline. Execute
|
||||||
`psql nominiatim` to enter command line mode.
|
`psql nominiatim` to enter command line mode.
|
||||||
|
|
||||||
# 3.0.0 -> 3.1.0
|
## 3.1.0 -> 3.2.0
|
||||||
|
|
||||||
|
### New reverse algorithm
|
||||||
|
|
||||||
|
The reverse algorithm has changed and requires new indexes. Run the following
|
||||||
|
SQL statements to create the indexes:
|
||||||
|
|
||||||
|
```
|
||||||
|
CREATE INDEX idx_placex_geometry_reverse_lookupPoint
|
||||||
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
|
WHERE (name is not null or housenumber is not null or rank_address between 26 and 27)
|
||||||
|
AND class not in ('railway','tunnel','bridge','man_made')
|
||||||
|
AND rank_address >= 26 AND indexed_status = 0 AND linked_place_id is null;
|
||||||
|
CREATE INDEX idx_placex_geometry_reverse_lookupPolygon
|
||||||
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
|
WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
|
||||||
|
AND rank_address between 4 and 25 AND type != 'postcode'
|
||||||
|
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
||||||
|
CREATE INDEX idx_placex_geometry_reverse_placeNode
|
||||||
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
|
WHERE osm_type = 'N' AND rank_search between 5 and 25
|
||||||
|
AND class = 'place' AND type != 'postcode'
|
||||||
|
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
||||||
|
```
|
||||||
|
|
||||||
|
You also need to grant the website user access to the `country_osm_grid` table:
|
||||||
|
|
||||||
|
```
|
||||||
|
GRANT SELECT ON table country_osm_grid to "www-user";
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace the `www-user` with the user name of your website server if necessary.
|
||||||
|
|
||||||
|
Finally, you can drop the now unused indexes:
|
||||||
|
|
||||||
|
```
|
||||||
|
DROP INDEX idx_placex_reverse_geometry;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3.0.0 -> 3.1.0
|
||||||
|
|
||||||
### Postcode Table
|
### Postcode Table
|
||||||
|
|
||||||
@@ -26,6 +64,17 @@ CREATE INDEX idx_postcode_geometry ON location_postcode USING GIST (geometry);
|
|||||||
CREATE UNIQUE INDEX idx_postcode_id ON location_postcode USING BTREE (place_id);
|
CREATE UNIQUE INDEX idx_postcode_id ON location_postcode USING BTREE (place_id);
|
||||||
CREATE INDEX idx_postcode_postcode ON location_postcode USING BTREE (postcode);
|
CREATE INDEX idx_postcode_postcode ON location_postcode USING BTREE (postcode);
|
||||||
GRANT SELECT ON location_postcode TO "www-data";
|
GRANT SELECT ON location_postcode TO "www-data";
|
||||||
|
drop type if exists nearfeaturecentr cascade;
|
||||||
|
create type nearfeaturecentr as (
|
||||||
|
place_id BIGINT,
|
||||||
|
keywords int[],
|
||||||
|
rank_address smallint,
|
||||||
|
rank_search smallint,
|
||||||
|
distance float,
|
||||||
|
isguess boolean,
|
||||||
|
postcode TEXT,
|
||||||
|
centroid GEOMETRY
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Add postcode column to `location_area` tables with SQL statement:
|
Add postcode column to `location_area` tables with SQL statement:
|
||||||
|
|||||||
152
docs/api/Details.md
Normal file
152
docs/api/Details.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# Place details
|
||||||
|
|
||||||
|
Lookup details about a single place by id. The default output is HTML for debugging search logic and results.
|
||||||
|
|
||||||
|
**The details page (including JSON output) exists for debugging only and must not be downloaded automatically**, see [Nominatim Usage Policy](https://operations.osmfoundation.org/policies/nominatim/).
|
||||||
|
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
The details API supports the following two request formats:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://nominatim.openstreetmap.org/details?osmtype=[N|W|R]&osmid=<value>&class=<value>
|
||||||
|
```
|
||||||
|
|
||||||
|
`osmtype` and `osmid` are required parameter. The type is one of node (N), way (W)
|
||||||
|
or relation (R). The id must be a number. The `class` parameter is optional and
|
||||||
|
allows to distinguish between entries, when the corresponding OSM object has more
|
||||||
|
than one main tag. For example, when a place is tagged with `tourism=hotel` and
|
||||||
|
`amenity=restaurant`, there will be two place entries in Nominatim, one for a
|
||||||
|
restaurant, one for a hotel. You need to specify `class=tourism` or `class=amentity`
|
||||||
|
to get exactly the one you want. If there are multiple places in the database
|
||||||
|
but the `class` parameter is left out, then one of the places will be chosen
|
||||||
|
at random and displayed.
|
||||||
|
|
||||||
|
```
|
||||||
|
https://nominatim.openstreetmap.org/details?placeid=<value>
|
||||||
|
```
|
||||||
|
|
||||||
|
Placeids are assigned sequentially during Nominatim data import. The id for a place is different between Nominatim installation (servers) and changes when data gets reimported. Therefore it can't be used as permanent id and shouldn't be used in bug reports.
|
||||||
|
|
||||||
|
|
||||||
|
Additional optional parameters are explained below.
|
||||||
|
|
||||||
|
### Output format
|
||||||
|
|
||||||
|
* `format=[html|json]`
|
||||||
|
|
||||||
|
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
||||||
|
|
||||||
|
* `json_callback=<string>`
|
||||||
|
|
||||||
|
Wrap json output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
||||||
|
Only has an effect for JSON output formats.
|
||||||
|
|
||||||
|
* `pretty=[0|1]`
|
||||||
|
|
||||||
|
For JSON output will add indentation to make it more human-readable. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
### Output details
|
||||||
|
|
||||||
|
* `addressdetails=[0|1]`
|
||||||
|
|
||||||
|
Include a breakdown of the address into elements. (Default for JSON: 0, for HTML: 1)
|
||||||
|
|
||||||
|
* `keywords=[0|1]`
|
||||||
|
|
||||||
|
Include a list of name keywords and address keywords (word ids). (Default: 0)
|
||||||
|
|
||||||
|
* `linkedplaces=[0|1]`
|
||||||
|
|
||||||
|
Include details of places higher in the address hierarchy. E.g. for a street this is usually the city, state, postal code, country. (Default: 1)
|
||||||
|
|
||||||
|
* `hierarchy=[0|1]`
|
||||||
|
|
||||||
|
Include details of places lower in the address hierarchy. E.g. for a city this usually a list of streets, suburbs, rivers. (Default for JSON: 0, for HTML: 1)
|
||||||
|
|
||||||
|
* `group_hierarchy=[0|1]`
|
||||||
|
|
||||||
|
For JSON output will group the places by type. (Default: 0)
|
||||||
|
|
||||||
|
* `polygon_geojson=[0|1]`
|
||||||
|
|
||||||
|
Include geometry of result. (Default for JSON: 0, for HTML: 1)
|
||||||
|
|
||||||
|
### Language of results
|
||||||
|
|
||||||
|
* `accept-language=<browser language string>`
|
||||||
|
|
||||||
|
Preferred language order for showing result, overrides the value
|
||||||
|
specified in the "Accept-Language" HTTP header.
|
||||||
|
Either use a standard RFC2616 accept-language string or a simple
|
||||||
|
comma-separated list of language codes.
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
##### HTML
|
||||||
|
|
||||||
|
[https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407](https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407)
|
||||||
|
|
||||||
|
##### JSON
|
||||||
|
|
||||||
|
[https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407&format=json](https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407&format=json)
|
||||||
|
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"place_id": 85993608,
|
||||||
|
"parent_place_id": 72765313,
|
||||||
|
"osm_type": "W",
|
||||||
|
"osm_id": 38210407,
|
||||||
|
"category": "place",
|
||||||
|
"type": "square",
|
||||||
|
"admin_level": "15",
|
||||||
|
"localname": "Pariser Platz",
|
||||||
|
"names": {
|
||||||
|
"name": "Pariser Platz",
|
||||||
|
"name:be": "Парыжская плошча",
|
||||||
|
"name:de": "Pariser Platz",
|
||||||
|
"name:es": "Plaza de París",
|
||||||
|
"name:he": "פאריזר פלאץ",
|
||||||
|
"name:ko": "파리저 광장",
|
||||||
|
"name:la": "Forum Parisinum",
|
||||||
|
"name:ru": "Парижская площадь",
|
||||||
|
"name:uk": "Паризька площа",
|
||||||
|
"name:zh": "巴黎廣場"
|
||||||
|
},
|
||||||
|
"addresstags": {
|
||||||
|
"postcode": "10117"
|
||||||
|
},
|
||||||
|
"housenumber": null,
|
||||||
|
"calculated_postcode": "10117",
|
||||||
|
"country_code": "de",
|
||||||
|
"indexed_date": "2018-08-18T17:02:45+00:00",
|
||||||
|
"importance": 0.339401620591472,
|
||||||
|
"calculated_importance": 0.339401620591472,
|
||||||
|
"extratags": {
|
||||||
|
"wikidata": "Q156716",
|
||||||
|
"wikipedia": "de:Pariser Platz"
|
||||||
|
},
|
||||||
|
"calculated_wikipedia": "de:Pariser_Platz",
|
||||||
|
"rank_address": 30,
|
||||||
|
"rank_search": 30,
|
||||||
|
"isarea": true,
|
||||||
|
"centroid": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
13.3786822618517,
|
||||||
|
52.5163654
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
13.3786822618517,
|
||||||
|
52.5163654
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
43
docs/api/Faq.md
Normal file
43
docs/api/Faq.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
## API Results
|
||||||
|
|
||||||
|
#### 1. The address of my search results contains far-away places that don't belong there.
|
||||||
|
|
||||||
|
Nominatim computes the address from two sources in the OpenStreetMap data:
|
||||||
|
from administrative boundaries and from place nodes. Boundaries are the more
|
||||||
|
useful source. They precisely describe an area. So it is very clear for
|
||||||
|
Nominatim if a point belongs to an area of not. Place nodes are more complicated.
|
||||||
|
These are only points without any precise extend. So Nominatim has to take a
|
||||||
|
guess and assume that an address belongs to the closest place nose it can find.
|
||||||
|
In an ideal world, Nominatim would not need the place nodes but there are
|
||||||
|
many places on earth where there are not precise boundaries available for
|
||||||
|
all parts that make up an address. This is in particular true for the more
|
||||||
|
local address parts, like villages and suburbs. Therefore it is not possible
|
||||||
|
to completely dismiss place nodes. And sometimes they sneak in where they
|
||||||
|
don't belong.
|
||||||
|
|
||||||
|
As a OpenStreetMap mapper, you can improve the situation in two ways: if you
|
||||||
|
see a place node for which already an administrative area exists, then you
|
||||||
|
should _link_ the two by adding the node with a 'label' role to the boundary
|
||||||
|
relation. If there is no administrative area, you can add the approximate
|
||||||
|
extend of the place and tag it place=<something> as well.
|
||||||
|
|
||||||
|
#### 2. When doing reverse search, the address details have parts that don't contain the point I was looking up.
|
||||||
|
|
||||||
|
There is a common misconception how the reverse API call works in Nominatim.
|
||||||
|
Reverse does not give you the address of the point you asked for. Reverse
|
||||||
|
returns the closest object to the point you asked for and then returns the
|
||||||
|
address of that object. Now, if you are close to a border, then the closest
|
||||||
|
object may be across that border. When Nominatim then returns the address,
|
||||||
|
contains the county/state/country across the border.
|
||||||
|
|
||||||
|
#### 3. I get different counties/states/countries when I change the zoom parameter in the reverse query. How is that possible?
|
||||||
|
|
||||||
|
This is basically the same problem as in the previous answer.
|
||||||
|
The zoom level influences at which [search rank](https://wiki.openstreetmap.org/wiki/Nominatim/Development_overview#Country_to_street_level) Nominatim starts looking
|
||||||
|
for the closest object. So the closest house number maybe on one side of the
|
||||||
|
border while the closest street is on the other. As the address details contain
|
||||||
|
the address of the closest object found, you might sometimes get one result,
|
||||||
|
sometimes the other for the closest point.
|
||||||
|
|
||||||
147
docs/api/Lookup.md
Normal file
147
docs/api/Lookup.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# Address lookup
|
||||||
|
|
||||||
|
The lookup API allows to query the address and other details of one or
|
||||||
|
multiple OSM objects like node, way or relation.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
The lookup API has the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://nominatim.openstreetmap.org/lookup?osm_ids=[N|W|R]<value>,…,…,&<params>
|
||||||
|
```
|
||||||
|
|
||||||
|
`osm_ids` is mandatory and must contain a comma-separated list of OSM ids each
|
||||||
|
prefixed with its type, one of node(N), way(W) or relation(R). Up to 50 ids
|
||||||
|
can be queried at the same time.
|
||||||
|
|
||||||
|
Additional optional parameters are explained below.
|
||||||
|
|
||||||
|
### Output format
|
||||||
|
|
||||||
|
* `format=[html|xml|json|jsonv2|geojson|geocodejson]`
|
||||||
|
|
||||||
|
See [Place Output Formats](Output.md) for details on each format. (Default: xml)
|
||||||
|
|
||||||
|
* `json_callback=<string>`
|
||||||
|
|
||||||
|
Wrap json output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
||||||
|
Only has an effect for JSON output formats.
|
||||||
|
|
||||||
|
### Output details
|
||||||
|
|
||||||
|
* `addressdetails=[0|1]`
|
||||||
|
|
||||||
|
Include a breakdown of the address into elements. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
* `extratags=[0|1]`
|
||||||
|
|
||||||
|
Include additional information in the result if available,
|
||||||
|
e.g. wikipedia link, opening hours. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
* `namedetails=[0|1]`
|
||||||
|
|
||||||
|
Include a list of alternative names in the results. These may include
|
||||||
|
language variants, references, operator and brand. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
### Language of results
|
||||||
|
|
||||||
|
* `accept-language=<browser language string>`
|
||||||
|
|
||||||
|
Preferred language order for showing search results, overrides the value
|
||||||
|
specified in the "Accept-Language" HTTP header.
|
||||||
|
Either use a standard RFC2616 accept-language string or a simple
|
||||||
|
comma-separated list of language codes.
|
||||||
|
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
* `email=<valid email address>`
|
||||||
|
|
||||||
|
If you are making large numbers of request please include an appropriate email
|
||||||
|
address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details.
|
||||||
|
|
||||||
|
* `debug=[0|1]`
|
||||||
|
|
||||||
|
Output assorted developer debug information. Data on internals of Nominatim's
|
||||||
|
"Search Loop" logic, and SQL queries. The output is (rough) HTML format.
|
||||||
|
This overrides the specified machine readable format. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
##### XML
|
||||||
|
|
||||||
|
[https://nominatim.openstreetmap.org/lookup?osm_ids=R146656,W104393803,N240109189](https://nominatim.openstreetmap.org/lookup?osm_ids=R146656,W104393803,N240109189)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<lookupresults timestamp="Mon, 29 Jun 15 18:01:33 +0000" attribution="Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright" querystring="R146656,W104393803,N240109189" polygon="false">
|
||||||
|
<place place_id="127761056" osm_type="relation" osm_id="146656" place_rank="16" lat="53.4791466" lon="-2.2447445" display_name="Manchester, Greater Manchester, North West England, England, United Kingdom" class="boundary" type="administrative" importance="0.704893333438333">
|
||||||
|
<city>Manchester</city>
|
||||||
|
<county>Greater Manchester</county>
|
||||||
|
<state_district>North West England</state_district>
|
||||||
|
<state>England</state>
|
||||||
|
<country>United Kingdom</country>
|
||||||
|
<country_code>gb</country_code>
|
||||||
|
</place>
|
||||||
|
<place place_id="77769745" osm_type="way" osm_id="104393803" place_rank="30" lat="52.5162024" lon="13.3777343363579" display_name="Brandenburg Gate, 1, Pariser Platz, Mitte, Berlin, 10117, Germany" class="tourism" type="attraction" importance="0.443472858361592">
|
||||||
|
<attraction>Brandenburg Gate</attraction>
|
||||||
|
<house_number>1</house_number>
|
||||||
|
<pedestrian>Pariser Platz</pedestrian>
|
||||||
|
<suburb>Mitte</suburb>
|
||||||
|
<city_district>Mitte</city_district>
|
||||||
|
<city>Berlin</city>
|
||||||
|
<state>Berlin</state>
|
||||||
|
<postcode>10117</postcode>
|
||||||
|
<country>Germany</country>
|
||||||
|
<country_code>de</country_code>
|
||||||
|
</place>
|
||||||
|
<place place_id="2570600569" osm_type="node" osm_id="240109189" place_rank="15" lat="52.5170365" lon="13.3888599" display_name="Berlin, Germany" class="place" type="city" importance="0.822149797630868">
|
||||||
|
<city>Berlin</city>
|
||||||
|
<state>Berlin</state>
|
||||||
|
<country>Germany</country>
|
||||||
|
<country_code>de</country_code>
|
||||||
|
</place>
|
||||||
|
</lookupresults>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### JSON with extratags
|
||||||
|
|
||||||
|
[https://nominatim.openstreetmap.org/lookup?osm_ids=W50637691&format=json](https://nominatim.openstreetmap.org/lookup?osm_ids=W50637691&format=json)
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"place_id": "84271358",
|
||||||
|
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||||
|
"osm_type": "way",
|
||||||
|
"osm_id": "50637691",
|
||||||
|
"lat": "52.39955055",
|
||||||
|
"lon": "13.04806574678",
|
||||||
|
"display_name": "Brandenburger Tor, Brandenburger Straße, Nördliche Innenstadt, Innenstadt, Potsdam, Brandenburg, 14467, Germany",
|
||||||
|
"class": "historic",
|
||||||
|
"type": "city_gate",
|
||||||
|
"importance": "0.221233780277011",
|
||||||
|
"address": {
|
||||||
|
"address29": "Brandenburger Tor",
|
||||||
|
"pedestrian": "Brandenburger Straße",
|
||||||
|
"suburb": "Nördliche Innenstadt",
|
||||||
|
"city": "Potsdam",
|
||||||
|
"state": "Brandenburg",
|
||||||
|
"postcode": "14467",
|
||||||
|
"country": "Germany",
|
||||||
|
"country_code": "de"
|
||||||
|
},
|
||||||
|
"extratags": {
|
||||||
|
"image": "http://commons.wikimedia.org/wiki/File:Potsdam_brandenburger_tor.jpg",
|
||||||
|
"wikidata": "Q695045",
|
||||||
|
"wikipedia": "de:Brandenburger Tor (Potsdam)",
|
||||||
|
"wheelchair": "yes",
|
||||||
|
"description": "Kleines Brandenburger Tor in Potsdam"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
222
docs/api/Output.md
Normal file
222
docs/api/Output.md
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
# Place Output
|
||||||
|
|
||||||
|
The [\reverse](Reverse.md), [\search](Search.md) and [\lookup](Lookup.md)
|
||||||
|
API calls produce very similar output which is explained in this section.
|
||||||
|
There is one section for each format which is selectable via the `format`
|
||||||
|
parameter.
|
||||||
|
|
||||||
|
## Formats
|
||||||
|
|
||||||
|
### JSON
|
||||||
|
|
||||||
|
The JSON format returns an array of places (for search and lookup) or
|
||||||
|
a single place (for reverse) of the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"place_id": "100149",
|
||||||
|
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||||
|
"osm_type": "node",
|
||||||
|
"osm_id": "107775",
|
||||||
|
"boundingbox": ["51.3473219", "51.6673219", "-0.2876474", "0.0323526"],
|
||||||
|
"lat": "51.5073219",
|
||||||
|
"lon": "-0.1276474",
|
||||||
|
"display_name": "London, Greater London, England, SW1A 2DU, United Kingdom",
|
||||||
|
"class": "place",
|
||||||
|
"type": "city",
|
||||||
|
"importance": 0.9654895765402,
|
||||||
|
"icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
|
||||||
|
"address": {
|
||||||
|
"city": "London",
|
||||||
|
"state_district": "Greater London",
|
||||||
|
"state": "England",
|
||||||
|
"postcode": "SW1A 2DU",
|
||||||
|
"country": "United Kingdom",
|
||||||
|
"country_code": "gb"
|
||||||
|
},
|
||||||
|
"extratags": {
|
||||||
|
"capital": "yes",
|
||||||
|
"website": "http://www.london.gov.uk",
|
||||||
|
"wikidata": "Q84",
|
||||||
|
"wikipedia": "en:London",
|
||||||
|
"population": "8416535"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
The possible fields are:
|
||||||
|
|
||||||
|
* `place_id` - reference to the Nominatim internal database ID
|
||||||
|
* `osm_type`, `osm_id` - reference to the OSM object
|
||||||
|
* `boundingbox` - area of corner coordinates
|
||||||
|
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
||||||
|
* `display_name` - full comma-separated address
|
||||||
|
* `class`, `type` - key and value of the main OSM tag
|
||||||
|
* `importance` - computed importance rank
|
||||||
|
* `icon` - link to class icon (if available)
|
||||||
|
* `address` - dictionary of address details (only with `addressdetails=1`)
|
||||||
|
* `extratags` - dictionary with additional useful tags like website or maxspeed
|
||||||
|
(only with `extratags=1`)
|
||||||
|
* `namedetails` - dictionary with full list of available names including ref etc.
|
||||||
|
* `geojson`, `svg`, `geotext`, `geokml` - full geometry
|
||||||
|
(only with the appropriate `polygon_*` parameter)
|
||||||
|
|
||||||
|
### JSONv2
|
||||||
|
|
||||||
|
This is the same as the JSON format with two changes:
|
||||||
|
|
||||||
|
* `class` renamed to `category`
|
||||||
|
* additional field `place_rank` with the search rank of the object
|
||||||
|
|
||||||
|
### GeoJSON
|
||||||
|
|
||||||
|
This format follows the [RFC7946](http://geojson.org). Every feature includes
|
||||||
|
a bounding box (`bbox`).
|
||||||
|
|
||||||
|
The feature list has the following fields:
|
||||||
|
|
||||||
|
* `place_id` - reference to the Nominatim internal database ID
|
||||||
|
* `osm_type`, `osm_id` - reference to the OSM object
|
||||||
|
* `category`, `type` - key and value of the main OSM tag
|
||||||
|
* `display_name` - full comma-separated address
|
||||||
|
* `place_rank` - class search rank
|
||||||
|
* `importance` - computed importance rank
|
||||||
|
* `icon` - link to class icon (if available)
|
||||||
|
* `address` - dictionary of address details (only with `addressdetails=1`)
|
||||||
|
* `extratags` - dictionary with additional useful tags like website or maxspeed
|
||||||
|
(only with `extratags=1`)
|
||||||
|
* `namedetails` - dictionary with full list of available names including ref etc.
|
||||||
|
|
||||||
|
Use `polygon_geojson` to output the full geometry of the object instead
|
||||||
|
of the centroid.
|
||||||
|
|
||||||
|
### GeocodeJSON
|
||||||
|
|
||||||
|
The GeocodeJSON format follows the
|
||||||
|
[GeocodeJSON spec 0.1.0](https://github.com/geocoders/geocodejson-spec).
|
||||||
|
The following feature attributes are implemented:
|
||||||
|
|
||||||
|
* `osm_type`, `osm_id` - reference to the OSM object (unofficial extension)
|
||||||
|
* `type` - value of the main tag of the object (e.g. residential, restaurant, ...)
|
||||||
|
* `label` - full comma-separated address
|
||||||
|
* `name` - localised name of the place
|
||||||
|
* `housenumber`, `street`, `locality`, `postcode`, `city`,
|
||||||
|
`district`, `county`, `state`, `country` -
|
||||||
|
provided when it can be determined from the address
|
||||||
|
(see [this issue](https://github.com/openstreetmap/Nominatim/issues/1080) for
|
||||||
|
current limitations on the correctness of the address) and `addressdetails=1`
|
||||||
|
was given
|
||||||
|
* `admin` - list of localised names of administrative boundaries (only with `addressdetails=1`)
|
||||||
|
|
||||||
|
Use `polygon_geojson` to output the full geometry of the object instead
|
||||||
|
of the centroid.
|
||||||
|
|
||||||
|
### XML
|
||||||
|
|
||||||
|
The XML response returns one or more place objects in slightly different
|
||||||
|
formats depending on the API call.
|
||||||
|
|
||||||
|
#### Reverse
|
||||||
|
|
||||||
|
```
|
||||||
|
<reversegeocode timestamp="Sat, 11 Aug 18 11:53:21 +0000"
|
||||||
|
attribution="Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright"
|
||||||
|
querystring="lat=48.400381&lon=11.745876&zoom=5&format=xml">
|
||||||
|
<result place_id="179509537" osm_type="relation" osm_id="2145268" ref="BY"
|
||||||
|
lat="48.9467562" lon="11.4038717"
|
||||||
|
boundingbox="47.2701114,50.5647142,8.9763497,13.8396373">
|
||||||
|
Bavaria, Germany
|
||||||
|
</result>
|
||||||
|
<addressparts>
|
||||||
|
<state>Bavaria</state>
|
||||||
|
<country>Germany</country>
|
||||||
|
<country_code>de</country_code>
|
||||||
|
</addressparts>
|
||||||
|
<extratags>
|
||||||
|
<tag key="place" value="state"/>
|
||||||
|
<tag key="wikidata" value="Q980"/>
|
||||||
|
<tag key="wikipedia" value="de:Bayern"/>
|
||||||
|
<tag key="population" value="12520000"/>
|
||||||
|
<tag key="name:prefix" value="Freistaat"/>
|
||||||
|
</extratags>
|
||||||
|
</reversegeocode>
|
||||||
|
```
|
||||||
|
|
||||||
|
The attributes of the outer `reversegeocode` element return generic information
|
||||||
|
about the query, including the time when the response was sent (in UTC),
|
||||||
|
attribution to OSM and the original querystring.
|
||||||
|
|
||||||
|
The place information can be found in the `result` element. The attributes of that element contain:
|
||||||
|
|
||||||
|
* `place_id` - reference to the Nominatim internal database ID
|
||||||
|
* `osm_type`, `osm_id` - reference to the OSM object
|
||||||
|
* `ref` - content of `ref` tag if it exists
|
||||||
|
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
||||||
|
* `boundingbox` - comma-separated list of corner coordinates
|
||||||
|
|
||||||
|
The full address address of the result can be found in the content of the
|
||||||
|
`result` element as a comma-separated list.
|
||||||
|
|
||||||
|
Additional information requested with `addressdetails=1`, `extratags=1` and
|
||||||
|
`namedetails=1` can be found in extra elements.
|
||||||
|
|
||||||
|
#### Search and Lookup
|
||||||
|
|
||||||
|
```
|
||||||
|
<searchresults timestamp="Sat, 11 Aug 18 11:55:35 +0000"
|
||||||
|
attribution="Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright"
|
||||||
|
querystring="london" polygon="false" exclude_place_ids="100149"
|
||||||
|
more_url="https://nominatim.openstreetmap.org/search.php?q=london&addressdetails=1&extratags=1&exclude_place_ids=100149&format=xml&accept-language=en-US%2Cen%3Bq%3D0.7%2Cde%3Bq%3D0.3">
|
||||||
|
<place place_id="100149" osm_type="node" osm_id="107775" place_rank="15"
|
||||||
|
boundingbox="51.3473219,51.6673219,-0.2876474,0.0323526" lat="51.5073219" lon="-0.1276474"
|
||||||
|
display_name="London, Greater London, England, SW1A 2DU, United Kingdom"
|
||||||
|
class="place" type="city" importance="0.9654895765402"
|
||||||
|
icon="https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png">
|
||||||
|
<extratags>
|
||||||
|
<tag key="capital" value="yes"/>
|
||||||
|
<tag key="website" value="http://www.london.gov.uk"/>
|
||||||
|
<tag key="wikidata" value="Q84"/>
|
||||||
|
<tag key="wikipedia" value="en:London"/>
|
||||||
|
<tag key="population" value="8416535"/>
|
||||||
|
</extratags>
|
||||||
|
<city>London</city>
|
||||||
|
<state_district>Greater London</state_district>
|
||||||
|
<state>England</state>
|
||||||
|
<postcode>SW1A 2DU</postcode>
|
||||||
|
<country>United Kingdom</country>
|
||||||
|
<country_code>gb</country_code>
|
||||||
|
</place>
|
||||||
|
</searchresults>
|
||||||
|
```
|
||||||
|
|
||||||
|
The attributes of the outer `searchresults` or `lookupresults` element return
|
||||||
|
generic information about the query:
|
||||||
|
|
||||||
|
* `timestamp` - UTC time when the response was sent
|
||||||
|
* `attribution` - OSM licensing information
|
||||||
|
* `querystring` - original query
|
||||||
|
* `polygon` - true when extra geometry information was requested
|
||||||
|
* `exclude_place_ids` - IDs of places that should be ignored in a follow-up request
|
||||||
|
* `more_url` - search call that will yield additional results for the query
|
||||||
|
just sent
|
||||||
|
|
||||||
|
The place information can be found in the `place` elements, of which there may
|
||||||
|
be more than one. The attributes of that element contain:
|
||||||
|
|
||||||
|
* `place_id` - reference to the Nominatim internal database ID
|
||||||
|
* `osm_type`, `osm_id` - reference to the OSM object
|
||||||
|
* `ref` - content of `ref` tag if it exists
|
||||||
|
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
||||||
|
* `boundingbox` - comma-separated list of corner coordinates
|
||||||
|
* `place_rank` - class search rank
|
||||||
|
* `display_name` - full comma-separated address
|
||||||
|
* `class`, `type` - key and value of the main OSM tag
|
||||||
|
* `importance` - computed importance rank
|
||||||
|
* `icon` - link to class icon (if available)
|
||||||
|
|
||||||
|
When `addressdetails=1` is requested, the localised address parts appear
|
||||||
|
as subelements with the type of the address part.
|
||||||
|
|
||||||
|
Additional information requested with `extratags=1` and `namedetails=1` can
|
||||||
|
be found in extra elements as sub-element of each place.
|
||||||
|
|
||||||
14
docs/api/Overview.md
Normal file
14
docs/api/Overview.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
### Nominatim API
|
||||||
|
|
||||||
|
Nominatim indexes named (or numbered) features within the OpenStreetMap (OSM) dataset and a subset of other unnamed features (pubs, hotels, churches, etc).
|
||||||
|
|
||||||
|
Its API has the following endpoints for querying the data:
|
||||||
|
|
||||||
|
* __[/search](Search.md)__ - search OSM objects by name or type
|
||||||
|
* __[/reverse](Reverse.md)__ - search OSM object by their location
|
||||||
|
* __[/lookup](Lookup.md)__ - look up address details for OSM objects by their ID
|
||||||
|
* __/status__ - query the status of the server
|
||||||
|
* __/deletable__ - list objects that have been deleted in OSM but are held
|
||||||
|
back in Nominatim in case the deletion was accidental
|
||||||
|
* __/polygons__ - list of broken polygons detected by Nominatim
|
||||||
|
* __[/details](Details.md)__ - show internal details for an object (for debugging only)
|
||||||
271
docs/api/Reverse.md
Normal file
271
docs/api/Reverse.md
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
# Reverse Geocoding
|
||||||
|
|
||||||
|
Reverse geocoding generates an address from a latitude and longitude or from
|
||||||
|
an OSM object.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
The main format of the reverse API is
|
||||||
|
|
||||||
|
```
|
||||||
|
https://nominatim.openstreetmap.org/reverse?<query>
|
||||||
|
```
|
||||||
|
|
||||||
|
There are two ways how the requested location can be specified:
|
||||||
|
|
||||||
|
* `lat=<value>` `lon=<value>`
|
||||||
|
|
||||||
|
A geographic location to generate an address for. The coordiantes must be
|
||||||
|
in WGS84 format.
|
||||||
|
|
||||||
|
* `osm_type=[N|W|R]` `osm_id=<value>`
|
||||||
|
|
||||||
|
A specific OSM node(N), way(W) or relation(R) to return an address for.
|
||||||
|
|
||||||
|
In both cases exactly one object is returned. The two input paramters cannot
|
||||||
|
be used at the same time. Both accept the additional optional parameters listed
|
||||||
|
below.
|
||||||
|
|
||||||
|
### Output format
|
||||||
|
|
||||||
|
* `format=[xml|json|jsonv2|geojson|geocodejson]`
|
||||||
|
|
||||||
|
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
||||||
|
|
||||||
|
* `json_callback=<string>`
|
||||||
|
|
||||||
|
Wrap json output in a callback function ([JSONP](https://en.wikipedia.org/wiki/JSONP)) i.e. `<string>(<json>)`.
|
||||||
|
Only has an effect for JSON output formats.
|
||||||
|
|
||||||
|
### Output details
|
||||||
|
|
||||||
|
* `addressdetails=[0|1]`
|
||||||
|
|
||||||
|
Include a breakdown of the address into elements. (Default: 1)
|
||||||
|
|
||||||
|
|
||||||
|
* `extratags=[0|1]`
|
||||||
|
|
||||||
|
Include additional information in the result if available,
|
||||||
|
e.g. wikipedia link, opening hours. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
* `namedetails=[0|1]`
|
||||||
|
|
||||||
|
Include a list of alternative names in the results. These may include
|
||||||
|
language variants, references, operator and brand. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
### Language of results
|
||||||
|
|
||||||
|
* `accept-language=<browser language string>`
|
||||||
|
|
||||||
|
Preferred language order for showing search results, overrides the value
|
||||||
|
specified in the "Accept-Language" HTTP header.
|
||||||
|
Either use a standard RFC2616 accept-language string or a simple
|
||||||
|
comma-separated list of language codes.
|
||||||
|
|
||||||
|
### Result limitation
|
||||||
|
|
||||||
|
* `zoom=[0-18]`
|
||||||
|
|
||||||
|
Level of detail required for the address. Default: 18. This is a number that corresponds
|
||||||
|
roughly to the zoom level used in map frameworks like Leaflet.js, Openlayers etc.
|
||||||
|
In terms of address details the zoom levels are as follows:
|
||||||
|
|
||||||
|
zoom | address detail
|
||||||
|
-----|---------------
|
||||||
|
3 | country
|
||||||
|
5 | state
|
||||||
|
8 | county
|
||||||
|
10 | city
|
||||||
|
14 | suburb
|
||||||
|
16 | street
|
||||||
|
18 | building
|
||||||
|
|
||||||
|
|
||||||
|
### Polygon output
|
||||||
|
|
||||||
|
* `polygon_geojson=1`
|
||||||
|
* `polygon_kml=1`
|
||||||
|
* `polygon_svg=1`
|
||||||
|
* `polygon_text=1`
|
||||||
|
|
||||||
|
Output geometry of results as a GeoJSON, KML, SVG or WKT. Only one of these
|
||||||
|
options can be used at a time. (Default: 0)
|
||||||
|
|
||||||
|
* `polygon_threshold=0.0`
|
||||||
|
|
||||||
|
Simplify the output geometry before returning. The parameter is the
|
||||||
|
tolerance in degrees with which the geometry may differ from the original
|
||||||
|
geometry. Topology is preserved in the result. (Default: 0.0)
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
* `email=<valid email address>`
|
||||||
|
|
||||||
|
If you are making large numbers of request please include an appropriate email
|
||||||
|
address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details.
|
||||||
|
|
||||||
|
|
||||||
|
* `debug=[0|1]`
|
||||||
|
|
||||||
|
Output assorted developer debug information. Data on internals of Nominatim's
|
||||||
|
"Search Loop" logic, and SQL queries. The output is (rough) HTML format.
|
||||||
|
This overrides the specified machine readable format. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
* [https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1](https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<reversegeocode timestamp="Fri, 06 Nov 09 16:33:54 +0000" querystring="...">
|
||||||
|
<result place_id="1620612" osm_type="node" osm_id="452010817">
|
||||||
|
135, Pilkington Avenue, Wylde Green, City of Birmingham, West Midlands (county), B72, United Kingdom
|
||||||
|
</result>
|
||||||
|
<addressparts>
|
||||||
|
<house_number>135</house_number>
|
||||||
|
<road>Pilkington Avenue</road>
|
||||||
|
<village>Wylde Green</village>
|
||||||
|
<town>Sutton Coldfield</town>
|
||||||
|
<city>City of Birmingham</city>
|
||||||
|
<county>West Midlands (county)</county>
|
||||||
|
<postcode>B72</postcode>
|
||||||
|
<country>United Kingdom</country>
|
||||||
|
<country_code>gb</country_code>
|
||||||
|
</addressparts>
|
||||||
|
</reversegeocode>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Example with `format=jsonv2`
|
||||||
|
|
||||||
|
* [https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521](https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"place_id":"134140761",
|
||||||
|
"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http:\/\/www.openstreetmap.org\/copyright",
|
||||||
|
"osm_type":"way",
|
||||||
|
"osm_id":"280940520",
|
||||||
|
"lat":"-34.4391708",
|
||||||
|
"lon":"-58.7064573",
|
||||||
|
"place_rank":"26",
|
||||||
|
"category":"highway",
|
||||||
|
"type":"motorway",
|
||||||
|
"importance":"0.1",
|
||||||
|
"addresstype":"road",
|
||||||
|
"display_name":"Autopista Pedro Eugenio Aramburu, El Triángulo, Partido de Malvinas Argentinas, Buenos Aires, 1.619, Argentina",
|
||||||
|
"name":"Autopista Pedro Eugenio Aramburu",
|
||||||
|
"address":{
|
||||||
|
"road":"Autopista Pedro Eugenio Aramburu",
|
||||||
|
"village":"El Triángulo",
|
||||||
|
"state_district":"Partido de Malvinas Argentinas",
|
||||||
|
"state":"Buenos Aires",
|
||||||
|
"postcode":"1.619",
|
||||||
|
"country":"Argentina",
|
||||||
|
"country_code":"ar"
|
||||||
|
},
|
||||||
|
"boundingbox":["-34.44159","-34.4370994","-58.7086067","-58.7044712"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Example with `format=geojson`
|
||||||
|
|
||||||
|
* [https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989](https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": {
|
||||||
|
"place_id": "18512203",
|
||||||
|
"osm_type": "node",
|
||||||
|
"osm_id": "1704756187",
|
||||||
|
"place_rank": "30",
|
||||||
|
"category": "place",
|
||||||
|
"type": "house",
|
||||||
|
"importance": "0",
|
||||||
|
"addresstype": "place",
|
||||||
|
"name": null,
|
||||||
|
"display_name": "71, Via Guglielmo Marconi, Saragozza-Porto, Bologna, BO, Emilia-Romagna, 40122, Italy",
|
||||||
|
"address": {
|
||||||
|
"house_number": "71",
|
||||||
|
"road": "Via Guglielmo Marconi",
|
||||||
|
"suburb": "Saragozza-Porto",
|
||||||
|
"city": "Bologna",
|
||||||
|
"county": "BO",
|
||||||
|
"state": "Emilia-Romagna",
|
||||||
|
"postcode": "40122",
|
||||||
|
"country": "Italy",
|
||||||
|
"country_code": "it"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bbox": [
|
||||||
|
11.3397676,
|
||||||
|
44.5014307,
|
||||||
|
11.3399676,
|
||||||
|
44.5016307
|
||||||
|
],
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
11.3398676,
|
||||||
|
44.5015307
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Example with `format=geocodejson`
|
||||||
|
|
||||||
|
[https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663](https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"geocoding": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||||
|
"licence": "ODbL",
|
||||||
|
"query": "60.229917843587,11.16630979382"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": {
|
||||||
|
"geocoding": {
|
||||||
|
"place_id": "42700574",
|
||||||
|
"osm_type": "node",
|
||||||
|
"osm_id": "3110596255",
|
||||||
|
"type": "house",
|
||||||
|
"accuracy": 0,
|
||||||
|
"label": "1, Løvenbergvegen, Mogreina, Ullensaker, Akershus, 2054, Norway",
|
||||||
|
"name": null,
|
||||||
|
"housenumber": "1",
|
||||||
|
"street": "Løvenbergvegen",
|
||||||
|
"postcode": "2054",
|
||||||
|
"county": "Akershus",
|
||||||
|
"country": "Norway",
|
||||||
|
"admin": {
|
||||||
|
"level7": "Ullensaker",
|
||||||
|
"level4": "Akershus",
|
||||||
|
"level2": "Norway"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
11.1658572,
|
||||||
|
60.2301296
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
349
docs/api/Search.md
Normal file
349
docs/api/Search.md
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
# Search queries
|
||||||
|
|
||||||
|
The search API allows to look up a location from a textual description.
|
||||||
|
Nominatim supports structured as well as free-form search queries.
|
||||||
|
|
||||||
|
The search query may also contain
|
||||||
|
[special phrases](https://wiki.openstreetmap.org/wiki/Nominatim/Special_Phrases)
|
||||||
|
which are translated into specific OpenStreetMap (OSM) tags (e.g. Pub => `amenity=pub`).
|
||||||
|
Note that this only limits the items to be found, it's not suited to return complete
|
||||||
|
lists of OSM objects of a specific type. For those use [Overpass API](https://overpass-api.de/).
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
The search API has the following two formats:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://nominatim.openstreetmap.org/search/<query>?<params>
|
||||||
|
```
|
||||||
|
|
||||||
|
This format only accepts a free-form query string where the
|
||||||
|
parts of the query are separated by slashes.
|
||||||
|
|
||||||
|
```
|
||||||
|
https://nominatim.openstreetmap.org/search?<params>
|
||||||
|
```
|
||||||
|
|
||||||
|
In this form, the query may be given through two different sets of parameters:
|
||||||
|
|
||||||
|
* `q=<query>`
|
||||||
|
|
||||||
|
Free-form query string to search for.
|
||||||
|
Free-form queries are processed first left-to-right and then right-to-left if that fails. So you may search for
|
||||||
|
[pilkington avenue, birmingham](//nominatim.openstreetmap.org/search?q=pilkington+avenue,birmingham) as well as for
|
||||||
|
[birmingham, pilkington avenue](//nominatim.openstreetmap.org/search?q=birmingham,+pilkington+avenue).
|
||||||
|
Commas are optional, but improve performance by reducing the complexity of the search.
|
||||||
|
|
||||||
|
|
||||||
|
* `street=<housenumber> <streetname>`
|
||||||
|
* `city=<city>`
|
||||||
|
* `county=<county>`
|
||||||
|
* `state=<state>`
|
||||||
|
* `country=<country>`
|
||||||
|
* `postalcode=<postalcode>`
|
||||||
|
|
||||||
|
Alternative query string format split into several parameters for structured requests.
|
||||||
|
Structured requests are faster but are less robust against alternative
|
||||||
|
OSM tagging schemas. **Do not combine with** `q=<query>` **parameter**.
|
||||||
|
|
||||||
|
All three query forms accept the additional paramters listed below.
|
||||||
|
|
||||||
|
### Output format
|
||||||
|
|
||||||
|
* `format=[html|xml|json|jsonv2|geojson|geocodejson]`
|
||||||
|
|
||||||
|
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
||||||
|
|
||||||
|
* `json_callback=<string>`
|
||||||
|
|
||||||
|
Wrap json output in a callback function ([JSONP](https://en.wikipedia.org/wiki/JSONP)) i.e. `<string>(<json>)`.
|
||||||
|
Only has an effect for JSON output formats.
|
||||||
|
|
||||||
|
### Output details
|
||||||
|
|
||||||
|
* `addressdetails=[0|1]`
|
||||||
|
|
||||||
|
Include a breakdown of the address into elements. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
* `extratags=[0|1]`
|
||||||
|
|
||||||
|
Include additional information in the result if available,
|
||||||
|
e.g. wikipedia link, opening hours. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
* `namedetails=[0|1]`
|
||||||
|
|
||||||
|
Include a list of alternative names in the results. These may include
|
||||||
|
language variants, references, operator and brand. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
### Language of results
|
||||||
|
|
||||||
|
* `accept-language=<browser language string>`
|
||||||
|
|
||||||
|
Preferred language order for showing search results, overrides the value
|
||||||
|
specified in the ["Accept-Language" HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language).
|
||||||
|
Either use a standard RFC2616 accept-language string or a simple
|
||||||
|
comma-separated list of language codes.
|
||||||
|
|
||||||
|
### Result limitation
|
||||||
|
|
||||||
|
* `countrycodes=<countrycode>[,<countrycode>][,<countrycode>]...`
|
||||||
|
|
||||||
|
Limit search results to one or more countries. `<countrycode>` must be the
|
||||||
|
ISO 3166-1alpha2 code, e.g. `gb` for the United Kingdom, `de` for Germany.
|
||||||
|
|
||||||
|
|
||||||
|
* `exclude_place_ids=<place_id,[place_id],[place_id]`
|
||||||
|
|
||||||
|
If you do not want certain OSM objects to appear in the search
|
||||||
|
result, give a comma separated list of the `place_id`s you want to skip.
|
||||||
|
This can be used to broaden search results. For example, if a previous
|
||||||
|
query only returned a few results, then including those here would cause
|
||||||
|
the search to return other, less accurate, matches (if possible).
|
||||||
|
|
||||||
|
|
||||||
|
* `limit=<integer>`
|
||||||
|
|
||||||
|
Limit the number of returned results. (Default: 10, Maximum: 50)
|
||||||
|
|
||||||
|
|
||||||
|
* `viewbox=<x1>,<y1>,<x2>,<y2>`
|
||||||
|
|
||||||
|
The preferred area to find search results. Any two corner points of the box
|
||||||
|
are accepted in any order as long as they span a real box.
|
||||||
|
|
||||||
|
|
||||||
|
* `bounded=[0|1]`
|
||||||
|
|
||||||
|
When a viewbox is given, restrict the result to items contained with that
|
||||||
|
viewbox (see above). When `viewbox` and `bounded=1` are given, an amenity
|
||||||
|
only search is allowed. In this case, give the special keyword for the
|
||||||
|
amenity in square brackets, e.g. `[pub]`. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
### Polygon output
|
||||||
|
|
||||||
|
* `polygon_geojson=1`
|
||||||
|
* `polygon_kml=1`
|
||||||
|
* `polygon_svg=1`
|
||||||
|
* `polygon_text=1`
|
||||||
|
|
||||||
|
Output geometry of results as a GeoJSON, KML, SVG or WKT. Only one of these
|
||||||
|
options can be used at a time. (Default: 0)
|
||||||
|
|
||||||
|
* `polygon_threshold=0.0`
|
||||||
|
|
||||||
|
Simplify the output geometry before returning. The parameter is the
|
||||||
|
tolerance in degrees with which the geometry may differ from the original
|
||||||
|
geometry. Topology is preserved in the result. (Default: 0.0)
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
* `email=<valid email address>`
|
||||||
|
|
||||||
|
If you are making large numbers of request please include an appropriate email
|
||||||
|
address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details.
|
||||||
|
|
||||||
|
* `dedupe=[0|1]`
|
||||||
|
|
||||||
|
Sometimes you have several objects in OSM identifying the same place or
|
||||||
|
object in reality. The simplest case is a street being split in many
|
||||||
|
different OSM ways due to different characteristics. Nominatim will
|
||||||
|
attempt to detect such duplicates and only return one match unless
|
||||||
|
this parameter is set to 0. (Default: 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* `debug=[0|1]`
|
||||||
|
|
||||||
|
Output assorted developer debug information. Data on internals of Nominatim's
|
||||||
|
"Search Loop" logic, and SQL queries. The output is (rough) HTML format.
|
||||||
|
This overrides the specified machine readable format. (Default: 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
|
||||||
|
##### XML with polygon points
|
||||||
|
|
||||||
|
* [https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon=1&addressdetails=1](https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon=1&addressdetails=1)
|
||||||
|
* [https://nominatim.openstreetmap.org/search/gb/birmingham/pilkington%20avenue/135?format=xml&polygon=1&addressdetails=1](https://nominatim.openstreetmap.org/search/gb/birmingham/pilkington%20avenue/135?format=xml&polygon=1&addressdetails=1)
|
||||||
|
* [https://nominatim.openstreetmap.org/search/135%20pilkington%20avenue,%20birmingham?format=xml&polygon=1&addressdetails=1](https://nominatim.openstreetmap.org/search/135%20pilkington%20avenue,%20birmingham?format=xml&polygon=1&addressdetails=1)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<searchresults timestamp="Sat, 07 Nov 09 14:42:10 +0000" querystring="135 pilkington, avenue birmingham" polygon="true">
|
||||||
|
<place
|
||||||
|
place_id="1620612" osm_type="node" osm_id="452010817"
|
||||||
|
boundingbox="52.548641204834,52.5488433837891,-1.81612110137939,-1.81592094898224"
|
||||||
|
polygonpoints="[['-1.81592098644987','52.5487429714954'],['-1.81592290792183','52.5487234624632'],...]"
|
||||||
|
lat="52.5487429714954" lon="-1.81602098644987"
|
||||||
|
display_name="135, Pilkington Avenue, Wylde Green, City of Birmingham, West Midlands (county), B72, United Kingdom"
|
||||||
|
class="place" type="house">
|
||||||
|
<house_number>135</house_number>
|
||||||
|
<road>Pilkington Avenue</road>
|
||||||
|
<village>Wylde Green</village>
|
||||||
|
<town>Sutton Coldfield</town>
|
||||||
|
<city>City of Birmingham</city>
|
||||||
|
<county>West Midlands (county)</county>
|
||||||
|
<postcode>B72</postcode>
|
||||||
|
<country>United Kingdom</country>
|
||||||
|
<country_code>gb</country_code>
|
||||||
|
</place>
|
||||||
|
</searchresults>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### JSON with SVG polygon
|
||||||
|
|
||||||
|
[https://nominatim.openstreetmap.org/search/Unter%20den%20Linden%201%20Berlin?format=json&addressdetails=1&limit=1&polygon_svg=1](https://nominatim.openstreetmap.org/search/Unter%20den%20Linden%201%20Berlin?format=json&addressdetails=1&limit=1&polygon_svg=1)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"address": {
|
||||||
|
"city": "Berlin",
|
||||||
|
"city_district": "Mitte",
|
||||||
|
"construction": "Unter den Linden",
|
||||||
|
"continent": "European Union",
|
||||||
|
"country": "Deutschland",
|
||||||
|
"country_code": "de",
|
||||||
|
"house_number": "1",
|
||||||
|
"neighbourhood": "Scheunenviertel",
|
||||||
|
"postcode": "10117",
|
||||||
|
"public_building": "Kommandantenhaus",
|
||||||
|
"state": "Berlin",
|
||||||
|
"suburb": "Mitte"
|
||||||
|
},
|
||||||
|
"boundingbox": [
|
||||||
|
"52.5170783996582",
|
||||||
|
"52.5173187255859",
|
||||||
|
"13.3975105285645",
|
||||||
|
"13.3981599807739"
|
||||||
|
],
|
||||||
|
"class": "amenity",
|
||||||
|
"display_name": "Kommandantenhaus, 1, Unter den Linden, Scheunenviertel, Mitte, Berlin, 10117, Deutschland, European Union",
|
||||||
|
"importance": 0.73606775332943,
|
||||||
|
"lat": "52.51719785",
|
||||||
|
"licence": "Data \u00a9 OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright",
|
||||||
|
"lon": "13.3978352028938",
|
||||||
|
"osm_id": "15976890",
|
||||||
|
"osm_type": "way",
|
||||||
|
"place_id": "30848715",
|
||||||
|
"svg": "M 13.397511 -52.517283599999999 L 13.397829400000001 -52.517299800000004 13.398131599999999 -52.517315099999998 13.398159400000001 -52.517112099999999 13.3975388 -52.517080700000001 Z",
|
||||||
|
"type": "public_building"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### JSON with address details
|
||||||
|
|
||||||
|
[https://nominatim.openstreetmap.org/?format=json&addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1](https://nominatim.openstreetmap.org/?format=json&addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"address": {
|
||||||
|
"bakery": "B\u00e4cker Kamps",
|
||||||
|
"city_district": "Mitte",
|
||||||
|
"continent": "European Union",
|
||||||
|
"country": "Deutschland",
|
||||||
|
"country_code": "de",
|
||||||
|
"footway": "Bahnsteig U6",
|
||||||
|
"neighbourhood": "Sprengelkiez",
|
||||||
|
"postcode": "13353",
|
||||||
|
"state": "Berlin",
|
||||||
|
"suburb": "Wedding"
|
||||||
|
},
|
||||||
|
"boundingbox": [
|
||||||
|
"52.5460929870605",
|
||||||
|
"52.5460968017578",
|
||||||
|
"13.3591794967651",
|
||||||
|
"13.3591804504395"
|
||||||
|
],
|
||||||
|
"class": "shop",
|
||||||
|
"display_name": "B\u00e4cker Kamps, Bahnsteig U6, Sprengelkiez, Wedding, Mitte, Berlin, 13353, Deutschland, European Union",
|
||||||
|
"icon": "https://nominatim.openstreetmap.org/images/mapicons/shopping_bakery.p.20.png",
|
||||||
|
"importance": 0.201,
|
||||||
|
"lat": "52.5460941",
|
||||||
|
"licence": "Data \u00a9 OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright",
|
||||||
|
"lon": "13.35918",
|
||||||
|
"osm_id": "317179427",
|
||||||
|
"osm_type": "node",
|
||||||
|
"place_id": "1453068",
|
||||||
|
"type": "bakery"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### GeoJSON
|
||||||
|
|
||||||
|
[https://nominatim.openstreetmap.org/search?q=17+Strada+Pictor+Alexandru+Romano%2C+Bukarest&format=geojson](https://nominatim.openstreetmap.org/search?q=17+Strada+Pictor+Alexandru+Romano%2C+Bukarest&format=geojson)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": {
|
||||||
|
"place_id": "35811445",
|
||||||
|
"osm_type": "node",
|
||||||
|
"osm_id": "2846295644",
|
||||||
|
"display_name": "17, Strada Pictor Alexandru Romano, Bukarest, Bucharest, Sector 2, Bucharest, 023964, Romania",
|
||||||
|
"place_rank": "30",
|
||||||
|
"category": "place",
|
||||||
|
"type": "house",
|
||||||
|
"importance": 0.62025
|
||||||
|
},
|
||||||
|
"bbox": [
|
||||||
|
26.1156689,
|
||||||
|
44.4354754,
|
||||||
|
26.1157689,
|
||||||
|
44.4355754
|
||||||
|
],
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
26.1157189,
|
||||||
|
44.4355254
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### GeocodeJSON
|
||||||
|
|
||||||
|
[https://nominatim.openstreetmap.org/search?q=%CE%91%CE%B3%CE%AF%CE%B1+%CE%A4%CF%81%CE%B9%CE%AC%CE%B4%CE%B1%2C+%CE%91%CE%B4%CF%89%CE%BD%CE%B9%CE%B4%CE%BF%CF%82%2C+Athens%2C+Greece&format=geocodejson](https://nominatim.openstreetmap.org/search?q=%CE%91%CE%B3%CE%AF%CE%B1+%CE%A4%CF%81%CE%B9%CE%AC%CE%B4%CE%B1%2C+%CE%91%CE%B4%CF%89%CE%BD%CE%B9%CE%B4%CE%BF%CF%82%2C+Athens%2C+Greece&format=geocodejson)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"geocoding": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
||||||
|
"licence": "ODbL",
|
||||||
|
"query": "Αγία Τριάδα, Αδωνιδος, Athens, Greece"
|
||||||
|
},
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": {
|
||||||
|
"geocoding": {
|
||||||
|
"type": "place_of_worship",
|
||||||
|
"label": "Αγία Τριάδα, Αδωνιδος, Άγιος Νικόλαος, 5º Δημοτικό Διαμέρισμα Αθηνών, Athens, Municipality of Athens, Regional Unit of Central Athens, Region of Attica, Attica, 11472, Greece",
|
||||||
|
"name": "Αγία Τριάδα",
|
||||||
|
"admin": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
23.72949633941,
|
||||||
|
38.0051697
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
24
docs/develop/overview.md
Normal file
24
docs/develop/overview.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Basic Architecture
|
||||||
|
|
||||||
|
Nominatim provides geocoding based on OpenStreetMap data. It uses a Postgresql
|
||||||
|
database as a backend for storing the data.
|
||||||
|
|
||||||
|
There are three basic parts to Nominatim's architecture: the data import,
|
||||||
|
the address computation and the search frontend.
|
||||||
|
|
||||||
|
The __data import__ stage reads the raw OSM data and extracts all information
|
||||||
|
that is useful for geocoding. This part is done by osm2pgsql, the same tool
|
||||||
|
that can also be used to import a rendering database. It uses the special
|
||||||
|
gazetteer output plugin in `osm2pgsql/output-gazetter.[ch]pp`. The result of
|
||||||
|
the import can be found in the database table `place`.
|
||||||
|
|
||||||
|
The __address computation__ or __indexing__ stage takes the data from `place`
|
||||||
|
and adds additional information needed for geocoding. It ranks the places by
|
||||||
|
importance, links objects that belong together and computes addresses and
|
||||||
|
the search index. Most of this work is done in Pl/pqSQL via database triggers
|
||||||
|
and can be found in the file `sql/functions.sql`.
|
||||||
|
|
||||||
|
The __search frontend__ implements the actual API. It takes queries for
|
||||||
|
search and reverse geocoding queries from the user, looks up the data and
|
||||||
|
returns the results in the requested format. This part is written in PHP
|
||||||
|
and can be found in the `lib/` and `website/` directories.
|
||||||
3
docs/extra.css
Normal file
3
docs/extra.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.toctree-l3 {
|
||||||
|
display: none!important
|
||||||
|
}
|
||||||
@@ -1 +1,8 @@
|
|||||||
Nominatim (from the Latin, 'by name') is a tool to search OSM data by name and address and to generate synthetic addresses of OSM points (reverse geocoding).
|
Nominatim (from the Latin, 'by name') is a tool to search OSM data by name and address and to generate synthetic addresses of OSM points (reverse geocoding).
|
||||||
|
|
||||||
|
This guide comes in three parts:
|
||||||
|
|
||||||
|
* __[API reference](api/Overview.md)__ for users of Nominatim
|
||||||
|
* __[Administration Guide](admin/Installation.md)__ for those who want
|
||||||
|
to install their own Nominatim server
|
||||||
|
* __[Developer's Guide](develop/overview.md)__ for developers of the software
|
||||||
|
|||||||
@@ -5,14 +5,28 @@ site_url: http://nominatim.org
|
|||||||
repo_url: https://github.com/openstreetmap/Nominatim
|
repo_url: https://github.com/openstreetmap/Nominatim
|
||||||
pages:
|
pages:
|
||||||
- 'Introduction' : 'index.md'
|
- 'Introduction' : 'index.md'
|
||||||
|
- 'API Reference':
|
||||||
|
- 'Overview': 'api/Overview.md'
|
||||||
|
- 'Search': 'api/Search.md'
|
||||||
|
- 'Reverse': 'api/Reverse.md'
|
||||||
|
- 'Address Lookup': 'api/Lookup.md'
|
||||||
|
- 'Details' : 'api/Details.md'
|
||||||
|
- 'Place Output Formats': 'api/Output.md'
|
||||||
|
- 'FAQ': 'api/Faq.md'
|
||||||
- 'Administration Guide':
|
- 'Administration Guide':
|
||||||
- 'Basic Installation': 'admin/Installation.md'
|
- 'Basic Installation': 'admin/Installation.md'
|
||||||
- 'Importing and Updating' : 'admin/Import-and-Update.md'
|
- 'Importing and Updating' : 'admin/Import-and-Update.md'
|
||||||
- 'Migration from older Versions' : 'admin/Migration.md'
|
- 'Migration from older Versions' : 'admin/Migration.md'
|
||||||
- 'Troubleshooting' : 'admin/Faq.md'
|
- 'Troubleshooting' : 'admin/Faq.md'
|
||||||
|
- 'Developers Guide':
|
||||||
|
- 'Overview' : 'develop/overview.md'
|
||||||
- 'Appendix':
|
- 'Appendix':
|
||||||
- 'Installation on CentOS 7' : 'appendix/Install-on-Centos-7.md'
|
- 'Installation on CentOS 7' : 'appendix/Install-on-Centos-7.md'
|
||||||
- 'Installation on Ubuntu 16' : 'appendix/Install-on-Ubuntu-16.md'
|
- 'Installation on Ubuntu 16' : 'appendix/Install-on-Ubuntu-16.md'
|
||||||
|
- 'Installation on Ubuntu 18' : 'appendix/Install-on-Ubuntu-18.md'
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- codehilite:
|
- codehilite:
|
||||||
use_pygments: False
|
use_pygments: False
|
||||||
|
- toc:
|
||||||
|
permalink:
|
||||||
|
extra_css: [extra.css]
|
||||||
|
|||||||
122
lib/AddressDetails.php
Normal file
122
lib/AddressDetails.php
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim;
|
||||||
|
|
||||||
|
require_once(CONST_BasePath.'/lib/ClassTypes.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detailed list of address parts for a single result
|
||||||
|
*/
|
||||||
|
class AddressDetails
|
||||||
|
{
|
||||||
|
private $aAddressLines;
|
||||||
|
|
||||||
|
public function __construct(&$oDB, $iPlaceID, $sHousenumber, $mLangPref)
|
||||||
|
{
|
||||||
|
if (is_array($mLangPref)) {
|
||||||
|
$mLangPref = 'ARRAY['.join(',', array_map('getDBQuoted', $mLangPref)).']';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$sHousenumber) {
|
||||||
|
$sHousenumber = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sSQL = 'SELECT *,';
|
||||||
|
$sSQL .= ' get_name_by_language(name,'.$mLangPref.') as localname';
|
||||||
|
$sSQL .= ' FROM get_addressdata('.$iPlaceID.','.$sHousenumber.')';
|
||||||
|
$sSQL .= ' ORDER BY rank_address desc,isaddress DESC';
|
||||||
|
|
||||||
|
$this->aAddressLines = chksql($oDB->getAll($sSQL));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function isAddress($aLine)
|
||||||
|
{
|
||||||
|
return $aLine['isaddress'] == 't' || $aLine['type'] == 'country_code';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddressDetails($bAll = false)
|
||||||
|
{
|
||||||
|
if ($bAll) {
|
||||||
|
return $this->aAddressLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_filter($this->aAddressLines, 'AddressDetails::isAddress');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLocaleAddress()
|
||||||
|
{
|
||||||
|
$aParts = array();
|
||||||
|
$sPrevResult = '';
|
||||||
|
|
||||||
|
foreach ($this->aAddressLines as $aLine) {
|
||||||
|
if ($aLine['isaddress'] == 't' && $sPrevResult != $aLine['localname']) {
|
||||||
|
$sPrevResult = $aLine['localname'];
|
||||||
|
$aParts[] = $sPrevResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return join(', ', $aParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddressNames()
|
||||||
|
{
|
||||||
|
$aAddress = array();
|
||||||
|
$aFallback = array();
|
||||||
|
|
||||||
|
foreach ($this->aAddressLines as $aLine) {
|
||||||
|
if (!self::isAddress($aLine)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bFallback = false;
|
||||||
|
$aTypeLabel = ClassTypes\getInfo($aLine);
|
||||||
|
|
||||||
|
if ($aTypeLabel === false) {
|
||||||
|
$aTypeLabel = ClassTypes\getFallbackInfo($aLine);
|
||||||
|
$bFallback = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sName = false;
|
||||||
|
if (isset($aLine['localname']) && $aLine['localname']) {
|
||||||
|
$sName = $aLine['localname'];
|
||||||
|
} elseif (isset($aLine['housenumber']) && $aLine['housenumber']) {
|
||||||
|
$sName = $aLine['housenumber'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sName) {
|
||||||
|
$sTypeLabel = strtolower(isset($aTypeLabel['simplelabel']) ? $aTypeLabel['simplelabel'] : $aTypeLabel['label']);
|
||||||
|
$sTypeLabel = str_replace(' ', '_', $sTypeLabel);
|
||||||
|
if (!isset($aAddress[$sTypeLabel])
|
||||||
|
|| isset($aFallback[$sTypeLabel])
|
||||||
|
|| $aLine['class'] == 'place'
|
||||||
|
) {
|
||||||
|
$aAddress[$sTypeLabel] = $sName;
|
||||||
|
if ($bFallback) {
|
||||||
|
$aFallback[$sTypeLabel] = $bFallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $aAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAdminLevels()
|
||||||
|
{
|
||||||
|
$aAddress = array();
|
||||||
|
foreach ($this->aAddressLines as $aLine) {
|
||||||
|
if (self::isAddress($aLine)
|
||||||
|
&& isset($aLine['admin_level'])
|
||||||
|
&& $aLine['admin_level'] < 15
|
||||||
|
&& !isset($aAddress['level'.$aLine['admin_level']])
|
||||||
|
) {
|
||||||
|
$aAddress['level'.$aLine['admin_level']] = $aLine['localname'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $aAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return $this->aAddressLines;
|
||||||
|
}
|
||||||
|
}
|
||||||
374
lib/ClassTypes.php
Normal file
374
lib/ClassTypes.php
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim\ClassTypes;
|
||||||
|
|
||||||
|
function getInfo($aPlace)
|
||||||
|
{
|
||||||
|
$aClassType = getList();
|
||||||
|
|
||||||
|
if (isset($aPlace['admin_level'])) {
|
||||||
|
$sName = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
||||||
|
if (isset($aClassType[$sName])) {
|
||||||
|
return $aClassType[$sName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sName = $aPlace['class'].':'.$aPlace['type'];
|
||||||
|
if (isset($aClassType[$sName])) {
|
||||||
|
return $aClassType[$sName];
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFallbackInfo($aPlace)
|
||||||
|
{
|
||||||
|
$aClassType = getList();
|
||||||
|
|
||||||
|
$sFallback = 'boundary:administrative:'.((int)($aPlace['rank_address']/2));
|
||||||
|
if (isset($aClassType[$sFallback])) {
|
||||||
|
return $aClassType[$sFallback];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array('simplelabel' => 'address'.$aPlace['rank_address']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProperty($aPlace, $sProp, $mDefault = false)
|
||||||
|
{
|
||||||
|
$aClassType = getList();
|
||||||
|
|
||||||
|
if (isset($aPlace['admin_level'])) {
|
||||||
|
$sName = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
||||||
|
if (isset($aClassType[$sName]) && isset($aClassType[$sName][$sProp])) {
|
||||||
|
return $aClassType[$sName][$sProp];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sName = $aPlace['class'].':'.$aPlace['type'];
|
||||||
|
if (isset($aClassType[$sName]) && isset($aClassType[$sName][$sProp])) {
|
||||||
|
return $aClassType[$sName][$sProp];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getListWithImportance()
|
||||||
|
{
|
||||||
|
static $aOrders = null;
|
||||||
|
if ($aOrders === null) {
|
||||||
|
$aOrders = getList();
|
||||||
|
$i = 1;
|
||||||
|
foreach ($aOrders as $sID => $a) {
|
||||||
|
$aOrders[$sID]['importance'] = $i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aOrders;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getList()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'boundary:administrative:1' => array('label' => 'Continent', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'boundary:administrative:2' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'place:country' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 6, 'defdiameter' => 15),
|
||||||
|
'boundary:administrative:3' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'boundary:administrative:4' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'place:state' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 5.12),
|
||||||
|
'boundary:administrative:5' => array('label' => 'State District', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'boundary:administrative:6' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'boundary:administrative:7' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'place:county' => array('label' => 'County', 'frequency' => 108, 'icon' => 'poi_boundary_administrative', 'defzoom' => 10, 'defdiameter' => 1.28),
|
||||||
|
'boundary:administrative:8' => array('label' => 'City', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'place:city' => array('label' => 'City', 'frequency' => 66, 'icon' => 'poi_place_city', 'defzoom' => 12, 'defdiameter' => 0.32),
|
||||||
|
'boundary:administrative:9' => array('label' => 'City District', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'boundary:administrative:10' => array('label' => 'Suburb', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'boundary:administrative:11' => array('label' => 'Neighbourhood', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'place:region' => array('label' => 'Region', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 0.04),
|
||||||
|
'place:island' => array('label' => 'Island', 'frequency' => 288, 'defzoom' => 11, 'defdiameter' => 0.64),
|
||||||
|
'boundary:administrative' => array('label' => 'Administrative', 'frequency' => 413, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'boundary:postal_code' => array('label' => 'Postcode', 'frequency' => 413, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
||||||
|
'place:town' => array('label' => 'Town', 'frequency' => 1497, 'icon' => 'poi_place_town', 'defzoom' => 14, 'defdiameter' => 0.08),
|
||||||
|
'place:village' => array('label' => 'Village', 'frequency' => 11230, 'icon' => 'poi_place_village', 'defzoom' => 15, 'defdiameter' => 0.04),
|
||||||
|
'place:hamlet' => array('label' => 'Hamlet', 'frequency' => 7075, 'icon' => 'poi_place_village', 'defzoom' => 15, 'defdiameter' => 0.04),
|
||||||
|
'place:suburb' => array('label' => 'Suburb', 'frequency' => 2528, 'icon' => 'poi_place_village', 'defdiameter' => 0.04),
|
||||||
|
'place:locality' => array('label' => 'Locality', 'frequency' => 4113, 'icon' => 'poi_place_village', 'defdiameter' => 0.02),
|
||||||
|
'landuse:farm' => array('label' => 'Farm', 'frequency' => 1201, 'defdiameter' => 0.02),
|
||||||
|
'place:farm' => array('label' => 'Farm', 'frequency' => 1162, 'defdiameter' => 0.02),
|
||||||
|
|
||||||
|
'highway:motorway_junction' => array('label' => 'Motorway Junction', 'frequency' => 1126, 'simplelabel' => 'Junction'),
|
||||||
|
'highway:motorway' => array('label' => 'Motorway', 'frequency' => 4627, 'simplelabel' => 'Road'),
|
||||||
|
'highway:trunk' => array('label' => 'Trunk', 'frequency' => 23084, 'simplelabel' => 'Road'),
|
||||||
|
'highway:primary' => array('label' => 'Primary', 'frequency' => 32138, 'simplelabel' => 'Road'),
|
||||||
|
'highway:secondary' => array('label' => 'Secondary', 'frequency' => 25807, 'simplelabel' => 'Road'),
|
||||||
|
'highway:tertiary' => array('label' => 'Tertiary', 'frequency' => 29829, 'simplelabel' => 'Road'),
|
||||||
|
'highway:residential' => array('label' => 'Residential', 'frequency' => 361498, 'simplelabel' => 'Road'),
|
||||||
|
'highway:unclassified' => array('label' => 'Unclassified', 'frequency' => 66441, 'simplelabel' => 'Road'),
|
||||||
|
'highway:living_street' => array('label' => 'Living Street', 'frequency' => 710, 'simplelabel' => 'Road'),
|
||||||
|
'highway:service' => array('label' => 'Service', 'frequency' => 9963, 'simplelabel' => 'Road'),
|
||||||
|
'highway:track' => array('label' => 'Track', 'frequency' => 2565, 'simplelabel' => 'Road'),
|
||||||
|
'highway:road' => array('label' => 'Road', 'frequency' => 591, 'simplelabel' => 'Road'),
|
||||||
|
'highway:byway' => array('label' => 'Byway', 'frequency' => 346, 'simplelabel' => 'Road'),
|
||||||
|
'highway:bridleway' => array('label' => 'Bridleway', 'frequency' => 1556),
|
||||||
|
'highway:cycleway' => array('label' => 'Cycleway', 'frequency' => 2419),
|
||||||
|
'highway:pedestrian' => array('label' => 'Pedestrian', 'frequency' => 2757),
|
||||||
|
'highway:footway' => array('label' => 'Footway', 'frequency' => 15008),
|
||||||
|
'highway:steps' => array('label' => 'Steps', 'frequency' => 444, 'simplelabel' => 'Footway'),
|
||||||
|
'highway:motorway_link' => array('label' => 'Motorway Link', 'frequency' => 795, 'simplelabel' => 'Road'),
|
||||||
|
'highway:trunk_link' => array('label' => 'Trunk Link', 'frequency' => 1258, 'simplelabel' => 'Road'),
|
||||||
|
'highway:primary_link' => array('label' => 'Primary Link', 'frequency' => 313, 'simplelabel' => 'Road'),
|
||||||
|
|
||||||
|
'landuse:industrial' => array('label' => 'Industrial', 'frequency' => 1062),
|
||||||
|
'landuse:residential' => array('label' => 'Residential', 'frequency' => 886),
|
||||||
|
'landuse:retail' => array('label' => 'Retail', 'frequency' => 754),
|
||||||
|
'landuse:commercial' => array('label' => 'Commercial', 'frequency' => 657),
|
||||||
|
|
||||||
|
'place:airport' => array('label' => 'Airport', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
||||||
|
'aeroway:aerodrome' => array('label' => 'Aerodrome', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
||||||
|
'aeroway' => array('label' => 'Aeroway', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
||||||
|
'railway:station' => array('label' => 'Station', 'frequency' => 3431, 'icon' => 'transport_train_station2', 'defdiameter' => 0.01),
|
||||||
|
'amenity:place_of_worship' => array('label' => 'Place Of Worship', 'frequency' => 9049, 'icon' => 'place_of_worship_unknown3'),
|
||||||
|
'amenity:pub' => array('label' => 'Pub', 'frequency' => 18969, 'icon' => 'food_pub'),
|
||||||
|
'amenity:bar' => array('label' => 'Bar', 'frequency' => 164, 'icon' => 'food_bar'),
|
||||||
|
'amenity:university' => array('label' => 'University', 'frequency' => 607, 'icon' => 'education_university'),
|
||||||
|
'tourism:museum' => array('label' => 'Museum', 'frequency' => 543, 'icon' => 'tourist_museum'),
|
||||||
|
'amenity:arts_centre' => array('label' => 'Arts Centre', 'frequency' => 136, 'icon' => 'tourist_art_gallery2'),
|
||||||
|
'tourism:zoo' => array('label' => 'Zoo', 'frequency' => 47, 'icon' => 'tourist_zoo'),
|
||||||
|
'tourism:theme_park' => array('label' => 'Theme Park', 'frequency' => 24, 'icon' => 'poi_point_of_interest'),
|
||||||
|
'tourism:attraction' => array('label' => 'Attraction', 'frequency' => 1463, 'icon' => 'poi_point_of_interest'),
|
||||||
|
'leisure:golf_course' => array('label' => 'Golf Course', 'frequency' => 712, 'icon' => 'sport_golf'),
|
||||||
|
'historic:castle' => array('label' => 'Castle', 'frequency' => 316, 'icon' => 'tourist_castle'),
|
||||||
|
'amenity:hospital' => array('label' => 'Hospital', 'frequency' => 879, 'icon' => 'health_hospital'),
|
||||||
|
'amenity:school' => array('label' => 'School', 'frequency' => 8192, 'icon' => 'education_school'),
|
||||||
|
'amenity:theatre' => array('label' => 'Theatre', 'frequency' => 371, 'icon' => 'tourist_theatre'),
|
||||||
|
'amenity:public_building' => array('label' => 'Public Building', 'frequency' => 985),
|
||||||
|
'amenity:library' => array('label' => 'Library', 'frequency' => 794, 'icon' => 'amenity_library'),
|
||||||
|
'amenity:townhall' => array('label' => 'Townhall', 'frequency' => 242),
|
||||||
|
'amenity:community_centre' => array('label' => 'Community Centre', 'frequency' => 157),
|
||||||
|
'amenity:fire_station' => array('label' => 'Fire Station', 'frequency' => 221, 'icon' => 'amenity_firestation3'),
|
||||||
|
'amenity:police' => array('label' => 'Police', 'frequency' => 334, 'icon' => 'amenity_police2'),
|
||||||
|
'amenity:bank' => array('label' => 'Bank', 'frequency' => 1248, 'icon' => 'money_bank2'),
|
||||||
|
'amenity:post_office' => array('label' => 'Post Office', 'frequency' => 859, 'icon' => 'amenity_post_office'),
|
||||||
|
'leisure:park' => array('label' => 'Park', 'frequency' => 2378),
|
||||||
|
'amenity:park' => array('label' => 'Park', 'frequency' => 53),
|
||||||
|
'landuse:park' => array('label' => 'Park', 'frequency' => 50),
|
||||||
|
'landuse:recreation_ground' => array('label' => 'Recreation Ground', 'frequency' => 517),
|
||||||
|
'tourism:hotel' => array('label' => 'Hotel', 'frequency' => 2150, 'icon' => 'accommodation_hotel2'),
|
||||||
|
'tourism:motel' => array('label' => 'Motel', 'frequency' => 43),
|
||||||
|
'amenity:cinema' => array('label' => 'Cinema', 'frequency' => 277, 'icon' => 'tourist_cinema'),
|
||||||
|
'tourism:artwork' => array('label' => 'Artwork', 'frequency' => 171, 'icon' => 'tourist_art_gallery2'),
|
||||||
|
'historic:archaeological_site' => array('label' => 'Archaeological Site', 'frequency' => 407, 'icon' => 'tourist_archaeological2'),
|
||||||
|
'amenity:doctors' => array('label' => 'Doctors', 'frequency' => 581, 'icon' => 'health_doctors'),
|
||||||
|
'leisure:sports_centre' => array('label' => 'Sports Centre', 'frequency' => 767, 'icon' => 'sport_leisure_centre'),
|
||||||
|
'leisure:swimming_pool' => array('label' => 'Swimming Pool', 'frequency' => 24, 'icon' => 'sport_swimming_outdoor'),
|
||||||
|
'shop:supermarket' => array('label' => 'Supermarket', 'frequency' => 2673, 'icon' => 'shopping_supermarket'),
|
||||||
|
'shop:convenience' => array('label' => 'Convenience', 'frequency' => 1469, 'icon' => 'shopping_convenience'),
|
||||||
|
'amenity:restaurant' => array('label' => 'Restaurant', 'frequency' => 3179, 'icon' => 'food_restaurant'),
|
||||||
|
'amenity:fast_food' => array('label' => 'Fast Food', 'frequency' => 2289, 'icon' => 'food_fastfood'),
|
||||||
|
'amenity:cafe' => array('label' => 'Cafe', 'frequency' => 1780, 'icon' => 'food_cafe'),
|
||||||
|
'tourism:guest_house' => array('label' => 'Guest House', 'frequency' => 223, 'icon' => 'accommodation_bed_and_breakfast'),
|
||||||
|
'amenity:pharmacy' => array('label' => 'Pharmacy', 'frequency' => 733, 'icon' => 'health_pharmacy_dispensing'),
|
||||||
|
'amenity:fuel' => array('label' => 'Fuel', 'frequency' => 1308, 'icon' => 'transport_fuel'),
|
||||||
|
'natural:peak' => array('label' => 'Peak', 'frequency' => 3212, 'icon' => 'poi_peak'),
|
||||||
|
'waterway:waterfall' => array('label' => 'Waterfall', 'frequency' => 24),
|
||||||
|
'natural:wood' => array('label' => 'Wood', 'frequency' => 1845, 'icon' => 'landuse_coniferous_and_deciduous'),
|
||||||
|
'natural:water' => array('label' => 'Water', 'frequency' => 1790),
|
||||||
|
'landuse:forest' => array('label' => 'Forest', 'frequency' => 467),
|
||||||
|
'landuse:cemetery' => array('label' => 'Cemetery', 'frequency' => 463),
|
||||||
|
'landuse:allotments' => array('label' => 'Allotments', 'frequency' => 408),
|
||||||
|
'landuse:farmyard' => array('label' => 'Farmyard', 'frequency' => 397),
|
||||||
|
'railway:rail' => array('label' => 'Rail', 'frequency' => 4894),
|
||||||
|
'waterway:canal' => array('label' => 'Canal', 'frequency' => 1723),
|
||||||
|
'waterway:river' => array('label' => 'River', 'frequency' => 4089),
|
||||||
|
'waterway:stream' => array('label' => 'Stream', 'frequency' => 2684),
|
||||||
|
'shop:bicycle' => array('label' => 'Bicycle', 'frequency' => 349, 'icon' => 'shopping_bicycle'),
|
||||||
|
'shop:clothes' => array('label' => 'Clothes', 'frequency' => 315, 'icon' => 'shopping_clothes'),
|
||||||
|
'shop:hairdresser' => array('label' => 'Hairdresser', 'frequency' => 312, 'icon' => 'shopping_hairdresser'),
|
||||||
|
'shop:doityourself' => array('label' => 'Doityourself', 'frequency' => 247, 'icon' => 'shopping_diy'),
|
||||||
|
'shop:estate_agent' => array('label' => 'Estate Agent', 'frequency' => 162, 'icon' => 'shopping_estateagent2'),
|
||||||
|
'shop:car' => array('label' => 'Car', 'frequency' => 159, 'icon' => 'shopping_car'),
|
||||||
|
'shop:garden_centre' => array('label' => 'Garden Centre', 'frequency' => 143, 'icon' => 'shopping_garden_centre'),
|
||||||
|
'shop:car_repair' => array('label' => 'Car Repair', 'frequency' => 141, 'icon' => 'shopping_car_repair'),
|
||||||
|
'shop:newsagent' => array('label' => 'Newsagent', 'frequency' => 132),
|
||||||
|
'shop:bakery' => array('label' => 'Bakery', 'frequency' => 129, 'icon' => 'shopping_bakery'),
|
||||||
|
'shop:furniture' => array('label' => 'Furniture', 'frequency' => 124),
|
||||||
|
'shop:butcher' => array('label' => 'Butcher', 'frequency' => 105, 'icon' => 'shopping_butcher'),
|
||||||
|
'shop:apparel' => array('label' => 'Apparel', 'frequency' => 98, 'icon' => 'shopping_clothes'),
|
||||||
|
'shop:electronics' => array('label' => 'Electronics', 'frequency' => 96),
|
||||||
|
'shop:department_store' => array('label' => 'Department Store', 'frequency' => 86),
|
||||||
|
'shop:books' => array('label' => 'Books', 'frequency' => 85),
|
||||||
|
'shop:yes' => array('label' => 'Shop', 'frequency' => 68),
|
||||||
|
'shop:outdoor' => array('label' => 'Outdoor', 'frequency' => 67),
|
||||||
|
'shop:mall' => array('label' => 'Mall', 'frequency' => 63),
|
||||||
|
'shop:florist' => array('label' => 'Florist', 'frequency' => 61),
|
||||||
|
'shop:charity' => array('label' => 'Charity', 'frequency' => 60),
|
||||||
|
'shop:hardware' => array('label' => 'Hardware', 'frequency' => 59),
|
||||||
|
'shop:laundry' => array('label' => 'Laundry', 'frequency' => 51, 'icon' => 'shopping_laundrette'),
|
||||||
|
'shop:shoes' => array('label' => 'Shoes', 'frequency' => 49),
|
||||||
|
'shop:beverages' => array('label' => 'Beverages', 'frequency' => 48, 'icon' => 'shopping_alcohol'),
|
||||||
|
'shop:dry_cleaning' => array('label' => 'Dry Cleaning', 'frequency' => 46),
|
||||||
|
'shop:carpet' => array('label' => 'Carpet', 'frequency' => 45),
|
||||||
|
'shop:computer' => array('label' => 'Computer', 'frequency' => 44),
|
||||||
|
'shop:alcohol' => array('label' => 'Alcohol', 'frequency' => 44, 'icon' => 'shopping_alcohol'),
|
||||||
|
'shop:optician' => array('label' => 'Optician', 'frequency' => 55, 'icon' => 'health_opticians'),
|
||||||
|
'shop:chemist' => array('label' => 'Chemist', 'frequency' => 42, 'icon' => 'health_pharmacy'),
|
||||||
|
'shop:gallery' => array('label' => 'Gallery', 'frequency' => 38, 'icon' => 'tourist_art_gallery2'),
|
||||||
|
'shop:mobile_phone' => array('label' => 'Mobile Phone', 'frequency' => 37),
|
||||||
|
'shop:sports' => array('label' => 'Sports', 'frequency' => 37),
|
||||||
|
'shop:jewelry' => array('label' => 'Jewelry', 'frequency' => 32, 'icon' => 'shopping_jewelry'),
|
||||||
|
'shop:pet' => array('label' => 'Pet', 'frequency' => 29),
|
||||||
|
'shop:beauty' => array('label' => 'Beauty', 'frequency' => 28),
|
||||||
|
'shop:stationery' => array('label' => 'Stationery', 'frequency' => 25),
|
||||||
|
'shop:shopping_centre' => array('label' => 'Shopping Centre', 'frequency' => 25),
|
||||||
|
'shop:general' => array('label' => 'General', 'frequency' => 25),
|
||||||
|
'shop:electrical' => array('label' => 'Electrical', 'frequency' => 25),
|
||||||
|
'shop:toys' => array('label' => 'Toys', 'frequency' => 23),
|
||||||
|
'shop:jeweller' => array('label' => 'Jeweller', 'frequency' => 23),
|
||||||
|
'shop:betting' => array('label' => 'Betting', 'frequency' => 23),
|
||||||
|
'shop:household' => array('label' => 'Household', 'frequency' => 21),
|
||||||
|
'shop:travel_agency' => array('label' => 'Travel Agency', 'frequency' => 21),
|
||||||
|
'shop:hifi' => array('label' => 'Hifi', 'frequency' => 21),
|
||||||
|
'amenity:shop' => array('label' => 'Shop', 'frequency' => 61),
|
||||||
|
'tourism:information' => array('label' => 'Information', 'frequency' => 224, 'icon' => 'amenity_information'),
|
||||||
|
|
||||||
|
'place:house' => array('label' => 'House', 'frequency' => 2086, 'defzoom' => 18),
|
||||||
|
'place:house_name' => array('label' => 'House', 'frequency' => 2086, 'defzoom' => 18),
|
||||||
|
'place:house_number' => array('label' => 'House Number', 'frequency' => 2086, 'defzoom' => 18),
|
||||||
|
'place:country_code' => array('label' => 'Country Code', 'frequency' => 2086, 'defzoom' => 18),
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
'leisure:pitch' => array('label' => 'Pitch', 'frequency' => 762),
|
||||||
|
'highway:unsurfaced' => array('label' => 'Unsurfaced', 'frequency' => 492),
|
||||||
|
'historic:ruins' => array('label' => 'Ruins', 'frequency' => 483, 'icon' => 'tourist_ruin'),
|
||||||
|
'amenity:college' => array('label' => 'College', 'frequency' => 473, 'icon' => 'education_school'),
|
||||||
|
'historic:monument' => array('label' => 'Monument', 'frequency' => 470, 'icon' => 'tourist_monument'),
|
||||||
|
'railway:subway' => array('label' => 'Subway', 'frequency' => 385),
|
||||||
|
'historic:memorial' => array('label' => 'Memorial', 'frequency' => 382, 'icon' => 'tourist_monument'),
|
||||||
|
'leisure:nature_reserve' => array('label' => 'Nature Reserve', 'frequency' => 342),
|
||||||
|
'leisure:common' => array('label' => 'Common', 'frequency' => 322),
|
||||||
|
'waterway:lock_gate' => array('label' => 'Lock Gate', 'frequency' => 321),
|
||||||
|
'natural:fell' => array('label' => 'Fell', 'frequency' => 308),
|
||||||
|
'amenity:nightclub' => array('label' => 'Nightclub', 'frequency' => 292),
|
||||||
|
'highway:path' => array('label' => 'Path', 'frequency' => 287),
|
||||||
|
'leisure:garden' => array('label' => 'Garden', 'frequency' => 285),
|
||||||
|
'landuse:reservoir' => array('label' => 'Reservoir', 'frequency' => 276),
|
||||||
|
'leisure:playground' => array('label' => 'Playground', 'frequency' => 264),
|
||||||
|
'leisure:stadium' => array('label' => 'Stadium', 'frequency' => 212),
|
||||||
|
'historic:mine' => array('label' => 'Mine', 'frequency' => 193, 'icon' => 'poi_mine'),
|
||||||
|
'natural:cliff' => array('label' => 'Cliff', 'frequency' => 193),
|
||||||
|
'tourism:caravan_site' => array('label' => 'Caravan Site', 'frequency' => 183, 'icon' => 'accommodation_caravan_park'),
|
||||||
|
'amenity:bus_station' => array('label' => 'Bus Station', 'frequency' => 181, 'icon' => 'transport_bus_station'),
|
||||||
|
'amenity:kindergarten' => array('label' => 'Kindergarten', 'frequency' => 179),
|
||||||
|
'highway:construction' => array('label' => 'Construction', 'frequency' => 176),
|
||||||
|
'amenity:atm' => array('label' => 'Atm', 'frequency' => 172, 'icon' => 'money_atm2'),
|
||||||
|
'amenity:emergency_phone' => array('label' => 'Emergency Phone', 'frequency' => 164),
|
||||||
|
'waterway:lock' => array('label' => 'Lock', 'frequency' => 146),
|
||||||
|
'waterway:riverbank' => array('label' => 'Riverbank', 'frequency' => 143),
|
||||||
|
'natural:coastline' => array('label' => 'Coastline', 'frequency' => 142),
|
||||||
|
'tourism:viewpoint' => array('label' => 'Viewpoint', 'frequency' => 140, 'icon' => 'tourist_view_point'),
|
||||||
|
'tourism:hostel' => array('label' => 'Hostel', 'frequency' => 140),
|
||||||
|
'tourism:bed_and_breakfast' => array('label' => 'Bed And Breakfast', 'frequency' => 140, 'icon' => 'accommodation_bed_and_breakfast'),
|
||||||
|
'railway:halt' => array('label' => 'Halt', 'frequency' => 135),
|
||||||
|
'railway:platform' => array('label' => 'Platform', 'frequency' => 134),
|
||||||
|
'railway:tram' => array('label' => 'Tram', 'frequency' => 130, 'icon' => 'transport_tram_stop'),
|
||||||
|
'amenity:courthouse' => array('label' => 'Courthouse', 'frequency' => 129, 'icon' => 'amenity_court'),
|
||||||
|
'amenity:recycling' => array('label' => 'Recycling', 'frequency' => 126, 'icon' => 'amenity_recycling'),
|
||||||
|
'amenity:dentist' => array('label' => 'Dentist', 'frequency' => 124, 'icon' => 'health_dentist'),
|
||||||
|
'natural:beach' => array('label' => 'Beach', 'frequency' => 121, 'icon' => 'tourist_beach'),
|
||||||
|
'place:moor' => array('label' => 'Moor', 'frequency' => 118),
|
||||||
|
'amenity:grave_yard' => array('label' => 'Grave Yard', 'frequency' => 110),
|
||||||
|
'waterway:drain' => array('label' => 'Drain', 'frequency' => 108),
|
||||||
|
'landuse:grass' => array('label' => 'Grass', 'frequency' => 106),
|
||||||
|
'landuse:village_green' => array('label' => 'Village Green', 'frequency' => 106),
|
||||||
|
'natural:bay' => array('label' => 'Bay', 'frequency' => 102),
|
||||||
|
'railway:tram_stop' => array('label' => 'Tram Stop', 'frequency' => 101, 'icon' => 'transport_tram_stop'),
|
||||||
|
'leisure:marina' => array('label' => 'Marina', 'frequency' => 98),
|
||||||
|
'highway:stile' => array('label' => 'Stile', 'frequency' => 97),
|
||||||
|
'natural:moor' => array('label' => 'Moor', 'frequency' => 95),
|
||||||
|
'railway:light_rail' => array('label' => 'Light Rail', 'frequency' => 91),
|
||||||
|
'railway:narrow_gauge' => array('label' => 'Narrow Gauge', 'frequency' => 90),
|
||||||
|
'natural:land' => array('label' => 'Land', 'frequency' => 86),
|
||||||
|
'amenity:village_hall' => array('label' => 'Village Hall', 'frequency' => 82),
|
||||||
|
'waterway:dock' => array('label' => 'Dock', 'frequency' => 80),
|
||||||
|
'amenity:veterinary' => array('label' => 'Veterinary', 'frequency' => 79),
|
||||||
|
'landuse:brownfield' => array('label' => 'Brownfield', 'frequency' => 77),
|
||||||
|
'leisure:track' => array('label' => 'Track', 'frequency' => 76),
|
||||||
|
'railway:historic_station' => array('label' => 'Historic Station', 'frequency' => 74),
|
||||||
|
'landuse:construction' => array('label' => 'Construction', 'frequency' => 72),
|
||||||
|
'amenity:prison' => array('label' => 'Prison', 'frequency' => 71, 'icon' => 'amenity_prison'),
|
||||||
|
'landuse:quarry' => array('label' => 'Quarry', 'frequency' => 71),
|
||||||
|
'amenity:telephone' => array('label' => 'Telephone', 'frequency' => 70),
|
||||||
|
'highway:traffic_signals' => array('label' => 'Traffic Signals', 'frequency' => 66),
|
||||||
|
'natural:heath' => array('label' => 'Heath', 'frequency' => 62),
|
||||||
|
'historic:house' => array('label' => 'House', 'frequency' => 61),
|
||||||
|
'amenity:social_club' => array('label' => 'Social Club', 'frequency' => 61),
|
||||||
|
'landuse:military' => array('label' => 'Military', 'frequency' => 61),
|
||||||
|
'amenity:health_centre' => array('label' => 'Health Centre', 'frequency' => 59),
|
||||||
|
'historic:building' => array('label' => 'Building', 'frequency' => 58),
|
||||||
|
'amenity:clinic' => array('label' => 'Clinic', 'frequency' => 57),
|
||||||
|
'highway:services' => array('label' => 'Services', 'frequency' => 56),
|
||||||
|
'amenity:ferry_terminal' => array('label' => 'Ferry Terminal', 'frequency' => 55),
|
||||||
|
'natural:marsh' => array('label' => 'Marsh', 'frequency' => 55),
|
||||||
|
'natural:hill' => array('label' => 'Hill', 'frequency' => 54),
|
||||||
|
'highway:raceway' => array('label' => 'Raceway', 'frequency' => 53),
|
||||||
|
'amenity:taxi' => array('label' => 'Taxi', 'frequency' => 47),
|
||||||
|
'amenity:take_away' => array('label' => 'Take Away', 'frequency' => 45),
|
||||||
|
'amenity:car_rental' => array('label' => 'Car Rental', 'frequency' => 44),
|
||||||
|
'place:islet' => array('label' => 'Islet', 'frequency' => 44),
|
||||||
|
'amenity:nursery' => array('label' => 'Nursery', 'frequency' => 44),
|
||||||
|
'amenity:nursing_home' => array('label' => 'Nursing Home', 'frequency' => 43),
|
||||||
|
'amenity:toilets' => array('label' => 'Toilets', 'frequency' => 38),
|
||||||
|
'amenity:hall' => array('label' => 'Hall', 'frequency' => 38),
|
||||||
|
'waterway:boatyard' => array('label' => 'Boatyard', 'frequency' => 36),
|
||||||
|
'highway:mini_roundabout' => array('label' => 'Mini Roundabout', 'frequency' => 35),
|
||||||
|
'historic:manor' => array('label' => 'Manor', 'frequency' => 35),
|
||||||
|
'tourism:chalet' => array('label' => 'Chalet', 'frequency' => 34),
|
||||||
|
'amenity:bicycle_parking' => array('label' => 'Bicycle Parking', 'frequency' => 34),
|
||||||
|
'amenity:hotel' => array('label' => 'Hotel', 'frequency' => 34),
|
||||||
|
'waterway:weir' => array('label' => 'Weir', 'frequency' => 33),
|
||||||
|
'natural:wetland' => array('label' => 'Wetland', 'frequency' => 33),
|
||||||
|
'natural:cave_entrance' => array('label' => 'Cave Entrance', 'frequency' => 32),
|
||||||
|
'amenity:crematorium' => array('label' => 'Crematorium', 'frequency' => 31),
|
||||||
|
'tourism:picnic_site' => array('label' => 'Picnic Site', 'frequency' => 31),
|
||||||
|
'landuse:wood' => array('label' => 'Wood', 'frequency' => 30),
|
||||||
|
'landuse:basin' => array('label' => 'Basin', 'frequency' => 30),
|
||||||
|
'natural:tree' => array('label' => 'Tree', 'frequency' => 30),
|
||||||
|
'leisure:slipway' => array('label' => 'Slipway', 'frequency' => 29),
|
||||||
|
'landuse:meadow' => array('label' => 'Meadow', 'frequency' => 29),
|
||||||
|
'landuse:piste' => array('label' => 'Piste', 'frequency' => 28),
|
||||||
|
'amenity:care_home' => array('label' => 'Care Home', 'frequency' => 28),
|
||||||
|
'amenity:club' => array('label' => 'Club', 'frequency' => 28),
|
||||||
|
'amenity:medical_centre' => array('label' => 'Medical Centre', 'frequency' => 27),
|
||||||
|
'historic:roman_road' => array('label' => 'Roman Road', 'frequency' => 27),
|
||||||
|
'historic:fort' => array('label' => 'Fort', 'frequency' => 26),
|
||||||
|
'railway:subway_entrance' => array('label' => 'Subway Entrance', 'frequency' => 26),
|
||||||
|
'historic:yes' => array('label' => 'Historic', 'frequency' => 25),
|
||||||
|
'highway:gate' => array('label' => 'Gate', 'frequency' => 25),
|
||||||
|
'leisure:fishing' => array('label' => 'Fishing', 'frequency' => 24),
|
||||||
|
'historic:museum' => array('label' => 'Museum', 'frequency' => 24),
|
||||||
|
'amenity:car_wash' => array('label' => 'Car Wash', 'frequency' => 24),
|
||||||
|
'railway:level_crossing' => array('label' => 'Level Crossing', 'frequency' => 23),
|
||||||
|
'leisure:bird_hide' => array('label' => 'Bird Hide', 'frequency' => 23),
|
||||||
|
'natural:headland' => array('label' => 'Headland', 'frequency' => 21),
|
||||||
|
'tourism:apartments' => array('label' => 'Apartments', 'frequency' => 21),
|
||||||
|
'amenity:shopping' => array('label' => 'Shopping', 'frequency' => 21),
|
||||||
|
'natural:scrub' => array('label' => 'Scrub', 'frequency' => 20),
|
||||||
|
'natural:fen' => array('label' => 'Fen', 'frequency' => 20),
|
||||||
|
'building:yes' => array('label' => 'Building', 'frequency' => 200),
|
||||||
|
'mountain_pass:yes' => array('label' => 'Mountain Pass', 'frequency' => 200),
|
||||||
|
|
||||||
|
'amenity:parking' => array('label' => 'Parking', 'frequency' => 3157),
|
||||||
|
'highway:bus_stop' => array('label' => 'Bus Stop', 'frequency' => 35777, 'icon' => 'transport_bus_stop2'),
|
||||||
|
'place:postcode' => array('label' => 'Postcode', 'frequency' => 27267),
|
||||||
|
'amenity:post_box' => array('label' => 'Post Box', 'frequency' => 9613),
|
||||||
|
|
||||||
|
'place:houses' => array('label' => 'Houses', 'frequency' => 85),
|
||||||
|
'railway:preserved' => array('label' => 'Preserved', 'frequency' => 227),
|
||||||
|
'waterway:derelict_canal' => array('label' => 'Derelict Canal', 'frequency' => 21),
|
||||||
|
'amenity:dead_pub' => array('label' => 'Dead Pub', 'frequency' => 20),
|
||||||
|
'railway:disused_station' => array('label' => 'Disused Station', 'frequency' => 114),
|
||||||
|
'railway:abandoned' => array('label' => 'Abandoned', 'frequency' => 641),
|
||||||
|
'railway:disused' => array('label' => 'Disused', 'frequency' => 72),
|
||||||
|
);
|
||||||
|
}
|
||||||
180
lib/DebugHtml.php
Normal file
180
lib/DebugHtml.php
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim;
|
||||||
|
|
||||||
|
class Debug
|
||||||
|
{
|
||||||
|
public static function newFunction($sHeading)
|
||||||
|
{
|
||||||
|
echo "<pre><h2>Debug output for $sHeading</h2></pre>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function newSection($sHeading)
|
||||||
|
{
|
||||||
|
echo "<hr><pre><h3>$sHeading</h3></pre>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function printVar($sHeading, $mVar)
|
||||||
|
{
|
||||||
|
echo '<pre><b>'.$sHeading. ':</b> ';
|
||||||
|
Debug::outputVar($mVar, str_repeat(' ', strlen($sHeading) + 3));
|
||||||
|
echo "</pre>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function fmtArrayVals($aArr)
|
||||||
|
{
|
||||||
|
return array('__debug_format' => 'array_vals', 'data' => $aArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function printDebugArray($sHeading, $oVar)
|
||||||
|
{
|
||||||
|
|
||||||
|
if ($oVar === null) {
|
||||||
|
Debug::printVar($sHeading, 'null');
|
||||||
|
} else {
|
||||||
|
Debug::printVar($sHeading, $oVar->debugInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function printDebugTable($sHeading, $aVar)
|
||||||
|
{
|
||||||
|
echo '<b>'.$sHeading.":</b>\n";
|
||||||
|
echo "<table border='1'>\n";
|
||||||
|
if (!empty($aVar)) {
|
||||||
|
echo " <tr>\n";
|
||||||
|
$aKeys = array();
|
||||||
|
$aInfo = reset($aVar);
|
||||||
|
if (!is_array($aInfo)) {
|
||||||
|
$aInfo = $aInfo->debugInfo();
|
||||||
|
}
|
||||||
|
foreach ($aInfo as $sKey => $mVal) {
|
||||||
|
echo ' <th><small>'.$sKey.'</small></th>'."\n";
|
||||||
|
$aKeys[] = $sKey;
|
||||||
|
}
|
||||||
|
echo " </tr>\n";
|
||||||
|
foreach ($aVar as $oRow) {
|
||||||
|
$aInfo = $oRow;
|
||||||
|
if (!is_array($oRow)) {
|
||||||
|
$aInfo = $oRow->debugInfo();
|
||||||
|
}
|
||||||
|
echo " <tr>\n";
|
||||||
|
foreach ($aKeys as $sKey) {
|
||||||
|
echo ' <td><pre>';
|
||||||
|
if (isset($aInfo[$sKey])) {
|
||||||
|
Debug::outputVar($aInfo[$sKey], '');
|
||||||
|
}
|
||||||
|
echo '</pre></td>'."\n";
|
||||||
|
}
|
||||||
|
echo " </tr>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "</table>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function printGroupedSearch($aSearches, $aWordsIDs)
|
||||||
|
{
|
||||||
|
echo '<table border="1">';
|
||||||
|
echo '<tr><th>rank</th><th>Name Tokens</th><th>Name Not</th>';
|
||||||
|
echo '<th>Address Tokens</th><th>Address Not</th>';
|
||||||
|
echo '<th>country</th><th>operator</th>';
|
||||||
|
echo '<th>class</th><th>type</th><th>postcode</th><th>housenumber</th></tr>';
|
||||||
|
foreach ($aSearches as $iRank => $aRankedSet) {
|
||||||
|
foreach ($aRankedSet as $aRow) {
|
||||||
|
$aRow->dumpAsHtmlTableRow($aWordsIDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo '</table>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function printGroupTable($sHeading, $aVar)
|
||||||
|
{
|
||||||
|
echo '<b>'.$sHeading.":</b>\n";
|
||||||
|
echo "<table border='1'>\n";
|
||||||
|
if (!empty($aVar)) {
|
||||||
|
echo " <tr>\n";
|
||||||
|
echo ' <th><small>Group</small></th>'."\n";
|
||||||
|
$aKeys = array();
|
||||||
|
$aInfo = reset($aVar)[0];
|
||||||
|
if (!is_array($aInfo)) {
|
||||||
|
$aInfo = $aInfo->debugInfo();
|
||||||
|
}
|
||||||
|
foreach ($aInfo as $sKey => $mVal) {
|
||||||
|
echo ' <th><small>'.$sKey.'</small></th>'."\n";
|
||||||
|
$aKeys[] = $sKey;
|
||||||
|
}
|
||||||
|
echo " </tr>\n";
|
||||||
|
foreach ($aVar as $sGrpKey => $aGroup) {
|
||||||
|
foreach ($aGroup as $oRow) {
|
||||||
|
$aInfo = $oRow;
|
||||||
|
if (!is_array($oRow)) {
|
||||||
|
$aInfo = $oRow->debugInfo();
|
||||||
|
}
|
||||||
|
echo " <tr>\n";
|
||||||
|
echo ' <td><pre>'.$sGrpKey.'</pre></td>'."\n";
|
||||||
|
foreach ($aKeys as $sKey) {
|
||||||
|
echo ' <td><pre>';
|
||||||
|
if (!empty($aInfo[$sKey])) {
|
||||||
|
Debug::outputVar($aInfo[$sKey], '');
|
||||||
|
}
|
||||||
|
echo '</pre></td>'."\n";
|
||||||
|
}
|
||||||
|
echo " </tr>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "</table>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function printSQL($sSQL)
|
||||||
|
{
|
||||||
|
echo '<p><tt><font color="#aaa">'.$sSQL.'</font></tt></p>'."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function outputVar($mVar, $sPreNL)
|
||||||
|
{
|
||||||
|
if (is_array($mVar) && !isset($mVar['__debug_format'])) {
|
||||||
|
$sPre = '';
|
||||||
|
foreach ($mVar as $mKey => $aValue) {
|
||||||
|
echo $sPre;
|
||||||
|
$iKeyLen = Debug::outputSimpleVar($mKey);
|
||||||
|
echo ' => ';
|
||||||
|
Debug::outputVar(
|
||||||
|
$aValue,
|
||||||
|
$sPreNL.str_repeat(' ', $iKeyLen + 4)
|
||||||
|
);
|
||||||
|
$sPre = "\n".$sPreNL;
|
||||||
|
}
|
||||||
|
} elseif (is_array($mVar) && isset($mVar['__debug_format'])) {
|
||||||
|
if (!empty($mVar['data'])) {
|
||||||
|
$sPre = '';
|
||||||
|
foreach ($mVar['data'] as $mValue) {
|
||||||
|
echo $sPre;
|
||||||
|
Debug::outputSimpleVar($mValue);
|
||||||
|
$sPre = ', ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (is_object($mVar) && method_exists($mVar, 'debugInfo')) {
|
||||||
|
Debug::outputVar($mVar->debugInfo(), $sPreNL);
|
||||||
|
} elseif (is_a($mVar, 'stdClass')) {
|
||||||
|
Debug::outputVar(json_decode(json_encode($mVar), true), $sPreNL);
|
||||||
|
} else {
|
||||||
|
Debug::outputSimpleVar($mVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function outputSimpleVar($mVar)
|
||||||
|
{
|
||||||
|
if (is_bool($mVar)) {
|
||||||
|
echo '<i>'.($mVar ? 'True' : 'False').'</i>';
|
||||||
|
return $mVar ? 4 : 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($mVar)) {
|
||||||
|
echo "'$mVar'";
|
||||||
|
return strlen($mVar) + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo (string)$mVar;
|
||||||
|
return strlen((string)$mVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
lib/DebugNone.php
Normal file
11
lib/DebugNone.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim;
|
||||||
|
|
||||||
|
class Debug
|
||||||
|
{
|
||||||
|
public static function __callStatic($name, $arguments)
|
||||||
|
{
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
287
lib/Geocode.php
287
lib/Geocode.php
@@ -7,6 +7,7 @@ require_once(CONST_BasePath.'/lib/Phrase.php');
|
|||||||
require_once(CONST_BasePath.'/lib/ReverseGeocode.php');
|
require_once(CONST_BasePath.'/lib/ReverseGeocode.php');
|
||||||
require_once(CONST_BasePath.'/lib/SearchDescription.php');
|
require_once(CONST_BasePath.'/lib/SearchDescription.php');
|
||||||
require_once(CONST_BasePath.'/lib/SearchContext.php');
|
require_once(CONST_BasePath.'/lib/SearchContext.php');
|
||||||
|
require_once(CONST_BasePath.'/lib/TokenList.php');
|
||||||
|
|
||||||
class Geocode
|
class Geocode
|
||||||
{
|
{
|
||||||
@@ -16,8 +17,6 @@ class Geocode
|
|||||||
|
|
||||||
protected $aLangPrefOrder = array();
|
protected $aLangPrefOrder = array();
|
||||||
|
|
||||||
protected $bIncludeAddressDetails = false;
|
|
||||||
|
|
||||||
protected $aExcludePlaceIDs = array();
|
protected $aExcludePlaceIDs = array();
|
||||||
protected $bReverseInPlan = false;
|
protected $bReverseInPlan = false;
|
||||||
|
|
||||||
@@ -86,7 +85,6 @@ class Geocode
|
|||||||
$aParams['exclude_place_ids'] = implode(',', $this->aExcludePlaceIDs);
|
$aParams['exclude_place_ids'] = implode(',', $this->aExcludePlaceIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->bIncludeAddressDetails) $aParams['addressdetails'] = '1';
|
|
||||||
if ($this->bBoundedSearch) $aParams['bounded'] = '1';
|
if ($this->bBoundedSearch) $aParams['bounded'] = '1';
|
||||||
|
|
||||||
if ($this->aCountryCodes) {
|
if ($this->aCountryCodes) {
|
||||||
@@ -151,6 +149,10 @@ class Geocode
|
|||||||
|
|
||||||
private function viewboxImportanceFactor($fX, $fY)
|
private function viewboxImportanceFactor($fX, $fY)
|
||||||
{
|
{
|
||||||
|
if (!$this->aViewBox) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
$fWidth = ($this->aViewBox[2] - $this->aViewBox[0])/2;
|
$fWidth = ($this->aViewBox[2] - $this->aViewBox[0])/2;
|
||||||
$fHeight = ($this->aViewBox[3] - $this->aViewBox[1])/2;
|
$fHeight = ($this->aViewBox[3] - $this->aViewBox[1])/2;
|
||||||
|
|
||||||
@@ -182,9 +184,6 @@ class Geocode
|
|||||||
|
|
||||||
public function loadParamArray($oParams, $sForceGeometryType = null)
|
public function loadParamArray($oParams, $sForceGeometryType = null)
|
||||||
{
|
{
|
||||||
$this->bIncludeAddressDetails
|
|
||||||
= $oParams->getBool('addressdetails', $this->bIncludeAddressDetails);
|
|
||||||
|
|
||||||
$this->bBoundedSearch = $oParams->getBool('bounded', $this->bBoundedSearch);
|
$this->bBoundedSearch = $oParams->getBool('bounded', $this->bBoundedSearch);
|
||||||
|
|
||||||
$this->setLimit($oParams->getInt('limit', $this->iFinalLimit));
|
$this->setLimit($oParams->getInt('limit', $this->iFinalLimit));
|
||||||
@@ -225,14 +224,14 @@ class Geocode
|
|||||||
$aViewbox = $oParams->getStringList('viewboxlbrt');
|
$aViewbox = $oParams->getStringList('viewboxlbrt');
|
||||||
if ($aViewbox) {
|
if ($aViewbox) {
|
||||||
if (count($aViewbox) != 4) {
|
if (count($aViewbox) != 4) {
|
||||||
userError("Bad parmater 'viewboxlbrt'. Expected 4 coordinates.");
|
userError("Bad parameter 'viewboxlbrt'. Expected 4 coordinates.");
|
||||||
}
|
}
|
||||||
$this->setViewbox($aViewbox);
|
$this->setViewbox($aViewbox);
|
||||||
} else {
|
} else {
|
||||||
$aViewbox = $oParams->getStringList('viewbox');
|
$aViewbox = $oParams->getStringList('viewbox');
|
||||||
if ($aViewbox) {
|
if ($aViewbox) {
|
||||||
if (count($aViewbox) != 4) {
|
if (count($aViewbox) != 4) {
|
||||||
userError("Bad parmater 'viewbox'. Expected 4 coordinates.");
|
userError("Bad parameter 'viewbox'. Expected 4 coordinates.");
|
||||||
}
|
}
|
||||||
$this->setViewBox($aViewbox);
|
$this->setViewBox($aViewbox);
|
||||||
} else {
|
} else {
|
||||||
@@ -246,8 +245,8 @@ class Geocode
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->oPlaceLookup->loadParamArray($oParams, $sForceGeometryType);
|
$this->oPlaceLookup->loadParamArray($oParams, $sForceGeometryType);
|
||||||
$this->oPlaceLookup->setIncludeAddressDetails(false);
|
|
||||||
$this->oPlaceLookup->setIncludePolygonAsPoints($oParams->getBool('polygon'));
|
$this->oPlaceLookup->setIncludePolygonAsPoints($oParams->getBool('polygon'));
|
||||||
|
$this->oPlaceLookup->setIncludeAddressDetails($oParams->getBool('addressdetails', false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setQueryFromParams($oParams)
|
public function setQueryFromParams($oParams)
|
||||||
@@ -303,7 +302,7 @@ class Geocode
|
|||||||
$this->loadStructuredAddressElement($sPostalCode, 'postalcode', 5, 11, array(5, 11));
|
$this->loadStructuredAddressElement($sPostalCode, 'postalcode', 5, 11, array(5, 11));
|
||||||
$this->loadStructuredAddressElement($sCountry, 'country', 4, 4, false);
|
$this->loadStructuredAddressElement($sCountry, 'country', 4, 4, false);
|
||||||
|
|
||||||
if (sizeof($this->aStructuredQuery) > 0) {
|
if (!empty($this->aStructuredQuery)) {
|
||||||
$this->sQuery = join(', ', $this->aStructuredQuery);
|
$this->sQuery = join(', ', $this->aStructuredQuery);
|
||||||
if ($this->iMaxAddressRank < 30) {
|
if ($this->iMaxAddressRank < 30) {
|
||||||
$this->sAllowedTypesSQLList = '(\'place\',\'boundary\')';
|
$this->sAllowedTypesSQLList = '(\'place\',\'boundary\')';
|
||||||
@@ -317,7 +316,7 @@ class Geocode
|
|||||||
|
|
||||||
$aParams = $this->aStructuredQuery;
|
$aParams = $this->aStructuredQuery;
|
||||||
|
|
||||||
if (sizeof($aParams) == 1) return false;
|
if (count($aParams) == 1) return false;
|
||||||
|
|
||||||
$aOrderToFallback = array('postalcode', 'street', 'city', 'county', 'state');
|
$aOrderToFallback = array('postalcode', 'street', 'city', 'county', 'state');
|
||||||
|
|
||||||
@@ -332,10 +331,10 @@ class Geocode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getGroupedSearches($aSearches, $aPhrases, $aValidTokens, $bIsStructured)
|
public function getGroupedSearches($aSearches, $aPhrases, $oValidTokens, $bIsStructured)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Calculate all searches using aValidTokens i.e.
|
Calculate all searches using oValidTokens i.e.
|
||||||
'Wodsworth Road, Sheffield' =>
|
'Wodsworth Road, Sheffield' =>
|
||||||
|
|
||||||
Phrase Wordset
|
Phrase Wordset
|
||||||
@@ -365,38 +364,37 @@ class Geocode
|
|||||||
//var_dump($oCurrentSearch);
|
//var_dump($oCurrentSearch);
|
||||||
//echo "</i>";
|
//echo "</i>";
|
||||||
|
|
||||||
// If the token is valid
|
// Tokens with full name matches.
|
||||||
if (isset($aValidTokens[' '.$sToken])) {
|
foreach ($oValidTokens->get(' '.$sToken) as $oSearchTerm) {
|
||||||
foreach ($aValidTokens[' '.$sToken] as $aSearchTerm) {
|
$aNewSearches = $oCurrentSearch->extendWithFullTerm(
|
||||||
$aNewSearches = $oCurrentSearch->extendWithFullTerm(
|
$oSearchTerm,
|
||||||
$aSearchTerm,
|
$oValidTokens->contains($sToken)
|
||||||
isset($aValidTokens[$sToken])
|
&& strpos($sToken, ' ') === false,
|
||||||
&& strpos($sToken, ' ') === false,
|
$sPhraseType,
|
||||||
$sPhraseType,
|
$iToken == 0 && $iPhrase == 0,
|
||||||
$iToken == 0 && $iPhrase == 0,
|
$iPhrase == 0,
|
||||||
$iPhrase == 0,
|
$iToken + 1 == count($aWordset)
|
||||||
$iToken + 1 == sizeof($aWordset)
|
&& $iPhrase + 1 == count($aPhrases)
|
||||||
&& $iPhrase + 1 == sizeof($aPhrases)
|
);
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($aNewSearches as $oSearch) {
|
foreach ($aNewSearches as $oSearch) {
|
||||||
if ($oSearch->getRank() < $this->iMaxRank) {
|
if ($oSearch->getRank() < $this->iMaxRank) {
|
||||||
$aNewWordsetSearches[] = $oSearch;
|
$aNewWordsetSearches[] = $oSearch;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Look for partial matches.
|
// Look for partial matches.
|
||||||
// Note that there is no point in adding country terms here
|
// Note that there is no point in adding country terms here
|
||||||
// because country is omitted in the address.
|
// because country is omitted in the address.
|
||||||
if (isset($aValidTokens[$sToken]) && $sPhraseType != 'country') {
|
if ($sPhraseType != 'country') {
|
||||||
// Allow searching for a word - but at extra cost
|
// Allow searching for a word - but at extra cost
|
||||||
foreach ($aValidTokens[$sToken] as $aSearchTerm) {
|
foreach ($oValidTokens->get($sToken) as $oSearchTerm) {
|
||||||
$aNewSearches = $oCurrentSearch->extendWithPartialTerm(
|
$aNewSearches = $oCurrentSearch->extendWithPartialTerm(
|
||||||
$aSearchTerm,
|
$sToken,
|
||||||
|
$oSearchTerm,
|
||||||
$bIsStructured,
|
$bIsStructured,
|
||||||
$iPhrase,
|
$iPhrase,
|
||||||
isset($aValidTokens[' '.$sToken]) ? $aValidTokens[' '.$sToken] : array()
|
$oValidTokens->get(' '.$sToken)
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($aNewSearches as $oSearch) {
|
foreach ($aNewSearches as $oSearch) {
|
||||||
@@ -411,7 +409,7 @@ class Geocode
|
|||||||
usort($aNewWordsetSearches, array('Nominatim\SearchDescription', 'bySearchRank'));
|
usort($aNewWordsetSearches, array('Nominatim\SearchDescription', 'bySearchRank'));
|
||||||
$aWordsetSearches = array_slice($aNewWordsetSearches, 0, 50);
|
$aWordsetSearches = array_slice($aNewWordsetSearches, 0, 50);
|
||||||
}
|
}
|
||||||
//var_Dump('<hr>',sizeof($aWordsetSearches)); exit;
|
//var_Dump('<hr>',count($aWordsetSearches)); exit;
|
||||||
|
|
||||||
$aNewPhraseSearches = array_merge($aNewPhraseSearches, $aNewWordsetSearches);
|
$aNewPhraseSearches = array_merge($aNewPhraseSearches, $aNewWordsetSearches);
|
||||||
usort($aNewPhraseSearches, array('Nominatim\SearchDescription', 'bySearchRank'));
|
usort($aNewPhraseSearches, array('Nominatim\SearchDescription', 'bySearchRank'));
|
||||||
@@ -442,12 +440,10 @@ class Geocode
|
|||||||
$iSearchCount = 0;
|
$iSearchCount = 0;
|
||||||
$aSearches = array();
|
$aSearches = array();
|
||||||
foreach ($aGroupedSearches as $iScore => $aNewSearches) {
|
foreach ($aGroupedSearches as $iScore => $aNewSearches) {
|
||||||
$iSearchCount += sizeof($aNewSearches);
|
$iSearchCount += count($aNewSearches);
|
||||||
$aSearches = array_merge($aSearches, $aNewSearches);
|
$aSearches = array_merge($aSearches, $aNewSearches);
|
||||||
if ($iSearchCount > 50) break;
|
if ($iSearchCount > 50) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (CONST_Debug) _debugDumpGroupedSearches($aGroupedSearches, $aValidTokens);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revisit searches, drop bad searches and give penalty to unlikely combinations.
|
// Revisit searches, drop bad searches and give penalty to unlikely combinations.
|
||||||
@@ -502,8 +498,11 @@ class Geocode
|
|||||||
|
|
||||||
public function lookup()
|
public function lookup()
|
||||||
{
|
{
|
||||||
|
Debug::newFunction('Geocode::lookup');
|
||||||
if (!$this->sQuery && !$this->aStructuredQuery) return array();
|
if (!$this->sQuery && !$this->aStructuredQuery) return array();
|
||||||
|
|
||||||
|
Debug::printDebugArray('Geocode', $this);
|
||||||
|
|
||||||
$oCtx = new SearchContext();
|
$oCtx = new SearchContext();
|
||||||
|
|
||||||
if ($this->aRoutePoints) {
|
if ($this->aRoutePoints) {
|
||||||
@@ -523,7 +522,11 @@ class Geocode
|
|||||||
$oCtx->setCountryList($this->aCountryCodes);
|
$oCtx->setCountryList($this->aCountryCodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug::newSection('Query Preprocessing');
|
||||||
|
|
||||||
$sNormQuery = $this->normTerm($this->sQuery);
|
$sNormQuery = $this->normTerm($this->sQuery);
|
||||||
|
Debug::printVar('Normalized query', $sNormQuery);
|
||||||
|
|
||||||
$sLanguagePrefArraySQL = getArraySQL(
|
$sLanguagePrefArraySQL = getArraySQL(
|
||||||
array_map('getDBQuoted', $this->aLangPrefOrder)
|
array_map('getDBQuoted', $this->aLangPrefOrder)
|
||||||
);
|
);
|
||||||
@@ -535,9 +538,9 @@ class Geocode
|
|||||||
|
|
||||||
// Conflicts between US state abreviations and various words for 'the' in different languages
|
// Conflicts between US state abreviations and various words for 'the' in different languages
|
||||||
if (isset($this->aLangPrefOrder['name:en'])) {
|
if (isset($this->aLangPrefOrder['name:en'])) {
|
||||||
$sQuery = preg_replace('/(^|,)\s*il\s*(,|$)/', '\1illinois\2', $sQuery);
|
$sQuery = preg_replace('/(^|,)\s*il\s*(,|$)/i', '\1illinois\2', $sQuery);
|
||||||
$sQuery = preg_replace('/(^|,)\s*al\s*(,|$)/', '\1alabama\2', $sQuery);
|
$sQuery = preg_replace('/(^|,)\s*al\s*(,|$)/i', '\1alabama\2', $sQuery);
|
||||||
$sQuery = preg_replace('/(^|,)\s*la\s*(,|$)/', '\1louisiana\2', $sQuery);
|
$sQuery = preg_replace('/(^|,)\s*la\s*(,|$)/i', '\1louisiana\2', $sQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we have anything that looks like a lat/lon pair?
|
// Do we have anything that looks like a lat/lon pair?
|
||||||
@@ -560,6 +563,10 @@ class Geocode
|
|||||||
$aSpecialTermsRaw,
|
$aSpecialTermsRaw,
|
||||||
PREG_SET_ORDER
|
PREG_SET_ORDER
|
||||||
);
|
);
|
||||||
|
if (!empty($aSpecialTermsRaw)) {
|
||||||
|
Debug::printVar('Special terms', $aSpecialTermsRaw);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($aSpecialTermsRaw as $aSpecialTerm) {
|
foreach ($aSpecialTermsRaw as $aSpecialTerm) {
|
||||||
$sQuery = str_replace($aSpecialTerm[0], ' ', $sQuery);
|
$sQuery = str_replace($aSpecialTerm[0], ' ', $sQuery);
|
||||||
if (!$sSpecialTerm) {
|
if (!$sSpecialTerm) {
|
||||||
@@ -582,7 +589,8 @@ class Geocode
|
|||||||
$sSQL = 'SELECT class, type FROM word ';
|
$sSQL = 'SELECT class, type FROM word ';
|
||||||
$sSQL .= ' WHERE word_token in (\' '.$sToken.'\')';
|
$sSQL .= ' WHERE word_token in (\' '.$sToken.'\')';
|
||||||
$sSQL .= ' AND class is not null AND class not in (\'place\')';
|
$sSQL .= ' AND class is not null AND class not in (\'place\')';
|
||||||
if (CONST_Debug) var_Dump($sSQL);
|
|
||||||
|
Debug::printSQL($sSQL);
|
||||||
$aSearchWords = chksql($this->oDB->getAll($sSQL));
|
$aSearchWords = chksql($this->oDB->getAll($sSQL));
|
||||||
$aNewSearches = array();
|
$aNewSearches = array();
|
||||||
foreach ($aSearches as $oSearch) {
|
foreach ($aSearches as $oSearch) {
|
||||||
@@ -609,10 +617,15 @@ class Geocode
|
|||||||
$bStructuredPhrases = false;
|
$bStructuredPhrases = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug::printDebugArray('Search context', $oCtx);
|
||||||
|
Debug::printDebugArray('Base search', empty($aSearches) ? null : $aSearches[0]);
|
||||||
|
Debug::printVar('Final query phrases', $aInPhrases);
|
||||||
|
|
||||||
// Convert each phrase to standard form
|
// Convert each phrase to standard form
|
||||||
// Create a list of standard words
|
// Create a list of standard words
|
||||||
// Get all 'sets' of words
|
// Get all 'sets' of words
|
||||||
// Generate a complete list of all
|
// Generate a complete list of all
|
||||||
|
Debug::newSection('Tokenization');
|
||||||
$aTokens = array();
|
$aTokens = array();
|
||||||
$aPhrases = array();
|
$aPhrases = array();
|
||||||
foreach ($aInPhrases as $iPhrase => $sPhrase) {
|
foreach ($aInPhrases as $iPhrase => $sPhrase) {
|
||||||
@@ -627,74 +640,54 @@ class Geocode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sizeof($aTokens)) {
|
Debug::printDebugTable('Phrases', $aPhrases);
|
||||||
// Check which tokens we have, get the ID numbers
|
Debug::printVar('Tokens', $aTokens);
|
||||||
|
|
||||||
|
$oValidTokens = new TokenList();
|
||||||
|
|
||||||
|
if (!empty($aTokens)) {
|
||||||
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code, operator, search_name_count';
|
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code, operator, search_name_count';
|
||||||
$sSQL .= ' FROM word ';
|
$sSQL .= ' FROM word ';
|
||||||
$sSQL .= ' WHERE word_token in ('.join(',', array_map('getDBQuoted', $aTokens)).')';
|
$sSQL .= ' WHERE word_token in ('.join(',', array_map('getDBQuoted', $aTokens)).')';
|
||||||
|
|
||||||
if (CONST_Debug) var_Dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
$aValidTokens = array();
|
$oValidTokens->addTokensFromDB(
|
||||||
$aDatabaseWords = chksql(
|
$this->oDB,
|
||||||
$this->oDB->getAll($sSQL),
|
$aTokens,
|
||||||
'Could not get word tokens.'
|
$this->aCountryCodes,
|
||||||
|
$sNormQuery,
|
||||||
|
$this->oNormalizer
|
||||||
);
|
);
|
||||||
$aWordFrequencyScores = array();
|
|
||||||
foreach ($aDatabaseWords as $aToken) {
|
|
||||||
// Filter country tokens that do not match restricted countries.
|
|
||||||
if ($this->aCountryCodes
|
|
||||||
&& $aToken['country_code']
|
|
||||||
&& !in_array($aToken['country_code'], $this->aCountryCodes)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special terms need to appear in their normalized form.
|
// Try more interpretations for Tokens that could not be matched.
|
||||||
if ($aToken['word'] && $aToken['class']) {
|
|
||||||
$sNormWord = $this->normTerm($aToken['word']);
|
|
||||||
if (strpos($sNormQuery, $sNormWord) === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aValidTokens[$aToken['word_token']])) {
|
|
||||||
$aValidTokens[$aToken['word_token']][] = $aToken;
|
|
||||||
} else {
|
|
||||||
$aValidTokens[$aToken['word_token']] = array($aToken);
|
|
||||||
}
|
|
||||||
$aWordFrequencyScores[$aToken['word_id']] = $aToken['search_name_count'] + 1;
|
|
||||||
}
|
|
||||||
if (CONST_Debug) var_Dump($aPhrases, $aValidTokens);
|
|
||||||
|
|
||||||
// US ZIP+4 codes - if there is no token, merge in the 5-digit ZIP code
|
|
||||||
foreach ($aTokens as $sToken) {
|
foreach ($aTokens as $sToken) {
|
||||||
if (!isset($aValidTokens[$sToken]) && preg_match('/^([0-9]{5}) [0-9]{4}$/', $sToken, $aData)) {
|
if ($sToken[0] == ' ' && !$oValidTokens->contains($sToken)) {
|
||||||
if (isset($aValidTokens[$aData[1]])) {
|
if (preg_match('/^ ([0-9]{5}) [0-9]{4}$/', $sToken, $aData)) {
|
||||||
foreach ($aValidTokens[$aData[1]] as $aToken) {
|
// US ZIP+4 codes - merge in the 5-digit ZIP code
|
||||||
if (!$aToken['class']) {
|
$oValidTokens->addToken(
|
||||||
if (isset($aValidTokens[$sToken])) {
|
$sToken,
|
||||||
$aValidTokens[$sToken][] = $aToken;
|
new Token\Postcode(null, $aData[1], 'us')
|
||||||
} else {
|
);
|
||||||
$aValidTokens[$sToken] = array($aToken);
|
} elseif (preg_match('/^ [0-9]+$/', $sToken)) {
|
||||||
}
|
// Unknown single word token with a number.
|
||||||
}
|
// Assume it is a house number.
|
||||||
}
|
$oValidTokens->addToken(
|
||||||
|
$sToken,
|
||||||
|
new Token\HouseNumber(null, trim($sToken))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($aTokens as $sToken) {
|
|
||||||
// Unknown single word token with a number - assume it is a house number
|
|
||||||
if (!isset($aValidTokens[' '.$sToken]) && strpos($sToken, ' ') === false && preg_match('/^[0-9]+$/', $sToken)) {
|
|
||||||
$aValidTokens[' '.$sToken] = array(array('class' => 'place', 'type' => 'house', 'word_token' => ' '.$sToken));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any words that have failed completely?
|
// Any words that have failed completely?
|
||||||
// TODO: suggestions
|
// TODO: suggestions
|
||||||
|
|
||||||
$aGroupedSearches = $this->getGroupedSearches($aSearches, $aPhrases, $aValidTokens, $bStructuredPhrases);
|
Debug::printGroupTable('Valid Tokens', $oValidTokens->debugInfo());
|
||||||
|
|
||||||
|
Debug::newSection('Search candidates');
|
||||||
|
|
||||||
|
$aGroupedSearches = $this->getGroupedSearches($aSearches, $aPhrases, $oValidTokens, $bStructuredPhrases);
|
||||||
|
|
||||||
if ($this->bReverseInPlan) {
|
if ($this->bReverseInPlan) {
|
||||||
// Reverse phrase array and also reverse the order of the wordsets in
|
// Reverse phrase array and also reverse the order of the wordsets in
|
||||||
@@ -702,10 +695,10 @@ class Geocode
|
|||||||
// because order in the address doesn't matter.
|
// because order in the address doesn't matter.
|
||||||
$aPhrases = array_reverse($aPhrases);
|
$aPhrases = array_reverse($aPhrases);
|
||||||
$aPhrases[0]->invertWordSets();
|
$aPhrases[0]->invertWordSets();
|
||||||
if (sizeof($aPhrases) > 1) {
|
if (count($aPhrases) > 1) {
|
||||||
$aPhrases[sizeof($aPhrases)-1]->invertWordSets();
|
$aPhrases[count($aPhrases)-1]->invertWordSets();
|
||||||
}
|
}
|
||||||
$aReverseGroupedSearches = $this->getGroupedSearches($aSearches, $aPhrases, $aValidTokens, false);
|
$aReverseGroupedSearches = $this->getGroupedSearches($aSearches, $aPhrases, $oValidTokens, false);
|
||||||
|
|
||||||
foreach ($aGroupedSearches as $aSearches) {
|
foreach ($aGroupedSearches as $aSearches) {
|
||||||
foreach ($aSearches as $aSearch) {
|
foreach ($aSearches as $aSearch) {
|
||||||
@@ -738,14 +731,17 @@ class Geocode
|
|||||||
$sHash = serialize($aSearch);
|
$sHash = serialize($aSearch);
|
||||||
if (isset($aSearchHash[$sHash])) {
|
if (isset($aSearchHash[$sHash])) {
|
||||||
unset($aGroupedSearches[$iGroup][$iSearch]);
|
unset($aGroupedSearches[$iGroup][$iSearch]);
|
||||||
if (sizeof($aGroupedSearches[$iGroup]) == 0) unset($aGroupedSearches[$iGroup]);
|
if (empty($aGroupedSearches[$iGroup])) unset($aGroupedSearches[$iGroup]);
|
||||||
} else {
|
} else {
|
||||||
$aSearchHash[$sHash] = 1;
|
$aSearchHash[$sHash] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONST_Debug) _debugDumpGroupedSearches($aGroupedSearches, $aValidTokens);
|
Debug::printGroupedSearch(
|
||||||
|
$aGroupedSearches,
|
||||||
|
$oValidTokens->debugTokenByWordIdList()
|
||||||
|
);
|
||||||
|
|
||||||
// Start the search process
|
// Start the search process
|
||||||
$iGroupLoop = 0;
|
$iGroupLoop = 0;
|
||||||
@@ -755,14 +751,14 @@ class Geocode
|
|||||||
foreach ($aSearches as $oSearch) {
|
foreach ($aSearches as $oSearch) {
|
||||||
$iQueryLoop++;
|
$iQueryLoop++;
|
||||||
|
|
||||||
if (CONST_Debug) {
|
Debug::newSection("Search Loop, group $iGroupLoop, loop $iQueryLoop");
|
||||||
echo "<hr><b>Search Loop, group $iGroupLoop, loop $iQueryLoop</b>";
|
Debug::printGroupedSearch(
|
||||||
_debugDumpGroupedSearches(array($iGroupedRank => array($oSearch)), $aValidTokens);
|
array($iGroupedRank => array($oSearch)),
|
||||||
}
|
$oValidTokens->debugTokenByWordIdList()
|
||||||
|
);
|
||||||
|
|
||||||
$aResults += $oSearch->query(
|
$aResults += $oSearch->query(
|
||||||
$this->oDB,
|
$this->oDB,
|
||||||
$aWordFrequencyScores,
|
|
||||||
$this->iMinAddressRank,
|
$this->iMinAddressRank,
|
||||||
$this->iMaxAddressRank,
|
$this->iMaxAddressRank,
|
||||||
$this->iLimit
|
$this->iLimit
|
||||||
@@ -771,7 +767,7 @@ class Geocode
|
|||||||
if ($iQueryLoop > 20) break;
|
if ($iQueryLoop > 20) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sizeof($aResults) && ($this->iMinAddressRank != 0 || $this->iMaxAddressRank != 30)) {
|
if (!empty($aResults) && ($this->iMinAddressRank != 0 || $this->iMaxAddressRank != 30)) {
|
||||||
// Need to verify passes rank limits before dropping out of the loop (yuk!)
|
// Need to verify passes rank limits before dropping out of the loop (yuk!)
|
||||||
// reduces the number of place ids, like a filter
|
// reduces the number of place ids, like a filter
|
||||||
// rank_address is 30 for interpolated housenumbers
|
// rank_address is 30 for interpolated housenumbers
|
||||||
@@ -806,7 +802,7 @@ class Geocode
|
|||||||
$aFilteredIDs = array();
|
$aFilteredIDs = array();
|
||||||
if ($aFilterSql) {
|
if ($aFilterSql) {
|
||||||
$sSQL = join(' UNION ', $aFilterSql);
|
$sSQL = join(' UNION ', $aFilterSql);
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aFilteredIDs = chksql($this->oDB->getCol($sSQL));
|
$aFilteredIDs = chksql($this->oDB->getCol($sSQL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -824,7 +820,7 @@ class Geocode
|
|||||||
$aResults = $tempIDs;
|
$aResults = $tempIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sizeof($aResults)) break;
|
if (!empty($aResults)) break;
|
||||||
if ($iGroupLoop > 4) break;
|
if ($iGroupLoop > 4) break;
|
||||||
if ($iQueryLoop > 30) break;
|
if ($iQueryLoop > 30) break;
|
||||||
}
|
}
|
||||||
@@ -835,7 +831,7 @@ class Geocode
|
|||||||
|
|
||||||
$oLookup = $oReverse->lookupPoint($oCtx->sqlNear, false);
|
$oLookup = $oReverse->lookupPoint($oCtx->sqlNear, false);
|
||||||
|
|
||||||
if (CONST_Debug) var_dump('Reverse search', $aLookup);
|
Debug::printVar('Reverse search', $oLookup);
|
||||||
|
|
||||||
if ($oLookup) {
|
if ($oLookup) {
|
||||||
$aResults = array($oLookup->iId => $oLookup);
|
$aResults = array($oLookup->iId => $oLookup);
|
||||||
@@ -843,7 +839,7 @@ class Geocode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No results? Done
|
// No results? Done
|
||||||
if (!sizeof($aResults)) {
|
if (empty($aResults)) {
|
||||||
if ($this->bFallback) {
|
if ($this->bFallback) {
|
||||||
if ($this->fallbackStructuredQuery()) {
|
if ($this->fallbackStructuredQuery()) {
|
||||||
return $this->lookup();
|
return $this->lookup();
|
||||||
@@ -864,20 +860,17 @@ class Geocode
|
|||||||
|
|
||||||
$aSearchResults = $this->oPlaceLookup->lookup($aResults);
|
$aSearchResults = $this->oPlaceLookup->lookup($aResults);
|
||||||
|
|
||||||
$aClassType = getClassTypesWithImportance();
|
$aClassType = ClassTypes\getListWithImportance();
|
||||||
$aRecheckWords = preg_split('/\b[\s,\\-]*/u', $sQuery);
|
$aRecheckWords = preg_split('/\b[\s,\\-]*/u', $sQuery);
|
||||||
foreach ($aRecheckWords as $i => $sWord) {
|
foreach ($aRecheckWords as $i => $sWord) {
|
||||||
if (!preg_match('/[\pL\pN]/', $sWord)) unset($aRecheckWords[$i]);
|
if (!preg_match('/[\pL\pN]/', $sWord)) unset($aRecheckWords[$i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONST_Debug) {
|
Debug::printVar('Recheck words', $aRecheckWords);
|
||||||
echo '<i>Recheck words:<\i>';
|
|
||||||
var_dump($aRecheckWords);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($aSearchResults as $iIdx => $aResult) {
|
foreach ($aSearchResults as $iIdx => $aResult) {
|
||||||
// Default
|
// Default
|
||||||
$fDiameter = getResultDiameter($aResult);
|
$fDiameter = ClassTypes\getProperty($aResult, 'defdiameter', 0.0001);
|
||||||
|
|
||||||
$aOutlineResult = $this->oPlaceLookup->getOutlines($aResult['place_id'], $aResult['lon'], $aResult['lat'], $fDiameter/2);
|
$aOutlineResult = $this->oPlaceLookup->getOutlines($aResult['place_id'], $aResult['lon'], $aResult['lat'], $fDiameter/2);
|
||||||
if ($aOutlineResult) {
|
if ($aOutlineResult) {
|
||||||
@@ -891,27 +884,15 @@ class Geocode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is there an icon set for this type of result?
|
// Is there an icon set for this type of result?
|
||||||
if (isset($aClassType[$aResult['class'].':'.$aResult['type']]['icon'])
|
$aClassInfo = ClassTypes\getInfo($aResult);
|
||||||
&& $aClassType[$aResult['class'].':'.$aResult['type']]['icon']
|
|
||||||
) {
|
|
||||||
$aResult['icon'] = CONST_Website_BaseURL.'images/mapicons/'.$aClassType[$aResult['class'].':'.$aResult['type']]['icon'].'.p.20.png';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['label'])
|
if ($aClassInfo) {
|
||||||
&& $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['label']
|
if (isset($aClassInfo['icon'])) {
|
||||||
) {
|
$aResult['icon'] = CONST_Website_BaseURL.'images/mapicons/'.$aClassInfo['icon'].'.p.20.png';
|
||||||
$aResult['label'] = $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['label'];
|
}
|
||||||
} elseif (isset($aClassType[$aResult['class'].':'.$aResult['type']]['label'])
|
|
||||||
&& $aClassType[$aResult['class'].':'.$aResult['type']]['label']
|
if (isset($aClassInfo['label'])) {
|
||||||
) {
|
$aResult['label'] = $aClassInfo['label'];
|
||||||
$aResult['label'] = $aClassType[$aResult['class'].':'.$aResult['type']]['label'];
|
|
||||||
}
|
|
||||||
// if tag '&addressdetails=1' is set in query
|
|
||||||
if ($this->bIncludeAddressDetails) {
|
|
||||||
// getAddressDetails() is defined in lib.php and uses the SQL function get_addressdata in functions.sql
|
|
||||||
$aResult['address'] = getAddressDetails($this->oDB, $sLanguagePrefArraySQL, $aResult['place_id'], $aResult['country_code'], $aResults[$aResult['place_id']]->iHouseNumber);
|
|
||||||
if ($aResult['extra_place'] == 'city' && !isset($aResult['address']['city'])) {
|
|
||||||
$aResult['address'] = array_merge(array('city' => array_values($aResult['address'])[0]), $aResult['address']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -921,12 +902,12 @@ class Geocode
|
|||||||
$aResult['importance'] = 0.001;
|
$aResult['importance'] = 0.001;
|
||||||
$aResult['foundorder'] = $aResult['addressimportance'];
|
$aResult['foundorder'] = $aResult['addressimportance'];
|
||||||
} else {
|
} else {
|
||||||
// Adjust importance for the number of exact string matches in the result
|
$aResult['importance'] = max(0.001, $aResult['importance']);
|
||||||
$aResult['importance'] *= $this->viewboxImportanceFactor(
|
$aResult['importance'] *= $this->viewboxImportanceFactor(
|
||||||
$aResult['lon'],
|
$aResult['lon'],
|
||||||
$aResult['lat']
|
$aResult['lat']
|
||||||
);
|
);
|
||||||
$aResult['importance'] = max(0.001, $aResult['importance']);
|
// Adjust importance for the number of exact string matches in the result
|
||||||
$iCountWords = 0;
|
$iCountWords = 0;
|
||||||
$sAddress = $aResult['langaddress'];
|
$sAddress = $aResult['langaddress'];
|
||||||
foreach ($aRecheckWords as $i => $sWord) {
|
foreach ($aRecheckWords as $i => $sWord) {
|
||||||
@@ -952,18 +933,16 @@ class Geocode
|
|||||||
$aResult['foundorder'] += 0.01;
|
$aResult['foundorder'] += 0.01;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (CONST_Debug) var_dump($aResult);
|
|
||||||
$aSearchResults[$iIdx] = $aResult;
|
$aSearchResults[$iIdx] = $aResult;
|
||||||
}
|
}
|
||||||
uasort($aSearchResults, 'byImportance');
|
uasort($aSearchResults, 'byImportance');
|
||||||
|
Debug::printVar('Pre-filter results', $aSearchResults);
|
||||||
|
|
||||||
$aOSMIDDone = array();
|
$aOSMIDDone = array();
|
||||||
$aClassTypeNameDone = array();
|
$aClassTypeNameDone = array();
|
||||||
$aToFilter = $aSearchResults;
|
$aToFilter = $aSearchResults;
|
||||||
$aSearchResults = array();
|
$aSearchResults = array();
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($aToFilter);
|
|
||||||
|
|
||||||
$bFirst = true;
|
$bFirst = true;
|
||||||
foreach ($aToFilter as $aResult) {
|
foreach ($aToFilter as $aResult) {
|
||||||
$this->aExcludePlaceIDs[$aResult['place_id']] = $aResult['place_id'];
|
$this->aExcludePlaceIDs[$aResult['place_id']] = $aResult['place_id'];
|
||||||
@@ -982,10 +961,32 @@ class Geocode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Absolute limit on number of results
|
// Absolute limit on number of results
|
||||||
if (sizeof($aSearchResults) >= $this->iFinalLimit) break;
|
if (count($aSearchResults) >= $this->iFinalLimit) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($aSearchResults);
|
Debug::printVar('Post-filter results', $aSearchResults);
|
||||||
return $aSearchResults;
|
return $aSearchResults;
|
||||||
} // end lookup()
|
} // end lookup()
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'Query' => $this->sQuery,
|
||||||
|
'Structured query' => $this->aStructuredQuery,
|
||||||
|
'Name keys' => Debug::fmtArrayVals($this->aLangPrefOrder),
|
||||||
|
'Excluded place IDs' => Debug::fmtArrayVals($this->aExcludePlaceIDs),
|
||||||
|
'Try reversed query'=> $this->bReverseInPlan,
|
||||||
|
'Limit (for searches)' => $this->iLimit,
|
||||||
|
'Limit (for results)'=> $this->iFinalLimit,
|
||||||
|
'Country codes' => Debug::fmtArrayVals($this->aCountryCodes),
|
||||||
|
'Bounded search' => $this->bBoundedSearch,
|
||||||
|
'Viewbox' => Debug::fmtArrayVals($this->aViewBox),
|
||||||
|
'Route points' => Debug::fmtArrayVals($this->aRoutePoints),
|
||||||
|
'Route width' => $this->aRouteWidth,
|
||||||
|
'Max rank' => $this->iMaxRank,
|
||||||
|
'Min address rank' => $this->iMinAddressRank,
|
||||||
|
'Max address rank' => $this->iMaxAddressRank,
|
||||||
|
'Address rank list' => Debug::fmtArrayVals($this->aAddressRankList)
|
||||||
|
);
|
||||||
|
}
|
||||||
} // end class
|
} // end class
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ParameterParser
|
|||||||
|
|
||||||
public function getInt($sName, $bDefault = false)
|
public function getInt($sName, $bDefault = false)
|
||||||
{
|
{
|
||||||
if (!isset($this->aParams[$sName]) || strlen($this->aParams[$sName]) == 0) {
|
if (!isset($this->aParams[$sName])) {
|
||||||
return $bDefault;
|
return $bDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ class ParameterParser
|
|||||||
|
|
||||||
public function getFloat($sName, $bDefault = false)
|
public function getFloat($sName, $bDefault = false)
|
||||||
{
|
{
|
||||||
if (!isset($this->aParams[$sName]) || strlen($this->aParams[$sName]) == 0) {
|
if (!isset($this->aParams[$sName])) {
|
||||||
return $bDefault;
|
return $bDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,8 @@ class ParameterParser
|
|||||||
$sValue = $this->getString($sName);
|
$sValue = $this->getString($sName);
|
||||||
|
|
||||||
if ($sValue) {
|
if ($sValue) {
|
||||||
return explode(',', $sValue);
|
// removes all NULL, FALSE and Empty Strings but leaves 0 (zero) values
|
||||||
|
return array_values(array_filter(explode(',', $sValue), 'strlen'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $aDefault;
|
return $aDefault;
|
||||||
@@ -98,7 +99,7 @@ class ParameterParser
|
|||||||
arsort($aLanguages);
|
arsort($aLanguages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!sizeof($aLanguages) && CONST_Default_Language) {
|
if (empty($aLanguages) && CONST_Default_Language) {
|
||||||
$aLanguages[CONST_Default_Language] = 1;
|
$aLanguages[CONST_Default_Language] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class Phrase
|
|||||||
$aResult = array(array(join(' ', $aWords)));
|
$aResult = array(array(join(' ', $aWords)));
|
||||||
$sFirstToken = '';
|
$sFirstToken = '';
|
||||||
if ($iDepth < Phrase::MAX_DEPTH) {
|
if ($iDepth < Phrase::MAX_DEPTH) {
|
||||||
while (sizeof($aWords) > 1) {
|
while (count($aWords) > 1) {
|
||||||
$sWord = array_shift($aWords);
|
$sWord = array_shift($aWords);
|
||||||
$sFirstToken .= ($sFirstToken?' ':'').$sWord;
|
$sFirstToken .= ($sFirstToken?' ':'').$sWord;
|
||||||
$aRest = $this->createWordSets($aWords, $iDepth + 1);
|
$aRest = $this->createWordSets($aWords, $iDepth + 1);
|
||||||
@@ -101,7 +101,7 @@ class Phrase
|
|||||||
$aResult = array(array(join(' ', $aWords)));
|
$aResult = array(array(join(' ', $aWords)));
|
||||||
$sFirstToken = '';
|
$sFirstToken = '';
|
||||||
if ($iDepth < Phrase::MAX_DEPTH) {
|
if ($iDepth < Phrase::MAX_DEPTH) {
|
||||||
while (sizeof($aWords) > 1) {
|
while (count($aWords) > 1) {
|
||||||
$sWord = array_pop($aWords);
|
$sWord = array_pop($aWords);
|
||||||
$sFirstToken = $sWord.($sFirstToken?' ':'').$sFirstToken;
|
$sFirstToken = $sWord.($sFirstToken?' ':'').$sFirstToken;
|
||||||
$aRest = $this->createInverseWordSets($aWords, $iDepth + 1);
|
$aRest = $this->createInverseWordSets($aWords, $iDepth + 1);
|
||||||
@@ -113,4 +113,14 @@ class Phrase
|
|||||||
|
|
||||||
return $aResult;
|
return $aResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'Type' => $this->sPhraseType,
|
||||||
|
'Phrase' => $this->sPhrase,
|
||||||
|
'Words' => $this->aWords,
|
||||||
|
'WordSets' => $this->aWordSets
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Nominatim;
|
namespace Nominatim;
|
||||||
|
|
||||||
|
require_once(CONST_BasePath.'/lib/AddressDetails.php');
|
||||||
require_once(CONST_BasePath.'/lib/Result.php');
|
require_once(CONST_BasePath.'/lib/Result.php');
|
||||||
|
|
||||||
class PlaceLookup
|
class PlaceLookup
|
||||||
@@ -42,29 +43,37 @@ class PlaceLookup
|
|||||||
$this->bIncludePolygonAsPoints = $b;
|
$this->bIncludePolygonAsPoints = $b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setIncludeAddressDetails($b)
|
||||||
|
{
|
||||||
|
$this->bAddressDetails = $b;
|
||||||
|
}
|
||||||
|
|
||||||
public function loadParamArray($oParams, $sGeomType = null)
|
public function loadParamArray($oParams, $sGeomType = null)
|
||||||
{
|
{
|
||||||
$aLangs = $oParams->getPreferredLanguages();
|
$aLangs = $oParams->getPreferredLanguages();
|
||||||
$this->aLangPrefOrderSql =
|
$this->aLangPrefOrderSql =
|
||||||
'ARRAY['.join(',', array_map('getDBQuoted', $aLangs)).']';
|
'ARRAY['.join(',', array_map('getDBQuoted', $aLangs)).']';
|
||||||
|
|
||||||
$this->bAddressDetails = $oParams->getBool('addressdetails', true);
|
|
||||||
$this->bExtraTags = $oParams->getBool('extratags', false);
|
$this->bExtraTags = $oParams->getBool('extratags', false);
|
||||||
$this->bNameDetails = $oParams->getBool('namedetails', false);
|
$this->bNameDetails = $oParams->getBool('namedetails', false);
|
||||||
|
|
||||||
$this->bDeDupe = $oParams->getBool('dedupe', $this->bDeDupe);
|
$this->bDeDupe = $oParams->getBool('dedupe', $this->bDeDupe);
|
||||||
|
|
||||||
if ($sGeomType === null || $sGeomType == 'text') {
|
|
||||||
$this->bIncludePolygonAsText = $oParams->getBool('polygon_text');
|
|
||||||
}
|
|
||||||
if ($sGeomType === null || $sGeomType == 'geojson') {
|
if ($sGeomType === null || $sGeomType == 'geojson') {
|
||||||
$this->bIncludePolygonAsGeoJSON = $oParams->getBool('polygon_geojson');
|
$this->bIncludePolygonAsGeoJSON = $oParams->getBool('polygon_geojson');
|
||||||
|
$this->bIncludePolygonAsPoints = false;
|
||||||
}
|
}
|
||||||
if ($sGeomType === null || $sGeomType == 'kml') {
|
|
||||||
$this->bIncludePolygonAsKML = $oParams->getBool('polygon_kml');
|
if ($oParams->getString('format', '') !== 'geojson') {
|
||||||
}
|
if ($sGeomType === null || $sGeomType == 'text') {
|
||||||
if ($sGeomType === null || $sGeomType == 'svg') {
|
$this->bIncludePolygonAsText = $oParams->getBool('polygon_text');
|
||||||
$this->bIncludePolygonAsSVG = $oParams->getBool('polygon_svg');
|
}
|
||||||
|
if ($sGeomType === null || $sGeomType == 'kml') {
|
||||||
|
$this->bIncludePolygonAsKML = $oParams->getBool('polygon_kml');
|
||||||
|
}
|
||||||
|
if ($sGeomType === null || $sGeomType == 'svg') {
|
||||||
|
$this->bIncludePolygonAsSVG = $oParams->getBool('polygon_svg');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->fPolygonSimplificationThreshold
|
$this->fPolygonSimplificationThreshold
|
||||||
= $oParams->getFloat('polygon_threshold', 0.0);
|
= $oParams->getFloat('polygon_threshold', 0.0);
|
||||||
@@ -127,11 +136,6 @@ class PlaceLookup
|
|||||||
'ARRAY['.join(',', array_map('getDBQuoted', $aLangPrefOrder)).']';
|
'ARRAY['.join(',', array_map('getDBQuoted', $aLangPrefOrder)).']';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setIncludeAddressDetails($bAddressDetails = true)
|
|
||||||
{
|
|
||||||
$this->bAddressDetails = $bAddressDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addressImportanceSql($sGeometry, $sPlaceId)
|
private function addressImportanceSql($sGeometry, $sPlaceId)
|
||||||
{
|
{
|
||||||
if ($this->sAnchorSql) {
|
if ($this->sAnchorSql) {
|
||||||
@@ -150,6 +154,9 @@ class PlaceLookup
|
|||||||
|
|
||||||
private function langAddressSql($sHousenumber)
|
private function langAddressSql($sHousenumber)
|
||||||
{
|
{
|
||||||
|
if ($this->bAddressDetails)
|
||||||
|
return ''; // langaddress will be computed from address details
|
||||||
|
|
||||||
return 'get_address_by_language(place_id,'.$sHousenumber.','.$this->aLangPrefOrderSql.') AS langaddress,';
|
return 'get_address_by_language(place_id,'.$sHousenumber.','.$this->aLangPrefOrderSql.') AS langaddress,';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,19 +171,21 @@ class PlaceLookup
|
|||||||
|
|
||||||
$aResults = $this->lookup(array($iPlaceID => new Result($iPlaceID)));
|
$aResults = $this->lookup(array($iPlaceID => new Result($iPlaceID)));
|
||||||
|
|
||||||
return sizeof($aResults) ? reset($aResults) : null;
|
return empty($aResults) ? null : reset($aResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function lookup($aResults, $iMinRank = 0, $iMaxRank = 30)
|
public function lookup($aResults, $iMinRank = 0, $iMaxRank = 30)
|
||||||
{
|
{
|
||||||
if (!sizeof($aResults)) {
|
Debug::newFunction('Place lookup');
|
||||||
|
|
||||||
|
if (empty($aResults)) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
$aSubSelects = array();
|
$aSubSelects = array();
|
||||||
|
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX);
|
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX);
|
||||||
if (CONST_Debug) var_dump('PLACEX', $sPlaceIDs);
|
|
||||||
if ($sPlaceIDs) {
|
if ($sPlaceIDs) {
|
||||||
|
Debug::printVar('Ids from placex', $sPlaceIDs);
|
||||||
$sSQL = 'SELECT ';
|
$sSQL = 'SELECT ';
|
||||||
$sSQL .= ' osm_type,';
|
$sSQL .= ' osm_type,';
|
||||||
$sSQL .= ' osm_id,';
|
$sSQL .= ' osm_id,';
|
||||||
@@ -233,7 +242,7 @@ class PlaceLookup
|
|||||||
$sSQL .= ' country_code, ';
|
$sSQL .= ' country_code, ';
|
||||||
$sSQL .= ' importance, ';
|
$sSQL .= ' importance, ';
|
||||||
if (!$this->bDeDupe) $sSQL .= 'place_id,';
|
if (!$this->bDeDupe) $sSQL .= 'place_id,';
|
||||||
$sSQL .= ' langaddress, ';
|
if (!$this->bAddressDetails) $sSQL .= 'langaddress, ';
|
||||||
$sSQL .= ' placename, ';
|
$sSQL .= ' placename, ';
|
||||||
$sSQL .= ' ref, ';
|
$sSQL .= ' ref, ';
|
||||||
if ($this->bExtraTags) $sSQL .= 'extratags, ';
|
if ($this->bExtraTags) $sSQL .= 'extratags, ';
|
||||||
@@ -246,23 +255,24 @@ class PlaceLookup
|
|||||||
// postcode table
|
// postcode table
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_POSTCODE);
|
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_POSTCODE);
|
||||||
if ($sPlaceIDs) {
|
if ($sPlaceIDs) {
|
||||||
|
Debug::printVar('Ids from location_postcode', $sPlaceIDs);
|
||||||
$sSQL = 'SELECT';
|
$sSQL = 'SELECT';
|
||||||
$sSQL .= " 'P' as osm_type,";
|
$sSQL .= " 'P' as osm_type,";
|
||||||
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id = lp.parent_place_id) as osm_id,';
|
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id = lp.parent_place_id) as osm_id,';
|
||||||
$sSQL .= " 'place' as class, 'postcode' as type,";
|
$sSQL .= " 'place' as class, 'postcode' as type,";
|
||||||
$sSQL .= ' null as admin_level, rank_search, rank_address,';
|
$sSQL .= ' null::smallint as admin_level, rank_search, rank_address,';
|
||||||
$sSQL .= ' place_id, parent_place_id,';
|
$sSQL .= ' place_id, parent_place_id,';
|
||||||
$sSQL .= ' null as housenumber,';
|
$sSQL .= ' -1 as housenumber,';
|
||||||
$sSQL .= ' country_code,';
|
$sSQL .= ' country_code,';
|
||||||
$sSQL .= $this->langAddressSql('-1');
|
$sSQL .= $this->langAddressSql('-1');
|
||||||
$sSQL .= ' postcode as placename,';
|
$sSQL .= ' postcode as placename,';
|
||||||
$sSQL .= ' postcode as ref,';
|
$sSQL .= ' postcode as ref,';
|
||||||
if ($this->bExtraTags) $sSQL .= 'null AS extra,';
|
if ($this->bExtraTags) $sSQL .= 'null::text AS extra,';
|
||||||
if ($this->bNameDetails) $sSQL .= 'null AS names,';
|
if ($this->bNameDetails) $sSQL .= 'null::text AS names,';
|
||||||
$sSQL .= ' ST_x(geometry) AS lon, ST_y(geometry) AS lat,';
|
$sSQL .= ' ST_x(geometry) AS lon, ST_y(geometry) AS lat,';
|
||||||
$sSQL .= ' (0.75-(rank_search::float/40)) AS importance, ';
|
$sSQL .= ' (0.75-(rank_search::float/40)) AS importance, ';
|
||||||
$sSQL .= $this->addressImportanceSql('geometry', 'lp.parent_place_id');
|
$sSQL .= $this->addressImportanceSql('geometry', 'lp.parent_place_id');
|
||||||
$sSQL .= ' null AS extra_place ';
|
$sSQL .= ' null::text AS extra_place ';
|
||||||
$sSQL .= 'FROM location_postcode lp';
|
$sSQL .= 'FROM location_postcode lp';
|
||||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
||||||
$sSQL .= " AND lp.rank_address between $iMinRank and $iMaxRank";
|
$sSQL .= " AND lp.rank_address between $iMinRank and $iMaxRank";
|
||||||
@@ -276,6 +286,7 @@ class PlaceLookup
|
|||||||
if (CONST_Use_US_Tiger_Data) {
|
if (CONST_Use_US_Tiger_Data) {
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_TIGER);
|
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_TIGER);
|
||||||
if ($sPlaceIDs) {
|
if ($sPlaceIDs) {
|
||||||
|
Debug::printVar('Ids from Tiger table', $sPlaceIDs);
|
||||||
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_TIGER);
|
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_TIGER);
|
||||||
// Tiger search only if a housenumber was searched and if it was found
|
// Tiger search only if a housenumber was searched and if it was found
|
||||||
// (realized through a join)
|
// (realized through a join)
|
||||||
@@ -284,7 +295,7 @@ class PlaceLookup
|
|||||||
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id=blub.parent_place_id) as osm_id, ';
|
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id=blub.parent_place_id) as osm_id, ';
|
||||||
$sSQL .= " 'place' AS class, ";
|
$sSQL .= " 'place' AS class, ";
|
||||||
$sSQL .= " 'house' AS type, ";
|
$sSQL .= " 'house' AS type, ";
|
||||||
$sSQL .= ' null AS admin_level, ';
|
$sSQL .= ' null::smallint AS admin_level, ';
|
||||||
$sSQL .= ' 30 AS rank_search, ';
|
$sSQL .= ' 30 AS rank_search, ';
|
||||||
$sSQL .= ' 30 AS rank_address, ';
|
$sSQL .= ' 30 AS rank_address, ';
|
||||||
$sSQL .= ' place_id, ';
|
$sSQL .= ' place_id, ';
|
||||||
@@ -292,15 +303,15 @@ class PlaceLookup
|
|||||||
$sSQL .= ' housenumber_for_place as housenumber,';
|
$sSQL .= ' housenumber_for_place as housenumber,';
|
||||||
$sSQL .= " 'us' AS country_code, ";
|
$sSQL .= " 'us' AS country_code, ";
|
||||||
$sSQL .= $this->langAddressSql('housenumber_for_place');
|
$sSQL .= $this->langAddressSql('housenumber_for_place');
|
||||||
$sSQL .= ' null AS placename, ';
|
$sSQL .= ' null::text AS placename, ';
|
||||||
$sSQL .= ' null AS ref, ';
|
$sSQL .= ' null::text AS ref, ';
|
||||||
if ($this->bExtraTags) $sSQL .= 'null AS extra,';
|
if ($this->bExtraTags) $sSQL .= 'null::text AS extra,';
|
||||||
if ($this->bNameDetails) $sSQL .= 'null AS names,';
|
if ($this->bNameDetails) $sSQL .= 'null::text AS names,';
|
||||||
$sSQL .= ' st_x(centroid) AS lon, ';
|
$sSQL .= ' st_x(centroid) AS lon, ';
|
||||||
$sSQL .= ' st_y(centroid) AS lat,';
|
$sSQL .= ' st_y(centroid) AS lat,';
|
||||||
$sSQL .= ' -1.15 AS importance, ';
|
$sSQL .= ' -1.15 AS importance, ';
|
||||||
$sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id');
|
$sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id');
|
||||||
$sSQL .= ' null AS extra_place ';
|
$sSQL .= ' null::text AS extra_place ';
|
||||||
$sSQL .= ' FROM (';
|
$sSQL .= ' FROM (';
|
||||||
$sSQL .= ' SELECT place_id, '; // interpolate the Tiger housenumbers here
|
$sSQL .= ' SELECT place_id, '; // interpolate the Tiger housenumbers here
|
||||||
$sSQL .= ' ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) AS centroid, ';
|
$sSQL .= ' ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) AS centroid, ';
|
||||||
@@ -321,6 +332,7 @@ class PlaceLookup
|
|||||||
// osmline - interpolated housenumbers
|
// osmline - interpolated housenumbers
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_OSMLINE);
|
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_OSMLINE);
|
||||||
if ($sPlaceIDs) {
|
if ($sPlaceIDs) {
|
||||||
|
Debug::printVar('Ids from interpolation', $sPlaceIDs);
|
||||||
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_OSMLINE);
|
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_OSMLINE);
|
||||||
// interpolation line search only if a housenumber was searched
|
// interpolation line search only if a housenumber was searched
|
||||||
// (realized through a join)
|
// (realized through a join)
|
||||||
@@ -329,7 +341,7 @@ class PlaceLookup
|
|||||||
$sSQL .= ' osm_id, ';
|
$sSQL .= ' osm_id, ';
|
||||||
$sSQL .= " 'place' AS class, ";
|
$sSQL .= " 'place' AS class, ";
|
||||||
$sSQL .= " 'house' AS type, ";
|
$sSQL .= " 'house' AS type, ";
|
||||||
$sSQL .= ' 15 AS admin_level, ';
|
$sSQL .= ' null::smallint AS admin_level, ';
|
||||||
$sSQL .= ' 30 AS rank_search, ';
|
$sSQL .= ' 30 AS rank_search, ';
|
||||||
$sSQL .= ' 30 AS rank_address, ';
|
$sSQL .= ' 30 AS rank_address, ';
|
||||||
$sSQL .= ' place_id, ';
|
$sSQL .= ' place_id, ';
|
||||||
@@ -337,16 +349,16 @@ class PlaceLookup
|
|||||||
$sSQL .= ' housenumber_for_place as housenumber,';
|
$sSQL .= ' housenumber_for_place as housenumber,';
|
||||||
$sSQL .= ' country_code, ';
|
$sSQL .= ' country_code, ';
|
||||||
$sSQL .= $this->langAddressSql('housenumber_for_place');
|
$sSQL .= $this->langAddressSql('housenumber_for_place');
|
||||||
$sSQL .= ' null AS placename, ';
|
$sSQL .= ' null::text AS placename, ';
|
||||||
$sSQL .= ' null AS ref, ';
|
$sSQL .= ' null::text AS ref, ';
|
||||||
if ($this->bExtraTags) $sSQL .= 'null AS extra, ';
|
if ($this->bExtraTags) $sSQL .= 'null::text AS extra, ';
|
||||||
if ($this->bNameDetails) $sSQL .= 'null AS names, ';
|
if ($this->bNameDetails) $sSQL .= 'null::text AS names, ';
|
||||||
$sSQL .= ' st_x(centroid) AS lon, ';
|
$sSQL .= ' st_x(centroid) AS lon, ';
|
||||||
$sSQL .= ' st_y(centroid) AS lat, ';
|
$sSQL .= ' st_y(centroid) AS lat, ';
|
||||||
// slightly smaller than the importance for normal houses
|
// slightly smaller than the importance for normal houses
|
||||||
$sSQL .= ' -0.1 AS importance, ';
|
$sSQL .= ' -0.1 AS importance, ';
|
||||||
$sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id');
|
$sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id');
|
||||||
$sSQL .= ' null AS extra_place ';
|
$sSQL .= ' null::text AS extra_place ';
|
||||||
$sSQL .= ' FROM (';
|
$sSQL .= ' FROM (';
|
||||||
$sSQL .= ' SELECT ';
|
$sSQL .= ' SELECT ';
|
||||||
$sSQL .= ' osm_id, ';
|
$sSQL .= ' osm_id, ';
|
||||||
@@ -378,7 +390,7 @@ class PlaceLookup
|
|||||||
$sSQL .= ' place_id AS osm_id, ';
|
$sSQL .= ' place_id AS osm_id, ';
|
||||||
$sSQL .= " 'place' AS class,";
|
$sSQL .= " 'place' AS class,";
|
||||||
$sSQL .= " 'house' AS type, ";
|
$sSQL .= " 'house' AS type, ";
|
||||||
$sSQL .= ' null AS admin_level, ';
|
$sSQL .= ' null::smallint AS admin_level, ';
|
||||||
$sSQL .= ' 30 AS rank_search,';
|
$sSQL .= ' 30 AS rank_search,';
|
||||||
$sSQL .= ' 30 AS rank_address, ';
|
$sSQL .= ' 30 AS rank_address, ';
|
||||||
$sSQL .= ' place_id,';
|
$sSQL .= ' place_id,';
|
||||||
@@ -386,10 +398,10 @@ class PlaceLookup
|
|||||||
$sSQL .= ' housenumber,';
|
$sSQL .= ' housenumber,';
|
||||||
$sSQL .= " 'us' AS country_code, ";
|
$sSQL .= " 'us' AS country_code, ";
|
||||||
$sSQL .= $this->langAddressSql('-1');
|
$sSQL .= $this->langAddressSql('-1');
|
||||||
$sSQL .= ' null AS placename, ';
|
$sSQL .= ' null::text AS placename, ';
|
||||||
$sSQL .= ' null AS ref, ';
|
$sSQL .= ' null::text AS ref, ';
|
||||||
if ($this->bExtraTags) $sSQL .= 'null AS extra, ';
|
if ($this->bExtraTags) $sSQL .= 'null::text AS extra, ';
|
||||||
if ($this->bNameDetails) $sSQL .= 'null AS names, ';
|
if ($this->bNameDetails) $sSQL .= 'null::text AS names, ';
|
||||||
$sSQL .= ' ST_X(centroid) AS lon, ';
|
$sSQL .= ' ST_X(centroid) AS lon, ';
|
||||||
$sSQL .= ' ST_Y(centroid) AS lat, ';
|
$sSQL .= ' ST_Y(centroid) AS lat, ';
|
||||||
$sSQL .= ' -1.10 AS importance, ';
|
$sSQL .= ' -1.10 AS importance, ';
|
||||||
@@ -397,7 +409,7 @@ class PlaceLookup
|
|||||||
'centroid',
|
'centroid',
|
||||||
'location_property_aux.parent_place_id'
|
'location_property_aux.parent_place_id'
|
||||||
);
|
);
|
||||||
$sSQL .= ' null AS extra_place ';
|
$sSQL .= ' null::text AS extra_place ';
|
||||||
$sSQL .= ' FROM location_property_aux ';
|
$sSQL .= ' FROM location_property_aux ';
|
||||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
||||||
|
|
||||||
@@ -406,25 +418,24 @@ class PlaceLookup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($aSubSelects);
|
if (empty($aSubSelects)) {
|
||||||
|
|
||||||
if (!sizeof($aSubSelects)) {
|
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$aPlaces = chksql(
|
$sSQL = join(' UNION ', $aSubSelects);
|
||||||
$this->oDB->getAll(join(' UNION ', $aSubSelects)),
|
Debug::printSQL($sSQL);
|
||||||
'Could not lookup place'
|
$aPlaces = chksql($this->oDB->getAll($sSQL), 'Could not lookup place');
|
||||||
);
|
|
||||||
|
|
||||||
$aClassType = getClassTypes();
|
|
||||||
foreach ($aPlaces as &$aPlace) {
|
foreach ($aPlaces as &$aPlace) {
|
||||||
if ($this->bAddressDetails) {
|
if ($this->bAddressDetails) {
|
||||||
// to get addressdetails for tiger data, the housenumber is needed
|
// to get addressdetails for tiger data, the housenumber is needed
|
||||||
$aPlace['aAddress'] = $this->getAddressNames(
|
$aPlace['address'] = new AddressDetails(
|
||||||
|
$this->oDB,
|
||||||
$aPlace['place_id'],
|
$aPlace['place_id'],
|
||||||
$aPlace['housenumber']
|
$aPlace['housenumber'],
|
||||||
|
$this->aLangPrefOrderSql
|
||||||
);
|
);
|
||||||
|
$aPlace['langaddress'] = $aPlace['address']->getLocaleAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->bExtraTags) {
|
if ($this->bExtraTags) {
|
||||||
@@ -443,77 +454,18 @@ class PlaceLookup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sAddressType = '';
|
$aPlace['addresstype'] = ClassTypes\getProperty(
|
||||||
$sClassType = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
$aPlace,
|
||||||
if (isset($aClassType[$sClassType]) && isset($aClassType[$sClassType]['simplelabel'])) {
|
'simplelabel',
|
||||||
$sAddressType = $aClassType[$aClassType]['simplelabel'];
|
$aPlace['class']
|
||||||
} else {
|
);
|
||||||
$sClassType = $aPlace['class'].':'.$aPlace['type'];
|
|
||||||
if (isset($aClassType[$sClassType]) && isset($aClassType[$sClassType]['simplelabel']))
|
|
||||||
$sAddressType = $aClassType[$sClassType]['simplelabel'];
|
|
||||||
else $sAddressType = $aPlace['class'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$aPlace['addresstype'] = $sAddressType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($aPlaces);
|
Debug::printVar('Places', $aPlaces);
|
||||||
|
|
||||||
return $aPlaces;
|
return $aPlaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getAddressDetails($iPlaceID, $bAll, $sHousenumber)
|
|
||||||
{
|
|
||||||
$sSQL = 'SELECT *,';
|
|
||||||
$sSQL .= ' get_name_by_language(name,'.$this->aLangPrefOrderSql.') as localname';
|
|
||||||
$sSQL .= ' FROM get_addressdata('.$iPlaceID.','.$sHousenumber.')';
|
|
||||||
if (!$bAll) {
|
|
||||||
$sSQL .= " WHERE isaddress OR type = 'country_code'";
|
|
||||||
}
|
|
||||||
$sSQL .= ' ORDER BY rank_address desc,isaddress DESC';
|
|
||||||
|
|
||||||
return chksql($this->oDB->getAll($sSQL));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAddressNames($iPlaceID, $sHousenumber = null)
|
|
||||||
{
|
|
||||||
$aAddressLines = $this->getAddressDetails(
|
|
||||||
$iPlaceID,
|
|
||||||
false,
|
|
||||||
$sHousenumber === null ? -1 : $sHousenumber
|
|
||||||
);
|
|
||||||
|
|
||||||
$aAddress = array();
|
|
||||||
$aFallback = array();
|
|
||||||
$aClassType = getClassTypes();
|
|
||||||
foreach ($aAddressLines as $aLine) {
|
|
||||||
$bFallback = false;
|
|
||||||
$aTypeLabel = false;
|
|
||||||
if (isset($aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']])) {
|
|
||||||
$aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']];
|
|
||||||
} elseif (isset($aClassType[$aLine['class'].':'.$aLine['type']])) {
|
|
||||||
$aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type']];
|
|
||||||
} elseif (isset($aClassType['boundary:administrative:'.((int)($aLine['rank_address']/2))])) {
|
|
||||||
$aTypeLabel = $aClassType['boundary:administrative:'.((int)($aLine['rank_address']/2))];
|
|
||||||
$bFallback = true;
|
|
||||||
} else {
|
|
||||||
$aTypeLabel = array('simplelabel' => 'address'.$aLine['rank_address']);
|
|
||||||
$bFallback = true;
|
|
||||||
}
|
|
||||||
if ($aTypeLabel && ((isset($aLine['localname']) && $aLine['localname']) || (isset($aLine['housenumber']) && $aLine['housenumber']))) {
|
|
||||||
$sTypeLabel = strtolower(isset($aTypeLabel['simplelabel'])?$aTypeLabel['simplelabel']:$aTypeLabel['label']);
|
|
||||||
$sTypeLabel = str_replace(' ', '_', $sTypeLabel);
|
|
||||||
if (!isset($aAddress[$sTypeLabel]) || (isset($aFallback[$sTypeLabel]) && $aFallback[$sTypeLabel]) || $aLine['class'] == 'place') {
|
|
||||||
$aAddress[$sTypeLabel] = $aLine['localname']?$aLine['localname']:$aLine['housenumber'];
|
|
||||||
}
|
|
||||||
$aFallback[$sTypeLabel] = $bFallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* returns an array which will contain the keys
|
/* returns an array which will contain the keys
|
||||||
* aBoundingBox
|
* aBoundingBox
|
||||||
* and may also contain one or more of the keys
|
* and may also contain one or more of the keys
|
||||||
@@ -526,7 +478,7 @@ class PlaceLookup
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
public function getOutlines($iPlaceID, $fLon = null, $fLat = null, $fRadius = null)
|
public function getOutlines($iPlaceID, $fLon = null, $fLat = null, $fRadius = null, $fLonReverse = null, $fLatReverse = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
$aOutlineResult = array();
|
$aOutlineResult = array();
|
||||||
@@ -534,15 +486,27 @@ class PlaceLookup
|
|||||||
|
|
||||||
if (CONST_Search_AreaPolygons) {
|
if (CONST_Search_AreaPolygons) {
|
||||||
// Get the bounding box and outline polygon
|
// Get the bounding box and outline polygon
|
||||||
$sSQL = 'select place_id,0 as numfeatures,st_area(geometry) as area,';
|
$sSQL = 'select place_id,0 as numfeatures,st_area(geometry) as area,';
|
||||||
$sSQL .= 'ST_Y(centroid) as centrelat,ST_X(centroid) as centrelon,';
|
if ($fLonReverse != null && $fLatReverse != null) {
|
||||||
$sSQL .= 'ST_YMin(geometry) as minlat,ST_YMax(geometry) as maxlat,';
|
$sSQL .= ' ST_Y(closest_point) as centrelat,';
|
||||||
$sSQL .= 'ST_XMin(geometry) as minlon,ST_XMax(geometry) as maxlon';
|
$sSQL .= ' ST_X(closest_point) as centrelon,';
|
||||||
|
} else {
|
||||||
|
$sSQL .= ' ST_Y(centroid) as centrelat, ST_X(centroid) as centrelon,';
|
||||||
|
}
|
||||||
|
$sSQL .= ' ST_YMin(geometry) as minlat,ST_YMax(geometry) as maxlat,';
|
||||||
|
$sSQL .= ' ST_XMin(geometry) as minlon,ST_XMax(geometry) as maxlon';
|
||||||
if ($this->bIncludePolygonAsGeoJSON) $sSQL .= ',ST_AsGeoJSON(geometry) as asgeojson';
|
if ($this->bIncludePolygonAsGeoJSON) $sSQL .= ',ST_AsGeoJSON(geometry) as asgeojson';
|
||||||
if ($this->bIncludePolygonAsKML) $sSQL .= ',ST_AsKML(geometry) as askml';
|
if ($this->bIncludePolygonAsKML) $sSQL .= ',ST_AsKML(geometry) as askml';
|
||||||
if ($this->bIncludePolygonAsSVG) $sSQL .= ',ST_AsSVG(geometry) as assvg';
|
if ($this->bIncludePolygonAsSVG) $sSQL .= ',ST_AsSVG(geometry) as assvg';
|
||||||
if ($this->bIncludePolygonAsText || $this->bIncludePolygonAsPoints) $sSQL .= ',ST_AsText(geometry) as astext';
|
if ($this->bIncludePolygonAsText || $this->bIncludePolygonAsPoints) $sSQL .= ',ST_AsText(geometry) as astext';
|
||||||
$sFrom = ' from placex where place_id = '.$iPlaceID;
|
if ($fLonReverse != null && $fLatReverse != null) {
|
||||||
|
$sFrom = ' from (SELECT * , CASE WHEN (class = \'highway\') AND (ST_GeometryType(geometry) = \'ST_LineString\') THEN ';
|
||||||
|
$sFrom .=' ST_ClosestPoint(geometry, ST_SetSRID(ST_Point('.$fLatReverse.','.$fLonReverse.'),4326))';
|
||||||
|
$sFrom .=' ELSE centroid END AS closest_point';
|
||||||
|
$sFrom .= ' from placex where place_id = '.$iPlaceID.') as plx';
|
||||||
|
} else {
|
||||||
|
$sFrom = ' from placex where place_id = '.$iPlaceID;
|
||||||
|
}
|
||||||
if ($this->fPolygonSimplificationThreshold > 0) {
|
if ($this->fPolygonSimplificationThreshold > 0) {
|
||||||
$sSQL .= ' from (select place_id,centroid,ST_SimplifyPreserveTopology(geometry,'.$this->fPolygonSimplificationThreshold.') as geometry'.$sFrom.') as plx';
|
$sSQL .= ' from (select place_id,centroid,ST_SimplifyPreserveTopology(geometry,'.$this->fPolygonSimplificationThreshold.') as geometry'.$sFrom.') as plx';
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -27,6 +27,17 @@ class Result
|
|||||||
/// Subranking within the results (the higher the worse).
|
/// Subranking within the results (the higher the worse).
|
||||||
public $iResultRank = 0;
|
public $iResultRank = 0;
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'Table' => $this->iTable,
|
||||||
|
'ID' => $this->iId,
|
||||||
|
'House number' => $this->iHouseNumber,
|
||||||
|
'Exact Matches' => $this->iExactMatches,
|
||||||
|
'Result rank' => $this->iResultRank
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function __construct($sId, $iTable = Result::TABLE_PLACEX)
|
public function __construct($sId, $iTable = Result::TABLE_PLACEX)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,6 +69,163 @@ class ReverseGeocode
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function lookupLargeArea($sPointSQL, $iMaxRank)
|
||||||
|
{
|
||||||
|
$oResult = null;
|
||||||
|
|
||||||
|
if ($iMaxRank > 4) {
|
||||||
|
$aPlace = $this->lookupPolygon($sPointSQL, $iMaxRank);
|
||||||
|
if ($aPlace) {
|
||||||
|
return new Result($aPlace['place_id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no polygon which contains the searchpoint is found,
|
||||||
|
// searches in the country_osm_grid table for a polygon.
|
||||||
|
return $this->lookupInCountry($sPointSQL, $iMaxRank);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function lookupInCountry($sPointSQL, $iMaxRank)
|
||||||
|
{
|
||||||
|
// searches for polygon in table country_osm_grid which contains the searchpoint
|
||||||
|
// and searches for the nearest place node to the searchpoint in this polygon
|
||||||
|
$sSQL = 'SELECT country_code FROM country_osm_grid';
|
||||||
|
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.') LIMIT 1';
|
||||||
|
|
||||||
|
$sCountryCode = chksql(
|
||||||
|
$this->oDB->getOne($sSQL),
|
||||||
|
'Could not determine country polygon containing the point.'
|
||||||
|
);
|
||||||
|
if ($sCountryCode) {
|
||||||
|
if ($iMaxRank > 4) {
|
||||||
|
// look for place nodes with the given country code
|
||||||
|
$sSQL = 'SELECT place_id FROM';
|
||||||
|
$sSQL .= ' (SELECT place_id, rank_search,';
|
||||||
|
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
||||||
|
$sSQL .= ' FROM placex';
|
||||||
|
$sSQL .= ' WHERE osm_type = \'N\'';
|
||||||
|
$sSQL .= ' AND country_code = \''.$sCountryCode.'\'';
|
||||||
|
$sSQL .= ' AND rank_search between 5 and ' .min(25, $iMaxRank);
|
||||||
|
$sSQL .= ' AND class = \'place\' AND type != \'postcode\'';
|
||||||
|
$sSQL .= ' AND name IS NOT NULL ';
|
||||||
|
$sSQL .= ' and indexed_status = 0 and linked_place_id is null';
|
||||||
|
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', geometry, 1.8)) p ';
|
||||||
|
$sSQL .= 'WHERE distance <= reverse_place_diameter(rank_search)';
|
||||||
|
$sSQL .= ' ORDER BY rank_search DESC, distance ASC';
|
||||||
|
$sSQL .= ' LIMIT 1';
|
||||||
|
|
||||||
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
|
$aPlace = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine place node.'
|
||||||
|
);
|
||||||
|
if ($aPlace) {
|
||||||
|
return new Result($aPlace['place_id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// still nothing, then return the country object
|
||||||
|
$sSQL = 'SELECT place_id, ST_distance('.$sPointSQL.', centroid) as distance';
|
||||||
|
$sSQL .= ' FROM placex';
|
||||||
|
$sSQL .= ' WHERE country_code = \''.$sCountryCode.'\'';
|
||||||
|
$sSQL .= ' AND rank_search = 4 AND rank_address = 4';
|
||||||
|
$sSQL .= ' AND class in (\'boundary\', \'place\')';
|
||||||
|
$sSQL .= ' AND linked_place_id is null';
|
||||||
|
$sSQL .= ' ORDER BY distance ASC';
|
||||||
|
|
||||||
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
|
$aPlace = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine place node.'
|
||||||
|
);
|
||||||
|
if ($aPlace) {
|
||||||
|
return new Result($aPlace['place_id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for areas or nodes for areas or nodes between state and suburb level.
|
||||||
|
*
|
||||||
|
* @param string $sPointSQL Search point as SQL string.
|
||||||
|
* @param int $iMaxRank Maximum address rank of the feature.
|
||||||
|
*
|
||||||
|
* @return Record of the found feature or null.
|
||||||
|
*
|
||||||
|
* Searches first for polygon that contains the search point.
|
||||||
|
* If such a polygon is found, place nodes with a higher rank are
|
||||||
|
* searched inside the polygon.
|
||||||
|
*/
|
||||||
|
protected function lookupPolygon($sPointSQL, $iMaxRank)
|
||||||
|
{
|
||||||
|
// polygon search begins at suburb-level
|
||||||
|
if ($iMaxRank > 25) $iMaxRank = 25;
|
||||||
|
// no polygon search over country-level
|
||||||
|
if ($iMaxRank < 5) $iMaxRank = 5;
|
||||||
|
// search for polygon
|
||||||
|
$sSQL = 'SELECT place_id, parent_place_id, rank_address, rank_search FROM';
|
||||||
|
$sSQL .= '(select place_id, parent_place_id, rank_address, rank_search, country_code, geometry';
|
||||||
|
$sSQL .= ' FROM placex';
|
||||||
|
$sSQL .= ' WHERE ST_GeometryType(geometry) in (\'ST_Polygon\', \'ST_MultiPolygon\')';
|
||||||
|
$sSQL .= ' AND rank_address Between 5 AND ' .$iMaxRank;
|
||||||
|
$sSQL .= ' AND geometry && '.$sPointSQL;
|
||||||
|
$sSQL .= ' AND type != \'postcode\' ';
|
||||||
|
$sSQL .= ' AND name is not null';
|
||||||
|
$sSQL .= ' AND indexed_status = 0 and linked_place_id is null';
|
||||||
|
$sSQL .= ' ORDER BY rank_address DESC LIMIT 50 ) as a';
|
||||||
|
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.' )';
|
||||||
|
$sSQL .= ' ORDER BY rank_address DESC LIMIT 1';
|
||||||
|
|
||||||
|
$aPoly = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine polygon containing the point.'
|
||||||
|
);
|
||||||
|
if ($aPoly) {
|
||||||
|
// if a polygon is found, search for placenodes begins ...
|
||||||
|
$iParentPlaceID = $aPoly['parent_place_id'];
|
||||||
|
$iRankAddress = $aPoly['rank_address'];
|
||||||
|
$iRankSearch = $aPoly['rank_search'];
|
||||||
|
$iPlaceID = $aPoly['place_id'];
|
||||||
|
|
||||||
|
if ($iRankAddress != $iMaxRank) {
|
||||||
|
$sSQL = 'SELECT place_id FROM ';
|
||||||
|
$sSQL .= '(SELECT place_id, rank_search, country_code, geometry,';
|
||||||
|
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
||||||
|
$sSQL .= ' FROM placex';
|
||||||
|
$sSQL .= ' WHERE osm_type = \'N\'';
|
||||||
|
// using rank_search because of a better differentiation
|
||||||
|
// for place nodes at rank_address 16
|
||||||
|
$sSQL .= ' AND rank_search > '.$iRankSearch;
|
||||||
|
$sSQL .= ' AND rank_search <= '.$iMaxRank;
|
||||||
|
$sSQL .= ' AND class = \'place\'';
|
||||||
|
$sSQL .= ' AND type != \'postcode\'';
|
||||||
|
$sSQL .= ' AND name IS NOT NULL ';
|
||||||
|
$sSQL .= ' AND indexed_status = 0 AND linked_place_id is null';
|
||||||
|
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', geometry, reverse_place_diameter('.$iRankSearch.'::smallint))';
|
||||||
|
$sSQL .= ' ORDER BY distance ASC,';
|
||||||
|
$sSQL .= ' rank_address DESC';
|
||||||
|
$sSQL .= ' limit 500) as a';
|
||||||
|
$sSQL .= ' WHERE ST_CONTAINS((SELECT geometry FROM placex WHERE place_id = '.$iPlaceID.'), geometry )';
|
||||||
|
$sSQL .= ' AND distance <= reverse_place_diameter(rank_search)';
|
||||||
|
$sSQL .= ' ORDER BY distance ASC, rank_search DESC';
|
||||||
|
$sSQL .= ' LIMIT 1';
|
||||||
|
|
||||||
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
|
$aPlacNode = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine place node.'
|
||||||
|
);
|
||||||
|
if ($aPlacNode) {
|
||||||
|
return $aPlacNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $aPoly;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function lookup($fLat, $fLon, $bDoInterpolation = true)
|
public function lookup($fLat, $fLon, $bDoInterpolation = true)
|
||||||
{
|
{
|
||||||
return $this->lookupPoint(
|
return $this->lookupPoint(
|
||||||
@@ -79,62 +236,34 @@ class ReverseGeocode
|
|||||||
|
|
||||||
public function lookupPoint($sPointSQL, $bDoInterpolation = true)
|
public function lookupPoint($sPointSQL, $bDoInterpolation = true)
|
||||||
{
|
{
|
||||||
|
// starts if the search is on POI or street level,
|
||||||
|
// searches for the nearest POI or street,
|
||||||
|
// if a street is found and a POI is searched for,
|
||||||
|
// the nearest POI which the found street is a parent of is choosen.
|
||||||
$iMaxRank = $this->iMaxRank;
|
$iMaxRank = $this->iMaxRank;
|
||||||
|
|
||||||
// Find the nearest point
|
// Find the nearest point
|
||||||
$fSearchDiam = 0.0004;
|
$fSearchDiam = 0.006;
|
||||||
$oResult = null;
|
$oResult = null;
|
||||||
$aPlace = null;
|
$aPlace = null;
|
||||||
$fMaxAreaDistance = 1;
|
|
||||||
$bIsTigerStreet = false;
|
|
||||||
while ($oResult === null && $fSearchDiam < $fMaxAreaDistance) {
|
|
||||||
$fSearchDiam = $fSearchDiam * 2;
|
|
||||||
|
|
||||||
// If we have to expand the search area by a large amount then we need a larger feature
|
// for POI or street level
|
||||||
// then there is a limit to how small the feature should be
|
if ($iMaxRank >= 26) {
|
||||||
if ($fSearchDiam > 2 && $iMaxRank > 4) $iMaxRank = 4;
|
$sSQL = 'select place_id,parent_place_id,rank_address,country_code,';
|
||||||
if ($fSearchDiam > 1 && $iMaxRank > 9) $iMaxRank = 8;
|
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
||||||
if ($fSearchDiam > 0.8 && $iMaxRank > 10) $iMaxRank = 10;
|
|
||||||
if ($fSearchDiam > 0.6 && $iMaxRank > 12) $iMaxRank = 12;
|
|
||||||
if ($fSearchDiam > 0.2 && $iMaxRank > 17) $iMaxRank = 17;
|
|
||||||
if ($fSearchDiam > 0.1 && $iMaxRank > 18) $iMaxRank = 18;
|
|
||||||
if ($fSearchDiam > 0.008 && $iMaxRank > 22) $iMaxRank = 22;
|
|
||||||
if ($fSearchDiam > 0.001 && $iMaxRank > 26) {
|
|
||||||
// try with interpolations before continuing
|
|
||||||
if ($bDoInterpolation) {
|
|
||||||
$aHouse = $this->lookupInterpolation($sPointSQL, $fSearchDiam/2);
|
|
||||||
|
|
||||||
if ($aHouse) {
|
|
||||||
$oResult = new Result($aHouse['place_id'], Result::TABLE_OSMLINE);
|
|
||||||
$oResult->iHouseNumber = closestHouseNumber($aHouse);
|
|
||||||
|
|
||||||
$aPlace = $aHouse;
|
|
||||||
$iParentPlaceID = $aHouse['parent_place_id']; // the street
|
|
||||||
$iMaxRank = 30;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no interpolation found, continue search
|
|
||||||
$iMaxRank = 26;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sSQL = 'select place_id,parent_place_id,rank_search,country_code,';
|
|
||||||
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
|
||||||
$sSQL .= ' FROM ';
|
$sSQL .= ' FROM ';
|
||||||
if ($fSearchDiam < 0.01) {
|
$sSQL .= ' placex';
|
||||||
$sSQL .= ' placex';
|
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
|
||||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
|
$sSQL .= ' AND';
|
||||||
$sSQL .= ' AND';
|
// only streets
|
||||||
|
if ($iMaxRank == 26) {
|
||||||
|
$sSQL .= ' rank_address = 26';
|
||||||
} else {
|
} else {
|
||||||
$sSQL .= ' (SELECT * FROM placex ';
|
$sSQL .= ' rank_address between 26 and '.$iMaxRank;
|
||||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
|
|
||||||
$sSQL .= ' LIMIT 1000) as p WHERE';
|
|
||||||
}
|
}
|
||||||
$sSQL .= ' rank_search != 28 and rank_search >= '.$iMaxRank;
|
|
||||||
$sSQL .= ' and (name is not null or housenumber is not null';
|
$sSQL .= ' and (name is not null or housenumber is not null';
|
||||||
$sSQL .= ' or rank_search between 26 and 27)';
|
$sSQL .= ' or rank_address between 26 and 27)';
|
||||||
$sSQL .= ' and class not in (\'waterway\',\'railway\',\'tunnel\',\'bridge\',\'man_made\')';
|
$sSQL .= ' and class not in (\'railway\',\'tunnel\',\'bridge\',\'man_made\')';
|
||||||
$sSQL .= ' and indexed_status = 0 and linked_place_id is null';
|
$sSQL .= ' and indexed_status = 0 and linked_place_id is null';
|
||||||
$sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') ';
|
$sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') ';
|
||||||
$sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
|
$sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
|
||||||
@@ -144,80 +273,95 @@ class ReverseGeocode
|
|||||||
$this->oDB->getRow($sSQL),
|
$this->oDB->getRow($sSQL),
|
||||||
'Could not determine closest place.'
|
'Could not determine closest place.'
|
||||||
);
|
);
|
||||||
if ($aPlace) {
|
|
||||||
$oResult = new Result($aPlace['place_id']);
|
|
||||||
$iParentPlaceID = $aPlace['parent_place_id'];
|
|
||||||
if ($bDoInterpolation) {
|
|
||||||
if ($aPlace['rank_search'] == 26 || $aPlace['rank_search'] == 27) {
|
|
||||||
$bIsTigerStreet = ($aPlace['country_code'] == 'us');
|
|
||||||
} elseif ($aPlace['rank_search'] == 30) {
|
|
||||||
// If a house was found, make sure there isn't an
|
|
||||||
// interpolation line that is closer.
|
|
||||||
$aHouse = $this->lookupInterpolation(
|
|
||||||
$sPointSQL,
|
|
||||||
$aPlace['distance']
|
|
||||||
);
|
|
||||||
if ($aHouse && $aPlace['distance'] < $aHouse['distance']) {
|
|
||||||
$oResult = new Result(
|
|
||||||
$aHouse['place_id'],
|
|
||||||
Result::TABLE_OSMLINE
|
|
||||||
);
|
|
||||||
$oResult->iHouseNumber = closestHouseNumber($aHouse);
|
|
||||||
|
|
||||||
$aPlace = $aHouse;
|
if (CONST_Debug) var_dump($aPlace);
|
||||||
$iParentPlaceID = $aHouse['parent_place_id'];
|
if ($aPlace) {
|
||||||
}
|
$iPlaceID = $aPlace['place_id'];
|
||||||
}
|
$oResult = new Result($iPlaceID);
|
||||||
|
$iRankAddress = $aPlace['rank_address'];
|
||||||
|
$iParentPlaceID = $aPlace['parent_place_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bDoInterpolation && $iMaxRank >= 30) {
|
||||||
|
$fDistance = $fSearchDiam;
|
||||||
|
if ($aPlace) {
|
||||||
|
// We can't reliably go from the closest street to an
|
||||||
|
// interpolation line because the closest interpolation
|
||||||
|
// may have a different street segments as a parent.
|
||||||
|
// Therefore allow an interpolation line to take precendence
|
||||||
|
// even when the street is closer.
|
||||||
|
$fDistance = $iRankAddress < 28 ? 0.001 : $aPlace['distance'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aHouse = $this->lookupInterpolation($sPointSQL, $fDistance);
|
||||||
|
|
||||||
|
if ($aHouse) {
|
||||||
|
$oResult = new Result($aHouse['place_id'], Result::TABLE_OSMLINE);
|
||||||
|
$oResult->iHouseNumber = closestHouseNumber($aHouse);
|
||||||
|
$aPlace = $aHouse;
|
||||||
|
$iRankAddress = 30;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Only street found? In the US we can check TIGER data for nearest housenumber
|
if ($aPlace) {
|
||||||
if (CONST_Use_US_Tiger_Data && $bIsTigerStreet && $this->iMaxRank >= 28) {
|
// if street and maxrank > streetlevel
|
||||||
$fSearchDiam = $aPlace['rank_search'] > 28 ? $aPlace['distance'] : 0.001;
|
if ($iRankAddress <= 27 && $iMaxRank > 27) {
|
||||||
$sSQL = 'SELECT place_id,parent_place_id,30 as rank_search,';
|
// find the closest object (up to a certain radius) of which the street is a parent of
|
||||||
$sSQL .= 'ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fraction,';
|
$sSQL = ' select place_id,';
|
||||||
$sSQL .= 'ST_distance('.$sPointSQL.', linegeo) as distance,';
|
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
||||||
$sSQL .= 'startnumber,endnumber,interpolationtype';
|
$sSQL .= ' FROM ';
|
||||||
$sSQL .= ' FROM location_property_tiger WHERE parent_place_id = '.$oResult->iId;
|
$sSQL .= ' placex';
|
||||||
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', linegeo, '.$fSearchDiam.')';
|
// radius ?
|
||||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, 0.001)';
|
||||||
|
$sSQL .= ' AND parent_place_id = '.$iPlaceID;
|
||||||
|
$sSQL .= ' and rank_address != 28';
|
||||||
|
$sSQL .= ' and (name is not null or housenumber is not null)';
|
||||||
|
$sSQL .= ' and class not in (\'railway\',\'tunnel\',\'bridge\',\'man_made\')';
|
||||||
|
$sSQL .= ' and indexed_status = 0 and linked_place_id is null';
|
||||||
|
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||||
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
|
$aStreet = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine closest place.'
|
||||||
|
);
|
||||||
|
if ($aStreet) {
|
||||||
|
if (CONST_Debug) var_dump($aStreet);
|
||||||
|
$oResult = new Result($aStreet['place_id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
// In the US we can check TIGER data for nearest housenumber
|
||||||
|
if (CONST_Use_US_Tiger_Data
|
||||||
$aPlaceTiger = chksql(
|
&& $iRankAddress <= 27
|
||||||
$this->oDB->getRow($sSQL),
|
&& $aPlace['country_code'] == 'us'
|
||||||
'Could not determine closest Tiger place.'
|
&& $this->iMaxRank >= 28
|
||||||
);
|
) {
|
||||||
if ($aPlaceTiger) {
|
$sSQL = 'SELECT place_id,parent_place_id,30 as rank_search,';
|
||||||
if (CONST_Debug) var_dump('found Tiger housenumber', $aPlaceTiger);
|
$sSQL .= 'ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fraction,';
|
||||||
$aPlace = $aPlaceTiger;
|
$sSQL .= 'ST_distance('.$sPointSQL.', linegeo) as distance,';
|
||||||
$oResult = new Result($aPlace['place_id'], Result::TABLE_TIGER);
|
$sSQL .= 'startnumber,endnumber,interpolationtype';
|
||||||
$oResult->iHouseNumber = closestHouseNumber($aPlaceTiger);
|
$sSQL .= ' FROM location_property_tiger WHERE parent_place_id = '.$oResult->iId;
|
||||||
$iParentPlaceID = $aPlace['parent_place_id'];
|
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', linegeo, 0.001)';
|
||||||
$iMaxRank = 30;
|
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||||
}
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
}
|
$aPlaceTiger = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
// The point we found might be too small - use the address to find what it is a child of
|
'Could not determine closest Tiger place.'
|
||||||
if ($oResult !== null && $iMaxRank < 28) {
|
);
|
||||||
if ($aPlace['rank_search'] > 28 && $iParentPlaceID) {
|
if ($aPlaceTiger) {
|
||||||
$iPlaceID = $iParentPlaceID;
|
if (CONST_Debug) var_dump('found Tiger housenumber', $aPlaceTiger);
|
||||||
|
$oResult = new Result($aPlaceTiger['place_id'], Result::TABLE_TIGER);
|
||||||
|
$oResult->iHouseNumber = closestHouseNumber($aPlaceTiger);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$iPlaceID = $oResult->iId;
|
// if no POI or street is found ...
|
||||||
}
|
$oResult = $this->lookupLargeArea($sPointSQL, 25);
|
||||||
$sSQL = 'select a.address_place_id';
|
|
||||||
$sSQL .= ' FROM place_addressline a, placex p';
|
|
||||||
$sSQL .= " WHERE a.place_id = $iPlaceID and a.address_place_id = p.place_id";
|
|
||||||
$sSQL .= ' AND p.linked_place_id is null';
|
|
||||||
$sSQL .= " ORDER BY abs(cached_rank_address - $iMaxRank) asc,cached_rank_address desc,isaddress desc,distance desc";
|
|
||||||
$sSQL .= ' LIMIT 1';
|
|
||||||
$iPlaceID = chksql($this->oDB->getOne($sSQL), 'Could not get parent for place.');
|
|
||||||
if ($iPlaceID) {
|
|
||||||
$oResult = new Result($iPlaceID);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// lower than street level ($iMaxRank < 26 )
|
||||||
|
$oResult = $this->lookupLargeArea($sPointSQL, $iMaxRank);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $oResult;
|
return $oResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class SearchContext
|
|||||||
/**
|
/**
|
||||||
* Get radius around reference point.
|
* Get radius around reference point.
|
||||||
*
|
*
|
||||||
* @return float Search radius around refernce point.
|
* @return float Search radius around reference point.
|
||||||
*/
|
*/
|
||||||
public function nearRadius()
|
public function nearRadius()
|
||||||
{
|
{
|
||||||
@@ -108,8 +108,8 @@ class SearchContext
|
|||||||
$aViewBox[3]
|
$aViewBox[3]
|
||||||
);
|
);
|
||||||
|
|
||||||
$fHeight = $aViewBox[0] - $aViewBox[2];
|
$fHeight = abs($aViewBox[0] - $aViewBox[2]);
|
||||||
$fWidth = $aViewBox[1] - $aViewBox[3];
|
$fWidth = abs($aViewBox[1] - $aViewBox[3]);
|
||||||
|
|
||||||
$this->sqlViewboxLarge = sprintf(
|
$this->sqlViewboxLarge = sprintf(
|
||||||
'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
|
'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
|
||||||
@@ -267,4 +267,18 @@ class SearchContext
|
|||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'Near radius' => $this->fNearRadius,
|
||||||
|
'Near point (SQL)' => $this->sqlNear,
|
||||||
|
'Bounded viewbox' => $this->bViewboxBounded,
|
||||||
|
'Viewbox (SQL, small)' => $this->sqlViewboxSmall,
|
||||||
|
'Viewbox (SQL, large)' => $this->sqlViewboxLarge,
|
||||||
|
'Viewbox (SQL, centre)' => $this->sqlViewboxCentre,
|
||||||
|
'Countries (SQL)' => $this->sqlCountryList,
|
||||||
|
'Excluded IDs (SQL)' => $this->sqlExcludeList
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class SearchDescription
|
|||||||
private $sCountryCode = '';
|
private $sCountryCode = '';
|
||||||
/// List of word ids making up the name of the object.
|
/// List of word ids making up the name of the object.
|
||||||
private $aName = array();
|
private $aName = array();
|
||||||
|
/// True if the name is rare enough to force index use on name.
|
||||||
|
private $bRareName = false;
|
||||||
/// List of word ids making up the address of the object.
|
/// List of word ids making up the address of the object.
|
||||||
private $aAddress = array();
|
private $aAddress = array();
|
||||||
/// Subset of word ids of full words making up the address.
|
/// Subset of word ids of full words making up the address.
|
||||||
@@ -43,7 +45,6 @@ class SearchDescription
|
|||||||
/// Index of phrase currently processed.
|
/// Index of phrase currently processed.
|
||||||
private $iNamePhrase = -1;
|
private $iNamePhrase = -1;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an empty search description.
|
* Create an empty search description.
|
||||||
*
|
*
|
||||||
@@ -58,7 +59,7 @@ class SearchDescription
|
|||||||
/**
|
/**
|
||||||
* Get current search rank.
|
* Get current search rank.
|
||||||
*
|
*
|
||||||
* The higher the search rank the lower the likelyhood that the
|
* The higher the search rank the lower the likelihood that the
|
||||||
* search is a correct interpretation of the search query.
|
* search is a correct interpretation of the search query.
|
||||||
*
|
*
|
||||||
* @return integer Search rank.
|
* @return integer Search rank.
|
||||||
@@ -94,8 +95,8 @@ class SearchDescription
|
|||||||
*/
|
*/
|
||||||
public function looksLikeFullAddress()
|
public function looksLikeFullAddress()
|
||||||
{
|
{
|
||||||
return sizeof($this->aName)
|
return (!empty($this->aName))
|
||||||
&& (sizeof($this->aAddress || $this->sCountryCode))
|
&& (!empty($this->aAddress) || $this->sCountryCode)
|
||||||
&& preg_match('/[0-9]+/', $this->sHouseNumber);
|
&& preg_match('/[0-9]+/', $this->sHouseNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +148,7 @@ class SearchDescription
|
|||||||
*/
|
*/
|
||||||
public function isValidSearch()
|
public function isValidSearch()
|
||||||
{
|
{
|
||||||
if (!sizeof($this->aName)) {
|
if (empty($this->aName)) {
|
||||||
if ($this->sHouseNumber) {
|
if ($this->sHouseNumber) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -165,30 +166,29 @@ class SearchDescription
|
|||||||
/**
|
/**
|
||||||
* Derive new searches by adding a full term to the existing search.
|
* Derive new searches by adding a full term to the existing search.
|
||||||
*
|
*
|
||||||
* @param mixed[] $aSearchTerm Description of the token.
|
* @param object $oSearchTerm Description of the token.
|
||||||
* @param bool $bHasPartial True if there are also tokens of partial terms
|
* @param bool $bHasPartial True if there are also tokens of partial terms
|
||||||
* with the same name.
|
* with the same name.
|
||||||
* @param string $sPhraseType Type of phrase the token is contained in.
|
* @param string $sPhraseType Type of phrase the token is contained in.
|
||||||
* @param bool $bFirstToken True if the token is at the beginning of the
|
* @param bool $bFirstToken True if the token is at the beginning of the
|
||||||
* query.
|
* query.
|
||||||
* @param bool $bFirstPhrase True if the token is in the first phrase of
|
* @param bool $bFirstPhrase True if the token is in the first phrase of
|
||||||
* the query.
|
* the query.
|
||||||
* @param bool $bLastToken True if the token is at the end of the query.
|
* @param bool $bLastToken True if the token is at the end of the query.
|
||||||
*
|
*
|
||||||
* @return SearchDescription[] List of derived search descriptions.
|
* @return SearchDescription[] List of derived search descriptions.
|
||||||
*/
|
*/
|
||||||
public function extendWithFullTerm($aSearchTerm, $bHasPartial, $sPhraseType, $bFirstToken, $bFirstPhrase, $bLastToken)
|
public function extendWithFullTerm($oSearchTerm, $bHasPartial, $sPhraseType, $bFirstToken, $bFirstPhrase, $bLastToken)
|
||||||
{
|
{
|
||||||
$aNewSearches = array();
|
$aNewSearches = array();
|
||||||
|
|
||||||
if (($sPhraseType == '' || $sPhraseType == 'country')
|
if (($sPhraseType == '' || $sPhraseType == 'country')
|
||||||
&& !empty($aSearchTerm['country_code'])
|
&& is_a($oSearchTerm, '\Nominatim\Token\Country')
|
||||||
&& $aSearchTerm['country_code'] != '0'
|
|
||||||
) {
|
) {
|
||||||
if (!$this->sCountryCode) {
|
if (!$this->sCountryCode) {
|
||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
$oSearch->sCountryCode = $aSearchTerm['country_code'];
|
$oSearch->sCountryCode = $oSearchTerm->sCountryCode;
|
||||||
// Country is almost always at the end of the string
|
// Country is almost always at the end of the string
|
||||||
// - increase score for finding it anywhere else (optimisation)
|
// - increase score for finding it anywhere else (optimisation)
|
||||||
if (!$bLastToken) {
|
if (!$bLastToken) {
|
||||||
@@ -197,15 +197,12 @@ class SearchDescription
|
|||||||
$aNewSearches[] = $oSearch;
|
$aNewSearches[] = $oSearch;
|
||||||
}
|
}
|
||||||
} elseif (($sPhraseType == '' || $sPhraseType == 'postalcode')
|
} elseif (($sPhraseType == '' || $sPhraseType == 'postalcode')
|
||||||
&& $aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'postcode'
|
&& is_a($oSearchTerm, '\Nominatim\Token\Postcode')
|
||||||
) {
|
) {
|
||||||
// We need to try the case where the postal code is the primary element
|
// We need to try the case where the postal code is the primary element
|
||||||
// (i.e. no way to tell if it is (postalcode, city) OR (city, postalcode)
|
// (i.e. no way to tell if it is (postalcode, city) OR (city, postalcode)
|
||||||
// so try both.
|
// so try both.
|
||||||
if (!$this->sPostcode
|
if (!$this->sPostcode) {
|
||||||
&& $aSearchTerm['word']
|
|
||||||
&& pg_escape_string($aSearchTerm['word']) == $aSearchTerm['word']
|
|
||||||
) {
|
|
||||||
// If we have structured search or this is the first term,
|
// If we have structured search or this is the first term,
|
||||||
// make the postcode the primary search element.
|
// make the postcode the primary search element.
|
||||||
if ($this->iOperator == Operator::NONE
|
if ($this->iOperator == Operator::NONE
|
||||||
@@ -216,71 +213,79 @@ class SearchDescription
|
|||||||
$oSearch->iOperator = Operator::POSTCODE;
|
$oSearch->iOperator = Operator::POSTCODE;
|
||||||
$oSearch->aAddress = array_merge($this->aAddress, $this->aName);
|
$oSearch->aAddress = array_merge($this->aAddress, $this->aName);
|
||||||
$oSearch->aName =
|
$oSearch->aName =
|
||||||
array($aSearchTerm['word_id'] => $aSearchTerm['word']);
|
array($oSearchTerm->iId => $oSearchTerm->sPostcode);
|
||||||
$aNewSearches[] = $oSearch;
|
$aNewSearches[] = $oSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a structured search or this is not the first term,
|
// If we have a structured search or this is not the first term,
|
||||||
// add the postcode as an addendum.
|
// add the postcode as an addendum.
|
||||||
if ($this->iOperator != Operator::POSTCODE
|
if ($this->iOperator != Operator::POSTCODE
|
||||||
&& ($sPhraseType == 'postalcode' || sizeof($this->aName))
|
&& ($sPhraseType == 'postalcode' || !empty($this->aName))
|
||||||
) {
|
) {
|
||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
$oSearch->sPostcode = $aSearchTerm['word'];
|
$oSearch->sPostcode = $oSearchTerm->sPostcode;
|
||||||
$aNewSearches[] = $oSearch;
|
$aNewSearches[] = $oSearch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif (($sPhraseType == '' || $sPhraseType == 'street')
|
} elseif (($sPhraseType == '' || $sPhraseType == 'street')
|
||||||
&& $aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'house'
|
&& is_a($oSearchTerm, '\Nominatim\Token\HouseNumber')
|
||||||
) {
|
) {
|
||||||
if (!$this->sHouseNumber && $this->iOperator != Operator::POSTCODE) {
|
if (!$this->sHouseNumber && $this->iOperator != Operator::POSTCODE) {
|
||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
$oSearch->sHouseNumber = trim($aSearchTerm['word_token']);
|
$oSearch->sHouseNumber = $oSearchTerm->sToken;
|
||||||
// sanity check: if the housenumber is not mainly made
|
// sanity check: if the housenumber is not mainly made
|
||||||
// up of numbers, add a penalty
|
// up of numbers, add a penalty
|
||||||
if (preg_match_all('/[^0-9]/', $oSearch->sHouseNumber, $aMatches) > 2) {
|
if (preg_match_all('/[^0-9]/', $oSearch->sHouseNumber, $aMatches) > 2) {
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
}
|
}
|
||||||
if (!isset($aSearchTerm['word_id'])) {
|
if (empty($oSearchTerm->iId)) {
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
}
|
}
|
||||||
// also must not appear in the middle of the address
|
// also must not appear in the middle of the address
|
||||||
if (sizeof($this->aAddress)
|
if (!empty($this->aAddress)
|
||||||
|| sizeof($this->aAddressNonSearch)
|
|| (!empty($this->aAddressNonSearch))
|
||||||
|| $this->sPostcode
|
|| $this->sPostcode
|
||||||
) {
|
) {
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
}
|
}
|
||||||
$aNewSearches[] = $oSearch;
|
$aNewSearches[] = $oSearch;
|
||||||
}
|
}
|
||||||
} elseif ($sPhraseType == '' && $aSearchTerm['class']) {
|
} elseif ($sPhraseType == ''
|
||||||
|
&& is_a($oSearchTerm, '\Nominatim\Token\SpecialTerm')
|
||||||
|
) {
|
||||||
if ($this->iOperator == Operator::NONE) {
|
if ($this->iOperator == Operator::NONE) {
|
||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
|
|
||||||
$iOp = Operator::NEAR; // near == in for the moment
|
$iOp = $oSearchTerm->iOperator;
|
||||||
if ($aSearchTerm['operator'] == '') {
|
if ($iOp == Operator::NONE) {
|
||||||
if (sizeof($this->aName) || $this->oContext->isBoundedSearch()) {
|
if (!empty($this->aName) || $this->oContext->isBoundedSearch()) {
|
||||||
$iOp = Operator::NAME;
|
$iOp = Operator::NAME;
|
||||||
|
} else {
|
||||||
|
$iOp = Operator::NEAR;
|
||||||
}
|
}
|
||||||
$oSearch->iSearchRank += 2;
|
$oSearch->iSearchRank += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
$oSearch->setPoiSearch($iOp, $aSearchTerm['class'], $aSearchTerm['type']);
|
$oSearch->setPoiSearch(
|
||||||
|
$iOp,
|
||||||
|
$oSearchTerm->sClass,
|
||||||
|
$oSearchTerm->sType
|
||||||
|
);
|
||||||
$aNewSearches[] = $oSearch;
|
$aNewSearches[] = $oSearch;
|
||||||
}
|
}
|
||||||
} elseif (isset($aSearchTerm['word_id'])
|
} elseif ($sPhraseType != 'country'
|
||||||
&& $aSearchTerm['word_id']
|
&& is_a($oSearchTerm, '\Nominatim\Token\Word')
|
||||||
&& $sPhraseType != 'country'
|
|
||||||
) {
|
) {
|
||||||
$iWordID = $aSearchTerm['word_id'];
|
$iWordID = $oSearchTerm->iId;
|
||||||
if (sizeof($this->aName)) {
|
// Full words can only be a name if they appear at the beginning
|
||||||
if (($sPhraseType == '' || !$bFirstPhrase)
|
// of the phrase. In structured search the name must forcably in
|
||||||
&& $sPhraseType != 'country'
|
// the first phrase. In unstructured search it may be in a later
|
||||||
&& !$bHasPartial
|
// phrase when the first phrase is a house number.
|
||||||
) {
|
if (!empty($this->aName) || !($bFirstPhrase || $sPhraseType == '')) {
|
||||||
|
if (($sPhraseType == '' || !$bFirstPhrase) && !$bHasPartial) {
|
||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
$oSearch->aAddress[$iWordID] = $iWordID;
|
$oSearch->aAddress[$iWordID] = $iWordID;
|
||||||
@@ -292,6 +297,11 @@ class SearchDescription
|
|||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
$oSearch->aName = array($iWordID => $iWordID);
|
$oSearch->aName = array($iWordID => $iWordID);
|
||||||
|
if (CONST_Search_NameOnlySearchFrequencyThreshold) {
|
||||||
|
$oSearch->bRareName =
|
||||||
|
$oSearchTerm->iSearchNameCount
|
||||||
|
< CONST_Search_NameOnlySearchFrequencyThreshold;
|
||||||
|
}
|
||||||
$aNewSearches[] = $oSearch;
|
$aNewSearches[] = $oSearch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,7 +312,8 @@ class SearchDescription
|
|||||||
/**
|
/**
|
||||||
* Derive new searches by adding a partial term to the existing search.
|
* Derive new searches by adding a partial term to the existing search.
|
||||||
*
|
*
|
||||||
* @param mixed[] $aSearchTerm Description of the token.
|
* @param string $sToken Term for the token.
|
||||||
|
* @param object $oSearchTerm Description of the token.
|
||||||
* @param bool $bStructuredPhrases True if the search is structured.
|
* @param bool $bStructuredPhrases True if the search is structured.
|
||||||
* @param integer $iPhrase Number of the phrase the token is in.
|
* @param integer $iPhrase Number of the phrase the token is in.
|
||||||
* @param array[] $aFullTokens List of full term tokens with the
|
* @param array[] $aFullTokens List of full term tokens with the
|
||||||
@@ -310,21 +321,21 @@ class SearchDescription
|
|||||||
*
|
*
|
||||||
* @return SearchDescription[] List of derived search descriptions.
|
* @return SearchDescription[] List of derived search descriptions.
|
||||||
*/
|
*/
|
||||||
public function extendWithPartialTerm($aSearchTerm, $bStructuredPhrases, $iPhrase, $aFullTokens)
|
public function extendWithPartialTerm($sToken, $oSearchTerm, $bStructuredPhrases, $iPhrase, $aFullTokens)
|
||||||
{
|
{
|
||||||
// Only allow name terms.
|
// Only allow name terms.
|
||||||
if (!(isset($aSearchTerm['word_id']) && $aSearchTerm['word_id'])) {
|
if (!(is_a($oSearchTerm, '\Nominatim\Token\Word'))) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$aNewSearches = array();
|
$aNewSearches = array();
|
||||||
$iWordID = $aSearchTerm['word_id'];
|
$iWordID = $oSearchTerm->iId;
|
||||||
|
|
||||||
if ((!$bStructuredPhrases || $iPhrase > 0)
|
if ((!$bStructuredPhrases || $iPhrase > 0)
|
||||||
&& sizeof($this->aName)
|
&& (!empty($this->aName))
|
||||||
&& strpos($aSearchTerm['word_token'], ' ') === false
|
&& strpos($sToken, ' ') === false
|
||||||
) {
|
) {
|
||||||
if ($aSearchTerm['search_name_count'] + 1 < CONST_Max_Word_Frequency) {
|
if ($oSearchTerm->iSearchNameCount < CONST_Max_Word_Frequency) {
|
||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank += 2;
|
$oSearch->iSearchRank += 2;
|
||||||
$oSearch->aAddress[$iWordID] = $iWordID;
|
$oSearch->aAddress[$iWordID] = $iWordID;
|
||||||
@@ -333,23 +344,21 @@ class SearchDescription
|
|||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
$oSearch->aAddressNonSearch[$iWordID] = $iWordID;
|
$oSearch->aAddressNonSearch[$iWordID] = $iWordID;
|
||||||
if (preg_match('#^[0-9]+$#', $aSearchTerm['word_token'])) {
|
if (preg_match('#^[0-9]+$#', $sToken)) {
|
||||||
$oSearch->iSearchRank += 2;
|
$oSearch->iSearchRank += 2;
|
||||||
}
|
}
|
||||||
if (sizeof($aFullTokens)) {
|
if (!empty($aFullTokens)) {
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
}
|
}
|
||||||
$aNewSearches[] = $oSearch;
|
$aNewSearches[] = $oSearch;
|
||||||
|
|
||||||
// revert to the token version?
|
// revert to the token version?
|
||||||
foreach ($aFullTokens as $aSearchTermToken) {
|
foreach ($aFullTokens as $oSearchTermToken) {
|
||||||
if (empty($aSearchTermToken['country_code'])
|
if (is_a($oSearchTermToken, '\Nominatim\Token\Word')) {
|
||||||
&& empty($aSearchTermToken['lat'])
|
|
||||||
&& empty($aSearchTermToken['class'])
|
|
||||||
) {
|
|
||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
$oSearch->aAddress[$aSearchTermToken['word_id']] = $aSearchTermToken['word_id'];
|
$oSearch->aAddress[$oSearchTermToken->iId]
|
||||||
|
= $oSearchTermToken->iId;
|
||||||
$aNewSearches[] = $oSearch;
|
$aNewSearches[] = $oSearch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,17 +366,26 @@ class SearchDescription
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((!$this->sPostcode && !$this->aAddress && !$this->aAddressNonSearch)
|
if ((!$this->sPostcode && !$this->aAddress && !$this->aAddressNonSearch)
|
||||||
&& (!sizeof($this->aName) || $this->iNamePhrase == $iPhrase)
|
&& (empty($this->aName) || $this->iNamePhrase == $iPhrase)
|
||||||
) {
|
) {
|
||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank += 2;
|
$oSearch->iSearchRank += 2;
|
||||||
if (!sizeof($this->aName)) {
|
if (empty($this->aName)) {
|
||||||
$oSearch->iSearchRank += 1;
|
$oSearch->iSearchRank += 1;
|
||||||
}
|
}
|
||||||
if (preg_match('#^[0-9]+$#', $aSearchTerm['word_token'])) {
|
if (preg_match('#^[0-9]+$#', $sToken)) {
|
||||||
$oSearch->iSearchRank += 2;
|
$oSearch->iSearchRank += 2;
|
||||||
}
|
}
|
||||||
if ($aSearchTerm['search_name_count'] + 1 < CONST_Max_Word_Frequency) {
|
if ($oSearchTerm->iSearchNameCount < CONST_Max_Word_Frequency) {
|
||||||
|
if (empty($this->aName)
|
||||||
|
&& CONST_Search_NameOnlySearchFrequencyThreshold
|
||||||
|
) {
|
||||||
|
$oSearch->bRareName =
|
||||||
|
$oSearchTerm->iSearchNameCount
|
||||||
|
< CONST_Search_NameOnlySearchFrequencyThreshold;
|
||||||
|
} else {
|
||||||
|
$oSearch->bRareName = false;
|
||||||
|
}
|
||||||
$oSearch->aName[$iWordID] = $iWordID;
|
$oSearch->aName[$iWordID] = $iWordID;
|
||||||
} else {
|
} else {
|
||||||
$oSearch->aNameNonSearch[$iWordID] = $iWordID;
|
$oSearch->aNameNonSearch[$iWordID] = $iWordID;
|
||||||
@@ -385,26 +403,22 @@ class SearchDescription
|
|||||||
/**
|
/**
|
||||||
* Query database for places that match this search.
|
* Query database for places that match this search.
|
||||||
*
|
*
|
||||||
* @param object $oDB Database connection to use.
|
* @param object $oDB Database connection to use.
|
||||||
* @param mixed[] $aWordFrequencyScores Number of times tokens appears
|
* @param integer $iMinRank Minimum address rank to restrict search to.
|
||||||
* overall in a planet database.
|
* @param integer $iMaxRank Maximum address rank to restrict search to.
|
||||||
* @param integer $iMinRank Minimum address rank to restrict
|
* @param integer $iLimit Maximum number of results.
|
||||||
* search to.
|
|
||||||
* @param integer $iMaxRank Maximum address rank to restrict
|
|
||||||
* search to.
|
|
||||||
* @param integer $iLimit Maximum number of results.
|
|
||||||
*
|
*
|
||||||
* @return mixed[] An array with two fields: IDs contains the list of
|
* @return mixed[] An array with two fields: IDs contains the list of
|
||||||
* matching place IDs and houseNumber the houseNumber
|
* matching place IDs and houseNumber the houseNumber
|
||||||
* if appicable or -1 if not.
|
* if appicable or -1 if not.
|
||||||
*/
|
*/
|
||||||
public function query(&$oDB, &$aWordFrequencyScores, $iMinRank, $iMaxRank, $iLimit)
|
public function query(&$oDB, $iMinRank, $iMaxRank, $iLimit)
|
||||||
{
|
{
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
$iHousenumber = -1;
|
$iHousenumber = -1;
|
||||||
|
|
||||||
if ($this->sCountryCode
|
if ($this->sCountryCode
|
||||||
&& !sizeof($this->aName)
|
&& empty($this->aName)
|
||||||
&& !$this->iOperator
|
&& !$this->iOperator
|
||||||
&& !$this->sClass
|
&& !$this->sClass
|
||||||
&& !$this->oContext->hasNearPoint()
|
&& !$this->oContext->hasNearPoint()
|
||||||
@@ -413,7 +427,7 @@ class SearchDescription
|
|||||||
if (4 >= $iMinRank && 4 <= $iMaxRank) {
|
if (4 >= $iMinRank && 4 <= $iMaxRank) {
|
||||||
$aResults = $this->queryCountry($oDB);
|
$aResults = $this->queryCountry($oDB);
|
||||||
}
|
}
|
||||||
} elseif (!sizeof($this->aName) && !sizeof($this->aAddress)) {
|
} elseif (empty($this->aName) && empty($this->aAddress)) {
|
||||||
// Neither name nor address? Then we must be
|
// Neither name nor address? Then we must be
|
||||||
// looking for a POI in a geographic area.
|
// looking for a POI in a geographic area.
|
||||||
if ($this->oContext->isBoundedSearch()) {
|
if ($this->oContext->isBoundedSearch()) {
|
||||||
@@ -427,40 +441,36 @@ class SearchDescription
|
|||||||
// First search for places according to name and address.
|
// First search for places according to name and address.
|
||||||
$aResults = $this->queryNamedPlace(
|
$aResults = $this->queryNamedPlace(
|
||||||
$oDB,
|
$oDB,
|
||||||
$aWordFrequencyScores,
|
|
||||||
$iMinRank,
|
$iMinRank,
|
||||||
$iMaxRank,
|
$iMaxRank,
|
||||||
$iLimit
|
$iLimit
|
||||||
);
|
);
|
||||||
|
|
||||||
//now search for housenumber, if housenumber provided
|
//now search for housenumber, if housenumber provided
|
||||||
if ($this->sHouseNumber && sizeof($aResults)) {
|
if ($this->sHouseNumber && !empty($aResults)) {
|
||||||
$aNamedPlaceIDs = $aResults;
|
$aNamedPlaceIDs = $aResults;
|
||||||
$aResults = $this->queryHouseNumber($oDB, $aNamedPlaceIDs, $iLimit);
|
$aResults = $this->queryHouseNumber($oDB, $aNamedPlaceIDs);
|
||||||
|
|
||||||
if (!sizeof($aResults) && $this->looksLikeFullAddress()) {
|
if (empty($aResults) && $this->looksLikeFullAddress()) {
|
||||||
$aResults = $aNamedPlaceIDs;
|
$aResults = $aNamedPlaceIDs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally get POIs if requested
|
// finally get POIs if requested
|
||||||
if ($this->sClass && sizeof($aResults)) {
|
if ($this->sClass && !empty($aResults)) {
|
||||||
$aResults = $this->queryPoiByOperator($oDB, $aResults, $iLimit);
|
$aResults = $this->queryPoiByOperator($oDB, $aResults, $iLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONST_Debug) {
|
Debug::printDebugTable('Place IDs', $aResults);
|
||||||
echo '<br><b>Place IDs:</b> ';
|
|
||||||
var_dump(array_keys($aResults));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sizeof($aResults) && $this->sPostcode) {
|
if (!empty($aResults) && $this->sPostcode) {
|
||||||
$sPlaceIds = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX);
|
$sPlaceIds = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX);
|
||||||
if ($sPlaceIds) {
|
if ($sPlaceIds) {
|
||||||
$sSQL = 'SELECT place_id FROM placex';
|
$sSQL = 'SELECT place_id FROM placex';
|
||||||
$sSQL .= ' WHERE place_id in ('.$sPlaceIds.')';
|
$sSQL .= ' WHERE place_id in ('.$sPlaceIds.')';
|
||||||
$sSQL .= " AND postcode = '".$this->sPostcode."'";
|
$sSQL .= " AND postcode = '".$this->sPostcode."'";
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aFilteredPlaceIDs = chksql($oDB->getCol($sSQL));
|
$aFilteredPlaceIDs = chksql($oDB->getCol($sSQL));
|
||||||
if ($aFilteredPlaceIDs) {
|
if ($aFilteredPlaceIDs) {
|
||||||
$aNewResults = array();
|
$aNewResults = array();
|
||||||
@@ -468,10 +478,7 @@ class SearchDescription
|
|||||||
$aNewResults[$iPlaceId] = $aResults[$iPlaceId];
|
$aNewResults[$iPlaceId] = $aResults[$iPlaceId];
|
||||||
}
|
}
|
||||||
$aResults = $aNewResults;
|
$aResults = $aNewResults;
|
||||||
if (CONST_Debug) {
|
Debug::printVar('Place IDs after postcode filtering', $aResults);
|
||||||
echo '<br><b>Place IDs after postcode filtering:</b> ';
|
|
||||||
var_dump(array_keys($aResults));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -490,7 +497,7 @@ class SearchDescription
|
|||||||
}
|
}
|
||||||
$sSQL .= ' ORDER BY st_area(geometry) DESC LIMIT 1';
|
$sSQL .= ' ORDER BY st_area(geometry) DESC LIMIT 1';
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
@@ -531,7 +538,7 @@ class SearchDescription
|
|||||||
$sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('ct.centroid').' ASC';
|
$sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('ct.centroid').' ASC';
|
||||||
}
|
}
|
||||||
$sSQL .= " limit $iLimit";
|
$sSQL .= " limit $iLimit";
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aDBResults = chksql($oDB->getCol($sSQL));
|
$aDBResults = chksql($oDB->getCol($sSQL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,7 +552,7 @@ class SearchDescription
|
|||||||
}
|
}
|
||||||
$sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('centroid').' ASC';
|
$sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('centroid').' ASC';
|
||||||
$sSQL .= " LIMIT $iLimit";
|
$sSQL .= " LIMIT $iLimit";
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aDBResults = chksql($oDB->getCol($sSQL));
|
$aDBResults = chksql($oDB->getCol($sSQL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,7 +568,7 @@ class SearchDescription
|
|||||||
{
|
{
|
||||||
$sSQL = 'SELECT p.place_id FROM location_postcode p ';
|
$sSQL = 'SELECT p.place_id FROM location_postcode p ';
|
||||||
|
|
||||||
if (sizeof($this->aAddress)) {
|
if (!empty($this->aAddress)) {
|
||||||
$sSQL .= ', search_name s ';
|
$sSQL .= ', search_name s ';
|
||||||
$sSQL .= 'WHERE s.place_id = p.parent_place_id ';
|
$sSQL .= 'WHERE s.place_id = p.parent_place_id ';
|
||||||
$sSQL .= 'AND array_cat(s.nameaddress_vector, s.name_vector)';
|
$sSQL .= 'AND array_cat(s.nameaddress_vector, s.name_vector)';
|
||||||
@@ -575,7 +582,7 @@ class SearchDescription
|
|||||||
$sSQL .= $this->oContext->excludeSQL(' AND p.place_id');
|
$sSQL .= $this->oContext->excludeSQL(' AND p.place_id');
|
||||||
$sSQL .= " LIMIT $iLimit";
|
$sSQL .= " LIMIT $iLimit";
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
@@ -585,12 +592,16 @@ class SearchDescription
|
|||||||
return $aResults;
|
return $aResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function queryNamedPlace(&$oDB, $aWordFrequencyScores, $iMinAddressRank, $iMaxAddressRank, $iLimit)
|
private function queryNamedPlace(&$oDB, $iMinAddressRank, $iMaxAddressRank, $iLimit)
|
||||||
{
|
{
|
||||||
$aTerms = array();
|
$aTerms = array();
|
||||||
$aOrder = array();
|
$aOrder = array();
|
||||||
|
|
||||||
if ($this->sHouseNumber && sizeof($this->aAddress)) {
|
// Sort by existence of the requested house number but only if not
|
||||||
|
// too many results are expected for the street, i.e. if the result
|
||||||
|
// will be narrowed down by an address. Remeber that with ordering
|
||||||
|
// every single result has to be checked.
|
||||||
|
if ($this->sHouseNumber && (!empty($this->aAddress) || $this->sPostcode)) {
|
||||||
$sHouseNumberRegex = '\\\\m'.$this->sHouseNumber.'\\\\M';
|
$sHouseNumberRegex = '\\\\m'.$this->sHouseNumber.'\\\\M';
|
||||||
$aOrder[] = ' (';
|
$aOrder[] = ' (';
|
||||||
$aOrder[0] .= 'EXISTS(';
|
$aOrder[0] .= 'EXISTS(';
|
||||||
@@ -616,16 +627,12 @@ class SearchDescription
|
|||||||
$aOrder[0] .= ') DESC';
|
$aOrder[0] .= ') DESC';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sizeof($this->aName)) {
|
if (!empty($this->aName)) {
|
||||||
$aTerms[] = 'name_vector @> '.getArraySQL($this->aName);
|
$aTerms[] = 'name_vector @> '.getArraySQL($this->aName);
|
||||||
}
|
}
|
||||||
if (sizeof($this->aAddress)) {
|
if (!empty($this->aAddress)) {
|
||||||
// For infrequent name terms disable index usage for address
|
// For infrequent name terms disable index usage for address
|
||||||
if (CONST_Search_NameOnlySearchFrequencyThreshold
|
if ($this->bRareName) {
|
||||||
&& sizeof($this->aName) == 1
|
|
||||||
&& $aWordFrequencyScores[$this->aName[reset($this->aName)]]
|
|
||||||
< CONST_Search_NameOnlySearchFrequencyThreshold
|
|
||||||
) {
|
|
||||||
$aTerms[] = 'array_cat(nameaddress_vector,ARRAY[]::integer[]) @> '.getArraySQL($this->aAddress);
|
$aTerms[] = 'array_cat(nameaddress_vector,ARRAY[]::integer[]) @> '.getArraySQL($this->aAddress);
|
||||||
} else {
|
} else {
|
||||||
$aTerms[] = 'nameaddress_vector @> '.getArraySQL($this->aAddress);
|
$aTerms[] = 'nameaddress_vector @> '.getArraySQL($this->aAddress);
|
||||||
@@ -652,7 +659,7 @@ class SearchDescription
|
|||||||
$aTerms[] = $this->oContext->withinSQL('centroid');
|
$aTerms[] = $this->oContext->withinSQL('centroid');
|
||||||
$aOrder[] = $this->oContext->distanceSQL('centroid');
|
$aOrder[] = $this->oContext->distanceSQL('centroid');
|
||||||
} elseif ($this->sPostcode) {
|
} elseif ($this->sPostcode) {
|
||||||
if (!sizeof($this->aAddress)) {
|
if (empty($this->aAddress)) {
|
||||||
$aTerms[] = "EXISTS(SELECT place_id FROM location_postcode p WHERE p.postcode = '".$this->sPostcode."' AND ST_DWithin(search_name.centroid, p.geometry, 0.1))";
|
$aTerms[] = "EXISTS(SELECT place_id FROM location_postcode p WHERE p.postcode = '".$this->sPostcode."' AND ST_DWithin(search_name.centroid, p.geometry, 0.1))";
|
||||||
} else {
|
} else {
|
||||||
$aOrder[] = "(SELECT min(ST_Distance(search_name.centroid, p.geometry)) FROM location_postcode p WHERE p.postcode = '".$this->sPostcode."')";
|
$aOrder[] = "(SELECT min(ST_Distance(search_name.centroid, p.geometry)) FROM location_postcode p WHERE p.postcode = '".$this->sPostcode."')";
|
||||||
@@ -675,12 +682,12 @@ class SearchDescription
|
|||||||
if ($this->sHouseNumber) {
|
if ($this->sHouseNumber) {
|
||||||
$sImportanceSQL = '- abs(26 - address_rank) + 3';
|
$sImportanceSQL = '- abs(26 - address_rank) + 3';
|
||||||
} else {
|
} else {
|
||||||
$sImportanceSQL = '(CASE WHEN importance = 0 OR importance IS NULL THEN 0.75-(search_rank::float/40) ELSE importance END)';
|
$sImportanceSQL = '(CASE WHEN importance = 0 OR importance IS NULL THEN 0.75001-(search_rank::float/40) ELSE importance END)';
|
||||||
}
|
}
|
||||||
$sImportanceSQL .= $this->oContext->viewboxImportanceSQL('centroid');
|
$sImportanceSQL .= $this->oContext->viewboxImportanceSQL('centroid');
|
||||||
$aOrder[] = "$sImportanceSQL DESC";
|
$aOrder[] = "$sImportanceSQL DESC";
|
||||||
|
|
||||||
if (sizeof($this->aFullNameAddress)) {
|
if (!empty($this->aFullNameAddress)) {
|
||||||
$sExactMatchSQL = ' ( ';
|
$sExactMatchSQL = ' ( ';
|
||||||
$sExactMatchSQL .= ' SELECT count(*) FROM ( ';
|
$sExactMatchSQL .= ' SELECT count(*) FROM ( ';
|
||||||
$sExactMatchSQL .= ' SELECT unnest('.getArraySQL($this->aFullNameAddress).')';
|
$sExactMatchSQL .= ' SELECT unnest('.getArraySQL($this->aFullNameAddress).')';
|
||||||
@@ -699,14 +706,14 @@ class SearchDescription
|
|||||||
|
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
|
|
||||||
if (sizeof($aTerms)) {
|
if (!empty($aTerms)) {
|
||||||
$sSQL = 'SELECT place_id,'.$sExactMatchSQL;
|
$sSQL = 'SELECT place_id,'.$sExactMatchSQL;
|
||||||
$sSQL .= ' FROM search_name';
|
$sSQL .= ' FROM search_name';
|
||||||
$sSQL .= ' WHERE '.join(' and ', $aTerms);
|
$sSQL .= ' WHERE '.join(' and ', $aTerms);
|
||||||
$sSQL .= ' ORDER BY '.join(', ', $aOrder);
|
$sSQL .= ' ORDER BY '.join(', ', $aOrder);
|
||||||
$sSQL .= ' LIMIT '.$iLimit;
|
$sSQL .= ' LIMIT '.$iLimit;
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
$aDBResults = chksql(
|
$aDBResults = chksql(
|
||||||
$oDB->getAll($sSQL),
|
$oDB->getAll($sSQL),
|
||||||
@@ -723,7 +730,7 @@ class SearchDescription
|
|||||||
return $aResults;
|
return $aResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function queryHouseNumber(&$oDB, $aRoadPlaceIDs, $iLimit)
|
private function queryHouseNumber(&$oDB, $aRoadPlaceIDs)
|
||||||
{
|
{
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aRoadPlaceIDs, Result::TABLE_PLACEX);
|
$sPlaceIDs = Result::joinIdsByTable($aRoadPlaceIDs, Result::TABLE_PLACEX);
|
||||||
@@ -737,9 +744,8 @@ class SearchDescription
|
|||||||
$sSQL .= 'WHERE parent_place_id in ('.$sPlaceIDs.')';
|
$sSQL .= 'WHERE parent_place_id in ('.$sPlaceIDs.')';
|
||||||
$sSQL .= " AND transliteration(housenumber) ~* E'".$sHouseNumberRegex."'";
|
$sSQL .= " AND transliteration(housenumber) ~* E'".$sHouseNumberRegex."'";
|
||||||
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
|
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
|
||||||
$sSQL .= " LIMIT $iLimit";
|
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
// XXX should inherit the exactMatches from its parent
|
// XXX should inherit the exactMatches from its parent
|
||||||
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
@@ -748,7 +754,7 @@ class SearchDescription
|
|||||||
|
|
||||||
$bIsIntHouseNumber= (bool) preg_match('/[0-9]+/', $this->sHouseNumber);
|
$bIsIntHouseNumber= (bool) preg_match('/[0-9]+/', $this->sHouseNumber);
|
||||||
$iHousenumber = intval($this->sHouseNumber);
|
$iHousenumber = intval($this->sHouseNumber);
|
||||||
if ($bIsIntHouseNumber && !sizeof($aResults)) {
|
if ($bIsIntHouseNumber && empty($aResults)) {
|
||||||
// if nothing found, search in the interpolation line table
|
// if nothing found, search in the interpolation line table
|
||||||
$sSQL = 'SELECT distinct place_id FROM location_property_osmline';
|
$sSQL = 'SELECT distinct place_id FROM location_property_osmline';
|
||||||
$sSQL .= ' WHERE startnumber is not NULL';
|
$sSQL .= ' WHERE startnumber is not NULL';
|
||||||
@@ -765,9 +771,8 @@ class SearchDescription
|
|||||||
$sSQL .= $iHousenumber.'>=startnumber and ';
|
$sSQL .= $iHousenumber.'>=startnumber and ';
|
||||||
$sSQL .= $iHousenumber.'<=endnumber';
|
$sSQL .= $iHousenumber.'<=endnumber';
|
||||||
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
|
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
|
||||||
$sSQL .= " limit $iLimit";
|
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$oResult = new Result($iPlaceId, Result::TABLE_OSMLINE);
|
$oResult = new Result($iPlaceId, Result::TABLE_OSMLINE);
|
||||||
@@ -777,14 +782,13 @@ class SearchDescription
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If nothing found try the aux fallback table
|
// If nothing found try the aux fallback table
|
||||||
if (CONST_Use_Aux_Location_data && !sizeof($aResults)) {
|
if (CONST_Use_Aux_Location_data && empty($aResults)) {
|
||||||
$sSQL = 'SELECT place_id FROM location_property_aux';
|
$sSQL = 'SELECT place_id FROM location_property_aux';
|
||||||
$sSQL .= ' WHERE parent_place_id in ('.$sPlaceIDs.')';
|
$sSQL .= ' WHERE parent_place_id in ('.$sPlaceIDs.')';
|
||||||
$sSQL .= " AND housenumber = '".$this->sHouseNumber."'";
|
$sSQL .= " AND housenumber = '".$this->sHouseNumber."'";
|
||||||
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
|
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
|
||||||
$sSQL .= " limit $iLimit";
|
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId, Result::TABLE_AUX);
|
$aResults[$iPlaceId] = new Result($iPlaceId, Result::TABLE_AUX);
|
||||||
@@ -792,7 +796,7 @@ class SearchDescription
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If nothing found then search in Tiger data (location_property_tiger)
|
// If nothing found then search in Tiger data (location_property_tiger)
|
||||||
if (CONST_Use_US_Tiger_Data && $bIsIntHouseNumber && !sizeof($aResults)) {
|
if (CONST_Use_US_Tiger_Data && $bIsIntHouseNumber && empty($aResults)) {
|
||||||
$sSQL = 'SELECT place_id FROM location_property_tiger';
|
$sSQL = 'SELECT place_id FROM location_property_tiger';
|
||||||
$sSQL .= ' WHERE parent_place_id in ('.$sPlaceIDs.') and (';
|
$sSQL .= ' WHERE parent_place_id in ('.$sPlaceIDs.') and (';
|
||||||
if ($iHousenumber % 2 == 0) {
|
if ($iHousenumber % 2 == 0) {
|
||||||
@@ -804,9 +808,8 @@ class SearchDescription
|
|||||||
$sSQL .= $iHousenumber.'>=startnumber and ';
|
$sSQL .= $iHousenumber.'>=startnumber and ';
|
||||||
$sSQL .= $iHousenumber.'<=endnumber';
|
$sSQL .= $iHousenumber.'<=endnumber';
|
||||||
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
|
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
|
||||||
$sSQL .= " limit $iLimit";
|
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$oResult = new Result($iPlaceId, Result::TABLE_TIGER);
|
$oResult = new Result($iPlaceId, Result::TABLE_TIGER);
|
||||||
@@ -840,7 +843,7 @@ class SearchDescription
|
|||||||
$sSQL .= ' ORDER BY rank_search ASC ';
|
$sSQL .= ' ORDER BY rank_search ASC ';
|
||||||
$sSQL .= " LIMIT $iLimit";
|
$sSQL .= " LIMIT $iLimit";
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId);
|
$aResults[$iPlaceId] = new Result($iPlaceId);
|
||||||
@@ -854,7 +857,7 @@ class SearchDescription
|
|||||||
$bCacheTable = (bool) chksql($oDB->getOne($sSQL));
|
$bCacheTable = (bool) chksql($oDB->getOne($sSQL));
|
||||||
|
|
||||||
$sSQL = "SELECT min(rank_search) FROM placex WHERE place_id in ($sPlaceIDs)";
|
$sSQL = "SELECT min(rank_search) FROM placex WHERE place_id in ($sPlaceIDs)";
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$iMaxRank = (int)chksql($oDB->getOne($sSQL));
|
$iMaxRank = (int)chksql($oDB->getOne($sSQL));
|
||||||
|
|
||||||
// For state / country level searches the normal radius search doesn't work very well
|
// For state / country level searches the normal radius search doesn't work very well
|
||||||
@@ -867,7 +870,7 @@ class SearchDescription
|
|||||||
$sSQL .= " AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon')";
|
$sSQL .= " AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon')";
|
||||||
$sSQL .= ' ORDER BY rank_search ASC ';
|
$sSQL .= ' ORDER BY rank_search ASC ';
|
||||||
$sSQL .= ' LIMIT 1';
|
$sSQL .= ' LIMIT 1';
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$sPlaceGeom = chksql($oDB->getOne($sSQL));
|
$sPlaceGeom = chksql($oDB->getOne($sSQL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,7 +880,7 @@ class SearchDescription
|
|||||||
$iMaxRank += 5;
|
$iMaxRank += 5;
|
||||||
$sSQL = 'SELECT place_id FROM placex';
|
$sSQL = 'SELECT place_id FROM placex';
|
||||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) and rank_search < $iMaxRank";
|
$sSQL .= " WHERE place_id in ($sPlaceIDs) and rank_search < $iMaxRank";
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aPlaceIDs = chksql($oDB->getCol($sSQL));
|
$aPlaceIDs = chksql($oDB->getCol($sSQL));
|
||||||
$sPlaceIDs = join(',', $aPlaceIDs);
|
$sPlaceIDs = join(',', $aPlaceIDs);
|
||||||
}
|
}
|
||||||
@@ -922,7 +925,7 @@ class SearchDescription
|
|||||||
}
|
}
|
||||||
$sSQL .= " limit $iLimit";
|
$sSQL .= " limit $iLimit";
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId);
|
$aResults[$iPlaceId] = new Result($iPlaceId);
|
||||||
@@ -954,7 +957,7 @@ class SearchDescription
|
|||||||
}
|
}
|
||||||
$sSQL .= " limit $iLimit";
|
$sSQL .= " limit $iLimit";
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId);
|
$aResults[$iPlaceId] = new Result($iPlaceId);
|
||||||
@@ -999,6 +1002,24 @@ class SearchDescription
|
|||||||
//////////// Debugging functions
|
//////////// Debugging functions
|
||||||
|
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'Search rank' => $this->iSearchRank,
|
||||||
|
'Country code' => $this->sCountryCode,
|
||||||
|
'Name terms' => $this->aName,
|
||||||
|
'Name terms (stop words)' => $this->aNameNonSearch,
|
||||||
|
'Address terms' => $this->aAddress,
|
||||||
|
'Address terms (stop words)' => $this->aAddressNonSearch,
|
||||||
|
'Address terms (full words)' => $this->aFullNameAddress,
|
||||||
|
'Special search' => $this->iOperator,
|
||||||
|
'Class' => $this->sClass,
|
||||||
|
'Type' => $this->sType,
|
||||||
|
'House number' => $this->sHouseNumber,
|
||||||
|
'Postcode' => $this->sPostcode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function dumpAsHtmlTableRow(&$aWordIDs)
|
public function dumpAsHtmlTableRow(&$aWordIDs)
|
||||||
{
|
{
|
||||||
$kf = function ($k) use (&$aWordIDs) {
|
$kf = function ($k) use (&$aWordIDs) {
|
||||||
|
|||||||
54
lib/Status.php
Normal file
54
lib/Status.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use PEAR;
|
||||||
|
|
||||||
|
class Status
|
||||||
|
{
|
||||||
|
protected $oDB;
|
||||||
|
|
||||||
|
public function __construct(&$oDB)
|
||||||
|
{
|
||||||
|
$this->oDB =& $oDB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function status()
|
||||||
|
{
|
||||||
|
if (!$this->oDB || PEAR::isError($this->oDB)) {
|
||||||
|
throw new Exception('No database', 700);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sStandardWord = $this->oDB->getOne("SELECT make_standard_name('a')");
|
||||||
|
if (PEAR::isError($sStandardWord)) {
|
||||||
|
throw new Exception('Module failed', 701);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sStandardWord != 'a') {
|
||||||
|
throw new Exception('Module call failed', 702);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code, ';
|
||||||
|
$sSQL .= "operator, search_name_count FROM word WHERE word_token IN (' a')";
|
||||||
|
$iWordID = $this->oDB->getOne($sSQL);
|
||||||
|
if (PEAR::isError($iWordID)) {
|
||||||
|
throw new Exception('Query failed', 703);
|
||||||
|
}
|
||||||
|
if (!$iWordID) {
|
||||||
|
throw new Exception('No value', 704);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataDate()
|
||||||
|
{
|
||||||
|
$sSQL = 'SELECT EXTRACT(EPOCH FROM lastimportdate) FROM import_status LIMIT 1';
|
||||||
|
$iDataDateEpoch = $this->oDB->getOne($sSQL);
|
||||||
|
|
||||||
|
if (PEAR::isError($iDataDateEpoch)) {
|
||||||
|
throw Exception('Data date query failed '.$iDataDateEpoch->getMessage(), 705);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $iDataDateEpoch;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lib/TokenCountry.php
Normal file
29
lib/TokenCountry.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim\Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A country token.
|
||||||
|
*/
|
||||||
|
class Country
|
||||||
|
{
|
||||||
|
/// Database word id, if available.
|
||||||
|
public $iId;
|
||||||
|
/// Two-letter country code (lower-cased).
|
||||||
|
public $sCountryCode;
|
||||||
|
|
||||||
|
public function __construct($iId, $sCountryCode)
|
||||||
|
{
|
||||||
|
$this->iId = $iId;
|
||||||
|
$this->sCountryCode = $sCountryCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'ID' => $this->iId,
|
||||||
|
'Type' => 'country',
|
||||||
|
'Info' => $this->sCountryCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lib/TokenHousenumber.php
Normal file
29
lib/TokenHousenumber.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim\Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A house number token.
|
||||||
|
*/
|
||||||
|
class HouseNumber
|
||||||
|
{
|
||||||
|
/// Database word id, if available.
|
||||||
|
public $iId;
|
||||||
|
/// Normalized house number.
|
||||||
|
public $sToken;
|
||||||
|
|
||||||
|
public function __construct($iId, $sToken)
|
||||||
|
{
|
||||||
|
$this->iId = $iId;
|
||||||
|
$this->sToken = $sToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'ID' => $this->iId,
|
||||||
|
'Type' => 'house number',
|
||||||
|
'Info' => array('nr' => $this->sToken)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
188
lib/TokenList.php
Normal file
188
lib/TokenList.php
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim;
|
||||||
|
|
||||||
|
require_once(CONST_BasePath.'/lib/TokenCountry.php');
|
||||||
|
require_once(CONST_BasePath.'/lib/TokenHousenumber.php');
|
||||||
|
require_once(CONST_BasePath.'/lib/TokenPostcode.php');
|
||||||
|
require_once(CONST_BasePath.'/lib/TokenSpecialTerm.php');
|
||||||
|
require_once(CONST_BasePath.'/lib/TokenWord.php');
|
||||||
|
require_once(CONST_BasePath.'/lib/SpecialSearchOperator.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves information about the tokens that appear in a search query.
|
||||||
|
*
|
||||||
|
* Tokens are sorted by their normalized form, the token word. There are different
|
||||||
|
* kinds of tokens, represented by different Token* classes. Note that
|
||||||
|
* tokens do not have a common base class. All tokens need to have a field
|
||||||
|
* with the word id that points to an entry in the `word` database table
|
||||||
|
* but otherwise the information saved about a token can be very different.
|
||||||
|
*
|
||||||
|
* There are two different kinds of token words: full words and partial terms.
|
||||||
|
*
|
||||||
|
* Full words start with a space. They represent a complete name of a place.
|
||||||
|
* All special tokens are normally full words.
|
||||||
|
*
|
||||||
|
* Partial terms have no space at the beginning. They may represent a part of
|
||||||
|
* a name of a place (e.g. in the name 'World Trade Center' a partial term
|
||||||
|
* would be 'Trade' or 'Trade Center'). They are only used in TokenWord.
|
||||||
|
*/
|
||||||
|
class TokenList
|
||||||
|
{
|
||||||
|
// List of list of tokens indexed by their word_token.
|
||||||
|
private $aTokens = array();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return total number of tokens.
|
||||||
|
*
|
||||||
|
* @return Integer
|
||||||
|
*/
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
return count($this->aTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there are tokens for the given token word.
|
||||||
|
*
|
||||||
|
* @param string $sWord Token word to look for.
|
||||||
|
*
|
||||||
|
* @return bool True if there is one or more token for the token word.
|
||||||
|
*/
|
||||||
|
public function contains($sWord)
|
||||||
|
{
|
||||||
|
return isset($this->aTokens[$sWord]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of tokens for the given token word.
|
||||||
|
*
|
||||||
|
* @param string $sWord Token word to look for.
|
||||||
|
*
|
||||||
|
* @return object[] Array of tokens for the given token word or an
|
||||||
|
* empty array if no tokens could be found.
|
||||||
|
*/
|
||||||
|
public function get($sWord)
|
||||||
|
{
|
||||||
|
return isset($this->aTokens[$sWord]) ? $this->aTokens[$sWord] : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add token information from the word table in the database.
|
||||||
|
*
|
||||||
|
* @param object $oDB Database connection.
|
||||||
|
* @param string[] $aTokens List of tokens to look up in the database.
|
||||||
|
* @param string[] $aCountryCodes List of country restrictions.
|
||||||
|
* @param string $sNormQuery Normalized query string.
|
||||||
|
* @param object $oNormalizer Normalizer function to use on tokens.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addTokensFromDB(&$oDB, &$aTokens, &$aCountryCodes, $sNormQuery, $oNormalizer)
|
||||||
|
{
|
||||||
|
// Check which tokens we have, get the ID numbers
|
||||||
|
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code,';
|
||||||
|
$sSQL .= ' operator, coalesce(search_name_count, 0) as count';
|
||||||
|
$sSQL .= ' FROM word WHERE word_token in (';
|
||||||
|
$sSQL .= join(',', array_map('getDBQuoted', $aTokens)).')';
|
||||||
|
|
||||||
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
|
$aDBWords = chksql($oDB->getAll($sSQL), 'Could not get word tokens.');
|
||||||
|
|
||||||
|
foreach ($aDBWords as $aWord) {
|
||||||
|
$oToken = null;
|
||||||
|
$iId = (int) $aWord['word_id'];
|
||||||
|
|
||||||
|
if ($aWord['class']) {
|
||||||
|
// Special terms need to appear in their normalized form.
|
||||||
|
if ($aWord['word']) {
|
||||||
|
$sNormWord = $aWord['word'];
|
||||||
|
if ($oNormalizer != null) {
|
||||||
|
$sNormWord = $oNormalizer->transliterate($aWord['word']);
|
||||||
|
}
|
||||||
|
if (strpos($sNormQuery, $sNormWord) === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aWord['class'] == 'place' && $aWord['type'] == 'house') {
|
||||||
|
$oToken = new Token\HouseNumber($iId, trim($aWord['word_token']));
|
||||||
|
} elseif ($aWord['class'] == 'place' && $aWord['type'] == 'postcode') {
|
||||||
|
if ($aWord['word']
|
||||||
|
&& pg_escape_string($aWord['word']) == $aWord['word']
|
||||||
|
) {
|
||||||
|
$oToken = new Token\Postcode(
|
||||||
|
$iId,
|
||||||
|
$aWord['word'],
|
||||||
|
$aWord['country_code']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// near and in operator the same at the moment
|
||||||
|
$oToken = new Token\SpecialTerm(
|
||||||
|
$iId,
|
||||||
|
$aWord['class'],
|
||||||
|
$aWord['type'],
|
||||||
|
$aWord['operator'] ? Operator::NEAR : Operator::NONE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif ($aWord['country_code']) {
|
||||||
|
// Filter country tokens that do not match restricted countries.
|
||||||
|
if (!$aCountryCodes
|
||||||
|
|| in_array($aWord['country_code'], $aCountryCodes)
|
||||||
|
) {
|
||||||
|
$oToken = new Token\Country($iId, $aWord['country_code']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$oToken = new Token\Word(
|
||||||
|
$iId,
|
||||||
|
$aWord['word_token'][0] != ' ',
|
||||||
|
(int) $aWord['count']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($oToken) {
|
||||||
|
$this->addToken($aWord['word_token'], $oToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new token for the given word.
|
||||||
|
*
|
||||||
|
* @param string $sWord Word the token describes.
|
||||||
|
* @param object $oToken Token object to add.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addToken($sWord, $oToken)
|
||||||
|
{
|
||||||
|
if (isset($this->aTokens[$sWord])) {
|
||||||
|
$this->aTokens[$sWord][] = $oToken;
|
||||||
|
} else {
|
||||||
|
$this->aTokens[$sWord] = array($oToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debugTokenByWordIdList()
|
||||||
|
{
|
||||||
|
$aWordsIDs = array();
|
||||||
|
foreach ($this->aTokens as $sToken => $aWords) {
|
||||||
|
foreach ($aWords as $aToken) {
|
||||||
|
if ($aToken->iId !== null) {
|
||||||
|
$aWordsIDs[$aToken->iId] =
|
||||||
|
'#'.$sToken.'('.$aToken->iId.')#';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aWordsIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return $this->aTokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
lib/TokenPostcode.php
Normal file
32
lib/TokenPostcode.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim\Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A postcode token.
|
||||||
|
*/
|
||||||
|
class Postcode
|
||||||
|
{
|
||||||
|
/// Database word id, if available.
|
||||||
|
public $iId;
|
||||||
|
/// Full nomralized postcode (upper cased).
|
||||||
|
public $sPostcode;
|
||||||
|
// Optional country code the postcode belongs to (currently unused).
|
||||||
|
public $sCountryCode;
|
||||||
|
|
||||||
|
public function __construct($iId, $sPostcode, $sCountryCode = '')
|
||||||
|
{
|
||||||
|
$this->iId = $iId;
|
||||||
|
$this->sPostcode = $sPostcode;
|
||||||
|
$this->sCountryCode = empty($sCountryCode) ? '' : $sCountryCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'ID' => $this->iId,
|
||||||
|
'Type' => 'postcode',
|
||||||
|
'Info' => $this->sPostcode.'('.$this->sCountryCode.')'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
lib/TokenSpecialTerm.php
Normal file
41
lib/TokenSpecialTerm.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim\Token;
|
||||||
|
|
||||||
|
require_once(CONST_BasePath.'/lib/SpecialSearchOperator.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A word token describing a place type.
|
||||||
|
*/
|
||||||
|
class SpecialTerm
|
||||||
|
{
|
||||||
|
/// Database word id, if applicable.
|
||||||
|
public $iId;
|
||||||
|
/// Class (or OSM tag key) of the place to look for.
|
||||||
|
public $sClass;
|
||||||
|
/// Type (or OSM tag value) of the place to look for.
|
||||||
|
public $sType;
|
||||||
|
/// Relationship of the operator to the object (see Operator class).
|
||||||
|
public $iOperator;
|
||||||
|
|
||||||
|
public function __construct($iID, $sClass, $sType, $iOperator)
|
||||||
|
{
|
||||||
|
$this->iId = $iID;
|
||||||
|
$this->sClass = $sClass;
|
||||||
|
$this->sType = $sType;
|
||||||
|
$this->iOperator = $iOperator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'ID' => $this->iId,
|
||||||
|
'Type' => 'special term',
|
||||||
|
'Info' => array(
|
||||||
|
'class' => $this->sClass,
|
||||||
|
'type' => $this->sType,
|
||||||
|
'operator' => \Nominatim\Operator::toString($this->iOperator)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
lib/TokenWord.php
Normal file
35
lib/TokenWord.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nominatim\Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A standard word token.
|
||||||
|
*/
|
||||||
|
class Word
|
||||||
|
{
|
||||||
|
/// Database word id, if applicable.
|
||||||
|
public $iId;
|
||||||
|
/// If true, the word may represent only part of a place name.
|
||||||
|
public $bPartial;
|
||||||
|
/// Number of appearances in the database.
|
||||||
|
public $iSearchNameCount;
|
||||||
|
|
||||||
|
public function __construct($iId, $bPartial, $iSearchNameCount)
|
||||||
|
{
|
||||||
|
$this->iId = $iId;
|
||||||
|
$this->bPartial = $bPartial;
|
||||||
|
$this->iSearchNameCount = $iSearchNameCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debugInfo()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'ID' => $this->iId,
|
||||||
|
'Type' => 'word',
|
||||||
|
'Info' => array(
|
||||||
|
'partial' => $this->bPartial,
|
||||||
|
'count' => $this->iSearchNameCount
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
lib/cmd.php
46
lib/cmd.php
@@ -16,7 +16,7 @@ function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnkn
|
|||||||
|
|
||||||
$aResult = array();
|
$aResult = array();
|
||||||
$bUnknown = false;
|
$bUnknown = false;
|
||||||
$iSize = sizeof($aArg);
|
$iSize = count($aArg);
|
||||||
for ($i = 1; $i < $iSize; $i++) {
|
for ($i = 1; $i < $iSize; $i++) {
|
||||||
if (isset($aQuick[$aArg[$i]])) {
|
if (isset($aQuick[$aArg[$i]])) {
|
||||||
$aLine = $aQuick[$aArg[$i]];
|
$aLine = $aQuick[$aArg[$i]];
|
||||||
@@ -134,7 +134,7 @@ function info($sMsg)
|
|||||||
echo date('Y-m-d H:i:s == ').$sMsg."\n";
|
echo date('Y-m-d H:i:s == ').$sMsg."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$aWarnings = [];
|
$aWarnings = array();
|
||||||
|
|
||||||
|
|
||||||
function warn($sMsg)
|
function warn($sMsg)
|
||||||
@@ -158,6 +158,16 @@ function runSQLScript($sScript, $bfatal = true, $bVerbose = false, $bIgnoreError
|
|||||||
$aDSNInfo = DB::parseDSN(CONST_Database_DSN);
|
$aDSNInfo = DB::parseDSN(CONST_Database_DSN);
|
||||||
if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
|
if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
|
||||||
$sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
|
$sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
|
||||||
|
if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
|
||||||
|
$sCMD .= ' -h ' . $aDSNInfo['hostspec'];
|
||||||
|
}
|
||||||
|
if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
|
||||||
|
$sCMD .= ' -U ' . $aDSNInfo['username'];
|
||||||
|
}
|
||||||
|
$aProcEnv = null;
|
||||||
|
if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
|
||||||
|
$aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
|
||||||
|
}
|
||||||
if (!$bVerbose) {
|
if (!$bVerbose) {
|
||||||
$sCMD .= ' -q';
|
$sCMD .= ' -q';
|
||||||
}
|
}
|
||||||
@@ -170,15 +180,19 @@ function runSQLScript($sScript, $bfatal = true, $bVerbose = false, $bIgnoreError
|
|||||||
2 => STDERR
|
2 => STDERR
|
||||||
);
|
);
|
||||||
$ahPipes = null;
|
$ahPipes = null;
|
||||||
$hProcess = @proc_open($sCMD, $aDescriptors, $ahPipes);
|
$hProcess = @proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
|
||||||
if (!is_resource($hProcess)) {
|
if (!is_resource($hProcess)) {
|
||||||
fail('unable to start pgsql');
|
fail('unable to start pgsql');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$bVerbose) {
|
||||||
|
fwrite($ahPipes[0], 'set client_min_messages to WARNING;');
|
||||||
|
}
|
||||||
|
|
||||||
while (strlen($sScript)) {
|
while (strlen($sScript)) {
|
||||||
$written = fwrite($ahPipes[0], $sScript);
|
$iWritten = fwrite($ahPipes[0], $sScript);
|
||||||
if ($written <= 0) break;
|
if ($iWritten <= 0) break;
|
||||||
$sScript = substr($sScript, $written);
|
$sScript = substr($sScript, $iWritten);
|
||||||
}
|
}
|
||||||
fclose($ahPipes[0]);
|
fclose($ahPipes[0]);
|
||||||
$iReturn = proc_close($hProcess);
|
$iReturn = proc_close($hProcess);
|
||||||
@@ -186,3 +200,23 @@ function runSQLScript($sScript, $bfatal = true, $bVerbose = false, $bIgnoreError
|
|||||||
fail("pgsql returned with error code ($iReturn)");
|
fail("pgsql returned with error code ($iReturn)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function runWithEnv($sCmd, $aEnv)
|
||||||
|
{
|
||||||
|
$aFDs = array(
|
||||||
|
0 => array('pipe', 'r'),
|
||||||
|
1 => STDOUT,
|
||||||
|
2 => STDERR
|
||||||
|
);
|
||||||
|
$aPipes = null;
|
||||||
|
$hProc = @proc_open($sCmd, $aFDs, $aPipes, null, $aEnv);
|
||||||
|
if (!is_resource($hProc)) {
|
||||||
|
fail('unable to run command:' . $sCmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($aPipes[0]); // no stdin
|
||||||
|
|
||||||
|
$iStat = proc_close($hProc);
|
||||||
|
return $iStat;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
require_once('init.php');
|
require_once('init.php');
|
||||||
require_once('cmd.php');
|
require_once('cmd.php');
|
||||||
|
require_once('DebugNone.php');
|
||||||
|
|
||||||
// handle http proxy when using file_get_contents
|
// handle http proxy when using file_get_contents
|
||||||
if (CONST_HTTP_Proxy) {
|
if (CONST_HTTP_Proxy) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
require_once('init.php');
|
require_once('init.php');
|
||||||
require_once('ParameterParser.php');
|
require_once('ParameterParser.php');
|
||||||
|
require_once(CONST_Debug ? 'DebugHtml.php' : 'DebugNone.php');
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
*
|
*
|
||||||
|
|||||||
418
lib/lib.php
418
lib/lib.php
@@ -1,6 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
function fail($sError, $sUserError = false)
|
function fail($sError, $sUserError = false)
|
||||||
{
|
{
|
||||||
if (!$sUserError) $sUserError = $sError;
|
if (!$sUserError) $sUserError = $sError;
|
||||||
@@ -14,7 +13,7 @@ function getProcessorCount()
|
|||||||
{
|
{
|
||||||
$sCPU = file_get_contents('/proc/cpuinfo');
|
$sCPU = file_get_contents('/proc/cpuinfo');
|
||||||
preg_match_all('#processor\s+: [0-9]+#', $sCPU, $aMatches);
|
preg_match_all('#processor\s+: [0-9]+#', $sCPU, $aMatches);
|
||||||
return sizeof($aMatches[0]);
|
return count($aMatches[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -60,349 +59,6 @@ function byImportance($a, $b)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getClassTypes()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'boundary:administrative:1' => array('label' => 'Continent', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:2' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:country' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 6, 'defdiameter' => 15),
|
|
||||||
'boundary:administrative:3' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:4' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:state' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 5.12),
|
|
||||||
'boundary:administrative:5' => array('label' => 'State District', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:6' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:7' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:county' => array('label' => 'County', 'frequency' => 108, 'icon' => 'poi_boundary_administrative', 'defzoom' => 10, 'defdiameter' => 1.28),
|
|
||||||
'boundary:administrative:8' => array('label' => 'City', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:city' => array('label' => 'City', 'frequency' => 66, 'icon' => 'poi_place_city', 'defzoom' => 12, 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:9' => array('label' => 'City District', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:10' => array('label' => 'Suburb', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:11' => array('label' => 'Neighbourhood', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:region' => array('label' => 'Region', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 0.04),
|
|
||||||
'place:island' => array('label' => 'Island', 'frequency' => 288, 'icon' => '', 'defzoom' => 11, 'defdiameter' => 0.64),
|
|
||||||
'boundary:administrative' => array('label' => 'Administrative', 'frequency' => 413, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:postal_code' => array('label' => 'Postcode', 'frequency' => 413, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:town' => array('label' => 'Town', 'frequency' => 1497, 'icon' => 'poi_place_town', 'defzoom' => 14, 'defdiameter' => 0.08),
|
|
||||||
'place:village' => array('label' => 'Village', 'frequency' => 11230, 'icon' => 'poi_place_village', 'defzoom' => 15, 'defdiameter' => 0.04),
|
|
||||||
'place:hamlet' => array('label' => 'Hamlet', 'frequency' => 7075, 'icon' => 'poi_place_village', 'defzoom' => 15, 'defdiameter' => 0.04),
|
|
||||||
'place:suburb' => array('label' => 'Suburb', 'frequency' => 2528, 'icon' => 'poi_place_village', 'defdiameter' => 0.04),
|
|
||||||
'place:locality' => array('label' => 'Locality', 'frequency' => 4113, 'icon' => 'poi_place_village', 'defdiameter' => 0.02),
|
|
||||||
'landuse:farm' => array('label' => 'Farm', 'frequency' => 1201, 'icon' => '', 'defdiameter' => 0.02),
|
|
||||||
'place:farm' => array('label' => 'Farm', 'frequency' => 1162, 'icon' => '', 'defdiameter' => 0.02),
|
|
||||||
|
|
||||||
'highway:motorway_junction' => array('label' => 'Motorway Junction', 'frequency' => 1126, 'icon' => '', 'simplelabel' => 'Junction'),
|
|
||||||
'highway:motorway' => array('label' => 'Motorway', 'frequency' => 4627, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:trunk' => array('label' => 'Trunk', 'frequency' => 23084, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:primary' => array('label' => 'Primary', 'frequency' => 32138, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:secondary' => array('label' => 'Secondary', 'frequency' => 25807, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:tertiary' => array('label' => 'Tertiary', 'frequency' => 29829, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:residential' => array('label' => 'Residential', 'frequency' => 361498, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:unclassified' => array('label' => 'Unclassified', 'frequency' => 66441, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:living_street' => array('label' => 'Living Street', 'frequency' => 710, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:service' => array('label' => 'Service', 'frequency' => 9963, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:track' => array('label' => 'Track', 'frequency' => 2565, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:road' => array('label' => 'Road', 'frequency' => 591, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:byway' => array('label' => 'Byway', 'frequency' => 346, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:bridleway' => array('label' => 'Bridleway', 'frequency' => 1556, 'icon' => ''),
|
|
||||||
'highway:cycleway' => array('label' => 'Cycleway', 'frequency' => 2419, 'icon' => ''),
|
|
||||||
'highway:pedestrian' => array('label' => 'Pedestrian', 'frequency' => 2757, 'icon' => ''),
|
|
||||||
'highway:footway' => array('label' => 'Footway', 'frequency' => 15008, 'icon' => ''),
|
|
||||||
'highway:steps' => array('label' => 'Steps', 'frequency' => 444, 'icon' => '', 'simplelabel' => 'Footway'),
|
|
||||||
'highway:motorway_link' => array('label' => 'Motorway Link', 'frequency' => 795, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:trunk_link' => array('label' => 'Trunk Link', 'frequency' => 1258, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
'highway:primary_link' => array('label' => 'Primary Link', 'frequency' => 313, 'icon' => '', 'simplelabel' => 'Road'),
|
|
||||||
|
|
||||||
'landuse:industrial' => array('label' => 'Industrial', 'frequency' => 1062, 'icon' => ''),
|
|
||||||
'landuse:residential' => array('label' => 'Residential', 'frequency' => 886, 'icon' => ''),
|
|
||||||
'landuse:retail' => array('label' => 'Retail', 'frequency' => 754, 'icon' => ''),
|
|
||||||
'landuse:commercial' => array('label' => 'Commercial', 'frequency' => 657, 'icon' => ''),
|
|
||||||
|
|
||||||
'place:airport' => array('label' => 'Airport', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
|
||||||
'aeroway:aerodrome' => array('label' => 'Aerodrome', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
|
||||||
'aeroway' => array('label' => 'Aeroway', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
|
||||||
'railway:station' => array('label' => 'Station', 'frequency' => 3431, 'icon' => 'transport_train_station2', 'defdiameter' => 0.01),
|
|
||||||
'amenity:place_of_worship' => array('label' => 'Place Of Worship', 'frequency' => 9049, 'icon' => 'place_of_worship_unknown3'),
|
|
||||||
'amenity:pub' => array('label' => 'Pub', 'frequency' => 18969, 'icon' => 'food_pub'),
|
|
||||||
'amenity:bar' => array('label' => 'Bar', 'frequency' => 164, 'icon' => 'food_bar'),
|
|
||||||
'amenity:university' => array('label' => 'University', 'frequency' => 607, 'icon' => 'education_university'),
|
|
||||||
'tourism:museum' => array('label' => 'Museum', 'frequency' => 543, 'icon' => 'tourist_museum'),
|
|
||||||
'amenity:arts_centre' => array('label' => 'Arts Centre', 'frequency' => 136, 'icon' => 'tourist_art_gallery2'),
|
|
||||||
'tourism:zoo' => array('label' => 'Zoo', 'frequency' => 47, 'icon' => 'tourist_zoo'),
|
|
||||||
'tourism:theme_park' => array('label' => 'Theme Park', 'frequency' => 24, 'icon' => 'poi_point_of_interest'),
|
|
||||||
'tourism:attraction' => array('label' => 'Attraction', 'frequency' => 1463, 'icon' => 'poi_point_of_interest'),
|
|
||||||
'leisure:golf_course' => array('label' => 'Golf Course', 'frequency' => 712, 'icon' => 'sport_golf'),
|
|
||||||
'historic:castle' => array('label' => 'Castle', 'frequency' => 316, 'icon' => 'tourist_castle'),
|
|
||||||
'amenity:hospital' => array('label' => 'Hospital', 'frequency' => 879, 'icon' => 'health_hospital'),
|
|
||||||
'amenity:school' => array('label' => 'School', 'frequency' => 8192, 'icon' => 'education_school'),
|
|
||||||
'amenity:theatre' => array('label' => 'Theatre', 'frequency' => 371, 'icon' => 'tourist_theatre'),
|
|
||||||
'amenity:public_building' => array('label' => 'Public Building', 'frequency' => 985, 'icon' => ''),
|
|
||||||
'amenity:library' => array('label' => 'Library', 'frequency' => 794, 'icon' => 'amenity_library'),
|
|
||||||
'amenity:townhall' => array('label' => 'Townhall', 'frequency' => 242, 'icon' => ''),
|
|
||||||
'amenity:community_centre' => array('label' => 'Community Centre', 'frequency' => 157, 'icon' => ''),
|
|
||||||
'amenity:fire_station' => array('label' => 'Fire Station', 'frequency' => 221, 'icon' => 'amenity_firestation3'),
|
|
||||||
'amenity:police' => array('label' => 'Police', 'frequency' => 334, 'icon' => 'amenity_police2'),
|
|
||||||
'amenity:bank' => array('label' => 'Bank', 'frequency' => 1248, 'icon' => 'money_bank2'),
|
|
||||||
'amenity:post_office' => array('label' => 'Post Office', 'frequency' => 859, 'icon' => 'amenity_post_office'),
|
|
||||||
'leisure:park' => array('label' => 'Park', 'frequency' => 2378, 'icon' => ''),
|
|
||||||
'amenity:park' => array('label' => 'Park', 'frequency' => 53, 'icon' => ''),
|
|
||||||
'landuse:park' => array('label' => 'Park', 'frequency' => 50, 'icon' => ''),
|
|
||||||
'landuse:recreation_ground' => array('label' => 'Recreation Ground', 'frequency' => 517, 'icon' => ''),
|
|
||||||
'tourism:hotel' => array('label' => 'Hotel', 'frequency' => 2150, 'icon' => 'accommodation_hotel2'),
|
|
||||||
'tourism:motel' => array('label' => 'Motel', 'frequency' => 43, 'icon' => ''),
|
|
||||||
'amenity:cinema' => array('label' => 'Cinema', 'frequency' => 277, 'icon' => 'tourist_cinema'),
|
|
||||||
'tourism:artwork' => array('label' => 'Artwork', 'frequency' => 171, 'icon' => 'tourist_art_gallery2'),
|
|
||||||
'historic:archaeological_site' => array('label' => 'Archaeological Site', 'frequency' => 407, 'icon' => 'tourist_archaeological2'),
|
|
||||||
'amenity:doctors' => array('label' => 'Doctors', 'frequency' => 581, 'icon' => 'health_doctors'),
|
|
||||||
'leisure:sports_centre' => array('label' => 'Sports Centre', 'frequency' => 767, 'icon' => 'sport_leisure_centre'),
|
|
||||||
'leisure:swimming_pool' => array('label' => 'Swimming Pool', 'frequency' => 24, 'icon' => 'sport_swimming_outdoor'),
|
|
||||||
'shop:supermarket' => array('label' => 'Supermarket', 'frequency' => 2673, 'icon' => 'shopping_supermarket'),
|
|
||||||
'shop:convenience' => array('label' => 'Convenience', 'frequency' => 1469, 'icon' => 'shopping_convenience'),
|
|
||||||
'amenity:restaurant' => array('label' => 'Restaurant', 'frequency' => 3179, 'icon' => 'food_restaurant'),
|
|
||||||
'amenity:fast_food' => array('label' => 'Fast Food', 'frequency' => 2289, 'icon' => 'food_fastfood'),
|
|
||||||
'amenity:cafe' => array('label' => 'Cafe', 'frequency' => 1780, 'icon' => 'food_cafe'),
|
|
||||||
'tourism:guest_house' => array('label' => 'Guest House', 'frequency' => 223, 'icon' => 'accommodation_bed_and_breakfast'),
|
|
||||||
'amenity:pharmacy' => array('label' => 'Pharmacy', 'frequency' => 733, 'icon' => 'health_pharmacy_dispensing'),
|
|
||||||
'amenity:fuel' => array('label' => 'Fuel', 'frequency' => 1308, 'icon' => 'transport_fuel'),
|
|
||||||
'natural:peak' => array('label' => 'Peak', 'frequency' => 3212, 'icon' => 'poi_peak'),
|
|
||||||
'waterway:waterfall' => array('label' => 'Waterfall', 'frequency' => 24, 'icon' => ''),
|
|
||||||
'natural:wood' => array('label' => 'Wood', 'frequency' => 1845, 'icon' => 'landuse_coniferous_and_deciduous'),
|
|
||||||
'natural:water' => array('label' => 'Water', 'frequency' => 1790, 'icon' => ''),
|
|
||||||
'landuse:forest' => array('label' => 'Forest', 'frequency' => 467, 'icon' => ''),
|
|
||||||
'landuse:cemetery' => array('label' => 'Cemetery', 'frequency' => 463, 'icon' => ''),
|
|
||||||
'landuse:allotments' => array('label' => 'Allotments', 'frequency' => 408, 'icon' => ''),
|
|
||||||
'landuse:farmyard' => array('label' => 'Farmyard', 'frequency' => 397, 'icon' => ''),
|
|
||||||
'railway:rail' => array('label' => 'Rail', 'frequency' => 4894, 'icon' => ''),
|
|
||||||
'waterway:canal' => array('label' => 'Canal', 'frequency' => 1723, 'icon' => ''),
|
|
||||||
'waterway:river' => array('label' => 'River', 'frequency' => 4089, 'icon' => ''),
|
|
||||||
'waterway:stream' => array('label' => 'Stream', 'frequency' => 2684, 'icon' => ''),
|
|
||||||
'shop:bicycle' => array('label' => 'Bicycle', 'frequency' => 349, 'icon' => 'shopping_bicycle'),
|
|
||||||
'shop:clothes' => array('label' => 'Clothes', 'frequency' => 315, 'icon' => 'shopping_clothes'),
|
|
||||||
'shop:hairdresser' => array('label' => 'Hairdresser', 'frequency' => 312, 'icon' => 'shopping_hairdresser'),
|
|
||||||
'shop:doityourself' => array('label' => 'Doityourself', 'frequency' => 247, 'icon' => 'shopping_diy'),
|
|
||||||
'shop:estate_agent' => array('label' => 'Estate Agent', 'frequency' => 162, 'icon' => 'shopping_estateagent2'),
|
|
||||||
'shop:car' => array('label' => 'Car', 'frequency' => 159, 'icon' => 'shopping_car'),
|
|
||||||
'shop:garden_centre' => array('label' => 'Garden Centre', 'frequency' => 143, 'icon' => 'shopping_garden_centre'),
|
|
||||||
'shop:car_repair' => array('label' => 'Car Repair', 'frequency' => 141, 'icon' => 'shopping_car_repair'),
|
|
||||||
'shop:newsagent' => array('label' => 'Newsagent', 'frequency' => 132, 'icon' => ''),
|
|
||||||
'shop:bakery' => array('label' => 'Bakery', 'frequency' => 129, 'icon' => 'shopping_bakery'),
|
|
||||||
'shop:furniture' => array('label' => 'Furniture', 'frequency' => 124, 'icon' => ''),
|
|
||||||
'shop:butcher' => array('label' => 'Butcher', 'frequency' => 105, 'icon' => 'shopping_butcher'),
|
|
||||||
'shop:apparel' => array('label' => 'Apparel', 'frequency' => 98, 'icon' => 'shopping_clothes'),
|
|
||||||
'shop:electronics' => array('label' => 'Electronics', 'frequency' => 96, 'icon' => ''),
|
|
||||||
'shop:department_store' => array('label' => 'Department Store', 'frequency' => 86, 'icon' => ''),
|
|
||||||
'shop:books' => array('label' => 'Books', 'frequency' => 85, 'icon' => ''),
|
|
||||||
'shop:yes' => array('label' => 'Shop', 'frequency' => 68, 'icon' => ''),
|
|
||||||
'shop:outdoor' => array('label' => 'Outdoor', 'frequency' => 67, 'icon' => ''),
|
|
||||||
'shop:mall' => array('label' => 'Mall', 'frequency' => 63, 'icon' => ''),
|
|
||||||
'shop:florist' => array('label' => 'Florist', 'frequency' => 61, 'icon' => ''),
|
|
||||||
'shop:charity' => array('label' => 'Charity', 'frequency' => 60, 'icon' => ''),
|
|
||||||
'shop:hardware' => array('label' => 'Hardware', 'frequency' => 59, 'icon' => ''),
|
|
||||||
'shop:laundry' => array('label' => 'Laundry', 'frequency' => 51, 'icon' => 'shopping_laundrette'),
|
|
||||||
'shop:shoes' => array('label' => 'Shoes', 'frequency' => 49, 'icon' => ''),
|
|
||||||
'shop:beverages' => array('label' => 'Beverages', 'frequency' => 48, 'icon' => 'shopping_alcohol'),
|
|
||||||
'shop:dry_cleaning' => array('label' => 'Dry Cleaning', 'frequency' => 46, 'icon' => ''),
|
|
||||||
'shop:carpet' => array('label' => 'Carpet', 'frequency' => 45, 'icon' => ''),
|
|
||||||
'shop:computer' => array('label' => 'Computer', 'frequency' => 44, 'icon' => ''),
|
|
||||||
'shop:alcohol' => array('label' => 'Alcohol', 'frequency' => 44, 'icon' => 'shopping_alcohol'),
|
|
||||||
'shop:optician' => array('label' => 'Optician', 'frequency' => 55, 'icon' => 'health_opticians'),
|
|
||||||
'shop:chemist' => array('label' => 'Chemist', 'frequency' => 42, 'icon' => 'health_pharmacy'),
|
|
||||||
'shop:gallery' => array('label' => 'Gallery', 'frequency' => 38, 'icon' => 'tourist_art_gallery2'),
|
|
||||||
'shop:mobile_phone' => array('label' => 'Mobile Phone', 'frequency' => 37, 'icon' => ''),
|
|
||||||
'shop:sports' => array('label' => 'Sports', 'frequency' => 37, 'icon' => ''),
|
|
||||||
'shop:jewelry' => array('label' => 'Jewelry', 'frequency' => 32, 'icon' => 'shopping_jewelry'),
|
|
||||||
'shop:pet' => array('label' => 'Pet', 'frequency' => 29, 'icon' => ''),
|
|
||||||
'shop:beauty' => array('label' => 'Beauty', 'frequency' => 28, 'icon' => ''),
|
|
||||||
'shop:stationery' => array('label' => 'Stationery', 'frequency' => 25, 'icon' => ''),
|
|
||||||
'shop:shopping_centre' => array('label' => 'Shopping Centre', 'frequency' => 25, 'icon' => ''),
|
|
||||||
'shop:general' => array('label' => 'General', 'frequency' => 25, 'icon' => ''),
|
|
||||||
'shop:electrical' => array('label' => 'Electrical', 'frequency' => 25, 'icon' => ''),
|
|
||||||
'shop:toys' => array('label' => 'Toys', 'frequency' => 23, 'icon' => ''),
|
|
||||||
'shop:jeweller' => array('label' => 'Jeweller', 'frequency' => 23, 'icon' => ''),
|
|
||||||
'shop:betting' => array('label' => 'Betting', 'frequency' => 23, 'icon' => ''),
|
|
||||||
'shop:household' => array('label' => 'Household', 'frequency' => 21, 'icon' => ''),
|
|
||||||
'shop:travel_agency' => array('label' => 'Travel Agency', 'frequency' => 21, 'icon' => ''),
|
|
||||||
'shop:hifi' => array('label' => 'Hifi', 'frequency' => 21, 'icon' => ''),
|
|
||||||
'amenity:shop' => array('label' => 'Shop', 'frequency' => 61, 'icon' => ''),
|
|
||||||
'tourism:information' => array('label' => 'Information', 'frequency' => 224, 'icon' => 'amenity_information'),
|
|
||||||
|
|
||||||
'place:house' => array('label' => 'House', 'frequency' => 2086, 'icon' => '', 'defzoom' => 18),
|
|
||||||
'place:house_name' => array('label' => 'House', 'frequency' => 2086, 'icon' => '', 'defzoom' => 18),
|
|
||||||
'place:house_number' => array('label' => 'House Number', 'frequency' => 2086, 'icon' => '', 'defzoom' => 18),
|
|
||||||
'place:country_code' => array('label' => 'Country Code', 'frequency' => 2086, 'icon' => '', 'defzoom' => 18),
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
'leisure:pitch' => array('label' => 'Pitch', 'frequency' => 762, 'icon' => ''),
|
|
||||||
'highway:unsurfaced' => array('label' => 'Unsurfaced', 'frequency' => 492, 'icon' => ''),
|
|
||||||
'historic:ruins' => array('label' => 'Ruins', 'frequency' => 483, 'icon' => 'tourist_ruin'),
|
|
||||||
'amenity:college' => array('label' => 'College', 'frequency' => 473, 'icon' => 'education_school'),
|
|
||||||
'historic:monument' => array('label' => 'Monument', 'frequency' => 470, 'icon' => 'tourist_monument'),
|
|
||||||
'railway:subway' => array('label' => 'Subway', 'frequency' => 385, 'icon' => ''),
|
|
||||||
'historic:memorial' => array('label' => 'Memorial', 'frequency' => 382, 'icon' => 'tourist_monument'),
|
|
||||||
'leisure:nature_reserve' => array('label' => 'Nature Reserve', 'frequency' => 342, 'icon' => ''),
|
|
||||||
'leisure:common' => array('label' => 'Common', 'frequency' => 322, 'icon' => ''),
|
|
||||||
'waterway:lock_gate' => array('label' => 'Lock Gate', 'frequency' => 321, 'icon' => ''),
|
|
||||||
'natural:fell' => array('label' => 'Fell', 'frequency' => 308, 'icon' => ''),
|
|
||||||
'amenity:nightclub' => array('label' => 'Nightclub', 'frequency' => 292, 'icon' => ''),
|
|
||||||
'highway:path' => array('label' => 'Path', 'frequency' => 287, 'icon' => ''),
|
|
||||||
'leisure:garden' => array('label' => 'Garden', 'frequency' => 285, 'icon' => ''),
|
|
||||||
'landuse:reservoir' => array('label' => 'Reservoir', 'frequency' => 276, 'icon' => ''),
|
|
||||||
'leisure:playground' => array('label' => 'Playground', 'frequency' => 264, 'icon' => ''),
|
|
||||||
'leisure:stadium' => array('label' => 'Stadium', 'frequency' => 212, 'icon' => ''),
|
|
||||||
'historic:mine' => array('label' => 'Mine', 'frequency' => 193, 'icon' => 'poi_mine'),
|
|
||||||
'natural:cliff' => array('label' => 'Cliff', 'frequency' => 193, 'icon' => ''),
|
|
||||||
'tourism:caravan_site' => array('label' => 'Caravan Site', 'frequency' => 183, 'icon' => 'accommodation_caravan_park'),
|
|
||||||
'amenity:bus_station' => array('label' => 'Bus Station', 'frequency' => 181, 'icon' => 'transport_bus_station'),
|
|
||||||
'amenity:kindergarten' => array('label' => 'Kindergarten', 'frequency' => 179, 'icon' => ''),
|
|
||||||
'highway:construction' => array('label' => 'Construction', 'frequency' => 176, 'icon' => ''),
|
|
||||||
'amenity:atm' => array('label' => 'Atm', 'frequency' => 172, 'icon' => 'money_atm2'),
|
|
||||||
'amenity:emergency_phone' => array('label' => 'Emergency Phone', 'frequency' => 164, 'icon' => ''),
|
|
||||||
'waterway:lock' => array('label' => 'Lock', 'frequency' => 146, 'icon' => ''),
|
|
||||||
'waterway:riverbank' => array('label' => 'Riverbank', 'frequency' => 143, 'icon' => ''),
|
|
||||||
'natural:coastline' => array('label' => 'Coastline', 'frequency' => 142, 'icon' => ''),
|
|
||||||
'tourism:viewpoint' => array('label' => 'Viewpoint', 'frequency' => 140, 'icon' => 'tourist_view_point'),
|
|
||||||
'tourism:hostel' => array('label' => 'Hostel', 'frequency' => 140, 'icon' => ''),
|
|
||||||
'tourism:bed_and_breakfast' => array('label' => 'Bed And Breakfast', 'frequency' => 140, 'icon' => 'accommodation_bed_and_breakfast'),
|
|
||||||
'railway:halt' => array('label' => 'Halt', 'frequency' => 135, 'icon' => ''),
|
|
||||||
'railway:platform' => array('label' => 'Platform', 'frequency' => 134, 'icon' => ''),
|
|
||||||
'railway:tram' => array('label' => 'Tram', 'frequency' => 130, 'icon' => 'transport_tram_stop'),
|
|
||||||
'amenity:courthouse' => array('label' => 'Courthouse', 'frequency' => 129, 'icon' => 'amenity_court'),
|
|
||||||
'amenity:recycling' => array('label' => 'Recycling', 'frequency' => 126, 'icon' => 'amenity_recycling'),
|
|
||||||
'amenity:dentist' => array('label' => 'Dentist', 'frequency' => 124, 'icon' => 'health_dentist'),
|
|
||||||
'natural:beach' => array('label' => 'Beach', 'frequency' => 121, 'icon' => 'tourist_beach'),
|
|
||||||
'place:moor' => array('label' => 'Moor', 'frequency' => 118, 'icon' => ''),
|
|
||||||
'amenity:grave_yard' => array('label' => 'Grave Yard', 'frequency' => 110, 'icon' => ''),
|
|
||||||
'waterway:drain' => array('label' => 'Drain', 'frequency' => 108, 'icon' => ''),
|
|
||||||
'landuse:grass' => array('label' => 'Grass', 'frequency' => 106, 'icon' => ''),
|
|
||||||
'landuse:village_green' => array('label' => 'Village Green', 'frequency' => 106, 'icon' => ''),
|
|
||||||
'natural:bay' => array('label' => 'Bay', 'frequency' => 102, 'icon' => ''),
|
|
||||||
'railway:tram_stop' => array('label' => 'Tram Stop', 'frequency' => 101, 'icon' => 'transport_tram_stop'),
|
|
||||||
'leisure:marina' => array('label' => 'Marina', 'frequency' => 98, 'icon' => ''),
|
|
||||||
'highway:stile' => array('label' => 'Stile', 'frequency' => 97, 'icon' => ''),
|
|
||||||
'natural:moor' => array('label' => 'Moor', 'frequency' => 95, 'icon' => ''),
|
|
||||||
'railway:light_rail' => array('label' => 'Light Rail', 'frequency' => 91, 'icon' => ''),
|
|
||||||
'railway:narrow_gauge' => array('label' => 'Narrow Gauge', 'frequency' => 90, 'icon' => ''),
|
|
||||||
'natural:land' => array('label' => 'Land', 'frequency' => 86, 'icon' => ''),
|
|
||||||
'amenity:village_hall' => array('label' => 'Village Hall', 'frequency' => 82, 'icon' => ''),
|
|
||||||
'waterway:dock' => array('label' => 'Dock', 'frequency' => 80, 'icon' => ''),
|
|
||||||
'amenity:veterinary' => array('label' => 'Veterinary', 'frequency' => 79, 'icon' => ''),
|
|
||||||
'landuse:brownfield' => array('label' => 'Brownfield', 'frequency' => 77, 'icon' => ''),
|
|
||||||
'leisure:track' => array('label' => 'Track', 'frequency' => 76, 'icon' => ''),
|
|
||||||
'railway:historic_station' => array('label' => 'Historic Station', 'frequency' => 74, 'icon' => ''),
|
|
||||||
'landuse:construction' => array('label' => 'Construction', 'frequency' => 72, 'icon' => ''),
|
|
||||||
'amenity:prison' => array('label' => 'Prison', 'frequency' => 71, 'icon' => 'amenity_prison'),
|
|
||||||
'landuse:quarry' => array('label' => 'Quarry', 'frequency' => 71, 'icon' => ''),
|
|
||||||
'amenity:telephone' => array('label' => 'Telephone', 'frequency' => 70, 'icon' => ''),
|
|
||||||
'highway:traffic_signals' => array('label' => 'Traffic Signals', 'frequency' => 66, 'icon' => ''),
|
|
||||||
'natural:heath' => array('label' => 'Heath', 'frequency' => 62, 'icon' => ''),
|
|
||||||
'historic:house' => array('label' => 'House', 'frequency' => 61, 'icon' => ''),
|
|
||||||
'amenity:social_club' => array('label' => 'Social Club', 'frequency' => 61, 'icon' => ''),
|
|
||||||
'landuse:military' => array('label' => 'Military', 'frequency' => 61, 'icon' => ''),
|
|
||||||
'amenity:health_centre' => array('label' => 'Health Centre', 'frequency' => 59, 'icon' => ''),
|
|
||||||
'historic:building' => array('label' => 'Building', 'frequency' => 58, 'icon' => ''),
|
|
||||||
'amenity:clinic' => array('label' => 'Clinic', 'frequency' => 57, 'icon' => ''),
|
|
||||||
'highway:services' => array('label' => 'Services', 'frequency' => 56, 'icon' => ''),
|
|
||||||
'amenity:ferry_terminal' => array('label' => 'Ferry Terminal', 'frequency' => 55, 'icon' => ''),
|
|
||||||
'natural:marsh' => array('label' => 'Marsh', 'frequency' => 55, 'icon' => ''),
|
|
||||||
'natural:hill' => array('label' => 'Hill', 'frequency' => 54, 'icon' => ''),
|
|
||||||
'highway:raceway' => array('label' => 'Raceway', 'frequency' => 53, 'icon' => ''),
|
|
||||||
'amenity:taxi' => array('label' => 'Taxi', 'frequency' => 47, 'icon' => ''),
|
|
||||||
'amenity:take_away' => array('label' => 'Take Away', 'frequency' => 45, 'icon' => ''),
|
|
||||||
'amenity:car_rental' => array('label' => 'Car Rental', 'frequency' => 44, 'icon' => ''),
|
|
||||||
'place:islet' => array('label' => 'Islet', 'frequency' => 44, 'icon' => ''),
|
|
||||||
'amenity:nursery' => array('label' => 'Nursery', 'frequency' => 44, 'icon' => ''),
|
|
||||||
'amenity:nursing_home' => array('label' => 'Nursing Home', 'frequency' => 43, 'icon' => ''),
|
|
||||||
'amenity:toilets' => array('label' => 'Toilets', 'frequency' => 38, 'icon' => ''),
|
|
||||||
'amenity:hall' => array('label' => 'Hall', 'frequency' => 38, 'icon' => ''),
|
|
||||||
'waterway:boatyard' => array('label' => 'Boatyard', 'frequency' => 36, 'icon' => ''),
|
|
||||||
'highway:mini_roundabout' => array('label' => 'Mini Roundabout', 'frequency' => 35, 'icon' => ''),
|
|
||||||
'historic:manor' => array('label' => 'Manor', 'frequency' => 35, 'icon' => ''),
|
|
||||||
'tourism:chalet' => array('label' => 'Chalet', 'frequency' => 34, 'icon' => ''),
|
|
||||||
'amenity:bicycle_parking' => array('label' => 'Bicycle Parking', 'frequency' => 34, 'icon' => ''),
|
|
||||||
'amenity:hotel' => array('label' => 'Hotel', 'frequency' => 34, 'icon' => ''),
|
|
||||||
'waterway:weir' => array('label' => 'Weir', 'frequency' => 33, 'icon' => ''),
|
|
||||||
'natural:wetland' => array('label' => 'Wetland', 'frequency' => 33, 'icon' => ''),
|
|
||||||
'natural:cave_entrance' => array('label' => 'Cave Entrance', 'frequency' => 32, 'icon' => ''),
|
|
||||||
'amenity:crematorium' => array('label' => 'Crematorium', 'frequency' => 31, 'icon' => ''),
|
|
||||||
'tourism:picnic_site' => array('label' => 'Picnic Site', 'frequency' => 31, 'icon' => ''),
|
|
||||||
'landuse:wood' => array('label' => 'Wood', 'frequency' => 30, 'icon' => ''),
|
|
||||||
'landuse:basin' => array('label' => 'Basin', 'frequency' => 30, 'icon' => ''),
|
|
||||||
'natural:tree' => array('label' => 'Tree', 'frequency' => 30, 'icon' => ''),
|
|
||||||
'leisure:slipway' => array('label' => 'Slipway', 'frequency' => 29, 'icon' => ''),
|
|
||||||
'landuse:meadow' => array('label' => 'Meadow', 'frequency' => 29, 'icon' => ''),
|
|
||||||
'landuse:piste' => array('label' => 'Piste', 'frequency' => 28, 'icon' => ''),
|
|
||||||
'amenity:care_home' => array('label' => 'Care Home', 'frequency' => 28, 'icon' => ''),
|
|
||||||
'amenity:club' => array('label' => 'Club', 'frequency' => 28, 'icon' => ''),
|
|
||||||
'amenity:medical_centre' => array('label' => 'Medical Centre', 'frequency' => 27, 'icon' => ''),
|
|
||||||
'historic:roman_road' => array('label' => 'Roman Road', 'frequency' => 27, 'icon' => ''),
|
|
||||||
'historic:fort' => array('label' => 'Fort', 'frequency' => 26, 'icon' => ''),
|
|
||||||
'railway:subway_entrance' => array('label' => 'Subway Entrance', 'frequency' => 26, 'icon' => ''),
|
|
||||||
'historic:yes' => array('label' => 'Historic', 'frequency' => 25, 'icon' => ''),
|
|
||||||
'highway:gate' => array('label' => 'Gate', 'frequency' => 25, 'icon' => ''),
|
|
||||||
'leisure:fishing' => array('label' => 'Fishing', 'frequency' => 24, 'icon' => ''),
|
|
||||||
'historic:museum' => array('label' => 'Museum', 'frequency' => 24, 'icon' => ''),
|
|
||||||
'amenity:car_wash' => array('label' => 'Car Wash', 'frequency' => 24, 'icon' => ''),
|
|
||||||
'railway:level_crossing' => array('label' => 'Level Crossing', 'frequency' => 23, 'icon' => ''),
|
|
||||||
'leisure:bird_hide' => array('label' => 'Bird Hide', 'frequency' => 23, 'icon' => ''),
|
|
||||||
'natural:headland' => array('label' => 'Headland', 'frequency' => 21, 'icon' => ''),
|
|
||||||
'tourism:apartments' => array('label' => 'Apartments', 'frequency' => 21, 'icon' => ''),
|
|
||||||
'amenity:shopping' => array('label' => 'Shopping', 'frequency' => 21, 'icon' => ''),
|
|
||||||
'natural:scrub' => array('label' => 'Scrub', 'frequency' => 20, 'icon' => ''),
|
|
||||||
'natural:fen' => array('label' => 'Fen', 'frequency' => 20, 'icon' => ''),
|
|
||||||
'building:yes' => array('label' => 'Building', 'frequency' => 200, 'icon' => ''),
|
|
||||||
'mountain_pass:yes' => array('label' => 'Mountain Pass', 'frequency' => 200, 'icon' => ''),
|
|
||||||
|
|
||||||
'amenity:parking' => array('label' => 'Parking', 'frequency' => 3157, 'icon' => ''),
|
|
||||||
'highway:bus_stop' => array('label' => 'Bus Stop', 'frequency' => 35777, 'icon' => 'transport_bus_stop2'),
|
|
||||||
'place:postcode' => array('label' => 'Postcode', 'frequency' => 27267, 'icon' => ''),
|
|
||||||
'amenity:post_box' => array('label' => 'Post Box', 'frequency' => 9613, 'icon' => ''),
|
|
||||||
|
|
||||||
'place:houses' => array('label' => 'Houses', 'frequency' => 85, 'icon' => ''),
|
|
||||||
'railway:preserved' => array('label' => 'Preserved', 'frequency' => 227, 'icon' => ''),
|
|
||||||
'waterway:derelict_canal' => array('label' => 'Derelict Canal', 'frequency' => 21, 'icon' => ''),
|
|
||||||
'amenity:dead_pub' => array('label' => 'Dead Pub', 'frequency' => 20, 'icon' => ''),
|
|
||||||
'railway:disused_station' => array('label' => 'Disused Station', 'frequency' => 114, 'icon' => ''),
|
|
||||||
'railway:abandoned' => array('label' => 'Abandoned', 'frequency' => 641, 'icon' => ''),
|
|
||||||
'railway:disused' => array('label' => 'Disused', 'frequency' => 72, 'icon' => ''),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getClassTypesWithImportance()
|
|
||||||
{
|
|
||||||
$aOrders = getClassTypes();
|
|
||||||
$i = 1;
|
|
||||||
foreach ($aOrders as $sID => $a) {
|
|
||||||
$aOrders[$sID]['importance'] = $i++;
|
|
||||||
}
|
|
||||||
return $aOrders;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getResultDiameter($aResult)
|
|
||||||
{
|
|
||||||
$aClassType = getClassTypes();
|
|
||||||
|
|
||||||
$fDiameter = 0.0001;
|
|
||||||
|
|
||||||
if (isset($aResult['class'])
|
|
||||||
&& isset($aResult['type'])
|
|
||||||
&& isset($aResult['admin_level'])
|
|
||||||
&& isset($aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter'])
|
|
||||||
&& $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter']
|
|
||||||
) {
|
|
||||||
$fDiameter = $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter'];
|
|
||||||
} elseif (isset($aResult['class'])
|
|
||||||
&& isset($aResult['type'])
|
|
||||||
&& isset($aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter'])
|
|
||||||
&& $aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter']
|
|
||||||
) {
|
|
||||||
$fDiameter = $aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $fDiameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function javascript_renderData($xVal, $iOptions = 0)
|
function javascript_renderData($xVal, $iOptions = 0)
|
||||||
{
|
{
|
||||||
$iOptions |= JSON_UNESCAPED_UNICODE;
|
$iOptions |= JSON_UNESCAPED_UNICODE;
|
||||||
@@ -425,73 +81,6 @@ function javascript_renderData($xVal, $iOptions = 0)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function _debugDumpGroupedSearches($aData, $aTokens)
|
|
||||||
{
|
|
||||||
$aWordsIDs = array();
|
|
||||||
if ($aTokens) {
|
|
||||||
foreach ($aTokens as $sToken => $aWords) {
|
|
||||||
if ($aWords) {
|
|
||||||
foreach ($aWords as $aToken) {
|
|
||||||
$aWordsIDs[$aToken['word_id']] =
|
|
||||||
'#'.$sToken.'('.$aToken['word_id'].')#';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo '<table border="1">';
|
|
||||||
echo '<tr><th>rank</th><th>Name Tokens</th><th>Name Not</th>';
|
|
||||||
echo '<th>Address Tokens</th><th>Address Not</th><th>country</th><th>operator</th>';
|
|
||||||
echo '<th>class</th><th>type</th><th>postcode</th><th>housenumber</th></tr>';
|
|
||||||
foreach ($aData as $iRank => $aRankedSet) {
|
|
||||||
foreach ($aRankedSet as $aRow) {
|
|
||||||
$aRow->dumpAsHtmlTableRow($aWordsIDs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo '</table>';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getAddressDetails(&$oDB, $sLanguagePrefArraySQL, $iPlaceID, $sCountryCode = false, $housenumber = -1, $bRaw = false)
|
|
||||||
{
|
|
||||||
$sSQL = "select *,get_name_by_language(name,$sLanguagePrefArraySQL) as localname from get_addressdata($iPlaceID, $housenumber)";
|
|
||||||
if (!$bRaw) $sSQL .= " WHERE isaddress OR type = 'country_code'";
|
|
||||||
$sSQL .= ' order by rank_address desc,isaddress desc';
|
|
||||||
|
|
||||||
$aAddressLines = chksql($oDB->getAll($sSQL));
|
|
||||||
if ($bRaw) return $aAddressLines;
|
|
||||||
//echo "<pre>";
|
|
||||||
//var_dump($aAddressLines);
|
|
||||||
$aAddress = array();
|
|
||||||
$aFallback = array();
|
|
||||||
$aClassType = getClassTypes();
|
|
||||||
foreach ($aAddressLines as $aLine) {
|
|
||||||
$bFallback = false;
|
|
||||||
$aTypeLabel = false;
|
|
||||||
if (isset($aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']])) {
|
|
||||||
$aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']];
|
|
||||||
} elseif (isset($aClassType[$aLine['class'].':'.$aLine['type']])) {
|
|
||||||
$aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type']];
|
|
||||||
} elseif (isset($aClassType['boundary:administrative:'.((int)($aLine['rank_address']/2))])) {
|
|
||||||
$aTypeLabel = $aClassType['boundary:administrative:'.((int)($aLine['rank_address']/2))];
|
|
||||||
$bFallback = true;
|
|
||||||
} else {
|
|
||||||
$aTypeLabel = array('simplelabel' => 'address'.$aLine['rank_address']);
|
|
||||||
$bFallback = true;
|
|
||||||
}
|
|
||||||
if ($aTypeLabel && ((isset($aLine['localname']) && $aLine['localname']) || (isset($aLine['housenumber']) && $aLine['housenumber']))) {
|
|
||||||
$sTypeLabel = strtolower(isset($aTypeLabel['simplelabel'])?$aTypeLabel['simplelabel']:$aTypeLabel['label']);
|
|
||||||
$sTypeLabel = str_replace(' ', '_', $sTypeLabel);
|
|
||||||
if (!isset($aAddress[$sTypeLabel]) || (isset($aFallback[$sTypeLabel]) && $aFallback[$sTypeLabel]) || $aLine['class'] == 'place') {
|
|
||||||
$aAddress[$sTypeLabel] = $aLine['localname']?$aLine['localname']:$aLine['housenumber'];
|
|
||||||
}
|
|
||||||
$aFallback[$sTypeLabel] = $bFallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function addQuotes($s)
|
function addQuotes($s)
|
||||||
{
|
{
|
||||||
return "'".$s."'";
|
return "'".$s."'";
|
||||||
@@ -530,11 +119,12 @@ function parseLatLon($sQuery)
|
|||||||
$sFound = $aData[0];
|
$sFound = $aData[0];
|
||||||
$fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60 + $aData[4]/3600);
|
$fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60 + $aData[4]/3600);
|
||||||
$fQueryLon = ($aData[5]=='E'?1:-1) * ($aData[6] + $aData[7]/60 + $aData[8]/3600);
|
$fQueryLon = ($aData[5]=='E'?1:-1) * ($aData[6] + $aData[7]/60 + $aData[8]/3600);
|
||||||
} elseif (preg_match('/\\s*([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″" ]+([NS])[, ]+([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″" ]+([EW])\\s*/', $sQuery, $aData)) {
|
} elseif (preg_match('/\\s*([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+[0-9.]*)[″" ]+([NS])[, ]+([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+[0-9.]*)[″" ]+([EW])\\s*/', $sQuery, $aData)) {
|
||||||
/* 1 2 3 4 5 6 7 8
|
/* 1 2 3 4 5 6 7 8
|
||||||
* degrees decimal seconds
|
* degrees decimal seconds
|
||||||
* 40 26 46 N 79 58 56 W
|
* 40 26 46 N 79 58 56 W
|
||||||
* 40° 26′ 46″ N, 79° 58′ 56″ W
|
* 40° 26′ 46″ N, 79° 58′ 56″ W
|
||||||
|
* 40° 26′ 46.78″ N, 79° 58′ 56.89″ W
|
||||||
*/
|
*/
|
||||||
$sFound = $aData[0];
|
$sFound = $aData[0];
|
||||||
$fQueryLat = ($aData[4]=='N'?1:-1) * ($aData[1] + $aData[2]/60 + $aData[3]/3600);
|
$fQueryLat = ($aData[4]=='N'?1:-1) * ($aData[1] + $aData[2]/60 + $aData[3]/3600);
|
||||||
|
|||||||
@@ -39,3 +39,14 @@ function detailsLink($aFeature, $sTitle = false)
|
|||||||
|
|
||||||
return '<a href="details.php?place_id='.$aFeature['place_id'].'">'.($sTitle?$sTitle:$aFeature['place_id']).'</a>';
|
return '<a href="details.php?place_id='.$aFeature['place_id'].'">'.($sTitle?$sTitle:$aFeature['place_id']).'</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function detailsPermaLink($aFeature, $sRefText = false)
|
||||||
|
{
|
||||||
|
$sOSMType = formatOSMType($aFeature['osm_type'], false);
|
||||||
|
|
||||||
|
if ($sOSMType) {
|
||||||
|
$sLabel = $sRefText ? $sRefText : $sOSMType.' '.$aFeature['osm_id'];
|
||||||
|
return '<a href="details.php?osmtype='.$aFeature['osm_type'].'&osmid='.$aFeature['osm_id'].'&class='.$aFeature['class'].'">'.$sLabel.'</a>';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|||||||
81
lib/template/address-geocodejson.php
Normal file
81
lib/template/address-geocodejson.php
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// https://github.com/geocoders/geocodejson-spec/
|
||||||
|
|
||||||
|
$aFilteredPlaces = array();
|
||||||
|
|
||||||
|
if (empty($aPlace)) {
|
||||||
|
if (isset($sError))
|
||||||
|
$aFilteredPlaces['error'] = $sError;
|
||||||
|
else $aFilteredPlaces['error'] = 'Unable to geocode';
|
||||||
|
javascript_renderData($aFilteredPlaces);
|
||||||
|
} else {
|
||||||
|
$aFilteredPlaces = array(
|
||||||
|
'type' => 'Feature',
|
||||||
|
'properties' => array(
|
||||||
|
'geocoding' => array()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isset($aPlace['place_id'])) $aFilteredPlaces['properties']['geocoding']['place_id'] = $aPlace['place_id'];
|
||||||
|
$sOSMType = formatOSMType($aPlace['osm_type']);
|
||||||
|
if ($sOSMType) {
|
||||||
|
$aFilteredPlaces['properties']['geocoding']['osm_type'] = $sOSMType;
|
||||||
|
$aFilteredPlaces['properties']['geocoding']['osm_id'] = $aPlace['osm_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['geocoding']['type'] = $aPlace['type'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['geocoding']['accuracy'] = (int) $fDistance;
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['geocoding']['label'] = $aPlace['langaddress'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['geocoding']['name'] = $aPlace['placename'];
|
||||||
|
|
||||||
|
if (isset($aPlace['address'])) {
|
||||||
|
$aFieldMappings = array(
|
||||||
|
'house_number' => 'housenumber',
|
||||||
|
'road' => 'street',
|
||||||
|
'locality' => 'locality',
|
||||||
|
'postcode' => 'postcode',
|
||||||
|
'city' => 'city',
|
||||||
|
'district' => 'district',
|
||||||
|
'county' => 'county',
|
||||||
|
'state' => 'state',
|
||||||
|
'country' => 'country'
|
||||||
|
);
|
||||||
|
|
||||||
|
$aAddressNames = $aPlace['address']->getAddressNames();
|
||||||
|
foreach ($aFieldMappings as $sFrom => $sTo) {
|
||||||
|
if (isset($aAddressNames[$sFrom])) {
|
||||||
|
$aFilteredPlaces['properties']['geocoding'][$sTo] = $aAddressNames[$sFrom];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['geocoding']['admin']
|
||||||
|
= $aPlace['address']->getAdminLevels();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPlace['asgeojson'])) {
|
||||||
|
$aFilteredPlaces['geometry'] = json_decode($aPlace['asgeojson']);
|
||||||
|
} else {
|
||||||
|
$aFilteredPlaces['geometry'] = array(
|
||||||
|
'type' => 'Point',
|
||||||
|
'coordinates' => array(
|
||||||
|
(float) $aPlace['lon'],
|
||||||
|
(float) $aPlace['lat']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
javascript_renderData(array(
|
||||||
|
'type' => 'FeatureCollection',
|
||||||
|
'geocoding' => array(
|
||||||
|
'version' => '0.1.0',
|
||||||
|
'attribution' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
||||||
|
'licence' => 'ODbL',
|
||||||
|
'query' => $sQuery
|
||||||
|
),
|
||||||
|
'features' => array($aFilteredPlaces)
|
||||||
|
));
|
||||||
|
}
|
||||||
69
lib/template/address-geojson.php
Normal file
69
lib/template/address-geojson.php
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$aFilteredPlaces = array();
|
||||||
|
|
||||||
|
if (empty($aPlace)) {
|
||||||
|
if (isset($sError))
|
||||||
|
$aFilteredPlaces['error'] = $sError;
|
||||||
|
else $aFilteredPlaces['error'] = 'Unable to geocode';
|
||||||
|
javascript_renderData($aFilteredPlaces);
|
||||||
|
} else {
|
||||||
|
$aFilteredPlaces = array(
|
||||||
|
'type' => 'Feature',
|
||||||
|
'properties' => array()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isset($aPlace['place_id'])) $aFilteredPlaces['properties']['place_id'] = $aPlace['place_id'];
|
||||||
|
$sOSMType = formatOSMType($aPlace['osm_type']);
|
||||||
|
if ($sOSMType) {
|
||||||
|
$aFilteredPlaces['properties']['osm_type'] = $sOSMType;
|
||||||
|
$aFilteredPlaces['properties']['osm_id'] = $aPlace['osm_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['place_rank'] = $aPlace['rank_search'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['category'] = $aPlace['class'];
|
||||||
|
$aFilteredPlaces['properties']['type'] = $aPlace['type'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['importance'] = $aPlace['importance'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['addresstype'] = strtolower($aPlace['addresstype']);
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['name'] = $aPlace['placename'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['properties']['display_name'] = $aPlace['langaddress'];
|
||||||
|
|
||||||
|
if (isset($aPlace['address'])) {
|
||||||
|
$aFilteredPlaces['properties']['address'] = $aPlace['address']->getAddressNames();
|
||||||
|
}
|
||||||
|
if (isset($aPlace['sExtraTags'])) $aFilteredPlaces['properties']['extratags'] = $aPlace['sExtraTags'];
|
||||||
|
if (isset($aPlace['sNameDetails'])) $aFilteredPlaces['properties']['namedetails'] = $aPlace['sNameDetails'];
|
||||||
|
|
||||||
|
if (isset($aPlace['aBoundingBox'])) {
|
||||||
|
$aFilteredPlaces['bbox'] = array(
|
||||||
|
(float) $aPlace['aBoundingBox'][2], // minlon
|
||||||
|
(float) $aPlace['aBoundingBox'][0], // minlat
|
||||||
|
(float) $aPlace['aBoundingBox'][3], // maxlon
|
||||||
|
(float) $aPlace['aBoundingBox'][1] // maxlat
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPlace['asgeojson'])) {
|
||||||
|
$aFilteredPlaces['geometry'] = json_decode($aPlace['asgeojson']);
|
||||||
|
} else {
|
||||||
|
$aFilteredPlaces['geometry'] = array(
|
||||||
|
'type' => 'Point',
|
||||||
|
'coordinates' => array(
|
||||||
|
(float) $aPlace['lon'],
|
||||||
|
(float) $aPlace['lat']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
javascript_renderData(array(
|
||||||
|
'type' => 'FeatureCollection',
|
||||||
|
'licence' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
||||||
|
'features' => array($aFilteredPlaces)
|
||||||
|
));
|
||||||
|
}
|
||||||
@@ -2,55 +2,61 @@
|
|||||||
|
|
||||||
$aFilteredPlaces = array();
|
$aFilteredPlaces = array();
|
||||||
|
|
||||||
if (!sizeof($aPlace))
|
if (empty($aPlace)) {
|
||||||
{
|
|
||||||
if (isset($sError))
|
if (isset($sError))
|
||||||
$aFilteredPlaces['error'] = $sError;
|
$aFilteredPlaces['error'] = $sError;
|
||||||
else
|
else $aFilteredPlaces['error'] = 'Unable to geocode';
|
||||||
$aFilteredPlaces['error'] = 'Unable to geocode';
|
} else {
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (isset($aPlace['place_id'])) $aFilteredPlaces['place_id'] = $aPlace['place_id'];
|
if (isset($aPlace['place_id'])) $aFilteredPlaces['place_id'] = $aPlace['place_id'];
|
||||||
$aFilteredPlaces['licence'] = "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright";
|
$aFilteredPlaces['licence'] = 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright';
|
||||||
$sOSMType = formatOSMType($aPlace['osm_type']);
|
$sOSMType = formatOSMType($aPlace['osm_type']);
|
||||||
if ($sOSMType)
|
if ($sOSMType) {
|
||||||
{
|
|
||||||
$aFilteredPlaces['osm_type'] = $sOSMType;
|
$aFilteredPlaces['osm_type'] = $sOSMType;
|
||||||
$aFilteredPlaces['osm_id'] = $aPlace['osm_id'];
|
$aFilteredPlaces['osm_id'] = $aPlace['osm_id'];
|
||||||
}
|
}
|
||||||
if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat'];
|
if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat'];
|
||||||
if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon'];
|
if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon'];
|
||||||
|
|
||||||
|
if ($sOutputFormat == 'jsonv2' || $sOutputFormat == 'geojson') {
|
||||||
|
$aFilteredPlaces['place_rank'] = $aPlace['rank_search'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['category'] = $aPlace['class'];
|
||||||
|
$aFilteredPlaces['type'] = $aPlace['type'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['importance'] = $aPlace['importance'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['addresstype'] = strtolower($aPlace['addresstype']);
|
||||||
|
|
||||||
|
$aFilteredPlaces['name'] = $aPlace['placename'];
|
||||||
|
}
|
||||||
|
|
||||||
$aFilteredPlaces['display_name'] = $aPlace['langaddress'];
|
$aFilteredPlaces['display_name'] = $aPlace['langaddress'];
|
||||||
if (isset($aPlace['aAddress'])) $aFilteredPlaces['address'] = $aPlace['aAddress'];
|
|
||||||
|
if (isset($aPlace['address'])) {
|
||||||
|
$aFilteredPlaces['address'] = $aPlace['address']->getAddressNames();
|
||||||
|
}
|
||||||
if (isset($aPlace['sExtraTags'])) $aFilteredPlaces['extratags'] = $aPlace['sExtraTags'];
|
if (isset($aPlace['sExtraTags'])) $aFilteredPlaces['extratags'] = $aPlace['sExtraTags'];
|
||||||
if (isset($aPlace['sNameDetails'])) $aFilteredPlaces['namedetails'] = $aPlace['sNameDetails'];
|
if (isset($aPlace['sNameDetails'])) $aFilteredPlaces['namedetails'] = $aPlace['sNameDetails'];
|
||||||
|
|
||||||
if (isset($aPlace['aBoundingBox']))
|
if (isset($aPlace['aBoundingBox'])) {
|
||||||
{
|
|
||||||
$aFilteredPlaces['boundingbox'] = $aPlace['aBoundingBox'];
|
$aFilteredPlaces['boundingbox'] = $aPlace['aBoundingBox'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['asgeojson']))
|
if (isset($aPlace['asgeojson'])) {
|
||||||
{
|
|
||||||
$aFilteredPlaces['geojson'] = json_decode($aPlace['asgeojson']);
|
$aFilteredPlaces['geojson'] = json_decode($aPlace['asgeojson']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['assvg']))
|
if (isset($aPlace['assvg'])) {
|
||||||
{
|
|
||||||
$aFilteredPlaces['svg'] = $aPlace['assvg'];
|
$aFilteredPlaces['svg'] = $aPlace['assvg'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['astext']))
|
if (isset($aPlace['astext'])) {
|
||||||
{
|
|
||||||
$aFilteredPlaces['geotext'] = $aPlace['astext'];
|
$aFilteredPlaces['geotext'] = $aPlace['astext'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['askml']))
|
if (isset($aPlace['askml'])) {
|
||||||
{
|
|
||||||
$aFilteredPlaces['geokml'] = $aPlace['askml'];
|
$aFilteredPlaces['geokml'] = $aPlace['askml'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
javascript_renderData($aFilteredPlaces);
|
javascript_renderData($aFilteredPlaces);
|
||||||
|
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$aFilteredPlaces = array();
|
|
||||||
|
|
||||||
if (!sizeof($aPlace))
|
|
||||||
{
|
|
||||||
if (isset($sError))
|
|
||||||
$aFilteredPlaces['error'] = $sError;
|
|
||||||
else
|
|
||||||
$aFilteredPlaces['error'] = 'Unable to geocode';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ($aPlace['place_id']) $aFilteredPlaces['place_id'] = $aPlace['place_id'];
|
|
||||||
$aFilteredPlaces['licence'] = "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright";
|
|
||||||
$sOSMType = formatOSMType($aPlace['osm_type']);
|
|
||||||
if ($sOSMType)
|
|
||||||
{
|
|
||||||
$aFilteredPlaces['osm_type'] = $sOSMType;
|
|
||||||
$aFilteredPlaces['osm_id'] = $aPlace['osm_id'];
|
|
||||||
}
|
|
||||||
if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat'];
|
|
||||||
if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['place_rank'] = $aPlace['rank_search'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['category'] = $aPlace['class'];
|
|
||||||
$aFilteredPlaces['type'] = $aPlace['type'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['importance'] = $aPlace['importance'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['addresstype'] = strtolower($aPlace['addresstype']);
|
|
||||||
|
|
||||||
$aFilteredPlaces['display_name'] = $aPlace['langaddress'];
|
|
||||||
$aFilteredPlaces['name'] = $aPlace['placename'];
|
|
||||||
|
|
||||||
if (isset($aPlace['aAddress'])) $aFilteredPlaces['address'] = $aPlace['aAddress'];
|
|
||||||
if (isset($aPlace['sExtraTags'])) $aFilteredPlaces['extratags'] = $aPlace['sExtraTags'];
|
|
||||||
if (isset($aPlace['sNameDetails'])) $aFilteredPlaces['namedetails'] = $aPlace['sNameDetails'];
|
|
||||||
|
|
||||||
if (isset($aPlace['aBoundingBox']))
|
|
||||||
{
|
|
||||||
$aFilteredPlaces['boundingbox'] = $aPlace['aBoundingBox'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['asgeojson']))
|
|
||||||
{
|
|
||||||
$aFilteredPlaces['geojson'] = json_decode($aPlace['asgeojson']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['assvg']))
|
|
||||||
{
|
|
||||||
$aFilteredPlaces['svg'] = $aPlace['assvg'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['astext']))
|
|
||||||
{
|
|
||||||
$aFilteredPlaces['geotext'] = $aPlace['astext'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['askml']))
|
|
||||||
{
|
|
||||||
$aFilteredPlaces['geokml'] = $aPlace['askml'];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
javascript_renderData($aFilteredPlaces);
|
|
||||||
@@ -1,103 +1,87 @@
|
|||||||
<?php
|
<?php
|
||||||
header("content-type: text/xml; charset=UTF-8");
|
header('content-type: text/xml; charset=UTF-8');
|
||||||
|
|
||||||
echo "<";
|
echo '<';
|
||||||
echo "?xml version=\"1.0\" encoding=\"UTF-8\" ?";
|
echo '?xml version="1.0" encoding="UTF-8" ?';
|
||||||
echo ">\n";
|
echo ">\n";
|
||||||
|
|
||||||
echo "<reversegeocode";
|
echo '<reversegeocode';
|
||||||
echo " timestamp='".date(DATE_RFC822)."'";
|
echo " timestamp='".date(DATE_RFC822)."'";
|
||||||
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
|
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
|
||||||
echo " querystring='".htmlspecialchars($_SERVER['QUERY_STRING'], ENT_QUOTES)."'";
|
echo " querystring='".htmlspecialchars($_SERVER['QUERY_STRING'], ENT_QUOTES)."'";
|
||||||
echo ">\n";
|
echo ">\n";
|
||||||
|
|
||||||
if (!sizeof($aPlace))
|
if (empty($aPlace)) {
|
||||||
{
|
|
||||||
if (isset($sError))
|
if (isset($sError))
|
||||||
echo "<error>$sError</error>";
|
echo "<error>$sError</error>";
|
||||||
else
|
else echo '<error>Unable to geocode</error>';
|
||||||
echo "<error>Unable to geocode</error>";
|
} else {
|
||||||
}
|
echo '<result';
|
||||||
else
|
|
||||||
{
|
|
||||||
echo "<result";
|
|
||||||
if ($aPlace['place_id']) echo ' place_id="'.$aPlace['place_id'].'"';
|
if ($aPlace['place_id']) echo ' place_id="'.$aPlace['place_id'].'"';
|
||||||
$sOSMType = formatOSMType($aPlace['osm_type']);
|
$sOSMType = formatOSMType($aPlace['osm_type']);
|
||||||
if ($sOSMType) echo ' osm_type="'.$sOSMType.'"'.' osm_id="'.$aPlace['osm_id'].'"';
|
if ($sOSMType) echo ' osm_type="'.$sOSMType.'"'.' osm_id="'.$aPlace['osm_id'].'"';
|
||||||
if ($aPlace['ref']) echo ' ref="'.htmlspecialchars($aPlace['ref']).'"';
|
if ($aPlace['ref']) echo ' ref="'.htmlspecialchars($aPlace['ref']).'"';
|
||||||
if (isset($aPlace['lat'])) echo ' lat="'.htmlspecialchars($aPlace['lat']).'"';
|
if (isset($aPlace['lat'])) echo ' lat="'.htmlspecialchars($aPlace['lat']).'"';
|
||||||
if (isset($aPlace['lon'])) echo ' lon="'.htmlspecialchars($aPlace['lon']).'"';
|
if (isset($aPlace['lon'])) echo ' lon="'.htmlspecialchars($aPlace['lon']).'"';
|
||||||
if (isset($aPlace['aBoundingBox']))
|
if (isset($aPlace['aBoundingBox'])) {
|
||||||
{
|
|
||||||
echo ' boundingbox="';
|
echo ' boundingbox="';
|
||||||
echo join(',', $aPlace['aBoundingBox']);
|
echo join(',', $aPlace['aBoundingBox']);
|
||||||
echo '"';
|
echo '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['asgeojson']))
|
if (isset($aPlace['asgeojson'])) {
|
||||||
{
|
|
||||||
echo ' geojson=\'';
|
echo ' geojson=\'';
|
||||||
echo $aPlace['asgeojson'];
|
echo $aPlace['asgeojson'];
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['assvg']))
|
if (isset($aPlace['assvg'])) {
|
||||||
{
|
|
||||||
echo ' geosvg=\'';
|
echo ' geosvg=\'';
|
||||||
echo $aPlace['assvg'];
|
echo $aPlace['assvg'];
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['astext']))
|
if (isset($aPlace['astext'])) {
|
||||||
{
|
|
||||||
echo ' geotext=\'';
|
echo ' geotext=\'';
|
||||||
echo $aPlace['astext'];
|
echo $aPlace['astext'];
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
echo ">".htmlspecialchars($aPlace['langaddress'])."</result>";
|
echo '>'.htmlspecialchars($aPlace['langaddress']).'</result>';
|
||||||
|
|
||||||
if (isset($aPlace['aAddress']))
|
if (isset($aPlace['address'])) {
|
||||||
{
|
echo '<addressparts>';
|
||||||
echo "<addressparts>";
|
foreach ($aPlace['address']->getAddressNames() as $sKey => $sValue) {
|
||||||
foreach($aPlace['aAddress'] as $sKey => $sValue)
|
$sKey = str_replace(' ', '_', $sKey);
|
||||||
{
|
|
||||||
$sKey = str_replace(' ','_',$sKey);
|
|
||||||
echo "<$sKey>";
|
echo "<$sKey>";
|
||||||
echo htmlspecialchars($sValue);
|
echo htmlspecialchars($sValue);
|
||||||
echo "</$sKey>";
|
echo "</$sKey>";
|
||||||
}
|
}
|
||||||
echo "</addressparts>";
|
echo '</addressparts>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['sExtraTags']))
|
if (isset($aPlace['sExtraTags'])) {
|
||||||
{
|
echo '<extratags>';
|
||||||
echo "<extratags>";
|
foreach ($aPlace['sExtraTags'] as $sKey => $sValue) {
|
||||||
foreach ($aPlace['sExtraTags'] as $sKey => $sValue)
|
|
||||||
{
|
|
||||||
echo '<tag key="'.htmlspecialchars($sKey).'" value="'.htmlspecialchars($sValue).'"/>';
|
echo '<tag key="'.htmlspecialchars($sKey).'" value="'.htmlspecialchars($sValue).'"/>';
|
||||||
}
|
}
|
||||||
echo "</extratags>";
|
echo '</extratags>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['sNameDetails']))
|
if (isset($aPlace['sNameDetails'])) {
|
||||||
{
|
echo '<namedetails>';
|
||||||
echo "<namedetails>";
|
foreach ($aPlace['sNameDetails'] as $sKey => $sValue) {
|
||||||
foreach ($aPlace['sNameDetails'] as $sKey => $sValue)
|
|
||||||
{
|
|
||||||
echo '<name desc="'.htmlspecialchars($sKey).'">';
|
echo '<name desc="'.htmlspecialchars($sKey).'">';
|
||||||
echo htmlspecialchars($sValue);
|
echo htmlspecialchars($sValue);
|
||||||
echo "</name>";
|
echo '</name>';
|
||||||
}
|
}
|
||||||
echo "</namedetails>";
|
echo '</namedetails>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['askml']))
|
if (isset($aPlace['askml'])) {
|
||||||
{
|
|
||||||
echo "\n<geokml>";
|
echo "\n<geokml>";
|
||||||
echo $aPlace['askml'];
|
echo $aPlace['askml'];
|
||||||
echo "</geokml>";
|
echo '</geokml>';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "</reversegeocode>";
|
echo '</reversegeocode>';
|
||||||
|
|||||||
@@ -46,8 +46,7 @@
|
|||||||
function hash_to_subtable($aAssociatedList)
|
function hash_to_subtable($aAssociatedList)
|
||||||
{
|
{
|
||||||
$sHTML = '';
|
$sHTML = '';
|
||||||
foreach($aAssociatedList as $sKey => $sValue)
|
foreach ($aAssociatedList as $sKey => $sValue) {
|
||||||
{
|
|
||||||
$sHTML = $sHTML.' <div class="line"><span class="name">'.$sValue.'</span> ('.$sKey.')</div>'."\n";
|
$sHTML = $sHTML.' <div class="line"><span class="name">'.$sValue.'</span> ('.$sKey.')</div>'."\n";
|
||||||
}
|
}
|
||||||
return $sHTML;
|
return $sHTML;
|
||||||
@@ -97,7 +96,10 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<h1><?php echo $aPointDetails['localname'] ?></h1>
|
<h1>
|
||||||
|
<?php echo $aPointDetails['localname'] ?>
|
||||||
|
<small><?php echo detailsPermaLink($aPointDetails, 'link to this page') ?></small>
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-2 text-right">
|
<div class="col-sm-2 text-right">
|
||||||
<?php map_icon($aPointDetails['icon']) ?>
|
<?php map_icon($aPointDetails['icon']) ?>
|
||||||
@@ -111,7 +113,7 @@
|
|||||||
|
|
||||||
kv('Name' , hash_to_subtable($aPointDetails['aNames']) );
|
kv('Name' , hash_to_subtable($aPointDetails['aNames']) );
|
||||||
kv('Type' , $aPointDetails['class'].':'.$aPointDetails['type'] );
|
kv('Type' , $aPointDetails['class'].':'.$aPointDetails['type'] );
|
||||||
kv('Last Updated' , $aPointDetails['indexed_date'] );
|
kv('Last Updated' , (new DateTime('@'.$aPointDetails['indexed_epoch']))->format(DateTime::RFC822) );
|
||||||
kv('Admin Level' , $aPointDetails['admin_level'] );
|
kv('Admin Level' , $aPointDetails['admin_level'] );
|
||||||
kv('Rank' , $aPointDetails['rank_search_label'] );
|
kv('Rank' , $aPointDetails['rank_search_label'] );
|
||||||
if ($aPointDetails['calculated_importance']) {
|
if ($aPointDetails['calculated_importance']) {
|
||||||
@@ -159,13 +161,10 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
foreach ($aAddressLines as $aAddressLine) {
|
||||||
foreach($aAddressLines as $aAddressLine)
|
|
||||||
{
|
|
||||||
_one_row($aAddressLine);
|
_one_row($aAddressLine);
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
@@ -173,39 +172,34 @@
|
|||||||
if ($aLinkedLines)
|
if ($aLinkedLines)
|
||||||
{
|
{
|
||||||
headline('Linked Places');
|
headline('Linked Places');
|
||||||
foreach($aLinkedLines as $aAddressLine)
|
foreach ($aLinkedLines as $aAddressLine) {
|
||||||
{
|
|
||||||
_one_row($aAddressLine);
|
_one_row($aAddressLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($bIncludeKeywords)
|
||||||
|
|
||||||
if ($aPlaceSearchNameKeywords)
|
|
||||||
{
|
{
|
||||||
headline('Name Keywords');
|
headline('Name Keywords');
|
||||||
foreach($aPlaceSearchNameKeywords as $aRow)
|
if ($aPlaceSearchNameKeywords) {
|
||||||
{
|
foreach ($aPlaceSearchNameKeywords as $aRow) {
|
||||||
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
|
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headline('Address Keywords');
|
||||||
|
if ($aPlaceSearchAddressKeywords) {
|
||||||
|
foreach ($aPlaceSearchAddressKeywords as $aRow) {
|
||||||
|
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($aPlaceSearchAddressKeywords)
|
if (!empty($aHierarchyLines))
|
||||||
{
|
|
||||||
headline('Address Keywords');
|
|
||||||
foreach($aPlaceSearchAddressKeywords as $aRow)
|
|
||||||
{
|
|
||||||
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sizeof($aParentOfLines))
|
|
||||||
{
|
{
|
||||||
headline('Parent Of');
|
headline('Parent Of');
|
||||||
|
|
||||||
$aGroupedAddressLines = array();
|
$aGroupedAddressLines = array();
|
||||||
foreach($aParentOfLines as $aAddressLine)
|
foreach ($aHierarchyLines as $aAddressLine) {
|
||||||
{
|
|
||||||
if ($aAddressLine['type'] == 'yes') $sType = $aAddressLine['class'];
|
if ($aAddressLine['type'] == 'yes') $sType = $aAddressLine['class'];
|
||||||
else $sType = $aAddressLine['type'];
|
else $sType = $aAddressLine['type'];
|
||||||
|
|
||||||
@@ -213,17 +207,15 @@
|
|||||||
$aGroupedAddressLines[$sType] = array();
|
$aGroupedAddressLines[$sType] = array();
|
||||||
$aGroupedAddressLines[$sType][] = $aAddressLine;
|
$aGroupedAddressLines[$sType][] = $aAddressLine;
|
||||||
}
|
}
|
||||||
foreach($aGroupedAddressLines as $sGroupHeading => $aParentOfLines)
|
foreach ($aGroupedAddressLines as $sGroupHeading => $aHierarchyLines) {
|
||||||
{
|
|
||||||
$sGroupHeading = ucwords($sGroupHeading);
|
$sGroupHeading = ucwords($sGroupHeading);
|
||||||
headline3($sGroupHeading);
|
headline3($sGroupHeading);
|
||||||
|
|
||||||
foreach($aParentOfLines as $aAddressLine)
|
foreach ($aHierarchyLines as $aAddressLine) {
|
||||||
{
|
|
||||||
_one_row($aAddressLine);
|
_one_row($aAddressLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sizeof($aParentOfLines) >= 500) {
|
if (count($aHierarchyLines) >= 500) {
|
||||||
echo '<p>There are more child objects which are not shown.</p>';
|
echo '<p>There are more child objects which are not shown.</p>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,7 +241,7 @@
|
|||||||
'lon' => $aPointDetails['lon'],
|
'lon' => $aPointDetails['lon'],
|
||||||
'lat' => $aPointDetails['lat'],
|
'lat' => $aPointDetails['lat'],
|
||||||
);
|
);
|
||||||
echo 'var nominatim_result = ' . json_encode($aPlace, JSON_PRETTY_PRINT) . ';';
|
echo 'var nominatim_result = ' . json_encode($aPlace, JSON_PRETTY_PRINT) . ';';
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
104
lib/template/details-json.php
Normal file
104
lib/template/details-json.php
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$aPlaceDetails = array();
|
||||||
|
|
||||||
|
$aPlaceDetails['place_id'] = (int) $aPointDetails['place_id'];
|
||||||
|
$aPlaceDetails['parent_place_id'] = (int) $aPointDetails['parent_place_id'];
|
||||||
|
|
||||||
|
$aPlaceDetails['osm_type'] = $aPointDetails['osm_type'];
|
||||||
|
$aPlaceDetails['osm_id'] = (int) $aPointDetails['osm_id'];
|
||||||
|
|
||||||
|
$aPlaceDetails['category'] = $aPointDetails['class'];
|
||||||
|
$aPlaceDetails['type'] = $aPointDetails['type'];
|
||||||
|
$aPlaceDetails['admin_level'] = $aPointDetails['admin_level'];
|
||||||
|
|
||||||
|
$aPlaceDetails['localname'] = $aPointDetails['localname'];
|
||||||
|
$aPlaceDetails['names'] = $aPointDetails['aNames'];
|
||||||
|
|
||||||
|
$aPlaceDetails['addresstags'] = $aPointDetails['aAddressTags'];
|
||||||
|
$aPlaceDetails['housenumber'] = $aPointDetails['housenumber'];
|
||||||
|
$aPlaceDetails['calculated_postcode'] = $aPointDetails['postcode'];
|
||||||
|
$aPlaceDetails['country_code'] = $aPointDetails['country_code'];
|
||||||
|
|
||||||
|
$aPlaceDetails['indexed_date'] = (new DateTime('@'.$aPointDetails['indexed_epoch']))->format(DateTime::RFC3339);
|
||||||
|
$aPlaceDetails['importance'] = (float) $aPointDetails['importance'];
|
||||||
|
$aPlaceDetails['calculated_importance'] = (float) $aPointDetails['calculated_importance'];
|
||||||
|
|
||||||
|
$aPlaceDetails['extratags'] = $aPointDetails['aExtraTags'];
|
||||||
|
$aPlaceDetails['calculated_wikipedia'] = $aPointDetails['wikipedia'];
|
||||||
|
if ($aPointDetails['icon']) {
|
||||||
|
$aPlaceDetails['icon'] = CONST_Website_BaseURL.'images/mapicons/'.$aPointDetails['icon'].'.n.32.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
$aPlaceDetails['rank_address'] = (int) $aPointDetails['rank_address'];
|
||||||
|
$aPlaceDetails['rank_search'] = (int) $aPointDetails['rank_search'];
|
||||||
|
|
||||||
|
$aPlaceDetails['isarea'] = ($aPointDetails['isarea'] == 't');
|
||||||
|
$aPlaceDetails['centroid'] = array(
|
||||||
|
'type' => 'Point',
|
||||||
|
'coordinates' => array( (float) $aPointDetails['lon'], (float) $aPointDetails['lat'] )
|
||||||
|
);
|
||||||
|
|
||||||
|
$aPlaceDetails['geometry'] = json_decode($aPointDetails['asgeojson']);
|
||||||
|
|
||||||
|
$funcMapAddressLine = function ($aFull) {
|
||||||
|
$aMapped = array(
|
||||||
|
'localname' => $aFull['localname'],
|
||||||
|
'place_id' => isset($aFull['place_id']) ? (int) $aFull['place_id'] : null,
|
||||||
|
'osm_id' => isset($aFull['osm_id']) ? (int) $aFull['osm_id'] : null,
|
||||||
|
'osm_type' => isset($aFull['osm_type']) ? $aFull['osm_type'] : null,
|
||||||
|
'class' => $aFull['class'],
|
||||||
|
'type' => $aFull['type'],
|
||||||
|
'admin_level' => isset($aFull['admin_level']) ? (int) $aFull['admin_level'] : null,
|
||||||
|
'rank_address' => $aFull['rank_address'] ? (int) $aFull['rank_address'] : null,
|
||||||
|
'distance' => (float) $aFull['distance']
|
||||||
|
);
|
||||||
|
|
||||||
|
return $aMapped;
|
||||||
|
};
|
||||||
|
|
||||||
|
$funcMapKeyword = function ($aFull) {
|
||||||
|
$aMapped = array(
|
||||||
|
'id' => (int) $aFull['word_id'],
|
||||||
|
'token' => $aFull['word_token']
|
||||||
|
);
|
||||||
|
return $aMapped;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($aAddressLines) {
|
||||||
|
$aPlaceDetails['address'] = array_map($funcMapAddressLine, $aAddressLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aLinkedLines) {
|
||||||
|
$aPlaceDetails['linked_places'] = array_map($funcMapAddressLine, $aLinkedLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bIncludeKeywords) {
|
||||||
|
$aPlaceDetails['keywords'] = array();
|
||||||
|
|
||||||
|
if ($aPlaceSearchNameKeywords) {
|
||||||
|
$aPlaceDetails['keywords']['name'] = array_map($funcMapKeyword, $aPlaceSearchNameKeywords);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aPlaceSearchAddressKeywords) {
|
||||||
|
$aPlaceDetails['keywords']['address'] = array_map($funcMapKeyword, $aPlaceSearchAddressKeywords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bIncludeHierarchy) {
|
||||||
|
if ($bGroupHierarchy) {
|
||||||
|
$aPlaceDetails['hierarchy'] = array();
|
||||||
|
foreach ($aHierarchyLines as $aAddressLine) {
|
||||||
|
if ($aAddressLine['type'] == 'yes') $sType = $aAddressLine['class'];
|
||||||
|
else $sType = $aAddressLine['type'];
|
||||||
|
|
||||||
|
if (!isset($aPlaceDetails['hierarchy'][$sType]))
|
||||||
|
$aPlaceDetails['hierarchy'][$sType] = array();
|
||||||
|
$aPlaceDetails['hierarchy'][$sType][] = $funcMapAddressLine($aAddressLine);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$aPlaceDetails['hierarchy'] = array_map($funcMapAddressLine, $aHierarchyLines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
javascript_renderData($aPlaceDetails);
|
||||||
@@ -11,4 +11,5 @@
|
|||||||
<script src="js/bootstrap.min.js"></script>
|
<script src="js/bootstrap.min.js"></script>
|
||||||
<script src="js/leaflet.min.js"></script>
|
<script src="js/leaflet.min.js"></script>
|
||||||
<script src="js/Control.Minimap.min.js"></script>
|
<script src="js/Control.Minimap.min.js"></script>
|
||||||
|
<script src="js/url-search-params.js"></script>
|
||||||
<script src="js/nominatim-ui.js"></script>
|
<script src="js/nominatim-ui.js"></script>
|
||||||
|
|||||||
@@ -1,42 +1,37 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$aOutput = array();
|
$aOutput = array();
|
||||||
$aOutput['licence'] = "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright";
|
$aOutput['licence'] = 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright';
|
||||||
$aOutput['batch'] = array();
|
$aOutput['batch'] = array();
|
||||||
|
|
||||||
foreach($aBatchResults as $aSearchResults)
|
foreach ($aBatchResults as $aSearchResults) {
|
||||||
{
|
|
||||||
if (!$aSearchResults) $aSearchResults = array();
|
if (!$aSearchResults) $aSearchResults = array();
|
||||||
$aFilteredPlaces = array();
|
$aFilteredPlaces = array();
|
||||||
foreach($aSearchResults as $iResNum => $aPointDetails)
|
foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
||||||
{
|
|
||||||
$aPlace = array(
|
$aPlace = array(
|
||||||
'place_id'=>$aPointDetails['place_id'],
|
'place_id'=>$aPointDetails['place_id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
||||||
if ($sOSMType)
|
if ($sOSMType) {
|
||||||
{
|
|
||||||
$aPlace['osm_type'] = $sOSMType;
|
$aPlace['osm_type'] = $sOSMType;
|
||||||
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['aBoundingBox']))
|
if (isset($aPointDetails['aBoundingBox'])) {
|
||||||
{
|
|
||||||
$aPlace['boundingbox'] = array(
|
$aPlace['boundingbox'] = array(
|
||||||
$aPointDetails['aBoundingBox'][0],
|
$aPointDetails['aBoundingBox'][0],
|
||||||
$aPointDetails['aBoundingBox'][1],
|
$aPointDetails['aBoundingBox'][1],
|
||||||
$aPointDetails['aBoundingBox'][2],
|
$aPointDetails['aBoundingBox'][2],
|
||||||
$aPointDetails['aBoundingBox'][3]);
|
$aPointDetails['aBoundingBox'][3]
|
||||||
|
);
|
||||||
|
|
||||||
if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons)
|
if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons) {
|
||||||
{
|
|
||||||
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['zoom']))
|
if (isset($aPointDetails['zoom'])) {
|
||||||
{
|
|
||||||
$aPlace['zoom'] = $aPointDetails['zoom'];
|
$aPlace['zoom'] = $aPointDetails['zoom'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,33 +45,27 @@ foreach($aBatchResults as $aSearchResults)
|
|||||||
|
|
||||||
$aPlace['importance'] = $aPointDetails['importance'];
|
$aPlace['importance'] = $aPointDetails['importance'];
|
||||||
|
|
||||||
if (isset($aPointDetails['icon']))
|
if (isset($aPointDetails['icon'])) {
|
||||||
{
|
|
||||||
$aPlace['icon'] = $aPointDetails['icon'];
|
$aPlace['icon'] = $aPointDetails['icon'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['address']) && sizeof($aPointDetails['address'])>0)
|
if (isset($aPointDetails['address'])) {
|
||||||
{
|
$aPlace['address'] = $aPointDetails['address']->getAddressNames();
|
||||||
$aPlace['address'] = $aPointDetails['address'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['asgeojson']))
|
if (isset($aPointDetails['asgeojson'])) {
|
||||||
{
|
|
||||||
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['assvg']))
|
if (isset($aPointDetails['assvg'])) {
|
||||||
{
|
|
||||||
$aPlace['svg'] = $aPointDetails['assvg'];
|
$aPlace['svg'] = $aPointDetails['assvg'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['astext']))
|
if (isset($aPointDetails['astext'])) {
|
||||||
{
|
|
||||||
$aPlace['geotext'] = $aPointDetails['astext'];
|
$aPlace['geotext'] = $aPointDetails['astext'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['askml']))
|
if (isset($aPointDetails['askml'])) {
|
||||||
{
|
|
||||||
$aPlace['geokml'] = $aPointDetails['askml'];
|
$aPlace['geokml'] = $aPointDetails['askml'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
73
lib/template/search-geocodejson.php
Normal file
73
lib/template/search-geocodejson.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$aFilteredPlaces = array();
|
||||||
|
foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
||||||
|
$aPlace = array(
|
||||||
|
'type' => 'Feature',
|
||||||
|
'properties' => array(
|
||||||
|
'geocoding' => array()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isset($aPointDetails['place_id'])) $aPlace['properties']['geocoding']['place_id'] = $aPointDetails['place_id'];
|
||||||
|
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
||||||
|
if ($sOSMType) {
|
||||||
|
$aPlace['properties']['geocoding']['osm_type'] = $sOSMType;
|
||||||
|
$aPlace['properties']['geocoding']['osm_id'] = $aPointDetails['osm_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aPlace['properties']['geocoding']['type'] = $aPointDetails['type'];
|
||||||
|
|
||||||
|
$aPlace['properties']['geocoding']['label'] = $aPointDetails['langaddress'];
|
||||||
|
|
||||||
|
$aPlace['properties']['geocoding']['name'] = $aPointDetails['placename'];
|
||||||
|
|
||||||
|
if (isset($aPointDetails['address'])) {
|
||||||
|
$aFieldMappings = array(
|
||||||
|
'house_number' => 'housenumber',
|
||||||
|
'road' => 'street',
|
||||||
|
'locality' => 'locality',
|
||||||
|
'postcode' => 'postcode',
|
||||||
|
'city' => 'city',
|
||||||
|
'district' => 'district',
|
||||||
|
'county' => 'county',
|
||||||
|
'state' => 'state',
|
||||||
|
'country' => 'country'
|
||||||
|
);
|
||||||
|
|
||||||
|
$aAddrNames = $aPointDetails['address']->getAddressNames();
|
||||||
|
foreach ($aFieldMappings as $sFrom => $sTo) {
|
||||||
|
if (isset($aAddrNames[$sFrom])) {
|
||||||
|
$aPlace['properties']['geocoding'][$sTo] = $aAddrNames[$sFrom];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$aPlace['properties']['geocoding']['admin']
|
||||||
|
= $aPointDetails['address']->getAdminLevels();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['asgeojson'])) {
|
||||||
|
$aPlace['geometry'] = json_decode($aPointDetails['asgeojson']);
|
||||||
|
} else {
|
||||||
|
$aPlace['geometry'] = array(
|
||||||
|
'type' => 'Point',
|
||||||
|
'coordinates' => array(
|
||||||
|
(float) $aPointDetails['lon'],
|
||||||
|
(float) $aPointDetails['lat']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$aFilteredPlaces[] = $aPlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
javascript_renderData(array(
|
||||||
|
'type' => 'FeatureCollection',
|
||||||
|
'geocoding' => array(
|
||||||
|
'version' => '0.1.0',
|
||||||
|
'attribution' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
||||||
|
'licence' => 'ODbL',
|
||||||
|
'query' => $sQuery
|
||||||
|
),
|
||||||
|
'features' => $aFilteredPlaces
|
||||||
|
));
|
||||||
71
lib/template/search-geojson.php
Normal file
71
lib/template/search-geojson.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$aFilteredPlaces = array();
|
||||||
|
foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
||||||
|
$aPlace = array(
|
||||||
|
'type' => 'Feature',
|
||||||
|
'properties' => array(
|
||||||
|
'place_id'=>$aPointDetails['place_id'],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
||||||
|
if ($sOSMType) {
|
||||||
|
$aPlace['properties']['osm_type'] = $sOSMType;
|
||||||
|
$aPlace['properties']['osm_id'] = $aPointDetails['osm_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['aBoundingBox'])) {
|
||||||
|
$aPlace['bbox'] = array(
|
||||||
|
(float) $aPointDetails['aBoundingBox'][2], // minlon
|
||||||
|
(float) $aPointDetails['aBoundingBox'][0], // minlat
|
||||||
|
(float) $aPointDetails['aBoundingBox'][3], // maxlon
|
||||||
|
(float) $aPointDetails['aBoundingBox'][1] // maxlat
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['zoom'])) {
|
||||||
|
$aPlace['properties']['zoom'] = $aPointDetails['zoom'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aPlace['properties']['display_name'] = $aPointDetails['name'];
|
||||||
|
|
||||||
|
$aPlace['properties']['place_rank'] = $aPointDetails['rank_search'];
|
||||||
|
$aPlace['properties']['category'] = $aPointDetails['class'];
|
||||||
|
|
||||||
|
$aPlace['properties']['type'] = $aPointDetails['type'];
|
||||||
|
|
||||||
|
$aPlace['properties']['importance'] = $aPointDetails['importance'];
|
||||||
|
|
||||||
|
if (isset($aPointDetails['icon']) && $aPointDetails['icon']) {
|
||||||
|
$aPlace['properties']['icon'] = $aPointDetails['icon'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['address'])) {
|
||||||
|
$aPlace['properties']['address'] = $aPointDetails['address']->getAddressNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['asgeojson'])) {
|
||||||
|
$aPlace['geometry'] = json_decode($aPointDetails['asgeojson']);
|
||||||
|
} else {
|
||||||
|
$aPlace['geometry'] = array(
|
||||||
|
'type' => 'Point',
|
||||||
|
'coordinates' => array(
|
||||||
|
(float) $aPointDetails['lon'],
|
||||||
|
(float) $aPointDetails['lat']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (isset($aPointDetails['sExtraTags'])) $aPlace['properties']['extratags'] = $aPointDetails['sExtraTags'];
|
||||||
|
if (isset($aPointDetails['sNameDetails'])) $aPlace['properties']['namedetails'] = $aPointDetails['sNameDetails'];
|
||||||
|
|
||||||
|
$aFilteredPlaces[] = $aPlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
javascript_renderData(array(
|
||||||
|
'type' => 'FeatureCollection',
|
||||||
|
'licence' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
||||||
|
'features' => $aFilteredPlaces
|
||||||
|
));
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
echo '</div>';
|
echo '</div>';
|
||||||
$i = $i+1;
|
$i = $i+1;
|
||||||
}
|
}
|
||||||
if (sizeof($aSearchResults) && $sMoreURL)
|
if (!empty($aSearchResults) && $sMoreURL)
|
||||||
{
|
{
|
||||||
echo '<div class="more"><a class="btn btn-primary" href="'.htmlentities($sMoreURL).'">Search for more results</a></div>';
|
echo '<div class="more"><a class="btn btn-primary" href="'.htmlentities($sMoreURL).'">Search for more results</a></div>';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,72 +1,66 @@
|
|||||||
<?php
|
<?php
|
||||||
header("content-type: application/json; charset=UTF-8");
|
|
||||||
|
|
||||||
$aFilteredPlaces = array();
|
$aFilteredPlaces = array();
|
||||||
foreach($aSearchResults as $iResNum => $aPointDetails)
|
foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
||||||
{
|
|
||||||
$aPlace = array(
|
$aPlace = array(
|
||||||
'place_id'=>$aPointDetails['place_id'],
|
'place_id'=>$aPointDetails['place_id'],
|
||||||
'licence'=>"Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright",
|
'licence'=>'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
||||||
);
|
);
|
||||||
|
|
||||||
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
||||||
if ($sOSMType)
|
if ($sOSMType) {
|
||||||
{
|
|
||||||
$aPlace['osm_type'] = $sOSMType;
|
$aPlace['osm_type'] = $sOSMType;
|
||||||
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['aBoundingBox']))
|
if (isset($aPointDetails['aBoundingBox'])) {
|
||||||
{
|
|
||||||
$aPlace['boundingbox'] = $aPointDetails['aBoundingBox'];
|
$aPlace['boundingbox'] = $aPointDetails['aBoundingBox'];
|
||||||
|
|
||||||
if (isset($aPointDetails['aPolyPoints']))
|
if (isset($aPointDetails['aPolyPoints'])) {
|
||||||
{
|
|
||||||
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['zoom']))
|
if (isset($aPointDetails['zoom'])) {
|
||||||
{
|
|
||||||
$aPlace['zoom'] = $aPointDetails['zoom'];
|
$aPlace['zoom'] = $aPointDetails['zoom'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$aPlace['lat'] = $aPointDetails['lat'];
|
$aPlace['lat'] = $aPointDetails['lat'];
|
||||||
$aPlace['lon'] = $aPointDetails['lon'];
|
$aPlace['lon'] = $aPointDetails['lon'];
|
||||||
|
|
||||||
$aPlace['display_name'] = $aPointDetails['name'];
|
$aPlace['display_name'] = $aPointDetails['name'];
|
||||||
|
|
||||||
$aPlace['class'] = $aPointDetails['class'];
|
if ($sOutputFormat == 'jsonv2' || $sOutputFormat == 'geojson') {
|
||||||
|
$aPlace['place_rank'] = $aPointDetails['rank_search'];
|
||||||
|
$aPlace['category'] = $aPointDetails['class'];
|
||||||
|
} else {
|
||||||
|
$aPlace['class'] = $aPointDetails['class'];
|
||||||
|
}
|
||||||
$aPlace['type'] = $aPointDetails['type'];
|
$aPlace['type'] = $aPointDetails['type'];
|
||||||
|
|
||||||
$aPlace['importance'] = $aPointDetails['importance'];
|
$aPlace['importance'] = $aPointDetails['importance'];
|
||||||
|
|
||||||
if (isset($aPointDetails['icon']) && $aPointDetails['icon'])
|
if (isset($aPointDetails['icon']) && $aPointDetails['icon']) {
|
||||||
{
|
|
||||||
$aPlace['icon'] = $aPointDetails['icon'];
|
$aPlace['icon'] = $aPointDetails['icon'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['address']))
|
if (isset($aPointDetails['address'])) {
|
||||||
{
|
$aPlace['address'] = $aPointDetails['address']->getAddressNames();
|
||||||
$aPlace['address'] = $aPointDetails['address'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['asgeojson']))
|
if (isset($aPointDetails['asgeojson'])) {
|
||||||
{
|
|
||||||
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['assvg']))
|
if (isset($aPointDetails['assvg'])) {
|
||||||
{
|
|
||||||
$aPlace['svg'] = $aPointDetails['assvg'];
|
$aPlace['svg'] = $aPointDetails['assvg'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['astext']))
|
if (isset($aPointDetails['astext'])) {
|
||||||
{
|
|
||||||
$aPlace['geotext'] = $aPointDetails['astext'];
|
$aPlace['geotext'] = $aPointDetails['astext'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['askml']))
|
if (isset($aPointDetails['askml'])) {
|
||||||
{
|
|
||||||
$aPlace['geokml'] = $aPointDetails['askml'];
|
$aPlace['geokml'] = $aPointDetails['askml'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$aFilteredPlaces = array();
|
|
||||||
foreach($aSearchResults as $iResNum => $aPointDetails)
|
|
||||||
{
|
|
||||||
$aPlace = array(
|
|
||||||
'place_id'=>$aPointDetails['place_id'],
|
|
||||||
'licence'=>"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
|
||||||
);
|
|
||||||
|
|
||||||
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
|
||||||
if ($sOSMType)
|
|
||||||
{
|
|
||||||
$aPlace['osm_type'] = $sOSMType;
|
|
||||||
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['aBoundingBox']))
|
|
||||||
{
|
|
||||||
$aPlace['boundingbox'] = $aPointDetails['aBoundingBox'];
|
|
||||||
|
|
||||||
if (isset($aPointDetails['aPolyPoints']))
|
|
||||||
{
|
|
||||||
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['zoom']))
|
|
||||||
{
|
|
||||||
$aPlace['zoom'] = $aPointDetails['zoom'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$aPlace['lat'] = $aPointDetails['lat'];
|
|
||||||
$aPlace['lon'] = $aPointDetails['lon'];
|
|
||||||
$aPlace['display_name'] = $aPointDetails['name'];
|
|
||||||
$aPlace['place_rank'] = $aPointDetails['rank_search'];
|
|
||||||
|
|
||||||
$aPlace['category'] = $aPointDetails['class'];
|
|
||||||
$aPlace['type'] = $aPointDetails['type'];
|
|
||||||
|
|
||||||
$aPlace['importance'] = $aPointDetails['importance'];
|
|
||||||
|
|
||||||
if (isset($aPointDetails['icon']))
|
|
||||||
{
|
|
||||||
$aPlace['icon'] = $aPointDetails['icon'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['address']) && sizeof($aPointDetails['address'])>0)
|
|
||||||
{
|
|
||||||
$aPlace['address'] = $aPointDetails['address'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['asgeojson']))
|
|
||||||
{
|
|
||||||
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['assvg']))
|
|
||||||
{
|
|
||||||
$aPlace['svg'] = $aPointDetails['assvg'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['astext']))
|
|
||||||
{
|
|
||||||
$aPlace['geotext'] = $aPointDetails['astext'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['askml']))
|
|
||||||
{
|
|
||||||
$aPlace['geokml'] = $aPointDetails['askml'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['sExtraTags'])) $aPlace['extratags'] = $aPointDetails['sExtraTags'];
|
|
||||||
if (isset($aPointDetails['sNameDetails'])) $aPlace['namedetails'] = $aPointDetails['sNameDetails'];
|
|
||||||
|
|
||||||
$aFilteredPlaces[] = $aPlace;
|
|
||||||
}
|
|
||||||
|
|
||||||
javascript_renderData($aFilteredPlaces);
|
|
||||||
@@ -1,72 +1,63 @@
|
|||||||
<?php
|
<?php
|
||||||
header("content-type: text/xml; charset=UTF-8");
|
header('content-type: text/xml; charset=UTF-8');
|
||||||
|
|
||||||
echo "<";
|
echo '<';
|
||||||
echo "?xml version=\"1.0\" encoding=\"UTF-8\" ?";
|
echo '?xml version="1.0" encoding="UTF-8" ?';
|
||||||
echo ">\n";
|
echo ">\n";
|
||||||
|
|
||||||
echo "<";
|
echo '<';
|
||||||
echo (isset($sXmlRootTag)?$sXmlRootTag:'searchresults');
|
echo (isset($sXmlRootTag)?$sXmlRootTag:'searchresults');
|
||||||
echo " timestamp='".date(DATE_RFC822)."'";
|
echo " timestamp='".date(DATE_RFC822)."'";
|
||||||
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
|
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
|
||||||
echo " querystring='".htmlspecialchars($sQuery, ENT_QUOTES)."'";
|
echo " querystring='".htmlspecialchars($sQuery, ENT_QUOTES)."'";
|
||||||
if (isset($aMoreParams['viewbox'])) echo " viewbox='".htmlspecialchars($aMoreParams['viewbox'], ENT_QUOTES)."'";
|
if (isset($aMoreParams['viewbox'])) echo " viewbox='".htmlspecialchars($aMoreParams['viewbox'], ENT_QUOTES)."'";
|
||||||
echo " polygon='".(isset($aMoreParams['polygon'])?'true':'false')."'";
|
echo " polygon='".(isset($aMoreParams['polygon'])?'true':'false')."'";
|
||||||
if (isset($aMoreParams['exclude_place_ids']))
|
if (isset($aMoreParams['exclude_place_ids'])) {
|
||||||
{
|
|
||||||
echo " exclude_place_ids='".htmlspecialchars($aMoreParams['exclude_place_ids'])."'";
|
echo " exclude_place_ids='".htmlspecialchars($aMoreParams['exclude_place_ids'])."'";
|
||||||
}
|
}
|
||||||
echo " more_url='".htmlspecialchars($sMoreURL)."'";
|
echo " more_url='".htmlspecialchars($sMoreURL)."'";
|
||||||
echo ">\n";
|
echo ">\n";
|
||||||
|
|
||||||
foreach($aSearchResults as $iResNum => $aResult)
|
foreach ($aSearchResults as $iResNum => $aResult) {
|
||||||
{
|
|
||||||
echo "<place place_id='".$aResult['place_id']."'";
|
echo "<place place_id='".$aResult['place_id']."'";
|
||||||
$sOSMType = formatOSMType($aResult['osm_type']);
|
$sOSMType = formatOSMType($aResult['osm_type']);
|
||||||
if ($sOSMType)
|
if ($sOSMType) {
|
||||||
{
|
|
||||||
echo " osm_type='$sOSMType'";
|
echo " osm_type='$sOSMType'";
|
||||||
echo " osm_id='".$aResult['osm_id']."'";
|
echo " osm_id='".$aResult['osm_id']."'";
|
||||||
}
|
}
|
||||||
echo " place_rank='".$aResult['rank_search']."'";
|
echo " place_rank='".$aResult['rank_search']."'";
|
||||||
|
|
||||||
if (isset($aResult['aBoundingBox']))
|
if (isset($aResult['aBoundingBox'])) {
|
||||||
{
|
|
||||||
echo ' boundingbox="';
|
echo ' boundingbox="';
|
||||||
echo join(',',$aResult['aBoundingBox']);
|
echo join(',', $aResult['aBoundingBox']);
|
||||||
echo '"';
|
echo '"';
|
||||||
|
|
||||||
if (isset($aResult['aPolyPoints']))
|
if (isset($aResult['aPolyPoints'])) {
|
||||||
{
|
|
||||||
echo ' polygonpoints=\'';
|
echo ' polygonpoints=\'';
|
||||||
echo json_encode($aResult['aPolyPoints']);
|
echo json_encode($aResult['aPolyPoints']);
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['asgeojson']))
|
if (isset($aResult['asgeojson'])) {
|
||||||
{
|
|
||||||
echo ' geojson=\'';
|
echo ' geojson=\'';
|
||||||
echo $aResult['asgeojson'];
|
echo $aResult['asgeojson'];
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['assvg']))
|
if (isset($aResult['assvg'])) {
|
||||||
{
|
|
||||||
echo ' geosvg=\'';
|
echo ' geosvg=\'';
|
||||||
echo $aResult['assvg'];
|
echo $aResult['assvg'];
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['astext']))
|
if (isset($aResult['astext'])) {
|
||||||
{
|
|
||||||
echo ' geotext=\'';
|
echo ' geotext=\'';
|
||||||
echo $aResult['astext'];
|
echo $aResult['astext'];
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['zoom']))
|
if (isset($aResult['zoom'])) {
|
||||||
{
|
|
||||||
echo " zoom='".$aResult['zoom']."'";
|
echo " zoom='".$aResult['zoom']."'";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,82 +68,67 @@ foreach($aSearchResults as $iResNum => $aResult)
|
|||||||
echo " class='".htmlspecialchars($aResult['class'])."'";
|
echo " class='".htmlspecialchars($aResult['class'])."'";
|
||||||
echo " type='".htmlspecialchars($aResult['type'], ENT_QUOTES)."'";
|
echo " type='".htmlspecialchars($aResult['type'], ENT_QUOTES)."'";
|
||||||
echo " importance='".htmlspecialchars($aResult['importance'])."'";
|
echo " importance='".htmlspecialchars($aResult['importance'])."'";
|
||||||
if (isset($aResult['icon']) && $aResult['icon'])
|
if (isset($aResult['icon']) && $aResult['icon']) {
|
||||||
{
|
|
||||||
echo " icon='".htmlspecialchars($aResult['icon'], ENT_QUOTES)."'";
|
echo " icon='".htmlspecialchars($aResult['icon'], ENT_QUOTES)."'";
|
||||||
}
|
}
|
||||||
|
|
||||||
$bHasDelim = false;
|
$bHasDelim = false;
|
||||||
|
|
||||||
if (isset($aResult['askml']))
|
if (isset($aResult['askml'])) {
|
||||||
{
|
if (!$bHasDelim) {
|
||||||
if (!$bHasDelim)
|
|
||||||
{
|
|
||||||
$bHasDelim = true;
|
$bHasDelim = true;
|
||||||
echo ">";
|
echo '>';
|
||||||
}
|
}
|
||||||
echo "\n<geokml>";
|
echo "\n<geokml>";
|
||||||
echo $aResult['askml'];
|
echo $aResult['askml'];
|
||||||
echo "</geokml>";
|
echo '</geokml>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['sExtraTags']))
|
if (isset($aResult['sExtraTags'])) {
|
||||||
{
|
if (!$bHasDelim) {
|
||||||
if (!$bHasDelim)
|
|
||||||
{
|
|
||||||
$bHasDelim = true;
|
$bHasDelim = true;
|
||||||
echo ">";
|
echo '>';
|
||||||
}
|
}
|
||||||
echo "\n<extratags>";
|
echo "\n<extratags>";
|
||||||
foreach ($aResult['sExtraTags'] as $sKey => $sValue)
|
foreach ($aResult['sExtraTags'] as $sKey => $sValue) {
|
||||||
{
|
|
||||||
echo '<tag key="'.htmlspecialchars($sKey).'" value="'.htmlspecialchars($sValue).'"/>';
|
echo '<tag key="'.htmlspecialchars($sKey).'" value="'.htmlspecialchars($sValue).'"/>';
|
||||||
}
|
}
|
||||||
echo "</extratags>";
|
echo '</extratags>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['sNameDetails']))
|
if (isset($aResult['sNameDetails'])) {
|
||||||
{
|
if (!$bHasDelim) {
|
||||||
if (!$bHasDelim)
|
|
||||||
{
|
|
||||||
$bHasDelim = true;
|
$bHasDelim = true;
|
||||||
echo ">";
|
echo '>';
|
||||||
}
|
}
|
||||||
echo "\n<namedetails>";
|
echo "\n<namedetails>";
|
||||||
foreach ($aResult['sNameDetails'] as $sKey => $sValue)
|
foreach ($aResult['sNameDetails'] as $sKey => $sValue) {
|
||||||
{
|
|
||||||
echo '<name desc="'.htmlspecialchars($sKey).'">';
|
echo '<name desc="'.htmlspecialchars($sKey).'">';
|
||||||
echo htmlspecialchars($sValue);
|
echo htmlspecialchars($sValue);
|
||||||
echo "</name>";
|
echo '</name>';
|
||||||
}
|
}
|
||||||
echo "</namedetails>";
|
echo '</namedetails>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['address']))
|
if (isset($aResult['address'])) {
|
||||||
{
|
if (!$bHasDelim) {
|
||||||
if (!$bHasDelim)
|
|
||||||
{
|
|
||||||
$bHasDelim = true;
|
$bHasDelim = true;
|
||||||
echo ">";
|
echo '>';
|
||||||
}
|
}
|
||||||
echo "\n";
|
echo "\n";
|
||||||
foreach($aResult['address'] as $sKey => $sValue)
|
foreach ($aResult['address']->getAddressNames() as $sKey => $sValue) {
|
||||||
{
|
$sKey = str_replace(' ', '_', $sKey);
|
||||||
$sKey = str_replace(' ','_',$sKey);
|
|
||||||
echo "<$sKey>";
|
echo "<$sKey>";
|
||||||
echo htmlspecialchars($sValue);
|
echo htmlspecialchars($sValue);
|
||||||
echo "</$sKey>";
|
echo "</$sKey>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bHasDelim)
|
if ($bHasDelim) {
|
||||||
{
|
echo '</place>';
|
||||||
echo "</place>";
|
} else {
|
||||||
}
|
echo '/>';
|
||||||
else
|
|
||||||
{
|
|
||||||
echo "/>";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "</" . (isset($sXmlRootTag)?$sXmlRootTag:'searchresults') . ">";
|
echo '</' . (isset($sXmlRootTag)?$sXmlRootTag:'searchresults') . '>';
|
||||||
|
|||||||
@@ -157,17 +157,18 @@ transliteration( PG_FUNCTION_ARGS )
|
|||||||
PG_RETURN_TEXT_P(result);
|
PG_RETURN_TEXT_P(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set isspace=1 if the replacement _only_ adds a space before the search string. I.e. to == " " + from
|
||||||
void str_replace(char* buffer, int* len, int* changes, char* from, int fromlen, char* to, int tolen, int isspace)
|
void str_replace(char* buffer, int* len, int* changes, char* from, int fromlen, char* to, int tolen, int isspace)
|
||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
// Search string is too long to be pressent
|
// Search string is too long to be present
|
||||||
if (fromlen > *len) return;
|
if (fromlen > *len) return;
|
||||||
|
|
||||||
p = strstr(buffer, from);
|
p = strstr(buffer, from);
|
||||||
while(p)
|
while(p)
|
||||||
{
|
{
|
||||||
if (!isspace || *(p-1) != ' ')
|
if (!isspace || (p > buffer && *(p-1) != ' '))
|
||||||
{
|
{
|
||||||
(*changes)++;
|
(*changes)++;
|
||||||
if (tolen != fromlen) memmove(p+tolen, p+fromlen, *len-(p-buffer)+1);
|
if (tolen != fromlen) memmove(p+tolen, p+fromlen, *len-(p-buffer)+1);
|
||||||
@@ -230,7 +231,7 @@ gettokenstring( PG_FUNCTION_ARGS )
|
|||||||
sourcedata = (unsigned char *)VARDATA(source);
|
sourcedata = (unsigned char *)VARDATA(source);
|
||||||
sourcedatalength = VARSIZE(source) - VARHDRSZ;
|
sourcedatalength = VARSIZE(source) - VARHDRSZ;
|
||||||
|
|
||||||
// Buffer for doing the replace in - string could get slightly longer (double is mastive overkill)
|
// Buffer for doing the replace in - string could get slightly longer (double is massive overkill)
|
||||||
buffer = (char *)palloc((sourcedatalength*2)*sizeof(char));
|
buffer = (char *)palloc((sourcedatalength*2)*sizeof(char));
|
||||||
memcpy(buffer+1, sourcedata, sourcedatalength);
|
memcpy(buffer+1, sourcedata, sourcedatalength);
|
||||||
buffer[0] = 32;
|
buffer[0] = 32;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
add_executable(nominatim export.c geometry.cpp import.c index.c input.c nominatim.c postgresql.c sprompt.c)
|
add_executable(nominatim export.c geometry.cpp import.c index.c input.c nominatim.c postgresql.c sprompt.c)
|
||||||
include(CheckIncludeFile)
|
|
||||||
CHECK_INCLUDE_FILE(byteswap.h HAVE_BYTESWAP_H)
|
CHECK_SYMBOL_EXISTS(bswap_32 "byteswap.h" HAVE_BYTESWAP)
|
||||||
CHECK_INCLUDE_FILE(sys/endian.h HAVE_SYS_ENDIAN_H)
|
CHECK_SYMBOL_EXISTS(bswap32 "sys/endian.h" HAVE_SYS_ENDIAN)
|
||||||
if(HAVE_BYTESWAP_H)
|
|
||||||
target_compile_definitions(nominatim PRIVATE HAVE_BYTESWAP_H)
|
target_compile_definitions(nominatim
|
||||||
endif(HAVE_BYTESWAP_H)
|
PRIVATE HAVE_BYTESWAP=$<BOOL:${HAVE_BYTESWAP}>
|
||||||
if(HAVE_SYS_ENDIAN_H)
|
PRIVATE HAVE_SYS_ENDIAN=$<BOOL:${HAVE_SYS_ENDIAN}>
|
||||||
target_compile_definitions(nominatim PRIVATE HAVE_SYS_ENDIAN_H)
|
)
|
||||||
endif(HAVE_SYS_ENDIAN_H)
|
|
||||||
target_link_libraries(nominatim ${LIBXML2_LIBRARIES} ${ZLIB_LIBRARIES} ${BZIP2_LIBRARIES} ${PostgreSQL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(nominatim ${LIBXML2_LIBRARIES} ${ZLIB_LIBRARIES} ${BZIP2_LIBRARIES} ${PostgreSQL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
||||||
|
|||||||
@@ -262,13 +262,16 @@ struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|||||||
|
|
||||||
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile)
|
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile)
|
||||||
{
|
{
|
||||||
struct index_thread_data * thread_data;
|
struct index_thread_data *thread_data;
|
||||||
|
|
||||||
PGconn *conn;
|
PGconn *conn;
|
||||||
PGresult * res;
|
PGresult *res;
|
||||||
|
int num_rows = 0, status_code = 0;
|
||||||
|
int db_has_locale = 0;
|
||||||
|
char *result_string = NULL;
|
||||||
|
|
||||||
int rank;
|
int rank;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
xmlTextWriterPtr writer;
|
xmlTextWriterPtr writer;
|
||||||
@@ -283,6 +286,23 @@ void nominatim_index(int rank_min, int rank_max, int num_threads, const char *co
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res = PQexec(conn, "SHOW lc_messages");
|
||||||
|
status_code = PQresultStatus(res);
|
||||||
|
if (status_code != PGRES_TUPLES_OK && status_code != PGRES_SINGLE_TUPLE) {
|
||||||
|
fprintf(stderr, "Failed determining database locale: %s\n", PQerrorMessage(conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
num_rows = PQntuples(res);
|
||||||
|
if (num_rows > 0)
|
||||||
|
{
|
||||||
|
result_string = PQgetvalue(res, 0, 0);
|
||||||
|
if (result_string && (strlen(result_string) > 0) && (strcasecmp(result_string, "C") != 0))
|
||||||
|
{
|
||||||
|
// non-default locale if the result exists, is non-empty, and is not "C"
|
||||||
|
db_has_locale = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
pg_prepare_params[0] = PG_OID_INT4;
|
||||||
res = PQprepare(conn, "index_sectors",
|
res = PQprepare(conn, "index_sectors",
|
||||||
"select geometry_sector,count(*) from placex where rank_search = $1 and indexed_status > 0 group by geometry_sector order by geometry_sector",
|
"select geometry_sector,count(*) from placex where rank_search = $1 and indexed_status > 0 group by geometry_sector order by geometry_sector",
|
||||||
@@ -392,19 +412,20 @@ void nominatim_index(int rank_min, int rank_max, int num_threads, const char *co
|
|||||||
}
|
}
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
|
||||||
// Make sure the error message is not localized as we parse it later.
|
if (db_has_locale)
|
||||||
res = PQexec(thread_data[i].conn, "SET lc_messages TO 'C'");
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to set langauge: %s\n", PQerrorMessage(thread_data[i].conn));
|
// Make sure the error message is not localized as we parse it later.
|
||||||
exit(EXIT_FAILURE);
|
res = PQexec(thread_data[i].conn, "SET lc_messages TO 'C'");
|
||||||
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed to set langauge: %s\n", PQerrorMessage(thread_data[i].conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
PQclear(res);
|
||||||
}
|
}
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
nominatim_exportCreatePreparedQueries(thread_data[i].conn);
|
nominatim_exportCreatePreparedQueries(thread_data[i].conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fprintf(stderr, "Starting indexing rank (%i to %i) using %i threads\n", rank_min, rank_max, num_threads);
|
fprintf(stderr, "Starting indexing rank (%i to %i) using %i threads\n", rank_min, rank_max, num_threads);
|
||||||
|
|
||||||
for (rank = rank_min; rank <= rank_max; rank++)
|
for (rank = rank_min; rank <= rank_max; rank++)
|
||||||
|
|||||||
@@ -7,20 +7,32 @@
|
|||||||
#define PG_OID_INT8 20
|
#define PG_OID_INT8 20
|
||||||
#define PG_OID_INT4 23
|
#define PG_OID_INT4 23
|
||||||
|
|
||||||
#if defined(HAVE_BYTESWAP_H)
|
#if HAVE_BYTESWAP
|
||||||
#include <byteswap.h>
|
#include <byteswap.h>
|
||||||
#elif defined(HAVE_SYS_ENDIAN_H)
|
#define PG_BSWAP32(x) bswap_32(x)
|
||||||
|
#define PG_BSWAP64(x) bswap_64(x)
|
||||||
|
#elif HAVE_SYS_ENDIAN
|
||||||
#include <sys/endian.h>
|
#include <sys/endian.h>
|
||||||
|
#define PG_BSWAP32(x) bswap32(x)
|
||||||
|
#define PG_BSWAP64(x) bswap64(x)
|
||||||
|
#else
|
||||||
|
#error "No appropriate byteswap found for your system."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||||
#define PGint16(x) (x)
|
|
||||||
#define PGint32(x) (x)
|
#define PGint32(x) (x)
|
||||||
#define PGint64(x) (x)
|
#define PGint64(x) (x)
|
||||||
|
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||||
|
#define PGint32(x) PG_BSWAP32(x)
|
||||||
|
#define PGint64(x) PG_BSWAP64(x)
|
||||||
|
#elif defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)
|
||||||
|
#define PGint32(x) (x)
|
||||||
|
#define PGint64(x) (x)
|
||||||
|
#elif defined(_BYTE_ORDER) && (_BYTE_ORDER == _LITTLE_ENDIAN)
|
||||||
|
#define PGint32(x) PG_BSWAP32(x)
|
||||||
|
#define PGint64(x) PG_BSWAP64(x)
|
||||||
#else
|
#else
|
||||||
#define PGint16(x) __bswap_16 (x)
|
#error "Cannot determine byte order."
|
||||||
#define PGint32(x) __bswap_32 (x)
|
|
||||||
#define PGint64(x) __bswap_64 (x)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char *build_conninfo(const char *db, const char *username, const char *password, const char *host, const char *port);
|
const char *build_conninfo(const char *db, const char *username, const char *password, const char *host, const char *port);
|
||||||
|
|||||||
Submodule osm2pgsql updated: 00fb2505c1...93b73e5f5c
29
phpcs.xml
29
phpcs.xml
@@ -8,6 +8,12 @@
|
|||||||
<!-- https://github.com/squizlabs/PHP_CodeSniffer/blob/master/CodeSniffer/Standards/PSR2/ruleset.xml -->
|
<!-- https://github.com/squizlabs/PHP_CodeSniffer/blob/master/CodeSniffer/Standards/PSR2/ruleset.xml -->
|
||||||
<rule ref="PSR2"/>
|
<rule ref="PSR2"/>
|
||||||
|
|
||||||
|
<exclude-pattern>./lib/template/*html*</exclude-pattern>
|
||||||
|
<exclude-pattern>./lib/template/includes/</exclude-pattern>
|
||||||
|
<exclude-pattern>./module/</exclude-pattern>
|
||||||
|
<exclude-pattern>./website/css</exclude-pattern>
|
||||||
|
<exclude-pattern>./website/js</exclude-pattern>
|
||||||
|
|
||||||
<rule ref="Generic.Files.LineLength">
|
<rule ref="Generic.Files.LineLength">
|
||||||
<properties>
|
<properties>
|
||||||
<property name="lineLimit" value="194"/>
|
<property name="lineLimit" value="194"/>
|
||||||
@@ -26,6 +32,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- eval, system, etc -->
|
||||||
|
<rule ref="Generic.PHP.ForbiddenFunctions">
|
||||||
|
<properties>
|
||||||
|
<property name="forbiddenFunctions" type="array" value="sizeof=>count,delete=>unset,print=>echo,create_function=>null,eval=>null"/>
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
|
||||||
<!-- **************************************************************
|
<!-- **************************************************************
|
||||||
DOCUMENTATION
|
DOCUMENTATION
|
||||||
************************************************************** -->
|
************************************************************** -->
|
||||||
@@ -80,18 +93,7 @@
|
|||||||
INDENTATION, SPACING
|
INDENTATION, SPACING
|
||||||
************************************************************** -->
|
************************************************************** -->
|
||||||
|
|
||||||
<!-- We don't need 2 blank lines after function -->
|
<rule ref="Squiz.Arrays.ArrayDeclaration.KeyNotAligned" />
|
||||||
<rule ref="Squiz.WhiteSpace.FunctionSpacing.After">
|
|
||||||
<severity>0</severity>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Aligned looks nicer, but causes too many warnings currently -->
|
|
||||||
<rule ref="Generic.Formatting.MultipleStatementAlignment.NotSame">
|
|
||||||
<severity>0</severity>
|
|
||||||
</rule>
|
|
||||||
<rule ref="Generic.Formatting.MultipleStatementAlignment.NotSameWarning">
|
|
||||||
<severity>0</severity>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Aligned looks nicer, but causes too many warnings currently -->
|
<!-- Aligned looks nicer, but causes too many warnings currently -->
|
||||||
<rule ref="Squiz.Arrays.ArrayDeclaration.DoubleArrowNotAligned">
|
<rule ref="Squiz.Arrays.ArrayDeclaration.DoubleArrowNotAligned">
|
||||||
@@ -103,7 +105,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- **************************************************************
|
<!-- **************************************************************
|
||||||
VARIABLES
|
VARIABLES
|
||||||
************************************************************** -->
|
************************************************************** -->
|
||||||
@@ -126,6 +127,8 @@
|
|||||||
<severity>0</severity>
|
<severity>0</severity>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
|
<!-- array() instead of [] for initialisation -->
|
||||||
|
<rule ref="Generic.Arrays.DisallowShortArraySyntax.Found" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ if (isset($_GET['debug']) && $_GET['debug']) @define('CONST_Debug', true);
|
|||||||
@define('CONST_Debug', false);
|
@define('CONST_Debug', false);
|
||||||
@define('CONST_Database_DSN', 'pgsql://@/nominatim'); // <driver>://<username>:<password>@<host>:<port>/<database>
|
@define('CONST_Database_DSN', 'pgsql://@/nominatim'); // <driver>://<username>:<password>@<host>:<port>/<database>
|
||||||
@define('CONST_Database_Web_User', 'www-data');
|
@define('CONST_Database_Web_User', 'www-data');
|
||||||
|
@define('CONST_Database_Module_Path', CONST_InstallPath.'/module');
|
||||||
@define('CONST_Max_Word_Frequency', '50000');
|
@define('CONST_Max_Word_Frequency', '50000');
|
||||||
@define('CONST_Limit_Reindexing', true);
|
@define('CONST_Limit_Reindexing', true);
|
||||||
// Restrict search languages.
|
// Restrict search languages.
|
||||||
@@ -20,7 +21,7 @@ if (isset($_GET['debug']) && $_GET['debug']) @define('CONST_Debug', true);
|
|||||||
// Rules for normalizing terms for comparison before doing comparisons.
|
// Rules for normalizing terms for comparison before doing comparisons.
|
||||||
// The default is to remove accents and punctuation and to lower-case the
|
// The default is to remove accents and punctuation and to lower-case the
|
||||||
// term. Spaces are kept but collapsed to one standard space.
|
// term. Spaces are kept but collapsed to one standard space.
|
||||||
@define('CONST_Term_Normalization_Rules', ":: NFD (); [:Nonspacing Mark:] >; :: lower (); [[:Punctuation:][:Space:]]+ > ' '; :: NFC ();");
|
@define('CONST_Term_Normalization_Rules', ":: NFD (); [[:Nonspacing Mark:] [:Cf:]] >; :: lower (); [[:Punctuation:][:Space:]]+ > ' '; :: NFC ();");
|
||||||
|
|
||||||
// Set to false to avoid importing extra postcodes for the US.
|
// Set to false to avoid importing extra postcodes for the US.
|
||||||
@define('CONST_Use_Extra_US_Postcodes', true);
|
@define('CONST_Use_Extra_US_Postcodes', true);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ $aTagsBlacklist
|
|||||||
'place' => array('house', 'houses'),
|
'place' => array('house', 'houses'),
|
||||||
);
|
);
|
||||||
|
|
||||||
// If a class is in the white list then all types will
|
// If a class is in the white list then all types will
|
||||||
// be ignored except the ones given in the list.
|
// be ignored except the ones given in the list.
|
||||||
// Also use this list to exclude an entire class from
|
// Also use this list to exclude an entire class from
|
||||||
// special phrases.
|
// special phrases.
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ CREATE OR REPLACE FUNCTION make_standard_name(name TEXT) RETURNS TEXT
|
|||||||
DECLARE
|
DECLARE
|
||||||
o TEXT;
|
o TEXT;
|
||||||
BEGIN
|
BEGIN
|
||||||
o := gettokenstring(transliteration(name));
|
o := public.gettokenstring(public.transliteration(name));
|
||||||
RETURN trim(substr(o,1,length(o)));
|
RETURN trim(substr(o,1,length(o)));
|
||||||
END;
|
END;
|
||||||
$$
|
$$
|
||||||
@@ -256,6 +256,28 @@ END;
|
|||||||
$$
|
$$
|
||||||
LANGUAGE plpgsql IMMUTABLE;
|
LANGUAGE plpgsql IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION reverse_place_diameter(rank_search SMALLINT)
|
||||||
|
RETURNS FLOAT
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
IF rank_search <= 4 THEN
|
||||||
|
RETURN 5.0;
|
||||||
|
ELSIF rank_search <= 8 THEN
|
||||||
|
RETURN 1.8;
|
||||||
|
ELSIF rank_search <= 12 THEN
|
||||||
|
RETURN 0.6;
|
||||||
|
ELSIF rank_search <= 17 THEN
|
||||||
|
RETURN 0.16;
|
||||||
|
ELSIF rank_search <= 18 THEN
|
||||||
|
RETURN 0.08;
|
||||||
|
ELSIF rank_search <= 19 THEN
|
||||||
|
RETURN 0.04;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN 0.02;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql IMMUTABLE;
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION get_postcode_rank(country_code VARCHAR(2), postcode TEXT,
|
CREATE OR REPLACE FUNCTION get_postcode_rank(country_code VARCHAR(2), postcode TEXT,
|
||||||
OUT rank_search SMALLINT, OUT rank_address SMALLINT)
|
OUT rank_search SMALLINT, OUT rank_address SMALLINT)
|
||||||
@@ -842,6 +864,10 @@ BEGIN
|
|||||||
SELECT * FROM get_postcode_rank(NEW.country_code, NEW.address->'postcode')
|
SELECT * FROM get_postcode_rank(NEW.country_code, NEW.address->'postcode')
|
||||||
INTO NEW.rank_search, NEW.rank_address;
|
INTO NEW.rank_search, NEW.rank_address;
|
||||||
|
|
||||||
|
IF NOT ST_GeometryType(NEW.geometry) IN ('ST_Polygon','ST_MultiPolygon') THEN
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
END IF;
|
||||||
|
|
||||||
ELSEIF NEW.class = 'place' THEN
|
ELSEIF NEW.class = 'place' THEN
|
||||||
IF NEW.type in ('continent') THEN
|
IF NEW.type in ('continent') THEN
|
||||||
NEW.rank_search := 2;
|
NEW.rank_search := 2;
|
||||||
@@ -1234,6 +1260,7 @@ DECLARE
|
|||||||
relation_members TEXT[];
|
relation_members TEXT[];
|
||||||
relMember RECORD;
|
relMember RECORD;
|
||||||
linkedplacex RECORD;
|
linkedplacex RECORD;
|
||||||
|
addr_item RECORD;
|
||||||
search_diameter FLOAT;
|
search_diameter FLOAT;
|
||||||
search_prevdiameter FLOAT;
|
search_prevdiameter FLOAT;
|
||||||
search_maxrank INTEGER;
|
search_maxrank INTEGER;
|
||||||
@@ -1310,8 +1337,8 @@ BEGIN
|
|||||||
i := getorcreate_housenumber_id(make_standard_name(NEW.housenumber));
|
i := getorcreate_housenumber_id(make_standard_name(NEW.housenumber));
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
addr_street = NEW.address->'street';
|
addr_street := NEW.address->'street';
|
||||||
addr_place = NEW.address->'place';
|
addr_place := NEW.address->'place';
|
||||||
|
|
||||||
IF NEW.address ? 'postcode' and NEW.address->'postcode' not similar to '%(,|;)%' THEN
|
IF NEW.address ? 'postcode' and NEW.address->'postcode' not similar to '%(,|;)%' THEN
|
||||||
i := getorcreate_postcode_id(NEW.address->'postcode');
|
i := getorcreate_postcode_id(NEW.address->'postcode');
|
||||||
@@ -1418,7 +1445,7 @@ BEGIN
|
|||||||
-- see if we can get it from a surrounding building
|
-- see if we can get it from a surrounding building
|
||||||
IF NEW.osm_type = 'N' AND addr_street IS NULL AND addr_place IS NULL
|
IF NEW.osm_type = 'N' AND addr_street IS NULL AND addr_place IS NULL
|
||||||
AND NEW.housenumber IS NULL THEN
|
AND NEW.housenumber IS NULL THEN
|
||||||
FOR location IN select * from placex where ST_Covers(geometry, place_centroid)
|
FOR location IN select address from placex where ST_Covers(geometry, place_centroid)
|
||||||
and address is not null
|
and address is not null
|
||||||
and (address ? 'housenumber' or address ? 'street' or address ? 'place')
|
and (address ? 'housenumber' or address ? 'street' or address ? 'place')
|
||||||
and rank_search > 28 AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon')
|
and rank_search > 28 AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon')
|
||||||
@@ -1455,9 +1482,7 @@ BEGIN
|
|||||||
IF NEW.parent_place_id IS NULL AND addr_street IS NOT NULL THEN
|
IF NEW.parent_place_id IS NULL AND addr_street IS NOT NULL THEN
|
||||||
address_street_word_ids := get_name_ids(make_standard_name(addr_street));
|
address_street_word_ids := get_name_ids(make_standard_name(addr_street));
|
||||||
IF address_street_word_ids IS NOT NULL THEN
|
IF address_street_word_ids IS NOT NULL THEN
|
||||||
FOR location IN SELECT * from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
|
SELECT place_id from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
||||||
NEW.parent_place_id := location.place_id;
|
|
||||||
END LOOP;
|
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
--DEBUG: RAISE WARNING 'Checked for addr:street (%)', NEW.parent_place_id;
|
--DEBUG: RAISE WARNING 'Checked for addr:street (%)', NEW.parent_place_id;
|
||||||
@@ -1465,88 +1490,80 @@ BEGIN
|
|||||||
IF NEW.parent_place_id IS NULL AND addr_place IS NOT NULL THEN
|
IF NEW.parent_place_id IS NULL AND addr_place IS NOT NULL THEN
|
||||||
address_street_word_ids := get_name_ids(make_standard_name(addr_place));
|
address_street_word_ids := get_name_ids(make_standard_name(addr_place));
|
||||||
IF address_street_word_ids IS NOT NULL THEN
|
IF address_street_word_ids IS NOT NULL THEN
|
||||||
FOR location IN SELECT * from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
|
SELECT place_id from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
||||||
NEW.parent_place_id := location.place_id;
|
|
||||||
END LOOP;
|
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
--DEBUG: RAISE WARNING 'Checked for addr:place (%)', NEW.parent_place_id;
|
--DEBUG: RAISE WARNING 'Checked for addr:place (%)', NEW.parent_place_id;
|
||||||
|
|
||||||
-- Is this node part of an interpolation?
|
-- Is this node part of an interpolation?
|
||||||
IF NEW.parent_place_id IS NULL AND NEW.osm_type = 'N' THEN
|
IF NEW.parent_place_id IS NULL AND NEW.osm_type = 'N' THEN
|
||||||
FOR location IN
|
SELECT q.parent_place_id FROM location_property_osmline q, planet_osm_ways x
|
||||||
SELECT q.parent_place_id FROM location_property_osmline q, planet_osm_ways x
|
WHERE q.linegeo && NEW.geometry and x.id = q.osm_id and NEW.osm_id = any(x.nodes)
|
||||||
WHERE q.linegeo && NEW.geometry and x.id = q.osm_id and NEW.osm_id = any(x.nodes)
|
LIMIT 1 INTO NEW.parent_place_id;
|
||||||
LIMIT 1
|
|
||||||
LOOP
|
|
||||||
NEW.parent_place_id := location.parent_place_id;
|
|
||||||
END LOOP;
|
|
||||||
END IF;
|
END IF;
|
||||||
--DEBUG: RAISE WARNING 'Checked for interpolation (%)', NEW.parent_place_id;
|
--DEBUG: RAISE WARNING 'Checked for interpolation (%)', NEW.parent_place_id;
|
||||||
|
|
||||||
-- Is this node part of a way?
|
-- Is this node part of a way?
|
||||||
IF NEW.parent_place_id IS NULL AND NEW.osm_type = 'N' THEN
|
IF NEW.parent_place_id IS NULL AND NEW.osm_type = 'N' THEN
|
||||||
|
|
||||||
FOR location IN select p.place_id, p.osm_id, p.parent_place_id, p.rank_search, p.address from placex p, planet_osm_ways w
|
FOR location IN
|
||||||
where p.osm_type = 'W' and p.rank_search >= 26 and p.geometry && NEW.geometry and w.id = p.osm_id and NEW.osm_id = any(w.nodes)
|
SELECT p.place_id, p.osm_id, p.rank_search, p.address from placex p, planet_osm_ways w
|
||||||
|
WHERE p.osm_type = 'W' and p.rank_search >= 26 and p.geometry && NEW.geometry and w.id = p.osm_id and NEW.osm_id = any(w.nodes)
|
||||||
LOOP
|
LOOP
|
||||||
--DEBUG: RAISE WARNING 'Node is part of way % ', location.osm_id;
|
--DEBUG: RAISE WARNING 'Node is part of way % ', location.osm_id;
|
||||||
|
|
||||||
-- Way IS a road then we are on it - that must be our road
|
-- Way IS a road then we are on it - that must be our road
|
||||||
IF location.rank_search < 28 AND NEW.parent_place_id IS NULL THEN
|
IF location.rank_search < 28 THEN
|
||||||
--RAISE WARNING 'node in way that is a street %',location;
|
--RAISE WARNING 'node in way that is a street %',location;
|
||||||
NEW.parent_place_id := location.place_id;
|
NEW.parent_place_id := location.place_id;
|
||||||
|
EXIT;
|
||||||
END IF;
|
END IF;
|
||||||
--DEBUG: RAISE WARNING 'Checked if way is street (%)', NEW.parent_place_id;
|
--DEBUG: RAISE WARNING 'Checked if way is street (%)', NEW.parent_place_id;
|
||||||
|
|
||||||
-- If the way mentions a street or place address, try that for parenting.
|
-- If the way mentions a street or place address, try that for parenting.
|
||||||
IF NEW.parent_place_id IS NULL AND location.address ? 'street' THEN
|
IF location.address is not null THEN
|
||||||
address_street_word_ids := get_name_ids(make_standard_name(location.address->'street'));
|
IF location.address ? 'street' THEN
|
||||||
IF address_street_word_ids IS NOT NULL THEN
|
address_street_word_ids := get_name_ids(make_standard_name(location.address->'street'));
|
||||||
FOR linkedplacex IN SELECT place_id from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
|
IF address_street_word_ids IS NOT NULL THEN
|
||||||
NEW.parent_place_id := linkedplacex.place_id;
|
SELECT place_id from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
||||||
END LOOP;
|
EXIT WHEN NEW.parent_place_id is not NULL;
|
||||||
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
--DEBUG: RAISE WARNING 'Checked for addr:street in way (%)', NEW.parent_place_id;
|
||||||
--DEBUG: RAISE WARNING 'Checked for addr:street in way (%)', NEW.parent_place_id;
|
|
||||||
|
|
||||||
IF NEW.parent_place_id IS NULL AND location.address ? 'place' THEN
|
IF location.address ? 'place' THEN
|
||||||
address_street_word_ids := get_name_ids(make_standard_name(location.address->'place'));
|
address_street_word_ids := get_name_ids(make_standard_name(location.address->'place'));
|
||||||
IF address_street_word_ids IS NOT NULL THEN
|
IF address_street_word_ids IS NOT NULL THEN
|
||||||
FOR linkedplacex IN SELECT place_id from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
|
SELECT place_id from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
||||||
NEW.parent_place_id := linkedplacex.place_id;
|
EXIT WHEN NEW.parent_place_id is not NULL;
|
||||||
END LOOP;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
|
||||||
--DEBUG: RAISE WARNING 'Checked for addr:place in way (%)', NEW.parent_place_id;
|
--DEBUG: RAISE WARNING 'Checked for addr:place in way (%)', NEW.parent_place_id;
|
||||||
|
END IF;
|
||||||
|
|
||||||
-- Is the WAY part of a relation
|
-- Is the WAY part of a relation
|
||||||
IF NEW.parent_place_id IS NULL THEN
|
FOR relation IN select * from planet_osm_rels where parts @> ARRAY[location.osm_id] and members @> ARRAY['w'||location.osm_id]
|
||||||
FOR relation IN select * from planet_osm_rels where parts @> ARRAY[location.osm_id] and members @> ARRAY['w'||location.osm_id]
|
LOOP
|
||||||
LOOP
|
-- At the moment we only process one type of relation - associatedStreet
|
||||||
-- At the moment we only process one type of relation - associatedStreet
|
IF relation.tags @> ARRAY['associatedStreet'] AND array_upper(relation.members, 1) IS NOT NULL THEN
|
||||||
IF relation.tags @> ARRAY['associatedStreet'] AND array_upper(relation.members, 1) IS NOT NULL THEN
|
FOR i IN 1..array_upper(relation.members, 1) BY 2 LOOP
|
||||||
FOR i IN 1..array_upper(relation.members, 1) BY 2 LOOP
|
IF NEW.parent_place_id IS NULL AND relation.members[i+1] = 'street' THEN
|
||||||
IF NEW.parent_place_id IS NULL AND relation.members[i+1] = 'street' THEN
|
--RAISE WARNING 'node in way that is in a relation %',relation;
|
||||||
--RAISE WARNING 'node in way that is in a relation %',relation;
|
SELECT place_id from placex where osm_type='W' and osm_id = substring(relation.members[i],2,200)::bigint
|
||||||
SELECT place_id from placex where osm_type='W' and osm_id = substring(relation.members[i],2,200)::bigint
|
and rank_search = 26 and name is not null INTO NEW.parent_place_id;
|
||||||
and rank_search = 26 and name is not null INTO NEW.parent_place_id;
|
|
||||||
END IF;
|
|
||||||
END LOOP;
|
|
||||||
END IF;
|
END IF;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END IF;
|
END IF;
|
||||||
|
END LOOP;
|
||||||
|
EXIT WHEN NEW.parent_place_id is not null;
|
||||||
--DEBUG: RAISE WARNING 'Checked for street relation in way (%)', NEW.parent_place_id;
|
--DEBUG: RAISE WARNING 'Checked for street relation in way (%)', NEW.parent_place_id;
|
||||||
|
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Still nothing, just use the nearest road
|
-- Still nothing, just use the nearest road
|
||||||
IF NEW.parent_place_id IS NULL THEN
|
IF NEW.parent_place_id IS NULL THEN
|
||||||
FOR location IN SELECT place_id FROM getNearestRoadFeature(NEW.partition, place_centroid) LOOP
|
SELECT place_id FROM getNearestRoadFeature(NEW.partition, place_centroid) INTO NEW.parent_place_id;
|
||||||
NEW.parent_place_id := location.place_id;
|
|
||||||
END LOOP;
|
|
||||||
END IF;
|
END IF;
|
||||||
--DEBUG: RAISE WARNING 'Checked for nearest way (%)', NEW.parent_place_id;
|
--DEBUG: RAISE WARNING 'Checked for nearest way (%)', NEW.parent_place_id;
|
||||||
|
|
||||||
@@ -1555,7 +1572,8 @@ BEGIN
|
|||||||
IF NEW.parent_place_id IS NOT NULL THEN
|
IF NEW.parent_place_id IS NOT NULL THEN
|
||||||
|
|
||||||
-- Get the details of the parent road
|
-- Get the details of the parent road
|
||||||
select * from search_name where place_id = NEW.parent_place_id INTO location;
|
select s.country_code, s.name_vector, s.nameaddress_vector from search_name s
|
||||||
|
where s.place_id = NEW.parent_place_id INTO location;
|
||||||
NEW.country_code := location.country_code;
|
NEW.country_code := location.country_code;
|
||||||
--DEBUG: RAISE WARNING 'Got parent details from search name';
|
--DEBUG: RAISE WARNING 'Got parent details from search name';
|
||||||
|
|
||||||
@@ -1766,44 +1784,43 @@ BEGIN
|
|||||||
parent_place_id_rank = 0;
|
parent_place_id_rank = 0;
|
||||||
|
|
||||||
|
|
||||||
-- convert isin to array of tokenids
|
-- convert address store to array of tokenids
|
||||||
--DEBUG: RAISE WARNING 'Starting address search';
|
--DEBUG: RAISE WARNING 'Starting address search';
|
||||||
isin_tokens := '{}'::int[];
|
isin_tokens := '{}'::int[];
|
||||||
IF NEW.address IS NOT NULL THEN
|
IF NEW.address IS NOT NULL THEN
|
||||||
isin := avals(NEW.address);
|
FOR addr_item IN SELECT * FROM each(NEW.address)
|
||||||
IF array_upper(isin, 1) IS NOT NULL THEN
|
LOOP
|
||||||
FOR i IN 1..array_upper(isin, 1) LOOP
|
IF addr_item.key IN ('city', 'tiger:county', 'state', 'suburb', 'province', 'district', 'region', 'county', 'municipality', 'hamlet', 'village', 'subdistrict', 'town', 'neighbourhood', 'quarter', 'parish') THEN
|
||||||
-- TODO further split terms with comma and semicolon
|
address_street_word_id := get_name_id(make_standard_name(addr_item.value));
|
||||||
address_street_word_id := get_name_id(make_standard_name(isin[i]));
|
|
||||||
IF address_street_word_id IS NOT NULL AND NOT(ARRAY[address_street_word_id] <@ isin_tokens) THEN
|
IF address_street_word_id IS NOT NULL AND NOT(ARRAY[address_street_word_id] <@ isin_tokens) THEN
|
||||||
nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
|
|
||||||
isin_tokens := isin_tokens || address_street_word_id;
|
isin_tokens := isin_tokens || address_street_word_id;
|
||||||
END IF;
|
END IF;
|
||||||
|
address_street_word_id := get_word_id(make_standard_name(addr_item.value));
|
||||||
-- merge word into address vector
|
|
||||||
address_street_word_id := get_word_id(make_standard_name(isin[i]));
|
|
||||||
IF address_street_word_id IS NOT NULL THEN
|
IF address_street_word_id IS NOT NULL THEN
|
||||||
nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
|
nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
|
||||||
END IF;
|
END IF;
|
||||||
END LOOP;
|
END IF;
|
||||||
END IF;
|
IF addr_item.key = 'is_in' THEN
|
||||||
END IF;
|
-- is_in items need splitting
|
||||||
|
isin := regexp_split_to_array(addr_item.value, E'[;,]');
|
||||||
|
IF array_upper(isin, 1) IS NOT NULL THEN
|
||||||
|
FOR i IN 1..array_upper(isin, 1) LOOP
|
||||||
|
address_street_word_id := get_name_id(make_standard_name(isin[i]));
|
||||||
|
IF address_street_word_id IS NOT NULL AND NOT(ARRAY[address_street_word_id] <@ isin_tokens) THEN
|
||||||
|
isin_tokens := isin_tokens || address_street_word_id;
|
||||||
|
END IF;
|
||||||
|
|
||||||
-- %NOTIGERDATA% IF 0 THEN
|
-- merge word into address vector
|
||||||
-- for the USA we have an additional address table. Merge in zip codes from there too
|
address_street_word_id := get_word_id(make_standard_name(isin[i]));
|
||||||
IF NEW.rank_search = 26 AND NEW.country_code = 'us' THEN
|
IF address_street_word_id IS NOT NULL THEN
|
||||||
FOR location IN SELECT distinct postcode from location_property_tiger where parent_place_id = NEW.place_id LOOP
|
nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
|
||||||
address_street_word_id := get_name_id(make_standard_name(location.postcode));
|
END IF;
|
||||||
nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
|
END LOOP;
|
||||||
isin_tokens := isin_tokens || address_street_word_id;
|
END IF;
|
||||||
|
END IF;
|
||||||
-- also merge in the single word version
|
|
||||||
address_street_word_id := get_word_id(make_standard_name(location.postcode));
|
|
||||||
nameaddress_vector := array_merge(nameaddress_vector, ARRAY[address_street_word_id]);
|
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END IF;
|
END IF;
|
||||||
--DEBUG: RAISE WARNING 'Tiger postcodes collected';
|
nameaddress_vector := array_merge(nameaddress_vector, isin_tokens);
|
||||||
-- %NOTIGERDATA% END IF;
|
|
||||||
|
|
||||||
-- RAISE WARNING 'ISIN: %', isin_tokens;
|
-- RAISE WARNING 'ISIN: %', isin_tokens;
|
||||||
|
|
||||||
@@ -1884,67 +1901,6 @@ BEGIN
|
|||||||
END LOOP;
|
END LOOP;
|
||||||
--DEBUG: RAISE WARNING 'address computed';
|
--DEBUG: RAISE WARNING 'address computed';
|
||||||
|
|
||||||
-- try using the isin value to find parent places
|
|
||||||
IF array_upper(isin_tokens, 1) IS NOT NULL THEN
|
|
||||||
FOR i IN 1..array_upper(isin_tokens, 1) LOOP
|
|
||||||
--RAISE WARNING ' getNearestNamedFeature: % % % %',NEW.partition, place_centroid, search_maxrank, isin_tokens[i];
|
|
||||||
IF NOT ARRAY[isin_tokens[i]] <@ nameaddress_vector THEN
|
|
||||||
|
|
||||||
FOR location IN SELECT * from getNearestNamedFeature(NEW.partition, place_centroid, search_maxrank, isin_tokens[i]) LOOP
|
|
||||||
|
|
||||||
--RAISE WARNING ' ISIN: %',location;
|
|
||||||
|
|
||||||
IF location.rank_search > 4 THEN
|
|
||||||
nameaddress_vector := array_merge(nameaddress_vector, location.keywords::integer[]);
|
|
||||||
INSERT INTO place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address)
|
|
||||||
VALUES (NEW.place_id, location.place_id, false, NOT address_havelevel[location.rank_address], location.distance, location.rank_address);
|
|
||||||
IF NEW.postcode is null AND location.postcode is not null
|
|
||||||
AND NOT address_havelevel[location.rank_address] THEN
|
|
||||||
NEW.postcode := location.postcode;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
address_havelevel[location.rank_address] := true;
|
|
||||||
|
|
||||||
IF location.rank_address > parent_place_id_rank THEN
|
|
||||||
NEW.parent_place_id = location.place_id;
|
|
||||||
parent_place_id_rank = location.rank_address;
|
|
||||||
END IF;
|
|
||||||
END IF;
|
|
||||||
END LOOP;
|
|
||||||
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END LOOP;
|
|
||||||
END IF;
|
|
||||||
--DEBUG: RAISE WARNING 'isin tokens processed';
|
|
||||||
|
|
||||||
-- for long ways we should add search terms for the entire length
|
|
||||||
IF st_length(NEW.geometry) > 0.05 THEN
|
|
||||||
|
|
||||||
location_rank_search := 0;
|
|
||||||
location_distance := 0;
|
|
||||||
|
|
||||||
FOR location IN SELECT * from getNearFeatures(NEW.partition, NEW.geometry, search_maxrank, isin_tokens) LOOP
|
|
||||||
|
|
||||||
IF location.rank_address != location_rank_search THEN
|
|
||||||
location_rank_search := location.rank_address;
|
|
||||||
location_distance := location.distance * 1.5;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF location.rank_search > 4 AND location.distance < location_distance THEN
|
|
||||||
|
|
||||||
-- Add it to the list of search terms
|
|
||||||
nameaddress_vector := array_merge(nameaddress_vector, location.keywords::integer[]);
|
|
||||||
INSERT INTO place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address)
|
|
||||||
VALUES (NEW.place_id, location.place_id, true, false, location.distance, location.rank_address);
|
|
||||||
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END LOOP;
|
|
||||||
|
|
||||||
END IF;
|
|
||||||
--DEBUG: RAISE WARNING 'search terms for long ways added';
|
|
||||||
|
|
||||||
IF NEW.address is not null AND NEW.address ? 'postcode'
|
IF NEW.address is not null AND NEW.address ? 'postcode'
|
||||||
AND NEW.address->'postcode' not similar to '%(,|;)%' THEN
|
AND NEW.address->'postcode' not similar to '%(,|;)%' THEN
|
||||||
NEW.postcode := upper(trim(NEW.address->'postcode'));
|
NEW.postcode := upper(trim(NEW.address->'postcode'));
|
||||||
@@ -2200,7 +2156,9 @@ BEGIN
|
|||||||
|
|
||||||
-- To paraphrase, if there isn't an existing item, OR if the admin level has changed
|
-- To paraphrase, if there isn't an existing item, OR if the admin level has changed
|
||||||
IF existingplacex.osm_type IS NULL OR
|
IF existingplacex.osm_type IS NULL OR
|
||||||
(coalesce(existingplacex.admin_level, 15) != coalesce(NEW.admin_level, 15) AND existingplacex.class = 'boundary' AND existingplacex.type = 'administrative')
|
(existingplacex.class = 'boundary' AND
|
||||||
|
((coalesce(existingplacex.admin_level, 15) != coalesce(NEW.admin_level, 15) AND existingplacex.type = 'administrative') OR
|
||||||
|
(existingplacex.type != NEW.type)))
|
||||||
THEN
|
THEN
|
||||||
|
|
||||||
IF existingplacex.osm_type IS NOT NULL THEN
|
IF existingplacex.osm_type IS NOT NULL THEN
|
||||||
|
|||||||
@@ -14,13 +14,29 @@ CREATE INDEX idx_placex_rank_search ON placex USING BTREE (rank_search) {ts:sear
|
|||||||
CREATE INDEX idx_placex_rank_address ON placex USING BTREE (rank_address) {ts:search-index};
|
CREATE INDEX idx_placex_rank_address ON placex USING BTREE (rank_address) {ts:search-index};
|
||||||
CREATE INDEX idx_placex_pendingsector ON placex USING BTREE (rank_search,geometry_sector) {ts:address-index} where indexed_status > 0;
|
CREATE INDEX idx_placex_pendingsector ON placex USING BTREE (rank_search,geometry_sector) {ts:address-index} where indexed_status > 0;
|
||||||
CREATE INDEX idx_placex_parent_place_id ON placex USING BTREE (parent_place_id) {ts:search-index} where parent_place_id IS NOT NULL;
|
CREATE INDEX idx_placex_parent_place_id ON placex USING BTREE (parent_place_id) {ts:search-index} where parent_place_id IS NOT NULL;
|
||||||
CREATE INDEX idx_placex_reverse_geometry ON placex USING gist (geometry) {ts:search-index} where rank_search != 28 and (name is not null or housenumber is not null) and class not in ('waterway','railway','tunnel','bridge','man_made');
|
|
||||||
|
CREATE INDEX idx_placex_geometry_reverse_lookupPoint
|
||||||
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
|
WHERE (name is not null or housenumber is not null or rank_address between 26 and 27)
|
||||||
|
AND class not in ('railway','tunnel','bridge','man_made')
|
||||||
|
AND rank_address >= 26 AND indexed_status = 0 AND linked_place_id is null;
|
||||||
|
CREATE INDEX idx_placex_geometry_reverse_lookupPolygon
|
||||||
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
|
WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
|
||||||
|
AND rank_address between 4 and 25 AND type != 'postcode'
|
||||||
|
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
||||||
|
CREATE INDEX idx_placex_geometry_reverse_placeNode
|
||||||
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
|
WHERE osm_type = 'N' AND rank_search between 5 and 25
|
||||||
|
AND class = 'place' AND type != 'postcode'
|
||||||
|
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
||||||
|
|
||||||
|
GRANT SELECT ON table country_osm_grid to "{www-user}";
|
||||||
|
|
||||||
CREATE INDEX idx_location_area_country_place_id ON location_area_country USING BTREE (place_id) {ts:address-index};
|
CREATE INDEX idx_location_area_country_place_id ON location_area_country USING BTREE (place_id) {ts:address-index};
|
||||||
|
|
||||||
CREATE INDEX idx_osmline_parent_place_id ON location_property_osmline USING BTREE (parent_place_id) {ts:search-index};
|
CREATE INDEX idx_osmline_parent_place_id ON location_property_osmline USING BTREE (parent_place_id) {ts:search-index};
|
||||||
|
|
||||||
CREATE INDEX idx_search_name_country_centroid ON search_name_country USING GIST (centroid) {ts:address-index};
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS place_id_idx;
|
DROP INDEX IF EXISTS place_id_idx;
|
||||||
CREATE UNIQUE INDEX idx_place_osm_unique on place using btree(osm_id,osm_type,class,type) {ts:address-index};
|
CREATE UNIQUE INDEX idx_place_osm_unique on place using btree(osm_id,osm_type,class,type) {ts:address-index};
|
||||||
|
|
||||||
|
|||||||
@@ -84,38 +84,6 @@ END
|
|||||||
$$
|
$$
|
||||||
LANGUAGE plpgsql;
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
create or replace function getNearestNamedFeature(in_partition INTEGER, point GEOMETRY, maxrank INTEGER, isin_token INTEGER) RETURNS setof nearfeature AS $$
|
|
||||||
DECLARE
|
|
||||||
r nearfeature%rowtype;
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
-- start
|
|
||||||
IF in_partition = -partition- THEN
|
|
||||||
FOR r IN
|
|
||||||
SELECT place_id, name_vector, address_rank, search_rank,
|
|
||||||
ST_Distance(centroid, point) as distance, null as isguess
|
|
||||||
FROM search_name_-partition-
|
|
||||||
WHERE name_vector @> ARRAY[isin_token]
|
|
||||||
AND search_rank < maxrank
|
|
||||||
UNION ALL
|
|
||||||
SELECT place_id, name_vector, address_rank, search_rank,
|
|
||||||
ST_Distance(centroid, point) as distance, null as isguess
|
|
||||||
FROM search_name_country
|
|
||||||
WHERE name_vector @> ARRAY[isin_token]
|
|
||||||
AND search_rank < maxrank
|
|
||||||
ORDER BY distance ASC limit 1
|
|
||||||
LOOP
|
|
||||||
RETURN NEXT r;
|
|
||||||
END LOOP;
|
|
||||||
RETURN;
|
|
||||||
END IF;
|
|
||||||
-- end
|
|
||||||
|
|
||||||
RAISE EXCEPTION 'Unknown partition %', in_partition;
|
|
||||||
END
|
|
||||||
$$
|
|
||||||
LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
create or replace function getNearestNamedRoadFeature(in_partition INTEGER, point GEOMETRY, isin_token INTEGER[])
|
create or replace function getNearestNamedRoadFeature(in_partition INTEGER, point GEOMETRY, isin_token INTEGER[])
|
||||||
RETURNS setof nearfeature AS $$
|
RETURNS setof nearfeature AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
@@ -128,8 +96,8 @@ BEGIN
|
|||||||
SELECT place_id, name_vector, address_rank, search_rank,
|
SELECT place_id, name_vector, address_rank, search_rank,
|
||||||
ST_Distance(centroid, point) as distance, null as isguess
|
ST_Distance(centroid, point) as distance, null as isguess
|
||||||
FROM search_name_-partition-
|
FROM search_name_-partition-
|
||||||
WHERE name_vector @> isin_token
|
WHERE name_vector && isin_token
|
||||||
AND ST_DWithin(centroid, point, 0.01)
|
AND ST_DWithin(centroid, point, 0.015)
|
||||||
AND search_rank between 26 and 27
|
AND search_rank between 26 and 27
|
||||||
ORDER BY distance ASC limit 1
|
ORDER BY distance ASC limit 1
|
||||||
LOOP
|
LOOP
|
||||||
@@ -156,8 +124,8 @@ BEGIN
|
|||||||
SELECT place_id, name_vector, address_rank, search_rank,
|
SELECT place_id, name_vector, address_rank, search_rank,
|
||||||
ST_Distance(centroid, point) as distance, null as isguess
|
ST_Distance(centroid, point) as distance, null as isguess
|
||||||
FROM search_name_-partition-
|
FROM search_name_-partition-
|
||||||
WHERE name_vector @> isin_token
|
WHERE name_vector && isin_token
|
||||||
AND ST_DWithin(centroid, point, 0.03)
|
AND ST_DWithin(centroid, point, 0.04)
|
||||||
AND search_rank between 16 and 22
|
AND search_rank between 16 and 22
|
||||||
ORDER BY distance ASC limit 1
|
ORDER BY distance ASC limit 1
|
||||||
LOOP
|
LOOP
|
||||||
@@ -185,15 +153,6 @@ BEGIN
|
|||||||
INSERT INTO search_name (place_id, search_rank, address_rank, importance, country_code, name_vector, nameaddress_vector, centroid)
|
INSERT INTO search_name (place_id, search_rank, address_rank, importance, country_code, name_vector, nameaddress_vector, centroid)
|
||||||
values (in_place_id, in_rank_search, in_rank_address, in_importance, in_country_code, in_name_vector, in_nameaddress_vector, in_centroid);
|
values (in_place_id, in_rank_search, in_rank_address, in_importance, in_country_code, in_name_vector, in_nameaddress_vector, in_centroid);
|
||||||
|
|
||||||
IF in_rank_search <= 4 THEN
|
|
||||||
DELETE FROM search_name_country WHERE place_id = in_place_id;
|
|
||||||
IF in_rank_address > 0 THEN
|
|
||||||
INSERT INTO search_name_country (place_id, search_rank, address_rank, name_vector, centroid)
|
|
||||||
values (in_place_id, in_rank_search, in_rank_address, in_name_vector, in_geometry);
|
|
||||||
END IF;
|
|
||||||
RETURN TRUE;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- start
|
-- start
|
||||||
IF in_partition = -partition- THEN
|
IF in_partition = -partition- THEN
|
||||||
DELETE FROM search_name_-partition- values WHERE place_id = in_place_id;
|
DELETE FROM search_name_-partition- values WHERE place_id = in_place_id;
|
||||||
@@ -216,7 +175,6 @@ DECLARE
|
|||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
DELETE from search_name WHERE place_id = in_place_id;
|
DELETE from search_name WHERE place_id = in_place_id;
|
||||||
DELETE from search_name_country WHERE place_id = in_place_id;
|
|
||||||
|
|
||||||
-- start
|
-- start
|
||||||
IF in_partition = -partition- THEN
|
IF in_partition = -partition- THEN
|
||||||
|
|||||||
@@ -38,10 +38,6 @@ CREATE TABLE search_name_blank (
|
|||||||
CREATE TABLE location_area_country () INHERITS (location_area_large) {ts:address-data};
|
CREATE TABLE location_area_country () INHERITS (location_area_large) {ts:address-data};
|
||||||
CREATE INDEX idx_location_area_country_geometry ON location_area_country USING GIST (geometry) {ts:address-index};
|
CREATE INDEX idx_location_area_country_geometry ON location_area_country USING GIST (geometry) {ts:address-index};
|
||||||
|
|
||||||
CREATE TABLE search_name_country () INHERITS (search_name_blank) {ts:address-data};
|
|
||||||
CREATE INDEX idx_search_name_country_place_id ON search_name_country USING BTREE (place_id) {ts:address-index};
|
|
||||||
CREATE INDEX idx_search_name_country_name_vector ON search_name_country USING GIN (name_vector) WITH (fastupdate = off) {ts:address-index};
|
|
||||||
|
|
||||||
-- start
|
-- start
|
||||||
CREATE TABLE location_area_large_-partition- () INHERITS (location_area_large) {ts:address-data};
|
CREATE TABLE location_area_large_-partition- () INHERITS (location_area_large) {ts:address-data};
|
||||||
CREATE INDEX idx_location_area_large_-partition-_place_id ON location_area_large_-partition- USING BTREE (place_id) {ts:address-index};
|
CREATE INDEX idx_location_area_large_-partition-_place_id ON location_area_large_-partition- USING BTREE (place_id) {ts:address-index};
|
||||||
@@ -52,6 +48,7 @@ CREATE INDEX idx_search_name_-partition-_place_id ON search_name_-partition- USI
|
|||||||
CREATE INDEX idx_search_name_-partition-_centroid ON search_name_-partition- USING GIST (centroid) {ts:address-index};
|
CREATE INDEX idx_search_name_-partition-_centroid ON search_name_-partition- USING GIST (centroid) {ts:address-index};
|
||||||
CREATE INDEX idx_search_name_-partition-_name_vector ON search_name_-partition- USING GIN (name_vector) WITH (fastupdate = off) {ts:address-index};
|
CREATE INDEX idx_search_name_-partition-_name_vector ON search_name_-partition- USING GIN (name_vector) WITH (fastupdate = off) {ts:address-index};
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS location_road_-partition-;
|
||||||
CREATE TABLE location_road_-partition- (
|
CREATE TABLE location_road_-partition- (
|
||||||
place_id BIGINT,
|
place_id BIGINT,
|
||||||
partition SMALLINT,
|
partition SMALLINT,
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ The tests can be configured with a set of environment variables:
|
|||||||
the test databases (db tests)
|
the test databases (db tests)
|
||||||
* `TEST_DB` - name of test database (db tests)
|
* `TEST_DB` - name of test database (db tests)
|
||||||
* `ABI_TEST_DB` - name of the database containing the API test data (api tests)
|
* `ABI_TEST_DB` - name of the database containing the API test data (api tests)
|
||||||
|
* `DB_HOST` - (optional) hostname of database host
|
||||||
|
* `DB_USER` - (optional) username of database login
|
||||||
|
* `DB_PASS` - (optional) password for database login
|
||||||
|
* `SERVER_MODULE_PATH` - (optional) path on the Postgres server to Nominatim
|
||||||
|
* module shared library file
|
||||||
* `TEST_SETTINGS_TEMPLATE` - file to write temporary Nominatim settings to
|
* `TEST_SETTINGS_TEMPLATE` - file to write temporary Nominatim settings to
|
||||||
* `REMOVE_TEMPLATE` - if true, the template database will not be reused during
|
* `REMOVE_TEMPLATE` - if true, the template database will not be reused during
|
||||||
the next run. Reusing the base templates speeds up tests
|
the next run. Reusing the base templates speeds up tests
|
||||||
|
|||||||
56
test/bdd/api/details/params.feature
Normal file
56
test/bdd/api/details/params.feature
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
@APIDB
|
||||||
|
Feature: Object details
|
||||||
|
Testing different parameter options for details API.
|
||||||
|
|
||||||
|
Scenario: JSON Details
|
||||||
|
When sending json details query for W78099902
|
||||||
|
Then the result is valid json
|
||||||
|
And result has attributes geometry
|
||||||
|
And result has not attributes keywords,address,linked_places,parentof
|
||||||
|
|
||||||
|
Scenario: JSON Details with keywords
|
||||||
|
When sending json details query for W78099902
|
||||||
|
| keywords |
|
||||||
|
| 1 |
|
||||||
|
Then the result is valid json
|
||||||
|
And result has attributes keywords
|
||||||
|
|
||||||
|
Scenario: JSON Details with addressdetails
|
||||||
|
When sending json details query for W78099902
|
||||||
|
| addressdetails |
|
||||||
|
| 1 |
|
||||||
|
Then the result is valid json
|
||||||
|
And result has attributes address
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Scenario: JSON Details with hierarchy
|
||||||
|
When sending json details query for W78099902
|
||||||
|
| hierarchy |
|
||||||
|
| 1 |
|
||||||
|
Then the result is valid json
|
||||||
|
And result has attributes hierarchy
|
||||||
|
|
||||||
|
Scenario: JSON Details with linkedplaces
|
||||||
|
When sending json details query for R123924
|
||||||
|
| linkedplaces |
|
||||||
|
| 1 |
|
||||||
|
Then the result is valid json
|
||||||
|
|
||||||
|
Scenario Outline: HTML Details with keywords
|
||||||
|
When sending html details query for <osmid>
|
||||||
|
| keywords |
|
||||||
|
| 1 |
|
||||||
|
Then the result is valid html
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| osmid |
|
||||||
|
| W78099902 |
|
||||||
|
| N3121929846 |
|
||||||
|
|
||||||
|
|
||||||
@@ -3,18 +3,34 @@ Feature: Object details
|
|||||||
Check details page for correctness
|
Check details page for correctness
|
||||||
|
|
||||||
Scenario Outline: Details via OSM id
|
Scenario Outline: Details via OSM id
|
||||||
When sending details query for <object>
|
When sending <format> details query for <object>
|
||||||
Then the result is valid html
|
Then the result is valid <format>
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| object |
|
| format | object |
|
||||||
| 492887 |
|
| html | 492887 |
|
||||||
| N4267356889 |
|
| json | 492887 |
|
||||||
| W230804120 |
|
| html | N4267356889 |
|
||||||
| R123924 |
|
| json | N4267356889 |
|
||||||
|
| html | W230804120 |
|
||||||
|
| json | W230804120 |
|
||||||
|
| html | R123924 |
|
||||||
|
| json | R123924 |
|
||||||
|
|
||||||
|
Scenario Outline: Details via unknown OSM id
|
||||||
|
When sending <format> details query for <object>
|
||||||
|
Then a HTTP 400 is returned
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| format | object |
|
||||||
|
| html | 1 |
|
||||||
|
| json | 1 |
|
||||||
|
| html | R1 |
|
||||||
|
| json | R1 |
|
||||||
|
|
||||||
Scenario: Details with keywords
|
Scenario: Details with keywords
|
||||||
When sending details query for W78099902
|
When sending details query for W78099902
|
||||||
| keywords |
|
| keywords |
|
||||||
| 1 |
|
| 1 |
|
||||||
Then the result is valid html
|
Then the result is valid html
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ Feature: Places by osm_type and osm_id Tests
|
|||||||
And exactly 3 results are returned
|
And exactly 3 results are returned
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| format |
|
| format |
|
||||||
| xml |
|
| xml |
|
||||||
| json |
|
| json |
|
||||||
|
| geojson |
|
||||||
|
|
||||||
Scenario: address lookup for non-existing or invalid node, way, relation
|
Scenario: address lookup for non-existing or invalid node, way, relation
|
||||||
When sending xml lookup query for X99,,N0,nN158845944,ABC,,W9
|
When sending xml lookup query for X99,,N0,nN158845944,ABC,,W9
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Feature: Parameters for Reverse API
|
|||||||
| format |
|
| format |
|
||||||
| json |
|
| json |
|
||||||
| jsonv2 |
|
| jsonv2 |
|
||||||
|
| geojson |
|
||||||
| xml |
|
| xml |
|
||||||
|
|
||||||
Scenario Outline: Coordinates must be floating-point numbers
|
Scenario Outline: Coordinates must be floating-point numbers
|
||||||
@@ -35,6 +36,7 @@ Feature: Parameters for Reverse API
|
|||||||
| xml |
|
| xml |
|
||||||
| json |
|
| json |
|
||||||
| jsonv2 |
|
| jsonv2 |
|
||||||
|
| geojson |
|
||||||
|
|
||||||
Scenario Outline: Reverse Geocoding with namedetails
|
Scenario Outline: Reverse Geocoding with namedetails
|
||||||
When sending <format> reverse coordinates 10.776455623137625,106.70175343751907
|
When sending <format> reverse coordinates 10.776455623137625,106.70175343751907
|
||||||
@@ -47,6 +49,7 @@ Feature: Parameters for Reverse API
|
|||||||
| xml |
|
| xml |
|
||||||
| json |
|
| json |
|
||||||
| jsonv2 |
|
| jsonv2 |
|
||||||
|
| geojson |
|
||||||
|
|
||||||
Scenario Outline: Reverse Geocoding contains TEXT geometry
|
Scenario Outline: Reverse Geocoding contains TEXT geometry
|
||||||
When sending <format> reverse coordinates 47.165989816710066,9.515774846076965
|
When sending <format> reverse coordinates 47.165989816710066,9.515774846076965
|
||||||
@@ -107,5 +110,18 @@ Feature: Parameters for Reverse API
|
|||||||
| xml | geojson |
|
| xml | geojson |
|
||||||
| json | geojson |
|
| json | geojson |
|
||||||
| jsonv2 | geojson |
|
| jsonv2 | geojson |
|
||||||
|
| geojson | geojson |
|
||||||
|
|
||||||
|
Scenario Outline: Reverse Geocoding in geojson format contains no non-geojson geometry
|
||||||
|
When sending geojson reverse coordinates 47.165989816710066,9.515774846076965
|
||||||
|
| polygon_text | polygon | polygon_svg | polygon_geokml |
|
||||||
|
| 1 | 1 | 1 | 1 |
|
||||||
|
Then result 0 has not attributes <response_attribute>
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| response_attribute |
|
||||||
|
| geotext |
|
||||||
|
| polygonpoints |
|
||||||
|
| svg |
|
||||||
|
| geokml |
|
||||||
|
|
||||||
|
|||||||
@@ -51,3 +51,43 @@ Feature: Reverse geocoding
|
|||||||
Then results contain
|
Then results contain
|
||||||
| display_name |
|
| display_name |
|
||||||
| Freie und Hansestadt Hamburg, Deutschland |
|
| Freie und Hansestadt Hamburg, Deutschland |
|
||||||
|
|
||||||
|
Scenario: When slightly outside town, the town is not shown
|
||||||
|
When sending jsonv2 reverse coordinates -32.122,-56.114
|
||||||
|
| zoom |
|
||||||
|
| 15 |
|
||||||
|
Then results contain
|
||||||
|
| display_name |
|
||||||
|
| Tacuarembó, Uruguay |
|
||||||
|
|
||||||
|
Scenario Outline: Zoom levels below 5 result in country
|
||||||
|
When sending jsonv2 reverse coordinates -33.28,-56.29
|
||||||
|
| zoom |
|
||||||
|
| <zoom> |
|
||||||
|
Then results contain
|
||||||
|
| display_name |
|
||||||
|
| Uruguay |
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| zoom |
|
||||||
|
| 0 |
|
||||||
|
| 1 |
|
||||||
|
| 2 |
|
||||||
|
| 3 |
|
||||||
|
| 4 |
|
||||||
|
|
||||||
|
Scenario: When on a street, the closest interpolation is shown
|
||||||
|
When sending jsonv2 reverse coordinates -33.2309430210215,-54.38126470020989
|
||||||
|
| zoom |
|
||||||
|
| 18 |
|
||||||
|
Then results contain
|
||||||
|
| display_name |
|
||||||
|
| 1429, Andrés Areguati, Treinta y Tres, 33000, Uruguay |
|
||||||
|
|
||||||
|
Scenario: When on a street with zoom 18, the closest housenumber is returned
|
||||||
|
When sending jsonv2 reverse coordinates 53.551826690895226,9.885258475318201
|
||||||
|
| zoom |
|
||||||
|
| 18 |
|
||||||
|
Then result addresses contain
|
||||||
|
| house_number |
|
||||||
|
| 33 |
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ Feature: Simple Reverse Tests
|
|||||||
Then the result is valid json
|
Then the result is valid json
|
||||||
When sending jsonv2 reverse coordinates <lat>,<lon>
|
When sending jsonv2 reverse coordinates <lat>,<lon>
|
||||||
Then the result is valid json
|
Then the result is valid json
|
||||||
|
When sending geojson reverse coordinates <lat>,<lon>
|
||||||
|
Then the result is valid geojson
|
||||||
When sending html reverse coordinates <lat>,<lon>
|
When sending html reverse coordinates <lat>,<lon>
|
||||||
Then the result is valid html
|
Then the result is valid html
|
||||||
|
|
||||||
@@ -42,6 +44,14 @@ Feature: Simple Reverse Tests
|
|||||||
| param | value |
|
| param | value |
|
||||||
| <parameter> | <value> |
|
| <parameter> | <value> |
|
||||||
Then the result is valid json
|
Then the result is valid json
|
||||||
|
When sending geojson reverse coordinates 53.603,10.041
|
||||||
|
| param | value |
|
||||||
|
| <parameter> | <value> |
|
||||||
|
Then the result is valid geojson
|
||||||
|
When sending geocodejson reverse coordinates 53.603,10.041
|
||||||
|
| param | value |
|
||||||
|
| <parameter> | <value> |
|
||||||
|
Then the result is valid geocodejson
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| parameter | value |
|
| parameter | value |
|
||||||
@@ -60,23 +70,25 @@ Feature: Simple Reverse Tests
|
|||||||
When sending <format> reverse coordinates 67.3245,0.456
|
When sending <format> reverse coordinates 67.3245,0.456
|
||||||
| json_callback |
|
| json_callback |
|
||||||
| foo |
|
| foo |
|
||||||
Then the result is valid json
|
Then the result is valid <outformat>
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| format |
|
| format | outformat |
|
||||||
| json |
|
| json | json |
|
||||||
| jsonv2 |
|
| jsonv2 | json |
|
||||||
|
| geojson | geojson |
|
||||||
|
|
||||||
Scenario Outline: Boundingbox is returned
|
Scenario Outline: Boundingbox is returned
|
||||||
When sending <format> reverse coordinates 14.62,108.1
|
When sending <format> reverse coordinates 14.62,108.1
|
||||||
| zoom |
|
| zoom |
|
||||||
| 4 |
|
| 8 |
|
||||||
Then result has bounding box in 9,20,102,113
|
Then result has bounding box in 9,20,102,113
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| format |
|
| format |
|
||||||
| json |
|
| json |
|
||||||
| jsonv2 |
|
| jsonv2 |
|
||||||
|
| geojson |
|
||||||
| xml |
|
| xml |
|
||||||
|
|
||||||
Scenario Outline: Reverse-geocoding with zoom
|
Scenario Outline: Reverse-geocoding with zoom
|
||||||
@@ -89,6 +101,7 @@ Feature: Simple Reverse Tests
|
|||||||
| format |
|
| format |
|
||||||
| json |
|
| json |
|
||||||
| jsonv2 |
|
| jsonv2 |
|
||||||
|
| geojson |
|
||||||
| html |
|
| html |
|
||||||
| xml |
|
| xml |
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ Feature: Search queries
|
|||||||
When sending json search query "restaurant"
|
When sending json search query "restaurant"
|
||||||
| bounded | viewbox |
|
| bounded | viewbox |
|
||||||
| 1 | 9.93027,53.61634,10.10073,53.54500 |
|
| 1 | 9.93027,53.61634,10.10073,53.54500 |
|
||||||
Then result has bounding box in 53.54500,53.61634,9.93027,10.10073
|
Then result has centroid in 53.54500,53.61634,9.93027,10.10073
|
||||||
|
|
||||||
Scenario: Prefer results within viewbox
|
Scenario: Prefer results within viewbox
|
||||||
When sending json search query "25 de Mayo" with address
|
When sending json search query "25 de Mayo" with address
|
||||||
@@ -276,6 +276,7 @@ Feature: Search queries
|
|||||||
| xml |
|
| xml |
|
||||||
| json |
|
| json |
|
||||||
| jsonv2 |
|
| jsonv2 |
|
||||||
|
| geojson |
|
||||||
|
|
||||||
Scenario Outline: Search with namedetails
|
Scenario Outline: Search with namedetails
|
||||||
When sending <format> search query "Hauptstr"
|
When sending <format> search query "Hauptstr"
|
||||||
@@ -288,6 +289,7 @@ Feature: Search queries
|
|||||||
| xml |
|
| xml |
|
||||||
| json |
|
| json |
|
||||||
| jsonv2 |
|
| jsonv2 |
|
||||||
|
| geojson |
|
||||||
|
|
||||||
Scenario Outline: Search result with contains TEXT geometry
|
Scenario Outline: Search result with contains TEXT geometry
|
||||||
When sending <format> search query "Highmore"
|
When sending <format> search query "Highmore"
|
||||||
@@ -348,6 +350,20 @@ Feature: Search queries
|
|||||||
| xml | geojson |
|
| xml | geojson |
|
||||||
| json | geojson |
|
| json | geojson |
|
||||||
| jsonv2 | geojson |
|
| jsonv2 | geojson |
|
||||||
|
| geojson | geojson |
|
||||||
|
|
||||||
|
Scenario Outline: Search result in geojson format contains no non-geojson geometry
|
||||||
|
When sending geojson search query "Highmore"
|
||||||
|
| polygon_text | polygon | polygon_svg | polygon_geokml |
|
||||||
|
| 1 | 1 | 1 | 1 |
|
||||||
|
Then result 0 has not attributes <response_attribute>
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| response_attribute |
|
||||||
|
| geotext |
|
||||||
|
| polygonpoints |
|
||||||
|
| svg |
|
||||||
|
| geokml |
|
||||||
|
|
||||||
Scenario: Search along a route
|
Scenario: Search along a route
|
||||||
When sending json search query "restaurant" with address
|
When sending json search query "restaurant" with address
|
||||||
@@ -356,3 +372,5 @@ Feature: Search queries
|
|||||||
Then result addresses contain
|
Then result addresses contain
|
||||||
| city |
|
| city |
|
||||||
| Rapid City |
|
| Rapid City |
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,14 @@ Feature: Simple Tests
|
|||||||
| param | value |
|
| param | value |
|
||||||
| <parameter> | <value> |
|
| <parameter> | <value> |
|
||||||
Then at least 1 result is returned
|
Then at least 1 result is returned
|
||||||
|
When sending geojson search query "Hamburg"
|
||||||
|
| param | value |
|
||||||
|
| <parameter> | <value> |
|
||||||
|
Then at least 1 result is returned
|
||||||
|
When sending geocodejson search query "Hamburg"
|
||||||
|
| param | value |
|
||||||
|
| <parameter> | <value> |
|
||||||
|
Then at least 1 result is returned
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| parameter | value |
|
| parameter | value |
|
||||||
@@ -68,6 +76,8 @@ Feature: Simple Tests
|
|||||||
Then the result is valid json
|
Then the result is valid json
|
||||||
When sending jsonv2 search query "<query>"
|
When sending jsonv2 search query "<query>"
|
||||||
Then the result is valid json
|
Then the result is valid json
|
||||||
|
When sending geojson search query "<query>"
|
||||||
|
Then the result is valid geojson
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| query |
|
| query |
|
||||||
@@ -223,6 +233,17 @@ Feature: Simple Tests
|
|||||||
When sending xml search query "Vaduz"
|
When sending xml search query "Vaduz"
|
||||||
| countrycodes |
|
| countrycodes |
|
||||||
| pl,1,,invalid,undefined,%3Cb%3E,bo,, |
|
| pl,1,,invalid,undefined,%3Cb%3E,bo,, |
|
||||||
Then result header contains
|
Then result header contains
|
||||||
| attr | value |
|
| attr | value |
|
||||||
| more_url | .*&countrycodes=pl%2Cbo&.* |
|
| more_url | .*&countrycodes=pl%2Cbo&.* |
|
||||||
|
|
||||||
|
Scenario Outline: Search with debug prints valid HTML
|
||||||
|
When sending html search query "<query>"
|
||||||
|
| extratags | addressdetails | namedetails | debug |
|
||||||
|
| 1 | 1 | 1 | 1 |
|
||||||
|
Then the result is valid html
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| query |
|
||||||
|
| 10, Alvierweg, 9490, Vaduz |
|
||||||
|
| Hamburg |
|
||||||
|
|||||||
17
test/bdd/api/status/failures.feature
Normal file
17
test/bdd/api/status/failures.feature
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@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: No database"
|
||||||
|
|
||||||
|
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 | No database |
|
||||||
|
And result has not attributes data_updated
|
||||||
16
test/bdd/api/status/simple.feature
Normal file
16
test/bdd/api/status/simple.feature
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
@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
|
||||||
@@ -20,3 +20,41 @@ Feature: Address computation
|
|||||||
| object | address | cached_rank_address |
|
| object | address | cached_rank_address |
|
||||||
| W1 | W10 | 10 |
|
| W1 | W10 | 10 |
|
||||||
| W1 | W11 | 10 |
|
| W1 | W11 | 10 |
|
||||||
|
|
||||||
|
Scenario: buildings with only addr:postcodes do not appear in the address of a way
|
||||||
|
Given the scene admin-areas
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | admin | addr+postcode | geometry |
|
||||||
|
| R1 | boundary | administrative | 6 | 112 | :b0 |
|
||||||
|
| R34 | boundary | administrative | 8 | 112 DE | :b1:E |
|
||||||
|
| R4 | boundary | administrative | 10 | 112 DE 34 | :b2:N |
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | geometry |
|
||||||
|
| W93 | highway | residential | :w2N |
|
||||||
|
And the places
|
||||||
|
| osm | class | type | addr+postcode | geometry |
|
||||||
|
| W22 | place | postcode | 445023 | :building:w2N |
|
||||||
|
When importing
|
||||||
|
Then place_addressline doesn't contain
|
||||||
|
| object | address |
|
||||||
|
| W93 | W22 |
|
||||||
|
|
||||||
|
Scenario: postcode boundaries do appear in the address of a way
|
||||||
|
Given the scene admin-areas
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | admin | addr+postcode | geometry |
|
||||||
|
| R1 | boundary | administrative | 6 | 112 | :b0 |
|
||||||
|
| R34 | boundary | administrative | 8 | 112 DE | :b1:E |
|
||||||
|
And the places
|
||||||
|
| osm | class | type | addr+postcode | geometry |
|
||||||
|
| R4 | place | postcode | 112 DE 34 | :b2:N |
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | geometry |
|
||||||
|
| W93 | highway | residential | :w2N |
|
||||||
|
And the places
|
||||||
|
| osm | class | type | addr+postcode | geometry |
|
||||||
|
| W22 | place | postcode | 445023 | :building:w2N |
|
||||||
|
When importing
|
||||||
|
Then place_addressline contains
|
||||||
|
| object | address |
|
||||||
|
| W93 | R4 |
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ Feature: Parenting of objects
|
|||||||
| N2 | W1 |
|
| N2 | W1 |
|
||||||
When searching for "4 galoo"
|
When searching for "4 galoo"
|
||||||
Then results contain
|
Then results contain
|
||||||
| ID | osm_type | osm_id | langaddress
|
| ID | osm_type | osm_id | langaddress |
|
||||||
| 0 | N | 1 | 4, galoo, 12345
|
| 0 | N | 1 | 4, galoo, 12345 |
|
||||||
When searching for "5 galoo"
|
When searching for "5 galoo"
|
||||||
Then results contain
|
Then results contain
|
||||||
| ID | osm_type | osm_id | langaddress
|
| ID | osm_type | osm_id | langaddress |
|
||||||
| 0 | N | 2 | 5, galoo, 99999
|
| 0 | N | 2 | 5, galoo, 99999 |
|
||||||
|
|
||||||
Scenario: Address without tags, closest street
|
Scenario: Address without tags, closest street
|
||||||
Given the scene roads-with-pois
|
Given the scene roads-with-pois
|
||||||
@@ -441,3 +441,22 @@ Feature: Parenting of objects
|
|||||||
| object | parent_place_id |
|
| object | parent_place_id |
|
||||||
| N1 | W2 |
|
| N1 | W2 |
|
||||||
|
|
||||||
|
# github #1056
|
||||||
|
Scenario: Full names should be preferably matched for nearest road
|
||||||
|
Given the grid
|
||||||
|
| 1 | | 2 | 5 |
|
||||||
|
| | | | |
|
||||||
|
| 3 | | | 4 |
|
||||||
|
| | 10| | |
|
||||||
|
And the places
|
||||||
|
| osm | class | type | name+name | geometry |
|
||||||
|
| W1 | highway | residential | Via Cavassico superiore | 1, 2 |
|
||||||
|
| W3 | highway | residential | Via Cavassico superiore | 2, 5 |
|
||||||
|
| W2 | highway | primary | Via Frazione Cavassico | 3, 4 |
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | addr+street |
|
||||||
|
| N10 | shop | yes | Via Cavassico superiore |
|
||||||
|
When importing
|
||||||
|
Then placex contains
|
||||||
|
| object | parent_place_id |
|
||||||
|
| N10 | W1 |
|
||||||
|
|||||||
@@ -70,9 +70,9 @@ Feature: Import into placex
|
|||||||
When importing
|
When importing
|
||||||
Then placex contains
|
Then placex contains
|
||||||
| object | postcode | country_code | rank_search | rank_address |
|
| object | postcode | country_code | rank_search | rank_address |
|
||||||
| N1 | E45 2CD | gb | 25 | 5 |
|
| N1 | E45 2CD | gb | 25 | 0 |
|
||||||
| N2 | E45 2 | gb | 23 | 5 |
|
| N2 | E45 2 | gb | 23 | 0 |
|
||||||
| N3 | Y45 | gb | 21 | 5 |
|
| N3 | Y45 | gb | 21 | 0 |
|
||||||
|
|
||||||
Scenario: wrongly formatted GB postcodes are down-ranked
|
Scenario: wrongly formatted GB postcodes are down-ranked
|
||||||
Given the places
|
Given the places
|
||||||
@@ -82,8 +82,8 @@ Feature: Import into placex
|
|||||||
When importing
|
When importing
|
||||||
Then placex contains
|
Then placex contains
|
||||||
| object | country_code | rank_search | rank_address |
|
| object | country_code | rank_search | rank_address |
|
||||||
| N1 | gb | 30 | 30 |
|
| N1 | gb | 30 | 0 |
|
||||||
| N2 | gb | 30 | 30 |
|
| N2 | gb | 30 | 0 |
|
||||||
|
|
||||||
Scenario: search and address rank for DE postcodes correctly assigned
|
Scenario: search and address rank for DE postcodes correctly assigned
|
||||||
Given the places
|
Given the places
|
||||||
@@ -95,10 +95,10 @@ Feature: Import into placex
|
|||||||
When importing
|
When importing
|
||||||
Then placex contains
|
Then placex contains
|
||||||
| object | country_code | rank_search | rank_address |
|
| object | country_code | rank_search | rank_address |
|
||||||
| N1 | de | 21 | 11 |
|
| N1 | de | 21 | 0 |
|
||||||
| N2 | de | 30 | 30 |
|
| N2 | de | 30 | 0 |
|
||||||
| N3 | de | 30 | 30 |
|
| N3 | de | 30 | 0 |
|
||||||
| N4 | de | 30 | 30 |
|
| N4 | de | 30 | 0 |
|
||||||
|
|
||||||
Scenario: search and address rank for other postcodes are correctly assigned
|
Scenario: search and address rank for other postcodes are correctly assigned
|
||||||
Given the places
|
Given the places
|
||||||
@@ -115,15 +115,15 @@ Feature: Import into placex
|
|||||||
When importing
|
When importing
|
||||||
Then placex contains
|
Then placex contains
|
||||||
| object | country_code | rank_search | rank_address |
|
| object | country_code | rank_search | rank_address |
|
||||||
| N1 | ca | 21 | 11 |
|
| N1 | ca | 21 | 0 |
|
||||||
| N2 | ca | 21 | 11 |
|
| N2 | ca | 21 | 0 |
|
||||||
| N3 | ca | 21 | 11 |
|
| N3 | ca | 21 | 0 |
|
||||||
| N4 | ca | 21 | 11 |
|
| N4 | ca | 21 | 0 |
|
||||||
| N5 | ca | 21 | 11 |
|
| N5 | ca | 21 | 0 |
|
||||||
| N6 | ca | 21 | 11 |
|
| N6 | ca | 21 | 0 |
|
||||||
| N7 | ca | 25 | 11 |
|
| N7 | ca | 25 | 0 |
|
||||||
| N8 | ca | 25 | 11 |
|
| N8 | ca | 25 | 0 |
|
||||||
| N9 | ca | 25 | 11 |
|
| N9 | ca | 25 | 0 |
|
||||||
|
|
||||||
Scenario: search and address ranks for places are correctly assigned
|
Scenario: search and address ranks for places are correctly assigned
|
||||||
Given the named places
|
Given the named places
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Feature: Import of postcodes
|
Feature: Import of postcodes
|
||||||
Tests for postcode estimation
|
Tests for postcode estimation
|
||||||
|
|
||||||
Scenario: Postcodes on the object are prefered over those on the address
|
Scenario: Postcodes on the object are preferred over those on the address
|
||||||
Given the scene admin-areas
|
Given the scene admin-areas
|
||||||
And the named places
|
And the named places
|
||||||
| osm | class | type | admin | addr+postcode | geometry |
|
| osm | class | type | admin | addr+postcode | geometry |
|
||||||
@@ -82,7 +82,7 @@ Feature: Import of postcodes
|
|||||||
| object | postcode |
|
| object | postcode |
|
||||||
| W22 | 112 DE 34 |
|
| W22 | 112 DE 34 |
|
||||||
|
|
||||||
Scenario: Roads get postcodes from nearby buildings without other info
|
Scenario: Roads get postcodes from nearby named buildings without other info
|
||||||
Given the scene admin-areas
|
Given the scene admin-areas
|
||||||
And the named places
|
And the named places
|
||||||
| osm | class | type | geometry |
|
| osm | class | type | geometry |
|
||||||
@@ -95,7 +95,19 @@ Feature: Import of postcodes
|
|||||||
| object | postcode |
|
| object | postcode |
|
||||||
| W93 | 445023 |
|
| W93 | 445023 |
|
||||||
|
|
||||||
@wip
|
Scenario: Roads get postcodes from nearby unnamed buildings without other info
|
||||||
|
Given the scene admin-areas
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | geometry |
|
||||||
|
| W93 | highway | residential | :w2N |
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | addr+postcode | geometry |
|
||||||
|
| W22 | place | postcode | 445023 | :building:w2N |
|
||||||
|
When importing
|
||||||
|
Then placex contains
|
||||||
|
| object | postcode |
|
||||||
|
| W93 | 445023 |
|
||||||
|
|
||||||
Scenario: Postcodes from admin boundaries are preferred over estimated postcodes
|
Scenario: Postcodes from admin boundaries are preferred over estimated postcodes
|
||||||
Given the scene admin-areas
|
Given the scene admin-areas
|
||||||
And the named places
|
And the named places
|
||||||
|
|||||||
@@ -23,3 +23,57 @@ Feature: Creation of search terms
|
|||||||
Then search_name contains
|
Then search_name contains
|
||||||
| object | name_vector | nameaddress_vector |
|
| object | name_vector | nameaddress_vector |
|
||||||
| N1 | foo | the road |
|
| N1 | foo | the road |
|
||||||
|
|
||||||
|
Scenario: Some addr: tags are added to address when the name exists
|
||||||
|
Given the scene roads-with-pois
|
||||||
|
And the places
|
||||||
|
| osm | class | type | name | geometry |
|
||||||
|
| N1 | place | state | new york | 80 80 |
|
||||||
|
| N1 | place | city | bonn | 81 81 |
|
||||||
|
| N1 | place | suburb | smalltown| 80 81 |
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | addr+city | addr+state | addr+suburb | geometry |
|
||||||
|
| W1 | highway | service | bonn | New York | Smalltown | :w-north |
|
||||||
|
When importing
|
||||||
|
Then search_name contains
|
||||||
|
| object | nameaddress_vector |
|
||||||
|
| W1 | bonn, new york, smalltown |
|
||||||
|
|
||||||
|
Scenario: A known addr:* tag is not added if the name is unknown
|
||||||
|
Given the scene roads-with-pois
|
||||||
|
And the places
|
||||||
|
| osm | class | type | name | addr+city | geometry |
|
||||||
|
| W1 | highway | residential | Road | Nandu | :w-north |
|
||||||
|
When importing
|
||||||
|
Then search_name contains not
|
||||||
|
| object | nameaddress_vector |
|
||||||
|
| W1 | nandu |
|
||||||
|
|
||||||
|
Scenario: addr:postcode is not added to the address terms
|
||||||
|
Given the scene roads-with-pois
|
||||||
|
And the places
|
||||||
|
| osm | class | type | name+ref | geometry |
|
||||||
|
| N1 | place | state | 12345 | 80 80 |
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | addr+postcode | geometry |
|
||||||
|
| W1 | highway | residential | 12345 | :w-north |
|
||||||
|
When importing
|
||||||
|
Then search_name contains not
|
||||||
|
| object | nameaddress_vector |
|
||||||
|
| W1 | 12345 |
|
||||||
|
|
||||||
|
Scenario: is_in is split and added to the address search terms
|
||||||
|
Given the scene roads-with-pois
|
||||||
|
And the places
|
||||||
|
| osm | class | type | name | geometry |
|
||||||
|
| N1 | place | state | new york | 80 80 |
|
||||||
|
| N1 | place | city | bonn | 81 81 |
|
||||||
|
| N1 | place | suburb | smalltown| 80 81 |
|
||||||
|
And the named places
|
||||||
|
| osm | class | type | addr+is_in | geometry |
|
||||||
|
| W1 | highway | service | bonn, New York, Smalltown | :w-north |
|
||||||
|
When importing
|
||||||
|
Then search_name contains
|
||||||
|
| object | nameaddress_vector |
|
||||||
|
| W1 | bonn, new york, smalltown |
|
||||||
|
|
||||||
|
|||||||
@@ -136,3 +136,13 @@ Feature: Import and search of names
|
|||||||
Then results contain
|
Then results contain
|
||||||
| ID | osm_type | osm_id |
|
| ID | osm_type | osm_id |
|
||||||
| 0 | R | 1 |
|
| 0 | R | 1 |
|
||||||
|
|
||||||
|
Scenario: Unprintable characters in postcodes are ignored
|
||||||
|
Given the named places
|
||||||
|
| osm | class | type | address |
|
||||||
|
| N234 | amenity | prison | 'postcode' : u'1234\u200e' |
|
||||||
|
When importing
|
||||||
|
And searching for "1234"
|
||||||
|
Then results contain
|
||||||
|
| ID | osm_type |
|
||||||
|
| 0 | P |
|
||||||
|
|||||||
@@ -85,3 +85,22 @@ Feature: Update of simple objects
|
|||||||
| osm | class | type | name | admin | geometry |
|
| osm | class | type | name | admin | geometry |
|
||||||
| W1 | boundary | administrative | Haha | 5 | 1, 2, 4, 3 |
|
| W1 | boundary | administrative | Haha | 5 | 1, 2, 4, 3 |
|
||||||
Then placex has no entry for W1
|
Then placex has no entry for W1
|
||||||
|
|
||||||
|
#895
|
||||||
|
Scenario: update rank when boundary is downgraded from admin to historic
|
||||||
|
Given the grid
|
||||||
|
| 1 | 2 |
|
||||||
|
| 3 | 4 |
|
||||||
|
And the places
|
||||||
|
| osm | class | type | name | admin | geometry |
|
||||||
|
| W1 | boundary | administrative | Haha | 5 | (1, 2, 4, 3, 1) |
|
||||||
|
When importing
|
||||||
|
Then placex contains
|
||||||
|
| object | rank_address |
|
||||||
|
| W1 | 10 |
|
||||||
|
When updating places
|
||||||
|
| osm | class | type | name | admin | geometry |
|
||||||
|
| W1 | boundary | historic | Haha | 5 | (1, 2, 4, 3, 1) |
|
||||||
|
Then placex contains
|
||||||
|
| object | rank_address |
|
||||||
|
| W1 | 0 |
|
||||||
|
|||||||
@@ -14,10 +14,14 @@ userconfig = {
|
|||||||
'BUILDDIR' : os.path.join(os.path.split(__file__)[0], "../../build"),
|
'BUILDDIR' : os.path.join(os.path.split(__file__)[0], "../../build"),
|
||||||
'REMOVE_TEMPLATE' : False,
|
'REMOVE_TEMPLATE' : False,
|
||||||
'KEEP_TEST_DB' : False,
|
'KEEP_TEST_DB' : False,
|
||||||
|
'DB_HOST' : None,
|
||||||
|
'DB_USER' : None,
|
||||||
|
'DB_PASS' : None,
|
||||||
'TEMPLATE_DB' : 'test_template_nominatim',
|
'TEMPLATE_DB' : 'test_template_nominatim',
|
||||||
'TEST_DB' : 'test_nominatim',
|
'TEST_DB' : 'test_nominatim',
|
||||||
'API_TEST_DB' : 'test_api_nominatim',
|
'API_TEST_DB' : 'test_api_nominatim',
|
||||||
'TEST_SETTINGS_FILE' : '/tmp/nominatim_settings.php',
|
'TEST_SETTINGS_FILE' : '/tmp/nominatim_settings.php',
|
||||||
|
'SERVER_MODULE_PATH' : None,
|
||||||
'PHPCOV' : False, # set to output directory to enable code coverage
|
'PHPCOV' : False, # set to output directory to enable code coverage
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +34,13 @@ class NominatimEnvironment(object):
|
|||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.build_dir = os.path.abspath(config['BUILDDIR'])
|
self.build_dir = os.path.abspath(config['BUILDDIR'])
|
||||||
self.src_dir = os.path.abspath(os.path.join(os.path.split(__file__)[0], "../.."))
|
self.src_dir = os.path.abspath(os.path.join(os.path.split(__file__)[0], "../.."))
|
||||||
|
self.db_host = config['DB_HOST']
|
||||||
|
self.db_user = config['DB_USER']
|
||||||
|
self.db_pass = config['DB_PASS']
|
||||||
self.template_db = config['TEMPLATE_DB']
|
self.template_db = config['TEMPLATE_DB']
|
||||||
self.test_db = config['TEST_DB']
|
self.test_db = config['TEST_DB']
|
||||||
self.api_test_db = config['API_TEST_DB']
|
self.api_test_db = config['API_TEST_DB']
|
||||||
|
self.server_module_path = config['SERVER_MODULE_PATH']
|
||||||
self.local_settings_file = config['TEST_SETTINGS_FILE']
|
self.local_settings_file = config['TEST_SETTINGS_FILE']
|
||||||
self.reuse_template = not config['REMOVE_TEMPLATE']
|
self.reuse_template = not config['REMOVE_TEMPLATE']
|
||||||
self.keep_scenario_db = config['KEEP_TEST_DB']
|
self.keep_scenario_db = config['KEEP_TEST_DB']
|
||||||
@@ -42,6 +50,17 @@ class NominatimEnvironment(object):
|
|||||||
|
|
||||||
self.template_db_done = False
|
self.template_db_done = False
|
||||||
|
|
||||||
|
def connect_database(self, dbname):
|
||||||
|
dbargs = {'database': dbname}
|
||||||
|
if self.db_host:
|
||||||
|
dbargs['host'] = self.db_host
|
||||||
|
if self.db_user:
|
||||||
|
dbargs['user'] = self.db_user
|
||||||
|
if self.db_pass:
|
||||||
|
dbargs['password'] = self.db_pass
|
||||||
|
conn = psycopg2.connect(**dbargs)
|
||||||
|
return conn
|
||||||
|
|
||||||
def next_code_coverage_file(self):
|
def next_code_coverage_file(self):
|
||||||
fn = os.path.join(self.code_coverage_path, "%06d.cov" % self.code_coverage_id)
|
fn = os.path.join(self.code_coverage_path, "%06d.cov" % self.code_coverage_id)
|
||||||
self.code_coverage_id += 1
|
self.code_coverage_id += 1
|
||||||
@@ -50,7 +69,11 @@ class NominatimEnvironment(object):
|
|||||||
|
|
||||||
def write_nominatim_config(self, dbname):
|
def write_nominatim_config(self, dbname):
|
||||||
f = open(self.local_settings_file, 'w')
|
f = open(self.local_settings_file, 'w')
|
||||||
f.write("<?php\n @define('CONST_Database_DSN', 'pgsql://@/%s');\n" % dbname)
|
f.write("<?php\n @define('CONST_Database_DSN', 'pgsql://%s:%s@%s/%s');\n" %
|
||||||
|
(self.db_user if self.db_user else '',
|
||||||
|
self.db_pass if self.db_pass else '',
|
||||||
|
self.db_host if self.db_host else '',
|
||||||
|
dbname))
|
||||||
f.write("@define('CONST_Osm2pgsql_Flatnode_File', null);\n")
|
f.write("@define('CONST_Osm2pgsql_Flatnode_File', null);\n")
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
@@ -61,7 +84,7 @@ class NominatimEnvironment(object):
|
|||||||
pass # ignore missing file
|
pass # ignore missing file
|
||||||
|
|
||||||
def db_drop_database(self, name):
|
def db_drop_database(self, name):
|
||||||
conn = psycopg2.connect(database='postgres')
|
conn = self.connect_database('postgres')
|
||||||
conn.set_isolation_level(0)
|
conn.set_isolation_level(0)
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
cur.execute('DROP DATABASE IF EXISTS %s' % (name, ))
|
cur.execute('DROP DATABASE IF EXISTS %s' % (name, ))
|
||||||
@@ -75,7 +98,7 @@ class NominatimEnvironment(object):
|
|||||||
|
|
||||||
if self.reuse_template:
|
if self.reuse_template:
|
||||||
# check that the template is there
|
# check that the template is there
|
||||||
conn = psycopg2.connect(database='postgres')
|
conn = self.connect_database('postgres')
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
cur.execute('select count(*) from pg_database where datname = %s',
|
cur.execute('select count(*) from pg_database where datname = %s',
|
||||||
(self.template_db,))
|
(self.template_db,))
|
||||||
@@ -86,47 +109,55 @@ class NominatimEnvironment(object):
|
|||||||
# just in case... make sure a previous table has been dropped
|
# just in case... make sure a previous table has been dropped
|
||||||
self.db_drop_database(self.template_db)
|
self.db_drop_database(self.template_db)
|
||||||
|
|
||||||
# call the first part of database setup
|
try:
|
||||||
self.write_nominatim_config(self.template_db)
|
# call the first part of database setup
|
||||||
self.run_setup_script('create-db', 'setup-db')
|
self.write_nominatim_config(self.template_db)
|
||||||
# remove external data to speed up indexing for tests
|
self.run_setup_script('create-db', 'setup-db')
|
||||||
conn = psycopg2.connect(database=self.template_db)
|
# remove external data to speed up indexing for tests
|
||||||
cur = conn.cursor()
|
conn = self.connect_database(self.template_db)
|
||||||
cur.execute("""select tablename from pg_tables
|
cur = conn.cursor()
|
||||||
where tablename in ('gb_postcode', 'us_postcode')""")
|
cur.execute("""select tablename from pg_tables
|
||||||
for t in cur:
|
where tablename in ('gb_postcode', 'us_postcode')""")
|
||||||
conn.cursor().execute('TRUNCATE TABLE %s' % (t[0],))
|
for t in cur:
|
||||||
conn.commit()
|
conn.cursor().execute('TRUNCATE TABLE %s' % (t[0],))
|
||||||
conn.close()
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# execute osm2pgsql import on an empty file to get the right tables
|
||||||
|
with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.xml') as fd:
|
||||||
|
fd.write(b'<osm version="0.6"></osm>')
|
||||||
|
fd.flush()
|
||||||
|
self.run_setup_script('import-data',
|
||||||
|
'ignore-errors',
|
||||||
|
'create-functions',
|
||||||
|
'create-tables',
|
||||||
|
'create-partition-tables',
|
||||||
|
'create-partition-functions',
|
||||||
|
'load-data',
|
||||||
|
'create-search-indices',
|
||||||
|
osm_file=fd.name,
|
||||||
|
osm2pgsql_cache='200')
|
||||||
|
except:
|
||||||
|
self.db_drop_database(self.template_db)
|
||||||
|
raise
|
||||||
|
|
||||||
# execute osm2pgsql import on an empty file to get the right tables
|
|
||||||
with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.xml') as fd:
|
|
||||||
fd.write(b'<osm version="0.6"></osm>')
|
|
||||||
fd.flush()
|
|
||||||
self.run_setup_script('import-data',
|
|
||||||
'ignore-errors',
|
|
||||||
'create-functions',
|
|
||||||
'create-tables',
|
|
||||||
'create-partition-tables',
|
|
||||||
'create-partition-functions',
|
|
||||||
'load-data',
|
|
||||||
'create-search-indices',
|
|
||||||
osm_file=fd.name,
|
|
||||||
osm2pgsql_cache='200')
|
|
||||||
|
|
||||||
def setup_api_db(self, context):
|
def setup_api_db(self, context):
|
||||||
self.write_nominatim_config(self.api_test_db)
|
self.write_nominatim_config(self.api_test_db)
|
||||||
|
|
||||||
|
def setup_unknown_db(self, context):
|
||||||
|
self.write_nominatim_config('UNKNOWN_DATABASE_NAME')
|
||||||
|
|
||||||
def setup_db(self, context):
|
def setup_db(self, context):
|
||||||
self.setup_template_db()
|
self.setup_template_db()
|
||||||
self.write_nominatim_config(self.test_db)
|
self.write_nominatim_config(self.test_db)
|
||||||
conn = psycopg2.connect(database=self.template_db)
|
conn = self.connect_database(self.template_db)
|
||||||
conn.set_isolation_level(0)
|
conn.set_isolation_level(0)
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
cur.execute('DROP DATABASE IF EXISTS %s' % (self.test_db, ))
|
cur.execute('DROP DATABASE IF EXISTS %s' % (self.test_db, ))
|
||||||
cur.execute('CREATE DATABASE %s TEMPLATE = %s' % (self.test_db, self.template_db))
|
cur.execute('CREATE DATABASE %s TEMPLATE = %s' % (self.test_db, self.template_db))
|
||||||
conn.close()
|
conn.close()
|
||||||
context.db = psycopg2.connect(database=self.test_db)
|
context.db = self.connect_database(self.test_db)
|
||||||
if python_version[0] < 3:
|
if python_version[0] < 3:
|
||||||
psycopg2.extras.register_hstore(context.db, globally=False, unicode=True)
|
psycopg2.extras.register_hstore(context.db, globally=False, unicode=True)
|
||||||
else:
|
else:
|
||||||
@@ -140,13 +171,17 @@ class NominatimEnvironment(object):
|
|||||||
self.db_drop_database(self.test_db)
|
self.db_drop_database(self.test_db)
|
||||||
|
|
||||||
def run_setup_script(self, *args, **kwargs):
|
def run_setup_script(self, *args, **kwargs):
|
||||||
|
if self.server_module_path:
|
||||||
|
kwargs = dict(kwargs)
|
||||||
|
kwargs['module_path'] = self.server_module_path
|
||||||
self.run_nominatim_script('setup', *args, **kwargs)
|
self.run_nominatim_script('setup', *args, **kwargs)
|
||||||
|
|
||||||
def run_update_script(self, *args, **kwargs):
|
def run_update_script(self, *args, **kwargs):
|
||||||
self.run_nominatim_script('update', *args, **kwargs)
|
self.run_nominatim_script('update', *args, **kwargs)
|
||||||
|
|
||||||
def run_nominatim_script(self, script, *args, **kwargs):
|
def run_nominatim_script(self, script, *args, **kwargs):
|
||||||
cmd = [os.path.join(self.build_dir, 'utils', '%s.php' % script)]
|
cmd = ['/usr/bin/env', 'php', '-Cq']
|
||||||
|
cmd.append(os.path.join(self.build_dir, 'utils', '%s.php' % script))
|
||||||
cmd.extend(['--%s' % x for x in args])
|
cmd.extend(['--%s' % x for x in args])
|
||||||
for k, v in kwargs.items():
|
for k, v in kwargs.items():
|
||||||
cmd.extend(('--' + k.replace('_', '-'), str(v)))
|
cmd.extend(('--' + k.replace('_', '-'), str(v)))
|
||||||
@@ -255,9 +290,10 @@ def before_scenario(context, scenario):
|
|||||||
context.nominatim.setup_db(context)
|
context.nominatim.setup_db(context)
|
||||||
elif 'APIDB' in context.tags:
|
elif 'APIDB' in context.tags:
|
||||||
context.nominatim.setup_api_db(context)
|
context.nominatim.setup_api_db(context)
|
||||||
|
elif 'UNKNOWNDB' in context.tags:
|
||||||
|
context.nominatim.setup_unknown_db(context)
|
||||||
context.scene = None
|
context.scene = None
|
||||||
|
|
||||||
def after_scenario(context, scenario):
|
def after_scenario(context, scenario):
|
||||||
if 'DB' in context.tags:
|
if 'DB' in context.tags:
|
||||||
context.nominatim.teardown_db(context)
|
context.nominatim.teardown_db(context)
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,15 @@ Feature: Tag evaluation
|
|||||||
| N3 | 'name: de' : 'Foo', 'name:\\\\' : 'real3' |
|
| N3 | 'name: de' : 'Foo', 'name:\\\\' : 'real3' |
|
||||||
| N4 | 'name: de' : 'Foo', 'name' : 'rea\\l3' |
|
| N4 | 'name: de' : 'Foo', 'name' : 'rea\\l3' |
|
||||||
|
|
||||||
|
Scenario: Unprintable character in address tag are maintained
|
||||||
|
When loading osm data
|
||||||
|
"""
|
||||||
|
n23 Tamenity=yes,name=foo,addr:postcode=1234%200e%
|
||||||
|
"""
|
||||||
|
Then place contains
|
||||||
|
| object | address |
|
||||||
|
| N23 | 'postcode' : u'1234\u200e' |
|
||||||
|
|
||||||
Scenario Outline: Included places
|
Scenario Outline: Included places
|
||||||
When loading osm data
|
When loading osm data
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ Feature: Update of relations by osm2pgsql
|
|||||||
n202 x0.0001 y0.0001
|
n202 x0.0001 y0.0001
|
||||||
n203 x0.0001 y0
|
n203 x0.0001 y0
|
||||||
w2 Tref=45' Nn200,n201,n202,n203,n200
|
w2 Tref=45' Nn200,n201,n202,n203,n200
|
||||||
r1 Ttype=multipolygon,tourism=hotel,name=XZ' Mw2@
|
r1 Ttype=multipolygon,tourism=hotel,name=XZ Mw2@
|
||||||
"""
|
"""
|
||||||
Then place contains
|
Then place contains
|
||||||
| object | class | type | name
|
| object | class | type | name |
|
||||||
| R1 | tourism | hotel | 'name' : 'XZ'
|
| R1 | tourism | hotel | 'name' : 'XZ' |
|
||||||
When updating osm data
|
When updating osm data
|
||||||
"""
|
"""
|
||||||
r1 Ttype=multipolygon,tourism=hotel,name=XZ Mn1@
|
r1 Ttype=multipolygon,tourism=hotel,name=XZ Mn1@
|
||||||
@@ -34,16 +34,16 @@ Feature: Update of relations by osm2pgsql
|
|||||||
r1 Ttype=multipolygon,tourism=hotel,name=XZ Mw2@
|
r1 Ttype=multipolygon,tourism=hotel,name=XZ Mw2@
|
||||||
"""
|
"""
|
||||||
Then place contains
|
Then place contains
|
||||||
| object | class | type | name
|
| object | class | type | name |
|
||||||
| R1 | tourism | hotel | 'name' : 'XZ'
|
| R1 | tourism | hotel | 'name' : 'XZ' |
|
||||||
When updating osm data
|
When updating osm data
|
||||||
"""
|
"""
|
||||||
r1 Ttype=multipolygon,amenity=prison,name=XZ Mw2@
|
r1 Ttype=multipolygon,amenity=prison,name=XZ Mw2@
|
||||||
"""
|
"""
|
||||||
Then place has no entry for R1:tourism
|
Then place has no entry for R1:tourism
|
||||||
And place contains
|
And place contains
|
||||||
| object | class | type | name
|
| object | class | type | name |
|
||||||
| R1 | amenity | prison | 'name' : 'XZ'
|
| R1 | amenity | prison | 'name' : 'XZ' |
|
||||||
|
|
||||||
Scenario: Change name of a relation
|
Scenario: Change name of a relation
|
||||||
When loading osm data
|
When loading osm data
|
||||||
@@ -56,15 +56,15 @@ Feature: Update of relations by osm2pgsql
|
|||||||
r1 Ttype=multipolygon,tourism=hotel,name=AB Mw2@
|
r1 Ttype=multipolygon,tourism=hotel,name=AB Mw2@
|
||||||
"""
|
"""
|
||||||
Then place contains
|
Then place contains
|
||||||
| object | class | type | name
|
| object | class | type | name |
|
||||||
| R1 | tourism | hotel | 'name' : 'AB'
|
| R1 | tourism | hotel | 'name' : 'AB' |
|
||||||
When updating osm data
|
When updating osm data
|
||||||
"""
|
"""
|
||||||
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
||||||
"""
|
"""
|
||||||
Then place contains
|
Then place contains
|
||||||
| object | class | type | name
|
| object | class | type | name |
|
||||||
| R1 | tourism | hotel | 'name' : 'XZ'
|
| R1 | tourism | hotel | 'name' : 'XY' |
|
||||||
|
|
||||||
Scenario: Change type of a relation into something unknown
|
Scenario: Change type of a relation into something unknown
|
||||||
When loading osm data
|
When loading osm data
|
||||||
@@ -77,8 +77,8 @@ Feature: Update of relations by osm2pgsql
|
|||||||
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
||||||
"""
|
"""
|
||||||
Then place contains
|
Then place contains
|
||||||
| object | class | type | name
|
| object | class | type | name |
|
||||||
| R1 | tourism | hotel | 'name' : 'XZ'
|
| R1 | tourism | hotel | 'name' : 'XY' |
|
||||||
When updating osm data
|
When updating osm data
|
||||||
"""
|
"""
|
||||||
r1 Ttype=multipolygon,amenities=prison,name=XY Mw2@
|
r1 Ttype=multipolygon,amenities=prison,name=XY Mw2@
|
||||||
@@ -96,8 +96,8 @@ Feature: Update of relations by osm2pgsql
|
|||||||
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
||||||
"""
|
"""
|
||||||
Then place contains
|
Then place contains
|
||||||
| object | class | type | name
|
| object | class | type | name |
|
||||||
| R1 | tourism | hotel | 'name' : 'XZ'
|
| R1 | tourism | hotel | 'name' : 'XY' |
|
||||||
When updating osm data
|
When updating osm data
|
||||||
"""
|
"""
|
||||||
r1 Ttourism=hotel,name=XY Mw2@
|
r1 Ttourism=hotel,name=XY Mw2@
|
||||||
@@ -115,8 +115,8 @@ Feature: Update of relations by osm2pgsql
|
|||||||
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
r1 Ttype=multipolygon,tourism=hotel,name=XY Mw2@
|
||||||
"""
|
"""
|
||||||
Then place contains
|
Then place contains
|
||||||
| object | class | type | name
|
| object | class | type | name |
|
||||||
| R1 | tourism | hotel | 'name' : 'XZ'
|
| R1 | tourism | hotel | 'name' : 'XY' |
|
||||||
When updating osm data
|
When updating osm data
|
||||||
"""
|
"""
|
||||||
r1 Ttype=multipolygonn,tourism=hotel,name=XY Mw2@
|
r1 Ttype=multipolygonn,tourism=hotel,name=XY Mw2@
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ Feature: Update of simple objects by osm2pgsql
|
|||||||
n2 Tplace=locality,name=spotty
|
n2 Tplace=locality,name=spotty
|
||||||
"""
|
"""
|
||||||
Then place contains
|
Then place contains
|
||||||
| object | type | name
|
| object | type | name+name |
|
||||||
| N1:tourism | hotel | 'name' : 'foo'
|
| N1:tourism | hotel | foo |
|
||||||
| N1:amenity | restaurant | 'name' : 'foo'
|
| N1:amenity | restaurant | foo |
|
||||||
| N2:place | locality | 'name' : 'spotty'
|
| N2:place | locality | spotty |
|
||||||
When updating osm data
|
When updating osm data
|
||||||
"""
|
"""
|
||||||
n1 dV Ttourism=hotel,name=foo
|
n1 dV Ttourism=hotel,name=foo
|
||||||
@@ -21,6 +21,6 @@ Feature: Update of simple objects by osm2pgsql
|
|||||||
Then place has no entry for N1:amenity
|
Then place has no entry for N1:amenity
|
||||||
And place has no entry for N2
|
And place has no entry for N2
|
||||||
And place contains
|
And place contains
|
||||||
| object | class | type | name
|
| object | class | type | name |
|
||||||
| N1:tourism | tourism | hotel | 'name' : 'foo'
|
| N1:tourism | tourism | hotel | 'name' : 'foo' |
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'SebastianBergmann/CodeCoverage/autoload.php';
|
require_once 'SebastianBergmann/CodeCoverage/autoload.php';
|
||||||
|
|
||||||
|
|
||||||
function coverage_shutdown($oCoverage)
|
function coverage_shutdown($oCoverage)
|
||||||
{
|
{
|
||||||
$oCoverage->stop();
|
$oCoverage->stop();
|
||||||
@@ -16,5 +17,3 @@ $coverage->start($_SERVER['COV_TEST_NAME']);
|
|||||||
register_shutdown_function('coverage_shutdown', $coverage);
|
register_shutdown_function('coverage_shutdown', $coverage);
|
||||||
|
|
||||||
include $_SERVER['COV_SCRIPT_FILENAME'];
|
include $_SERVER['COV_SCRIPT_FILENAME'];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class PlaceColumn:
|
|||||||
self.add_hstore('extratags', key[6:], value)
|
self.add_hstore('extratags', key[6:], value)
|
||||||
elif key.startswith('addr+'):
|
elif key.startswith('addr+'):
|
||||||
self.add_hstore('address', key[5:], value)
|
self.add_hstore('address', key[5:], value)
|
||||||
|
elif key in ('name', 'address', 'extratags'):
|
||||||
|
self.columns[key] = eval('{' + value + '}')
|
||||||
else:
|
else:
|
||||||
assert_in(key, ('class', 'type'))
|
assert_in(key, ('class', 'type'))
|
||||||
self.columns[key] = None if value == '' else value
|
self.columns[key] = None if value == '' else value
|
||||||
@@ -130,6 +132,18 @@ def compare_place_id(expected, result, column, context):
|
|||||||
LazyFmt("Bad place id in column %s. Expected: %s, got: %s.",
|
LazyFmt("Bad place id in column %s. Expected: %s, got: %s.",
|
||||||
column, expected, PlaceObjName(result, context.db)))
|
column, expected, PlaceObjName(result, context.db)))
|
||||||
|
|
||||||
|
def check_database_integrity(context):
|
||||||
|
""" Check some generic constraints on the tables.
|
||||||
|
"""
|
||||||
|
# place_addressline should not have duplicate (place_id, address_place_id)
|
||||||
|
cur = context.db.cursor()
|
||||||
|
cur.execute("""SELECT count(*) FROM
|
||||||
|
(SELECT place_id, address_place_id, count(*) as c
|
||||||
|
FROM place_addressline GROUP BY place_id, address_place_id) x
|
||||||
|
WHERE c > 1""")
|
||||||
|
eq_(0, cur.fetchone()[0], "Duplicates found in place_addressline")
|
||||||
|
|
||||||
|
|
||||||
class NominatimID:
|
class NominatimID:
|
||||||
""" Splits a unique identifier for places into its components.
|
""" Splits a unique identifier for places into its components.
|
||||||
As place_ids cannot be used for testing, we use a unique
|
As place_ids cannot be used for testing, we use a unique
|
||||||
@@ -288,6 +302,7 @@ def import_and_index_data_from_place_table(context):
|
|||||||
and ST_GeometryType(geometry) = 'ST_LineString'""")
|
and ST_GeometryType(geometry) = 'ST_LineString'""")
|
||||||
context.db.commit()
|
context.db.commit()
|
||||||
context.nominatim.run_setup_script('calculate-postcodes', 'index', 'index-noanalyse')
|
context.nominatim.run_setup_script('calculate-postcodes', 'index', 'index-noanalyse')
|
||||||
|
check_database_integrity(context)
|
||||||
|
|
||||||
@when("updating places")
|
@when("updating places")
|
||||||
def update_place_table(context):
|
def update_place_table(context):
|
||||||
@@ -312,6 +327,8 @@ def update_place_table(context):
|
|||||||
if cur.rowcount == 0:
|
if cur.rowcount == 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
check_database_integrity(context)
|
||||||
|
|
||||||
@when("marking for delete (?P<oids>.*)")
|
@when("marking for delete (?P<oids>.*)")
|
||||||
def delete_places(context, oids):
|
def delete_places(context, oids):
|
||||||
context.nominatim.run_setup_script(
|
context.nominatim.run_setup_script(
|
||||||
@@ -425,8 +442,8 @@ def check_placex_contents(context, exact):
|
|||||||
|
|
||||||
context.db.commit()
|
context.db.commit()
|
||||||
|
|
||||||
@then("search_name contains")
|
@then("search_name contains(?P<exclude> not)?")
|
||||||
def check_search_name_contents(context):
|
def check_search_name_contents(context, exclude):
|
||||||
cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||||
|
|
||||||
for row in context.table:
|
for row in context.table:
|
||||||
@@ -444,11 +461,16 @@ def check_search_name_contents(context):
|
|||||||
FROM word, (SELECT unnest(%s) as term) t
|
FROM word, (SELECT unnest(%s) as term) t
|
||||||
WHERE word_token = make_standard_name(t.term)""",
|
WHERE word_token = make_standard_name(t.term)""",
|
||||||
(terms,))
|
(terms,))
|
||||||
ok_(subcur.rowcount >= len(terms),
|
if not exclude:
|
||||||
"No word entry found for " + row[h])
|
ok_(subcur.rowcount >= len(terms),
|
||||||
|
"No word entry found for " + row[h])
|
||||||
for wid in subcur:
|
for wid in subcur:
|
||||||
assert_in(wid[0], res[h],
|
if exclude:
|
||||||
"Missing term for %s/%s: %s" % (pid, h, wid[1]))
|
assert_not_in(wid[0], res[h],
|
||||||
|
"Found term for %s/%s: %s" % (pid, h, wid[1]))
|
||||||
|
else:
|
||||||
|
assert_in(wid[0], res[h],
|
||||||
|
"Missing term for %s/%s: %s" % (pid, h, wid[1]))
|
||||||
else:
|
else:
|
||||||
assert_db_column(res, h, row[h], context)
|
assert_db_column(res, h, row[h], context)
|
||||||
|
|
||||||
@@ -476,6 +498,21 @@ def check_place_addressline(context):
|
|||||||
|
|
||||||
context.db.commit()
|
context.db.commit()
|
||||||
|
|
||||||
|
@then("place_addressline doesn't contain")
|
||||||
|
def check_place_addressline_exclude(context):
|
||||||
|
cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||||
|
|
||||||
|
for row in context.table:
|
||||||
|
pid = NominatimID(row['object']).get_place_id(cur)
|
||||||
|
apid = NominatimID(row['address']).get_place_id(cur)
|
||||||
|
cur.execute(""" SELECT * FROM place_addressline
|
||||||
|
WHERE place_id = %s AND address_place_id = %s""",
|
||||||
|
(pid, apid))
|
||||||
|
eq_(0, cur.rowcount,
|
||||||
|
"Row found for place %s and address %s" % (row['object'], row['address']))
|
||||||
|
|
||||||
|
context.db.commit()
|
||||||
|
|
||||||
@then("(?P<oid>\w+) expands to(?P<neg> no)? interpolation")
|
@then("(?P<oid>\w+) expands to(?P<neg> no)? interpolation")
|
||||||
def check_location_property_osmline(context, oid, neg):
|
def check_location_property_osmline(context, oid, neg):
|
||||||
cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
cur = context.db.cursor(cursor_factory=psycopg2.extras.DictCursor)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user