mirror of
https://github.com/osm-search/Nominatim.git
synced 2026-02-16 15:47:58 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edd77e3184 | ||
|
|
f549379e31 |
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
sudo: required
|
sudo: required
|
||||||
dist: xenial
|
dist: trusty
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- "3.6"
|
- "3.6"
|
||||||
@@ -11,8 +11,6 @@ git:
|
|||||||
env:
|
env:
|
||||||
- TEST_SUITE=tests
|
- TEST_SUITE=tests
|
||||||
- TEST_SUITE=monaco
|
- TEST_SUITE=monaco
|
||||||
before_install:
|
|
||||||
- phpenv global 7.1
|
|
||||||
install:
|
install:
|
||||||
- vagrant/install-on-travis-ci.sh
|
- vagrant/install-on-travis-ci.sh
|
||||||
before_script:
|
before_script:
|
||||||
@@ -21,10 +19,10 @@ script:
|
|||||||
- cd $TRAVIS_BUILD_DIR/
|
- cd $TRAVIS_BUILD_DIR/
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then phpcs --report-width=120 . ; fi
|
- if [[ $TEST_SUITE == "tests" ]]; then phpcs --report-width=120 . ; fi
|
||||||
- cd $TRAVIS_BUILD_DIR/test/php
|
- cd $TRAVIS_BUILD_DIR/test/php
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then /usr/bin/phpunit ./ ; fi
|
- if [[ $TEST_SUITE == "tests" ]]; then phpunit ./ ; 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 -DREMOVE_TEMPLATE=1 --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
|
- 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 wget --no-verbose --output-document=../data/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf; fi
|
||||||
|
|||||||
113
CMakeLists.txt
113
CMakeLists.txt
@@ -19,8 +19,8 @@ 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 4)
|
set(NOMINATIM_VERSION_MINOR 2)
|
||||||
set(NOMINATIM_VERSION_PATCH 2)
|
set(NOMINATIM_VERSION_PATCH 1)
|
||||||
|
|
||||||
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
|
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
|
||||||
|
|
||||||
@@ -35,68 +35,66 @@ add_definitions(-DNOMINATIM_VERSION="${NOMINATIM_VERSION}")
|
|||||||
|
|
||||||
set(BUILD_TESTS off CACHE BOOL "Build test suite" FORCE)
|
set(BUILD_TESTS off CACHE BOOL "Build test suite" FORCE)
|
||||||
set(WITH_LUA off CACHE BOOL "Build with lua support" FORCE)
|
set(WITH_LUA off CACHE BOOL "Build with lua support" FORCE)
|
||||||
set(ONLY_DOCS off CACHE BOOL "Build documentation only")
|
|
||||||
|
|
||||||
if (NOT ONLY_DOCS)
|
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/osm2pgsql/CMakeLists.txt")
|
||||||
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/osm2pgsql/CMakeLists.txt")
|
|
||||||
message(FATAL_ERROR "The osm2pgsql directory is empty.\
|
message(FATAL_ERROR "The osm2pgsql directory is empty.\
|
||||||
Did you forget to check out Nominatim recursively?\
|
Did you forget to check out Nominatim recursively?\
|
||||||
\nTry updating submodules with: git submodule update --init")
|
\nTry updating submodules with: git submodule update --init")
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(osm2pgsql)
|
add_subdirectory(osm2pgsql)
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
unset(PostgreSQL_TYPE_INCLUDE_DIR CACHE)
|
unset(PostgreSQL_TYPE_INCLUDE_DIR CACHE)
|
||||||
set(PostgreSQL_TYPE_INCLUDE_DIR "/usr/include/")
|
set(PostgreSQL_TYPE_INCLUDE_DIR "/usr/include/")
|
||||||
find_package(PostgreSQL REQUIRED)
|
find_package(PostgreSQL REQUIRED)
|
||||||
include_directories(${PostgreSQL_INCLUDE_DIRS})
|
include_directories(${PostgreSQL_INCLUDE_DIRS})
|
||||||
link_directories(${PostgreSQL_LIBRARY_DIRS})
|
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 "")
|
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}")
|
||||||
message(STATUS "Using pyosmium-get-changes at ${PYOSMIUM_PATH}")
|
message(STATUS "Using pyosmium-get-changes at ${PYOSMIUM_PATH}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
find_program(PG_CONFIG pg_config)
|
find_program(PG_CONFIG pg_config)
|
||||||
execute_process(COMMAND ${PG_CONFIG} --pgxs
|
execute_process(COMMAND ${PG_CONFIG} --pgxs
|
||||||
OUTPUT_VARIABLE PGXS
|
OUTPUT_VARIABLE PGXS
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
|
||||||
if (NOT EXISTS "${PGXS}")
|
if (NOT EXISTS "${PGXS}")
|
||||||
message(FATAL_ERROR "Postgresql server package not found.")
|
message(FATAL_ERROR "Postgresql server package not found.")
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(ZLIB REQUIRED)
|
|
||||||
|
|
||||||
find_package(BZip2 REQUIRED)
|
|
||||||
|
|
||||||
find_package(LibXml2 REQUIRED)
|
|
||||||
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})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
||||||
|
find_package(BZip2 REQUIRED)
|
||||||
|
|
||||||
|
find_package(LibXml2 REQUIRED)
|
||||||
|
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
|
||||||
#
|
#
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
set(WEBSITESCRIPTS
|
set(CUSTOMFILES
|
||||||
|
settings/phrase_settings.php
|
||||||
website/deletable.php
|
website/deletable.php
|
||||||
website/details.php
|
website/details.php
|
||||||
website/hierarchy.php
|
website/hierarchy.php
|
||||||
@@ -105,31 +103,24 @@ set(WEBSITESCRIPTS
|
|||||||
website/reverse.php
|
website/reverse.php
|
||||||
website/search.php
|
website/search.php
|
||||||
website/status.php
|
website/status.php
|
||||||
)
|
utils/blocks.php
|
||||||
|
|
||||||
set(CUSTOMSCRIPTS
|
|
||||||
utils/country_languages.php
|
utils/country_languages.php
|
||||||
|
utils/imports.php
|
||||||
utils/importWikipedia.php
|
utils/importWikipedia.php
|
||||||
utils/export.php
|
utils/export.php
|
||||||
utils/query.php
|
utils/query.php
|
||||||
|
utils/server_compare.php
|
||||||
utils/setup.php
|
utils/setup.php
|
||||||
utils/specialphrases.php
|
utils/specialphrases.php
|
||||||
utils/update.php
|
utils/update.php
|
||||||
utils/warm.php
|
utils/warm.php
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach (script_source ${CUSTOMSCRIPTS})
|
foreach (cfile ${CUSTOMFILES})
|
||||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/script.tmpl
|
configure_file(${PROJECT_SOURCE_DIR}/${cfile} ${PROJECT_BINARY_DIR}/${cfile})
|
||||||
${PROJECT_BINARY_DIR}/${script_source})
|
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
foreach (script_source ${WEBSITESCRIPTS})
|
configure_file(${PROJECT_SOURCE_DIR}/settings/defaults.php ${PROJECT_BINARY_DIR}/settings/settings.php)
|
||||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/website.tmpl
|
|
||||||
${PROJECT_BINARY_DIR}/${script_source})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
configure_file(${PROJECT_SOURCE_DIR}/settings/defaults.php
|
|
||||||
${PROJECT_BINARY_DIR}/settings/settings.php)
|
|
||||||
|
|
||||||
set(WEBPATHS css images js)
|
set(WEBPATHS css images js)
|
||||||
|
|
||||||
@@ -146,30 +137,26 @@ endforeach()
|
|||||||
#
|
#
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
if (NOT ONLY_DOCS)
|
include(CTest)
|
||||||
include(CTest)
|
|
||||||
|
|
||||||
set(TEST_BDD db osm2pgsql api)
|
set(TEST_BDD db osm2pgsql api)
|
||||||
|
|
||||||
foreach (test ${TEST_BDD})
|
foreach (test ${TEST_BDD})
|
||||||
add_test(NAME bdd_${test}
|
add_test(NAME bdd_${test}
|
||||||
COMMAND lettuce features/${test}
|
COMMAND lettuce features/${test}
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests)
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests)
|
||||||
set_tests_properties(bdd_${test}
|
set_tests_properties(bdd_${test}
|
||||||
PROPERTIES ENVIRONMENT "NOMINATIM_DIR=${PROJECT_BINARY_DIR}")
|
PROPERTIES ENVIRONMENT "NOMINATIM_DIR=${PROJECT_BINARY_DIR}")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
add_test(NAME php
|
add_test(NAME php
|
||||||
COMMAND phpunit ./
|
COMMAND phpunit ./
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests-php)
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests-php)
|
||||||
endif()
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
if (NOT ONLY_DOCS)
|
add_subdirectory(module)
|
||||||
add_subdirectory(module)
|
add_subdirectory(nominatim)
|
||||||
add_subdirectory(nominatim)
|
|
||||||
endif()
|
|
||||||
add_subdirectory(docs)
|
add_subdirectory(docs)
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
|
|||||||
50
ChangeLog
50
ChangeLog
@@ -1,54 +1,6 @@
|
|||||||
3.4.2
|
3.2.1
|
||||||
* security fix: fix possible SQL injection via details API
|
* security fix: fix possible SQL injection via details API
|
||||||
|
|
||||||
3.4.1
|
|
||||||
* update osm2pgsql
|
|
||||||
* move deletion to copy thread (fixes deadlock in updates)
|
|
||||||
* fix filtering where valid address objects got dropped
|
|
||||||
* fix typo in import styles
|
|
||||||
|
|
||||||
3.4.0
|
|
||||||
|
|
||||||
* increase required version for PostgreSQL(9.3), PostGIS(2.2) and PHP(7.0)
|
|
||||||
* better error reporting for out-of-memory errors
|
|
||||||
* exclude postcode ranges separated by colon from centre point calculation
|
|
||||||
* update osm2pgsql, better handling of imports without flatnode file
|
|
||||||
* switch to more efficient algorithm for word set computation
|
|
||||||
* use only boundries for country and state parts of addresses
|
|
||||||
* improve updates of addresses with housenumbers and interpolations
|
|
||||||
* remove country from place_addressline table and use country_code instead
|
|
||||||
* optimise indexes on search_name partition tables
|
|
||||||
* improve searching of attached streets for large objects like airports
|
|
||||||
* drop support for python 2
|
|
||||||
* new scripts for importing Wikidata for importance
|
|
||||||
* create and drop indexes concurrently to not clash with auto vacuum
|
|
||||||
* various documentation improvements
|
|
||||||
|
|
||||||
|
|
||||||
3.3.0
|
|
||||||
|
|
||||||
* zoom 17 in reverse now zooms in on minor streets
|
|
||||||
* fix use of postcode relations in address
|
|
||||||
* support for housenumber 0 on interpolations
|
|
||||||
* replace database abstraction DB with PDO and switch to using exceptions
|
|
||||||
* exclude line features at rank 30 from reverse geocoding
|
|
||||||
* remove self-reference and country from place_addressline
|
|
||||||
* make json output more readable (less escaping)
|
|
||||||
* update conversion scripts for postcodes
|
|
||||||
* scripts in utils/ are no longer executable (always use scripts in build dir)
|
|
||||||
* remove Natural Earth country fallback (OSM is complete enough)
|
|
||||||
* make rank assignments configurable
|
|
||||||
* allow accept languages with underscore
|
|
||||||
* new reverse-only import mode (without search index table)
|
|
||||||
* rely on boundaries only for states and countries
|
|
||||||
* update osm2pgsql, now using a configurable style
|
|
||||||
* provide multiple import styles
|
|
||||||
* improve search when house number and postcodes are dropped
|
|
||||||
* overhaul of setup code
|
|
||||||
* add support for PHPUnit 6
|
|
||||||
* update test database
|
|
||||||
* various documentation improvements
|
|
||||||
|
|
||||||
3.2.0
|
3.2.0
|
||||||
|
|
||||||
* complete rewrite of reverse search algorithm
|
* complete rewrite of reverse search algorithm
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -47,17 +47,11 @@ License
|
|||||||
|
|
||||||
The source code is available under a GPLv2 license.
|
The source code is available under a GPLv2 license.
|
||||||
|
|
||||||
|
Contact and Bug reports
|
||||||
|
======================
|
||||||
|
|
||||||
Contributing
|
For questions you can join the geocoding mailinglist, see
|
||||||
============
|
|
||||||
|
|
||||||
Contributions are welcome. For details see [contribution guide](CONTRIBUTING.md).
|
|
||||||
|
|
||||||
Both bug reports and pull requests are welcome.
|
|
||||||
|
|
||||||
|
|
||||||
Mailing list
|
|
||||||
============
|
|
||||||
|
|
||||||
For questions you can join the geocoding mailing list, see
|
|
||||||
https://lists.openstreetmap.org/listinfo/geocoding
|
https://lists.openstreetmap.org/listinfo/geocoding
|
||||||
|
|
||||||
|
Bugs may be reported on the github project site:
|
||||||
|
https://github.com/openstreetmap/Nominatim
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ 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
|
||||||
|
|
||||||
@define('CONST_Database_DSN', 'pgsql:host=localhost;port=9999;user=postgres;dbname=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`
|
||||||
|
|
||||||
|
|||||||
9
Vagrantfile
vendored
9
Vagrantfile
vendored
@@ -23,15 +23,6 @@ Vagrant.configure("2") do |config|
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
config.vm.define "ubuntu18nginx" do |sub|
|
|
||||||
sub.vm.box = "bento/ubuntu-18.04"
|
|
||||||
sub.vm.provision :shell do |s|
|
|
||||||
s.path = "vagrant/Install-on-Ubuntu-18-nginx.sh"
|
|
||||||
s.privileged = false
|
|
||||||
s.args = [checkout]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
config.vm.define "ubuntu16" do |sub|
|
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|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
#!@PHP_BIN@ -Cq
|
|
||||||
<?php
|
|
||||||
require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
|
|
||||||
require_once(CONST_BasePath.'/@script_source@');
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once(dirname(dirname(__FILE__)).'/settings/settings.php');
|
|
||||||
require_once(CONST_BasePath.'/@script_source@');
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# Fallback Country Boundaries
|
|
||||||
|
|
||||||
Each place is assigned a `country_code` and partition. Partitions derive from `country_code`.
|
|
||||||
|
|
||||||
Nominatim imports two pre-generated files
|
|
||||||
|
|
||||||
* `data/country_name.sql` (country code, name, default language, partition)
|
|
||||||
* `data/country_osm_grid.sql` (country code, geometry)
|
|
||||||
|
|
||||||
before creating places in the database. This helps with fast lookups and missing data (e.g. if the data the user wants to import doesn't contain any country places).
|
|
||||||
|
|
||||||
The number of countries in the world can change (South Sudan created 2011, Germany reunification), so can their boundaries. This document explain how the pre-generated files can be updated.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Country code
|
|
||||||
|
|
||||||
Each place is assigned a two letter country_code based on its location, e.g. `gb` for Great Britain. Or `NULL` if no suitable country is found (usually it's in open water then).
|
|
||||||
|
|
||||||
In `sql/functions.sql: get_country_code(geometry)` the place's center is checked against
|
|
||||||
|
|
||||||
1. country places already imported from the user's data file. Places are imported by rank low-to-high. Lowest rank 2 is countries so most places should be matched. Still the data file might be incomplete.
|
|
||||||
2. if unmatched: OSM grid boundaries
|
|
||||||
3. if still unmatched: OSM grid boundaries, but allow a small distance
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Partitions
|
|
||||||
|
|
||||||
Each place is assigned partition, which is a number 0..250. 0 is fallback/other.
|
|
||||||
|
|
||||||
During place indexing (`sql/functions.sql: placex_insert()`) a place is assigned the partition based on its country code (`sql/functions.sql: get_partition(country_code)`). It checks in the `country_name` table.
|
|
||||||
|
|
||||||
Most countries have their own partition, some share a partition. Thus partition counts vary greatly.
|
|
||||||
|
|
||||||
Several database tables are split by partition to allow queries to run against less indices and improve caching.
|
|
||||||
|
|
||||||
* `location_area_large_<partition>`
|
|
||||||
* `search_name_<partition>`
|
|
||||||
* `location_road_<partition>`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Data files
|
|
||||||
|
|
||||||
### data/country_name.sql
|
|
||||||
|
|
||||||
Export from existing database table plus manual changes. `country_default_language_code` most taken from [https://wiki.openstreetmap.org/wiki/Nominatim/Country_Codes](), see `utils/country_languages.php`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### data/country_osm_grid.sql
|
|
||||||
|
|
||||||
`country_grid.sql` merges territories by country. Then uses `function.sql: quad_split_geometry` to split each country into multiple [Quadtree](https://en.wikipedia.org/wiki/Quadtree) polygons for faster point-in-polygon lookups.
|
|
||||||
|
|
||||||
To visualize one country as geojson feature collection, e.g. for loading into [geojson.io](http://geojson.io/):
|
|
||||||
|
|
||||||
```
|
|
||||||
-- http://www.postgresonline.com/journal/archives/267-Creating-GeoJSON-Feature-Collections-with-JSON-and-PostGIS-functions.html
|
|
||||||
|
|
||||||
SELECT row_to_json(fc)
|
|
||||||
FROM (
|
|
||||||
SELECT 'FeatureCollection' As type, array_to_json(array_agg(f)) As features
|
|
||||||
FROM (
|
|
||||||
SELECT 'Feature' As type,
|
|
||||||
ST_AsGeoJSON(lg.geometry)::json As geometry,
|
|
||||||
row_to_json((country_code, area)) As properties
|
|
||||||
FROM country_osm_grid As lg where country_code='mx'
|
|
||||||
) As f
|
|
||||||
) As fc;
|
|
||||||
```
|
|
||||||
|
|
||||||
`cat /tmp/query.sql | psql -At nominatim > /tmp/mexico.quad.geojson`
|
|
||||||
|
|
||||||

|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 320 KiB |
@@ -1,56 +0,0 @@
|
|||||||
# GB Postcodes
|
|
||||||
|
|
||||||
|
|
||||||
The server [importing instructions](https://www.nominatim.org/release-docs/latest/admin/Import-and-Update/) allow optionally download [`gb_postcode_data.sql.gz`](https://www.nominatim.org/data/gb_postcode_data.sql.gz). This document explains how the file got created.
|
|
||||||
|
|
||||||
## GB vs UK
|
|
||||||
|
|
||||||
GB (Great Britain) is more correct as the Ordnance Survey dataset doesn't contain postcodes from Northern Ireland.
|
|
||||||
|
|
||||||
## Importing separately after the initial import
|
|
||||||
|
|
||||||
If you forgot to download the file, or have a new version, you can import it separately:
|
|
||||||
|
|
||||||
1. Import the downloaded `gb_postcode_data.sql.gz` file.
|
|
||||||
|
|
||||||
2. Run the SQL query `SELECT count(getorcreate_postcode_id(postcode)) FROM gb_postcode;`. This will update the search index.
|
|
||||||
|
|
||||||
3. Run `utils/setup.php --calculate-postcodes` from the build directory. This will copy data form the `gb_postcode` table to the `location_postcodes` table.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Converting Code-Point Open data
|
|
||||||
|
|
||||||
1. Download from [Code-Point® Open](https://www.ordnancesurvey.co.uk/business-and-government/products/code-point-open.html). It requires an email address where a download link will be send to.
|
|
||||||
|
|
||||||
2. `unzip codepo_gb.zip`
|
|
||||||
|
|
||||||
Unpacked you'll see a directory of CSV files.
|
|
||||||
|
|
||||||
$ more codepo_gb/Data/CSV/n.csv
|
|
||||||
"N1 0AA",10,530626,183961,"E92000001","E19000003","E18000007","","E09000019","E05000368"
|
|
||||||
"N1 0AB",10,530559,183978,"E92000001","E19000003","E18000007","","E09000019","E05000368"
|
|
||||||
|
|
||||||
The coordinates are "Northings" and "Eastings" in [OSGB 1936](http://epsg.io/1314) projection. They can be projected to WGS84 like this
|
|
||||||
|
|
||||||
SELECT ST_AsText(ST_Transform(ST_SetSRID('POINT(530626 183961)'::geometry,27700), 4326));
|
|
||||||
POINT(-0.117872733220225 51.5394424719303)
|
|
||||||
|
|
||||||
[-0.117872733220225 51.5394424719303 on OSM map](https://www.openstreetmap.org/?mlon=-0.117872733220225&mlat=51.5394424719303&zoom=16)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3. Create database, import CSV files, add geometry column, dump into file
|
|
||||||
|
|
||||||
DBNAME=create_gb_postcode_file
|
|
||||||
createdb $DBNAME
|
|
||||||
echo 'CREATE EXTENSION postgis' | psql $DBNAME
|
|
||||||
|
|
||||||
cat data/gb_postcode_table.sql | psql $DBNAME
|
|
||||||
cat codepo_gb/Data/CSV/*.csv | ./data-sources/gb-postcodes/convert_codepoint.php | psql $DBNAME
|
|
||||||
cat codepo_gb/Doc/licence.txt | iconv -f iso-8859-1 -t utf-8 | dos2unix | sed 's/^/-- /g' > gb_postcode_data.sql
|
|
||||||
pg_dump -a -t gb_postcode $DBNAME | grep -v '^--' >> gb_postcode_data.sql
|
|
||||||
|
|
||||||
gzip -9 -f gb_postcode_data.sql
|
|
||||||
ls -lah gb_postcode_data.*
|
|
||||||
# dropdb $DBNAME
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
echo <<< EOT
|
|
||||||
|
|
||||||
ALTER TABLE gb_postcode ADD COLUMN easting bigint;
|
|
||||||
ALTER TABLE gb_postcode ADD COLUMN northing bigint;
|
|
||||||
|
|
||||||
TRUNCATE gb_postcode;
|
|
||||||
|
|
||||||
COPY gb_postcode (id, postcode, easting, northing) FROM stdin;
|
|
||||||
|
|
||||||
EOT;
|
|
||||||
|
|
||||||
$iCounter = 0;
|
|
||||||
while ($sLine = fgets(STDIN)) {
|
|
||||||
$aColumns = str_getcsv($sLine);
|
|
||||||
|
|
||||||
// insert space before the third last position
|
|
||||||
// https://stackoverflow.com/a/9144834
|
|
||||||
$postcode = $aColumns[0];
|
|
||||||
$postcode = preg_replace('/\s*(...)$/', ' $1', $postcode);
|
|
||||||
|
|
||||||
echo join("\t", array($iCounter, $postcode, $aColumns[2], $aColumns[3]))."\n";
|
|
||||||
|
|
||||||
$iCounter = $iCounter + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
echo <<< EOT
|
|
||||||
\.
|
|
||||||
|
|
||||||
UPDATE gb_postcode SET geometry=ST_Transform(ST_SetSRID(CONCAT('POINT(', easting, ' ', northing, ')')::geometry, 27700), 4326);
|
|
||||||
|
|
||||||
ALTER TABLE gb_postcode DROP COLUMN easting;
|
|
||||||
ALTER TABLE gb_postcode DROP COLUMN northing;
|
|
||||||
|
|
||||||
EOT;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# US TIGER address data
|
|
||||||
|
|
||||||
Convert [TIGER](https://www.census.gov/geo/maps-data/data/tiger.html)/Line dataset of the US Census Bureau to SQL files which can be imported by Nominatim. The created tables in the Nominatim database are separate from OpenStreetMap tables and get queried at search time separately.
|
|
||||||
|
|
||||||
The dataset gets updated once per year. Downloading is prone to be slow (can take a full day) and converting them can take hours as well.
|
|
||||||
|
|
||||||
Replace '2019' with the current year throughout.
|
|
||||||
|
|
||||||
1. Install the GDAL library and python bindings and the unzip tool
|
|
||||||
|
|
||||||
# Ubuntu:
|
|
||||||
sudo apt-get install python3-gdal unzip
|
|
||||||
|
|
||||||
2. Get the TIGER 2019 data. You will need the EDGES files
|
|
||||||
(3,233 zip files, 11GB total).
|
|
||||||
|
|
||||||
wget -r ftp://ftp2.census.gov/geo/tiger/TIGER2019/EDGES/
|
|
||||||
|
|
||||||
3. Convert the data into SQL statements. Adjust the file paths in the scripts as needed
|
|
||||||
|
|
||||||
cd data-sources/us-tiger
|
|
||||||
./convert.sh <input-path> <output-path>
|
|
||||||
|
|
||||||
4. Maybe: package the created files
|
|
||||||
|
|
||||||
tar -czf tiger2019-nominatim-preprocessed.tar.gz tiger
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
INPATH=$1
|
|
||||||
OUTPATH=$2
|
|
||||||
|
|
||||||
if [[ ! -d "$INPATH" ]]; then
|
|
||||||
echo "input path does not exist"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -d "$OUTPATH" ]]; then
|
|
||||||
echo "output path does not exist"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
INREGEX='_([0-9]{5})_edges.zip'
|
|
||||||
WORKPATH="$OUTPATH/tmp-workdir/"
|
|
||||||
mkdir -p "$WORKPATH"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
INFILES=($INPATH/*.zip)
|
|
||||||
echo "Found ${#INFILES[*]} files."
|
|
||||||
|
|
||||||
for F in ${INFILES[*]}; do
|
|
||||||
# echo $F
|
|
||||||
|
|
||||||
if [[ "$F" =~ $INREGEX ]]; then
|
|
||||||
COUNTYID=${BASH_REMATCH[1]}
|
|
||||||
SHAPEFILE="$WORKPATH/$(basename $F '.zip').shp"
|
|
||||||
SQLFILE="$OUTPATH/$COUNTYID.sql"
|
|
||||||
|
|
||||||
unzip -o -q -d "$WORKPATH" "$F"
|
|
||||||
if [[ ! -e "$SHAPEFILE" ]]; then
|
|
||||||
echo "Unzip failed. $SHAPEFILE not found."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
./tiger_address_convert.py "$SHAPEFILE" "$SQLFILE"
|
|
||||||
|
|
||||||
rm $WORKPATH/*
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
OUTFILES=($OUTPATH/*.sql)
|
|
||||||
echo "Wrote ${#OUTFILES[*]} files."
|
|
||||||
|
|
||||||
rmdir $WORKPATH
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
## Add Wikipedia and Wikidata to Nominatim
|
|
||||||
|
|
||||||
OSM contributors frequently tag items with links to Wikipedia and Wikidata. Nominatim can use the page ranking of Wikipedia pages to help indicate the relative importance of osm features. This is done by calculating an importance score between 0 and 1 based on the number of inlinks to an article for a location. If two places have the same name and one is more important than the other, the wikipedia score often points to the correct place.
|
|
||||||
|
|
||||||
These scripts extract and prepare both Wikipedia page rank and Wikidata links for use in Nominatim.
|
|
||||||
|
|
||||||
#### Create a new postgres DB for Processing
|
|
||||||
|
|
||||||
Due to the size of initial and intermediate tables, processing can be done in an external database:
|
|
||||||
```
|
|
||||||
CREATE DATABASE wikiprocessingdb;
|
|
||||||
```
|
|
||||||
---
|
|
||||||
Wikipedia
|
|
||||||
---
|
|
||||||
|
|
||||||
Processing these data requires a large amount of disk space (~1TB) and considerable time (>24 hours).
|
|
||||||
|
|
||||||
#### Import & Process Wikipedia tables
|
|
||||||
|
|
||||||
This step downloads and converts [Wikipedia](https://dumps.wikimedia.org/) page data SQL dumps to postgreSQL files which can be imported and processed with pagelink information from Wikipedia language sites to calculate importance scores.
|
|
||||||
|
|
||||||
- The script will processes data from whatever set of Wikipedia languages are specified in the initial languages array
|
|
||||||
|
|
||||||
- Note that processing the top 40 Wikipedia languages can take over a day, and will add nearly 1TB to the processing database. The final output tables will be approximately 11GB and 2GB in size
|
|
||||||
|
|
||||||
To download, convert, and import the data, then process summary statistics and compute importance scores, run:
|
|
||||||
```
|
|
||||||
./wikipedia_import.sh
|
|
||||||
```
|
|
||||||
---
|
|
||||||
Wikidata
|
|
||||||
---
|
|
||||||
|
|
||||||
This script downloads and processes Wikidata to enrich the previously created Wikipedia tables for use in Nominatim.
|
|
||||||
|
|
||||||
#### Import & Process Wikidata
|
|
||||||
|
|
||||||
This step downloads and converts [Wikidata](https://dumps.wikimedia.org/wikidatawiki/) page data SQL dumps to postgreSQL files which can be processed and imported into Nominatim database. Also utilizes Wikidata Query Service API to discover and include place types.
|
|
||||||
|
|
||||||
- Script presumes that the user has already processed Wikipedia tables as specified above
|
|
||||||
|
|
||||||
- Script requires wikidata_place_types.txt and wikidata_place_type_levles.csv
|
|
||||||
|
|
||||||
- script requires the [jq json parser](https://stedolan.github.io/jq/)
|
|
||||||
|
|
||||||
- Script processes data from whatever set of Wikipedia languages are specified in the initial languages array
|
|
||||||
|
|
||||||
- Script queries Wikidata Query Service API and imports all instances of place types listed in wikidata_place_types.txt
|
|
||||||
|
|
||||||
- Script updates wikipedia_articles table with extracted wikidata
|
|
||||||
|
|
||||||
By including Wikidata in the wikipedia_articles table, new connections can be made on the fly from the Nominatim placex table to wikipedia_article importance scores.
|
|
||||||
|
|
||||||
To download, convert, and import the data, then process required items, run:
|
|
||||||
```
|
|
||||||
./wikidata_import.sh
|
|
||||||
```
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
psqlcmd() {
|
|
||||||
psql wikiprocessingdb
|
|
||||||
}
|
|
||||||
|
|
||||||
mysql2pgsqlcmd() {
|
|
||||||
./mysql2pgsql.perl /dev/stdin /dev/stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# list the languages to process (refer to List of Wikipedias here: https://en.wikipedia.org/wiki/List_of_Wikipedias)
|
|
||||||
|
|
||||||
language=( "ar" "bg" "ca" "cs" "da" "de" "en" "es" "eo" "eu" "fa" "fr" "ko" "hi" "hr" "id" "it" "he" "lt" "hu" "ms" "nl" "ja" "no" "pl" "pt" "kk" "ro" "ru" "sk" "sl" "sr" "fi" "sv" "tr" "uk" "vi" "vo" "war" "zh" )
|
|
||||||
|
|
||||||
|
|
||||||
# get a few wikidata dump tables
|
|
||||||
|
|
||||||
wget https://dumps.wikimedia.org/wikidatawiki/latest/wikidatawiki-latest-geo_tags.sql.gz
|
|
||||||
wget https://dumps.wikimedia.org/wikidatawiki/latest/wikidatawiki-latest-page.sql.gz
|
|
||||||
wget https://dumps.wikimedia.org/wikidatawiki/latest/wikidatawiki-latest-wb_items_per_site.sql.gz
|
|
||||||
|
|
||||||
|
|
||||||
# import wikidata tables
|
|
||||||
|
|
||||||
gzip -dc wikidatawiki-latest-geo_tags.sql.gz | mysql2pgsqlcmd | psqlcmd
|
|
||||||
gzip -dc wikidatawiki-latest-page.sql.gz | mysql2pgsqlcmd | psqlcmd
|
|
||||||
gzip -dc wikidatawiki-latest-wb_items_per_site.sql.gz | mysql2pgsqlcmd | psqlcmd
|
|
||||||
|
|
||||||
|
|
||||||
# get wikidata places from wikidata query API
|
|
||||||
|
|
||||||
while read F ; do
|
|
||||||
wget "https://query.wikidata.org/bigdata/namespace/wdq/sparql?format=json&query=SELECT ?item WHERE{?item wdt:P31*/wdt:P279*wd:$F;}" -O $F.json
|
|
||||||
jq -r '.results | .[] | .[] | [.item.value] | @csv' $F.json >> $F.txt
|
|
||||||
awk -v qid=$F '{print $0 ","qid}' $F.txt | sed -e 's!"http://www.wikidata.org/entity/!!' | sed 's/"//g' >> $F.csv
|
|
||||||
cat $F.csv >> wikidata_place_dump.csv
|
|
||||||
rm $F.json $F.txt $F.csv
|
|
||||||
done < wikidata_place_types.txt
|
|
||||||
|
|
||||||
|
|
||||||
# import wikidata places
|
|
||||||
|
|
||||||
echo "CREATE TABLE wikidata_place_dump (item text, instance_of text);" | psqlcmd
|
|
||||||
echo "COPY wikidata_place_dump (item, instance_of) FROM '/srv/nominatim/Nominatim/data-sources/wikipedia-wikidata/wikidata_place_dump.csv' DELIMITER ',' CSV;" | psqlcmd
|
|
||||||
|
|
||||||
echo "CREATE TABLE wikidata_place_type_levels (place_type text, level integer);" | psqlcmd
|
|
||||||
echo "COPY wikidata_place_type_levels (place_type, level) FROM '/srv/nominatim/Nominatim/data-sources/wikipedia-wikidata/wikidata_place_type_levels.csv' DELIMITER ',' CSV HEADER;" | psqlcmd
|
|
||||||
|
|
||||||
|
|
||||||
# create derived tables
|
|
||||||
|
|
||||||
echo "CREATE TABLE geo_earth_primary AS SELECT gt_page_id, gt_lat, gt_lon FROM geo_tags WHERE gt_globe = 'earth' AND gt_primary = 1 AND NOT( gt_lat < -90 OR gt_lat > 90 OR gt_lon < -180 OR gt_lon > 180 OR gt_lat=0 OR gt_lon=0) ;" | psqlcmd
|
|
||||||
echo "CREATE TABLE geo_earth_wikidata AS SELECT DISTINCT geo_earth_primary.gt_page_id, geo_earth_primary.gt_lat, geo_earth_primary.gt_lon, page.page_title, page.page_namespace FROM geo_earth_primary LEFT OUTER JOIN page ON (geo_earth_primary.gt_page_id = page.page_id) ORDER BY geo_earth_primary.gt_page_id;" | psqlcmd
|
|
||||||
|
|
||||||
echo "ALTER TABLE wikidata_place_dump ADD COLUMN ont_level integer, ADD COLUMN lat numeric(11,8), ADD COLUMN lon numeric(11,8);" | psqlcmd
|
|
||||||
echo "UPDATE wikidata_place_dump SET ont_level = wikidata_place_type_levels.level FROM wikidata_place_type_levels WHERE wikidata_place_dump.instance_of = wikidata_place_type_levels.place_type;" | psqlcmd
|
|
||||||
|
|
||||||
echo "CREATE TABLE wikidata_places AS SELECT DISTINCT ON (item) item, instance_of, MAX(ont_level) AS ont_level, lat, lon FROM wikidata_place_dump GROUP BY item, instance_of, ont_level, lat, lon ORDER BY item;" | psqlcmd
|
|
||||||
echo "UPDATE wikidata_places SET lat = geo_earth_wikidata.gt_lat, lon = geo_earth_wikidata.gt_lon FROM geo_earth_wikidata WHERE wikidata_places.item = geo_earth_wikidata.page_title" | psqlcmd
|
|
||||||
|
|
||||||
|
|
||||||
# process language pages
|
|
||||||
|
|
||||||
echo "CREATE TABLE wikidata_pages (item text, instance_of text, lat numeric(11,8), lon numeric(11,8), ips_site_page text, language text );" | psqlcmd
|
|
||||||
|
|
||||||
for i in "${language[@]}"
|
|
||||||
do
|
|
||||||
echo "CREATE TABLE wikidata_${i}_pages as select wikidata_places.item, wikidata_places.instance_of, wikidata_places.lat, wikidata_places.lon, wb_items_per_site.ips_site_page FROM wikidata_places LEFT JOIN wb_items_per_site ON (CAST (( LTRIM(wikidata_places.item, 'Q')) AS INTEGER) = wb_items_per_site.ips_item_id) WHERE ips_site_id = '${i}wiki' AND LEFT(wikidata_places.item,1) = 'Q' order by wikidata_places.item;" | psqlcmd
|
|
||||||
echo "ALTER TABLE wikidata_${i}_pages ADD COLUMN language text;" | psqlcmd
|
|
||||||
echo "UPDATE wikidata_${i}_pages SET language = '${i}';" | psqlcmd
|
|
||||||
echo "INSERT INTO wikidata_pages SELECT item, instance_of, lat, lon, ips_site_page, language FROM wikidata_${i}_pages;" | psqlcmd
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "ALTER TABLE wikidata_pages ADD COLUMN wp_page_title text;" | psqlcmd
|
|
||||||
echo "UPDATE wikidata_pages SET wp_page_title = REPLACE(ips_site_page, ' ', '_');" | psqlcmd
|
|
||||||
echo "ALTER TABLE wikidata_pages DROP COLUMN ips_site_page;" | psqlcmd
|
|
||||||
|
|
||||||
|
|
||||||
# add wikidata to wikipedia_article table
|
|
||||||
|
|
||||||
echo "UPDATE wikipedia_article SET lat = wikidata_pages.lat, lon = wikidata_pages.lon, wd_page_title = wikidata_pages.item, instance_of = wikidata_pages.instance_of FROM wikidata_pages WHERE wikipedia_article.language = wikidata_pages.language AND wikipedia_article.title = wikidata_pages.wp_page_title;" | psqlcmd
|
|
||||||
echo "CREATE TABLE wikipedia_article_slim AS SELECT * FROM wikipedia_article WHERE wikidata_id IS NOT NULL;" | psqlcmd
|
|
||||||
echo "ALTER TABLE wikipedia_article RENAME TO wikipedia_article_full;" | psqlcmd
|
|
||||||
echo "ALTER TABLE wikipedia_article_slim RENAME TO wikipedia_article;" | psqlcmd
|
|
||||||
|
|
||||||
|
|
||||||
# clean up intermediate tables
|
|
||||||
|
|
||||||
echo "DROP TABLE wikidata_place_dump;" | psqlcmd
|
|
||||||
echo "DROP TABLE geo_earth_primary;" | psqlcmd
|
|
||||||
for i in "${language[@]}"
|
|
||||||
do
|
|
||||||
echo "DROP TABLE wikidata_${i}_pages;" | psqlcmd
|
|
||||||
done
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
psqlcmd() {
|
|
||||||
psql wikiprocessingdb
|
|
||||||
}
|
|
||||||
|
|
||||||
mysql2pgsqlcmd() {
|
|
||||||
./mysql2pgsql.perl /dev/stdin /dev/stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# list the languages to process (refer to List of Wikipedias here: https://en.wikipedia.org/wiki/List_of_Wikipedias)
|
|
||||||
|
|
||||||
language=( "ar" "bg" "ca" "cs" "da" "de" "en" "es" "eo" "eu" "fa" "fr" "ko" "hi" "hr" "id" "it" "he" "lt" "hu" "ms" "nl" "ja" "no" "pl" "pt" "kk" "ro" "ru" "sk" "sl" "sr" "fi" "sv" "tr" "uk" "vi" "vo" "war" "zh" )
|
|
||||||
|
|
||||||
|
|
||||||
# create wikipedia calculation tables
|
|
||||||
|
|
||||||
echo "CREATE TABLE linkcounts (language text, title text, count integer, sumcount integer, lat double precision, lon double precision);" | psqlcmd
|
|
||||||
echo "CREATE TABLE wikipedia_article (language text NOT NULL, title text NOT NULL, langcount integer, othercount integer, totalcount integer, lat double precision, lon double precision, importance double precision, title_en text, osm_type character(1), osm_id bigint );" | psqlcmd
|
|
||||||
echo "CREATE TABLE wikipedia_redirect (language text, from_title text, to_title text );" | psqlcmd
|
|
||||||
|
|
||||||
|
|
||||||
# download individual wikipedia language tables
|
|
||||||
|
|
||||||
for i in "${language[@]}"
|
|
||||||
do
|
|
||||||
wget https://dumps.wikimedia.org/${i}wiki/latest/${i}wiki-latest-page.sql.gz
|
|
||||||
wget https://dumps.wikimedia.org/${i}wiki/latest/${i}wiki-latest-pagelinks.sql.gz
|
|
||||||
wget https://dumps.wikimedia.org/${i}wiki/latest/${i}wiki-latest-langlinks.sql.gz
|
|
||||||
wget https://dumps.wikimedia.org/${i}wiki/latest/${i}wiki-latest-redirect.sql.gz
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
# import individual wikipedia language tables
|
|
||||||
|
|
||||||
for i in "${language[@]}"
|
|
||||||
do
|
|
||||||
gzip -dc ${i}wiki-latest-pagelinks.sql.gz | sed "s/\`pagelinks\`/\`${i}pagelinks\`/g" | mysql2pgsqlcmd | psqlcmd
|
|
||||||
gzip -dc ${i}wiki-latest-page.sql.gz | sed "s/\`page\`/\`${i}page\`/g" | mysql2pgsqlcmd | psqlcmd
|
|
||||||
gzip -dc ${i}wiki-latest-langlinks.sql.gz | sed "s/\`langlinks\`/\`${i}langlinks\`/g" | mysql2pgsqlcmd | psqlcmd
|
|
||||||
gzip -dc ${i}wiki-latest-redirect.sql.gz | sed "s/\`redirect\`/\`${i}redirect\`/g" | mysql2pgsqlcmd | psqlcmd
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
# process language tables and associated pagelink counts
|
|
||||||
|
|
||||||
for i in "${language[@]}"
|
|
||||||
do
|
|
||||||
echo "create table ${i}pagelinkcount as select pl_title as title,count(*) as count from ${i}pagelinks where pl_namespace = 0 group by pl_title;" | psqlcmd
|
|
||||||
echo "insert into linkcounts select '${i}',pl_title,count(*) from ${i}pagelinks where pl_namespace = 0 group by pl_title;" | psqlcmd
|
|
||||||
echo "insert into wikipedia_redirect select '${i}',page_title,rd_title from ${i}redirect join ${i}page on (rd_from = page_id) where page_namespace = 0 and rd_namespace = 0;" | psqlcmd
|
|
||||||
echo "alter table ${i}pagelinkcount add column othercount integer;" | psqlcmd
|
|
||||||
echo "update ${i}pagelinkcount set othercount = 0;" | psqlcmd
|
|
||||||
for j in "${language[@]}"
|
|
||||||
do
|
|
||||||
echo "update ${i}pagelinkcount set othercount = ${i}pagelinkcount.othercount + x.count from (select page_title as title,count from ${i}langlinks join ${i}page on (ll_from = page_id) join ${j}pagelinkcount on (ll_lang = '${j}' and ll_title = title)) as x where x.title = ${i}pagelinkcount.title;" | psqlcmd
|
|
||||||
done
|
|
||||||
echo "insert into wikipedia_article select '${i}', title, count, othercount, count+othercount from ${i}pagelinkcount;" | psqlcmd
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
# calculate importance score for each wikipedia page
|
|
||||||
|
|
||||||
echo "update wikipedia_article set importance = log(totalcount)/log((select max(totalcount) from wikipedia_article))" | psqlcmd
|
|
||||||
|
|
||||||
|
|
||||||
# clean up intermediate tables to conserve space
|
|
||||||
|
|
||||||
for i in "${language[@]}"
|
|
||||||
do
|
|
||||||
echo "DROP TABLE ${i}pagelinks;" | psqlcmd
|
|
||||||
echo "DROP TABLE ${i}page;" | psqlcmd
|
|
||||||
echo "DROP TABLE ${i}langlinks;" | psqlcmd
|
|
||||||
echo "DROP TABLE ${i}redirect;" | psqlcmd
|
|
||||||
echo "DROP TABLE ${i}pagelinkcount;" | psqlcmd
|
|
||||||
done
|
|
||||||
@@ -1,199 +0,0 @@
|
|||||||
place_type,level
|
|
||||||
Q9842,4
|
|
||||||
Q9430,3
|
|
||||||
Q928830,4
|
|
||||||
Q9259,1
|
|
||||||
Q91028,5
|
|
||||||
Q8514,2
|
|
||||||
Q8502,2
|
|
||||||
Q83405,3
|
|
||||||
Q82794,2
|
|
||||||
Q820477,1
|
|
||||||
Q811979,1
|
|
||||||
Q8072,2
|
|
||||||
Q79007,2
|
|
||||||
Q786014,3
|
|
||||||
Q75848,2
|
|
||||||
Q75520,2
|
|
||||||
Q728937,4
|
|
||||||
Q7275,2
|
|
||||||
Q719456,3
|
|
||||||
Q7075,3
|
|
||||||
Q697295,4
|
|
||||||
Q6852233,2
|
|
||||||
Q682943,3
|
|
||||||
Q665487,5
|
|
||||||
Q655686,3
|
|
||||||
Q643589,5
|
|
||||||
Q641226,2
|
|
||||||
Q631305,2
|
|
||||||
Q6256,2
|
|
||||||
Q6023295,2
|
|
||||||
Q5773747,5
|
|
||||||
Q56061,1
|
|
||||||
Q55659167,4
|
|
||||||
Q55488,4
|
|
||||||
Q55465477,3
|
|
||||||
Q54050,2
|
|
||||||
Q532,3
|
|
||||||
Q53060,2
|
|
||||||
Q52177058,4
|
|
||||||
Q515716,5
|
|
||||||
Q5153984,4
|
|
||||||
Q515,3
|
|
||||||
Q5144960,5
|
|
||||||
Q5119,4
|
|
||||||
Q5119,4
|
|
||||||
Q5107,2
|
|
||||||
Q5084,4
|
|
||||||
Q5031071,4
|
|
||||||
Q5003624,2
|
|
||||||
Q4989906,1
|
|
||||||
Q4976993,3
|
|
||||||
Q486972,1
|
|
||||||
Q486972,2
|
|
||||||
Q483110,3
|
|
||||||
Q4830453,4
|
|
||||||
Q47521,3
|
|
||||||
Q473972,1
|
|
||||||
Q46831,2
|
|
||||||
Q46614560,5
|
|
||||||
Q44782,3
|
|
||||||
Q44613,4
|
|
||||||
Q44539,4
|
|
||||||
Q44494,2
|
|
||||||
Q44377,2
|
|
||||||
Q4421,2
|
|
||||||
Q43501,2
|
|
||||||
Q4286337,3
|
|
||||||
Q42523,3
|
|
||||||
Q41176,2
|
|
||||||
Q40357,3
|
|
||||||
Q4022,4
|
|
||||||
Q40080,2
|
|
||||||
Q39816,2
|
|
||||||
Q39715,3
|
|
||||||
Q39614,1
|
|
||||||
Q3957,3
|
|
||||||
Q3947,4
|
|
||||||
Q3914,3
|
|
||||||
Q38723,2
|
|
||||||
Q38720,3
|
|
||||||
Q3623867,5
|
|
||||||
Q35666,2
|
|
||||||
Q355304,3
|
|
||||||
Q35509,2
|
|
||||||
Q35112127,3
|
|
||||||
Q34985575,4
|
|
||||||
Q34876,5
|
|
||||||
Q34763,2
|
|
||||||
Q34627,4
|
|
||||||
Q3455524,3
|
|
||||||
Q34442,4
|
|
||||||
Q33837,2
|
|
||||||
Q33506,3
|
|
||||||
Q32815,4
|
|
||||||
Q3257686,2
|
|
||||||
Q3240715,2
|
|
||||||
Q3191695,5
|
|
||||||
Q3153117,2
|
|
||||||
Q30198,2
|
|
||||||
Q30139652,3
|
|
||||||
Q294422,3
|
|
||||||
Q2870166,3
|
|
||||||
Q27686,3
|
|
||||||
Q274153,3
|
|
||||||
Q271669,1
|
|
||||||
Q2659904,2
|
|
||||||
Q24529780,2
|
|
||||||
Q24354,3
|
|
||||||
Q2354973,4
|
|
||||||
Q23442,2
|
|
||||||
Q23413,3
|
|
||||||
Q23397,3
|
|
||||||
Q2327515,4
|
|
||||||
Q2311958,5
|
|
||||||
Q22927291,6
|
|
||||||
Q22698,1
|
|
||||||
Q2175765,4
|
|
||||||
Q205495,4
|
|
||||||
Q204832,3
|
|
||||||
Q2042028,2
|
|
||||||
Q202216,6
|
|
||||||
Q1970725,3
|
|
||||||
Q194203,5
|
|
||||||
Q194195,2
|
|
||||||
Q190429,2
|
|
||||||
Q185187,3
|
|
||||||
Q185113,2
|
|
||||||
Q183366,2
|
|
||||||
Q1799794,1
|
|
||||||
Q1788454,4
|
|
||||||
Q1785071,3
|
|
||||||
Q1777138,3
|
|
||||||
Q177634,2
|
|
||||||
Q177380,2
|
|
||||||
Q174814,4
|
|
||||||
Q174782,2
|
|
||||||
Q17350442,2
|
|
||||||
Q17343829,3
|
|
||||||
Q17334923,0
|
|
||||||
Q17018380,3
|
|
||||||
Q16970,4
|
|
||||||
Q16917,3
|
|
||||||
Q16831714,4
|
|
||||||
Q165,3
|
|
||||||
Q160742,4
|
|
||||||
Q159719,3
|
|
||||||
Q159334,4
|
|
||||||
Q15640612,5
|
|
||||||
Q15324,2
|
|
||||||
Q15284,5
|
|
||||||
Q15243209,6
|
|
||||||
Q152081,1
|
|
||||||
Q15195406,4
|
|
||||||
Q1500350,5
|
|
||||||
Q149621,5
|
|
||||||
Q14757767,4
|
|
||||||
Q14350,3
|
|
||||||
Q1410668,3
|
|
||||||
Q1394476,3
|
|
||||||
Q1377575,2
|
|
||||||
Q1353183,3
|
|
||||||
Q134447,4
|
|
||||||
Q133215,3
|
|
||||||
Q133056,2
|
|
||||||
Q13221722,3
|
|
||||||
Q13220204,2
|
|
||||||
Q1311958,4
|
|
||||||
Q1303167,3
|
|
||||||
Q130003,3
|
|
||||||
Q12518,2
|
|
||||||
Q12516,3
|
|
||||||
Q1248784,3
|
|
||||||
Q123705,3
|
|
||||||
Q12323,3
|
|
||||||
Q12284,4
|
|
||||||
Q12280,4
|
|
||||||
Q121359,2
|
|
||||||
Q1210950,2
|
|
||||||
Q11755880,3
|
|
||||||
Q11707,3
|
|
||||||
Q11315,3
|
|
||||||
Q11303,3
|
|
||||||
Q1115575,4
|
|
||||||
Q1107656,1
|
|
||||||
Q10864048,1
|
|
||||||
Q1076486,2
|
|
||||||
Q105731,3
|
|
||||||
Q105190,3
|
|
||||||
Q1048525,3
|
|
||||||
Q102496,5
|
|
||||||
Q28872924,1
|
|
||||||
Q15617994,1
|
|
||||||
Q159313,2
|
|
||||||
Q24398318,3
|
|
||||||
Q327333,2
|
|
||||||
Q43229,1
|
|
||||||
Q860861,1
|
|
||||||
Q4989906,1
|
|
||||||
|
@@ -1,195 +0,0 @@
|
|||||||
Q9842
|
|
||||||
Q9430
|
|
||||||
Q928830
|
|
||||||
Q9259
|
|
||||||
Q91028
|
|
||||||
Q8514
|
|
||||||
Q8502
|
|
||||||
Q83405
|
|
||||||
Q82794
|
|
||||||
Q820477
|
|
||||||
Q811979
|
|
||||||
Q8072
|
|
||||||
Q79007
|
|
||||||
Q786014
|
|
||||||
Q75848
|
|
||||||
Q75520
|
|
||||||
Q728937
|
|
||||||
Q7275
|
|
||||||
Q719456
|
|
||||||
Q7075
|
|
||||||
Q697295
|
|
||||||
Q6852233
|
|
||||||
Q682943
|
|
||||||
Q665487
|
|
||||||
Q655686
|
|
||||||
Q643589
|
|
||||||
Q641226
|
|
||||||
Q631305
|
|
||||||
Q6256
|
|
||||||
Q6023295
|
|
||||||
Q5773747
|
|
||||||
Q56061
|
|
||||||
Q55659167
|
|
||||||
Q55488
|
|
||||||
Q55465477
|
|
||||||
Q54050
|
|
||||||
Q532
|
|
||||||
Q53060
|
|
||||||
Q52177058
|
|
||||||
Q515716
|
|
||||||
Q5153984
|
|
||||||
Q515
|
|
||||||
Q5144960
|
|
||||||
Q5119
|
|
||||||
Q5107
|
|
||||||
Q5084
|
|
||||||
Q5031071
|
|
||||||
Q5003624
|
|
||||||
Q4989906
|
|
||||||
Q4976993
|
|
||||||
Q486972
|
|
||||||
Q483110
|
|
||||||
Q4830453
|
|
||||||
Q47521
|
|
||||||
Q473972
|
|
||||||
Q46831
|
|
||||||
Q46614560
|
|
||||||
Q44782
|
|
||||||
Q44613
|
|
||||||
Q44539
|
|
||||||
Q44494
|
|
||||||
Q44377
|
|
||||||
Q4421
|
|
||||||
Q43501
|
|
||||||
Q4286337
|
|
||||||
Q42523
|
|
||||||
Q41176
|
|
||||||
Q40357
|
|
||||||
Q4022
|
|
||||||
Q40080
|
|
||||||
Q39816
|
|
||||||
Q39715
|
|
||||||
Q39614
|
|
||||||
Q3957
|
|
||||||
Q3947
|
|
||||||
Q3914
|
|
||||||
Q38723
|
|
||||||
Q38720
|
|
||||||
Q3623867
|
|
||||||
Q35666
|
|
||||||
Q355304
|
|
||||||
Q35509
|
|
||||||
Q35112127
|
|
||||||
Q34985575
|
|
||||||
Q34876
|
|
||||||
Q34763
|
|
||||||
Q34627
|
|
||||||
Q3455524
|
|
||||||
Q34442
|
|
||||||
Q33837
|
|
||||||
Q33506
|
|
||||||
Q32815
|
|
||||||
Q3257686
|
|
||||||
Q3240715
|
|
||||||
Q3191695
|
|
||||||
Q3153117
|
|
||||||
Q30198
|
|
||||||
Q30139652
|
|
||||||
Q294422
|
|
||||||
Q2870166
|
|
||||||
Q27686
|
|
||||||
Q274153
|
|
||||||
Q271669
|
|
||||||
Q2659904
|
|
||||||
Q24529780
|
|
||||||
Q24354
|
|
||||||
Q2354973
|
|
||||||
Q23442
|
|
||||||
Q23413
|
|
||||||
Q23397
|
|
||||||
Q2327515
|
|
||||||
Q2311958
|
|
||||||
Q22927291
|
|
||||||
Q22698
|
|
||||||
Q2175765
|
|
||||||
Q205495
|
|
||||||
Q204832
|
|
||||||
Q2042028
|
|
||||||
Q202216
|
|
||||||
Q1970725
|
|
||||||
Q194203
|
|
||||||
Q194195
|
|
||||||
Q190429
|
|
||||||
Q185187
|
|
||||||
Q185113
|
|
||||||
Q183366
|
|
||||||
Q1799794
|
|
||||||
Q1788454
|
|
||||||
Q1785071
|
|
||||||
Q1777138
|
|
||||||
Q177634
|
|
||||||
Q177380
|
|
||||||
Q174814
|
|
||||||
Q174782
|
|
||||||
Q17350442
|
|
||||||
Q17343829
|
|
||||||
Q17334923
|
|
||||||
Q17018380
|
|
||||||
Q16970
|
|
||||||
Q16917
|
|
||||||
Q16831714
|
|
||||||
Q165
|
|
||||||
Q160742
|
|
||||||
Q159719
|
|
||||||
Q159334
|
|
||||||
Q15640612
|
|
||||||
Q15324
|
|
||||||
Q15284
|
|
||||||
Q15243209
|
|
||||||
Q152081
|
|
||||||
Q15195406
|
|
||||||
Q1500350
|
|
||||||
Q149621
|
|
||||||
Q14757767
|
|
||||||
Q14350
|
|
||||||
Q1410668
|
|
||||||
Q1394476
|
|
||||||
Q1377575
|
|
||||||
Q1353183
|
|
||||||
Q134447
|
|
||||||
Q133215
|
|
||||||
Q133056
|
|
||||||
Q13221722
|
|
||||||
Q13220204
|
|
||||||
Q1311958
|
|
||||||
Q1303167
|
|
||||||
Q130003
|
|
||||||
Q12518
|
|
||||||
Q12516
|
|
||||||
Q1248784
|
|
||||||
Q123705
|
|
||||||
Q12323
|
|
||||||
Q12284
|
|
||||||
Q12280
|
|
||||||
Q121359
|
|
||||||
Q1210950
|
|
||||||
Q11755880
|
|
||||||
Q11707
|
|
||||||
Q11315
|
|
||||||
Q11303
|
|
||||||
Q1115575
|
|
||||||
Q1107656
|
|
||||||
Q10864048
|
|
||||||
Q1076486
|
|
||||||
Q105731
|
|
||||||
Q105190
|
|
||||||
Q1048525
|
|
||||||
Q102496
|
|
||||||
Q28872924
|
|
||||||
Q15617994
|
|
||||||
Q159313
|
|
||||||
Q24398318
|
|
||||||
Q327333
|
|
||||||
Q43229
|
|
||||||
Q860861
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
|
|
||||||
## Wikidata place types and related OSM Tags
|
|
||||||
|
|
||||||
Wikidata does not have any official ontologies, however the [DBpedia project](https://wiki.dbpedia.org/) has created an [ontology](https://wiki.dbpedia.org/services-resources/ontology) that covered [place types](http://mappings.dbpedia.org/server/ontology/classes/#Place). The table below used the DBpedia place ontology as a starting point, and is provided as a cross-reference to the relevant OSM tags.
|
|
||||||
|
|
||||||
The Wikidata place types listed in the table below can be used in conjunction with the [Wikidata Query Service](https://query.wikidata.org/) to retrieve instances of those place types from the Wikidata knowledgebase.
|
|
||||||
|
|
||||||
```
|
|
||||||
SELECT ?item ?lat ?lon
|
|
||||||
WHERE {
|
|
||||||
?item wdt:P31*/wdt:P279*wd:Q9430; wdt:P625 ?pt.
|
|
||||||
?item p:P625?loc.
|
|
||||||
?loc psv:P625?cnode.
|
|
||||||
?cnode wikibase:geoLatitude?lat.
|
|
||||||
?cnode wikibase:geoLongitude?lon.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
An example json return for all instances of the Wikidata item "Q9430" (Ocean) can be seen at [json](https://query.wikidata.org/bigdata/namespace/wdq/sparql?format=json&query=SELECT?item?lat?lon%20WHERE{?item%20wdt:P31*/wdt:P279*wd:Q9430;wdt:P625?pt.?item%20p:P625?loc.?loc%20psv:P625?cnode.?cnode%20wikibase:geoLatitude?lat.?cnode%20wikibase:geoLongitude?lon.})
|
|
||||||
|
|
||||||
**NOTE** the OSM tags listed are those listed in the wikidata entries, and not all the possible matches for tags within OSM.
|
|
||||||
|
|
||||||
|
|
||||||
title | concept | OSM Tag |
|
|
||||||
-----------|---------------------------------------|------------------|
|
|
||||||
[Q17334923](https://www.wikidata.org/entity/Q17334923) | Location | |
|
|
||||||
[Q811979](https://www.wikidata.org/entity/Q811979) | Architectural Structure | |
|
|
||||||
[Q194195](https://www.wikidata.org/entity/Q194195) | Amusement park |
|
|
||||||
[Q204832](https://www.wikidata.org/entity/Q204832) | Roller coaster | [attraction=roller_coaster](https://wiki.openstreetmap.org/wiki/Tag:attraction=roller_coaster) |
|
|
||||||
[Q2870166](https://www.wikidata.org/entity/Q2870166) | Water ride | |
|
|
||||||
[Q641226](https://www.wikidata.org/entity/Q641226) | Arena | [amenity=events_centre](https://wiki.openstreetmap.org/wiki/Tag:amenity=events_centre) |
|
|
||||||
[Q41176](https://www.wikidata.org/entity/Q41176) | Building | [building=yes](https://wiki.openstreetmap.org/wiki/Key:building) |
|
|
||||||
[Q1303167](https://www.wikidata.org/entity/Q1303167) | Barn | [building=barn](https://wiki.openstreetmap.org/wiki/Tag:building=barn) |
|
|
||||||
[Q655686](https://www.wikidata.org/entity/Q655686) | Commercial building | [building=commercial](https://wiki.openstreetmap.org/wiki/Tag:building=commercial) |
|
|
||||||
[Q4830453](https://www.wikidata.org/entity/Q4830453) | Business | |
|
|
||||||
[Q7075](https://www.wikidata.org/entity/Q7075) | Library | [amenity=library](https://wiki.openstreetmap.org/wiki/Tag:amenity=library) |
|
|
||||||
[Q133215](https://www.wikidata.org/entity/Q133215) | Casino | [amenity=casino](https://wiki.openstreetmap.org/wiki/Tag:amenity=casino) |
|
|
||||||
[Q23413](https://www.wikidata.org/entity/Q23413) | Castle | [historic=castle](https://wiki.openstreetmap.org/wiki/Tag:historic=castle) |
|
|
||||||
[Q83405](https://www.wikidata.org/entity/Q83405) | Factory | |
|
|
||||||
[Q53060](https://www.wikidata.org/entity/Q53060) | Gate | [barrier=gate](https://wiki.openstreetmap.org/wiki/Tag:barrier=gate) |cnode%20wikibase:geoLatitude?lat.?cnode%20wikibase:geoLongitude?lon.})
|
|
||||||
[Q11755880](https://www.wikidata.org/entity/Q11755880) | Residential Building | [building=residential](https://wiki.openstreetmap.org/wiki/Tag:building=residential) |
|
|
||||||
[Q3947](https://www.wikidata.org/entity/Q3947) | House | [building=house](https://wiki.openstreetmap.org/wiki/Tag:building=house) |
|
|
||||||
[Q35112127](https://www.wikidata.org/entity/Q35112127) | Historic Building | |
|
|
||||||
[Q5773747](https://www.wikidata.org/entity/Q5773747) | Historic house | |
|
|
||||||
[Q38723](https://www.wikidata.org/entity/Q38723) | Higher Education Institution |
|
|
||||||
[Q3914](https://www.wikidata.org/entity/Q3914) | School | [amenity=school](https://wiki.openstreetmap.org/wiki/Tag:amenity=school) |
|
|
||||||
[Q9842](https://www.wikidata.org/entity/Q9842) | Primary school | |
|
|
||||||
[Q159334](https://www.wikidata.org/entity/Q159334) | Secondary school | |
|
|
||||||
[Q16917](https://www.wikidata.org/entity/Q16917) | Hospital | [amenity=hospital](https://wiki.openstreetmap.org/wiki/Tag:amenity=hospital), [healthcare=hospital](https://wiki.openstreetmap.org/wiki/Tag:healthcare=hospital), [building=hospital](https://wiki.openstreetmap.org/wiki/Tag:building=hospital) |
|
|
||||||
[Q27686](https://www.wikidata.org/entity/Q27686) | Hotel | [tourism=hotel](https://wiki.openstreetmap.org/wiki/Tag:tourism=hotel), [building=hotel](https://wiki.openstreetmap.org/wiki/Tag:building=hotel) |
|
|
||||||
[Q33506](https://www.wikidata.org/entity/Q33506) | Museum | [tourism=museum](https://wiki.openstreetmap.org/wiki/Tag:tourism=museum) |
|
|
||||||
[Q40357](https://www.wikidata.org/entity/Q40357) | Prison | [amenity=prison](https://wiki.openstreetmap.org/wiki/Tag:amenity=prison) |
|
|
||||||
[Q24398318](https://www.wikidata.org/entity/Q24398318) | Religious Building | |
|
|
||||||
[Q160742](https://www.wikidata.org/entity/Q160742) | Abbey | |
|
|
||||||
[Q16970](https://www.wikidata.org/entity/Q16970) | Church (building) | [building=church](https://wiki.openstreetmap.org/wiki/Tag:building=church) |
|
|
||||||
[Q44613](https://www.wikidata.org/entity/Q44613) | Monastery | [amenity=monastery](https://wiki.openstreetmap.org/wiki/Tag:amenity=monastery) |
|
|
||||||
[Q32815](https://www.wikidata.org/entity/Q32815) | Mosque | [building=mosque](https://wiki.openstreetmap.org/wiki/Tag:building=mosque) |
|
|
||||||
[Q697295](https://www.wikidata.org/entity/Q697295) | Shrine | [building=shrine](https://wiki.openstreetmap.org/wiki/Tag:building=shrine) |
|
|
||||||
[Q34627](https://www.wikidata.org/entity/Q34627) | Synagogue | [building=synagogue](https://wiki.openstreetmap.org/wiki/Tag:building=synagogue) |
|
|
||||||
[Q44539](https://www.wikidata.org/entity/Q44539) | Temple | [building=temple](https://wiki.openstreetmap.org/wiki/Tag:building=temple) |
|
|
||||||
[Q11707](https://www.wikidata.org/entity/Q11707) | Restaurant | [amenity=restaurant](https://wiki.openstreetmap.org/wiki/Tag:amenity=restaurant) |
|
|
||||||
[Q11315](https://www.wikidata.org/entity/Q11315) | Shopping mall | [shop=mall](https://wiki.openstreetmap.org/wiki/Tag:shop=mall), [shop=shopping_centre](https://wiki.openstreetmap.org/wiki/Tag:shop=shopping_centre) |
|
|
||||||
[Q11303](https://www.wikidata.org/entity/Q11303) | Skyscraper | |
|
|
||||||
[Q17350442](https://www.wikidata.org/entity/Q17350442) | Venue | |
|
|
||||||
[Q41253](https://www.wikidata.org/entity/Q41253) | Movie Theater | [amenity=cinema](https://wiki.openstreetmap.org/wiki/Tag:amenity=cinema) |
|
|
||||||
[Q483110](https://www.wikidata.org/entity/Q483110) | Stadium | [leisure=stadium](https://wiki.openstreetmap.org/wiki/Tag:leisure=stadium), [building=stadium](https://wiki.openstreetmap.org/wiki/Tag:building=stadium) |
|
|
||||||
[Q24354](https://www.wikidata.org/entity/Q24354) | Theater (structure) | [amenity=theatre](https://wiki.openstreetmap.org/wiki/Tag:amenity=theatre) |
|
|
||||||
[Q121359](https://www.wikidata.org/entity/Q121359) | Infrastructure | |
|
|
||||||
[Q1248784](https://www.wikidata.org/entity/Q1248784) | Airport | |
|
|
||||||
[Q12323](https://www.wikidata.org/entity/Q12323) | Dam | [waterway=dam](https://wiki.openstreetmap.org/wiki/Tag:waterway=dam) |
|
|
||||||
[Q1353183](https://www.wikidata.org/entity/Q1353183) | Launch pad | |
|
|
||||||
[Q105190](https://www.wikidata.org/entity/Q105190) | Levee | [man_made=dyke](https://wiki.openstreetmap.org/wiki/Tag:man_made=dyke) |
|
|
||||||
[Q105731](https://www.wikidata.org/entity/Q105731) | Lock (water navigation) | [lock=yes](https://wiki.openstreetmap.org/wiki/Key:lock) |
|
|
||||||
[Q44782](https://www.wikidata.org/entity/Q44782) | Port | |
|
|
||||||
[Q159719](https://www.wikidata.org/entity/Q159719) | Power station | [power=plant](https://wiki.openstreetmap.org/wiki/Tag:power=plant) |
|
|
||||||
[Q174814](https://www.wikidata.org/entity/Q174814) | Electrical substation | |
|
|
||||||
[Q134447](https://www.wikidata.org/entity/Q134447) | Nuclear power plant | [plant:source=nuclear](https://wiki.openstreetmap.org/wiki/Tag:plant:source=nuclear) |
|
|
||||||
[Q786014](https://www.wikidata.org/entity/Q786014) | Rest area | [highway=rest_area](https://wiki.openstreetmap.org/wiki/Tag:highway=rest_area), [highway=services](https://wiki.openstreetmap.org/wiki/Tag:highway=services) |
|
|
||||||
[Q12280](https://www.wikidata.org/entity/Q12280) | Bridge | [bridge=* ](https://wiki.openstreetmap.org/wiki/Key:bridge), [man_made=bridge](https://wiki.openstreetmap.org/wiki/Tag:man_made=bridge) |
|
|
||||||
[Q728937](https://www.wikidata.org/entity/Q728937) | Railroad Line | [railway=rail](https://wiki.openstreetmap.org/wiki/Tag:railway=rail) |
|
|
||||||
[Q1311958](https://www.wikidata.org/entity/Q1311958) | Railway Tunnel | |
|
|
||||||
[Q34442](https://www.wikidata.org/entity/Q34442) | Road | [highway=* ](https://wiki.openstreetmap.org/wiki/Key:highway), [route=road](https://wiki.openstreetmap.org/wiki/Tag:route=road) |
|
|
||||||
[Q1788454](https://www.wikidata.org/entity/Q1788454) | Road junction | |
|
|
||||||
[Q44377](https://www.wikidata.org/entity/Q44377) | Tunnel | [tunnel=* ](https://wiki.openstreetmap.org/wiki/Key:tunnel) |
|
|
||||||
[Q5031071](https://www.wikidata.org/entity/Q5031071) | Canal tunnel | |
|
|
||||||
[Q719456](https://www.wikidata.org/entity/Q719456) | Station | [public_transport=station](https://wiki.openstreetmap.org/wiki/Tag:public_transport=station) |
|
|
||||||
[Q205495](https://www.wikidata.org/entity/Q205495) | Filling station | [amenity=fuel](https://wiki.openstreetmap.org/wiki/Tag:amenity=fuel) |
|
|
||||||
[Q928830](https://www.wikidata.org/entity/Q928830) | Metro station | [station=subway](https://wiki.openstreetmap.org/wiki/Tag:station=subway) |
|
|
||||||
[Q55488](https://www.wikidata.org/entity/Q55488) | Train station | [railway=station](https://wiki.openstreetmap.org/wiki/Tag:railway=station) |
|
|
||||||
[Q2175765](https://www.wikidata.org/entity/Q2175765) | Tram stop | [railway=tram_stop](https://wiki.openstreetmap.org/wiki/Tag:railway=tram_stop), [public_transport=stop_position](https://wiki.openstreetmap.org/wiki/Tag:public_transport=stop_position) |
|
|
||||||
[Q6852233](https://www.wikidata.org/entity/Q6852233) | Military building | |
|
|
||||||
[Q44494](https://www.wikidata.org/entity/Q44494) | Mill (grinding) | |
|
|
||||||
[Q185187](https://www.wikidata.org/entity/Q185187) | Watermill | [man_made=watermill](https://wiki.openstreetmap.org/wiki/Tag:man_made=watermill) |
|
|
||||||
[Q38720](https://www.wikidata.org/entity/Q38720) | Windmill | [man_made=windmill](https://wiki.openstreetmap.org/wiki/Tag:man_made=windmill) |
|
|
||||||
[Q4989906](https://www.wikidata.org/entity/Q4989906) | Monument | [historic=monument](https://wiki.openstreetmap.org/wiki/Tag:historic=monument) |
|
|
||||||
[Q5003624](https://www.wikidata.org/entity/Q5003624) | Memorial | [historic=memorial](https://wiki.openstreetmap.org/wiki/Tag:historic=memorial) |
|
|
||||||
[Q271669](https://www.wikidata.org/entity/Q271669) | Landform | |
|
|
||||||
[Q190429](https://www.wikidata.org/entity/Q190429) | Depression (geology) | |
|
|
||||||
[Q17018380](https://www.wikidata.org/entity/Q17018380) | Bight (geography) | |
|
|
||||||
[Q54050](https://www.wikidata.org/entity/Q54050) | Hill | |
|
|
||||||
[Q1210950](https://www.wikidata.org/entity/Q1210950) | Channel (geography) | |
|
|
||||||
[Q23442](https://www.wikidata.org/entity/Q23442) | Island | [place=island](https://wiki.openstreetmap.org/wiki/Tag:place=island) |
|
|
||||||
[Q42523](https://www.wikidata.org/entity/Q42523) | Atoll | |
|
|
||||||
[Q34763](https://www.wikidata.org/entity/Q34763) | Peninsula | |
|
|
||||||
[Q355304](https://www.wikidata.org/entity/Q355304) | Watercourse | |
|
|
||||||
[Q30198](https://www.wikidata.org/entity/Q30198) | Marsh | [wetland=marsh](https://wiki.openstreetmap.org/wiki/Tag:wetland=marsh) |
|
|
||||||
[Q75520](https://www.wikidata.org/entity/Q75520) | Plateau | |
|
|
||||||
[Q2042028](https://www.wikidata.org/entity/Q2042028) | Ravine | |
|
|
||||||
[Q631305](https://www.wikidata.org/entity/Q631305) | Rock formation | |
|
|
||||||
[Q12516](https://www.wikidata.org/entity/Q12516) | Pyramid | |
|
|
||||||
[Q1076486](https://www.wikidata.org/entity/Q1076486) | Sports venue | |
|
|
||||||
[Q682943](https://www.wikidata.org/entity/Q682943) | Cricket field | [sport=cricket](https://wiki.openstreetmap.org/wiki/Tag:sport=cricket) |
|
|
||||||
[Q1048525](https://www.wikidata.org/entity/Q1048525) | Golf course | [leisure=golf_course](https://wiki.openstreetmap.org/wiki/Tag:leisure=golf_course) |
|
|
||||||
[Q1777138](https://www.wikidata.org/entity/Q1777138) | Race track | [highway=raceway](https://wiki.openstreetmap.org/wiki/Tag:highway=raceway) |
|
|
||||||
[Q130003](https://www.wikidata.org/entity/Q130003) | Ski resort | |
|
|
||||||
[Q174782](https://www.wikidata.org/entity/Q174782) | Town square | [place=square](https://wiki.openstreetmap.org/wiki/Tag:place=square) |
|
|
||||||
[Q12518](https://www.wikidata.org/entity/Q12518) | Tower | [building=tower](https://wiki.openstreetmap.org/wiki/Tag:building=tower), [man_made=tower](https://wiki.openstreetmap.org/wiki/Tag:man_made=tower) |
|
|
||||||
[Q39715](https://www.wikidata.org/entity/Q39715) | Lighthouse | [man_made=lighthouse](https://wiki.openstreetmap.org/wiki/Tag:man_made=lighthouse) |
|
|
||||||
[Q274153](https://www.wikidata.org/entity/Q274153) | Water tower | [building=water_tower](https://wiki.openstreetmap.org/wiki/Tag:building=water_tower), [man_made=water_tower](https://wiki.openstreetmap.org/wiki/Tag:man_made=water_tower) |
|
|
||||||
[Q43501](https://www.wikidata.org/entity/Q43501) | Zoo | [tourism=zoo](https://wiki.openstreetmap.org/wiki/Tag:tourism=zoo) |
|
|
||||||
[Q39614](https://www.wikidata.org/entity/Q39614) | Cemetery | [amenity=grave_yard](https://wiki.openstreetmap.org/wiki/Tag:amenity=grave_yard), [landuse=cemetery](https://wiki.openstreetmap.org/wiki/Tag:landuse=cemetery) |
|
|
||||||
[Q152081](https://www.wikidata.org/entity/Q152081) | Concentration camp | |
|
|
||||||
[Q1107656](https://www.wikidata.org/entity/Q1107656) | Garden | [leisure=garden](https://wiki.openstreetmap.org/wiki/Tag:leisure=garden) |
|
|
||||||
[Q820477](https://www.wikidata.org/entity/Q820477) | Mine | |
|
|
||||||
[Q33837](https://www.wikidata.org/entity/Q33837) | Archipelago | [place=archipelago](https://wiki.openstreetmap.org/wiki/Tag:place=archipelago) |
|
|
||||||
[Q40080](https://www.wikidata.org/entity/Q40080) | Beach | [natural=beach](https://wiki.openstreetmap.org/wiki/Tag:natural=beach) |
|
|
||||||
[Q15324](https://www.wikidata.org/entity/Q15324) | Body of water | [natural=water](https://wiki.openstreetmap.org/wiki/Tag:natural=water) |
|
|
||||||
[Q23397](https://www.wikidata.org/entity/Q23397) | Lake | [water=lake](https://wiki.openstreetmap.org/wiki/Tag:water=lake) |
|
|
||||||
[Q9430](https://www.wikidata.org/entity/Q9430) | Ocean | |
|
|
||||||
[Q165](https://www.wikidata.org/entity/Q165) | Sea | |
|
|
||||||
[Q47521](https://www.wikidata.org/entity/Q47521) | Stream | |
|
|
||||||
[Q12284](https://www.wikidata.org/entity/Q12284) | Canal | [waterway=canal](https://wiki.openstreetmap.org/wiki/Tag:waterway=canal) |
|
|
||||||
[Q4022](https://www.wikidata.org/entity/Q4022) | River | [waterway=river](https://wiki.openstreetmap.org/wiki/Tag:waterway=river), [type=waterway](https://wiki.openstreetmap.org/wiki/Relation:waterway) |
|
|
||||||
[Q185113](https://www.wikidata.org/entity/Q185113) | Cape | [natural=cape](https://wiki.openstreetmap.org/wiki/Tag:natural=cape) |
|
|
||||||
[Q35509](https://www.wikidata.org/entity/Q35509) | Cave | [natural=cave_entrance](https://wiki.openstreetmap.org/wiki/Tag:natural=cave_entrance) |
|
|
||||||
[Q8514](https://www.wikidata.org/entity/Q8514) | Desert | |
|
|
||||||
[Q4421](https://www.wikidata.org/entity/Q4421) | Forest | [natural=wood](https://wiki.openstreetmap.org/wiki/Tag:natural=wood) |
|
|
||||||
[Q35666](https://www.wikidata.org/entity/Q35666) | Glacier | [natural=glacier](https://wiki.openstreetmap.org/wiki/Tag:natural=glacier) |
|
|
||||||
[Q177380](https://www.wikidata.org/entity/Q177380) | Hot spring | |
|
|
||||||
[Q8502](https://www.wikidata.org/entity/Q8502) | Mountain | [natural=peak](https://wiki.openstreetmap.org/wiki/Tag:natural=peak) |
|
|
||||||
[Q133056](https://www.wikidata.org/entity/Q133056) | Mountain pass | |
|
|
||||||
[Q46831](https://www.wikidata.org/entity/Q46831) | Mountain range | |
|
|
||||||
[Q39816](https://www.wikidata.org/entity/Q39816) | Valley | [natural=valley](https://wiki.openstreetmap.org/wiki/Tag:natural=valley) |
|
|
||||||
[Q8072](https://www.wikidata.org/entity/Q8072) | Volcano | [natural=volcano](https://wiki.openstreetmap.org/wiki/Tag:natural=volcano) |
|
|
||||||
[Q43229](https://www.wikidata.org/entity/Q43229) | Organization | |
|
|
||||||
[Q327333](https://www.wikidata.org/entity/Q327333) | Government agency | [office=government](https://wiki.openstreetmap.org/wiki/Tag:office=government)|
|
|
||||||
[Q22698](https://www.wikidata.org/entity/Q22698) | Park | [leisure=park](https://wiki.openstreetmap.org/wiki/Tag:leisure=park) |
|
|
||||||
[Q159313](https://www.wikidata.org/entity/Q159313) | Urban agglomeration | |
|
|
||||||
[Q177634](https://www.wikidata.org/entity/Q177634) | Community | |
|
|
||||||
[Q5107](https://www.wikidata.org/entity/Q5107) | Continent | [place=continent](https://wiki.openstreetmap.org/wiki/Tag:place=continent) |
|
|
||||||
[Q6256](https://www.wikidata.org/entity/Q6256) | Country | [place=country](https://wiki.openstreetmap.org/wiki/Tag:place=country) |
|
|
||||||
[Q75848](https://www.wikidata.org/entity/Q75848) | Gated community | |
|
|
||||||
[Q3153117](https://www.wikidata.org/entity/Q3153117) | Intercommunality | |
|
|
||||||
[Q82794](https://www.wikidata.org/entity/Q82794) | Region | |
|
|
||||||
[Q56061](https://www.wikidata.org/entity/Q56061) | Administrative division | [boundary=administrative](https://wiki.openstreetmap.org/wiki/Tag:boundary=administrative) |
|
|
||||||
[Q665487](https://www.wikidata.org/entity/Q665487) | Diocese | |
|
|
||||||
[Q4976993](https://www.wikidata.org/entity/Q4976993) | Parish | [boundary=civil_parish](https://wiki.openstreetmap.org/wiki/Tag:boundary=civil_parish) |
|
|
||||||
[Q194203](https://www.wikidata.org/entity/Q194203) | Arrondissements of France | |
|
|
||||||
[Q91028](https://www.wikidata.org/entity/Q91028) | Arrondissements of Belgium | |
|
|
||||||
[Q3623867](https://www.wikidata.org/entity/Q3623867) | Arrondissements of Benin | |
|
|
||||||
[Q2311958](https://www.wikidata.org/entity/Q2311958) | Canton (country subdivision) | [political_division=canton](https://wiki.openstreetmap.org/wiki/FR:Cantons_in_France) |
|
|
||||||
[Q643589](https://www.wikidata.org/entity/Q643589) | Department | |
|
|
||||||
[Q202216](https://www.wikidata.org/entity/Q202216) | Overseas department and region | |
|
|
||||||
[Q149621](https://www.wikidata.org/entity/Q149621) | District | [place=district](https://wiki.openstreetmap.org/wiki/Tag:place=district) |
|
|
||||||
[Q15243209](https://www.wikidata.org/wiki/Q15243209) | Historic district | |
|
|
||||||
[Q5144960](https://www.wikidata.org/entity/Q5144960) | Microregion | |
|
|
||||||
[Q15284](https://www.wikidata.org/entity/Q15284) | Municipality | |
|
|
||||||
[Q515716](https://www.wikidata.org/entity/Q515716) | Prefecture | |
|
|
||||||
[Q34876](https://www.wikidata.org/entity/Q34876) | Province | |
|
|
||||||
[Q3191695](https://www.wikidata.org/entity/Q3191695) | Regency (Indonesia) | |
|
|
||||||
[Q1970725](https://www.wikidata.org/entity/Q1970725) | Natural region | |
|
|
||||||
[Q486972](https://www.wikidata.org/entity/Q486972) | Human settlement | |
|
|
||||||
[Q515](https://www.wikidata.org/entity/Q515) | City | [place=city](https://wiki.openstreetmap.org/wiki/Tag:place=city) |
|
|
||||||
[Q5119](https://www.wikidata.org/entity/Q5119) | Capital city | [capital=yes](https://wiki.openstreetmap.org/wiki/Key:capital) |
|
|
||||||
[Q4286337](https://www.wikidata.org/entity/Q4286337) | City district | |
|
|
||||||
[Q1394476](https://www.wikidata.org/entity/Q1394476) | Civil township | |
|
|
||||||
[Q1115575](https://www.wikidata.org/entity/Q1115575) | Civil parish | [designation=civil_parish](https://wiki.openstreetmap.org/wiki/Tag:designation=civil_parish) |
|
|
||||||
[Q5153984](https://www.wikidata.org/entity/Q5153984) | Commune-level subdivisions | |
|
|
||||||
[Q123705](https://www.wikidata.org/entity/Q123705) | Neighbourhood | [place=neighbourhood](https://wiki.openstreetmap.org/wiki/Tag:place=neighbourhood) |
|
|
||||||
[Q1500350](https://www.wikidata.org/entity/Q1500350) | Townships of China | |
|
|
||||||
[Q17343829](https://www.wikidata.org/entity/Q17343829) | Unincorporated Community | |
|
|
||||||
[Q3957](https://www.wikidata.org/entity/Q3957) | Town | [place=town](https://wiki.openstreetmap.org/wiki/Tag:place=town) |
|
|
||||||
[Q532](https://www.wikidata.org/entity/Q532) | Village | [place=village](https://wiki.openstreetmap.org/wiki/Tag:place=village) |
|
|
||||||
[Q5084](https://www.wikidata.org/entity/Q5084) | Hamlet | [place=hamlet](https://wiki.openstreetmap.org/wiki/Tag:place=hamlet) |
|
|
||||||
[Q7275](https://www.wikidata.org/entity/Q7275) | State | |
|
|
||||||
[Q79007](https://www.wikidata.org/entity/Q79007) | Street | |
|
|
||||||
[Q473972](https://www.wikidata.org/entity/Q473972) | Protected area | [boundary=protected_area](https://wiki.openstreetmap.org/wiki/Tag:boundary=protected_area) |
|
|
||||||
[Q1377575](https://www.wikidata.org/entity/Q1377575) | Wildlife refuge | |
|
|
||||||
[Q1410668](https://www.wikidata.org/entity/Q1410668) | National Wildlife Refuge | [protection_title=National Wildlife Refuge](ownership=national), [ownership=national](https://wiki.openstreetmap.org/wiki/Tag:ownership=national)|
|
|
||||||
[Q9259](https://www.wikidata.org/entity/Q9259) | World Heritage Site | |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Future Work
|
|
||||||
|
|
||||||
The Wikidata improvements to Nominatim can be further enhanced by:
|
|
||||||
|
|
||||||
- continuing to add new Wikidata links to OSM objects
|
|
||||||
- increasing the number of place types accounted for in the wikipedia_articles table
|
|
||||||
- working to use place types in the wikipedia_article matching process
|
|
||||||
258
data/country_naturalearthdata.sql
Normal file
258
data/country_naturalearthdata.sql
Normal file
File diff suppressed because one or more lines are too long
38126
data/us_postcode.sql
Normal file
38126
data/us_postcode.sql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
|||||||
SET statement_timeout = 0;
|
|
||||||
SET client_encoding = 'UTF8';
|
|
||||||
SET check_function_bodies = false;
|
|
||||||
SET client_min_messages = warning;
|
|
||||||
|
|
||||||
SET search_path = public, pg_catalog;
|
|
||||||
|
|
||||||
SET default_tablespace = '';
|
|
||||||
|
|
||||||
SET default_with_oids = false;
|
|
||||||
|
|
||||||
CREATE TABLE us_postcode (
|
|
||||||
postcode text,
|
|
||||||
x double precision,
|
|
||||||
y double precision
|
|
||||||
);
|
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
configure_file(mkdocs.yml ../mkdocs.yml)
|
configure_file(mkdocs.yml ../mkdocs.yml)
|
||||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/appendix)
|
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/appendix)
|
||||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data-sources)
|
|
||||||
|
|
||||||
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
|
||||||
@@ -13,12 +12,6 @@ ADD_CUSTOM_TARGET(doc
|
|||||||
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}/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_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/extra.css ${CMAKE_CURRENT_BINARY_DIR}/extra.css
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/data-sources/overview.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/overview.md
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/us-tiger/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/US-Tiger.md
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/gb-postcodes/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/GB-Postcodes.md
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/country-grid/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/Country-Grid.md
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/country-grid/mexico.quad.png ${CMAKE_CURRENT_BINARY_DIR}/data-sources/mexico.quad.png
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/wikipedia-wikidata/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/Wikipedia-Wikidata.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-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 ${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
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ If the reported rank is 26 or higher, you can also safely add `--index-noanalyse
|
|||||||
|
|
||||||
### PHP "open_basedir restriction in effect" warnings
|
### 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](https://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/
|
||||||
@@ -46,7 +46,7 @@ your `php.ini` file. Find the entry about timezone and set it to
|
|||||||
something like this:
|
something like this:
|
||||||
|
|
||||||
; Defines the default timezone used by the date functions
|
; Defines the default timezone used by the date functions
|
||||||
; https://php.net/date.timezone
|
; http://php.net/date.timezone
|
||||||
date.timezone = 'America/Denver'
|
date.timezone = 'America/Denver'
|
||||||
|
|
||||||
Or
|
Or
|
||||||
@@ -66,36 +66,11 @@ server development libraries (`postgresql-server-dev-9.5` on Ubuntu)
|
|||||||
and recompile (`cmake .. && make`).
|
and recompile (`cmake .. && make`).
|
||||||
|
|
||||||
|
|
||||||
## I see the error "ERROR: permission denied for language c"
|
|
||||||
|
|
||||||
`nominatim.so`, written in C, is required to be installed on the database
|
|
||||||
server. Some managed database (cloud) services like Amazon RDS do not allow
|
|
||||||
this. There is currently no work-around other than installing a database
|
|
||||||
on a non-managed machine.
|
|
||||||
|
|
||||||
|
|
||||||
### 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.
|
||||||
|
|
||||||
### I see the error: "ERROR: mmap (remap) failed"
|
|
||||||
|
|
||||||
This may be a simple out-of-memory error. Try reducing the memory used
|
|
||||||
for `--osm2pgsql-cache`. Also make sure that overcommitting memory is
|
|
||||||
allowed: `cat /proc/sys/vm/overcommit_memory` should print 0 or 1.
|
|
||||||
|
|
||||||
If you are using a flatnode file, then it may also be that the underlying
|
|
||||||
filesystem does not fully support 'mmap'. A notable candidate is virtualbox's
|
|
||||||
vboxfs.
|
|
||||||
|
|
||||||
### nominatim UPDATE failed: ERROR: buffer 179261 is not owned by resource owner Portal
|
|
||||||
|
|
||||||
Several users [reported this](https://github.com/openstreetmap/Nominatim/issues/1168) during the initial import of the database. It's
|
|
||||||
something PostgreSQL internal Nominatim doesn't control. And PostgreSQL forums
|
|
||||||
suggest it's threading related but definitely some kind of crash of a process.
|
|
||||||
Users reported either rebooting the server, different hardware or just trying
|
|
||||||
the import again worked.
|
|
||||||
|
|
||||||
### The website shows: "Could not get word tokens"
|
### The website shows: "Could not get word tokens"
|
||||||
|
|
||||||
@@ -118,11 +93,6 @@ However, you can solve this the quick and dirty way by commenting out that line
|
|||||||
sudo systemctl restart httpd
|
sudo systemctl restart httpd
|
||||||
|
|
||||||
|
|
||||||
### "must be an array or an object that implements Countable" warning in /usr/share/pear/DB.php
|
|
||||||
|
|
||||||
The warning started with PHP 7.2. Make sure you have at least [version 1.9.3 of PEAR DB](https://github.com/pear/DB/releases)
|
|
||||||
installed.
|
|
||||||
|
|
||||||
### Website reports "DB Error: insufficient permissions"
|
### 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`.
|
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`.
|
||||||
@@ -148,7 +118,7 @@ Example error message
|
|||||||
CONTEXT: PL/pgSQL function make_standard_name(text) line 5 at assignment]
|
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 Postgresql database, i.e. user postgres, needs to have access to that file.
|
||||||
|
|
||||||
The permission need to be read & executable by everybody, e.g.
|
The permission need to be read & executable by everybody, e.g.
|
||||||
|
|
||||||
@@ -163,7 +133,7 @@ When running SELinux, make sure that the
|
|||||||
|
|
||||||
### Setup.php fails with "DB Error: extension not found"
|
### Setup.php fails with "DB Error: extension not found"
|
||||||
|
|
||||||
Make sure you have the PostgreSQL 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.
|
||||||
|
|
||||||
|
|
||||||
@@ -173,7 +143,7 @@ See the installation instruction for a full list of required packages.
|
|||||||
|
|
||||||
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 [Pear module 'DB'](https://pear.php.net/package/DB/) installed.
|
have the [Pear module 'DB'](http://pear.php.net/package/DB/) installed.
|
||||||
|
|
||||||
sudo pear install DB
|
sudo pear install DB
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ the directory exists. There should be at least 40GB of free space.
|
|||||||
### 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
|
||||||
but it will improve the quality of the results if this is installed.
|
but it will improve the quality of the results if this is installed.
|
||||||
This data is available as a binary download:
|
This data is available as a binary download:
|
||||||
|
|
||||||
@@ -45,108 +45,26 @@ This data is available as a binary download:
|
|||||||
wget https://www.nominatim.org/data/wikipedia_redirect.sql.bin
|
wget https://www.nominatim.org/data/wikipedia_redirect.sql.bin
|
||||||
|
|
||||||
Combined the 2 files are around 1.5GB and add around 30GB to the install
|
Combined the 2 files are around 1.5GB and add around 30GB to the install
|
||||||
size of Nominatim. They also increase the install time by an hour or so.
|
size of nominatim. They also increase the install time by an hour or so.
|
||||||
|
|
||||||
*NOTE:* you'll need to download the Wikipedia rankings before performing
|
*NOTE:* you'll need to download the Wikipedia rankings before performing
|
||||||
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.
|
||||||
|
|
||||||
### Great Britain, USA postcodes
|
### UK postcodes
|
||||||
|
|
||||||
Nominatim can use postcodes from an external source to improve searches that
|
Nominatim can use postcodes from an external source to improve searches that involve a UK postcode. This data can be optionally downloaded:
|
||||||
involve a GB or US postcode. This data can be optionally downloaded:
|
|
||||||
|
|
||||||
cd $NOMINATIM_SOURCE_DIR/data
|
cd $NOMINATIM_SOURCE_DIR/data
|
||||||
wget https://www.nominatim.org/data/gb_postcode_data.sql.gz
|
wget https://www.nominatim.org/data/gb_postcode_data.sql.gz
|
||||||
wget https://www.nominatim.org/data/us_postcode_data.sql.gz
|
|
||||||
|
|
||||||
## Choosing the Data to Import
|
|
||||||
|
|
||||||
In its default setup Nominatim is configured to import the full OSM data
|
|
||||||
set for the entire planet. Such a setup requires a powerful machine with
|
|
||||||
at least 32GB of RAM and around 800GB of SSD hard disks. Depending on your
|
|
||||||
use case there are various ways to reduce the amount of data imported. This
|
|
||||||
section discusses these methods. They can also be combined.
|
|
||||||
|
|
||||||
### Using an extract
|
|
||||||
|
|
||||||
If you only need geocoding for a smaller region, then precomputed extracts
|
|
||||||
are a good way to reduce the database size and import time.
|
|
||||||
[Geofabrik](https://download.geofabrik.de) offers extracts for most countries.
|
|
||||||
They even have daily updates which can be used with the update process described
|
|
||||||
below. There are also
|
|
||||||
[other providers for extracts](https://wiki.openstreetmap.org/wiki/Planet.osm#Downloading).
|
|
||||||
|
|
||||||
Please be aware that some extracts are not cut exactly along the country
|
|
||||||
boundaries. As a result some parts of the boundary may be missing which means
|
|
||||||
that Nominatim cannot compute the areas for some administrative areas.
|
|
||||||
|
|
||||||
### Dropping Data Required for Dynamic Updates
|
|
||||||
|
|
||||||
About half of the data in Nominatim's database is not really used for serving
|
|
||||||
the API. It is only there to allow the data to be updated from the latest
|
|
||||||
changes from OSM. For many uses these dynamic updates are not really required.
|
|
||||||
If you don't plan to apply updates, the dynamic part of the database can be
|
|
||||||
safely dropped using the following command:
|
|
||||||
|
|
||||||
```
|
|
||||||
./utils/setup.php --drop
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that you still need to provide for sufficient disk space for the initial
|
|
||||||
import. So this option is particularly interesting if you plan to transfer the
|
|
||||||
database or reuse the space later.
|
|
||||||
|
|
||||||
### Reverse-only Imports
|
|
||||||
|
|
||||||
If you only want to use the Nominatim database for reverse lookups or
|
|
||||||
if you plan to use the installation only for exports to a
|
|
||||||
[photon](https://photon.komoot.de/) database, then you can set up a database
|
|
||||||
without search indexes. Add `--reverse-only` to your setup command above.
|
|
||||||
|
|
||||||
This saves about 5% of disk space.
|
|
||||||
|
|
||||||
### Filtering Imported Data
|
|
||||||
|
|
||||||
Nominatim normally sets up a full search database containing administrative
|
|
||||||
boundaries, places, streets, addresses and POI data. There are also other
|
|
||||||
import styles available which only read selected data:
|
|
||||||
|
|
||||||
* **settings/import-admin.style**
|
|
||||||
Only import administrative boundaries and places.
|
|
||||||
* **settings/import-street.style**
|
|
||||||
Like the admin style but also adds streets.
|
|
||||||
* **settings/import-address.style**
|
|
||||||
Import all data necessary to compute addresses down to house number level.
|
|
||||||
* **settings/import-full.style**
|
|
||||||
Default style that also includes points of interest.
|
|
||||||
|
|
||||||
The style can be changed with the configuration `CONST_Import_Style`.
|
|
||||||
|
|
||||||
To give you an idea of the impact of using the different styles, the table
|
|
||||||
below gives rough estimates of the final database size after import of a
|
|
||||||
2018 planet and after using the `--drop` option. It also shows the time
|
|
||||||
needed for the import on a machine with 32GB RAM, 4 CPUS and SSDs. Note that
|
|
||||||
the given sizes are just an estimate meant for comparison of style requirements.
|
|
||||||
Your planet import is likely to be larger as the OSM data grows with time.
|
|
||||||
|
|
||||||
style | Import time | DB size | after drop
|
|
||||||
----------|--------------|------------|------------
|
|
||||||
admin | 5h | 190 GB | 20 GB
|
|
||||||
street | 42h | 400 GB | 180 GB
|
|
||||||
address | 59h | 500 GB | 260 GB
|
|
||||||
full | 80h | 575 GB | 300 GB
|
|
||||||
|
|
||||||
You can also customize the styles further. For an description of the
|
|
||||||
style format see [the development section](../develop/Import.md).
|
|
||||||
|
|
||||||
## Initial import of the data
|
## Initial import of the data
|
||||||
|
|
||||||
**Important:** first try the import with a small extract, 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).
|
||||||
|
|
||||||
Download the data to import and load the data with the following command
|
Download the data to import and load the data with the following command:
|
||||||
from the build directory:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./utils/setup.php --osm-file <data file> --all [--osm2pgsql-cache 28000] 2>&1 | tee setup.log
|
./utils/setup.php --osm-file <data file> --all [--osm2pgsql-cache 28000] 2>&1 | tee setup.log
|
||||||
@@ -159,7 +77,7 @@ about the same size as the file you are importing but never more than
|
|||||||
2/3 of RAM available. If your machine starts swapping reduce the size.
|
2/3 of RAM available. If your machine starts swapping reduce the size.
|
||||||
|
|
||||||
Computing word frequency for search terms can improve the performance of
|
Computing word frequency for search terms can improve the performance of
|
||||||
forward geocoding in particular under high load as it helps PostgreSQL's query
|
forward geocoding in particular under high load as it helps Postgres' query
|
||||||
planner to make the right decisions. To recompute word counts run:
|
planner to make the right decisions. To recompute word counts run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -183,51 +101,69 @@ 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](https://www.census.gov/geo/maps-data/data/tiger.html)
|
Nominatim is able to use the official TIGER address set to complement the
|
||||||
address set to complement the OSM house number data in the US. You can add
|
OSM house number data in the US. You can add TIGER data to your own Nominatim
|
||||||
TIGER data to your own Nominatim instance by following these steps. The
|
instance by following these steps:
|
||||||
entire US adds about 10GB to your database.
|
|
||||||
|
|
||||||
1. Get preprocessed TIGER 2019 data and unpack it into the
|
1. Install the GDAL library and python bindings and the unzip tool
|
||||||
|
|
||||||
|
* Ubuntu: `sudo apt-get install python-gdal unzip`
|
||||||
|
* CentOS: `sudo yum install gdal-python unzip`
|
||||||
|
|
||||||
|
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
|
||||||
wget https://nominatim.org/data/tiger2019-nominatim-preprocessed.tar.gz
|
wget https://nominatim.org/data/tiger2017-nominatim-preprocessed.tar.gz
|
||||||
tar xf tiger2019-nominatim-preprocessed.tar.gz
|
tar xf tiger2017-nominatim-preprocessed.tar.gz
|
||||||
|
|
||||||
`data-source/us-tiger/README.md` explains how the data got preprocessed.
|
3. Import the data into your Nominatim database:
|
||||||
|
|
||||||
2. Import the data into your Nominatim database:
|
|
||||||
|
|
||||||
./utils/setup.php --import-tiger-data
|
./utils/setup.php --import-tiger-data
|
||||||
|
|
||||||
3. Enable use of the Tiger data in your `settings/local.php` by adding:
|
4. Enable use of the Tiger data in your `settings/local.php` by adding:
|
||||||
|
|
||||||
@define('CONST_Use_US_Tiger_Data', true);
|
@define('CONST_Use_US_Tiger_Data', true);
|
||||||
|
|
||||||
4. Apply the new settings:
|
5. Apply the new settings:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The entire US adds about 10GB to your database.
|
||||||
|
|
||||||
|
You can also process the data from the original TIGER data to create the
|
||||||
|
SQL files, Nominatim needs for the import:
|
||||||
|
|
||||||
|
1. Get the TIGER 2017 data. You will need the EDGES files
|
||||||
|
(3,234 zip files, 11GB total).
|
||||||
|
|
||||||
|
wget -r ftp://ftp2.census.gov/geo/tiger/TIGER2017/EDGES/
|
||||||
|
|
||||||
|
2. Convert the data into SQL statements:
|
||||||
|
|
||||||
|
./utils/imports.php --parse-tiger <tiger edge data directory>
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
## Updates
|
## Updates
|
||||||
|
|
||||||
There are many different ways 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. Make sure to use python3.
|
It is recommended to install Pyosmium via pip. Run (as the same user who
|
||||||
Run (as the same user who will later run the updates):
|
will later run the updates):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip3 install --user osmium
|
pip install --user osmium
|
||||||
```
|
```
|
||||||
|
|
||||||
Nominatim needs a tool called `pyosmium-get-updates` which 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`:
|
||||||
|
|
||||||
@@ -243,7 +179,7 @@ to update using the global minutely diffs.
|
|||||||
|
|
||||||
If you want a different update source you will need to add some settings
|
If you want a different update source you will need to add some settings
|
||||||
to `settings/local.php`. For example, to use the daily country extracts
|
to `settings/local.php`. For example, to use the daily country extracts
|
||||||
diffs for Ireland from Geofabrik add the following:
|
diffs for Ireland from geofabrik add the following:
|
||||||
|
|
||||||
// base URL of the replication service
|
// base URL of the replication service
|
||||||
@define('CONST_Replication_Url', 'https://download.geofabrik.de/europe/ireland-and-northern-ireland-updates');
|
@define('CONST_Replication_Url', 'https://download.geofabrik.de/europe/ireland-and-northern-ireland-updates');
|
||||||
@@ -259,7 +195,7 @@ To set up the update process now run the following command:
|
|||||||
It outputs the date where updates will start. Recheck that this date is
|
It outputs the date where updates will start. Recheck that this date is
|
||||||
what you expect.
|
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
|
||||||
|
|||||||
@@ -34,28 +34,28 @@ osm2pgsql README for additional dependencies required for compiling osm2pgsql.
|
|||||||
For running tests:
|
For running tests:
|
||||||
|
|
||||||
* [behave](http://pythonhosted.org/behave/)
|
* [behave](http://pythonhosted.org/behave/)
|
||||||
* [Psycopg2](https://initd.org/psycopg)
|
* [Psycopg2](http://initd.org/psycopg)
|
||||||
* [nose](https://nose.readthedocs.io)
|
* [nose](https://nose.readthedocs.io)
|
||||||
* [phpunit](https://phpunit.de)
|
* [phpunit](https://phpunit.de)
|
||||||
|
|
||||||
For running Nominatim:
|
For running Nominatim:
|
||||||
|
|
||||||
* [PostgreSQL](https://www.postgresql.org) (9.3 or later)
|
* [PostgreSQL](http://www.postgresql.org) (9.1 or later)
|
||||||
* [PostGIS](https://postgis.org) (2.2 or later)
|
* [PostGIS](http://postgis.refractions.net) (2.0 or later)
|
||||||
* [PHP](https://php.net) (7.0 or later)
|
* [PHP](http://php.net) (5.4 or later)
|
||||||
* PHP-pgsql
|
* PHP-pgsql
|
||||||
* PHP-intl (bundled with PHP)
|
* PHP-intl (bundled with PHP)
|
||||||
* [PEAR::DB](https://pear.php.net/package/DB)
|
* [PEAR::DB](http://pear.php.net/package/DB)
|
||||||
* a webserver (apache or nginx are recommended)
|
* a webserver (apache or nginx are recommended)
|
||||||
|
|
||||||
For running continuous updates:
|
For running continuous updates:
|
||||||
|
|
||||||
* [pyosmium](https://osmcode.org/pyosmium/) (with Python 3)
|
* [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 are strongly recommended.
|
planet import 32GB of RAM or more strongly are recommended.
|
||||||
|
|
||||||
For a full planet install you will need at least 700GB of hard disk space
|
For a full planet install you will need at least 700GB of hard disk space
|
||||||
(take into account that the OSM database is growing fast). SSD disks
|
(take into account that the OSM database is growing fast). SSD disks
|
||||||
|
|||||||
@@ -3,60 +3,8 @@
|
|||||||
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.
|
||||||
|
|
||||||
SQL statements should be executed from the PostgreSQL commandline. Execute
|
SQL statements should be executed from the postgres commandline. Execute
|
||||||
`psql nominatim` to enter command line mode.
|
`psql nominiatim` to enter command line mode.
|
||||||
|
|
||||||
## 3.3.0 -> 3.4.0
|
|
||||||
|
|
||||||
### Reorganisation of location_area_country table
|
|
||||||
|
|
||||||
The table `location_area_country` has been optimized. You need to switch to the
|
|
||||||
new format when you run updates. While updates are disabled, run the following
|
|
||||||
SQL commands:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE location_area_country_new AS
|
|
||||||
SELECT place_id, country_code, geometry FROM location_area_country;
|
|
||||||
DROP TABLE location_area_country;
|
|
||||||
ALTER TABLE location_area_country_new RENAME TO location_area_country;
|
|
||||||
CREATE INDEX idx_location_area_country_geometry ON location_area_country USING GIST (geometry);
|
|
||||||
CREATE INDEX idx_location_area_country_place_id ON location_area_country USING BTREE (place_id);
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3.2.0 -> 3.3.0
|
|
||||||
|
|
||||||
### New database connection string (DSN) format
|
|
||||||
|
|
||||||
Previously database connection setting (`CONST_Database_DSN` in `settings/*.php`) had the format
|
|
||||||
|
|
||||||
* (simple) `pgsql://@/nominatim`
|
|
||||||
* (complex) `pgsql://johndoe:secret@machine1.domain.com:1234/db1`
|
|
||||||
|
|
||||||
The new format is
|
|
||||||
|
|
||||||
* (simple) `pgsql:dbname=nominatim`
|
|
||||||
* (complex) `pgsql:dbname=db1;host=machine1.domain.com;port=1234;user=johndoe;password=secret`
|
|
||||||
|
|
||||||
### Natural Earth country boundaries no longer needed as fallback
|
|
||||||
|
|
||||||
```
|
|
||||||
DROP TABLE country_naturalearthdata;
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, update all SQL functions:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configurable Address Levels
|
|
||||||
|
|
||||||
The new configurable address levels require a new table. Create it with the
|
|
||||||
following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./utils/update.php --update-address-levels
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3.1.0 -> 3.2.0
|
## 3.1.0 -> 3.2.0
|
||||||
|
|
||||||
@@ -67,17 +15,17 @@ SQL statements to create the indexes:
|
|||||||
|
|
||||||
```
|
```
|
||||||
CREATE INDEX idx_placex_geometry_reverse_lookupPoint
|
CREATE INDEX idx_placex_geometry_reverse_lookupPoint
|
||||||
ON placex USING gist (geometry)
|
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)
|
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 class not in ('railway','tunnel','bridge','man_made')
|
||||||
AND rank_address >= 26 AND indexed_status = 0 AND linked_place_id is null;
|
AND rank_address >= 26 AND indexed_status = 0 AND linked_place_id is null;
|
||||||
CREATE INDEX idx_placex_geometry_reverse_lookupPolygon
|
CREATE INDEX idx_placex_geometry_reverse_lookupPolygon
|
||||||
ON placex USING gist (geometry)
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
|
WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
|
||||||
AND rank_address between 4 and 25 AND type != 'postcode'
|
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;
|
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
||||||
CREATE INDEX idx_placex_geometry_reverse_placeNode
|
CREATE INDEX idx_placex_geometry_reverse_placeNode
|
||||||
ON placex USING gist (geometry)
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
WHERE osm_type = 'N' AND rank_search between 5 and 25
|
WHERE osm_type = 'N' AND rank_search between 5 and 25
|
||||||
AND class = 'place' AND type != 'postcode'
|
AND class = 'place' AND type != 'postcode'
|
||||||
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
||||||
@@ -91,18 +39,12 @@ GRANT SELECT ON table country_osm_grid to "www-user";
|
|||||||
|
|
||||||
Replace the `www-user` with the user name of your website server if necessary.
|
Replace the `www-user` with the user name of your website server if necessary.
|
||||||
|
|
||||||
You can now drop the unused indexes:
|
Finally, you can drop the now unused indexes:
|
||||||
|
|
||||||
```
|
```
|
||||||
DROP INDEX idx_placex_reverse_geometry;
|
DROP INDEX idx_placex_reverse_geometry;
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, update all SQL functions:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3.0.0 -> 3.1.0
|
## 3.0.0 -> 3.1.0
|
||||||
|
|
||||||
### Postcode Table
|
### Postcode Table
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ but the `class` parameter is left out, then one of the places will be chosen
|
|||||||
at random and displayed.
|
at random and displayed.
|
||||||
|
|
||||||
```
|
```
|
||||||
https://nominatim.openstreetmap.org/details?place_id=<value>
|
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.
|
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.
|
||||||
@@ -40,7 +40,7 @@ See [Place Output Formats](Output.md) for details on each format. (Default: html
|
|||||||
|
|
||||||
* `json_callback=<string>`
|
* `json_callback=<string>`
|
||||||
|
|
||||||
Wrap JSON output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
Wrap json output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
||||||
Only has an effect for JSON output formats.
|
Only has an effect for JSON output formats.
|
||||||
|
|
||||||
* `pretty=[0|1]`
|
* `pretty=[0|1]`
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
Nominatim computes the address from two sources in the OpenStreetMap data:
|
Nominatim computes the address from two sources in the OpenStreetMap data:
|
||||||
from administrative boundaries and from place nodes. Boundaries are the more
|
from administrative boundaries and from place nodes. Boundaries are the more
|
||||||
useful source. They precisely describe an area. So it is very clear for
|
useful source. They precisely describe an area. So it is very clear for
|
||||||
Nominatim if a point belongs to an area or not. Place nodes are more complicated.
|
Nominatim if a point belongs to an area of not. Place nodes are more complicated.
|
||||||
These are only points without any precise extent. So Nominatim has to take a
|
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 node it can find.
|
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
|
In an ideal world, Nominatim would not need the place nodes but there are
|
||||||
many places on earth where there are no precise boundaries available for
|
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
|
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
|
local address parts, like villages and suburbs. Therefore it is not possible
|
||||||
to completely dismiss place nodes. And sometimes they sneak in where they
|
to completely dismiss place nodes. And sometimes they sneak in where they
|
||||||
@@ -21,7 +21,7 @@ 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
|
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
|
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
|
relation. If there is no administrative area, you can add the approximate
|
||||||
extent of the place and tag it place=<something> as well.
|
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.
|
#### 2. When doing reverse search, the address details have parts that don't contain the point I was looking up.
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ 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
|
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
|
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,
|
object may be across that border. When Nominatim then returns the address,
|
||||||
it contains the county/state/country across the border.
|
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?
|
#### 3. I get different counties/states/countries when I change the zoom parameter in the reverse query. How is that possible?
|
||||||
|
|
||||||
@@ -41,21 +41,3 @@ 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,
|
the address of the closest object found, you might sometimes get one result,
|
||||||
sometimes the other for the closest point.
|
sometimes the other for the closest point.
|
||||||
|
|
||||||
#### 4. Can you return the continent?
|
|
||||||
|
|
||||||
Nominatim assigns each map feature one country. Those outside any administrative
|
|
||||||
boundaries are assigned a special no-country. Continents or other super-national
|
|
||||||
administrations (e.g. European Union, NATO, Custom unions) are not supported,
|
|
||||||
see also [Administrative Boundary](https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative#Super-national_administrations).
|
|
||||||
|
|
||||||
#### 5. Can you return the timezone?
|
|
||||||
|
|
||||||
See this separate OpenStreetMap-based project [Timezone Boundary Builder](https://github.com/evansiroky/timezone-boundary-builder).
|
|
||||||
|
|
||||||
#### 6. I want to download a list of streets/restaurants of a city/region
|
|
||||||
|
|
||||||
The [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API) is more
|
|
||||||
suited for these kinds of queries.
|
|
||||||
|
|
||||||
That said if you installed your own Nominatim instance you can use the
|
|
||||||
`/utils/export.php` PHP script as basis to return such lists.
|
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ Additional optional parameters are explained below.
|
|||||||
|
|
||||||
### Output format
|
### Output format
|
||||||
|
|
||||||
* `format=[xml|json|jsonv2|geojson|geocodejson]`
|
* `format=[html|xml|json|jsonv2|geojson|geocodejson]`
|
||||||
|
|
||||||
See [Place Output Formats](Output.md) for details on each format. (Default: xml)
|
See [Place Output Formats](Output.md) for details on each format. (Default: xml)
|
||||||
|
|
||||||
* `json_callback=<string>`
|
* `json_callback=<string>`
|
||||||
|
|
||||||
Wrap JSON output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
Wrap json output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
||||||
Only has an effect for JSON output formats.
|
Only has an effect for JSON output formats.
|
||||||
|
|
||||||
### Output details
|
### Output details
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Place Output
|
# Place Output
|
||||||
|
|
||||||
The [/reverse](Reverse.md), [/search](Search.md) and [/lookup](Lookup.md)
|
The [\reverse](Reverse.md), [\search](Search.md) and [\lookup](Lookup.md)
|
||||||
API calls produce very similar output which is explained in this section.
|
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`
|
There is one section for each format which is selectable via the `format`
|
||||||
parameter.
|
parameter.
|
||||||
@@ -46,7 +46,7 @@ a single place (for reverse) of the following format:
|
|||||||
|
|
||||||
The possible fields are:
|
The possible fields are:
|
||||||
|
|
||||||
* `place_id` - reference to the Nominatim internal database ID (see notes below)
|
* `place_id` - reference to the Nominatim internal database ID
|
||||||
* `osm_type`, `osm_id` - reference to the OSM object
|
* `osm_type`, `osm_id` - reference to the OSM object
|
||||||
* `boundingbox` - area of corner coordinates
|
* `boundingbox` - area of corner coordinates
|
||||||
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
||||||
@@ -70,12 +70,12 @@ This is the same as the JSON format with two changes:
|
|||||||
|
|
||||||
### GeoJSON
|
### GeoJSON
|
||||||
|
|
||||||
This format follows the [RFC7946](https://geojson.org). Every feature includes
|
This format follows the [RFC7946](http://geojson.org). Every feature includes
|
||||||
a bounding box (`bbox`).
|
a bounding box (`bbox`).
|
||||||
|
|
||||||
The feature list has the following fields:
|
The feature list has the following fields:
|
||||||
|
|
||||||
* `place_id` - reference to the Nominatim internal database ID (see notes below)
|
* `place_id` - reference to the Nominatim internal database ID
|
||||||
* `osm_type`, `osm_id` - reference to the OSM object
|
* `osm_type`, `osm_id` - reference to the OSM object
|
||||||
* `category`, `type` - key and value of the main OSM tag
|
* `category`, `type` - key and value of the main OSM tag
|
||||||
* `display_name` - full comma-separated address
|
* `display_name` - full comma-separated address
|
||||||
@@ -83,7 +83,7 @@ The feature list has the following fields:
|
|||||||
* `importance` - computed importance rank
|
* `importance` - computed importance rank
|
||||||
* `icon` - link to class icon (if available)
|
* `icon` - link to class icon (if available)
|
||||||
* `address` - dictionary of address details (only with `addressdetails=1`)
|
* `address` - dictionary of address details (only with `addressdetails=1`)
|
||||||
* `extratags` - dictionary with additional useful tags like `website` or `maxspeed`
|
* `extratags` - dictionary with additional useful tags like website or maxspeed
|
||||||
(only with `extratags=1`)
|
(only with `extratags=1`)
|
||||||
* `namedetails` - dictionary with full list of available names including ref etc.
|
* `namedetails` - dictionary with full list of available names including ref etc.
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ formats depending on the API call.
|
|||||||
|
|
||||||
```
|
```
|
||||||
<reversegeocode timestamp="Sat, 11 Aug 18 11:53:21 +0000"
|
<reversegeocode timestamp="Sat, 11 Aug 18 11:53:21 +0000"
|
||||||
attribution="Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright"
|
attribution="Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright"
|
||||||
querystring="lat=48.400381&lon=11.745876&zoom=5&format=xml">
|
querystring="lat=48.400381&lon=11.745876&zoom=5&format=xml">
|
||||||
<result place_id="179509537" osm_type="relation" osm_id="2145268" ref="BY"
|
<result place_id="179509537" osm_type="relation" osm_id="2145268" ref="BY"
|
||||||
lat="48.9467562" lon="11.4038717"
|
lat="48.9467562" lon="11.4038717"
|
||||||
@@ -148,13 +148,13 @@ attribution to OSM and the original querystring.
|
|||||||
|
|
||||||
The place information can be found in the `result` element. The attributes of that element contain:
|
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 (see notes below)
|
* `place_id` - reference to the Nominatim internal database ID
|
||||||
* `osm_type`, `osm_id` - reference to the OSM object
|
* `osm_type`, `osm_id` - reference to the OSM object
|
||||||
* `ref` - content of `ref` tag if it exists
|
* `ref` - content of `ref` tag if it exists
|
||||||
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
||||||
* `boundingbox` - comma-separated list of corner coordinates
|
* `boundingbox` - comma-separated list of corner coordinates
|
||||||
|
|
||||||
The full address of the result can be found in the content of the
|
The full address address of the result can be found in the content of the
|
||||||
`result` element as a comma-separated list.
|
`result` element as a comma-separated list.
|
||||||
|
|
||||||
Additional information requested with `addressdetails=1`, `extratags=1` and
|
Additional information requested with `addressdetails=1`, `extratags=1` and
|
||||||
@@ -164,7 +164,7 @@ Additional information requested with `addressdetails=1`, `extratags=1` and
|
|||||||
|
|
||||||
```
|
```
|
||||||
<searchresults timestamp="Sat, 11 Aug 18 11:55:35 +0000"
|
<searchresults timestamp="Sat, 11 Aug 18 11:55:35 +0000"
|
||||||
attribution="Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright"
|
attribution="Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright"
|
||||||
querystring="london" polygon="false" exclude_place_ids="100149"
|
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">
|
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"
|
<place place_id="100149" osm_type="node" osm_id="107775" place_rank="15"
|
||||||
@@ -203,7 +203,7 @@ generic information about the query:
|
|||||||
The place information can be found in the `place` elements, of which there may
|
The place information can be found in the `place` elements, of which there may
|
||||||
be more than one. The attributes of that element contain:
|
be more than one. The attributes of that element contain:
|
||||||
|
|
||||||
* `place_id` - reference to the Nominatim internal database ID (see notes below)
|
* `place_id` - reference to the Nominatim internal database ID
|
||||||
* `osm_type`, `osm_id` - reference to the OSM object
|
* `osm_type`, `osm_id` - reference to the OSM object
|
||||||
* `ref` - content of `ref` tag if it exists
|
* `ref` - content of `ref` tag if it exists
|
||||||
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
||||||
@@ -220,27 +220,3 @@ as subelements with the type of the address part.
|
|||||||
Additional information requested with `extratags=1` and `namedetails=1` can
|
Additional information requested with `extratags=1` and `namedetails=1` can
|
||||||
be found in extra elements as sub-element of each place.
|
be found in extra elements as sub-element of each place.
|
||||||
|
|
||||||
|
|
||||||
## Notes on field values
|
|
||||||
|
|
||||||
### place_id is not a persistent id
|
|
||||||
|
|
||||||
The `place_id` is created when a Nominatim database gets installed. A
|
|
||||||
single place will have a different value on another server or even when
|
|
||||||
the same data gets re-imported. It's thus not useful to treat it as
|
|
||||||
permanent for later use.
|
|
||||||
|
|
||||||
The combination `osm_type`+`osm_id` is slighly better but remember in
|
|
||||||
OpenStreetMap mappers can delete, split, recreate places (and those
|
|
||||||
get a new `osm_id`), there is no link between those old and new ids.
|
|
||||||
Places can also change their meaning without changing their `osm_id`,
|
|
||||||
e.g. when a restaurant is retagged as supermarket. For a more in-depth
|
|
||||||
discussion see [Permanent ID](https://wiki.openstreetmap.org/wiki/Permanent_ID).
|
|
||||||
|
|
||||||
Nominatim merges some places (e.g. center node of a city with the boundary
|
|
||||||
relation) so `osm_type`+`osm_id`+`class_name` would be more unique.
|
|
||||||
|
|
||||||
### boundingbox
|
|
||||||
|
|
||||||
Comma separated list of min latitude, max latitude, min longitude, max longitude.
|
|
||||||
The whole planet would be `-90,90,-180,180`.
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ There are two ways how the requested location can be specified:
|
|||||||
|
|
||||||
A specific OSM node(N), way(W) or relation(R) to return an address for.
|
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 parameters cannot
|
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
|
be used at the same time. Both accept the additional optional parameters listed
|
||||||
below.
|
below.
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ See [Place Output Formats](Output.md) for details on each format. (Default: html
|
|||||||
|
|
||||||
* `json_callback=<string>`
|
* `json_callback=<string>`
|
||||||
|
|
||||||
Wrap JSON output in a callback function ([JSONP](https://en.wikipedia.org/wiki/JSONP)) i.e. `<string>(<json>)`.
|
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.
|
Only has an effect for JSON output formats.
|
||||||
|
|
||||||
### Output details
|
### Output details
|
||||||
@@ -80,8 +80,7 @@ In terms of address details the zoom levels are as follows:
|
|||||||
8 | county
|
8 | county
|
||||||
10 | city
|
10 | city
|
||||||
14 | suburb
|
14 | suburb
|
||||||
16 | major streets
|
16 | street
|
||||||
17 | major and minor streets
|
|
||||||
18 | building
|
18 | building
|
||||||
|
|
||||||
|
|
||||||
@@ -146,7 +145,7 @@ This overrides the specified machine readable format. (Default: 0)
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"place_id":"134140761",
|
"place_id":"134140761",
|
||||||
"licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/www.openstreetmap.org\/copyright",
|
"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http:\/\/www.openstreetmap.org\/copyright",
|
||||||
"osm_type":"way",
|
"osm_type":"way",
|
||||||
"osm_id":"280940520",
|
"osm_id":"280940520",
|
||||||
"lat":"-34.4391708",
|
"lat":"-34.4391708",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Search queries
|
# Search queries
|
||||||
|
|
||||||
The search API allows you to look up a location from a textual description.
|
The search API allows to look up a location from a textual description.
|
||||||
Nominatim supports structured as well as free-form search queries.
|
Nominatim supports structured as well as free-form search queries.
|
||||||
|
|
||||||
The search query may also contain
|
The search query may also contain
|
||||||
@@ -46,7 +46,7 @@ In this form, the query may be given through two different sets of parameters:
|
|||||||
Structured requests are faster but are less robust against alternative
|
Structured requests are faster but are less robust against alternative
|
||||||
OSM tagging schemas. **Do not combine with** `q=<query>` **parameter**.
|
OSM tagging schemas. **Do not combine with** `q=<query>` **parameter**.
|
||||||
|
|
||||||
All three query forms accept the additional parameters listed below.
|
All three query forms accept the additional paramters listed below.
|
||||||
|
|
||||||
### Output format
|
### Output format
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ See [Place Output Formats](Output.md) for details on each format. (Default: html
|
|||||||
|
|
||||||
* `json_callback=<string>`
|
* `json_callback=<string>`
|
||||||
|
|
||||||
Wrap JSON output in a callback function ([JSONP](https://en.wikipedia.org/wiki/JSONP)) i.e. `<string>(<json>)`.
|
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.
|
Only has an effect for JSON output formats.
|
||||||
|
|
||||||
### Output details
|
### Output details
|
||||||
@@ -112,8 +112,7 @@ Limit the number of returned results. (Default: 10, Maximum: 50)
|
|||||||
* `viewbox=<x1>,<y1>,<x2>,<y2>`
|
* `viewbox=<x1>,<y1>,<x2>,<y2>`
|
||||||
|
|
||||||
The preferred area to find search results. Any two corner points of the box
|
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. `x` is longitude,
|
are accepted in any order as long as they span a real box.
|
||||||
`y` is latitude.
|
|
||||||
|
|
||||||
|
|
||||||
* `bounded=[0|1]`
|
* `bounded=[0|1]`
|
||||||
@@ -238,7 +237,7 @@ This overrides the specified machine readable format. (Default: 0)
|
|||||||
|
|
||||||
##### JSON with address details
|
##### JSON with address details
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/?addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1](https://nominatim.openstreetmap.org/?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](https://nominatim.openstreetmap.org/?format=json&addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
# Additional Data Sources
|
|
||||||
|
|
||||||
This guide explains how data sources other than OpenStreetMap mentioned in
|
|
||||||
the install instructions got obtained and converted.
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# Documentation Pages
|
|
||||||
|
|
||||||
The [Nominatim documentation](https://nominatim.org/release-docs/develop/) is built using the [MkDocs](https://www.mkdocs.org/) static site generation framework. The master branch is automatically deployed every night on under [https://nominatim.org/release-docs/develop/]()
|
|
||||||
|
|
||||||
To preview local changes:
|
|
||||||
|
|
||||||
1. Install MkDocs
|
|
||||||
|
|
||||||
```
|
|
||||||
pip3 install --user mkdocs
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
2. In build directory run
|
|
||||||
|
|
||||||
```
|
|
||||||
make doc
|
|
||||||
INFO - Cleaning site directory
|
|
||||||
INFO - Building documentation to directory: /home/vagrant/build/site-html
|
|
||||||
```
|
|
||||||
|
|
||||||
This runs `mkdocs build` plus extra transformion of some files and adds symlinks (see `CMakeLists.txt` for the exact steps).
|
|
||||||
|
|
||||||
|
|
||||||
3. Start webserver for local testing
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdocs serve
|
|
||||||
[server:296] Serving on http://127.0.0.1:8000
|
|
||||||
[handlers:62] Start watching changes
|
|
||||||
```
|
|
||||||
|
|
||||||
If you develop inside a Vagrant virtual machine:
|
|
||||||
* add port forwarding to your Vagrantfile, e.g. `config.vm.network "forwarded_port", guest: 8000, host: 8000`
|
|
||||||
* use `mkdocs serve --dev-addr 0.0.0.0:8000` because the default localhost
|
|
||||||
IP does not get forwarded.
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
# OSM Data Import
|
|
||||||
|
|
||||||
OSM data is initially imported using osm2pgsql. Nominatim uses its own data
|
|
||||||
output style 'gazetteer', which differs from the output style created for
|
|
||||||
map rendering.
|
|
||||||
|
|
||||||
## Database Layout
|
|
||||||
|
|
||||||
The gazetteer style produces a single table `place` with the following rows:
|
|
||||||
|
|
||||||
* `osm_type` - kind of OSM object (**N** - node, **W** - way, **R** - relation)
|
|
||||||
* `osm_id` - original OSM ID
|
|
||||||
* `class` - key of principal tag defining the object type
|
|
||||||
* `type` - value of principal tag defining the object type
|
|
||||||
* `name` - collection of tags that contain a name or reference
|
|
||||||
* `admin_level` - numerical value of the tagged administrative level
|
|
||||||
* `address` - collection of tags defining the address of an object
|
|
||||||
* `extratags` - collection of additional interesting tags that are not
|
|
||||||
directly relevant for searching
|
|
||||||
* `geometry` - geometry of the object (in WGS84)
|
|
||||||
|
|
||||||
A single OSM object may appear multiple times in this table when it is tagged
|
|
||||||
with multiple tags that may constitute a principal tag. Take for example a
|
|
||||||
motorway bridge. In OSM, this would be a way which is tagged with
|
|
||||||
`highway=motorway` and `bridge=yes`. This way would appear in the `place` table
|
|
||||||
once with `class` of `highway` and once with a `class` of `bridge`. Thus the
|
|
||||||
*unique key* for `place` is (`osm_type`, `osm_id`, `class`).
|
|
||||||
|
|
||||||
## Configuring the Import
|
|
||||||
|
|
||||||
How tags are interpreted and assigned to the different `place` columns can be
|
|
||||||
configured via the import style configuration file (`CONST_Import_style`). This
|
|
||||||
is a JSON file which contains a list of rules which are matched against every
|
|
||||||
tag of every object and then assign the tag its specific role.
|
|
||||||
|
|
||||||
### Configuration Rules
|
|
||||||
|
|
||||||
A single rule looks like this:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"keys" : ["key1", "key2", ...],
|
|
||||||
"values" : {
|
|
||||||
"value1" : "prop",
|
|
||||||
"value2" : "prop1,prop2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
A rule first defines a list of keys to apply the rule to. This is always a list
|
|
||||||
of strings. The string may have four forms. An empty string matches against
|
|
||||||
any key. A string that ends in an asterisk `*` is a prefix match and accordingly
|
|
||||||
matches against any key that starts with the given string (minus the `*`). A
|
|
||||||
suffix match can be defined similarly with a string that starts with a `*`. Any
|
|
||||||
other string constitutes an exact match.
|
|
||||||
|
|
||||||
The second part of the rules defines a list of values and the properties that
|
|
||||||
apply to a successful match. Value strings may be either empty, which
|
|
||||||
means that they match any value, or describe an exact match. Prefix
|
|
||||||
or suffix matching of values is not possible.
|
|
||||||
|
|
||||||
For a rule to match, it has to find a valid combination of keys and values. The
|
|
||||||
resulting property is that of the matched values.
|
|
||||||
|
|
||||||
The rules in a configuration file are processed sequentially and the first
|
|
||||||
match for each tag wins.
|
|
||||||
|
|
||||||
A rule where key and value are the empty string is special. This defines the
|
|
||||||
fallback when none of the rules match. The fallback is always used as a last
|
|
||||||
resort when nothing else matches, no matter where the rule appears in the file.
|
|
||||||
Defining multiple fallback rules is not allowed. What happens in this case,
|
|
||||||
is undefined.
|
|
||||||
|
|
||||||
### Tag Properties
|
|
||||||
|
|
||||||
One or more of the following properties may be given for each tag:
|
|
||||||
|
|
||||||
* `main`
|
|
||||||
|
|
||||||
A principal tag. A new row will be added for the object with key and value
|
|
||||||
as `class` and `type`.
|
|
||||||
|
|
||||||
* `with_name`
|
|
||||||
|
|
||||||
When the tag is a principal tag (`main` property set): only really add a new
|
|
||||||
row, if there is any name tag found (a reference tag is not sufficient, see
|
|
||||||
below).
|
|
||||||
|
|
||||||
* `with_name_key`
|
|
||||||
|
|
||||||
When the tag is a principal tag (`main` property set): only really add a new
|
|
||||||
row, if there is also a name tag that matches the key of the principal tag.
|
|
||||||
For example, if the main tag is `bridge=yes`, then it will only be added as
|
|
||||||
an extra row, if there is a tag `bridge:name[:XXX]` for the same object.
|
|
||||||
If this property is set, all other names that are not domain-specific are
|
|
||||||
ignored.
|
|
||||||
|
|
||||||
* `fallback`
|
|
||||||
|
|
||||||
When the tag is a principal tag (`main` property set): only really add a new
|
|
||||||
row, when no other principal tags for this object have been found. Only one
|
|
||||||
fallback tag can win for an object.
|
|
||||||
|
|
||||||
* `operator`
|
|
||||||
|
|
||||||
When the tag is a principal tag (`main` property set): also include the
|
|
||||||
`operator` tag in the list of names. This is a special construct for an
|
|
||||||
out-dated tagging practise in OSM. Fuel stations and chain restaurants
|
|
||||||
in particular used to have the name of the chain tagged as `operator`.
|
|
||||||
These days the chain can be more commonly found in the `brand` tag but
|
|
||||||
there is still enough old data around to warrant this special case.
|
|
||||||
|
|
||||||
* `name`
|
|
||||||
|
|
||||||
Add tag to the list of names.
|
|
||||||
|
|
||||||
* `ref`
|
|
||||||
|
|
||||||
Add tag to the list of names as a reference. At the moment this only means
|
|
||||||
that the object is not considered to be named for `with_name`.
|
|
||||||
|
|
||||||
* `address`
|
|
||||||
|
|
||||||
Add tag to the list of address tags. If the tag starts with `addr:` or
|
|
||||||
`is_in:`, then this prefix is cut off before adding it to the list.
|
|
||||||
|
|
||||||
* `postcode`
|
|
||||||
|
|
||||||
Add the value as a postcode to the address tags. If multiple tags are
|
|
||||||
candidate for postcodes, one wins out and the others are dropped.
|
|
||||||
|
|
||||||
* `country`
|
|
||||||
|
|
||||||
Add the value as a country code to the address tags. The value must be a
|
|
||||||
two letter country code, otherwise it is ignored. If there are multiple
|
|
||||||
tags that match, then one wins out and the others are dropped.
|
|
||||||
|
|
||||||
* `house`
|
|
||||||
|
|
||||||
If no principle tags can be found for the object, still add the object with
|
|
||||||
`class`=`place` and `type`=`house`. Use this for address nodes that have no
|
|
||||||
other function.
|
|
||||||
|
|
||||||
* `interpolation`
|
|
||||||
|
|
||||||
Add this object as an address interpolation (appears as `class`=`place` and
|
|
||||||
`type`=`houses` in the database).
|
|
||||||
|
|
||||||
* `extra`
|
|
||||||
|
|
||||||
Add tag to the list of extra tags.
|
|
||||||
|
|
||||||
* `skip`
|
|
||||||
|
|
||||||
Skip the tag completely. Useful when a custom default fallback is defined
|
|
||||||
or to define exceptions to rules.
|
|
||||||
|
|
||||||
A rule can define as many of these properties for one match as it likes. For
|
|
||||||
example, if the property is `"main,extra"` then the tag will open a new row
|
|
||||||
but also have the tag appear in the list of extra tags.
|
|
||||||
|
|
||||||
There are a number of pre-defined styles in the `settings/` directory. It is
|
|
||||||
advisable to start from one of these styles when defining your own.
|
|
||||||
|
|
||||||
### Changing the Style of Existing Databases
|
|
||||||
|
|
||||||
There is normally no issue changing the style of a database that is already
|
|
||||||
imported and now kept up-to-date with change files. Just be aware that any
|
|
||||||
change in the style applies to updates only. If you want to change the data
|
|
||||||
that is already in the database, then a reimport is necessary.
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
# Place Ranking in Nominatim
|
|
||||||
|
|
||||||
Nominatim uses two metrics to rank a place: search rank and address rank.
|
|
||||||
Both can be assigned a value between 0 and 30. They serve slightly
|
|
||||||
different purposes, which are explained in this chapter.
|
|
||||||
|
|
||||||
## Search rank
|
|
||||||
|
|
||||||
The search rank describes the extent and importance of a place. It is used
|
|
||||||
when ranking search result. Simply put, if there are two results for a
|
|
||||||
search query which are otherwise equal, then the result with the _lower_
|
|
||||||
search rank will be appear higher in the result list.
|
|
||||||
|
|
||||||
Search ranks are not so important these days because many well-known
|
|
||||||
places use the Wikipedia importance ranking instead.
|
|
||||||
|
|
||||||
## Address rank
|
|
||||||
|
|
||||||
The address rank describes where a place shows up in an address hierarchy.
|
|
||||||
Usually only administrative boundaries and place nodes and areas are
|
|
||||||
eligible to be part of an address. All other objects have an address rank
|
|
||||||
of 0.
|
|
||||||
|
|
||||||
Note that the search rank of a place plays a role in the address computation
|
|
||||||
as well. When collecting the places that should make up the address parts
|
|
||||||
then only places are taken into account that have a lower address rank than
|
|
||||||
the search rank of the base object.
|
|
||||||
|
|
||||||
## Rank configuration
|
|
||||||
|
|
||||||
Search and address ranks are assigned to a place when it is first imported
|
|
||||||
into the database. There are a few hard-coded rules for the assignment:
|
|
||||||
|
|
||||||
* postcodes follow special rules according to their length
|
|
||||||
* boundaries that are not areas and railway=rail are dropped completely
|
|
||||||
* the following are always search rank 30 and address rank 0:
|
|
||||||
* highway nodes
|
|
||||||
* landuse that is not an area
|
|
||||||
|
|
||||||
Other than that, the ranks can be freely assigned via the JSON file
|
|
||||||
defined with `CONST_Address_Level_Config` according to their type and
|
|
||||||
the country they are in.
|
|
||||||
|
|
||||||
The address level configuration must consist of an array of configuration
|
|
||||||
entries, each containing a tag definition and an optional country array:
|
|
||||||
|
|
||||||
```
|
|
||||||
[ {
|
|
||||||
"tags" : {
|
|
||||||
"place" : {
|
|
||||||
"county" : 12,
|
|
||||||
"city" : 16,
|
|
||||||
},
|
|
||||||
"landuse" : {
|
|
||||||
"residential" : 22,
|
|
||||||
"" : 30
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"countries" : [ "ca", "us" ],
|
|
||||||
"tags" : {
|
|
||||||
"boundary" : {
|
|
||||||
"administrative8" : 18,
|
|
||||||
"administrative9" : 20
|
|
||||||
},
|
|
||||||
"landuse" : {
|
|
||||||
"residential" : [22, 0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
The `countries` field contains a list of countries (as ISO 3166-1 alpha 2 code)
|
|
||||||
for which the definition applies. When the field is omitted, then the
|
|
||||||
definition is used as a fallback, when nothing more specific for a given
|
|
||||||
country exists.
|
|
||||||
|
|
||||||
`tags` contains the ranks for key/value pairs. The ranks can be either a
|
|
||||||
single number, in which case they are the search and address rank, or an array
|
|
||||||
of search and address rank (in that order). The value may be left empty.
|
|
||||||
Then the rank is used when no more specific value is found for the given
|
|
||||||
key.
|
|
||||||
|
|
||||||
Countries and key/value combination may appear in multiple definitions. Just
|
|
||||||
make sure that each combination of counrty/key/value appears only once per
|
|
||||||
file. Otherwise the import will fail with a UNIQUE INDEX constraint violation
|
|
||||||
on import.
|
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Basic Architecture
|
# Basic Architecture
|
||||||
|
|
||||||
Nominatim provides geocoding based on OpenStreetMap data. It uses a PostgreSQL
|
Nominatim provides geocoding based on OpenStreetMap data. It uses a Postgresql
|
||||||
database as a backend for storing the data.
|
database as a backend for storing the data.
|
||||||
|
|
||||||
There are three basic parts to Nominatim's architecture: the data import,
|
There are three basic parts to Nominatim's architecture: the data import,
|
||||||
@@ -15,10 +15,10 @@ the import can be found in the database table `place`.
|
|||||||
The __address computation__ or __indexing__ stage takes the data from `place`
|
The __address computation__ or __indexing__ stage takes the data from `place`
|
||||||
and adds additional information needed for geocoding. It ranks the places by
|
and adds additional information needed for geocoding. It ranks the places by
|
||||||
importance, links objects that belong together and computes addresses and
|
importance, links objects that belong together and computes addresses and
|
||||||
the search index. Most of this work is done in PL/pgSQL via database triggers
|
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`.
|
and can be found in the file `sql/functions.sql`.
|
||||||
|
|
||||||
The __search frontend__ implements the actual API. It takes search
|
The __search frontend__ implements the actual API. It takes queries for
|
||||||
and reverse geocoding queries from the user, looks up the data and
|
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
|
returns the results in the requested format. This part is written in PHP
|
||||||
and can be found in the `lib/` and `website/` directories.
|
and can be found in the `lib/` and `website/` directories.
|
||||||
|
|||||||
@@ -20,15 +20,6 @@ pages:
|
|||||||
- 'Troubleshooting' : 'admin/Faq.md'
|
- 'Troubleshooting' : 'admin/Faq.md'
|
||||||
- 'Developers Guide':
|
- 'Developers Guide':
|
||||||
- 'Overview' : 'develop/overview.md'
|
- 'Overview' : 'develop/overview.md'
|
||||||
- 'OSM Data Import' : 'develop/Import.md'
|
|
||||||
- 'Place Ranking' : 'develop/Ranking.md'
|
|
||||||
- 'Documentation' : 'develop/Documentation.md'
|
|
||||||
- 'External Data Sources':
|
|
||||||
- 'Overview' : 'data-sources/overview.md'
|
|
||||||
- 'US Census (Tiger)': 'data-sources/US-Tiger.md'
|
|
||||||
- 'GB Postcodes': 'data-sources/GB-Postcodes.md'
|
|
||||||
- 'Country Grid': 'data-sources/Country-Grid.md'
|
|
||||||
- 'Wikipedia & Wikidata': 'data-sources/Wikipedia-Wikidata.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'
|
||||||
|
|||||||
@@ -14,24 +14,24 @@ class AddressDetails
|
|||||||
public function __construct(&$oDB, $iPlaceID, $sHousenumber, $mLangPref)
|
public function __construct(&$oDB, $iPlaceID, $sHousenumber, $mLangPref)
|
||||||
{
|
{
|
||||||
if (is_array($mLangPref)) {
|
if (is_array($mLangPref)) {
|
||||||
$mLangPref = $oDB->getArraySQL($oDB->getDBQuotedList($mLangPref));
|
$mLangPref = 'ARRAY['.join(',', array_map('getDBQuoted', $mLangPref)).']';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($sHousenumber)) {
|
if (!$sHousenumber) {
|
||||||
$sHousenumber = -1;
|
$sHousenumber = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sSQL = 'SELECT *,';
|
$sSQL = 'SELECT *,';
|
||||||
$sSQL .= ' get_name_by_language(name,'.$mLangPref.') as localname';
|
$sSQL .= ' get_name_by_language(name,'.$mLangPref.') as localname';
|
||||||
$sSQL .= ' FROM get_addressdata('.$iPlaceID.','.$sHousenumber.')';
|
$sSQL .= ' FROM get_addressdata('.$iPlaceID.','.$sHousenumber.')';
|
||||||
$sSQL .= ' ORDER BY rank_address DESC, isaddress DESC';
|
$sSQL .= ' ORDER BY rank_address desc,isaddress DESC';
|
||||||
|
|
||||||
$this->aAddressLines = $oDB->getAll($sSQL);
|
$this->aAddressLines = chksql($oDB->getAll($sSQL));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function isAddress($aLine)
|
private static function isAddress($aLine)
|
||||||
{
|
{
|
||||||
return $aLine['isaddress'] || $aLine['type'] == 'country_code';
|
return $aLine['isaddress'] == 't' || $aLine['type'] == 'country_code';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAddressDetails($bAll = false)
|
public function getAddressDetails($bAll = false)
|
||||||
@@ -40,7 +40,7 @@ class AddressDetails
|
|||||||
return $this->aAddressLines;
|
return $this->aAddressLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_filter($this->aAddressLines, array(__CLASS__, 'isAddress'));
|
return array_filter($this->aAddressLines, 'AddressDetails::isAddress');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLocaleAddress()
|
public function getLocaleAddress()
|
||||||
@@ -49,7 +49,7 @@ class AddressDetails
|
|||||||
$sPrevResult = '';
|
$sPrevResult = '';
|
||||||
|
|
||||||
foreach ($this->aAddressLines as $aLine) {
|
foreach ($this->aAddressLines as $aLine) {
|
||||||
if ($aLine['isaddress'] && $sPrevResult != $aLine['localname']) {
|
if ($aLine['isaddress'] == 't' && $sPrevResult != $aLine['localname']) {
|
||||||
$sPrevResult = $aLine['localname'];
|
$sPrevResult = $aLine['localname'];
|
||||||
$aParts[] = $sPrevResult;
|
$aParts[] = $sPrevResult;
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ class AddressDetails
|
|||||||
public function getAdminLevels()
|
public function getAdminLevels()
|
||||||
{
|
{
|
||||||
$aAddress = array();
|
$aAddress = array();
|
||||||
foreach (array_reverse($this->aAddressLines) as $aLine) {
|
foreach ($this->aAddressLines as $aLine) {
|
||||||
if (self::isAddress($aLine)
|
if (self::isAddress($aLine)
|
||||||
&& isset($aLine['admin_level'])
|
&& isset($aLine['admin_level'])
|
||||||
&& $aLine['admin_level'] < 15
|
&& $aLine['admin_level'] < 15
|
||||||
|
|||||||
298
lib/DB.php
298
lib/DB.php
@@ -1,298 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/DatabaseError.php');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses PDO to access the database specified in the CONST_Database_DSN
|
|
||||||
* setting.
|
|
||||||
*/
|
|
||||||
class DB
|
|
||||||
{
|
|
||||||
protected $connection;
|
|
||||||
|
|
||||||
public function __construct($sDSN = CONST_Database_DSN)
|
|
||||||
{
|
|
||||||
$this->sDSN = $sDSN;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function connect($bNew = false, $bPersistent = true)
|
|
||||||
{
|
|
||||||
if (isset($this->connection) && !$bNew) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$aConnOptions = array(
|
|
||||||
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
|
|
||||||
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
|
|
||||||
\PDO::ATTR_PERSISTENT => $bPersistent
|
|
||||||
);
|
|
||||||
|
|
||||||
// https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php
|
|
||||||
try {
|
|
||||||
$conn = new \PDO($this->sDSN, null, null, $aConnOptions);
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
$sMsg = 'Failed to establish database connection:' . $e->getMessage();
|
|
||||||
throw new \Nominatim\DatabaseError($sMsg, 500, null, $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->exec("SET DateStyle TO 'sql,european'");
|
|
||||||
$conn->exec("SET client_encoding TO 'utf-8'");
|
|
||||||
$iMaxExecution = ini_get('max_execution_time');
|
|
||||||
if ($iMaxExecution > 0) $conn->setAttribute(\PDO::ATTR_TIMEOUT, $iMaxExecution); // seconds
|
|
||||||
|
|
||||||
$this->connection = $conn;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the number of rows that were modified or deleted by the SQL
|
|
||||||
// statement. If no rows were affected returns 0.
|
|
||||||
public function exec($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
|
||||||
{
|
|
||||||
$val = null;
|
|
||||||
try {
|
|
||||||
if (isset($aInputVars)) {
|
|
||||||
$stmt = $this->connection->prepare($sSQL);
|
|
||||||
$stmt->execute($aInputVars);
|
|
||||||
} else {
|
|
||||||
$val = $this->connection->exec($sSQL);
|
|
||||||
}
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
|
||||||
}
|
|
||||||
return $val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes query. Returns first row as array.
|
|
||||||
* Returns false if no result found.
|
|
||||||
*
|
|
||||||
* @param string $sSQL
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
*/
|
|
||||||
public function getRow($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
|
||||||
$row = $stmt->fetch();
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
|
||||||
}
|
|
||||||
return $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes query. Returns first value of first result.
|
|
||||||
* Returns false if no results found.
|
|
||||||
*
|
|
||||||
* @param string $sSQL
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
*/
|
|
||||||
public function getOne($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
|
||||||
$row = $stmt->fetch(\PDO::FETCH_NUM);
|
|
||||||
if ($row === false) return false;
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
|
||||||
}
|
|
||||||
return $row[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes query. Returns array of results (arrays).
|
|
||||||
* Returns empty array if no results found.
|
|
||||||
*
|
|
||||||
* @param string $sSQL
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
*/
|
|
||||||
public function getAll($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
|
||||||
$rows = $stmt->fetchAll();
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
|
||||||
}
|
|
||||||
return $rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes query. Returns array of the first value of each result.
|
|
||||||
* Returns empty array if no results found.
|
|
||||||
*
|
|
||||||
* @param string $sSQL
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
*/
|
|
||||||
public function getCol($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
|
||||||
{
|
|
||||||
$aVals = array();
|
|
||||||
try {
|
|
||||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
|
||||||
|
|
||||||
while ($val = $stmt->fetchColumn(0)) { // returns first column or false
|
|
||||||
$aVals[] = $val;
|
|
||||||
}
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
|
||||||
}
|
|
||||||
return $aVals;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes query. Returns associate array mapping first value to second value of each result.
|
|
||||||
* Returns empty array if no results found.
|
|
||||||
*
|
|
||||||
* @param string $sSQL
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
*/
|
|
||||||
public function getAssoc($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
|
|
||||||
|
|
||||||
$aList = array();
|
|
||||||
while ($aRow = $stmt->fetch(\PDO::FETCH_NUM)) {
|
|
||||||
$aList[$aRow[0]] = $aRow[1];
|
|
||||||
}
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
|
||||||
}
|
|
||||||
return $aList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes query. Returns a PDO statement to iterate over.
|
|
||||||
*
|
|
||||||
* @param string $sSQL
|
|
||||||
*
|
|
||||||
* @return PDOStatement
|
|
||||||
*/
|
|
||||||
public function getQueryStatement($sSQL, $aInputVars = null, $sErrMessage = 'Database query failed')
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (isset($aInputVars)) {
|
|
||||||
$stmt = $this->connection->prepare($sSQL);
|
|
||||||
$stmt->execute($aInputVars);
|
|
||||||
} else {
|
|
||||||
$stmt = $this->connection->query($sSQL);
|
|
||||||
}
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
throw new \Nominatim\DatabaseError($sErrMessage, 500, null, $e, $sSQL);
|
|
||||||
}
|
|
||||||
return $stmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* St. John's Way => 'St. John\'s Way'
|
|
||||||
*
|
|
||||||
* @param string $sVal Text to be quoted.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDBQuoted($sVal)
|
|
||||||
{
|
|
||||||
return $this->connection->quote($sVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like getDBQuoted, but takes an array.
|
|
||||||
*
|
|
||||||
* @param array $aVals List of text to be quoted.
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
*/
|
|
||||||
public function getDBQuotedList($aVals)
|
|
||||||
{
|
|
||||||
return array_map(function ($sVal) {
|
|
||||||
return $this->getDBQuoted($sVal);
|
|
||||||
}, $aVals);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [1,2,'b'] => 'ARRAY[1,2,'b']''
|
|
||||||
*
|
|
||||||
* @param array $aVals List of text to be quoted.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getArraySQL($a)
|
|
||||||
{
|
|
||||||
return 'ARRAY['.join(',', $a).']';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a table exists in the database. Returns true if it does.
|
|
||||||
*
|
|
||||||
* @param string $sTableName
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function tableExists($sTableName)
|
|
||||||
{
|
|
||||||
$sSQL = 'SELECT count(*) FROM pg_tables WHERE tablename = :tablename';
|
|
||||||
return ($this->getOne($sSQL, array(':tablename' => $sTableName)) == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Since the DSN includes the database name, checks if the connection works.
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function databaseExists()
|
|
||||||
{
|
|
||||||
$bExists = true;
|
|
||||||
try {
|
|
||||||
$this->connect(true);
|
|
||||||
} catch (\Nominatim\DatabaseError $e) {
|
|
||||||
$bExists = false;
|
|
||||||
}
|
|
||||||
return $bExists;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* e.g. 9.6, 10, 11.2
|
|
||||||
*
|
|
||||||
* @return float
|
|
||||||
*/
|
|
||||||
public function getPostgresVersion()
|
|
||||||
{
|
|
||||||
$sVersionString = $this->getOne('SHOW server_version_num');
|
|
||||||
preg_match('#([0-9]?[0-9])([0-9][0-9])[0-9][0-9]#', $sVersionString, $aMatches);
|
|
||||||
return (float) ($aMatches[1].'.'.$aMatches[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* e.g. 2, 2.2
|
|
||||||
*
|
|
||||||
* @return float
|
|
||||||
*/
|
|
||||||
public function getPostgisVersion()
|
|
||||||
{
|
|
||||||
$sVersionString = $this->getOne('select postgis_lib_version()');
|
|
||||||
preg_match('#^([0-9]+)[.]([0-9]+)[.]#', $sVersionString, $aMatches);
|
|
||||||
return (float) ($aMatches[1].'.'.$aMatches[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function parseDSN($sDSN)
|
|
||||||
{
|
|
||||||
// https://secure.php.net/manual/en/ref.pdo-pgsql.connection.php
|
|
||||||
$aInfo = array();
|
|
||||||
if (preg_match('/^pgsql:(.+)/', $sDSN, $aMatches)) {
|
|
||||||
foreach (explode(';', $aMatches[1]) as $sKeyVal) {
|
|
||||||
list($sKey, $sVal) = explode('=', $sKeyVal, 2);
|
|
||||||
if ($sKey == 'host') $sKey = 'hostspec';
|
|
||||||
if ($sKey == 'dbname') $sKey = 'database';
|
|
||||||
if ($sKey == 'user') $sKey = 'username';
|
|
||||||
$aInfo[$sKey] = $sVal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
class DatabaseError extends \Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
public function __construct($message, $code = 500, Exception $previous = null, $oPDOErr, $sSql = null)
|
|
||||||
{
|
|
||||||
parent::__construct($message, $code, $previous);
|
|
||||||
// https://secure.php.net/manual/en/class.pdoexception.php
|
|
||||||
$this->oPDOErr = $oPDOErr;
|
|
||||||
$this->sSql = $sSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSqlError()
|
|
||||||
{
|
|
||||||
return $this->oPDOErr->getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSqlDebugDump()
|
|
||||||
{
|
|
||||||
if (CONST_Debug) {
|
|
||||||
return var_export($this->oPDOErr, true);
|
|
||||||
} else {
|
|
||||||
return $this->sSql;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -348,7 +348,10 @@ class Geocode
|
|||||||
$aNewPhraseSearches = array();
|
$aNewPhraseSearches = array();
|
||||||
$sPhraseType = $bIsStructured ? $oPhrase->getPhraseType() : '';
|
$sPhraseType = $bIsStructured ? $oPhrase->getPhraseType() : '';
|
||||||
|
|
||||||
foreach ($oPhrase->getWordSets() as $aWordset) {
|
foreach ($oPhrase->getWordSets() as $iWordSet => $aWordset) {
|
||||||
|
// Too many permutations - too expensive
|
||||||
|
if ($iWordSet > 120) break;
|
||||||
|
|
||||||
$aWordsetSearches = $aSearches;
|
$aWordsetSearches = $aSearches;
|
||||||
|
|
||||||
// Add all words from this wordset
|
// Add all words from this wordset
|
||||||
@@ -524,8 +527,8 @@ class Geocode
|
|||||||
$sNormQuery = $this->normTerm($this->sQuery);
|
$sNormQuery = $this->normTerm($this->sQuery);
|
||||||
Debug::printVar('Normalized query', $sNormQuery);
|
Debug::printVar('Normalized query', $sNormQuery);
|
||||||
|
|
||||||
$sLanguagePrefArraySQL = $this->oDB->getArraySQL(
|
$sLanguagePrefArraySQL = getArraySQL(
|
||||||
$this->oDB->getDBQuotedList($this->aLangPrefOrder)
|
array_map('getDBQuoted', $this->aLangPrefOrder)
|
||||||
);
|
);
|
||||||
|
|
||||||
$sQuery = $this->sQuery;
|
$sQuery = $this->sQuery;
|
||||||
@@ -543,6 +546,7 @@ class Geocode
|
|||||||
// Do we have anything that looks like a lat/lon pair?
|
// Do we have anything that looks like a lat/lon pair?
|
||||||
$sQuery = $oCtx->setNearPointFromQuery($sQuery);
|
$sQuery = $oCtx->setNearPointFromQuery($sQuery);
|
||||||
|
|
||||||
|
$aResults = array();
|
||||||
if ($sQuery || $this->aStructuredQuery) {
|
if ($sQuery || $this->aStructuredQuery) {
|
||||||
// Start with a single blank search
|
// Start with a single blank search
|
||||||
$aSearches = array(new SearchDescription($oCtx));
|
$aSearches = array(new SearchDescription($oCtx));
|
||||||
@@ -578,9 +582,8 @@ class Geocode
|
|||||||
|
|
||||||
if ($sSpecialTerm && !$aSearches[0]->hasOperator()) {
|
if ($sSpecialTerm && !$aSearches[0]->hasOperator()) {
|
||||||
$sSpecialTerm = pg_escape_string($sSpecialTerm);
|
$sSpecialTerm = pg_escape_string($sSpecialTerm);
|
||||||
$sToken = $this->oDB->getOne(
|
$sToken = chksql(
|
||||||
'SELECT make_standard_name(:term)',
|
$this->oDB->getOne("SELECT make_standard_name('$sSpecialTerm')"),
|
||||||
array(':term' => $sSpecialTerm),
|
|
||||||
'Cannot decode query. Wrong encoding?'
|
'Cannot decode query. Wrong encoding?'
|
||||||
);
|
);
|
||||||
$sSQL = 'SELECT class, type FROM word ';
|
$sSQL = 'SELECT class, type FROM word ';
|
||||||
@@ -588,7 +591,7 @@ class Geocode
|
|||||||
$sSQL .= ' AND class is not null AND class not in (\'place\')';
|
$sSQL .= ' AND class is not null AND class not in (\'place\')';
|
||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aSearchWords = $this->oDB->getAll($sSQL);
|
$aSearchWords = chksql($this->oDB->getAll($sSQL));
|
||||||
$aNewSearches = array();
|
$aNewSearches = array();
|
||||||
foreach ($aSearches as $oSearch) {
|
foreach ($aSearches as $oSearch) {
|
||||||
foreach ($aSearchWords as $aSearchTerm) {
|
foreach ($aSearchWords as $aSearchTerm) {
|
||||||
@@ -626,9 +629,8 @@ class Geocode
|
|||||||
$aTokens = array();
|
$aTokens = array();
|
||||||
$aPhrases = array();
|
$aPhrases = array();
|
||||||
foreach ($aInPhrases as $iPhrase => $sPhrase) {
|
foreach ($aInPhrases as $iPhrase => $sPhrase) {
|
||||||
$sPhrase = $this->oDB->getOne(
|
$sPhrase = chksql(
|
||||||
'SELECT make_standard_name(:phrase)',
|
$this->oDB->getOne('SELECT make_standard_name('.getDBQuoted($sPhrase).')'),
|
||||||
array(':phrase' => $sPhrase),
|
|
||||||
'Cannot normalize query string (is it a UTF-8 string?)'
|
'Cannot normalize query string (is it a UTF-8 string?)'
|
||||||
);
|
);
|
||||||
if (trim($sPhrase)) {
|
if (trim($sPhrase)) {
|
||||||
@@ -638,6 +640,7 @@ class Geocode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug::printDebugTable('Phrases', $aPhrases);
|
||||||
Debug::printVar('Tokens', $aTokens);
|
Debug::printVar('Tokens', $aTokens);
|
||||||
|
|
||||||
$oValidTokens = new TokenList();
|
$oValidTokens = new TokenList();
|
||||||
@@ -645,7 +648,7 @@ class Geocode
|
|||||||
if (!empty($aTokens)) {
|
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(',', $this->oDB->getDBQuotedList($aTokens)).')';
|
$sSQL .= ' WHERE word_token in ('.join(',', array_map('getDBQuoted', $aTokens)).')';
|
||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
@@ -682,11 +685,6 @@ class Geocode
|
|||||||
|
|
||||||
Debug::printGroupTable('Valid Tokens', $oValidTokens->debugInfo());
|
Debug::printGroupTable('Valid Tokens', $oValidTokens->debugInfo());
|
||||||
|
|
||||||
foreach ($aPhrases as $oPhrase) {
|
|
||||||
$oPhrase->computeWordSets($oValidTokens);
|
|
||||||
}
|
|
||||||
Debug::printDebugTable('Phrases', $aPhrases);
|
|
||||||
|
|
||||||
Debug::newSection('Search candidates');
|
Debug::newSection('Search candidates');
|
||||||
|
|
||||||
$aGroupedSearches = $this->getGroupedSearches($aSearches, $aPhrases, $oValidTokens, $bStructuredPhrases);
|
$aGroupedSearches = $this->getGroupedSearches($aSearches, $aPhrases, $oValidTokens, $bStructuredPhrases);
|
||||||
@@ -748,10 +746,8 @@ class Geocode
|
|||||||
// Start the search process
|
// Start the search process
|
||||||
$iGroupLoop = 0;
|
$iGroupLoop = 0;
|
||||||
$iQueryLoop = 0;
|
$iQueryLoop = 0;
|
||||||
$aNextResults = array();
|
|
||||||
foreach ($aGroupedSearches as $iGroupedRank => $aSearches) {
|
foreach ($aGroupedSearches as $iGroupedRank => $aSearches) {
|
||||||
$iGroupLoop++;
|
$iGroupLoop++;
|
||||||
$aResults = $aNextResults;
|
|
||||||
foreach ($aSearches as $oSearch) {
|
foreach ($aSearches as $oSearch) {
|
||||||
$iQueryLoop++;
|
$iQueryLoop++;
|
||||||
|
|
||||||
@@ -761,42 +757,16 @@ class Geocode
|
|||||||
$oValidTokens->debugTokenByWordIdList()
|
$oValidTokens->debugTokenByWordIdList()
|
||||||
);
|
);
|
||||||
|
|
||||||
$aNewResults = $oSearch->query(
|
$aResults += $oSearch->query(
|
||||||
$this->oDB,
|
$this->oDB,
|
||||||
$this->iMinAddressRank,
|
$this->iMinAddressRank,
|
||||||
$this->iMaxAddressRank,
|
$this->iMaxAddressRank,
|
||||||
$this->iLimit
|
$this->iLimit
|
||||||
);
|
);
|
||||||
|
|
||||||
// The same result may appear in different rounds, only
|
|
||||||
// use the one with minimal rank.
|
|
||||||
foreach ($aNewResults as $iPlace => $oRes) {
|
|
||||||
if (!isset($aResults[$iPlace])
|
|
||||||
|| $aResults[$iPlace]->iResultRank > $oRes->iResultRank) {
|
|
||||||
$aResults[$iPlace] = $oRes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($iQueryLoop > 20) break;
|
if ($iQueryLoop > 20) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($aResults)) {
|
|
||||||
$aSplitResults = Result::splitResults($aResults);
|
|
||||||
Debug::printVar('Split results', $aSplitResults);
|
|
||||||
if ($iGroupLoop <= 4 && empty($aSplitResults['tail'])
|
|
||||||
&& reset($aSplitResults['head'])->iResultRank > 0) {
|
|
||||||
// Haven't found an exact match for the query yet.
|
|
||||||
// Therefore add result from the next group level.
|
|
||||||
$aNextResults = $aSplitResults['head'];
|
|
||||||
foreach ($aNextResults as $oRes) {
|
|
||||||
$oRes->iResultRank--;
|
|
||||||
}
|
|
||||||
$aResults = array();
|
|
||||||
} else {
|
|
||||||
$aResults = $aSplitResults['head'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($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
|
||||||
@@ -833,7 +803,7 @@ class Geocode
|
|||||||
if ($aFilterSql) {
|
if ($aFilterSql) {
|
||||||
$sSQL = join(' UNION ', $aFilterSql);
|
$sSQL = join(' UNION ', $aFilterSql);
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aFilteredIDs = $this->oDB->getCol($sSQL);
|
$aFilteredIDs = chksql($this->oDB->getCol($sSQL));
|
||||||
}
|
}
|
||||||
|
|
||||||
$tempIDs = array();
|
$tempIDs = array();
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class ParameterParser
|
|||||||
$sLangString = $this->getString('accept-language', $sFallback);
|
$sLangString = $this->getString('accept-language', $sFallback);
|
||||||
|
|
||||||
if ($sLangString) {
|
if ($sLangString) {
|
||||||
if (preg_match_all('/(([a-z]{1,8})([-_][a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $sLangString, $aLanguagesParse, PREG_SET_ORDER)) {
|
if (preg_match_all('/(([a-z]{1,8})(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $sLangString, $aLanguagesParse, PREG_SET_ORDER)) {
|
||||||
foreach ($aLanguagesParse as $iLang => $aLanguage) {
|
foreach ($aLanguagesParse as $iLang => $aLanguage) {
|
||||||
$aLanguages[$aLanguage[1]] = isset($aLanguage[5])?(float)$aLanguage[5]:1 - ($iLang/100);
|
$aLanguages[$aLanguage[1]] = isset($aLanguage[5])?(float)$aLanguage[5]:1 - ($iLang/100);
|
||||||
if (!isset($aLanguages[$aLanguage[2]])) $aLanguages[$aLanguage[2]] = $aLanguages[$aLanguage[1]]/10;
|
if (!isset($aLanguages[$aLanguage[2]])) $aLanguages[$aLanguage[2]] = $aLanguages[$aLanguage[1]]/10;
|
||||||
|
|||||||
100
lib/Phrase.php
100
lib/Phrase.php
@@ -9,8 +9,7 @@ namespace Nominatim;
|
|||||||
*/
|
*/
|
||||||
class Phrase
|
class Phrase
|
||||||
{
|
{
|
||||||
const MAX_WORDSET_LEN = 20;
|
const MAX_DEPTH = 7;
|
||||||
const MAX_WORDSETS = 100;
|
|
||||||
|
|
||||||
// Complete phrase as a string.
|
// Complete phrase as a string.
|
||||||
private $sPhrase;
|
private $sPhrase;
|
||||||
@@ -21,24 +20,13 @@ class Phrase
|
|||||||
// Possible segmentations of the phrase.
|
// Possible segmentations of the phrase.
|
||||||
private $aWordSets;
|
private $aWordSets;
|
||||||
|
|
||||||
public static function cmpByArraylen($aA, $aB)
|
|
||||||
{
|
|
||||||
$iALen = count($aA);
|
|
||||||
$iBLen = count($aB);
|
|
||||||
|
|
||||||
if ($iALen == $iBLen) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($iALen < $iBLen) ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct($sPhrase, $sPhraseType)
|
public function __construct($sPhrase, $sPhraseType)
|
||||||
{
|
{
|
||||||
$this->sPhrase = trim($sPhrase);
|
$this->sPhrase = trim($sPhrase);
|
||||||
$this->sPhraseType = $sPhraseType;
|
$this->sPhraseType = $sPhraseType;
|
||||||
$this->aWords = explode(' ', $this->sPhrase);
|
$this->aWords = explode(' ', $this->sPhrase);
|
||||||
|
$this->aWordSets = $this->createWordSets($this->aWords, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,17 +60,10 @@ class Phrase
|
|||||||
*/
|
*/
|
||||||
public function addTokens(&$aTokens)
|
public function addTokens(&$aTokens)
|
||||||
{
|
{
|
||||||
$iNumWords = count($this->aWords);
|
foreach ($this->aWordSets as $aSet) {
|
||||||
|
foreach ($aSet as $sWord) {
|
||||||
for ($i = 0; $i < $iNumWords; $i++) {
|
$aTokens[' '.$sWord] = ' '.$sWord;
|
||||||
$sPhrase = $this->aWords[$i];
|
$aTokens[$sWord] = $sWord;
|
||||||
$aTokens[' '.$sPhrase] = ' '.$sPhrase;
|
|
||||||
$aTokens[$sPhrase] = $sPhrase;
|
|
||||||
|
|
||||||
for ($j = $i + 1; $j < $iNumWords; $j++) {
|
|
||||||
$sPhrase .= ' '.$this->aWords[$j];
|
|
||||||
$aTokens[' '.$sPhrase] = ' '.$sPhrase;
|
|
||||||
$aTokens[$sPhrase] = $sPhrase;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,60 +75,45 @@ class Phrase
|
|||||||
*/
|
*/
|
||||||
public function invertWordSets()
|
public function invertWordSets()
|
||||||
{
|
{
|
||||||
foreach ($this->aWordSets as $i => $aSet) {
|
$this->aWordSets = $this->createInverseWordSets($this->aWords, 0);
|
||||||
$this->aWordSets[$i] = array_reverse($aSet);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function computeWordSets($oTokens)
|
private function createWordSets($aWords, $iDepth)
|
||||||
{
|
{
|
||||||
$iNumWords = count($this->aWords);
|
$aResult = array(array(join(' ', $aWords)));
|
||||||
// Caches the word set for the partial phrase up to word i.
|
$sFirstToken = '';
|
||||||
$aSetCache = array_fill(0, $iNumWords, array());
|
if ($iDepth < Phrase::MAX_DEPTH) {
|
||||||
|
while (count($aWords) > 1) {
|
||||||
// Initialise first element of cache. There can only be the word.
|
$sWord = array_shift($aWords);
|
||||||
if ($oTokens->containsAny($this->aWords[0])) {
|
$sFirstToken .= ($sFirstToken?' ':'').$sWord;
|
||||||
$aSetCache[0][] = array($this->aWords[0]);
|
$aRest = $this->createWordSets($aWords, $iDepth + 1);
|
||||||
}
|
foreach ($aRest as $aSet) {
|
||||||
|
$aResult[] = array_merge(array($sFirstToken), $aSet);
|
||||||
// Now do the next elements using what we already have.
|
|
||||||
for ($i = 1; $i < $iNumWords; $i++) {
|
|
||||||
for ($j = $i; $j > 0; $j--) {
|
|
||||||
$sPartial = $j == $i ? $this->aWords[$j] : $this->aWords[$j].' '.$sPartial;
|
|
||||||
if (!empty($aSetCache[$j - 1]) && $oTokens->containsAny($sPartial)) {
|
|
||||||
$aPartial = array($sPartial);
|
|
||||||
foreach ($aSetCache[$j - 1] as $aSet) {
|
|
||||||
if (count($aSet) < Phrase::MAX_WORDSET_LEN) {
|
|
||||||
$aSetCache[$i][] = array_merge($aSet, $aPartial);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count($aSetCache[$i]) > 2 * Phrase::MAX_WORDSETS) {
|
|
||||||
usort(
|
|
||||||
$aSetCache[$i],
|
|
||||||
array('\Nominatim\Phrase', 'cmpByArraylen')
|
|
||||||
);
|
|
||||||
$aSetCache[$i] = array_slice(
|
|
||||||
$aSetCache[$i],
|
|
||||||
0,
|
|
||||||
Phrase::MAX_WORDSETS
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally the current full phrase
|
return $aResult;
|
||||||
$sPartial = $this->aWords[0].' '.$sPartial;
|
}
|
||||||
if ($oTokens->containsAny($sPartial)) {
|
|
||||||
$aSetCache[$i][] = array($sPartial);
|
private function createInverseWordSets($aWords, $iDepth)
|
||||||
|
{
|
||||||
|
$aResult = array(array(join(' ', $aWords)));
|
||||||
|
$sFirstToken = '';
|
||||||
|
if ($iDepth < Phrase::MAX_DEPTH) {
|
||||||
|
while (count($aWords) > 1) {
|
||||||
|
$sWord = array_pop($aWords);
|
||||||
|
$sFirstToken = $sWord.($sFirstToken?' ':'').$sFirstToken;
|
||||||
|
$aRest = $this->createInverseWordSets($aWords, $iDepth + 1);
|
||||||
|
foreach ($aRest as $aSet) {
|
||||||
|
$aResult[] = array_merge(array($sFirstToken), $aSet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->aWordSets = $aSetCache[$iNumWords - 1];
|
return $aResult;
|
||||||
usort($this->aWordSets, array('\Nominatim\Phrase', 'cmpByArraylen'));
|
|
||||||
$this->aWordSets = array_slice($this->aWordSets, 0, Phrase::MAX_WORDSETS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function debugInfo()
|
public function debugInfo()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class PlaceLookup
|
|||||||
{
|
{
|
||||||
$aLangs = $oParams->getPreferredLanguages();
|
$aLangs = $oParams->getPreferredLanguages();
|
||||||
$this->aLangPrefOrderSql =
|
$this->aLangPrefOrderSql =
|
||||||
'ARRAY['.join(',', $this->oDB->getDBQuotedList($aLangs)).']';
|
'ARRAY['.join(',', array_map('getDBQuoted', $aLangs)).']';
|
||||||
|
|
||||||
$this->bExtraTags = $oParams->getBool('extratags', false);
|
$this->bExtraTags = $oParams->getBool('extratags', false);
|
||||||
$this->bNameDetails = $oParams->getBool('namedetails', false);
|
$this->bNameDetails = $oParams->getBool('namedetails', false);
|
||||||
@@ -132,9 +132,8 @@ class PlaceLookup
|
|||||||
|
|
||||||
public function setLanguagePreference($aLangPrefOrder)
|
public function setLanguagePreference($aLangPrefOrder)
|
||||||
{
|
{
|
||||||
$this->aLangPrefOrderSql = $this->oDB->getArraySQL(
|
$this->aLangPrefOrderSql =
|
||||||
$this->oDB->getDBQuotedList($aLangPrefOrder)
|
'ARRAY['.join(',', array_map('getDBQuoted', $aLangPrefOrder)).']';
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addressImportanceSql($sGeometry, $sPlaceId)
|
private function addressImportanceSql($sGeometry, $sPlaceId)
|
||||||
@@ -163,8 +162,8 @@ class PlaceLookup
|
|||||||
|
|
||||||
public function lookupOSMID($sType, $iID)
|
public function lookupOSMID($sType, $iID)
|
||||||
{
|
{
|
||||||
$sSQL = 'select place_id from placex where osm_type = :type and osm_id = :id';
|
$sSQL = "select place_id from placex where osm_type = '".$sType."' and osm_id = ".$iID;
|
||||||
$iPlaceID = $this->oDB->getOne($sSQL, array(':type' => $sType, ':id' => $iID));
|
$iPlaceID = chksql($this->oDB->getOne($sSQL));
|
||||||
|
|
||||||
if (!$iPlaceID) {
|
if (!$iPlaceID) {
|
||||||
return null;
|
return null;
|
||||||
@@ -425,10 +424,9 @@ class PlaceLookup
|
|||||||
|
|
||||||
$sSQL = join(' UNION ', $aSubSelects);
|
$sSQL = join(' UNION ', $aSubSelects);
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aPlaces = $this->oDB->getAll($sSQL, null, 'Could not lookup place');
|
$aPlaces = chksql($this->oDB->getAll($sSQL), 'Could not lookup place');
|
||||||
|
|
||||||
foreach ($aPlaces as &$aPlace) {
|
foreach ($aPlaces as &$aPlace) {
|
||||||
$aPlace['importance'] = (float) $aPlace['importance'];
|
|
||||||
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['address'] = new AddressDetails(
|
$aPlace['address'] = new AddressDetails(
|
||||||
@@ -515,9 +513,9 @@ class PlaceLookup
|
|||||||
$sSQL .= $sFrom;
|
$sSQL .= $sFrom;
|
||||||
}
|
}
|
||||||
|
|
||||||
$aPointPolygon = $this->oDB->getRow($sSQL, null, 'Could not get outline');
|
$aPointPolygon = chksql($this->oDB->getRow($sSQL), 'Could not get outline');
|
||||||
|
|
||||||
if ($aPointPolygon && $aPointPolygon['place_id']) {
|
if ($aPointPolygon['place_id']) {
|
||||||
if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null) {
|
if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null) {
|
||||||
$aOutlineResult['lat'] = $aPointPolygon['centrelat'];
|
$aOutlineResult['lat'] = $aPointPolygon['centrelat'];
|
||||||
$aOutlineResult['lon'] = $aPointPolygon['centrelon'];
|
$aOutlineResult['lon'] = $aPointPolygon['centrelon'];
|
||||||
|
|||||||
@@ -68,32 +68,4 @@ class Result
|
|||||||
|
|
||||||
return $sHousenumbers;
|
return $sHousenumbers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Split a result array into highest ranked result and the rest
|
|
||||||
*
|
|
||||||
* @param object[] $aResults List of results to split.
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
*/
|
|
||||||
public static function splitResults($aResults)
|
|
||||||
{
|
|
||||||
$aHead = array();
|
|
||||||
$aTail = array();
|
|
||||||
$iMinRank = 10000;
|
|
||||||
|
|
||||||
foreach ($aResults as $oRes) {
|
|
||||||
if ($oRes->iResultRank < $iMinRank) {
|
|
||||||
$aTail = array_merge($aTail, $aHead);
|
|
||||||
$aHead = array($oRes->iId => $oRes);
|
|
||||||
$iMinRank = $oRes->iResultRank;
|
|
||||||
} elseif ($oRes->iResultRank == $iMinRank) {
|
|
||||||
$aHead[$oRes->iId] = $oRes;
|
|
||||||
} else {
|
|
||||||
$aTail[$oRes->iId] = $oRes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array('head' => $aHead, 'tail' => $aTail);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ class ReverseGeocode
|
|||||||
13 => 18,
|
13 => 18,
|
||||||
14 => 22, // Suburb
|
14 => 22, // Suburb
|
||||||
15 => 22,
|
15 => 22,
|
||||||
16 => 26, // major street
|
16 => 26, // Street, TODO: major street?
|
||||||
17 => 27, // minor street
|
17 => 26,
|
||||||
18 => 30, // or >, Building
|
18 => 30, // or >, Building
|
||||||
19 => 30, // or >, Building
|
19 => 30, // or >, Building
|
||||||
);
|
);
|
||||||
@@ -63,9 +63,8 @@ class ReverseGeocode
|
|||||||
$sSQL .= ' and indexed_status = 0 and startnumber is not NULL ';
|
$sSQL .= ' and indexed_status = 0 and startnumber is not NULL ';
|
||||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||||
|
|
||||||
return $this->oDB->getRow(
|
return chksql(
|
||||||
$sSQL,
|
$this->oDB->getRow($sSQL),
|
||||||
null,
|
|
||||||
'Could not determine closest housenumber on an osm interpolation line.'
|
'Could not determine closest housenumber on an osm interpolation line.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -93,9 +92,8 @@ class ReverseGeocode
|
|||||||
$sSQL = 'SELECT country_code FROM country_osm_grid';
|
$sSQL = 'SELECT country_code FROM country_osm_grid';
|
||||||
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.') LIMIT 1';
|
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.') LIMIT 1';
|
||||||
|
|
||||||
$sCountryCode = $this->oDB->getOne(
|
$sCountryCode = chksql(
|
||||||
$sSQL,
|
$this->oDB->getOne($sSQL),
|
||||||
null,
|
|
||||||
'Could not determine country polygon containing the point.'
|
'Could not determine country polygon containing the point.'
|
||||||
);
|
);
|
||||||
if ($sCountryCode) {
|
if ($sCountryCode) {
|
||||||
@@ -117,7 +115,10 @@ class ReverseGeocode
|
|||||||
$sSQL .= ' LIMIT 1';
|
$sSQL .= ' LIMIT 1';
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
$aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
$aPlace = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine place node.'
|
||||||
|
);
|
||||||
if ($aPlace) {
|
if ($aPlace) {
|
||||||
return new Result($aPlace['place_id']);
|
return new Result($aPlace['place_id']);
|
||||||
}
|
}
|
||||||
@@ -133,7 +134,10 @@ class ReverseGeocode
|
|||||||
$sSQL .= ' ORDER BY distance ASC';
|
$sSQL .= ' ORDER BY distance ASC';
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
$aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
$aPlace = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine place node.'
|
||||||
|
);
|
||||||
if ($aPlace) {
|
if ($aPlace) {
|
||||||
return new Result($aPlace['place_id']);
|
return new Result($aPlace['place_id']);
|
||||||
}
|
}
|
||||||
@@ -174,8 +178,10 @@ class ReverseGeocode
|
|||||||
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.' )';
|
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.' )';
|
||||||
$sSQL .= ' ORDER BY rank_address DESC LIMIT 1';
|
$sSQL .= ' ORDER BY rank_address DESC LIMIT 1';
|
||||||
|
|
||||||
$aPoly = $this->oDB->getRow($sSQL, null, 'Could not determine polygon containing the point.');
|
$aPoly = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine polygon containing the point.'
|
||||||
|
);
|
||||||
if ($aPoly) {
|
if ($aPoly) {
|
||||||
// if a polygon is found, search for placenodes begins ...
|
// if a polygon is found, search for placenodes begins ...
|
||||||
$iParentPlaceID = $aPoly['parent_place_id'];
|
$iParentPlaceID = $aPoly['parent_place_id'];
|
||||||
@@ -207,7 +213,10 @@ class ReverseGeocode
|
|||||||
$sSQL .= ' LIMIT 1';
|
$sSQL .= ' LIMIT 1';
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
$aPlacNode = $this->oDB->getRow($sSQL, null, 'Could not determine place node.');
|
$aPlacNode = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine place node.'
|
||||||
|
);
|
||||||
if ($aPlacNode) {
|
if ($aPlacNode) {
|
||||||
return $aPlacNode;
|
return $aPlacNode;
|
||||||
}
|
}
|
||||||
@@ -246,18 +255,24 @@ class ReverseGeocode
|
|||||||
$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 {
|
||||||
$sSQL .= ' rank_address between 26 and '.$iMaxRank;
|
$sSQL .= ' rank_address between 26 and '.$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_address between 26 and 27)';
|
$sSQL .= ' or rank_address between 26 and 27)';
|
||||||
$sSQL .= ' and (rank_address between 26 and 27';
|
$sSQL .= ' and class not in (\'railway\',\'tunnel\',\'bridge\',\'man_made\')';
|
||||||
$sSQL .= ' or ST_GeometryType(geometry) != \'ST_LineString\')';
|
|
||||||
$sSQL .= ' and class not in (\'boundary\')';
|
|
||||||
$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.'))';
|
||||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
$aPlace = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.');
|
$aPlace = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine closest place.'
|
||||||
|
);
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($aPlace);
|
if (CONST_Debug) var_dump($aPlace);
|
||||||
if ($aPlace) {
|
if ($aPlace) {
|
||||||
@@ -299,14 +314,16 @@ class ReverseGeocode
|
|||||||
// radius ?
|
// radius ?
|
||||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, 0.001)';
|
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, 0.001)';
|
||||||
$sSQL .= ' AND parent_place_id = '.$iPlaceID;
|
$sSQL .= ' AND parent_place_id = '.$iPlaceID;
|
||||||
$sSQL .= ' and rank_address > 28';
|
$sSQL .= ' and rank_address != 28';
|
||||||
$sSQL .= ' and ST_GeometryType(geometry) != \'ST_LineString\'';
|
|
||||||
$sSQL .= ' and (name is not null or housenumber is not null)';
|
$sSQL .= ' and (name is not null or housenumber is not null)';
|
||||||
$sSQL .= ' and class not in (\'boundary\')';
|
$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 .= ' ORDER BY distance ASC limit 1';
|
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
$aStreet = $this->oDB->getRow($sSQL, null, 'Could not determine closest place.');
|
$aStreet = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine closest place.'
|
||||||
|
);
|
||||||
if ($aStreet) {
|
if ($aStreet) {
|
||||||
if (CONST_Debug) var_dump($aStreet);
|
if (CONST_Debug) var_dump($aStreet);
|
||||||
$oResult = new Result($aStreet['place_id']);
|
$oResult = new Result($aStreet['place_id']);
|
||||||
@@ -327,7 +344,10 @@ class ReverseGeocode
|
|||||||
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', linegeo, 0.001)';
|
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', linegeo, 0.001)';
|
||||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
$sSQL .= ' ORDER BY distance ASC limit 1';
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
$aPlaceTiger = $this->oDB->getRow($sSQL, null, 'Could not determine closest Tiger place.');
|
$aPlaceTiger = chksql(
|
||||||
|
$this->oDB->getRow($sSQL),
|
||||||
|
'Could not determine closest Tiger place.'
|
||||||
|
);
|
||||||
if ($aPlaceTiger) {
|
if ($aPlaceTiger) {
|
||||||
if (CONST_Debug) var_dump('found Tiger housenumber', $aPlaceTiger);
|
if (CONST_Debug) var_dump('found Tiger housenumber', $aPlaceTiger);
|
||||||
$oResult = new Result($aPlaceTiger['place_id'], Result::TABLE_TIGER);
|
$oResult = new Result($aPlaceTiger['place_id'], Result::TABLE_TIGER);
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class SearchContext
|
|||||||
* The viewbox may be bounded which means that no search results
|
* The viewbox may be bounded which means that no search results
|
||||||
* must be outside the viewbox.
|
* must be outside the viewbox.
|
||||||
*
|
*
|
||||||
* @param object $oDB Nominatim::DB instance to use for computing the box.
|
* @param object $oDB DB connection to use for computing the box.
|
||||||
* @param string[] $aRoutePoints List of x,y coordinates along a route.
|
* @param string[] $aRoutePoints List of x,y coordinates along a route.
|
||||||
* @param float $fRouteWidth Buffer around the route to use.
|
* @param float $fRouteWidth Buffer around the route to use.
|
||||||
* @param bool $bBounded True if the viewbox bounded.
|
* @param bool $bBounded True if the viewbox bounded.
|
||||||
@@ -146,11 +146,11 @@ class SearchContext
|
|||||||
$this->sqlViewboxCentre .= ")'::geometry,4326)";
|
$this->sqlViewboxCentre .= ")'::geometry,4326)";
|
||||||
|
|
||||||
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/69).')';
|
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/69).')';
|
||||||
$sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get small viewbox');
|
$sGeom = chksql($oDB->getOne('select '.$sSQL), 'Could not get small viewbox');
|
||||||
$this->sqlViewboxSmall = "'".$sGeom."'::geometry";
|
$this->sqlViewboxSmall = "'".$sGeom."'::geometry";
|
||||||
|
|
||||||
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/30).')';
|
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/30).')';
|
||||||
$sGeom = $oDB->getOne('select '.$sSQL, null, 'Could not get large viewbox');
|
$sGeom = chksql($oDB->getOne('select '.$sSQL), 'Could not get large viewbox');
|
||||||
$this->sqlViewboxLarge = "'".$sGeom."'::geometry";
|
$this->sqlViewboxLarge = "'".$sGeom."'::geometry";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -237,8 +237,7 @@ class SearchDescription
|
|||||||
$oSearch->sHouseNumber = $oSearchTerm->sToken;
|
$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('/\\d/', $oSearch->sHouseNumber) === 0
|
if (preg_match_all('/[^0-9]/', $oSearch->sHouseNumber, $aMatches) > 2) {
|
||||||
|| preg_match_all('/[^0-9]/', $oSearch->sHouseNumber, $aMatches) > 2) {
|
|
||||||
$oSearch->iSearchRank++;
|
$oSearch->iSearchRank++;
|
||||||
}
|
}
|
||||||
if (empty($oSearchTerm->iId)) {
|
if (empty($oSearchTerm->iId)) {
|
||||||
@@ -288,7 +287,7 @@ class SearchDescription
|
|||||||
if (!empty($this->aName) || !($bFirstPhrase || $sPhraseType == '')) {
|
if (!empty($this->aName) || !($bFirstPhrase || $sPhraseType == '')) {
|
||||||
if (($sPhraseType == '' || !$bFirstPhrase) && !$bHasPartial) {
|
if (($sPhraseType == '' || !$bFirstPhrase) && !$bHasPartial) {
|
||||||
$oSearch = clone $this;
|
$oSearch = clone $this;
|
||||||
$oSearch->iSearchRank += 2;
|
$oSearch->iSearchRank++;
|
||||||
$oSearch->aAddress[$iWordID] = $iWordID;
|
$oSearch->aAddress[$iWordID] = $iWordID;
|
||||||
$aNewSearches[] = $oSearch;
|
$aNewSearches[] = $oSearch;
|
||||||
} else {
|
} else {
|
||||||
@@ -404,7 +403,7 @@ class SearchDescription
|
|||||||
/**
|
/**
|
||||||
* Query database for places that match this search.
|
* Query database for places that match this search.
|
||||||
*
|
*
|
||||||
* @param object $oDB Nominatim::DB instance to use.
|
* @param object $oDB Database connection to use.
|
||||||
* @param integer $iMinRank Minimum address rank to restrict search to.
|
* @param integer $iMinRank Minimum address rank to restrict search to.
|
||||||
* @param integer $iMaxRank Maximum address rank to restrict search to.
|
* @param integer $iMaxRank Maximum address rank to restrict search to.
|
||||||
* @param integer $iLimit Maximum number of results.
|
* @param integer $iLimit Maximum number of results.
|
||||||
@@ -449,18 +448,11 @@ class SearchDescription
|
|||||||
|
|
||||||
//now search for housenumber, if housenumber provided
|
//now search for housenumber, if housenumber provided
|
||||||
if ($this->sHouseNumber && !empty($aResults)) {
|
if ($this->sHouseNumber && !empty($aResults)) {
|
||||||
// Downgrade the rank of the street results, they are missing
|
$aNamedPlaceIDs = $aResults;
|
||||||
// the housenumber.
|
$aResults = $this->queryHouseNumber($oDB, $aNamedPlaceIDs);
|
||||||
foreach ($aResults as $oRes) {
|
|
||||||
$oRes->iResultRank++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$aHnResults = $this->queryHouseNumber($oDB, $aResults);
|
if (empty($aResults) && $this->looksLikeFullAddress()) {
|
||||||
|
$aResults = $aNamedPlaceIDs;
|
||||||
if (!empty($aHnResults)) {
|
|
||||||
foreach ($aHnResults as $oRes) {
|
|
||||||
$aResults[$oRes->iId] = $oRes;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,13 +469,16 @@ class SearchDescription
|
|||||||
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."'";
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aFilteredPlaceIDs = $oDB->getCol($sSQL);
|
$aFilteredPlaceIDs = chksql($oDB->getCol($sSQL));
|
||||||
if ($aFilteredPlaceIDs) {
|
if ($aFilteredPlaceIDs) {
|
||||||
|
$aNewResults = array();
|
||||||
foreach ($aFilteredPlaceIDs as $iPlaceId) {
|
foreach ($aFilteredPlaceIDs as $iPlaceId) {
|
||||||
$aResults[$iPlaceId]->iResultRank++;
|
$aNewResults[$iPlaceId] = $aResults[$iPlaceId];
|
||||||
}
|
}
|
||||||
|
$aResults = $aNewResults;
|
||||||
|
Debug::printVar('Place IDs after postcode filtering', $aResults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -504,10 +499,8 @@ class SearchDescription
|
|||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
$iPlaceId = $oDB->getOne($sSQL);
|
|
||||||
|
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
if ($iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId);
|
$aResults[$iPlaceId] = new Result($iPlaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,7 +516,8 @@ class SearchDescription
|
|||||||
$aDBResults = array();
|
$aDBResults = array();
|
||||||
$sPoiTable = $this->poiTable();
|
$sPoiTable = $this->poiTable();
|
||||||
|
|
||||||
if ($oDB->tableExists($sPoiTable)) {
|
$sSQL = 'SELECT count(*) FROM pg_tables WHERE tablename = \''.$sPoiTable."'";
|
||||||
|
if (chksql($oDB->getOne($sSQL))) {
|
||||||
$sSQL = 'SELECT place_id FROM '.$sPoiTable.' ct';
|
$sSQL = 'SELECT place_id FROM '.$sPoiTable.' ct';
|
||||||
if ($this->oContext->sqlCountryList) {
|
if ($this->oContext->sqlCountryList) {
|
||||||
$sSQL .= ' JOIN placex USING (place_id)';
|
$sSQL .= ' JOIN placex USING (place_id)';
|
||||||
@@ -543,14 +537,14 @@ class SearchDescription
|
|||||||
} elseif ($this->oContext->hasNearPoint()) {
|
} elseif ($this->oContext->hasNearPoint()) {
|
||||||
$sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('ct.centroid').' ASC';
|
$sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('ct.centroid').' ASC';
|
||||||
}
|
}
|
||||||
$sSQL .= " LIMIT $iLimit";
|
$sSQL .= " limit $iLimit";
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aDBResults = $oDB->getCol($sSQL);
|
$aDBResults = chksql($oDB->getCol($sSQL));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->oContext->hasNearPoint()) {
|
if ($this->oContext->hasNearPoint()) {
|
||||||
$sSQL = 'SELECT place_id FROM placex WHERE ';
|
$sSQL = 'SELECT place_id FROM placex WHERE ';
|
||||||
$sSQL .= 'class = :class and type = :type';
|
$sSQL .= 'class=\''.$this->sClass."' and type='".$this->sType."'";
|
||||||
$sSQL .= ' AND '.$this->oContext->withinSQL('geometry');
|
$sSQL .= ' AND '.$this->oContext->withinSQL('geometry');
|
||||||
$sSQL .= ' AND linked_place_id is null';
|
$sSQL .= ' AND linked_place_id is null';
|
||||||
if ($this->oContext->sqlCountryList) {
|
if ($this->oContext->sqlCountryList) {
|
||||||
@@ -559,10 +553,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";
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aDBResults = $oDB->getCol(
|
$aDBResults = chksql($oDB->getCol($sSQL));
|
||||||
$sSQL,
|
|
||||||
array(':class' => $this->sClass, ':type' => $this->sType)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
@@ -581,23 +572,20 @@ class SearchDescription
|
|||||||
$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)';
|
||||||
$sSQL .= ' @> '.$oDB->getArraySQL($this->aAddress).' AND ';
|
$sSQL .= ' @> '.getArraySQL($this->aAddress).' AND ';
|
||||||
} else {
|
} else {
|
||||||
$sSQL .= 'WHERE ';
|
$sSQL .= 'WHERE ';
|
||||||
}
|
}
|
||||||
|
|
||||||
$sSQL .= "p.postcode = '".reset($this->aName)."'";
|
$sSQL .= "p.postcode = '".reset($this->aName)."'";
|
||||||
$sSQL .= $this->countryCodeSQL(' AND p.country_code');
|
$sSQL .= $this->countryCodeSQL(' AND p.country_code');
|
||||||
if ($this->oContext->bViewboxBounded) {
|
|
||||||
$sSQL .= ' AND ST_Intersects('.$this->oContext->sqlViewboxSmall.', geometry)';
|
|
||||||
}
|
|
||||||
$sSQL .= $this->oContext->excludeSQL(' AND p.place_id');
|
$sSQL .= $this->oContext->excludeSQL(' AND p.place_id');
|
||||||
$sSQL .= " LIMIT $iLimit";
|
$sSQL .= " LIMIT $iLimit";
|
||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
foreach ($oDB->getCol($sSQL) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId, Result::TABLE_POSTCODE);
|
$aResults[$iPlaceId] = new Result($iPlaceId, Result::TABLE_POSTCODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,14 +628,14 @@ class SearchDescription
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($this->aName)) {
|
if (!empty($this->aName)) {
|
||||||
$aTerms[] = 'name_vector @> '.$oDB->getArraySQL($this->aName);
|
$aTerms[] = 'name_vector @> '.getArraySQL($this->aName);
|
||||||
}
|
}
|
||||||
if (!empty($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 ($this->bRareName) {
|
if ($this->bRareName) {
|
||||||
$aTerms[] = 'array_cat(nameaddress_vector,ARRAY[]::integer[]) @> '.$oDB->getArraySQL($this->aAddress);
|
$aTerms[] = 'array_cat(nameaddress_vector,ARRAY[]::integer[]) @> '.getArraySQL($this->aAddress);
|
||||||
} else {
|
} else {
|
||||||
$aTerms[] = 'nameaddress_vector @> '.$oDB->getArraySQL($this->aAddress);
|
$aTerms[] = 'nameaddress_vector @> '.getArraySQL($this->aAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,7 +690,7 @@ class SearchDescription
|
|||||||
if (!empty($this->aFullNameAddress)) {
|
if (!empty($this->aFullNameAddress)) {
|
||||||
$sExactMatchSQL = ' ( ';
|
$sExactMatchSQL = ' ( ';
|
||||||
$sExactMatchSQL .= ' SELECT count(*) FROM ( ';
|
$sExactMatchSQL .= ' SELECT count(*) FROM ( ';
|
||||||
$sExactMatchSQL .= ' SELECT unnest('.$oDB->getArraySQL($this->aFullNameAddress).')';
|
$sExactMatchSQL .= ' SELECT unnest('.getArraySQL($this->aFullNameAddress).')';
|
||||||
$sExactMatchSQL .= ' INTERSECT ';
|
$sExactMatchSQL .= ' INTERSECT ';
|
||||||
$sExactMatchSQL .= ' SELECT unnest(nameaddress_vector)';
|
$sExactMatchSQL .= ' SELECT unnest(nameaddress_vector)';
|
||||||
$sExactMatchSQL .= ' ) s';
|
$sExactMatchSQL .= ' ) s';
|
||||||
@@ -713,7 +701,7 @@ class SearchDescription
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->sHouseNumber || $this->sClass) {
|
if ($this->sHouseNumber || $this->sClass) {
|
||||||
$iLimit = 40;
|
$iLimit = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
$aResults = array();
|
$aResults = array();
|
||||||
@@ -727,7 +715,10 @@ class SearchDescription
|
|||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
$aDBResults = $oDB->getAll($sSQL, null, 'Could not get places for search terms.');
|
$aDBResults = chksql(
|
||||||
|
$oDB->getAll($sSQL),
|
||||||
|
'Could not get places for search terms.'
|
||||||
|
);
|
||||||
|
|
||||||
foreach ($aDBResults as $aResult) {
|
foreach ($aDBResults as $aResult) {
|
||||||
$oResult = new Result($aResult['place_id']);
|
$oResult = new Result($aResult['place_id']);
|
||||||
@@ -757,7 +748,7 @@ class SearchDescription
|
|||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
// XXX should inherit the exactMatches from its parent
|
// XXX should inherit the exactMatches from its parent
|
||||||
foreach ($oDB->getCol($sSQL) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId);
|
$aResults[$iPlaceId] = new Result($iPlaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,7 +774,7 @@ class SearchDescription
|
|||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach ($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);
|
||||||
$oResult->iHouseNumber = $iHousenumber;
|
$oResult->iHouseNumber = $iHousenumber;
|
||||||
$aResults[$iPlaceId] = $oResult;
|
$aResults[$iPlaceId] = $oResult;
|
||||||
@@ -799,7 +790,7 @@ class SearchDescription
|
|||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach ($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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -820,7 +811,7 @@ class SearchDescription
|
|||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach ($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);
|
||||||
$oResult->iHouseNumber = $iHousenumber;
|
$oResult->iHouseNumber = $iHousenumber;
|
||||||
$aResults[$iPlaceId] = $oResult;
|
$aResults[$iPlaceId] = $oResult;
|
||||||
@@ -854,7 +845,7 @@ class SearchDescription
|
|||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach ($oDB->getCol($sSQL) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId);
|
$aResults[$iPlaceId] = new Result($iPlaceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -862,11 +853,12 @@ class SearchDescription
|
|||||||
// NEAR and IN are handled the same
|
// NEAR and IN are handled the same
|
||||||
if ($this->iOperator == Operator::TYPE || $this->iOperator == Operator::NEAR) {
|
if ($this->iOperator == Operator::TYPE || $this->iOperator == Operator::NEAR) {
|
||||||
$sClassTable = $this->poiTable();
|
$sClassTable = $this->poiTable();
|
||||||
$bCacheTable = $oDB->tableExists($sClassTable);
|
$sSQL = "SELECT count(*) FROM pg_tables WHERE tablename = '$sClassTable'";
|
||||||
|
$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)";
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$iMaxRank = (int) $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
|
||||||
$sPlaceGeom = false;
|
$sPlaceGeom = false;
|
||||||
@@ -879,7 +871,7 @@ class SearchDescription
|
|||||||
$sSQL .= ' ORDER BY rank_search ASC ';
|
$sSQL .= ' ORDER BY rank_search ASC ';
|
||||||
$sSQL .= ' LIMIT 1';
|
$sSQL .= ' LIMIT 1';
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$sPlaceGeom = $oDB->getOne($sSQL);
|
$sPlaceGeom = chksql($oDB->getOne($sSQL));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sPlaceGeom) {
|
if ($sPlaceGeom) {
|
||||||
@@ -889,7 +881,7 @@ class SearchDescription
|
|||||||
$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";
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
$aPlaceIDs = $oDB->getCol($sSQL);
|
$aPlaceIDs = chksql($oDB->getCol($sSQL));
|
||||||
$sPlaceIDs = join(',', $aPlaceIDs);
|
$sPlaceIDs = join(',', $aPlaceIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -935,7 +927,7 @@ class SearchDescription
|
|||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach ($oDB->getCol($sSQL) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId);
|
$aResults[$iPlaceId] = new Result($iPlaceId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -967,7 +959,7 @@ class SearchDescription
|
|||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
foreach ($oDB->getCol($sSQL) as $iPlaceId) {
|
foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
|
||||||
$aResults[$iPlaceId] = new Result($iPlaceId);
|
$aResults[$iPlaceId] = new Result($iPlaceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace Nominatim;
|
namespace Nominatim;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use PEAR;
|
||||||
|
|
||||||
class Status
|
class Status
|
||||||
{
|
{
|
||||||
@@ -15,18 +16,12 @@ class Status
|
|||||||
|
|
||||||
public function status()
|
public function status()
|
||||||
{
|
{
|
||||||
if (!$this->oDB) {
|
if (!$this->oDB || PEAR::isError($this->oDB)) {
|
||||||
throw new Exception('No database', 700);
|
throw new Exception('No database', 700);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
$this->oDB->connect();
|
|
||||||
} catch (\Nominatim\DatabaseError $e) {
|
|
||||||
throw new Exception('Database connection failed', 700);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sStandardWord = $this->oDB->getOne("SELECT make_standard_name('a')");
|
$sStandardWord = $this->oDB->getOne("SELECT make_standard_name('a')");
|
||||||
if ($sStandardWord === false) {
|
if (PEAR::isError($sStandardWord)) {
|
||||||
throw new Exception('Module failed', 701);
|
throw new Exception('Module failed', 701);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +32,7 @@ class Status
|
|||||||
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code, ';
|
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code, ';
|
||||||
$sSQL .= "operator, search_name_count FROM word WHERE word_token IN (' a')";
|
$sSQL .= "operator, search_name_count FROM word WHERE word_token IN (' a')";
|
||||||
$iWordID = $this->oDB->getOne($sSQL);
|
$iWordID = $this->oDB->getOne($sSQL);
|
||||||
if ($iWordID === false) {
|
if (PEAR::isError($iWordID)) {
|
||||||
throw new Exception('Query failed', 703);
|
throw new Exception('Query failed', 703);
|
||||||
}
|
}
|
||||||
if (!$iWordID) {
|
if (!$iWordID) {
|
||||||
@@ -50,7 +45,7 @@ class Status
|
|||||||
$sSQL = 'SELECT EXTRACT(EPOCH FROM lastimportdate) FROM import_status LIMIT 1';
|
$sSQL = 'SELECT EXTRACT(EPOCH FROM lastimportdate) FROM import_status LIMIT 1';
|
||||||
$iDataDateEpoch = $this->oDB->getOne($sSQL);
|
$iDataDateEpoch = $this->oDB->getOne($sSQL);
|
||||||
|
|
||||||
if ($iDataDateEpoch === false) {
|
if (PEAR::isError($iDataDateEpoch)) {
|
||||||
throw Exception('Data date query failed '.$iDataDateEpoch->getMessage(), 705);
|
throw Exception('Data date query failed '.$iDataDateEpoch->getMessage(), 705);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,18 +55,6 @@ class TokenList
|
|||||||
return isset($this->aTokens[$sWord]);
|
return isset($this->aTokens[$sWord]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there are partial or full tokens for the given 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 containsAny($sWord)
|
|
||||||
{
|
|
||||||
return isset($this->aTokens[$sWord]) || isset($this->aTokens[' '.$sWord]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of tokens for the given token word.
|
* Get the list of tokens for the given token word.
|
||||||
*
|
*
|
||||||
@@ -83,7 +71,7 @@ class TokenList
|
|||||||
/**
|
/**
|
||||||
* Add token information from the word table in the database.
|
* Add token information from the word table in the database.
|
||||||
*
|
*
|
||||||
* @param object $oDB Nominatim::DB instance.
|
* @param object $oDB Database connection.
|
||||||
* @param string[] $aTokens List of tokens to look up in the database.
|
* @param string[] $aTokens List of tokens to look up in the database.
|
||||||
* @param string[] $aCountryCodes List of country restrictions.
|
* @param string[] $aCountryCodes List of country restrictions.
|
||||||
* @param string $sNormQuery Normalized query string.
|
* @param string $sNormQuery Normalized query string.
|
||||||
@@ -97,11 +85,11 @@ class TokenList
|
|||||||
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code,';
|
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code,';
|
||||||
$sSQL .= ' operator, coalesce(search_name_count, 0) as count';
|
$sSQL .= ' operator, coalesce(search_name_count, 0) as count';
|
||||||
$sSQL .= ' FROM word WHERE word_token in (';
|
$sSQL .= ' FROM word WHERE word_token in (';
|
||||||
$sSQL .= join(',', $oDB->getDBQuotedList($aTokens)).')';
|
$sSQL .= join(',', array_map('getDBQuoted', $aTokens)).')';
|
||||||
|
|
||||||
Debug::printSQL($sSQL);
|
Debug::printSQL($sSQL);
|
||||||
|
|
||||||
$aDBWords = $oDB->getAll($sSQL, null, 'Could not get word tokens.');
|
$aDBWords = chksql($oDB->getAll($sSQL), 'Could not get word tokens.');
|
||||||
|
|
||||||
foreach ($aDBWords as $aWord) {
|
foreach ($aDBWords as $aWord) {
|
||||||
$oToken = null;
|
$oToken = null;
|
||||||
|
|||||||
11
lib/cmd.php
11
lib/cmd.php
@@ -120,6 +120,15 @@ function showUsage($aSpec, $bExit = false, $sError = false)
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function chksql($oSql, $sMsg = false)
|
||||||
|
{
|
||||||
|
if (PEAR::isError($oSql)) {
|
||||||
|
fail($sMsg || $oSql->getMessage(), $oSql->userinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oSql;
|
||||||
|
}
|
||||||
|
|
||||||
function info($sMsg)
|
function info($sMsg)
|
||||||
{
|
{
|
||||||
echo date('Y-m-d H:i:s == ').$sMsg."\n";
|
echo date('Y-m-d H:i:s == ').$sMsg."\n";
|
||||||
@@ -146,7 +155,7 @@ function repeatWarnings()
|
|||||||
function runSQLScript($sScript, $bfatal = true, $bVerbose = false, $bIgnoreErrors = false)
|
function runSQLScript($sScript, $bfatal = true, $bVerbose = false, $bIgnoreErrors = false)
|
||||||
{
|
{
|
||||||
// Convert database DSN to psql parameters
|
// Convert database DSN to psql parameters
|
||||||
$aDSNInfo = \Nominatim\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']) {
|
if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
|
||||||
|
|||||||
43
lib/db.php
Normal file
43
lib/db.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once('DB.php');
|
||||||
|
|
||||||
|
|
||||||
|
function &getDB($bNew = false, $bPersistent = false)
|
||||||
|
{
|
||||||
|
// Get the database object
|
||||||
|
$oDB = chksql(
|
||||||
|
DB::connect(CONST_Database_DSN.($bNew?'?new_link=true':''), $bPersistent),
|
||||||
|
'Failed to establish database connection'
|
||||||
|
);
|
||||||
|
$oDB->setFetchMode(DB_FETCHMODE_ASSOC);
|
||||||
|
$oDB->query("SET DateStyle TO 'sql,european'");
|
||||||
|
$oDB->query("SET client_encoding TO 'utf-8'");
|
||||||
|
$iMaxExecution = ini_get('max_execution_time') * 1000;
|
||||||
|
if ($iMaxExecution > 0) $oDB->query("SET statement_timeout TO $iMaxExecution");
|
||||||
|
return $oDB;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDBQuoted($s)
|
||||||
|
{
|
||||||
|
return "'".pg_escape_string($s)."'";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getArraySQL($a)
|
||||||
|
{
|
||||||
|
return 'ARRAY['.join(',', $a).']';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPostgresVersion(&$oDB)
|
||||||
|
{
|
||||||
|
$sVersionString = $oDB->getOne('select version()');
|
||||||
|
preg_match('#PostgreSQL ([0-9]+)[.]([0-9]+)[^0-9]#', $sVersionString, $aMatches);
|
||||||
|
return (float) ($aMatches[1].'.'.$aMatches[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPostgisVersion(&$oDB)
|
||||||
|
{
|
||||||
|
$sVersionString = $oDB->getOne('select postgis_full_version()');
|
||||||
|
preg_match('#POSTGIS="([0-9]+)[.]([0-9]+)[.]([0-9]+)( r([0-9]+))?"#', $sVersionString, $aMatches);
|
||||||
|
return (float) ($aMatches[1].'.'.$aMatches[2]);
|
||||||
|
}
|
||||||
@@ -10,83 +10,79 @@ require_once(CONST_Debug ? 'DebugHtml.php' : 'DebugNone.php');
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function userError($sMsg)
|
|
||||||
|
function chksql($oSql, $sMsg = 'Database request failed')
|
||||||
{
|
{
|
||||||
throw new Exception($sMsg, 400);
|
if (!PEAR::isError($oSql)) return $oSql;
|
||||||
}
|
|
||||||
|
|
||||||
|
header('HTTP/1.0 500 Internal Server Error');
|
||||||
|
header('Content-type: text/html; charset=utf-8');
|
||||||
|
|
||||||
function exception_handler_html($exception)
|
$sSqlError = $oSql->getMessage();
|
||||||
{
|
|
||||||
http_response_code($exception->getCode());
|
|
||||||
header('Content-type: text/html; charset=UTF-8');
|
|
||||||
include(CONST_BasePath.'/lib/template/error-html.php');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
function exception_handler_json($exception)
|
echo <<<INTERNALFAIL
|
||||||
{
|
<html>
|
||||||
http_response_code($exception->getCode());
|
<head><title>Internal Server Error</title></head>
|
||||||
header('Content-type: application/json; charset=utf-8');
|
<body>
|
||||||
include(CONST_BasePath.'/lib/template/error-json.php');
|
<h1>Internal Server Error</h1>
|
||||||
exit();
|
<p>Nominatim has encountered an internal error while accessing the database.
|
||||||
}
|
This may happen because the database is broken or because of a bug in
|
||||||
|
the software. If you think it is a bug, feel free to report
|
||||||
|
it over on <a href="https://github.com/openstreetmap/Nominatim/issues">
|
||||||
|
Github</a>. Please include the URL that caused the problem and the
|
||||||
|
complete error details below.</p>
|
||||||
|
<p><b>Message:</b> $sMsg</p>
|
||||||
|
<p><b>SQL Error:</b> $sSqlError</p>
|
||||||
|
<p><b>Details:</b> <pre>
|
||||||
|
INTERNALFAIL;
|
||||||
|
|
||||||
function exception_handler_xml($exception)
|
if (CONST_Debug) {
|
||||||
{
|
var_dump($oSql);
|
||||||
http_response_code($exception->getCode());
|
|
||||||
header('Content-type: text/xml; charset=utf-8');
|
|
||||||
echo '<?xml version="1.0" encoding="UTF-8" ?>'."\n";
|
|
||||||
include(CONST_BasePath.'/lib/template/error-xml.php');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
function shutdown_exception_handler_html()
|
|
||||||
{
|
|
||||||
$error = error_get_last();
|
|
||||||
if ($error !== null && $error['type'] === E_ERROR) {
|
|
||||||
exception_handler_html(new Exception($error['message'], 500));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function shutdown_exception_handler_xml()
|
|
||||||
{
|
|
||||||
$error = error_get_last();
|
|
||||||
if ($error !== null && $error['type'] === E_ERROR) {
|
|
||||||
exception_handler_xml(new Exception($error['message'], 500));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function shutdown_exception_handler_json()
|
|
||||||
{
|
|
||||||
$error = error_get_last();
|
|
||||||
if ($error !== null && $error['type'] === E_ERROR) {
|
|
||||||
exception_handler_json(new Exception($error['message'], 500));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function set_exception_handler_by_format($sFormat = null)
|
|
||||||
{
|
|
||||||
// Multiple calls to register_shutdown_function will cause multiple callbacks
|
|
||||||
// to be executed, we only want the last executed. Thus we don't want to register
|
|
||||||
// one by default without an explicit $sFormat set.
|
|
||||||
|
|
||||||
if (!isset($sFormat)) {
|
|
||||||
set_exception_handler('exception_handler_html');
|
|
||||||
} elseif ($sFormat == 'html') {
|
|
||||||
set_exception_handler('exception_handler_html');
|
|
||||||
register_shutdown_function('shutdown_exception_handler_html');
|
|
||||||
} elseif ($sFormat == 'xml') {
|
|
||||||
set_exception_handler('exception_handler_xml');
|
|
||||||
register_shutdown_function('shutdown_exception_handler_xml');
|
|
||||||
} else {
|
} else {
|
||||||
set_exception_handler('exception_handler_json');
|
echo "<pre>\n".$oSql->getUserInfo().'</pre>';
|
||||||
register_shutdown_function('shutdown_exception_handler_json');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
echo '</pre></p></body></html>';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function failInternalError($sError, $sSQL = false, $vDumpVar = false)
|
||||||
|
{
|
||||||
|
header('HTTP/1.0 500 Internal Server Error');
|
||||||
|
header('Content-type: text/html; charset=utf-8');
|
||||||
|
echo '<html><body><h1>Internal Server Error</h1>';
|
||||||
|
echo '<p>Nominatim has encountered an internal error while processing your request. This is most likely because of a bug in the software.</p>';
|
||||||
|
echo '<p><b>Details:</b> '.$sError,'</p>';
|
||||||
|
echo '<p>Feel free to file an issue on <a href="https://github.com/openstreetmap/Nominatim/issues">Github</a>. ';
|
||||||
|
echo 'Please include the error message above and the URL you used.</p>';
|
||||||
|
if (CONST_Debug) {
|
||||||
|
echo '<hr><h2>Debugging Information</h2><br>';
|
||||||
|
if ($sSQL) {
|
||||||
|
echo '<h3>SQL query</h3><code>'.$sSQL.'</code>';
|
||||||
|
}
|
||||||
|
if ($vDumpVar) {
|
||||||
|
echo '<h3>Result</h3> <code>';
|
||||||
|
var_dump($vDumpVar);
|
||||||
|
echo '</code>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "\n</body></html>\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function userError($sError)
|
||||||
|
{
|
||||||
|
header('HTTP/1.0 400 Bad Request');
|
||||||
|
header('Content-type: text/html; charset=utf-8');
|
||||||
|
echo '<html><body><h1>Bad Request</h1>';
|
||||||
|
echo '<p>Nominatim has encountered an error with your request.</p>';
|
||||||
|
echo '<p><b>Details:</b> '.$sError.'</p>';
|
||||||
|
echo '<p>If you feel this error is incorrect feel file an issue on <a href="https://github.com/openstreetmap/Nominatim/issues">Github</a>. ';
|
||||||
|
echo 'Please include the error message above and the URL you used.</p>';
|
||||||
|
echo "\n</body></html>\n";
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
// set a default
|
|
||||||
set_exception_handler_by_format();
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
@@ -100,6 +96,6 @@ if (CONST_NoAccessControl) {
|
|||||||
header('Access-Control-Allow-Headers: '.$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
header('Access-Control-Allow-Headers: '.$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') exit;
|
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') exit;
|
||||||
|
|
||||||
if (CONST_Debug) header('Content-type: text/html; charset=utf-8');
|
if (CONST_Debug) header('Content-type: text/html; charset=utf-8');
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/lib.php');
|
require_once(CONST_BasePath.'/lib/lib.php');
|
||||||
require_once(CONST_BasePath.'/lib/DB.php');
|
require_once(CONST_BasePath.'/lib/db.php');
|
||||||
|
|
||||||
if (get_magic_quotes_gpc()) {
|
if (get_magic_quotes_gpc()) {
|
||||||
echo "Please disable magic quotes in your php.ini configuration\n";
|
echo "Please disable magic quotes in your php.ini configuration\n";
|
||||||
|
|||||||
43
lib/lib.php
43
lib/lib.php
@@ -4,7 +4,7 @@ function fail($sError, $sUserError = false)
|
|||||||
{
|
{
|
||||||
if (!$sUserError) $sUserError = $sError;
|
if (!$sUserError) $sUserError = $sError;
|
||||||
error_log('ERROR: '.$sError);
|
error_log('ERROR: '.$sError);
|
||||||
var_dump($sUserError)."\n";
|
echo $sUserError."\n";
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,26 +61,23 @@ function byImportance($a, $b)
|
|||||||
|
|
||||||
function javascript_renderData($xVal, $iOptions = 0)
|
function javascript_renderData($xVal, $iOptions = 0)
|
||||||
{
|
{
|
||||||
$sCallback = isset($_GET['json_callback']) ? $_GET['json_callback'] : '';
|
$iOptions |= JSON_UNESCAPED_UNICODE;
|
||||||
if ($sCallback && !preg_match('/^[$_\p{L}][$_\p{L}\p{Nd}.[\]]*$/u', $sCallback)) {
|
|
||||||
// Unset, we call javascript_renderData again during exception handling
|
|
||||||
unset($_GET['json_callback']);
|
|
||||||
throw new Exception('Invalid json_callback value', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$iOptions |= JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
|
|
||||||
if (isset($_GET['pretty']) && in_array(strtolower($_GET['pretty']), array('1', 'true'))) {
|
if (isset($_GET['pretty']) && in_array(strtolower($_GET['pretty']), array('1', 'true'))) {
|
||||||
$iOptions |= JSON_PRETTY_PRINT;
|
$iOptions |= JSON_PRETTY_PRINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
$jsonout = json_encode($xVal, $iOptions);
|
$jsonout = json_encode($xVal, $iOptions);
|
||||||
|
|
||||||
if ($sCallback) {
|
if (!isset($_GET['json_callback'])) {
|
||||||
|
header('Content-Type: application/json; charset=UTF-8');
|
||||||
|
echo $jsonout;
|
||||||
|
} else {
|
||||||
|
if (preg_match('/^[$_\p{L}][$_\p{L}\p{Nd}.[\]]*$/u', $_GET['json_callback'])) {
|
||||||
header('Content-Type: application/javascript; charset=UTF-8');
|
header('Content-Type: application/javascript; charset=UTF-8');
|
||||||
echo $_GET['json_callback'].'('.$jsonout.')';
|
echo $_GET['json_callback'].'('.$jsonout.')';
|
||||||
} else {
|
} else {
|
||||||
header('Content-Type: application/json; charset=UTF-8');
|
header('HTTP/1.0 400 Bad Request');
|
||||||
echo $jsonout;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,25 +225,3 @@ function closestHouseNumber($aRow)
|
|||||||
|
|
||||||
return max(min($aRow['endnumber'], $iHn), $aRow['startnumber']);
|
return max(min($aRow['endnumber'], $iHn), $aRow['startnumber']);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSearchRankLabel($iRank)
|
|
||||||
{
|
|
||||||
if (!isset($iRank)) return 'unknown';
|
|
||||||
if ($iRank < 2) return 'continent';
|
|
||||||
if ($iRank < 4) return 'sea';
|
|
||||||
if ($iRank < 8) return 'country';
|
|
||||||
if ($iRank < 12) return 'state';
|
|
||||||
if ($iRank < 16) return 'county';
|
|
||||||
if ($iRank == 16) return 'city';
|
|
||||||
if ($iRank == 17) return 'town / island';
|
|
||||||
if ($iRank == 18) return 'village / hamlet';
|
|
||||||
if ($iRank == 20) return 'suburb';
|
|
||||||
if ($iRank == 21) return 'postcode area';
|
|
||||||
if ($iRank == 22) return 'croft / farm / locality / islet';
|
|
||||||
if ($iRank == 23) return 'postcode area';
|
|
||||||
if ($iRank == 25) return 'postcode point';
|
|
||||||
if ($iRank == 26) return 'street / major landmark';
|
|
||||||
if ($iRank == 27) return 'minory street / path';
|
|
||||||
if ($iRank == 28) return 'house / building';
|
|
||||||
return 'other: ' . $iRank;
|
|
||||||
}
|
|
||||||
|
|||||||
26
lib/log.php
26
lib/log.php
@@ -36,19 +36,9 @@ function logStart(&$oDB, $sType = '', $sQuery = '', $aLanguageList = array())
|
|||||||
$sUserAgent = $_SERVER['HTTP_USER_AGENT'];
|
$sUserAgent = $_SERVER['HTTP_USER_AGENT'];
|
||||||
else $sUserAgent = '';
|
else $sUserAgent = '';
|
||||||
$sSQL = 'insert into new_query_log (type,starttime,query,ipaddress,useragent,language,format,searchterm)';
|
$sSQL = 'insert into new_query_log (type,starttime,query,ipaddress,useragent,language,format,searchterm)';
|
||||||
$sSQL .= ' values (';
|
$sSQL .= ' values ('.getDBQuoted($sType).','.getDBQuoted($hLog[0]).','.getDBQuoted($hLog[2]);
|
||||||
$sSQL .= join(',', $oDB->getDBQuotedList(array(
|
$sSQL .= ','.getDBQuoted($hLog[1]).','.getDBQuoted($sUserAgent).','.getDBQuoted(join(',', $aLanguageList)).','.getDBQuoted($sOutputFormat).','.getDBQuoted($hLog[3]).')';
|
||||||
$sType,
|
$oDB->query($sSQL);
|
||||||
$hLog[0],
|
|
||||||
$hLog[2],
|
|
||||||
$hLog[1],
|
|
||||||
$sUserAgent,
|
|
||||||
join(',', $aLanguageList),
|
|
||||||
$sOutputFormat,
|
|
||||||
$hLog[3]
|
|
||||||
)));
|
|
||||||
$sSQL .= ')';
|
|
||||||
$oDB->exec($sSQL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $hLog;
|
return $hLog;
|
||||||
@@ -63,11 +53,11 @@ function logEnd(&$oDB, $hLog, $iNumResults)
|
|||||||
if (!$aEndTime[1]) $aEndTime[1] = '0';
|
if (!$aEndTime[1]) $aEndTime[1] = '0';
|
||||||
$sEndTime = date('Y-m-d H:i:s', $aEndTime[0]).'.'.$aEndTime[1];
|
$sEndTime = date('Y-m-d H:i:s', $aEndTime[0]).'.'.$aEndTime[1];
|
||||||
|
|
||||||
$sSQL = 'update new_query_log set endtime = '.$oDB->getDBQuoted($sEndTime).', results = '.$iNumResults;
|
$sSQL = 'update new_query_log set endtime = '.getDBQuoted($sEndTime).', results = '.$iNumResults;
|
||||||
$sSQL .= ' where starttime = '.$oDB->getDBQuoted($hLog[0]);
|
$sSQL .= ' where starttime = '.getDBQuoted($hLog[0]);
|
||||||
$sSQL .= ' and ipaddress = '.$oDB->getDBQuoted($hLog[1]);
|
$sSQL .= ' and ipaddress = '.getDBQuoted($hLog[1]);
|
||||||
$sSQL .= ' and query = '.$oDB->getDBQuoted($hLog[2]);
|
$sSQL .= ' and query = '.getDBQuoted($hLog[2]);
|
||||||
$oDB->exec($sSQL);
|
$oDB->query($sSQL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONST_Log_File) {
|
if (CONST_Log_File) {
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim\Setup;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an address level description.
|
|
||||||
*/
|
|
||||||
class AddressLevelParser
|
|
||||||
{
|
|
||||||
private $aLevels;
|
|
||||||
|
|
||||||
public function __construct($sDescriptionFile)
|
|
||||||
{
|
|
||||||
$sJson = file_get_contents($sDescriptionFile);
|
|
||||||
$this->aLevels = json_decode($sJson, true);
|
|
||||||
if (!$this->aLevels) {
|
|
||||||
switch (json_last_error()) {
|
|
||||||
case JSON_ERROR_NONE:
|
|
||||||
break;
|
|
||||||
case JSON_ERROR_DEPTH:
|
|
||||||
fail('JSON error - Maximum stack depth exceeded');
|
|
||||||
break;
|
|
||||||
case JSON_ERROR_STATE_MISMATCH:
|
|
||||||
fail('JSON error - Underflow or the modes mismatch');
|
|
||||||
break;
|
|
||||||
case JSON_ERROR_CTRL_CHAR:
|
|
||||||
fail('JSON error - Unexpected control character found');
|
|
||||||
break;
|
|
||||||
case JSON_ERROR_SYNTAX:
|
|
||||||
fail('JSON error - Syntax error, malformed JSON');
|
|
||||||
break;
|
|
||||||
case JSON_ERROR_UTF8:
|
|
||||||
fail('JSON error - Malformed UTF-8 characters, possibly incorrectly encoded');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fail('JSON error - Unknown error');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dump the description into a database table.
|
|
||||||
*
|
|
||||||
* @param object $oDB Database conneciton to use.
|
|
||||||
* @param string $sTable Name of table to create.
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*
|
|
||||||
* A new table is created. Any previously existing table is dropped.
|
|
||||||
* The table has the following columns:
|
|
||||||
* country, class, type, rank_search, rank_address.
|
|
||||||
*/
|
|
||||||
public function createTable($oDB, $sTable)
|
|
||||||
{
|
|
||||||
$oDB->exec('DROP TABLE IF EXISTS '.$sTable);
|
|
||||||
$sSql = 'CREATE TABLE '.$sTable;
|
|
||||||
$sSql .= '(country_code varchar(2), class TEXT, type TEXT,';
|
|
||||||
$sSql .= ' rank_search SMALLINT, rank_address SMALLINT)';
|
|
||||||
$oDB->exec($sSql);
|
|
||||||
|
|
||||||
$sSql = 'CREATE UNIQUE INDEX ON '.$sTable.' (country_code, class, type)';
|
|
||||||
$oDB->exec($sSql);
|
|
||||||
|
|
||||||
$sSql = 'INSERT INTO '.$sTable.' VALUES ';
|
|
||||||
foreach ($this->aLevels as $aLevel) {
|
|
||||||
$aCountries = array();
|
|
||||||
if (isset($aLevel['countries'])) {
|
|
||||||
foreach ($aLevel['countries'] as $sCountry) {
|
|
||||||
$aCountries[$sCountry] = $oDB->getDBQuoted($sCountry);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$aCountries['NULL'] = 'NULL';
|
|
||||||
}
|
|
||||||
foreach ($aLevel['tags'] as $sKey => $aValues) {
|
|
||||||
foreach ($aValues as $sValue => $mRanks) {
|
|
||||||
$aFields = array(
|
|
||||||
$oDB->getDBQuoted($sKey),
|
|
||||||
$sValue ? $oDB->getDBQuoted($sValue) : 'NULL'
|
|
||||||
);
|
|
||||||
if (is_array($mRanks)) {
|
|
||||||
$aFields[] = (string) $mRanks[0];
|
|
||||||
$aFields[] = (string) $mRanks[1];
|
|
||||||
} else {
|
|
||||||
$aFields[] = (string) $mRanks;
|
|
||||||
$aFields[] = (string) $mRanks;
|
|
||||||
}
|
|
||||||
$sLine = ','.join(',', $aFields).'),';
|
|
||||||
|
|
||||||
foreach ($aCountries as $sCountries) {
|
|
||||||
$sSql .= '('.$sCountries.$sLine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$oDB->exec(rtrim($sSql, ','));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,913 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim\Setup;
|
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/setup/AddressLevelParser.php');
|
|
||||||
|
|
||||||
class SetupFunctions
|
|
||||||
{
|
|
||||||
protected $iCacheMemory;
|
|
||||||
protected $iInstances;
|
|
||||||
protected $sModulePath;
|
|
||||||
protected $aDSNInfo;
|
|
||||||
protected $bVerbose;
|
|
||||||
protected $sIgnoreErrors;
|
|
||||||
protected $bEnableDiffUpdates;
|
|
||||||
protected $bEnableDebugStatements;
|
|
||||||
protected $bNoPartitions;
|
|
||||||
protected $oDB = null;
|
|
||||||
|
|
||||||
public function __construct(array $aCMDResult)
|
|
||||||
{
|
|
||||||
// by default, use all but one processor, but never more than 15.
|
|
||||||
$this->iInstances = isset($aCMDResult['threads'])
|
|
||||||
? $aCMDResult['threads']
|
|
||||||
: (min(16, getProcessorCount()) - 1);
|
|
||||||
|
|
||||||
if ($this->iInstances < 1) {
|
|
||||||
$this->iInstances = 1;
|
|
||||||
warn('resetting threads to '.$this->iInstances);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assume we can steal all the cache memory in the box (unless told otherwise)
|
|
||||||
if (isset($aCMDResult['osm2pgsql-cache'])) {
|
|
||||||
$this->iCacheMemory = $aCMDResult['osm2pgsql-cache'];
|
|
||||||
} else {
|
|
||||||
$this->iCacheMemory = getCacheMemoryMB();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->sModulePath = CONST_Database_Module_Path;
|
|
||||||
info('module path: ' . $this->sModulePath);
|
|
||||||
|
|
||||||
// parse database string
|
|
||||||
$this->aDSNInfo = \Nominatim\DB::parseDSN(CONST_Database_DSN);
|
|
||||||
if (!isset($this->aDSNInfo['port'])) {
|
|
||||||
$this->aDSNInfo['port'] = 5432;
|
|
||||||
}
|
|
||||||
|
|
||||||
// setting member variables based on command line options stored in $aCMDResult
|
|
||||||
$this->bVerbose = $aCMDResult['verbose'];
|
|
||||||
|
|
||||||
//setting default values which are not set by the update.php array
|
|
||||||
if (isset($aCMDResult['ignore-errors'])) {
|
|
||||||
$this->sIgnoreErrors = $aCMDResult['ignore-errors'];
|
|
||||||
} else {
|
|
||||||
$this->sIgnoreErrors = false;
|
|
||||||
}
|
|
||||||
if (isset($aCMDResult['enable-debug-statements'])) {
|
|
||||||
$this->bEnableDebugStatements = $aCMDResult['enable-debug-statements'];
|
|
||||||
} else {
|
|
||||||
$this->bEnableDebugStatements = false;
|
|
||||||
}
|
|
||||||
if (isset($aCMDResult['no-partitions'])) {
|
|
||||||
$this->bNoPartitions = $aCMDResult['no-partitions'];
|
|
||||||
} else {
|
|
||||||
$this->bNoPartitions = false;
|
|
||||||
}
|
|
||||||
if (isset($aCMDResult['enable-diff-updates'])) {
|
|
||||||
$this->bEnableDiffUpdates = $aCMDResult['enable-diff-updates'];
|
|
||||||
} else {
|
|
||||||
$this->bEnableDiffUpdates = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createDB()
|
|
||||||
{
|
|
||||||
info('Create DB');
|
|
||||||
$oDB = new \Nominatim\DB;
|
|
||||||
|
|
||||||
if ($oDB->databaseExists()) {
|
|
||||||
fail('database already exists ('.CONST_Database_DSN.')');
|
|
||||||
}
|
|
||||||
|
|
||||||
$sCreateDBCmd = 'createdb -E UTF-8 -p '.$this->aDSNInfo['port'].' '.$this->aDSNInfo['database'];
|
|
||||||
if (isset($this->aDSNInfo['username'])) {
|
|
||||||
$sCreateDBCmd .= ' -U '.$this->aDSNInfo['username'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->aDSNInfo['hostspec'])) {
|
|
||||||
$sCreateDBCmd .= ' -h '.$this->aDSNInfo['hostspec'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = $this->runWithPgEnv($sCreateDBCmd);
|
|
||||||
if ($result != 0) fail('Error executing external command: '.$sCreateDBCmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function connect()
|
|
||||||
{
|
|
||||||
$this->oDB = new \Nominatim\DB();
|
|
||||||
$this->oDB->connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setupDB()
|
|
||||||
{
|
|
||||||
info('Setup DB');
|
|
||||||
|
|
||||||
$fPostgresVersion = $this->oDB->getPostgresVersion();
|
|
||||||
echo 'Postgres version found: '.$fPostgresVersion."\n";
|
|
||||||
|
|
||||||
if ($fPostgresVersion < 9.03) {
|
|
||||||
fail('Minimum supported version of Postgresql is 9.3.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS hstore');
|
|
||||||
$this->pgsqlRunScript('CREATE EXTENSION IF NOT EXISTS postgis');
|
|
||||||
|
|
||||||
$fPostgisVersion = $this->oDB->getPostgisVersion();
|
|
||||||
echo 'Postgis version found: '.$fPostgisVersion."\n";
|
|
||||||
|
|
||||||
if ($fPostgisVersion < 2.2) {
|
|
||||||
echo "Minimum required Postgis version 2.2\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$i = $this->oDB->getOne("select count(*) from pg_user where usename = '".CONST_Database_Web_User."'");
|
|
||||||
if ($i == 0) {
|
|
||||||
echo "\nERROR: Web user '".CONST_Database_Web_User."' does not exist. Create it with:\n";
|
|
||||||
echo "\n createuser ".CONST_Database_Web_User."\n\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try accessing the C module, so we know early if something is wrong
|
|
||||||
checkModulePresence(); // raises exception on failure
|
|
||||||
|
|
||||||
if (!file_exists(CONST_ExtraDataPath.'/country_osm_grid.sql.gz')) {
|
|
||||||
echo 'Error: you need to download the country_osm_grid first:';
|
|
||||||
echo "\n wget -O ".CONST_ExtraDataPath."/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
|
|
||||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
|
|
||||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
|
|
||||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode_table.sql');
|
|
||||||
|
|
||||||
$sPostcodeFilename = CONST_BasePath.'/data/gb_postcode_data.sql.gz';
|
|
||||||
if (file_exists($sPostcodeFilename)) {
|
|
||||||
$this->pgsqlRunScriptFile($sPostcodeFilename);
|
|
||||||
} else {
|
|
||||||
warn('optional external GB postcode table file ('.$sPostcodeFilename.') not found. Skipping.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$sPostcodeFilename = CONST_BasePath.'/data/us_postcode_data.sql.gz';
|
|
||||||
if (file_exists($sPostcodeFilename)) {
|
|
||||||
$this->pgsqlRunScriptFile($sPostcodeFilename);
|
|
||||||
} else {
|
|
||||||
warn('optional external US postcode table file ('.$sPostcodeFilename.') not found. Skipping.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->bNoPartitions) {
|
|
||||||
$this->pgsqlRunScript('update country_name set partition = 0');
|
|
||||||
}
|
|
||||||
|
|
||||||
// the following will be needed by createFunctions later but
|
|
||||||
// is only defined in the subsequently called createTables
|
|
||||||
// Create dummies here that will be overwritten by the proper
|
|
||||||
// versions in create-tables.
|
|
||||||
$this->pgsqlRunScript('CREATE TABLE IF NOT EXISTS place_boundingbox ()');
|
|
||||||
$this->pgsqlRunScript('CREATE TYPE wikipedia_article_match AS ()', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function importData($sOSMFile)
|
|
||||||
{
|
|
||||||
info('Import data');
|
|
||||||
|
|
||||||
$osm2pgsql = CONST_Osm2pgsql_Binary;
|
|
||||||
if (!file_exists($osm2pgsql)) {
|
|
||||||
echo "Check CONST_Osm2pgsql_Binary in your local settings file.\n";
|
|
||||||
echo "Normally you should not need to set this manually.\n";
|
|
||||||
fail("osm2pgsql not found in '$osm2pgsql'");
|
|
||||||
}
|
|
||||||
|
|
||||||
$osm2pgsql .= ' -S '.CONST_Import_Style;
|
|
||||||
|
|
||||||
if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
|
|
||||||
$osm2pgsql .= ' --flat-nodes '.CONST_Osm2pgsql_Flatnode_File;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CONST_Tablespace_Osm2pgsql_Data)
|
|
||||||
$osm2pgsql .= ' --tablespace-slim-data '.CONST_Tablespace_Osm2pgsql_Data;
|
|
||||||
if (CONST_Tablespace_Osm2pgsql_Index)
|
|
||||||
$osm2pgsql .= ' --tablespace-slim-index '.CONST_Tablespace_Osm2pgsql_Index;
|
|
||||||
if (CONST_Tablespace_Place_Data)
|
|
||||||
$osm2pgsql .= ' --tablespace-main-data '.CONST_Tablespace_Place_Data;
|
|
||||||
if (CONST_Tablespace_Place_Index)
|
|
||||||
$osm2pgsql .= ' --tablespace-main-index '.CONST_Tablespace_Place_Index;
|
|
||||||
$osm2pgsql .= ' -lsc -O gazetteer --hstore --number-processes 1';
|
|
||||||
$osm2pgsql .= ' -C '.$this->iCacheMemory;
|
|
||||||
$osm2pgsql .= ' -P '.$this->aDSNInfo['port'];
|
|
||||||
if (isset($this->aDSNInfo['username'])) {
|
|
||||||
$osm2pgsql .= ' -U '.$this->aDSNInfo['username'];
|
|
||||||
}
|
|
||||||
if (isset($this->aDSNInfo['hostspec'])) {
|
|
||||||
$osm2pgsql .= ' -H '.$this->aDSNInfo['hostspec'];
|
|
||||||
}
|
|
||||||
$osm2pgsql .= ' -d '.$this->aDSNInfo['database'].' '.$sOSMFile;
|
|
||||||
|
|
||||||
$this->runWithPgEnv($osm2pgsql);
|
|
||||||
|
|
||||||
if (!$this->sIgnoreErrors && !$this->oDB->getRow('select * from place limit 1')) {
|
|
||||||
fail('No Data');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createFunctions()
|
|
||||||
{
|
|
||||||
info('Create Functions');
|
|
||||||
|
|
||||||
// Try accessing the C module, so we know early if something is wrong
|
|
||||||
checkModulePresence(); // raises exception on failure
|
|
||||||
|
|
||||||
$this->createSqlFunctions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createTables($bReverseOnly = false)
|
|
||||||
{
|
|
||||||
info('Create Tables');
|
|
||||||
|
|
||||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/tables.sql');
|
|
||||||
$sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:address-data}',
|
|
||||||
CONST_Tablespace_Address_Data,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:address-index}',
|
|
||||||
CONST_Tablespace_Address_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:search-data}',
|
|
||||||
CONST_Tablespace_Search_Data,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:search-index}',
|
|
||||||
CONST_Tablespace_Search_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:aux-data}',
|
|
||||||
CONST_Tablespace_Aux_Data,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:aux-index}',
|
|
||||||
CONST_Tablespace_Aux_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->pgsqlRunScript($sTemplate, false);
|
|
||||||
|
|
||||||
if ($bReverseOnly) {
|
|
||||||
$this->pgExec('DROP TABLE search_name');
|
|
||||||
}
|
|
||||||
|
|
||||||
$oAlParser = new AddressLevelParser(CONST_Address_Level_Config);
|
|
||||||
$oAlParser->createTable($this->oDB, 'address_levels');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createPartitionTables()
|
|
||||||
{
|
|
||||||
info('Create Partition Tables');
|
|
||||||
|
|
||||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-tables.src.sql');
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:address-data}',
|
|
||||||
CONST_Tablespace_Address_Data,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:address-index}',
|
|
||||||
CONST_Tablespace_Address_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:search-data}',
|
|
||||||
CONST_Tablespace_Search_Data,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:search-index}',
|
|
||||||
CONST_Tablespace_Search_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:aux-data}',
|
|
||||||
CONST_Tablespace_Aux_Data,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:aux-index}',
|
|
||||||
CONST_Tablespace_Aux_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->pgsqlRunPartitionScript($sTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createPartitionFunctions()
|
|
||||||
{
|
|
||||||
info('Create Partition Functions');
|
|
||||||
|
|
||||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/partition-functions.src.sql');
|
|
||||||
$this->pgsqlRunPartitionScript($sTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function importWikipediaArticles()
|
|
||||||
{
|
|
||||||
$sWikiArticlesFile = CONST_Wikipedia_Data_Path.'/wikipedia_article.sql.bin';
|
|
||||||
$sWikiRedirectsFile = CONST_Wikipedia_Data_Path.'/wikipedia_redirect.sql.bin';
|
|
||||||
if (file_exists($sWikiArticlesFile)) {
|
|
||||||
info('Importing wikipedia articles');
|
|
||||||
$this->pgsqlRunDropAndRestore($sWikiArticlesFile);
|
|
||||||
} else {
|
|
||||||
warn('wikipedia article dump file not found - places will have default importance');
|
|
||||||
}
|
|
||||||
if (file_exists($sWikiRedirectsFile)) {
|
|
||||||
info('Importing wikipedia redirects');
|
|
||||||
$this->pgsqlRunDropAndRestore($sWikiRedirectsFile);
|
|
||||||
} else {
|
|
||||||
warn('wikipedia redirect dump file not found - some place importance values may be missing');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadData($bDisableTokenPrecalc)
|
|
||||||
{
|
|
||||||
info('Drop old Data');
|
|
||||||
|
|
||||||
$this->pgExec('TRUNCATE word');
|
|
||||||
echo '.';
|
|
||||||
$this->pgExec('TRUNCATE placex');
|
|
||||||
echo '.';
|
|
||||||
$this->pgExec('TRUNCATE location_property_osmline');
|
|
||||||
echo '.';
|
|
||||||
$this->pgExec('TRUNCATE place_addressline');
|
|
||||||
echo '.';
|
|
||||||
$this->pgExec('TRUNCATE place_boundingbox');
|
|
||||||
echo '.';
|
|
||||||
$this->pgExec('TRUNCATE location_area');
|
|
||||||
echo '.';
|
|
||||||
if (!$this->dbReverseOnly()) {
|
|
||||||
$this->pgExec('TRUNCATE search_name');
|
|
||||||
echo '.';
|
|
||||||
}
|
|
||||||
$this->pgExec('TRUNCATE search_name_blank');
|
|
||||||
echo '.';
|
|
||||||
$this->pgExec('DROP SEQUENCE seq_place');
|
|
||||||
echo '.';
|
|
||||||
$this->pgExec('CREATE SEQUENCE seq_place start 100000');
|
|
||||||
echo '.';
|
|
||||||
|
|
||||||
$sSQL = 'select distinct partition from country_name';
|
|
||||||
$aPartitions = $this->oDB->getCol($sSQL);
|
|
||||||
|
|
||||||
if (!$this->bNoPartitions) $aPartitions[] = 0;
|
|
||||||
foreach ($aPartitions as $sPartition) {
|
|
||||||
$this->pgExec('TRUNCATE location_road_'.$sPartition);
|
|
||||||
echo '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// used by getorcreate_word_id to ignore frequent partial words
|
|
||||||
$sSQL = 'CREATE OR REPLACE FUNCTION get_maxwordfreq() RETURNS integer AS ';
|
|
||||||
$sSQL .= '$$ SELECT '.CONST_Max_Word_Frequency.' as maxwordfreq; $$ LANGUAGE SQL IMMUTABLE';
|
|
||||||
$this->pgExec($sSQL);
|
|
||||||
echo ".\n";
|
|
||||||
|
|
||||||
// pre-create the word list
|
|
||||||
if (!$bDisableTokenPrecalc) {
|
|
||||||
info('Loading word list');
|
|
||||||
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/words.sql');
|
|
||||||
}
|
|
||||||
|
|
||||||
info('Load Data');
|
|
||||||
$sColumns = 'osm_type, osm_id, class, type, name, admin_level, address, extratags, geometry';
|
|
||||||
|
|
||||||
$aDBInstances = array();
|
|
||||||
$iLoadThreads = max(1, $this->iInstances - 1);
|
|
||||||
for ($i = 0; $i < $iLoadThreads; $i++) {
|
|
||||||
// https://secure.php.net/manual/en/function.pg-connect.php
|
|
||||||
$DSN = CONST_Database_DSN;
|
|
||||||
$DSN = preg_replace('/^pgsql:/', '', $DSN);
|
|
||||||
$DSN = preg_replace('/;/', ' ', $DSN);
|
|
||||||
$aDBInstances[$i] = pg_connect($DSN, PGSQL_CONNECT_FORCE_NEW);
|
|
||||||
pg_ping($aDBInstances[$i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($i = 0; $i < $iLoadThreads; $i++) {
|
|
||||||
$sSQL = "INSERT INTO placex ($sColumns) SELECT $sColumns FROM place WHERE osm_id % $iLoadThreads = $i";
|
|
||||||
$sSQL .= " and not (class='place' and type='houses' and osm_type='W'";
|
|
||||||
$sSQL .= " and ST_GeometryType(geometry) = 'ST_LineString')";
|
|
||||||
$sSQL .= ' and ST_IsValid(geometry)';
|
|
||||||
if ($this->bVerbose) echo "$sSQL\n";
|
|
||||||
if (!pg_send_query($aDBInstances[$i], $sSQL)) {
|
|
||||||
fail(pg_last_error($aDBInstances[$i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// last thread for interpolation lines
|
|
||||||
// https://secure.php.net/manual/en/function.pg-connect.php
|
|
||||||
$DSN = CONST_Database_DSN;
|
|
||||||
$DSN = preg_replace('/^pgsql:/', '', $DSN);
|
|
||||||
$DSN = preg_replace('/;/', ' ', $DSN);
|
|
||||||
$aDBInstances[$iLoadThreads] = pg_connect($DSN, PGSQL_CONNECT_FORCE_NEW);
|
|
||||||
pg_ping($aDBInstances[$iLoadThreads]);
|
|
||||||
$sSQL = 'insert into location_property_osmline';
|
|
||||||
$sSQL .= ' (osm_id, address, linegeo)';
|
|
||||||
$sSQL .= ' SELECT osm_id, address, geometry from place where ';
|
|
||||||
$sSQL .= "class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'";
|
|
||||||
if ($this->bVerbose) echo "$sSQL\n";
|
|
||||||
if (!pg_send_query($aDBInstances[$iLoadThreads], $sSQL)) {
|
|
||||||
fail(pg_last_error($aDBInstances[$iLoadThreads]));
|
|
||||||
}
|
|
||||||
|
|
||||||
$bFailed = false;
|
|
||||||
for ($i = 0; $i <= $iLoadThreads; $i++) {
|
|
||||||
while (($hPGresult = pg_get_result($aDBInstances[$i])) !== false) {
|
|
||||||
$resultStatus = pg_result_status($hPGresult);
|
|
||||||
// PGSQL_EMPTY_QUERY, PGSQL_COMMAND_OK, PGSQL_TUPLES_OK,
|
|
||||||
// PGSQL_COPY_OUT, PGSQL_COPY_IN, PGSQL_BAD_RESPONSE,
|
|
||||||
// PGSQL_NONFATAL_ERROR and PGSQL_FATAL_ERROR
|
|
||||||
// echo 'Query result ' . $i . ' is: ' . $resultStatus . "\n";
|
|
||||||
if ($resultStatus != PGSQL_COMMAND_OK && $resultStatus != PGSQL_TUPLES_OK) {
|
|
||||||
$resultError = pg_result_error($hPGresult);
|
|
||||||
echo '-- error text ' . $i . ': ' . $resultError . "\n";
|
|
||||||
$bFailed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($bFailed) {
|
|
||||||
fail('SQL errors loading placex and/or location_property_osmline tables');
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
|
||||||
pg_close($aDBInstances[$i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "\n";
|
|
||||||
info('Reanalysing database');
|
|
||||||
$this->pgsqlRunScript('ANALYSE');
|
|
||||||
|
|
||||||
$sDatabaseDate = getDatabaseDate($this->oDB);
|
|
||||||
$this->oDB->exec('TRUNCATE import_status');
|
|
||||||
if (!$sDatabaseDate) {
|
|
||||||
warn('could not determine database date.');
|
|
||||||
} else {
|
|
||||||
$sSQL = "INSERT INTO import_status (lastimportdate) VALUES('".$sDatabaseDate."')";
|
|
||||||
$this->oDB->exec($sSQL);
|
|
||||||
echo "Latest data imported from $sDatabaseDate.\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function importTigerData()
|
|
||||||
{
|
|
||||||
info('Import Tiger data');
|
|
||||||
|
|
||||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
|
|
||||||
$sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:aux-data}',
|
|
||||||
CONST_Tablespace_Aux_Data,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:aux-index}',
|
|
||||||
CONST_Tablespace_Aux_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$this->pgsqlRunScript($sTemplate, false);
|
|
||||||
|
|
||||||
$aDBInstances = array();
|
|
||||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
|
||||||
// https://secure.php.net/manual/en/function.pg-connect.php
|
|
||||||
$DSN = CONST_Database_DSN;
|
|
||||||
$DSN = preg_replace('/^pgsql:/', '', $DSN);
|
|
||||||
$DSN = preg_replace('/;/', ' ', $DSN);
|
|
||||||
$aDBInstances[$i] = pg_connect($DSN, PGSQL_CONNECT_FORCE_NEW | PGSQL_CONNECT_ASYNC);
|
|
||||||
pg_ping($aDBInstances[$i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
|
|
||||||
echo $sFile.': ';
|
|
||||||
$hFile = fopen($sFile, 'r');
|
|
||||||
$sSQL = fgets($hFile, 100000);
|
|
||||||
$iLines = 0;
|
|
||||||
while (true) {
|
|
||||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
|
||||||
if (!pg_connection_busy($aDBInstances[$i])) {
|
|
||||||
while (pg_get_result($aDBInstances[$i]));
|
|
||||||
$sSQL = fgets($hFile, 100000);
|
|
||||||
if (!$sSQL) break 2;
|
|
||||||
if (!pg_send_query($aDBInstances[$i], $sSQL)) fail(pg_last_error($aDBInstances[$i]));
|
|
||||||
$iLines++;
|
|
||||||
if ($iLines == 1000) {
|
|
||||||
echo '.';
|
|
||||||
$iLines = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
usleep(10);
|
|
||||||
}
|
|
||||||
fclose($hFile);
|
|
||||||
|
|
||||||
$bAnyBusy = true;
|
|
||||||
while ($bAnyBusy) {
|
|
||||||
$bAnyBusy = false;
|
|
||||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
|
||||||
if (pg_connection_busy($aDBInstances[$i])) $bAnyBusy = true;
|
|
||||||
}
|
|
||||||
usleep(10);
|
|
||||||
}
|
|
||||||
echo "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($i = 0; $i < $this->iInstances; $i++) {
|
|
||||||
pg_close($aDBInstances[$i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
info('Creating indexes on Tiger data');
|
|
||||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_finish.sql');
|
|
||||||
$sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:aux-data}',
|
|
||||||
CONST_Tablespace_Aux_Data,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:aux-index}',
|
|
||||||
CONST_Tablespace_Aux_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$this->pgsqlRunScript($sTemplate, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function calculatePostcodes($bCMDResultAll)
|
|
||||||
{
|
|
||||||
info('Calculate Postcodes');
|
|
||||||
$this->pgExec('TRUNCATE location_postcode');
|
|
||||||
|
|
||||||
$sSQL = 'INSERT INTO location_postcode';
|
|
||||||
$sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
|
|
||||||
$sSQL .= "SELECT nextval('seq_place'), 1, country_code,";
|
|
||||||
$sSQL .= " upper(trim (both ' ' from address->'postcode')) as pc,";
|
|
||||||
$sSQL .= ' ST_Centroid(ST_Collect(ST_Centroid(geometry)))';
|
|
||||||
$sSQL .= ' FROM placex';
|
|
||||||
$sSQL .= " WHERE address ? 'postcode' AND address->'postcode' NOT SIMILAR TO '%(,|;)%'";
|
|
||||||
$sSQL .= ' AND geometry IS NOT null';
|
|
||||||
$sSQL .= ' GROUP BY country_code, pc';
|
|
||||||
$this->pgExec($sSQL);
|
|
||||||
|
|
||||||
// only add postcodes that are not yet available in OSM
|
|
||||||
$sSQL = 'INSERT INTO location_postcode';
|
|
||||||
$sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
|
|
||||||
$sSQL .= "SELECT nextval('seq_place'), 1, 'us', postcode,";
|
|
||||||
$sSQL .= ' ST_SetSRID(ST_Point(x,y),4326)';
|
|
||||||
$sSQL .= ' FROM us_postcode WHERE postcode NOT IN';
|
|
||||||
$sSQL .= ' (SELECT postcode FROM location_postcode';
|
|
||||||
$sSQL .= " WHERE country_code = 'us')";
|
|
||||||
$this->pgExec($sSQL);
|
|
||||||
|
|
||||||
// add missing postcodes for GB (if available)
|
|
||||||
$sSQL = 'INSERT INTO location_postcode';
|
|
||||||
$sSQL .= ' (place_id, indexed_status, country_code, postcode, geometry) ';
|
|
||||||
$sSQL .= "SELECT nextval('seq_place'), 1, 'gb', postcode, geometry";
|
|
||||||
$sSQL .= ' FROM gb_postcode WHERE postcode NOT IN';
|
|
||||||
$sSQL .= ' (SELECT postcode FROM location_postcode';
|
|
||||||
$sSQL .= " WHERE country_code = 'gb')";
|
|
||||||
$this->pgExec($sSQL);
|
|
||||||
|
|
||||||
if (!$bCMDResultAll) {
|
|
||||||
$sSQL = "DELETE FROM word WHERE class='place' and type='postcode'";
|
|
||||||
$sSQL .= 'and word NOT IN (SELECT postcode FROM location_postcode)';
|
|
||||||
$this->pgExec($sSQL);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sSQL = 'SELECT count(getorcreate_postcode_id(v)) FROM ';
|
|
||||||
$sSQL .= '(SELECT distinct(postcode) as v FROM location_postcode) p';
|
|
||||||
$this->pgExec($sSQL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index($bIndexNoanalyse)
|
|
||||||
{
|
|
||||||
$sOutputFile = '';
|
|
||||||
$sBaseCmd = CONST_InstallPath.'/nominatim/nominatim -i -d '.$this->aDSNInfo['database'].' -P '
|
|
||||||
.$this->aDSNInfo['port'].' -t '.$this->iInstances.$sOutputFile;
|
|
||||||
if (isset($this->aDSNInfo['hostspec'])) {
|
|
||||||
$sBaseCmd .= ' -H '.$this->aDSNInfo['hostspec'];
|
|
||||||
}
|
|
||||||
if (isset($this->aDSNInfo['username'])) {
|
|
||||||
$sBaseCmd .= ' -U '.$this->aDSNInfo['username'];
|
|
||||||
}
|
|
||||||
|
|
||||||
info('Index ranks 0 - 4');
|
|
||||||
$iStatus = $this->runWithPgEnv($sBaseCmd.' -R 4');
|
|
||||||
if ($iStatus != 0) {
|
|
||||||
fail('error status ' . $iStatus . ' running nominatim!');
|
|
||||||
}
|
|
||||||
if (!$bIndexNoanalyse) $this->pgsqlRunScript('ANALYSE');
|
|
||||||
|
|
||||||
info('Index ranks 5 - 25');
|
|
||||||
$iStatus = $this->runWithPgEnv($sBaseCmd.' -r 5 -R 25');
|
|
||||||
if ($iStatus != 0) {
|
|
||||||
fail('error status ' . $iStatus . ' running nominatim!');
|
|
||||||
}
|
|
||||||
if (!$bIndexNoanalyse) $this->pgsqlRunScript('ANALYSE');
|
|
||||||
|
|
||||||
info('Index ranks 26 - 30');
|
|
||||||
$iStatus = $this->runWithPgEnv($sBaseCmd.' -r 26');
|
|
||||||
if ($iStatus != 0) {
|
|
||||||
fail('error status ' . $iStatus . ' running nominatim!');
|
|
||||||
}
|
|
||||||
|
|
||||||
info('Index postcodes');
|
|
||||||
$sSQL = 'UPDATE location_postcode SET indexed_status = 0';
|
|
||||||
$this->pgExec($sSQL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createSearchIndices()
|
|
||||||
{
|
|
||||||
info('Create Search indices');
|
|
||||||
|
|
||||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/indices.src.sql');
|
|
||||||
if (!$this->dbReverseOnly()) {
|
|
||||||
$sTemplate .= file_get_contents(CONST_BasePath.'/sql/indices_search.src.sql');
|
|
||||||
}
|
|
||||||
$sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:address-index}',
|
|
||||||
CONST_Tablespace_Address_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:search-index}',
|
|
||||||
CONST_Tablespace_Search_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$sTemplate = $this->replaceTablespace(
|
|
||||||
'{ts:aux-index}',
|
|
||||||
CONST_Tablespace_Aux_Index,
|
|
||||||
$sTemplate
|
|
||||||
);
|
|
||||||
$this->pgsqlRunScript($sTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createCountryNames()
|
|
||||||
{
|
|
||||||
info('Create search index for default country names');
|
|
||||||
|
|
||||||
$this->pgsqlRunScript("select getorcreate_country(make_standard_name('uk'), 'gb')");
|
|
||||||
$this->pgsqlRunScript("select getorcreate_country(make_standard_name('united states'), 'us')");
|
|
||||||
$this->pgsqlRunScript('select count(*) from (select getorcreate_country(make_standard_name(country_code), country_code) from country_name where country_code is not null) as x');
|
|
||||||
$this->pgsqlRunScript("select count(*) from (select getorcreate_country(make_standard_name(name->'name'), country_code) from country_name where name ? 'name') as x");
|
|
||||||
$sSQL = 'select count(*) from (select getorcreate_country(make_standard_name(v),'
|
|
||||||
.'country_code) from (select country_code, skeys(name) as k, svals(name) as v from country_name) x where k ';
|
|
||||||
if (CONST_Languages) {
|
|
||||||
$sSQL .= 'in ';
|
|
||||||
$sDelim = '(';
|
|
||||||
foreach (explode(',', CONST_Languages) as $sLang) {
|
|
||||||
$sSQL .= $sDelim."'name:$sLang'";
|
|
||||||
$sDelim = ',';
|
|
||||||
}
|
|
||||||
$sSQL .= ')';
|
|
||||||
} else {
|
|
||||||
// all include all simple name tags
|
|
||||||
$sSQL .= "like 'name:%'";
|
|
||||||
}
|
|
||||||
$sSQL .= ') v';
|
|
||||||
$this->pgsqlRunScript($sSQL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function drop()
|
|
||||||
{
|
|
||||||
info('Drop tables only required for updates');
|
|
||||||
|
|
||||||
// The implementation is potentially a bit dangerous because it uses
|
|
||||||
// a positive selection of tables to keep, and deletes everything else.
|
|
||||||
// Including any tables that the unsuspecting user might have manually
|
|
||||||
// created. USE AT YOUR OWN PERIL.
|
|
||||||
// tables we want to keep. everything else goes.
|
|
||||||
$aKeepTables = array(
|
|
||||||
'*columns',
|
|
||||||
'import_polygon_*',
|
|
||||||
'import_status',
|
|
||||||
'place_addressline',
|
|
||||||
'location_postcode',
|
|
||||||
'location_property*',
|
|
||||||
'placex',
|
|
||||||
'search_name',
|
|
||||||
'seq_*',
|
|
||||||
'word',
|
|
||||||
'query_log',
|
|
||||||
'new_query_log',
|
|
||||||
'spatial_ref_sys',
|
|
||||||
'country_name',
|
|
||||||
'place_classtype_*',
|
|
||||||
'country_osm_grid'
|
|
||||||
);
|
|
||||||
|
|
||||||
$aDropTables = array();
|
|
||||||
$aHaveTables = $this->oDB->getCol("SELECT tablename FROM pg_tables WHERE schemaname='public'");
|
|
||||||
|
|
||||||
foreach ($aHaveTables as $sTable) {
|
|
||||||
$bFound = false;
|
|
||||||
foreach ($aKeepTables as $sKeep) {
|
|
||||||
if (fnmatch($sKeep, $sTable)) {
|
|
||||||
$bFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$bFound) array_push($aDropTables, $sTable);
|
|
||||||
}
|
|
||||||
foreach ($aDropTables as $sDrop) {
|
|
||||||
if ($this->bVerbose) echo "Dropping table $sDrop\n";
|
|
||||||
$this->oDB->exec("DROP TABLE IF EXISTS $sDrop CASCADE");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
|
|
||||||
if (file_exists(CONST_Osm2pgsql_Flatnode_File)) {
|
|
||||||
if ($this->bVerbose) echo 'Deleting '.CONST_Osm2pgsql_Flatnode_File."\n";
|
|
||||||
unlink(CONST_Osm2pgsql_Flatnode_File);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function pgsqlRunDropAndRestore($sDumpFile)
|
|
||||||
{
|
|
||||||
$sCMD = 'pg_restore -p '.$this->aDSNInfo['port'].' -d '.$this->aDSNInfo['database'].' --no-owner -Fc --clean '.$sDumpFile;
|
|
||||||
if ($this->oDB->getPostgresVersion() >= 9.04) {
|
|
||||||
$sCMD .= ' --if-exists';
|
|
||||||
}
|
|
||||||
if (isset($this->aDSNInfo['hostspec'])) {
|
|
||||||
$sCMD .= ' -h '.$this->aDSNInfo['hostspec'];
|
|
||||||
}
|
|
||||||
if (isset($this->aDSNInfo['username'])) {
|
|
||||||
$sCMD .= ' -U '.$this->aDSNInfo['username'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->runWithPgEnv($sCMD);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function pgsqlRunScript($sScript, $bfatal = true)
|
|
||||||
{
|
|
||||||
runSQLScript(
|
|
||||||
$sScript,
|
|
||||||
$bfatal,
|
|
||||||
$this->bVerbose,
|
|
||||||
$this->sIgnoreErrors
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createSqlFunctions()
|
|
||||||
{
|
|
||||||
$sTemplate = file_get_contents(CONST_BasePath.'/sql/functions.sql');
|
|
||||||
$sTemplate = str_replace('{modulepath}', $this->sModulePath, $sTemplate);
|
|
||||||
if ($this->bEnableDiffUpdates) {
|
|
||||||
$sTemplate = str_replace('RETURN NEW; -- %DIFFUPDATES%', '--', $sTemplate);
|
|
||||||
}
|
|
||||||
if ($this->bEnableDebugStatements) {
|
|
||||||
$sTemplate = str_replace('--DEBUG:', '', $sTemplate);
|
|
||||||
}
|
|
||||||
if (CONST_Limit_Reindexing) {
|
|
||||||
$sTemplate = str_replace('--LIMIT INDEXING:', '', $sTemplate);
|
|
||||||
}
|
|
||||||
if (!CONST_Use_US_Tiger_Data) {
|
|
||||||
$sTemplate = str_replace('-- %NOTIGERDATA% ', '', $sTemplate);
|
|
||||||
}
|
|
||||||
if (!CONST_Use_Aux_Location_data) {
|
|
||||||
$sTemplate = str_replace('-- %NOAUXDATA% ', '', $sTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sReverseOnly = $this->dbReverseOnly() ? 'true' : 'false';
|
|
||||||
$sTemplate = str_replace('%REVERSE-ONLY%', $sReverseOnly, $sTemplate);
|
|
||||||
|
|
||||||
$this->pgsqlRunScript($sTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function pgsqlRunPartitionScript($sTemplate)
|
|
||||||
{
|
|
||||||
$sSQL = 'select distinct partition from country_name';
|
|
||||||
$aPartitions = $this->oDB->getCol($sSQL);
|
|
||||||
if (!$this->bNoPartitions) $aPartitions[] = 0;
|
|
||||||
|
|
||||||
preg_match_all('#^-- start(.*?)^-- end#ms', $sTemplate, $aMatches, PREG_SET_ORDER);
|
|
||||||
foreach ($aMatches as $aMatch) {
|
|
||||||
$sResult = '';
|
|
||||||
foreach ($aPartitions as $sPartitionName) {
|
|
||||||
$sResult .= str_replace('-partition-', $sPartitionName, $aMatch[1]);
|
|
||||||
}
|
|
||||||
$sTemplate = str_replace($aMatch[0], $sResult, $sTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->pgsqlRunScript($sTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function pgsqlRunScriptFile($sFilename)
|
|
||||||
{
|
|
||||||
if (!file_exists($sFilename)) fail('unable to find '.$sFilename);
|
|
||||||
|
|
||||||
$sCMD = 'psql -p '.$this->aDSNInfo['port'].' -d '.$this->aDSNInfo['database'];
|
|
||||||
if (!$this->bVerbose) {
|
|
||||||
$sCMD .= ' -q';
|
|
||||||
}
|
|
||||||
if (isset($this->aDSNInfo['hostspec'])) {
|
|
||||||
$sCMD .= ' -h '.$this->aDSNInfo['hostspec'];
|
|
||||||
}
|
|
||||||
if (isset($this->aDSNInfo['username'])) {
|
|
||||||
$sCMD .= ' -U '.$this->aDSNInfo['username'];
|
|
||||||
}
|
|
||||||
$aProcEnv = null;
|
|
||||||
if (isset($this->aDSNInfo['password'])) {
|
|
||||||
$aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
|
|
||||||
}
|
|
||||||
$ahGzipPipes = null;
|
|
||||||
if (preg_match('/\\.gz$/', $sFilename)) {
|
|
||||||
$aDescriptors = array(
|
|
||||||
0 => array('pipe', 'r'),
|
|
||||||
1 => array('pipe', 'w'),
|
|
||||||
2 => array('file', '/dev/null', 'a')
|
|
||||||
);
|
|
||||||
$hGzipProcess = proc_open('zcat '.$sFilename, $aDescriptors, $ahGzipPipes);
|
|
||||||
if (!is_resource($hGzipProcess)) fail('unable to start zcat');
|
|
||||||
$aReadPipe = $ahGzipPipes[1];
|
|
||||||
fclose($ahGzipPipes[0]);
|
|
||||||
} else {
|
|
||||||
$sCMD .= ' -f '.$sFilename;
|
|
||||||
$aReadPipe = array('pipe', 'r');
|
|
||||||
}
|
|
||||||
$aDescriptors = array(
|
|
||||||
0 => $aReadPipe,
|
|
||||||
1 => array('pipe', 'w'),
|
|
||||||
2 => array('file', '/dev/null', 'a')
|
|
||||||
);
|
|
||||||
$ahPipes = null;
|
|
||||||
$hProcess = proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
|
|
||||||
if (!is_resource($hProcess)) fail('unable to start pgsql');
|
|
||||||
// TODO: error checking
|
|
||||||
while (!feof($ahPipes[1])) {
|
|
||||||
echo fread($ahPipes[1], 4096);
|
|
||||||
}
|
|
||||||
fclose($ahPipes[1]);
|
|
||||||
$iReturn = proc_close($hProcess);
|
|
||||||
if ($iReturn > 0) {
|
|
||||||
fail("pgsql returned with error code ($iReturn)");
|
|
||||||
}
|
|
||||||
if ($ahGzipPipes) {
|
|
||||||
fclose($ahGzipPipes[1]);
|
|
||||||
proc_close($hGzipProcess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function replaceTablespace($sTemplate, $sTablespace, $sSql)
|
|
||||||
{
|
|
||||||
if ($sTablespace) {
|
|
||||||
$sSql = str_replace($sTemplate, 'TABLESPACE "'.$sTablespace.'"', $sSql);
|
|
||||||
} else {
|
|
||||||
$sSql = str_replace($sTemplate, '', $sSql);
|
|
||||||
}
|
|
||||||
return $sSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function runWithPgEnv($sCmd)
|
|
||||||
{
|
|
||||||
if ($this->bVerbose) {
|
|
||||||
echo "Execute: $sCmd\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$aProcEnv = null;
|
|
||||||
|
|
||||||
if (isset($this->aDSNInfo['password'])) {
|
|
||||||
$aProcEnv = array_merge(array('PGPASSWORD' => $this->aDSNInfo['password']), $_ENV);
|
|
||||||
}
|
|
||||||
|
|
||||||
return runWithEnv($sCmd, $aProcEnv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the SQL command on the open database.
|
|
||||||
*
|
|
||||||
* @param string $sSQL SQL command to execute.
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*
|
|
||||||
* @pre connect() must have been called.
|
|
||||||
*/
|
|
||||||
private function pgExec($sSQL)
|
|
||||||
{
|
|
||||||
$this->oDB->exec($sSQL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the database is in reverse-only mode.
|
|
||||||
*
|
|
||||||
* @return True if there is no search_name table and infrastructure.
|
|
||||||
*/
|
|
||||||
private function dbReverseOnly()
|
|
||||||
{
|
|
||||||
return !($this->oDB->tableExists('search_name'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
function checkInFile($sOSMFile)
|
|
||||||
{
|
|
||||||
if (!isset($sOSMFile)) {
|
|
||||||
fail('missing --osm-file for data import');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file_exists($sOSMFile)) {
|
|
||||||
fail('the path supplied to --osm-file does not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_readable($sOSMFile)) {
|
|
||||||
fail('osm-file "' . $aCMDResult['osm-file'] . '" not readable');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkModulePresence()
|
|
||||||
{
|
|
||||||
// Try accessing the C module, so we know early if something is wrong.
|
|
||||||
// Raises Nominatim\DatabaseError on failure
|
|
||||||
|
|
||||||
$sModulePath = CONST_Database_Module_Path;
|
|
||||||
$sSQL = "CREATE FUNCTION nominatim_test_import_func(text) RETURNS text AS '";
|
|
||||||
$sSQL .= $sModulePath . "/nominatim.so', 'transliteration' LANGUAGE c IMMUTABLE STRICT";
|
|
||||||
$sSQL .= ';DROP FUNCTION nominatim_test_import_func(text);';
|
|
||||||
|
|
||||||
$oDB = new \Nominatim\DB();
|
|
||||||
$oDB->connect();
|
|
||||||
$oDB->exec($sSQL, null, 'Database server failed to load '.$sModulePath.'/nominatim.so module');
|
|
||||||
}
|
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
|
|
||||||
function _one_row($aAddressLine){
|
function _one_row($aAddressLine){
|
||||||
$bNotUsed = isset($aAddressLine['isaddress']) && !$aAddressLine['isaddress'];
|
$bNotUsed = (isset($aAddressLine['isaddress']) && $aAddressLine['isaddress'] == 'f');
|
||||||
|
|
||||||
echo '<tr class="' . ($bNotUsed?'notused':'') . '">'."\n";
|
echo '<tr class="' . ($bNotUsed?'notused':'') . '">'."\n";
|
||||||
echo ' <td class="name">'.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'<span class="noname">No Name</span>')."</td>\n";
|
echo ' <td class="name">'.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'<span class="noname">No Name</span>')."</td>\n";
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
if ($aPointDetails['calculated_importance']) {
|
if ($aPointDetails['calculated_importance']) {
|
||||||
kv('Importance' , $aPointDetails['calculated_importance'].($aPointDetails['importance']?'':' (estimated)') );
|
kv('Importance' , $aPointDetails['calculated_importance'].($aPointDetails['importance']?'':' (estimated)') );
|
||||||
}
|
}
|
||||||
kv('Coverage' , ($aPointDetails['isarea']?'Polygon':'Point') );
|
kv('Coverage' , ($aPointDetails['isarea']=='t'?'Polygon':'Point') );
|
||||||
kv('Centre Point' , $aPointDetails['lat'].','.$aPointDetails['lon'] );
|
kv('Centre Point' , $aPointDetails['lat'].','.$aPointDetails['lon'] );
|
||||||
kv('OSM' , osmLink($aPointDetails) );
|
kv('OSM' , osmLink($aPointDetails) );
|
||||||
if ($aPointDetails['wikipedia'])
|
if ($aPointDetails['wikipedia'])
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ if ($aPointDetails['icon']) {
|
|||||||
$aPlaceDetails['rank_address'] = (int) $aPointDetails['rank_address'];
|
$aPlaceDetails['rank_address'] = (int) $aPointDetails['rank_address'];
|
||||||
$aPlaceDetails['rank_search'] = (int) $aPointDetails['rank_search'];
|
$aPlaceDetails['rank_search'] = (int) $aPointDetails['rank_search'];
|
||||||
|
|
||||||
$aPlaceDetails['isarea'] = $aPointDetails['isarea'];
|
$aPlaceDetails['isarea'] = ($aPointDetails['isarea'] == 't');
|
||||||
$aPlaceDetails['centroid'] = array(
|
$aPlaceDetails['centroid'] = array(
|
||||||
'type' => 'Point',
|
'type' => 'Point',
|
||||||
'coordinates' => array( (float) $aPointDetails['lon'], (float) $aPointDetails['lat'] )
|
'coordinates' => array( (float) $aPointDetails['lon'], (float) $aPointDetails['lat'] )
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$title = 'Internal Server Error';
|
|
||||||
if ( $exception->getCode() == 400 ) {
|
|
||||||
$title = 'Bad Request';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
em { font-weight: bold; font-family: monospace; color: #e00404; background-color: #ffeaea; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1><?php echo $title ?></h1>
|
|
||||||
|
|
||||||
<?php if (get_class($exception) == 'Nominatim\DatabaseError') { ?>
|
|
||||||
|
|
||||||
<p>Nominatim has encountered an internal error while accessing the database.
|
|
||||||
This may happen because the database is broken or because of a bug in
|
|
||||||
the software.</p>
|
|
||||||
|
|
||||||
<?php } else { ?>
|
|
||||||
|
|
||||||
<p>Nominatim has encountered an error with your request.</p>
|
|
||||||
|
|
||||||
<?php } ?>
|
|
||||||
|
|
||||||
|
|
||||||
<h3>Details</h3>
|
|
||||||
|
|
||||||
<?php echo $exception->getMessage() ?>
|
|
||||||
|
|
||||||
<?php if (CONST_Debug) { ?>
|
|
||||||
<p>
|
|
||||||
Exception <em><?php echo get_class($exception) ?></em> thrown in <em><?php echo $exception->getFile() . '('. $exception->getLine() . ')' ?></em>.
|
|
||||||
|
|
||||||
<?php if (get_class($exception) == 'Nominatim\DatabaseError') { ?>
|
|
||||||
|
|
||||||
<h3>SQL Error</h3>
|
|
||||||
<em><?php echo $exception->getSqlError() ?></em>
|
|
||||||
|
|
||||||
<pre><?php echo $exception->getSqlDebugDump() ?></pre>
|
|
||||||
|
|
||||||
<?php } ?>
|
|
||||||
|
|
||||||
<h3>Stack trace</h3>
|
|
||||||
<pre><?php echo $exception->getTraceAsString() ?></pre>
|
|
||||||
|
|
||||||
<?php } ?>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
If you feel this error is incorrect feel file an issue on
|
|
||||||
<a href="https://github.com/openstreetmap/Nominatim/issues">Github</a>.
|
|
||||||
|
|
||||||
Please include the error message above and the URL you used.
|
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
$error = array(
|
|
||||||
'code' => $exception->getCode(),
|
|
||||||
'message' => $exception->getMessage()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (CONST_Debug) {
|
|
||||||
$error['details'] = $exception->getFile() . '('. $exception->getLine() . ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo javascript_renderData(array('error' => $error));
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<error>
|
|
||||||
<code><?php echo $exception->getCode() ?></code>
|
|
||||||
<message><?php echo $exception->getMessage() ?></message>
|
|
||||||
<?php if (CONST_Debug) { ?>
|
|
||||||
<details><?php echo $exception->getFile() . '('. $exception->getLine() . ')' ?></details>
|
|
||||||
<?php } ?>
|
|
||||||
</error>
|
|
||||||
@@ -341,7 +341,7 @@ if (/(create\s+table\s+)([-_\w]+)\s/i) { # example: CREATE TABLE `english_engli
|
|||||||
# in the foreign-key case it will only remove the foreign-key constraint, not the other table entirely.)
|
# in the foreign-key case it will only remove the foreign-key constraint, not the other table entirely.)
|
||||||
# (source: 8.1.3 docs, section "drop table")
|
# (source: 8.1.3 docs, section "drop table")
|
||||||
warn "table $table will be dropped CASCADE\n";
|
warn "table $table will be dropped CASCADE\n";
|
||||||
$pre_create_sql .= "DROP TABLE $table CASCADE;\n"; # custom dumps may be missing the 'dump' commands
|
$pre_create_sql .= "DROP TABLE $table CASCADE\\g\n"; # custom dumps may be missing the 'dump' commands
|
||||||
}
|
}
|
||||||
|
|
||||||
s/(create\s+table\s+)([-_\w]+)\s/$1 $table /i;
|
s/(create\s+table\s+)([-_\w]+)\s/$1 $table /i;
|
||||||
@@ -367,7 +367,6 @@ if ($create_sql ne "") { # we are inside create table statement so lets
|
|||||||
s/INSERT METHOD[=\s+][^;\s]+//i;
|
s/INSERT METHOD[=\s+][^;\s]+//i;
|
||||||
s/PASSWORD=[^;\s]+//i;
|
s/PASSWORD=[^;\s]+//i;
|
||||||
s/ROW_FORMAT=(?:DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT)+//i;
|
s/ROW_FORMAT=(?:DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT)+//i;
|
||||||
s/KEY_BLOCK_SIZE=8//i;
|
|
||||||
s/DELAY KEY WRITE=[^;\s]+//i;
|
s/DELAY KEY WRITE=[^;\s]+//i;
|
||||||
s/INDEX DIRECTORY[=\s+][^;\s]+//i;
|
s/INDEX DIRECTORY[=\s+][^;\s]+//i;
|
||||||
s/DATA DIRECTORY=[^;\s]+//i;
|
s/DATA DIRECTORY=[^;\s]+//i;
|
||||||
@@ -390,7 +389,6 @@ if ($create_sql ne "") { # we are inside create table statement so lets
|
|||||||
s/DEFAULT CHARSET=[^;\s]+//i; # my mysql version is 4.1.11
|
s/DEFAULT CHARSET=[^;\s]+//i; # my mysql version is 4.1.11
|
||||||
s/ENGINE\s*=\s*[^;\s]+//i; # my mysql version is 4.1.11
|
s/ENGINE\s*=\s*[^;\s]+//i; # my mysql version is 4.1.11
|
||||||
s/ROW_FORMAT=[^;\s]+//i; # my mysql version is 5.0.22
|
s/ROW_FORMAT=[^;\s]+//i; # my mysql version is 5.0.22
|
||||||
s/KEY_BLOCK_SIZE=8//i;
|
|
||||||
s/MIN_ROWS=[^;\s]+//i;
|
s/MIN_ROWS=[^;\s]+//i;
|
||||||
s/MAX_ROWS=[^;\s]+//i;
|
s/MAX_ROWS=[^;\s]+//i;
|
||||||
s/AVG_ROW_LENGTH=[^;\s]+//i;
|
s/AVG_ROW_LENGTH=[^;\s]+//i;
|
||||||
Submodule osm2pgsql updated: 5f3f736348...93b73e5f5c
@@ -7,14 +7,14 @@
|
|||||||
convertWarningsToExceptions="true"
|
convertWarningsToExceptions="true"
|
||||||
processIsolation="false"
|
processIsolation="false"
|
||||||
stopOnFailure="false"
|
stopOnFailure="false"
|
||||||
bootstrap="./bootstrap.php"
|
syntaxCheck="true"
|
||||||
beStrictAboutTestsThatDoNotTestAnything="true"
|
bootstrap="test/php/bootstrap.php"
|
||||||
>
|
>
|
||||||
<php>
|
<php>
|
||||||
</php>
|
</php>
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="Nominatim PHP Test Suite">
|
<testsuite name="Nominatim PHP Test Suite">
|
||||||
<directory>./Nominatim</directory>
|
<directory>./test/php/Nominatim</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
<filter>
|
<filter>
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
[
|
|
||||||
{ "tags" : {
|
|
||||||
"place" : {
|
|
||||||
"sea" : [2, 0],
|
|
||||||
"continent" : [2, 0],
|
|
||||||
"country" : [4, 0],
|
|
||||||
"state" : [8, 0],
|
|
||||||
"region" : [18, 0],
|
|
||||||
"county" : 12,
|
|
||||||
"city" : 16,
|
|
||||||
"island" : [17, 0],
|
|
||||||
"town" : [18, 16],
|
|
||||||
"village" : [19, 16],
|
|
||||||
"hamlet" : [19, 16],
|
|
||||||
"municipality" : [19, 16],
|
|
||||||
"district" : [19, 16],
|
|
||||||
"unincorporated_area" : [19, 16],
|
|
||||||
"borough" : [19, 16],
|
|
||||||
"suburb" : 20,
|
|
||||||
"croft" : 20,
|
|
||||||
"subdivision" : 20,
|
|
||||||
"isolated_dwelling" : 20,
|
|
||||||
"farm" : [20, 0],
|
|
||||||
"locality" : [20, 0],
|
|
||||||
"islet" : [20, 0],
|
|
||||||
"mountain_pass" : [20, 0],
|
|
||||||
"neighbourhood" : 22,
|
|
||||||
"quarter" : 22,
|
|
||||||
"city_block" : 22,
|
|
||||||
"houses" : [28, 0]
|
|
||||||
},
|
|
||||||
"boundary" : {
|
|
||||||
"administrative2" : 4,
|
|
||||||
"administrative3" : 6,
|
|
||||||
"administrative4" : 8,
|
|
||||||
"administrative5" : 10,
|
|
||||||
"administrative6" : 12,
|
|
||||||
"administrative7" : 14,
|
|
||||||
"administrative8" : 16,
|
|
||||||
"administrative9" : 18,
|
|
||||||
"administrative10" : 20,
|
|
||||||
"administrative11" : 22,
|
|
||||||
"administrative12" : 24
|
|
||||||
},
|
|
||||||
"landuse" : {
|
|
||||||
"residential" : 22,
|
|
||||||
"farm" : 22,
|
|
||||||
"farmyard" : 22,
|
|
||||||
"industrial" : 22,
|
|
||||||
"commercial" : 22,
|
|
||||||
"allotments" : 22,
|
|
||||||
"retail" : 22,
|
|
||||||
"" : [22, 0]
|
|
||||||
},
|
|
||||||
"leisure" : {
|
|
||||||
"park" : [24, 0]
|
|
||||||
},
|
|
||||||
"natural" : {
|
|
||||||
"peak" : [18, 0],
|
|
||||||
"volcano" : [18, 0],
|
|
||||||
"mountain_range" : [18, 0],
|
|
||||||
"sea" : [4, 0]
|
|
||||||
},
|
|
||||||
"waterway" : {
|
|
||||||
"" : [17, 0]
|
|
||||||
},
|
|
||||||
"highway" : {
|
|
||||||
"" : 26,
|
|
||||||
"service" : 27,
|
|
||||||
"cycleway" : 27,
|
|
||||||
"path" : 27,
|
|
||||||
"footway" : 27,
|
|
||||||
"steps" : 27,
|
|
||||||
"bridleway" : 27,
|
|
||||||
"motorway_link" : 27,
|
|
||||||
"primary_link" : 27,
|
|
||||||
"trunk_link" : 27,
|
|
||||||
"secondary_link" : 27,
|
|
||||||
"tertiary_link" : 27
|
|
||||||
},
|
|
||||||
"mountain_pass" : {
|
|
||||||
"" : [20, 0]
|
|
||||||
},
|
|
||||||
"historic" : {
|
|
||||||
"neighbourhood" : [30, 0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "countries" : [ "de" ],
|
|
||||||
"tags" : {
|
|
||||||
"place" : {
|
|
||||||
"county" : [12, 0]
|
|
||||||
},
|
|
||||||
"boundary" : {
|
|
||||||
"administrative5" : [10, 0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "countries" : [ "be" ],
|
|
||||||
"tags" : {
|
|
||||||
"boundary" : {
|
|
||||||
"administrative7" : [14, 0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ if (isset($_GET['debug']) && $_GET['debug']) @define('CONST_Debug', true);
|
|||||||
|
|
||||||
// General settings
|
// General settings
|
||||||
@define('CONST_Debug', false);
|
@define('CONST_Debug', false);
|
||||||
@define('CONST_Database_DSN', 'pgsql:dbname=nominatim'); // or add ;host=...;port=...;user=...;password=...
|
@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_Database_Module_Path', CONST_InstallPath.'/module');
|
||||||
@define('CONST_Max_Word_Frequency', '50000');
|
@define('CONST_Max_Word_Frequency', '50000');
|
||||||
@@ -23,6 +23,8 @@ if (isset($_GET['debug']) && $_GET['debug']) @define('CONST_Debug', true);
|
|||||||
// 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:] [:Cf:]] >; :: 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.
|
||||||
|
@define('CONST_Use_Extra_US_Postcodes', true);
|
||||||
/* Set to true after importing Tiger house number data for the US.
|
/* Set to true after importing Tiger house number data for the US.
|
||||||
Note: The tables must already exist or queries will throw errors.
|
Note: The tables must already exist or queries will throw errors.
|
||||||
After changing this setting run ./utils/setup --create-functions
|
After changing this setting run ./utils/setup --create-functions
|
||||||
@@ -47,9 +49,6 @@ if (isset($_GET['debug']) && $_GET['debug']) @define('CONST_Debug', true);
|
|||||||
@define('CONST_Pyosmium_Binary', '@PYOSMIUM_PATH@');
|
@define('CONST_Pyosmium_Binary', '@PYOSMIUM_PATH@');
|
||||||
@define('CONST_Tiger_Data_Path', CONST_ExtraDataPath.'/tiger');
|
@define('CONST_Tiger_Data_Path', CONST_ExtraDataPath.'/tiger');
|
||||||
@define('CONST_Wikipedia_Data_Path', CONST_ExtraDataPath);
|
@define('CONST_Wikipedia_Data_Path', CONST_ExtraDataPath);
|
||||||
@define('CONST_Phrase_Config', CONST_BasePath.'/settings/phrase_settings.php');
|
|
||||||
@define('CONST_Address_Level_Config', CONST_BasePath.'/settings/address-levels.json');
|
|
||||||
@define('CONST_Import_Style', CONST_BasePath.'/settings/import-full.style');
|
|
||||||
|
|
||||||
// osm2pgsql settings
|
// osm2pgsql settings
|
||||||
@define('CONST_Osm2pgsql_Flatnode_File', null);
|
@define('CONST_Osm2pgsql_Flatnode_File', null);
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"keys" : [ "" ],
|
|
||||||
"values" : {
|
|
||||||
"no" : "skip"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["name:prefix", "name:suffix", "name:botanical", "*wikidata"],
|
|
||||||
"values" : {
|
|
||||||
"" : "skip"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["ref", "int_ref", "nat_ref", "reg_ref", "loc_ref", "old_ref",
|
|
||||||
"iata", "icao", "pcode"],
|
|
||||||
"values" : {
|
|
||||||
"" : "ref"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["name", "name:*", "int_name", "int_name:*", "nat_name", "nat_name:*",
|
|
||||||
"reg_name", "reg_name:*", "loc_name", "loc_name:*",
|
|
||||||
"old_name", "old_name:*", "alt_name", "alt_name:*", "alt_name_*",
|
|
||||||
"official_name", "official_name:*", "place_name", "place_name:*",
|
|
||||||
"short_name", "short_name:*", "brand"],
|
|
||||||
"values" : {
|
|
||||||
"" : "name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["landuse"],
|
|
||||||
"values" : {
|
|
||||||
"cemetry" : "skip",
|
|
||||||
"" : "fallback,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["boundary"],
|
|
||||||
"values" : {
|
|
||||||
"administrative" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["place"],
|
|
||||||
"values" : {
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:housename"],
|
|
||||||
"values" : {
|
|
||||||
"" : "name,house"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:housenumber", "addr:conscriptionnumber", "addr:streetnumber"],
|
|
||||||
"values" : {
|
|
||||||
"" : "address,house"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:interpolation"],
|
|
||||||
"values" : {
|
|
||||||
"" : "interpolation,address"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["postal_code", "postcode", "addr:postcode",
|
|
||||||
"tiger:zip_left", "tiger:zip_right"],
|
|
||||||
"values" : {
|
|
||||||
"" : "postcode,fallback"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["country_code", "ISO3166-1", "is_in:country_code", "is_in:country",
|
|
||||||
"addr:country", "addr:country", "addr:country_code"],
|
|
||||||
"values" : {
|
|
||||||
"" : "country"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:*", "is_in:*", "tiger:county"],
|
|
||||||
"values" : {
|
|
||||||
"" : "address"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["highway"],
|
|
||||||
"values" : {
|
|
||||||
"motorway" : "main",
|
|
||||||
"trunk" : "main",
|
|
||||||
"primary" : "main",
|
|
||||||
"secondary" : "main",
|
|
||||||
"tertiary" : "main",
|
|
||||||
"unclassified" : "main",
|
|
||||||
"residential" : "main",
|
|
||||||
"living_street" : "main",
|
|
||||||
"pedestrian" : "main",
|
|
||||||
"road" : "main",
|
|
||||||
"service" : "main,with_name",
|
|
||||||
"cycleway" : "main,with_name",
|
|
||||||
"path" : "main,with_name",
|
|
||||||
"footway" : "main,with_name",
|
|
||||||
"steps" : "main,with_name",
|
|
||||||
"bridleway" : "main,with_name",
|
|
||||||
"track" : "main,with_name",
|
|
||||||
"byway": "main,with_name",
|
|
||||||
"motorway_link" : "main,with_name",
|
|
||||||
"trunk_link" : "main,with_name",
|
|
||||||
"primary_link" : "main,with_name",
|
|
||||||
"secondary_link" : "main,with_name",
|
|
||||||
"tertiary_link" : "main,with_name"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"keys" : ["name:prefix", "name:suffix", "name:botanical", "*wikidata"],
|
|
||||||
"values" : {
|
|
||||||
"" : "skip"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["ref", "int_ref", "nat_ref", "reg_ref", "loc_ref", "old_ref",
|
|
||||||
"iata", "icao", "pcode"],
|
|
||||||
"values" : {
|
|
||||||
"" : "ref"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["name", "name:*", "int_name", "int_name:*", "nat_name", "nat_name:*",
|
|
||||||
"reg_name", "reg_name:*", "loc_name", "loc_name:*",
|
|
||||||
"old_name", "old_name:*", "alt_name", "alt_name:*", "alt_name_*",
|
|
||||||
"official_name", "official_name:*", "place_name", "place_name:*",
|
|
||||||
"short_name", "short_name:*", "brand"],
|
|
||||||
"values" : {
|
|
||||||
"" : "name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["landuse"],
|
|
||||||
"values" : {
|
|
||||||
"cemetry" : "skip",
|
|
||||||
"" : "fallback,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["boundary"],
|
|
||||||
"values" : {
|
|
||||||
"administrative" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["place"],
|
|
||||||
"values" : {
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["country_code", "ISO3166-1", "is_in:country_code", "is_in:country",
|
|
||||||
"addr:country", "addr:country", "addr:country_code"],
|
|
||||||
"values" : {
|
|
||||||
"" : "country"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:*", "is_in:*", "tiger:county"],
|
|
||||||
"values" : {
|
|
||||||
"" : "address"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["postal_code", "postcode", "addr:postcode",
|
|
||||||
"tiger:zip_left", "tiger:zip_right"],
|
|
||||||
"values" : {
|
|
||||||
"" : "postcode"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["capital"],
|
|
||||||
"values" : {
|
|
||||||
"" : "extra"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"keys" : ["*source"],
|
|
||||||
"values" : {
|
|
||||||
"" : "skip"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["name:prefix", "name:suffix", "name:botanical", "wikidata",
|
|
||||||
"*:wikidata"],
|
|
||||||
"values" : {
|
|
||||||
"" : "extra"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["ref", "int_ref", "nat_ref", "reg_ref", "loc_ref", "old_ref",
|
|
||||||
"iata", "icao", "pcode", "pcode:*"],
|
|
||||||
"values" : {
|
|
||||||
"" : "ref"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["name", "name:*", "int_name", "int_name:*", "nat_name", "nat_name:*",
|
|
||||||
"reg_name", "reg_name:*", "loc_name", "loc_name:*",
|
|
||||||
"old_name", "old_name:*", "alt_name", "alt_name:*", "alt_name_*",
|
|
||||||
"official_name", "official_name:*", "place_name", "place_name:*",
|
|
||||||
"short_name", "short_name:*", "brand"],
|
|
||||||
"values" : {
|
|
||||||
"" : "name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:housename"],
|
|
||||||
"values" : {
|
|
||||||
"" : "name,house"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["emergency"],
|
|
||||||
"values" : {
|
|
||||||
"fire_hydrant" : "skip",
|
|
||||||
"yes" : "skip",
|
|
||||||
"no" : "skip",
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["historic", "military"],
|
|
||||||
"values" : {
|
|
||||||
"no" : "skip",
|
|
||||||
"yes" : "skip",
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["natural"],
|
|
||||||
"values" : {
|
|
||||||
"yes" : "skip",
|
|
||||||
"no" : "skip",
|
|
||||||
"coastline" : "skip",
|
|
||||||
"" : "main,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["landuse"],
|
|
||||||
"values" : {
|
|
||||||
"cemetry" : "main,with_name",
|
|
||||||
"" : "main,fallback,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["highway"],
|
|
||||||
"values" : {
|
|
||||||
"no" : "skip",
|
|
||||||
"turning_circle" : "skip",
|
|
||||||
"mini_roundabout" : "skip",
|
|
||||||
"noexit" : "skip",
|
|
||||||
"crossing" : "skip",
|
|
||||||
"traffic_signals" : "main,with_name",
|
|
||||||
"service" : "main,with_name",
|
|
||||||
"cycleway" : "main,with_name",
|
|
||||||
"path" : "main,with_name",
|
|
||||||
"footway" : "main,with_name",
|
|
||||||
"steps" : "main,with_name",
|
|
||||||
"bridleway" : "main,with_name",
|
|
||||||
"track" : "main,with_name",
|
|
||||||
"byway": "main,with_name",
|
|
||||||
"motorway_link" : "main,with_name",
|
|
||||||
"trunk_link" : "main,with_name",
|
|
||||||
"primary_link" : "main,with_name",
|
|
||||||
"secondary_link" : "main,with_name",
|
|
||||||
"tertiary_link" : "main,with_name",
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["railway"],
|
|
||||||
"values" : {
|
|
||||||
"level_crossing" : "skip",
|
|
||||||
"no" : "skip",
|
|
||||||
"" : "main,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["man_made"],
|
|
||||||
"values" : {
|
|
||||||
"survey_point" : "skip",
|
|
||||||
"cutline" : "skip",
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["aerialway"],
|
|
||||||
"values" : {
|
|
||||||
"pylon" : "skip",
|
|
||||||
"no" : "skip",
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["boundary"],
|
|
||||||
"values" : {
|
|
||||||
"" : "main,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["amenity"],
|
|
||||||
"values" : {
|
|
||||||
"restaurant" : "main,operator",
|
|
||||||
"fuel" : "main,operator"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["aeroway", "amenity", "club", "craft", "leisure",
|
|
||||||
"office", "mountain_pass"],
|
|
||||||
"values" : {
|
|
||||||
"no" : "skip",
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["shop"],
|
|
||||||
"values" : {
|
|
||||||
"no" : "skip",
|
|
||||||
"" : "main,operator"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["tourism"],
|
|
||||||
"values" : {
|
|
||||||
"yes" : "skip",
|
|
||||||
"no" : "skip",
|
|
||||||
"" : "main,operator"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["bridge", "tunnel"],
|
|
||||||
"values" : {
|
|
||||||
"" : "main,with_name_key"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["waterway"],
|
|
||||||
"values" : {
|
|
||||||
"riverbank" : "skip",
|
|
||||||
"" : "main,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["place"],
|
|
||||||
"values" : {
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["junction"],
|
|
||||||
"values" : {
|
|
||||||
"" : "main,fallback,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["postal_code", "postcode", "addr:postcode",
|
|
||||||
"tiger:zip_left", "tiger:zip_right"],
|
|
||||||
"values" : {
|
|
||||||
"" : "postcode,fallback"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["country_code", "ISO3166-1", "is_in:country_code", "is_in:country",
|
|
||||||
"addr:country", "addr:country", "addr:country_code"],
|
|
||||||
"values" : {
|
|
||||||
"" : "country"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:housenumber", "addr:conscriptionnumber", "addr:streetnumber"],
|
|
||||||
"values" : {
|
|
||||||
"" : "address,house"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:interpolation"],
|
|
||||||
"values" : {
|
|
||||||
"" : "interpolation,address"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:*", "is_in:*", "tiger:county", "is_in"],
|
|
||||||
"values" : {
|
|
||||||
"" : "address"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["building"],
|
|
||||||
"values" : {
|
|
||||||
"no" : "skip",
|
|
||||||
"" : "main,fallback,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["tracktype", "traffic_calming", "service", "cuisine", "capital",
|
|
||||||
"dispensing", "religion", "denomination", "sport",
|
|
||||||
"internet_access", "lanes", "surface", "smoothness", "width",
|
|
||||||
"est_width", "incline", "opening_hours", "collection_times",
|
|
||||||
"service_times", "disused", "wheelchair", "sac_scale",
|
|
||||||
"trail_visibility", "mtb:scale", "mtb:description", "wood",
|
|
||||||
"drive_through", "drive_in", "access", "vehicle", "bicyle",
|
|
||||||
"foot", "goods", "hgv", "motor_vehicle", "motor_car", "oneway",
|
|
||||||
"date_on", "date_off", "day_on", "day_off", "hour_on", "hour_off",
|
|
||||||
"maxweight", "maxheight", "maxspeed", "fee", "toll", "charge",
|
|
||||||
"population", "description", "image", "attribution", "fax",
|
|
||||||
"email", "url", "website", "phone", "real_ale", "smoking",
|
|
||||||
"food", "camera", "brewery", "locality", "wikipedia",
|
|
||||||
"wikipedia:*", "access:*", "contact:*", "drink:*", "toll:*"],
|
|
||||||
"values" : {
|
|
||||||
"" : "extra"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"keys" : ["name:prefix", "name:suffix", "name:botanical", "*wikidata"],
|
|
||||||
"values" : {
|
|
||||||
"" : "skip"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["ref", "int_ref", "nat_ref", "reg_ref", "loc_ref", "old_ref",
|
|
||||||
"iata", "icao", "pcode"],
|
|
||||||
"values" : {
|
|
||||||
"" : "ref"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["name", "name:*", "int_name", "int_name:*", "nat_name", "nat_name:*",
|
|
||||||
"reg_name", "reg_name:*", "loc_name", "loc_name:*",
|
|
||||||
"old_name", "old_name:*", "alt_name", "alt_name:*", "alt_name_*",
|
|
||||||
"official_name", "official_name:*", "place_name", "place_name:*",
|
|
||||||
"short_name", "short_name:*", "brand"],
|
|
||||||
"values" : {
|
|
||||||
"" : "name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["landuse"],
|
|
||||||
"values" : {
|
|
||||||
"cemetry" : "skip",
|
|
||||||
"" : "fallback,with_name"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["boundary"],
|
|
||||||
"values" : {
|
|
||||||
"administrative" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["place"],
|
|
||||||
"values" : {
|
|
||||||
"" : "main"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["country_code", "ISO3166-1", "is_in:country_code", "is_in:country",
|
|
||||||
"addr:country", "addr:country", "addr:country_code"],
|
|
||||||
"values" : {
|
|
||||||
"" : "country"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["addr:*", "is_in:*", "tiger:county"],
|
|
||||||
"values" : {
|
|
||||||
"" : "address"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keys" : ["highway"],
|
|
||||||
"values" : {
|
|
||||||
"motorway" : "main",
|
|
||||||
"trunk" : "main",
|
|
||||||
"primary" : "main",
|
|
||||||
"secondary" : "main",
|
|
||||||
"tertiary" : "main",
|
|
||||||
"unclassified" : "main",
|
|
||||||
"residential" : "main",
|
|
||||||
"living_street" : "main",
|
|
||||||
"pedestrian" : "main",
|
|
||||||
"road" : "main",
|
|
||||||
"service" : "main,with_name",
|
|
||||||
"cycleway" : "main,with_name",
|
|
||||||
"path" : "main,with_name",
|
|
||||||
"footway" : "main,with_name",
|
|
||||||
"steps" : "main,with_name",
|
|
||||||
"bridleway" : "main,with_name",
|
|
||||||
"track" : "main,with_name",
|
|
||||||
"byway": "main,with_name",
|
|
||||||
"motorway_link" : "main,with_name",
|
|
||||||
"trunk_link" : "main,with_name",
|
|
||||||
"primary_link" : "main,with_name",
|
|
||||||
"secondary_link" : "main,with_name",
|
|
||||||
"tertiary_link" : "main,with_name"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
4
settings/settings.php
Normal file
4
settings/settings.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
echo "ERROR: Scripts must be run from build directory.\n";
|
||||||
|
exit;
|
||||||
@@ -547,7 +547,7 @@ BEGIN
|
|||||||
-- RAISE WARNING 'get_country_code, start: %', ST_AsText(place_centre);
|
-- RAISE WARNING 'get_country_code, start: %', ST_AsText(place_centre);
|
||||||
|
|
||||||
-- Try for a OSM polygon
|
-- Try for a OSM polygon
|
||||||
FOR nearcountry IN select country_code from location_area_country where country_code is not null and st_covers(geometry, place_centre) limit 1
|
FOR nearcountry IN select country_code from location_area_country where country_code is not null and not isguess and st_covers(geometry, place_centre) limit 1
|
||||||
LOOP
|
LOOP
|
||||||
RETURN nearcountry.country_code;
|
RETURN nearcountry.country_code;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
@@ -561,6 +561,14 @@ BEGIN
|
|||||||
RETURN nearcountry.country_code;
|
RETURN nearcountry.country_code;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
|
||||||
|
-- RAISE WARNING 'natural earth: %', ST_AsText(place_centre);
|
||||||
|
|
||||||
|
-- Natural earth data
|
||||||
|
FOR nearcountry IN select country_code from country_naturalearthdata where st_covers(geometry, place_centre) limit 1
|
||||||
|
LOOP
|
||||||
|
RETURN nearcountry.country_code;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
-- RAISE WARNING 'near osm fallback: %', ST_AsText(place_centre);
|
-- RAISE WARNING 'near osm fallback: %', ST_AsText(place_centre);
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -569,6 +577,14 @@ BEGIN
|
|||||||
RETURN nearcountry.country_code;
|
RETURN nearcountry.country_code;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
|
||||||
|
-- RAISE WARNING 'near natural earth: %', ST_AsText(place_centre);
|
||||||
|
|
||||||
|
-- Natural earth data
|
||||||
|
FOR nearcountry IN select country_code from country_naturalearthdata where st_dwithin(geometry, place_centre, 0.5) limit 1
|
||||||
|
LOOP
|
||||||
|
RETURN nearcountry.country_code;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END;
|
END;
|
||||||
$$
|
$$
|
||||||
@@ -768,28 +784,6 @@ END;
|
|||||||
$$
|
$$
|
||||||
LANGUAGE plpgsql;
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION osmline_reinsert(node_id BIGINT, geom GEOMETRY)
|
|
||||||
RETURNS BOOLEAN
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
existingline RECORD;
|
|
||||||
BEGIN
|
|
||||||
SELECT w.id FROM planet_osm_ways w, location_property_osmline p
|
|
||||||
WHERE p.linegeo && geom and p.osm_id = w.id and p.indexed_status = 0
|
|
||||||
and node_id = any(w.nodes) INTO existingline;
|
|
||||||
|
|
||||||
IF existingline.id is not NULL THEN
|
|
||||||
DELETE FROM location_property_osmline WHERE osm_id = existingline.id;
|
|
||||||
INSERT INTO location_property_osmline (osm_id, address, linegeo)
|
|
||||||
SELECT osm_id, address, geometry FROM place
|
|
||||||
WHERE osm_type = 'W' and osm_id = existingline.id;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
RETURN true;
|
|
||||||
END;
|
|
||||||
$$
|
|
||||||
LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION osmline_insert() RETURNS TRIGGER
|
CREATE OR REPLACE FUNCTION osmline_insert() RETURNS TRIGGER
|
||||||
AS $$
|
AS $$
|
||||||
@@ -823,12 +817,11 @@ DECLARE
|
|||||||
i INTEGER;
|
i INTEGER;
|
||||||
postcode TEXT;
|
postcode TEXT;
|
||||||
result BOOLEAN;
|
result BOOLEAN;
|
||||||
is_area BOOLEAN;
|
|
||||||
country_code VARCHAR(2);
|
country_code VARCHAR(2);
|
||||||
default_language VARCHAR(10);
|
default_language VARCHAR(10);
|
||||||
diameter FLOAT;
|
diameter FLOAT;
|
||||||
classtable TEXT;
|
classtable TEXT;
|
||||||
classtype TEXT;
|
line RECORD;
|
||||||
BEGIN
|
BEGIN
|
||||||
--DEBUG: RAISE WARNING '% % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type;
|
--DEBUG: RAISE WARNING '% % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type;
|
||||||
|
|
||||||
@@ -855,10 +848,11 @@ BEGIN
|
|||||||
IF NEW.osm_type = 'X' THEN
|
IF NEW.osm_type = 'X' THEN
|
||||||
-- E'X'ternal records should already be in the right format so do nothing
|
-- E'X'ternal records should already be in the right format so do nothing
|
||||||
ELSE
|
ELSE
|
||||||
is_area := ST_GeometryType(NEW.geometry) IN ('ST_Polygon','ST_MultiPolygon');
|
NEW.rank_search := 30;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
|
||||||
IF NEW.class in ('place','boundary')
|
-- By doing in postgres we have the country available to us - currently only used for postcode
|
||||||
AND NEW.type in ('postcode','postal_code') THEN
|
IF NEW.class in ('place','boundary') AND NEW.type in ('postcode','postal_code') THEN
|
||||||
|
|
||||||
IF NEW.address IS NULL OR NOT NEW.address ? 'postcode' THEN
|
IF NEW.address IS NULL OR NOT NEW.address ? 'postcode' THEN
|
||||||
-- most likely just a part of a multipolygon postcode boundary, throw it away
|
-- most likely just a part of a multipolygon postcode boundary, throw it away
|
||||||
@@ -870,70 +864,145 @@ 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 is_area THEN
|
IF NOT ST_GeometryType(NEW.geometry) IN ('ST_Polygon','ST_MultiPolygon') THEN
|
||||||
NEW.rank_address := 0;
|
NEW.rank_address := 0;
|
||||||
END IF;
|
END IF;
|
||||||
ELSEIF NEW.class = 'boundary' AND NOT is_area THEN
|
|
||||||
return NULL;
|
|
||||||
ELSEIF NEW.class = 'boundary' AND NEW.type = 'administrative'
|
|
||||||
AND NEW.admin_level <= 4 AND NEW.osm_type = 'W' THEN
|
|
||||||
return NULL;
|
|
||||||
ELSEIF NEW.class = 'railway' AND NEW.type in ('rail') THEN
|
|
||||||
return NULL;
|
|
||||||
ELSEIF NEW.osm_type = 'N' AND NEW.class = 'highway' THEN
|
|
||||||
NEW.rank_search = 30;
|
|
||||||
NEW.rank_address = 0;
|
|
||||||
ELSEIF NEW.class = 'landuse' AND NOT is_area THEN
|
|
||||||
NEW.rank_search = 30;
|
|
||||||
NEW.rank_address = 0;
|
|
||||||
ELSE
|
|
||||||
-- do table lookup stuff
|
|
||||||
IF NEW.class = 'boundary' and NEW.type = 'administrative' THEN
|
|
||||||
classtype = NEW.type || NEW.admin_level::TEXT;
|
|
||||||
ELSE
|
|
||||||
classtype = NEW.type;
|
|
||||||
END IF;
|
|
||||||
SELECT l.rank_search, l.rank_address FROM address_levels l
|
|
||||||
WHERE (l.country_code = NEW.country_code or l.country_code is NULL)
|
|
||||||
AND l.class = NEW.class AND (l.type = classtype or l.type is NULL)
|
|
||||||
ORDER BY l.country_code, l.class, l.type LIMIT 1
|
|
||||||
INTO NEW.rank_search, NEW.rank_address;
|
|
||||||
|
|
||||||
IF NEW.rank_search is NULL THEN
|
ELSEIF NEW.class = 'place' THEN
|
||||||
|
IF NEW.type in ('continent') THEN
|
||||||
|
NEW.rank_search := 2;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
NEW.country_code := NULL;
|
||||||
|
ELSEIF NEW.type in ('sea') THEN
|
||||||
|
NEW.rank_search := 2;
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
NEW.country_code := NULL;
|
||||||
|
ELSEIF NEW.type in ('country') THEN
|
||||||
|
NEW.rank_search := 4;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSEIF NEW.type in ('state') THEN
|
||||||
|
NEW.rank_search := 8;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSEIF NEW.type in ('region') THEN
|
||||||
|
NEW.rank_search := 18; -- dropped from previous value of 10
|
||||||
|
NEW.rank_address := 0; -- So badly miss-used that better to just drop it!
|
||||||
|
ELSEIF NEW.type in ('county') THEN
|
||||||
|
NEW.rank_search := 12;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSEIF NEW.type in ('city') THEN
|
||||||
|
NEW.rank_search := 16;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSEIF NEW.type in ('island') THEN
|
||||||
|
NEW.rank_search := 17;
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
ELSEIF NEW.type in ('town') THEN
|
||||||
|
NEW.rank_search := 18;
|
||||||
|
NEW.rank_address := 16;
|
||||||
|
ELSEIF NEW.type in ('village','hamlet','municipality','district','unincorporated_area','borough') THEN
|
||||||
|
NEW.rank_search := 19;
|
||||||
|
NEW.rank_address := 16;
|
||||||
|
ELSEIF NEW.type in ('suburb','croft','subdivision','isolated_dwelling') THEN
|
||||||
|
NEW.rank_search := 20;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSEIF NEW.type in ('farm','locality','islet','mountain_pass') THEN
|
||||||
|
NEW.rank_search := 20;
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
-- Irish townlands, tagged as place=locality and locality=townland
|
||||||
|
IF (NEW.extratags -> 'locality') = 'townland' THEN
|
||||||
|
NEW.rank_address := 20;
|
||||||
|
END IF;
|
||||||
|
ELSEIF NEW.type in ('neighbourhood') THEN
|
||||||
|
NEW.rank_search := 22;
|
||||||
|
NEW.rank_address := 22;
|
||||||
|
ELSEIF NEW.type in ('house','building') THEN
|
||||||
|
NEW.rank_search := 30;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSEIF NEW.type in ('houses') THEN
|
||||||
|
-- can't guarantee all required nodes loaded yet due to caching in osm2pgsql
|
||||||
|
NEW.rank_search := 28;
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
ELSEIF NEW.class = 'boundary' THEN
|
||||||
|
IF ST_GeometryType(NEW.geometry) NOT IN ('ST_Polygon','ST_MultiPolygon') THEN
|
||||||
|
-- RAISE WARNING 'invalid boundary %',NEW.osm_id;
|
||||||
|
return NULL;
|
||||||
|
END IF;
|
||||||
|
NEW.rank_search := NEW.admin_level * 2;
|
||||||
|
IF NEW.type = 'administrative' THEN
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSE
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
END IF;
|
||||||
|
ELSEIF NEW.class = 'landuse' AND ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') THEN
|
||||||
|
NEW.rank_search := 22;
|
||||||
|
IF NEW.type in ('residential', 'farm', 'farmyard', 'industrial', 'commercial', 'allotments', 'retail') THEN
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSE
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
END IF;
|
||||||
|
ELSEIF NEW.class = 'leisure' and NEW.type in ('park') THEN
|
||||||
|
NEW.rank_search := 24;
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
ELSEIF NEW.class = 'natural' and NEW.type in ('peak','volcano','mountain_range') THEN
|
||||||
|
NEW.rank_search := 18;
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
ELSEIF NEW.class = 'natural' and NEW.type = 'sea' THEN
|
||||||
|
NEW.rank_search := 4;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
-- any feature more than 5 square miles is probably worth indexing
|
||||||
|
ELSEIF ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') AND ST_Area(NEW.geometry) > 0.1 THEN
|
||||||
|
NEW.rank_search := 22;
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
ELSEIF NEW.class = 'railway' AND NEW.type in ('rail') THEN
|
||||||
|
RETURN NULL;
|
||||||
|
ELSEIF NEW.class = 'waterway' THEN
|
||||||
|
IF NEW.osm_type = 'R' THEN
|
||||||
|
NEW.rank_search := 16;
|
||||||
|
ELSE
|
||||||
|
NEW.rank_search := 17;
|
||||||
|
END IF;
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
ELSEIF NEW.class = 'highway' AND NEW.osm_type != 'N' AND NEW.type in ('service','cycleway','path','footway','steps','bridleway','motorway_link','primary_link','trunk_link','secondary_link','tertiary_link') THEN
|
||||||
|
NEW.rank_search := 27;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSEIF NEW.class = 'highway' AND NEW.osm_type != 'N' THEN
|
||||||
|
NEW.rank_search := 26;
|
||||||
|
NEW.rank_address := NEW.rank_search;
|
||||||
|
ELSEIF NEW.class = 'mountain_pass' THEN
|
||||||
|
NEW.rank_search := 20;
|
||||||
|
NEW.rank_address := 0;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NEW.rank_search > 30 THEN
|
||||||
NEW.rank_search := 30;
|
NEW.rank_search := 30;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF NEW.rank_address is NULL THEN
|
IF NEW.rank_address > 30 THEN
|
||||||
NEW.rank_address := 30;
|
NEW.rank_address := 30;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- some postcorrections
|
|
||||||
IF NEW.class = 'waterway' AND NEW.osm_type = 'R' THEN
|
|
||||||
-- Slightly promote waterway relations so that they are processed
|
|
||||||
-- before their members.
|
|
||||||
NEW.rank_search := NEW.rank_search - 1;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF (NEW.extratags -> 'capital') = 'yes' THEN
|
IF (NEW.extratags -> 'capital') = 'yes' THEN
|
||||||
NEW.rank_search := NEW.rank_search - 1;
|
NEW.rank_search := NEW.rank_search - 1;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- a country code make no sense below rank 4 (country)
|
-- a country code make no sense below rank 4 (country)
|
||||||
IF NEW.rank_search < 4 THEN
|
IF NEW.rank_search < 4 THEN
|
||||||
NEW.country_code := NULL;
|
NEW.country_code := NULL;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
-- Block import below rank 22
|
||||||
|
-- IF NEW.rank_search > 22 THEN
|
||||||
|
-- RETURN NULL;
|
||||||
|
-- END IF;
|
||||||
|
|
||||||
--DEBUG: RAISE WARNING 'placex_insert:END: % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type;
|
--DEBUG: RAISE WARNING 'placex_insert:END: % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type;
|
||||||
|
|
||||||
RETURN NEW; -- %DIFFUPDATES% The following is not needed until doing diff updates, and slows the main index process down
|
RETURN NEW; -- %DIFFUPDATES% The following is not needed until doing diff updates, and slows the main index process down
|
||||||
|
|
||||||
IF NEW.osm_type = 'N' and NEW.rank_search > 28 THEN
|
IF NEW.rank_address > 0 THEN
|
||||||
-- might be part of an interpolation
|
|
||||||
result := osmline_reinsert(NEW.osm_id, NEW.geometry);
|
|
||||||
ELSEIF NEW.rank_address > 0 THEN
|
|
||||||
IF (ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') AND ST_IsValid(NEW.geometry)) THEN
|
IF (ST_GeometryType(NEW.geometry) in ('ST_Polygon','ST_MultiPolygon') AND ST_IsValid(NEW.geometry)) THEN
|
||||||
-- Performance: We just can't handle re-indexing for country level changes
|
-- Performance: We just can't handle re-indexing for country level changes
|
||||||
IF st_area(NEW.geometry) < 1 THEN
|
IF st_area(NEW.geometry) < 1 THEN
|
||||||
@@ -1178,7 +1247,6 @@ TRIGGER
|
|||||||
DECLARE
|
DECLARE
|
||||||
|
|
||||||
place_centroid GEOMETRY;
|
place_centroid GEOMETRY;
|
||||||
near_centroid GEOMETRY;
|
|
||||||
|
|
||||||
search_maxdistance FLOAT[];
|
search_maxdistance FLOAT[];
|
||||||
search_mindistance FLOAT[];
|
search_mindistance FLOAT[];
|
||||||
@@ -1238,9 +1306,6 @@ BEGIN
|
|||||||
|
|
||||||
NEW.indexed_date = now();
|
NEW.indexed_date = now();
|
||||||
|
|
||||||
IF NOT %REVERSE-ONLY% THEN
|
|
||||||
DELETE from search_name WHERE place_id = NEW.place_id;
|
|
||||||
END IF;
|
|
||||||
result := deleteSearchName(NEW.partition, NEW.place_id);
|
result := deleteSearchName(NEW.partition, NEW.place_id);
|
||||||
DELETE FROM place_addressline WHERE place_id = NEW.place_id;
|
DELETE FROM place_addressline WHERE place_id = NEW.place_id;
|
||||||
result := deleteRoad(NEW.partition, NEW.place_id);
|
result := deleteRoad(NEW.partition, NEW.place_id);
|
||||||
@@ -1255,8 +1320,6 @@ BEGIN
|
|||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
--DEBUG: RAISE WARNING 'Copy over address tags';
|
--DEBUG: RAISE WARNING 'Copy over address tags';
|
||||||
-- housenumber is a computed field, so start with an empty value
|
|
||||||
NEW.housenumber := NULL;
|
|
||||||
IF NEW.address is not NULL THEN
|
IF NEW.address is not NULL THEN
|
||||||
IF NEW.address ? 'conscriptionnumber' THEN
|
IF NEW.address ? 'conscriptionnumber' THEN
|
||||||
i := getorcreate_housenumber_id(make_standard_name(NEW.address->'conscriptionnumber'));
|
i := getorcreate_housenumber_id(make_standard_name(NEW.address->'conscriptionnumber'));
|
||||||
@@ -1285,8 +1348,6 @@ BEGIN
|
|||||||
-- Speed up searches - just use the centroid of the feature
|
-- Speed up searches - just use the centroid of the feature
|
||||||
-- cheaper but less acurate
|
-- cheaper but less acurate
|
||||||
place_centroid := ST_PointOnSurface(NEW.geometry);
|
place_centroid := ST_PointOnSurface(NEW.geometry);
|
||||||
-- For searching near features rather use the centroid
|
|
||||||
near_centroid := ST_Envelope(NEW.geometry);
|
|
||||||
NEW.centroid := null;
|
NEW.centroid := null;
|
||||||
NEW.postcode := null;
|
NEW.postcode := null;
|
||||||
--DEBUG: RAISE WARNING 'Computing preliminary centroid at %',ST_AsText(place_centroid);
|
--DEBUG: RAISE WARNING 'Computing preliminary centroid at %',ST_AsText(place_centroid);
|
||||||
@@ -1332,6 +1393,10 @@ BEGIN
|
|||||||
--DEBUG: RAISE WARNING 'Waterway processed';
|
--DEBUG: RAISE WARNING 'Waterway processed';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
-- Adding ourselves to the list simplifies address calculations later
|
||||||
|
INSERT INTO place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address)
|
||||||
|
VALUES (NEW.place_id, NEW.place_id, true, true, 0, NEW.rank_address);
|
||||||
|
|
||||||
-- What level are we searching from
|
-- What level are we searching from
|
||||||
search_maxrank := NEW.rank_search;
|
search_maxrank := NEW.rank_search;
|
||||||
|
|
||||||
@@ -1417,7 +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
|
||||||
SELECT place_id from getNearestNamedRoadFeature(NEW.partition, near_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
SELECT place_id from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
||||||
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;
|
||||||
@@ -1425,7 +1490,7 @@ 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
|
||||||
SELECT place_id from getNearestNamedPlaceFeature(NEW.partition, near_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
SELECT place_id from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
||||||
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;
|
||||||
@@ -1460,7 +1525,7 @@ BEGIN
|
|||||||
IF location.address ? 'street' THEN
|
IF location.address ? 'street' THEN
|
||||||
address_street_word_ids := get_name_ids(make_standard_name(location.address->'street'));
|
address_street_word_ids := get_name_ids(make_standard_name(location.address->'street'));
|
||||||
IF address_street_word_ids IS NOT NULL THEN
|
IF address_street_word_ids IS NOT NULL THEN
|
||||||
SELECT place_id from getNearestNamedRoadFeature(NEW.partition, near_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
SELECT place_id from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
||||||
EXIT WHEN NEW.parent_place_id is not NULL;
|
EXIT WHEN NEW.parent_place_id is not NULL;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
@@ -1469,7 +1534,7 @@ BEGIN
|
|||||||
IF 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
|
||||||
SELECT place_id from getNearestNamedPlaceFeature(NEW.partition, near_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
SELECT place_id from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) INTO NEW.parent_place_id;
|
||||||
EXIT WHEN NEW.parent_place_id is not NULL;
|
EXIT WHEN NEW.parent_place_id is not NULL;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
@@ -1498,7 +1563,7 @@ BEGIN
|
|||||||
|
|
||||||
-- 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
|
||||||
SELECT place_id FROM getNearestRoadFeature(NEW.partition, near_centroid) INTO NEW.parent_place_id;
|
SELECT place_id FROM getNearestRoadFeature(NEW.partition, place_centroid) INTO NEW.parent_place_id;
|
||||||
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;
|
||||||
|
|
||||||
@@ -1507,9 +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 p.country_code, p.postcode FROM placex p
|
select s.country_code, s.name_vector, s.nameaddress_vector from search_name s
|
||||||
WHERE p.place_id = NEW.parent_place_id INTO location;
|
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';
|
||||||
|
|
||||||
@@ -1518,10 +1582,10 @@ BEGIN
|
|||||||
IF NEW.address is not null AND NEW.address ? 'postcode' THEN
|
IF NEW.address is not null AND NEW.address ? 'postcode' THEN
|
||||||
NEW.postcode = upper(trim(NEW.address->'postcode'));
|
NEW.postcode = upper(trim(NEW.address->'postcode'));
|
||||||
ELSE
|
ELSE
|
||||||
NEW.postcode := location.postcode;
|
SELECT postcode FROM placex WHERE place_id = NEW.parent_place_id INTO NEW.postcode;
|
||||||
END IF;
|
END IF;
|
||||||
IF NEW.postcode is null THEN
|
IF NEW.postcode is null THEN
|
||||||
NEW.postcode := get_nearest_postcode(NEW.country_code, NEW.geometry);
|
NEW.postcode := get_nearest_postcode(NEW.country_code, place_centroid);
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
@@ -1531,34 +1595,21 @@ BEGIN
|
|||||||
return NEW;
|
return NEW;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
-- Merge address from parent
|
||||||
|
nameaddress_vector := array_merge(nameaddress_vector, location.nameaddress_vector);
|
||||||
|
nameaddress_vector := array_merge(nameaddress_vector, location.name_vector);
|
||||||
|
|
||||||
-- Performance, it would be more acurate to do all the rest of the import
|
-- Performance, it would be more acurate to do all the rest of the import
|
||||||
-- process but it takes too long
|
-- process but it takes too long
|
||||||
-- Just be happy with inheriting from parent road only
|
-- Just be happy with inheriting from parent road only
|
||||||
|
|
||||||
IF NEW.rank_search <= 25 and NEW.rank_address > 0 THEN
|
IF NEW.rank_search <= 25 and NEW.rank_address > 0 THEN
|
||||||
result := add_location(NEW.place_id, NEW.country_code, NEW.partition, name_vector, NEW.rank_search, NEW.rank_address, upper(trim(NEW.address->'postcode')), NEW.geometry);
|
result := add_location(NEW.place_id, NEW.country_code, NEW.partition, name_vector, NEW.rank_search, NEW.rank_address, upper(trim(NEW.address->'postcode')), NEW.geometry);
|
||||||
--DEBUG: RAISE WARNING 'Place added to location table';
|
--DEBUG: RAISE WARNING 'Place added to location table';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
result := insertSearchName(NEW.partition, NEW.place_id, name_vector,
|
result := insertSearchName(NEW.partition, NEW.place_id, NEW.country_code, name_vector, nameaddress_vector, NEW.rank_search, NEW.rank_address, NEW.importance, place_centroid, NEW.geometry);
|
||||||
NEW.rank_search, NEW.rank_address, NEW.geometry);
|
|
||||||
|
|
||||||
IF NOT %REVERSE-ONLY% THEN
|
|
||||||
-- Merge address from parent
|
|
||||||
SELECT s.name_vector, s.nameaddress_vector FROM search_name s
|
|
||||||
WHERE s.place_id = NEW.parent_place_id INTO location;
|
|
||||||
|
|
||||||
nameaddress_vector := array_merge(nameaddress_vector,
|
|
||||||
location.nameaddress_vector);
|
|
||||||
nameaddress_vector := array_merge(nameaddress_vector, location.name_vector);
|
|
||||||
|
|
||||||
INSERT INTO search_name (place_id, search_rank, address_rank,
|
|
||||||
importance, country_code, name_vector,
|
|
||||||
nameaddress_vector, centroid)
|
|
||||||
VALUES (NEW.place_id, NEW.rank_search, NEW.rank_address,
|
|
||||||
NEW.importance, NEW.country_code, name_vector,
|
|
||||||
nameaddress_vector, place_centroid);
|
|
||||||
--DEBUG: RAISE WARNING 'Place added to search table';
|
--DEBUG: RAISE WARNING 'Place added to search table';
|
||||||
END IF;
|
|
||||||
|
|
||||||
return NEW;
|
return NEW;
|
||||||
END IF;
|
END IF;
|
||||||
@@ -1744,13 +1795,11 @@ BEGIN
|
|||||||
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
|
||||||
isin_tokens := isin_tokens || address_street_word_id;
|
isin_tokens := isin_tokens || address_street_word_id;
|
||||||
END IF;
|
END IF;
|
||||||
IF NOT %REVERSE-ONLY% THEN
|
|
||||||
address_street_word_id := get_word_id(make_standard_name(addr_item.value));
|
address_street_word_id := get_word_id(make_standard_name(addr_item.value));
|
||||||
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 IF;
|
END IF;
|
||||||
END IF;
|
|
||||||
IF addr_item.key = 'is_in' THEN
|
IF addr_item.key = 'is_in' THEN
|
||||||
-- is_in items need splitting
|
-- is_in items need splitting
|
||||||
isin := regexp_split_to_array(addr_item.value, E'[;,]');
|
isin := regexp_split_to_array(addr_item.value, E'[;,]');
|
||||||
@@ -1762,20 +1811,16 @@ BEGIN
|
|||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- merge word into address vector
|
-- merge word into address vector
|
||||||
IF NOT %REVERSE-ONLY% THEN
|
|
||||||
address_street_word_id := get_word_id(make_standard_name(isin[i]));
|
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 IF;
|
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END IF;
|
END IF;
|
||||||
IF NOT %REVERSE-ONLY% THEN
|
|
||||||
nameaddress_vector := array_merge(nameaddress_vector, isin_tokens);
|
nameaddress_vector := array_merge(nameaddress_vector, isin_tokens);
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- RAISE WARNING 'ISIN: %', isin_tokens;
|
-- RAISE WARNING 'ISIN: %', isin_tokens;
|
||||||
|
|
||||||
@@ -1824,7 +1869,7 @@ BEGIN
|
|||||||
|
|
||||||
-- RAISE WARNING '% isaddress: %', location.place_id, location_isaddress;
|
-- RAISE WARNING '% isaddress: %', location.place_id, location_isaddress;
|
||||||
-- Add it to the list of search terms
|
-- Add it to the list of search terms
|
||||||
IF NOT %REVERSE-ONLY% THEN
|
IF location.rank_search > 4 THEN
|
||||||
nameaddress_vector := array_merge(nameaddress_vector, location.keywords::integer[]);
|
nameaddress_vector := array_merge(nameaddress_vector, location.keywords::integer[]);
|
||||||
END IF;
|
END IF;
|
||||||
INSERT INTO place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address)
|
INSERT INTO place_addressline (place_id, address_place_id, fromarea, isaddress, distance, cached_rank_address)
|
||||||
@@ -1878,18 +1923,8 @@ BEGIN
|
|||||||
--DEBUG: RAISE WARNING 'insert into road location table (full)';
|
--DEBUG: RAISE WARNING 'insert into road location table (full)';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
result := insertSearchName(NEW.partition, NEW.place_id, name_vector,
|
result := insertSearchName(NEW.partition, NEW.place_id, NEW.country_code, name_vector, nameaddress_vector, NEW.rank_search, NEW.rank_address, NEW.importance, place_centroid, NEW.geometry);
|
||||||
NEW.rank_search, NEW.rank_address, NEW.geometry);
|
--DEBUG: RAISE WARNING 'added to serach name (full)';
|
||||||
--DEBUG: RAISE WARNING 'added to search name (full)';
|
|
||||||
|
|
||||||
IF NOT %REVERSE-ONLY% THEN
|
|
||||||
INSERT INTO search_name (place_id, search_rank, address_rank,
|
|
||||||
importance, country_code, name_vector,
|
|
||||||
nameaddress_vector, centroid)
|
|
||||||
VALUES (NEW.place_id, NEW.rank_search, NEW.rank_address,
|
|
||||||
NEW.importance, NEW.country_code, name_vector,
|
|
||||||
nameaddress_vector, place_centroid);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
@@ -1948,9 +1983,6 @@ BEGIN
|
|||||||
--DEBUG: RAISE WARNING 'placex_delete:09 % %',OLD.osm_type,OLD.osm_id;
|
--DEBUG: RAISE WARNING 'placex_delete:09 % %',OLD.osm_type,OLD.osm_id;
|
||||||
|
|
||||||
IF OLD.name is not null THEN
|
IF OLD.name is not null THEN
|
||||||
IF NOT %REVERSE-ONLY% THEN
|
|
||||||
DELETE from search_name WHERE place_id = OLD.place_id;
|
|
||||||
END IF;
|
|
||||||
b := deleteSearchName(OLD.partition, OLD.place_id);
|
b := deleteSearchName(OLD.partition, OLD.place_id);
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
@@ -2225,13 +2257,12 @@ BEGIN
|
|||||||
indexed_status = 2,
|
indexed_status = 2,
|
||||||
geometry = NEW.geometry
|
geometry = NEW.geometry
|
||||||
where place_id = existingplacex.place_id;
|
where place_id = existingplacex.place_id;
|
||||||
|
|
||||||
-- if a node(=>house), which is part of a interpolation line, changes (e.g. the street attribute) => mark this line for reparenting
|
-- if a node(=>house), which is part of a interpolation line, changes (e.g. the street attribute) => mark this line for reparenting
|
||||||
-- (already here, because interpolation lines are reindexed before nodes, so in the second call it would be too late)
|
-- (already here, because interpolation lines are reindexed before nodes, so in the second call it would be too late)
|
||||||
IF NEW.osm_type='N'
|
IF NEW.osm_type='N' and NEW.class='place' and NEW.type='house' THEN
|
||||||
and (coalesce(existing.address, ''::hstore) != coalesce(NEW.address, ''::hstore)
|
-- Is this node part of an interpolation line? search for it in location_property_osmline and mark the interpolation line for reparenting
|
||||||
or existing.geometry::text != NEW.geometry::text)
|
update location_property_osmline p set indexed_status = 2 from planet_osm_ways w where p.linegeo && NEW.geometry and p.osm_id = w.id and NEW.osm_id = any(w.nodes);
|
||||||
THEN
|
|
||||||
result:= osmline_reinsert(NEW.osm_id, NEW.geometry);
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- linked places should get potential new naming and addresses
|
-- linked places should get potential new naming and addresses
|
||||||
@@ -2323,9 +2354,6 @@ create type addressline as (
|
|||||||
distance FLOAT
|
distance FLOAT
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Compute the list of address parts for the given place.
|
|
||||||
--
|
|
||||||
-- If in_housenumber is greator or equal 0, look for an interpolation.
|
|
||||||
CREATE OR REPLACE FUNCTION get_addressdata(in_place_id BIGINT, in_housenumber INTEGER) RETURNS setof addressline
|
CREATE OR REPLACE FUNCTION get_addressdata(in_place_id BIGINT, in_housenumber INTEGER) RETURNS setof addressline
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
@@ -2340,72 +2368,53 @@ DECLARE
|
|||||||
searchhousename HSTORE;
|
searchhousename HSTORE;
|
||||||
searchrankaddress INTEGER;
|
searchrankaddress INTEGER;
|
||||||
searchpostcode TEXT;
|
searchpostcode TEXT;
|
||||||
postcode_isaddress BOOL;
|
|
||||||
searchclass TEXT;
|
searchclass TEXT;
|
||||||
searchtype TEXT;
|
searchtype TEXT;
|
||||||
countryname HSTORE;
|
countryname HSTORE;
|
||||||
|
hadcountry BOOLEAN;
|
||||||
BEGIN
|
BEGIN
|
||||||
-- The place ein question might not have a direct entry in place_addressline.
|
|
||||||
-- Look for the parent of such places then and save if in for_place_id.
|
|
||||||
|
|
||||||
postcode_isaddress := true;
|
|
||||||
|
|
||||||
-- first query osmline (interpolation lines)
|
-- first query osmline (interpolation lines)
|
||||||
IF in_housenumber >= 0 THEN
|
select parent_place_id, country_code, 30, postcode, null, 'place', 'house' from location_property_osmline
|
||||||
SELECT parent_place_id, country_code, in_housenumber::text, 30, postcode,
|
WHERE place_id = in_place_id AND in_housenumber>=startnumber AND in_housenumber <= endnumber
|
||||||
null, 'place', 'house'
|
INTO for_place_id,searchcountrycode, searchrankaddress, searchpostcode, searchhousename, searchclass, searchtype;
|
||||||
FROM location_property_osmline
|
IF for_place_id IS NOT NULL THEN
|
||||||
WHERE place_id = in_place_id AND in_housenumber>=startnumber
|
searchhousenumber = in_housenumber::text;
|
||||||
AND in_housenumber <= endnumber
|
|
||||||
INTO for_place_id, searchcountrycode, searchhousenumber, searchrankaddress,
|
|
||||||
searchpostcode, searchhousename, searchclass, searchtype;
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
--then query tiger data
|
--then query tiger data
|
||||||
-- %NOTIGERDATA% IF 0 THEN
|
-- %NOTIGERDATA% IF 0 THEN
|
||||||
IF for_place_id IS NULL AND in_housenumber >= 0 THEN
|
IF for_place_id IS NULL THEN
|
||||||
SELECT parent_place_id, 'us', in_housenumber::text, 30, postcode, null,
|
select parent_place_id,'us', 30, postcode, null, 'place', 'house' from location_property_tiger
|
||||||
'place', 'house'
|
WHERE place_id = in_place_id AND in_housenumber>=startnumber AND in_housenumber <= endnumber
|
||||||
FROM location_property_tiger
|
INTO for_place_id,searchcountrycode, searchrankaddress, searchpostcode, searchhousename, searchclass, searchtype;
|
||||||
WHERE place_id = in_place_id AND in_housenumber >= startnumber
|
IF for_place_id IS NOT NULL THEN
|
||||||
AND in_housenumber <= endnumber
|
searchhousenumber = in_housenumber::text;
|
||||||
INTO for_place_id, searchcountrycode, searchhousenumber, searchrankaddress,
|
END IF;
|
||||||
searchpostcode, searchhousename, searchclass, searchtype;
|
|
||||||
END IF;
|
END IF;
|
||||||
-- %NOTIGERDATA% END IF;
|
-- %NOTIGERDATA% END IF;
|
||||||
|
|
||||||
-- %NOAUXDATA% IF 0 THEN
|
-- %NOAUXDATA% IF 0 THEN
|
||||||
IF for_place_id IS NULL THEN
|
IF for_place_id IS NULL THEN
|
||||||
SELECT parent_place_id, 'us', housenumber, 30, postcode, null, 'place', 'house'
|
select parent_place_id,'us', housenumber, 30, postcode, null, 'place', 'house' from location_property_aux
|
||||||
FROM location_property_aux
|
|
||||||
WHERE place_id = in_place_id
|
WHERE place_id = in_place_id
|
||||||
INTO for_place_id,searchcountrycode, searchhousenumber, searchrankaddress,
|
INTO for_place_id,searchcountrycode, searchhousenumber, searchrankaddress, searchpostcode, searchhousename, searchclass, searchtype;
|
||||||
searchpostcode, searchhousename, searchclass, searchtype;
|
|
||||||
END IF;
|
END IF;
|
||||||
-- %NOAUXDATA% END IF;
|
-- %NOAUXDATA% END IF;
|
||||||
|
|
||||||
-- postcode table
|
-- postcode table
|
||||||
IF for_place_id IS NULL THEN
|
IF for_place_id IS NULL THEN
|
||||||
SELECT parent_place_id, country_code, rank_search, postcode, 'place', 'postcode'
|
select parent_place_id, country_code, rank_address, postcode, 'place', 'postcode'
|
||||||
FROM location_postcode
|
FROM location_postcode
|
||||||
WHERE place_id = in_place_id
|
WHERE place_id = in_place_id
|
||||||
INTO for_place_id, searchcountrycode, searchrankaddress, searchpostcode,
|
INTO for_place_id, searchcountrycode, searchrankaddress, searchpostcode, searchclass, searchtype;
|
||||||
searchclass, searchtype;
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- POI objects in the placex table
|
|
||||||
IF for_place_id IS NULL THEN
|
IF for_place_id IS NULL THEN
|
||||||
SELECT parent_place_id, country_code, housenumber, rank_search, postcode,
|
select parent_place_id, country_code, housenumber, rank_search, postcode, name, class, type from placex
|
||||||
name, class, type
|
|
||||||
FROM placex
|
|
||||||
WHERE place_id = in_place_id and rank_search > 27
|
WHERE place_id = in_place_id and rank_search > 27
|
||||||
INTO for_place_id, searchcountrycode, searchhousenumber, searchrankaddress,
|
INTO for_place_id, searchcountrycode, searchhousenumber, searchrankaddress, searchpostcode, searchhousename, searchclass, searchtype;
|
||||||
searchpostcode, searchhousename, searchclass, searchtype;
|
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- If for_place_id is still NULL at this point then the object has its own
|
|
||||||
-- entry in place_address line. However, still check if there is not linked
|
|
||||||
-- place we should be using instead.
|
|
||||||
IF for_place_id IS NULL THEN
|
IF for_place_id IS NULL THEN
|
||||||
select coalesce(linked_place_id, place_id), country_code,
|
select coalesce(linked_place_id, place_id), country_code,
|
||||||
housenumber, rank_search, postcode, null
|
housenumber, rank_search, postcode, null
|
||||||
@@ -2415,105 +2424,103 @@ BEGIN
|
|||||||
|
|
||||||
--RAISE WARNING '% % % %',searchcountrycode, searchhousenumber, searchrankaddress, searchpostcode;
|
--RAISE WARNING '% % % %',searchcountrycode, searchhousenumber, searchrankaddress, searchpostcode;
|
||||||
|
|
||||||
found := 1000; -- the lowest rank_address included
|
found := 1000;
|
||||||
|
hadcountry := false;
|
||||||
-- Return the record for the base entry.
|
|
||||||
FOR location IN
|
FOR location IN
|
||||||
SELECT placex.place_id, osm_type, osm_id, name,
|
select placex.place_id, osm_type, osm_id, name,
|
||||||
class, type, admin_level,
|
class, type, admin_level, true as isaddress,
|
||||||
type not in ('postcode', 'postal_code') as isaddress,
|
CASE WHEN rank_address = 0 THEN 100 WHEN rank_address = 11 THEN 5 ELSE rank_address END as rank_address,
|
||||||
CASE WHEN rank_address = 0 THEN 100
|
|
||||||
WHEN rank_address = 11 THEN 5
|
|
||||||
ELSE rank_address END as rank_address,
|
|
||||||
0 as distance, country_code, postcode
|
0 as distance, country_code, postcode
|
||||||
FROM placex
|
from placex
|
||||||
WHERE place_id = for_place_id
|
where place_id = for_place_id
|
||||||
LOOP
|
|
||||||
--RAISE WARNING '%',location;
|
|
||||||
IF searchcountrycode IS NULL AND location.country_code IS NOT NULL THEN
|
|
||||||
searchcountrycode := location.country_code;
|
|
||||||
END IF;
|
|
||||||
IF location.rank_address < 4 THEN
|
|
||||||
-- no country locations for ranks higher than country
|
|
||||||
searchcountrycode := NULL;
|
|
||||||
END IF;
|
|
||||||
countrylocation := ROW(location.place_id, location.osm_type, location.osm_id,
|
|
||||||
location.name, location.class, location.type,
|
|
||||||
location.admin_level, true, location.isaddress,
|
|
||||||
location.rank_address, location.distance)::addressline;
|
|
||||||
RETURN NEXT countrylocation;
|
|
||||||
found := location.rank_address;
|
|
||||||
END LOOP;
|
|
||||||
|
|
||||||
FOR location IN
|
|
||||||
SELECT placex.place_id, osm_type, osm_id, name,
|
|
||||||
CASE WHEN extratags ? 'place' THEN 'place' ELSE class END as class,
|
|
||||||
CASE WHEN extratags ? 'place' THEN extratags->'place' ELSE type END as type,
|
|
||||||
admin_level, fromarea, isaddress,
|
|
||||||
CASE WHEN rank_address = 11 THEN 5 ELSE rank_address END as rank_address,
|
|
||||||
distance, country_code, postcode
|
|
||||||
FROM place_addressline join placex on (address_place_id = placex.place_id)
|
|
||||||
WHERE place_addressline.place_id = for_place_id
|
|
||||||
AND (cached_rank_address >= 4 AND cached_rank_address < searchrankaddress)
|
|
||||||
AND linked_place_id is null
|
|
||||||
AND (placex.country_code IS NULL OR searchcountrycode IS NULL
|
|
||||||
OR placex.country_code = searchcountrycode)
|
|
||||||
ORDER BY rank_address desc, isaddress desc, fromarea desc,
|
|
||||||
distance asc, rank_search desc
|
|
||||||
LOOP
|
LOOP
|
||||||
--RAISE WARNING '%',location;
|
--RAISE WARNING '%',location;
|
||||||
IF searchcountrycode IS NULL AND location.country_code IS NOT NULL THEN
|
IF searchcountrycode IS NULL AND location.country_code IS NOT NULL THEN
|
||||||
searchcountrycode := location.country_code;
|
searchcountrycode := location.country_code;
|
||||||
END IF;
|
END IF;
|
||||||
IF location.type in ('postcode', 'postal_code') THEN
|
IF location.type in ('postcode', 'postal_code') THEN
|
||||||
postcode_isaddress := false;
|
|
||||||
IF location.osm_type != 'R' THEN
|
|
||||||
location.isaddress := FALSE;
|
location.isaddress := FALSE;
|
||||||
|
ELSEIF location.rank_address = 4 THEN
|
||||||
|
hadcountry := true;
|
||||||
|
END IF;
|
||||||
|
IF location.rank_address < 4 AND NOT hadcountry THEN
|
||||||
|
select name from country_name where country_code = searchcountrycode limit 1 INTO countryname;
|
||||||
|
IF countryname IS NOT NULL THEN
|
||||||
|
countrylocation := ROW(null, null, null, countryname, 'place', 'country', null, true, true, 4, 0)::addressline;
|
||||||
|
RETURN NEXT countrylocation;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
countrylocation := ROW(location.place_id, location.osm_type, location.osm_id,
|
countrylocation := ROW(location.place_id, location.osm_type, location.osm_id, location.name, location.class,
|
||||||
location.name, location.class, location.type,
|
location.type, location.admin_level, true, location.isaddress, location.rank_address,
|
||||||
location.admin_level, location.fromarea,
|
location.distance)::addressline;
|
||||||
location.isaddress, location.rank_address,
|
RETURN NEXT countrylocation;
|
||||||
|
found := location.rank_address;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
FOR location IN
|
||||||
|
select placex.place_id, osm_type, osm_id, name,
|
||||||
|
CASE WHEN extratags ? 'place' THEN 'place' ELSE class END as class,
|
||||||
|
CASE WHEN extratags ? 'place' THEN extratags->'place' ELSE type END as type,
|
||||||
|
admin_level, fromarea, isaddress,
|
||||||
|
CASE WHEN address_place_id = for_place_id AND rank_address = 0 THEN 100 WHEN rank_address = 11 THEN 5 ELSE rank_address END as rank_address,
|
||||||
|
distance,country_code,postcode
|
||||||
|
from place_addressline join placex on (address_place_id = placex.place_id)
|
||||||
|
where place_addressline.place_id = for_place_id
|
||||||
|
and (cached_rank_address > 0 AND cached_rank_address < searchrankaddress)
|
||||||
|
and address_place_id != for_place_id and linked_place_id is null
|
||||||
|
and (placex.country_code IS NULL OR searchcountrycode IS NULL OR placex.country_code = searchcountrycode)
|
||||||
|
order by rank_address desc,isaddress desc,fromarea desc,distance asc,rank_search desc
|
||||||
|
LOOP
|
||||||
|
--RAISE WARNING '%',location;
|
||||||
|
IF searchcountrycode IS NULL AND location.country_code IS NOT NULL THEN
|
||||||
|
searchcountrycode := location.country_code;
|
||||||
|
END IF;
|
||||||
|
IF location.type in ('postcode', 'postal_code') THEN
|
||||||
|
location.isaddress := FALSE;
|
||||||
|
END IF;
|
||||||
|
IF location.rank_address = 4 AND location.isaddress THEN
|
||||||
|
hadcountry := true;
|
||||||
|
END IF;
|
||||||
|
IF location.rank_address < 4 AND NOT hadcountry THEN
|
||||||
|
select name from country_name where country_code = searchcountrycode limit 1 INTO countryname;
|
||||||
|
IF countryname IS NOT NULL THEN
|
||||||
|
countrylocation := ROW(null, null, null, countryname, 'place', 'country', null, true, true, 4, 0)::addressline;
|
||||||
|
RETURN NEXT countrylocation;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
countrylocation := ROW(location.place_id, location.osm_type, location.osm_id, location.name, location.class,
|
||||||
|
location.type, location.admin_level, location.fromarea, location.isaddress, location.rank_address,
|
||||||
location.distance)::addressline;
|
location.distance)::addressline;
|
||||||
RETURN NEXT countrylocation;
|
RETURN NEXT countrylocation;
|
||||||
found := location.rank_address;
|
found := location.rank_address;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
|
||||||
-- If no country was included yet, add the name information from country_name.
|
|
||||||
IF found > 4 THEN
|
IF found > 4 THEN
|
||||||
SELECT name FROM country_name
|
select name from country_name where country_code = searchcountrycode limit 1 INTO countryname;
|
||||||
WHERE country_code = searchcountrycode LIMIT 1 INTO countryname;
|
|
||||||
--RAISE WARNING '% % %',found,searchcountrycode,countryname;
|
--RAISE WARNING '% % %',found,searchcountrycode,countryname;
|
||||||
IF countryname IS NOT NULL THEN
|
IF countryname IS NOT NULL THEN
|
||||||
location := ROW(null, null, null, countryname, 'place', 'country',
|
location := ROW(null, null, null, countryname, 'place', 'country', null, true, true, 4, 0)::addressline;
|
||||||
null, true, true, 4, 0)::addressline;
|
|
||||||
RETURN NEXT location;
|
RETURN NEXT location;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Finally add some artificial rows.
|
|
||||||
IF searchcountrycode IS NOT NULL THEN
|
IF searchcountrycode IS NOT NULL THEN
|
||||||
location := ROW(null, null, null, hstore('ref', searchcountrycode),
|
location := ROW(null, null, null, hstore('ref', searchcountrycode), 'place', 'country_code', null, true, false, 4, 0)::addressline;
|
||||||
'place', 'country_code', null, true, false, 4, 0)::addressline;
|
|
||||||
RETURN NEXT location;
|
RETURN NEXT location;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF searchhousename IS NOT NULL THEN
|
IF searchhousename IS NOT NULL THEN
|
||||||
location := ROW(in_place_id, null, null, searchhousename, searchclass,
|
location := ROW(in_place_id, null, null, searchhousename, searchclass, searchtype, null, true, true, 29, 0)::addressline;
|
||||||
searchtype, null, true, true, 29, 0)::addressline;
|
|
||||||
RETURN NEXT location;
|
RETURN NEXT location;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF searchhousenumber IS NOT NULL THEN
|
IF searchhousenumber IS NOT NULL THEN
|
||||||
location := ROW(in_place_id, null, null, hstore('ref', searchhousenumber),
|
location := ROW(in_place_id, null, null, hstore('ref', searchhousenumber), 'place', 'house_number', null, true, true, 28, 0)::addressline;
|
||||||
'place', 'house_number', null, true, true, 28, 0)::addressline;
|
|
||||||
RETURN NEXT location;
|
RETURN NEXT location;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF searchpostcode IS NOT NULL THEN
|
IF searchpostcode IS NOT NULL THEN
|
||||||
location := ROW(null, null, null, hstore('ref', searchpostcode), 'place',
|
location := ROW(null, null, null, hstore('ref', searchpostcode), 'place', 'postcode', null, true, true, 5, 0)::addressline;
|
||||||
'postcode', null, false, postcode_isaddress, 5, 0)::addressline;
|
|
||||||
RETURN NEXT location;
|
RETURN NEXT location;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
@@ -2523,6 +2530,96 @@ $$
|
|||||||
LANGUAGE plpgsql;
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION get_searchrank_label(rank INTEGER) RETURNS TEXT
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
BEGIN
|
||||||
|
IF rank < 2 THEN
|
||||||
|
RETURN 'Continent';
|
||||||
|
ELSEIF rank < 4 THEN
|
||||||
|
RETURN 'Sea';
|
||||||
|
ELSEIF rank < 8 THEN
|
||||||
|
RETURN 'Country';
|
||||||
|
ELSEIF rank < 12 THEN
|
||||||
|
RETURN 'State';
|
||||||
|
ELSEIF rank < 16 THEN
|
||||||
|
RETURN 'County';
|
||||||
|
ELSEIF rank = 16 THEN
|
||||||
|
RETURN 'City';
|
||||||
|
ELSEIF rank = 17 THEN
|
||||||
|
RETURN 'Town / Island';
|
||||||
|
ELSEIF rank = 18 THEN
|
||||||
|
RETURN 'Village / Hamlet';
|
||||||
|
ELSEIF rank = 20 THEN
|
||||||
|
RETURN 'Suburb';
|
||||||
|
ELSEIF rank = 21 THEN
|
||||||
|
RETURN 'Postcode Area';
|
||||||
|
ELSEIF rank = 22 THEN
|
||||||
|
RETURN 'Croft / Farm / Locality / Islet';
|
||||||
|
ELSEIF rank = 23 THEN
|
||||||
|
RETURN 'Postcode Area';
|
||||||
|
ELSEIF rank = 25 THEN
|
||||||
|
RETURN 'Postcode Point';
|
||||||
|
ELSEIF rank = 26 THEN
|
||||||
|
RETURN 'Street / Major Landmark';
|
||||||
|
ELSEIF rank = 27 THEN
|
||||||
|
RETURN 'Minory Street / Path';
|
||||||
|
ELSEIF rank = 28 THEN
|
||||||
|
RETURN 'House / Building';
|
||||||
|
ELSE
|
||||||
|
RETURN 'Other: '||rank;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION get_addressrank_label(rank INTEGER) RETURNS TEXT
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
BEGIN
|
||||||
|
IF rank = 0 THEN
|
||||||
|
RETURN 'None';
|
||||||
|
ELSEIF rank < 2 THEN
|
||||||
|
RETURN 'Continent';
|
||||||
|
ELSEIF rank < 4 THEN
|
||||||
|
RETURN 'Sea';
|
||||||
|
ELSEIF rank = 5 THEN
|
||||||
|
RETURN 'Postcode';
|
||||||
|
ELSEIF rank < 8 THEN
|
||||||
|
RETURN 'Country';
|
||||||
|
ELSEIF rank < 12 THEN
|
||||||
|
RETURN 'State';
|
||||||
|
ELSEIF rank < 16 THEN
|
||||||
|
RETURN 'County';
|
||||||
|
ELSEIF rank = 16 THEN
|
||||||
|
RETURN 'City';
|
||||||
|
ELSEIF rank = 17 THEN
|
||||||
|
RETURN 'Town / Village / Hamlet';
|
||||||
|
ELSEIF rank = 20 THEN
|
||||||
|
RETURN 'Suburb';
|
||||||
|
ELSEIF rank = 21 THEN
|
||||||
|
RETURN 'Postcode Area';
|
||||||
|
ELSEIF rank = 22 THEN
|
||||||
|
RETURN 'Croft / Farm / Locality / Islet';
|
||||||
|
ELSEIF rank = 23 THEN
|
||||||
|
RETURN 'Postcode Area';
|
||||||
|
ELSEIF rank = 25 THEN
|
||||||
|
RETURN 'Postcode Point';
|
||||||
|
ELSEIF rank = 26 THEN
|
||||||
|
RETURN 'Street / Major Landmark';
|
||||||
|
ELSEIF rank = 27 THEN
|
||||||
|
RETURN 'Minory Street / Path';
|
||||||
|
ELSEIF rank = 28 THEN
|
||||||
|
RETURN 'House / Building';
|
||||||
|
ELSE
|
||||||
|
RETURN 'Other: '||rank;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION aux_create_property(pointgeo GEOMETRY, in_housenumber TEXT,
|
CREATE OR REPLACE FUNCTION aux_create_property(pointgeo GEOMETRY, in_housenumber TEXT,
|
||||||
in_street TEXT, in_isin TEXT, in_postcode TEXT, in_countrycode char(2)) RETURNS INTEGER
|
in_street TEXT, in_isin TEXT, in_postcode TEXT, in_countrycode char(2)) RETURNS INTEGER
|
||||||
AS $$
|
AS $$
|
||||||
|
|||||||
@@ -1,27 +1,31 @@
|
|||||||
-- Indices used only during search and update.
|
-- Indices used only during search and update.
|
||||||
-- These indices are created only after the indexing process is done.
|
-- These indices are created only after the indexing process is done.
|
||||||
|
|
||||||
CREATE INDEX CONCURRENTLY idx_word_word_id on word USING BTREE (word_id) {ts:search-index};
|
CREATE INDEX idx_word_word_id on word USING BTREE (word_id) {ts:search-index};
|
||||||
|
|
||||||
CREATE INDEX CONCURRENTLY idx_place_addressline_address_place_id on place_addressline USING BTREE (address_place_id) {ts:search-index};
|
CREATE INDEX idx_search_name_nameaddress_vector ON search_name USING GIN (nameaddress_vector) WITH (fastupdate = off) {ts:search-index};
|
||||||
|
CREATE INDEX idx_search_name_name_vector ON search_name USING GIN (name_vector) WITH (fastupdate = off) {ts:search-index};
|
||||||
|
CREATE INDEX idx_search_name_centroid ON search_name USING GIST (centroid) {ts:search-index};
|
||||||
|
|
||||||
DROP INDEX CONCURRENTLY IF EXISTS idx_placex_rank_search;
|
CREATE INDEX idx_place_addressline_address_place_id on place_addressline USING BTREE (address_place_id) {ts:search-index};
|
||||||
CREATE INDEX CONCURRENTLY idx_placex_rank_search ON placex USING BTREE (rank_search) {ts:search-index};
|
|
||||||
CREATE INDEX CONCURRENTLY idx_placex_rank_address ON placex USING BTREE (rank_address) {ts:search-index};
|
|
||||||
CREATE INDEX CONCURRENTLY idx_placex_pendingsector ON placex USING BTREE (rank_search,geometry_sector) {ts:address-index} where indexed_status > 0;
|
|
||||||
CREATE INDEX CONCURRENTLY idx_placex_parent_place_id ON placex USING BTREE (parent_place_id) {ts:search-index} where parent_place_id IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE INDEX CONCURRENTLY idx_placex_geometry_reverse_lookupPoint
|
DROP INDEX IF EXISTS idx_placex_rank_search;
|
||||||
|
CREATE INDEX idx_placex_rank_search ON placex USING BTREE (rank_search) {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_parent_place_id ON placex USING BTREE (parent_place_id) {ts:search-index} where parent_place_id IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX idx_placex_geometry_reverse_lookupPoint
|
||||||
ON placex USING gist (geometry) {ts:search-index}
|
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)
|
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 class not in ('railway','tunnel','bridge','man_made')
|
||||||
AND rank_address >= 26 AND indexed_status = 0 AND linked_place_id is null;
|
AND rank_address >= 26 AND indexed_status = 0 AND linked_place_id is null;
|
||||||
CREATE INDEX CONCURRENTLY idx_placex_geometry_reverse_lookupPolygon
|
CREATE INDEX idx_placex_geometry_reverse_lookupPolygon
|
||||||
ON placex USING gist (geometry) {ts:search-index}
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
|
WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
|
||||||
AND rank_address between 4 and 25 AND type != 'postcode'
|
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;
|
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
||||||
CREATE INDEX CONCURRENTLY idx_placex_geometry_reverse_placeNode
|
CREATE INDEX idx_placex_geometry_reverse_placeNode
|
||||||
ON placex USING gist (geometry) {ts:search-index}
|
ON placex USING gist (geometry) {ts:search-index}
|
||||||
WHERE osm_type = 'N' AND rank_search between 5 and 25
|
WHERE osm_type = 'N' AND rank_search between 5 and 25
|
||||||
AND class = 'place' AND type != 'postcode'
|
AND class = 'place' AND type != 'postcode'
|
||||||
@@ -29,14 +33,13 @@ CREATE INDEX CONCURRENTLY idx_placex_geometry_reverse_placeNode
|
|||||||
|
|
||||||
GRANT SELECT ON table country_osm_grid to "{www-user}";
|
GRANT SELECT ON table country_osm_grid to "{www-user}";
|
||||||
|
|
||||||
CREATE INDEX CONCURRENTLY 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 CONCURRENTLY 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 CONCURRENTLY idx_osmline_parent_osm_id ON location_property_osmline USING BTREE (osm_id) {ts:search-index};
|
|
||||||
|
|
||||||
DROP INDEX CONCURRENTLY IF EXISTS place_id_idx;
|
DROP INDEX IF EXISTS place_id_idx;
|
||||||
CREATE UNIQUE INDEX CONCURRENTLY 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};
|
||||||
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX CONCURRENTLY idx_postcode_id ON location_postcode USING BTREE (place_id) {ts:search-index};
|
CREATE UNIQUE INDEX idx_postcode_id ON location_postcode USING BTREE (place_id) {ts:search-index};
|
||||||
CREATE INDEX CONCURRENTLY idx_postcode_postcode ON location_postcode USING BTREE (postcode) {ts:search-index};
|
CREATE INDEX idx_postcode_postcode ON location_postcode USING BTREE (postcode) {ts:search-index};
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
-- Indices used for /search API.
|
|
||||||
-- These indices are created only after the indexing process is done.
|
|
||||||
|
|
||||||
CREATE INDEX CONCURRENTLY idx_search_name_nameaddress_vector ON search_name USING GIN (nameaddress_vector) WITH (fastupdate = off) {ts:search-index};
|
|
||||||
CREATE INDEX CONCURRENTLY idx_search_name_name_vector ON search_name USING GIN (name_vector) WITH (fastupdate = off) {ts:search-index};
|
|
||||||
CREATE INDEX CONCURRENTLY idx_search_name_centroid ON search_name USING GIST (centroid) {ts:search-index};
|
|
||||||
@@ -6,9 +6,11 @@ BEGIN
|
|||||||
-- start
|
-- start
|
||||||
IF in_partition = -partition- THEN
|
IF in_partition = -partition- THEN
|
||||||
FOR r IN
|
FOR r IN
|
||||||
SELECT place_id, keywords, rank_address, rank_search, min(ST_Distance(feature, centroid)) as distance, isguess, postcode, centroid
|
SELECT place_id, keywords, rank_address, rank_search, min(ST_Distance(feature, centroid)) as distance, isguess, postcode, centroid FROM (
|
||||||
FROM location_area_large_-partition-
|
SELECT * FROM location_area_large_-partition- WHERE ST_Intersects(geometry, feature) and rank_search < maxrank
|
||||||
WHERE ST_Intersects(geometry, feature) and rank_search < maxrank
|
UNION ALL
|
||||||
|
SELECT * FROM location_area_country WHERE ST_Intersects(geometry, feature) and rank_search < maxrank
|
||||||
|
) as location_area
|
||||||
GROUP BY place_id, keywords, rank_address, rank_search, isguess, postcode, centroid
|
GROUP BY place_id, keywords, rank_address, rank_search, isguess, postcode, centroid
|
||||||
ORDER BY rank_address, isin_tokens && keywords desc, isguess asc,
|
ORDER BY rank_address, isin_tokens && keywords desc, isguess asc,
|
||||||
ST_Distance(feature, centroid) *
|
ST_Distance(feature, centroid) *
|
||||||
@@ -62,9 +64,9 @@ BEGIN
|
|||||||
RETURN TRUE;
|
RETURN TRUE;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF in_rank_search <= 4 and not in_estimate THEN
|
IF in_rank_search <= 4 THEN
|
||||||
INSERT INTO location_area_country (place_id, country_code, geometry)
|
INSERT INTO location_area_country (partition, place_id, country_code, keywords, rank_search, rank_address, isguess, centroid, geometry)
|
||||||
values (in_place_id, in_country_code, in_geometry);
|
values (in_partition, in_place_id, in_country_code, in_keywords, in_rank_search, in_rank_address, in_estimate, in_centroid, in_geometry);
|
||||||
RETURN TRUE;
|
RETURN TRUE;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ BEGIN
|
|||||||
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 centroid && ST_Expand(point, 0.015)
|
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
|
||||||
@@ -123,7 +125,7 @@ BEGIN
|
|||||||
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 centroid && ST_Expand(point, 0.04)
|
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
|
||||||
@@ -140,11 +142,17 @@ LANGUAGE plpgsql;
|
|||||||
|
|
||||||
|
|
||||||
create or replace function insertSearchName(
|
create or replace function insertSearchName(
|
||||||
in_partition INTEGER, in_place_id BIGINT, in_name_vector INTEGER[],
|
in_partition INTEGER, in_place_id BIGINT, in_country_code VARCHAR(2),
|
||||||
in_rank_search INTEGER, in_rank_address INTEGER, in_geometry GEOMETRY)
|
in_name_vector INTEGER[], in_nameaddress_vector INTEGER[],
|
||||||
RETURNS BOOLEAN AS $$
|
in_rank_search INTEGER, in_rank_address INTEGER, in_importance FLOAT,
|
||||||
|
in_centroid GEOMETRY, in_geometry GEOMETRY) RETURNS BOOLEAN AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
|
DELETE FROM search_name WHERE place_id = in_place_id;
|
||||||
|
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);
|
||||||
|
|
||||||
-- 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;
|
||||||
@@ -165,6 +173,9 @@ LANGUAGE plpgsql;
|
|||||||
create or replace function deleteSearchName(in_partition INTEGER, in_place_id BIGINT) RETURNS BOOLEAN AS $$
|
create or replace function deleteSearchName(in_partition INTEGER, in_place_id BIGINT) RETURNS BOOLEAN AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
|
DELETE from search_name WHERE place_id = in_place_id;
|
||||||
|
|
||||||
-- start
|
-- start
|
||||||
IF in_partition = -partition- THEN
|
IF in_partition = -partition- THEN
|
||||||
DELETE from search_name_-partition- WHERE place_id = in_place_id;
|
DELETE from search_name_-partition- WHERE place_id = in_place_id;
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ CREATE TABLE search_name_blank (
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
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};
|
||||||
|
|
||||||
-- 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};
|
||||||
@@ -42,8 +45,8 @@ CREATE INDEX idx_location_area_large_-partition-_geometry ON location_area_large
|
|||||||
|
|
||||||
CREATE TABLE search_name_-partition- () INHERITS (search_name_blank) {ts:address-data};
|
CREATE TABLE search_name_-partition- () INHERITS (search_name_blank) {ts:address-data};
|
||||||
CREATE INDEX idx_search_name_-partition-_place_id ON search_name_-partition- USING BTREE (place_id) {ts:address-index};
|
CREATE INDEX idx_search_name_-partition-_place_id ON search_name_-partition- USING BTREE (place_id) {ts:address-index};
|
||||||
CREATE INDEX idx_search_name_-partition-_centroid_street ON search_name_-partition- USING GIST (centroid) {ts:address-index} where search_rank between 26 and 27;
|
CREATE INDEX idx_search_name_-partition-_centroid ON search_name_-partition- USING GIST (centroid) {ts:address-index};
|
||||||
CREATE INDEX idx_search_name_-partition-_centroid_place ON search_name_-partition- USING GIST (centroid) {ts:address-index} where search_rank between 2 and 25;
|
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-;
|
DROP TABLE IF EXISTS location_road_-partition-;
|
||||||
CREATE TABLE location_road_-partition- (
|
CREATE TABLE location_road_-partition- (
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ drop table if exists import_osmosis_log;
|
|||||||
CREATE TABLE import_osmosis_log (
|
CREATE TABLE import_osmosis_log (
|
||||||
batchend timestamp,
|
batchend timestamp,
|
||||||
batchseq integer,
|
batchseq integer,
|
||||||
batchsize bigint,
|
batchsize integer,
|
||||||
starttime timestamp,
|
starttime timestamp,
|
||||||
endtime timestamp,
|
endtime timestamp,
|
||||||
event text
|
event text
|
||||||
@@ -36,7 +36,6 @@ GRANT SELECT ON new_query_log TO "{www-user}" ;
|
|||||||
|
|
||||||
GRANT SELECT ON TABLE country_name TO "{www-user}";
|
GRANT SELECT ON TABLE country_name TO "{www-user}";
|
||||||
GRANT SELECT ON TABLE gb_postcode TO "{www-user}";
|
GRANT SELECT ON TABLE gb_postcode TO "{www-user}";
|
||||||
GRANT SELECT ON TABLE us_postcode TO "{www-user}";
|
|
||||||
|
|
||||||
drop table IF EXISTS word;
|
drop table IF EXISTS word;
|
||||||
CREATE TABLE word (
|
CREATE TABLE word (
|
||||||
@@ -70,15 +69,6 @@ CREATE TABLE location_area (
|
|||||||
|
|
||||||
CREATE TABLE location_area_large () INHERITS (location_area);
|
CREATE TABLE location_area_large () INHERITS (location_area);
|
||||||
|
|
||||||
DROP TABLE IF EXISTS location_area_country;
|
|
||||||
CREATE TABLE location_area_country (
|
|
||||||
place_id BIGINT,
|
|
||||||
country_code varchar(2),
|
|
||||||
geometry GEOMETRY(Geometry, 4326)
|
|
||||||
) {ts:address-data};
|
|
||||||
CREATE INDEX idx_location_area_country_geometry ON location_area_country USING GIST (geometry) {ts:address-index};
|
|
||||||
|
|
||||||
|
|
||||||
drop table IF EXISTS location_property CASCADE;
|
drop table IF EXISTS location_property CASCADE;
|
||||||
CREATE TABLE location_property (
|
CREATE TABLE location_property (
|
||||||
place_id BIGINT,
|
place_id BIGINT,
|
||||||
|
|||||||
@@ -26,11 +26,6 @@ BEGIN
|
|||||||
endnumber = in_startnumber;
|
endnumber = in_startnumber;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF startnumber < 0 THEN
|
|
||||||
RAISE WARNING 'Negative house number range (% to %) on %, %', startnumber, endnumber, in_street, in_isin;
|
|
||||||
RETURN 0;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
numberrange := endnumber - startnumber;
|
numberrange := endnumber - startnumber;
|
||||||
|
|
||||||
IF (interpolationtype = 'odd' AND startnumber%2 = 0) OR (interpolationtype = 'even' AND startnumber%2 = 1) THEN
|
IF (interpolationtype = 'odd' AND startnumber%2 = 0) OR (interpolationtype = 'even' AND startnumber%2 = 1) THEN
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ SELECT country_code,
|
|||||||
ST_Centroid(ST_Collect(ST_Centroid(geometry))) as centroid
|
ST_Centroid(ST_Collect(ST_Centroid(geometry))) as centroid
|
||||||
FROM placex
|
FROM placex
|
||||||
WHERE address ? 'postcode'
|
WHERE address ? 'postcode'
|
||||||
AND address->'postcode' NOT SIMILAR TO '%(,|;|:)%'
|
AND address->'postcode' NOT SIMILAR TO '%(,|;)%'
|
||||||
AND geometry IS NOT null
|
AND geometry IS NOT null
|
||||||
GROUP BY country_code, pc;
|
GROUP BY country_code, pc;
|
||||||
|
|
||||||
@@ -48,11 +48,5 @@ INSERT INTO location_postcode
|
|||||||
SELECT nextval('seq_place'), 1, country_code, pc, centroid
|
SELECT nextval('seq_place'), 1, country_code, pc, centroid
|
||||||
FROM tmp_new_postcode_locations new;
|
FROM tmp_new_postcode_locations new;
|
||||||
|
|
||||||
-- Remove unused word entries
|
|
||||||
DELETE FROM word
|
|
||||||
WHERE class = 'place' AND type = 'postcode'
|
|
||||||
AND NOT EXISTS (SELECT 0 FROM location_postcode p
|
|
||||||
WHERE p.postcode = word.word);
|
|
||||||
|
|
||||||
-- Finally index the newly inserted postcodes
|
-- Finally index the newly inserted postcodes
|
||||||
UPDATE location_postcode SET indexed_status = 0 WHERE indexed_status > 0;
|
UPDATE location_postcode SET indexed_status = 0 WHERE indexed_status > 0;
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
all: bdd php
|
|
||||||
|
|
||||||
bdd:
|
|
||||||
cd bdd && behave -DREMOVE_TEMPLATE=1
|
|
||||||
|
|
||||||
php:
|
|
||||||
cd php && phpunit ./
|
|
||||||
|
|
||||||
.PHONY: bdd php
|
|
||||||
@@ -66,19 +66,18 @@ To run the functional tests, do
|
|||||||
cd test/bdd
|
cd test/bdd
|
||||||
behave
|
behave
|
||||||
|
|
||||||
The tests can be configured with a set of environment variables (`behave -D key=val`):
|
The tests can be configured with a set of environment variables:
|
||||||
|
|
||||||
* `BUILDDIR` - build directory of Nominatim installation to test
|
* `BUILD_DIR` - build directory of Nominatim installation to test
|
||||||
* `TEMPLATE_DB` - name of template database used as a skeleton for
|
* `TEMPLATE_DB` - name of template database used as a skeleton for
|
||||||
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)
|
||||||
* `API_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_HOST` - (optional) hostname of database host
|
||||||
* `DB_PORT` - (optional) port of database on host
|
|
||||||
* `DB_USER` - (optional) username of database login
|
* `DB_USER` - (optional) username of database login
|
||||||
* `DB_PASS` - (optional) password for database login
|
* `DB_PASS` - (optional) password for database login
|
||||||
* `SERVER_MODULE_PATH` - (optional) path on the Postgres server to Nominatim
|
* `SERVER_MODULE_PATH` - (optional) path on the Postgres server to Nominatim
|
||||||
module shared library file
|
* 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
|
||||||
@@ -118,8 +117,8 @@ planets are likely to work as well but you may see isolated test
|
|||||||
failures where the data has changed. To recreate the input data
|
failures where the data has changed. To recreate the input data
|
||||||
for the test database run:
|
for the test database run:
|
||||||
|
|
||||||
wget https://ftp5.gwdg.de/pub/misc/openstreetmap/planet.openstreetmap.org/pbf/planet-180924.osm.pbf
|
wget https://free.nchc.org.tw/osm.planet/pbf/planet-160725.osm.pbf
|
||||||
osmconvert planet-180924.osm.pbf -B=test/testdb/testdb.polys -o=testdb.pbf
|
osmconvert planet-160725.osm.pbf -B=test/testdb/testdb.polys -o=testdb.pbf
|
||||||
|
|
||||||
Before importing make sure to add the following to your local settings:
|
Before importing make sure to add the following to your local settings:
|
||||||
|
|
||||||
|
|||||||
@@ -34,10 +34,3 @@ Feature: Object details
|
|||||||
| 1 |
|
| 1 |
|
||||||
Then the result is valid html
|
Then the result is valid html
|
||||||
|
|
||||||
# ticket #1343
|
|
||||||
Scenario: Details of a country with keywords
|
|
||||||
When sending details query for R287072
|
|
||||||
| keywords |
|
|
||||||
| 1 |
|
|
||||||
Then the result is valid html
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
@APIDB
|
|
||||||
Feature: Places by osm_type and osm_id Tests
|
|
||||||
Simple tests for errors in various response formats.
|
|
||||||
|
|
||||||
Scenario Outline: Force error by providing too many ids
|
|
||||||
When sending <format> lookup query for N1,N2,N3,N4,N5,N6,N7,N8,N9,N10,N11,N12,N13,N14,N15,N16,N17,N18,N19,N20,N21,N22,N23,N24,N25,N26,N27,N28,N29,N30,N31,N32,N33,N34,N35,N36,N37,N38,N39,N40,N41,N42,N43,N44,N45,N46,N47,N48,N49,N50,N51
|
|
||||||
Then a <format> user error is returned
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
| format |
|
|
||||||
| xml |
|
|
||||||
| json |
|
|
||||||
| geojson |
|
|
||||||
@@ -1,33 +1,18 @@
|
|||||||
@APIDB
|
@APIDB
|
||||||
Feature: Places by osm_type and osm_id Tests
|
Feature: Places by osm_type and osm_id Tests
|
||||||
Simple tests for response format.
|
Simple tests for internal server errors and response format.
|
||||||
|
|
||||||
Scenario Outline: address lookup for existing node, way, relation
|
Scenario Outline: address lookup for existing node, way, relation
|
||||||
When sending <format> lookup query for N3284625766,W6065798,,R123924,X99,N0
|
When sending <format> lookup query for N3284625766,W6065798,,R123924,X99,N0
|
||||||
Then the result is valid <outformat>
|
Then the result is valid <format>
|
||||||
And exactly 3 results are returned
|
And exactly 3 results are returned
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| format | outformat |
|
| format |
|
||||||
| xml | xml |
|
| xml |
|
||||||
| json | json |
|
| json |
|
||||||
| jsonv2 | json |
|
| geojson |
|
||||||
| geojson | geojson |
|
|
||||||
| geocodejson | geocodejson |
|
|
||||||
|
|
||||||
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
|
||||||
Then exactly 0 results are returned
|
Then exactly 0 results are returned
|
||||||
|
|
||||||
Scenario Outline: Boundingbox is returned
|
|
||||||
When sending <format> lookup query for N3284625766,W6065798
|
|
||||||
Then exactly 2 results are returned
|
|
||||||
And result 0 has bounding box in -32.812,-32.811,-56.509,-56.508
|
|
||||||
And result 1 has bounding box in 47.14,47.15,9.51,9.53
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
| format |
|
|
||||||
| json |
|
|
||||||
| jsonv2 |
|
|
||||||
| geojson |
|
|
||||||
| xml |
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Feature: Localization of reverse search results
|
|||||||
When sending json reverse coordinates 18.1147,-15.95
|
When sending json reverse coordinates 18.1147,-15.95
|
||||||
Then result addresses contain
|
Then result addresses contain
|
||||||
| ID | country |
|
| ID | country |
|
||||||
| 0 | موريتانيا |
|
| 0 | Mauritanie موريتانيا |
|
||||||
|
|
||||||
Scenario: accept-language parameter
|
Scenario: accept-language parameter
|
||||||
When sending json reverse coordinates 18.1147,-15.95
|
When sending json reverse coordinates 18.1147,-15.95
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Feature: Reverse geocoding
|
|||||||
| way | place | house |
|
| way | place | house |
|
||||||
And result addresses contain
|
And result addresses contain
|
||||||
| house_number | road | postcode | country_code |
|
| house_number | road | postcode | country_code |
|
||||||
| 909 | West 1st Street | 57274 | us |
|
| 906 | West 1st Street | 57274 | us |
|
||||||
|
|
||||||
@Tiger
|
@Tiger
|
||||||
Scenario: No TIGER house number for zoom < 18
|
Scenario: No TIGER house number for zoom < 18
|
||||||
@@ -31,7 +31,7 @@ Feature: Reverse geocoding
|
|||||||
| way | place | house |
|
| way | place | house |
|
||||||
And result addresses contain
|
And result addresses contain
|
||||||
| house_number | road |
|
| house_number | road |
|
||||||
| 1416 | Juan Antonio Lavalleja |
|
| 1410 | Juan Antonio Lavalleja |
|
||||||
|
|
||||||
Scenario: Address with non-numerical house number
|
Scenario: Address with non-numerical house number
|
||||||
When sending jsonv2 reverse coordinates 53.579805460944,9.9475670458196
|
When sending jsonv2 reverse coordinates 53.579805460944,9.9475670458196
|
||||||
@@ -50,7 +50,7 @@ Feature: Reverse geocoding
|
|||||||
When sending jsonv2 reverse coordinates 54.046489113,8.5546870529
|
When sending jsonv2 reverse coordinates 54.046489113,8.5546870529
|
||||||
Then results contain
|
Then results contain
|
||||||
| display_name |
|
| display_name |
|
||||||
| Hamburg, Deutschland |
|
| Freie und Hansestadt Hamburg, Deutschland |
|
||||||
|
|
||||||
Scenario: When slightly outside town, the town is not shown
|
Scenario: When slightly outside town, the town is not shown
|
||||||
When sending jsonv2 reverse coordinates -32.122,-56.114
|
When sending jsonv2 reverse coordinates -32.122,-56.114
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ Feature: Search queries
|
|||||||
| en |
|
| en |
|
||||||
Then results contain
|
Then results contain
|
||||||
| display_name |
|
| display_name |
|
||||||
| Plei Ya Rê, Vietnam |
|
| Plei Ya Rê, Kon Tum province, Vietnam |
|
||||||
|
|
||||||
Scenario: Address details with unknown class types
|
Scenario: Address details with unknown class types
|
||||||
When sending json search query "Hundeauslauf, Hamburg" with address
|
When sending json search query "Hundeauslauf, Hamburg" with address
|
||||||
|
|||||||
@@ -26,18 +26,6 @@ Feature: Searches with postcodes
|
|||||||
| country_code |
|
| country_code |
|
||||||
| li |
|
| li |
|
||||||
|
|
||||||
Scenario: Postcode search with bounded viewbox restriction
|
|
||||||
When sending json search query "9486" with address
|
|
||||||
| bounded | viewbox |
|
|
||||||
| 1 | 9.55,47.20,9.58,47.22 |
|
|
||||||
Then result addresses contain
|
|
||||||
| postcode |
|
|
||||||
| 9486 |
|
|
||||||
When sending json search query "9486" with address
|
|
||||||
| bounded | viewbox |
|
|
||||||
| 1 | 5.00,20.00,6.00,21.00 |
|
|
||||||
Then exactly 0 results are returned
|
|
||||||
|
|
||||||
Scenario: Postcode search with structured query
|
Scenario: Postcode search with structured query
|
||||||
When sending json search query "" with address
|
When sending json search query "" with address
|
||||||
| postalcode | country |
|
| postalcode | country |
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ Feature: Search queries
|
|||||||
| type | value |
|
| type | value |
|
||||||
| house_number | 86 |
|
| house_number | 86 |
|
||||||
| road | Schellingstraße |
|
| road | Schellingstraße |
|
||||||
| neighbourhood | Auenviertel |
|
|
||||||
| suburb | Eilbek |
|
| suburb | Eilbek |
|
||||||
| postcode | 22089 |
|
| postcode | 22089 |
|
||||||
| city_district | Wandsbek |
|
| city_district | Wandsbek |
|
||||||
|
| state | Hamburg |
|
||||||
| country | Deutschland |
|
| country | Deutschland |
|
||||||
| country_code | de |
|
| country_code | de |
|
||||||
|
|
||||||
@@ -37,10 +37,10 @@ Feature: Search queries
|
|||||||
| type | value |
|
| type | value |
|
||||||
| house_number | 73 |
|
| house_number | 73 |
|
||||||
| road | Schellingstraße |
|
| road | Schellingstraße |
|
||||||
| neighbourhood | Auenviertel |
|
|
||||||
| suburb | Eilbek |
|
| suburb | Eilbek |
|
||||||
| postcode | 22089 |
|
| postcode | 22089 |
|
||||||
| city_district | Wandsbek |
|
| city_district | Wandsbek |
|
||||||
|
| state | Hamburg |
|
||||||
| country | Deutschland |
|
| country | Deutschland |
|
||||||
| country_code | de |
|
| country_code | de |
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ Feature: Simple Tests
|
|||||||
When sending json search query "Tokyo"
|
When sending json search query "Tokyo"
|
||||||
| param | value |
|
| param | value |
|
||||||
|json_callback | <data> |
|
|json_callback | <data> |
|
||||||
Then a json user error is returned
|
Then a HTTP 400 is returned
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
| data |
|
| data |
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Feature: Status queries against unknown database
|
|||||||
Scenario: Failed status as text
|
Scenario: Failed status as text
|
||||||
When sending text status query
|
When sending text status query
|
||||||
Then a HTTP 500 is returned
|
Then a HTTP 500 is returned
|
||||||
And the page contents equals "ERROR: Database connection failed"
|
And the page contents equals "ERROR: No database"
|
||||||
|
|
||||||
Scenario: Failed status as json
|
Scenario: Failed status as json
|
||||||
When sending json status query
|
When sending json status query
|
||||||
@@ -13,5 +13,5 @@ Feature: Status queries against unknown database
|
|||||||
And the result is valid json
|
And the result is valid json
|
||||||
And results contain
|
And results contain
|
||||||
| status | message |
|
| status | message |
|
||||||
| 700 | Database connection failed |
|
| 700 | No database |
|
||||||
And result has not attributes data_updated
|
And result has not attributes data_updated
|
||||||
|
|||||||
@@ -344,23 +344,3 @@ Feature: Import of address interpolations
|
|||||||
When importing
|
When importing
|
||||||
Then W1 expands to no interpolation
|
Then W1 expands to no interpolation
|
||||||
|
|
||||||
Scenario: Two point interpolation starting at 0
|
|
||||||
Given the places
|
|
||||||
| osm | class | type | housenr | geometry |
|
|
||||||
| N1 | place | house | 0 | 1 1 |
|
|
||||||
| N2 | place | house | 2 | 1 1.001 |
|
|
||||||
And the places
|
|
||||||
| osm | class | type | addr+interpolation | geometry |
|
|
||||||
| W1 | place | houses | even | 1 1, 1 1.001 |
|
|
||||||
And the ways
|
|
||||||
| id | nodes |
|
|
||||||
| 1 | 1,2 |
|
|
||||||
When importing
|
|
||||||
Then W1 expands to interpolation
|
|
||||||
| start | end | geometry |
|
|
||||||
| 0 | 2 | 1 1, 1 1.001 |
|
|
||||||
When sending jsonv2 reverse coordinates 1,1
|
|
||||||
Then results contain
|
|
||||||
| ID | osm_type | osm_id | type | display_name |
|
|
||||||
| 0 | way | 1 | house | 0 |
|
|
||||||
|
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ Feature: Parenting of objects
|
|||||||
When importing
|
When importing
|
||||||
Then placex contains
|
Then placex contains
|
||||||
| object | parent_place_id |
|
| object | parent_place_id |
|
||||||
| W1 | W2 |
|
| W1 | W3 |
|
||||||
|
|
||||||
Scenario: Building with addr:street tags
|
Scenario: Building with addr:street tags
|
||||||
Given the scene building-on-street-corner
|
Given the scene building-on-street-corner
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user