mirror of
https://github.com/osm-search/Nominatim.git
synced 2026-02-16 15:47:58 +00:00
Compare commits
4 Commits
v3.2.0
...
release_2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7eb263a3a4 | ||
|
|
ed4e46c3d9 | ||
|
|
a7ef9426d3 | ||
|
|
0bc81fe59c |
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,11 +1,31 @@
|
|||||||
*.log
|
*.log
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
build
|
nominatim/*.d
|
||||||
|
nominatim/*.o
|
||||||
|
nominatim/nominatim
|
||||||
|
module/nominatim.so
|
||||||
|
module/nominatim.o
|
||||||
|
settings/configuration.txt
|
||||||
|
settings/download.lock
|
||||||
|
settings/state.txt
|
||||||
settings/local.php
|
settings/local.php
|
||||||
|
|
||||||
|
.deps/
|
||||||
|
autom4te.cache/
|
||||||
|
config.*
|
||||||
|
configure
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
stamp-h1
|
||||||
|
missing
|
||||||
|
INSTALL
|
||||||
|
aclocal.m4
|
||||||
|
depcomp
|
||||||
|
install-sh
|
||||||
|
compile
|
||||||
|
|
||||||
data/wiki_import.sql
|
data/wiki_import.sql
|
||||||
data/wiki_specialphrases.sql
|
data/wiki_specialphrases.sql
|
||||||
data/osmosischange.osc
|
data/osmosischange.osc
|
||||||
|
|
||||||
.vagrant
|
|
||||||
|
|||||||
32
.travis.yml
32
.travis.yml
@@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
language: python
|
|
||||||
python:
|
|
||||||
- "3.6"
|
|
||||||
addons:
|
|
||||||
postgresql: "9.6"
|
|
||||||
git:
|
|
||||||
depth: 3
|
|
||||||
env:
|
|
||||||
- TEST_SUITE=tests
|
|
||||||
- TEST_SUITE=monaco
|
|
||||||
install:
|
|
||||||
- vagrant/install-on-travis-ci.sh
|
|
||||||
before_script:
|
|
||||||
- psql -U postgres -c "create extension postgis"
|
|
||||||
script:
|
|
||||||
- cd $TRAVIS_BUILD_DIR/
|
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then phpcs --report-width=120 . ; fi
|
|
||||||
- cd $TRAVIS_BUILD_DIR/test/php
|
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then phpunit ./ ; fi
|
|
||||||
- cd $TRAVIS_BUILD_DIR/test/bdd
|
|
||||||
- # behave --format=progress3 api
|
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then behave --format=progress3 db ; fi
|
|
||||||
- if [[ $TEST_SUITE == "tests" ]]; then behave --format=progress3 osm2pgsql ; fi
|
|
||||||
- cd $TRAVIS_BUILD_DIR/build
|
|
||||||
- if [[ $TEST_SUITE == "monaco" ]]; then wget --no-verbose --output-document=../data/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf; fi
|
|
||||||
- if [[ $TEST_SUITE == "monaco" ]]; then /usr/bin/env php ./utils/setup.php --osm-file ../data/monaco.osm.pbf --osm2pgsql-cache 1000 --all 2>&1 | grep -v 'ETA (seconds)'; fi
|
|
||||||
- if [[ $TEST_SUITE == "monaco" ]]; then /usr/bin/env php ./utils/specialphrases.php --wiki-import | psql -d test_api_nominatim >/dev/null; fi
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
||||||
22
AUTHORS
22
AUTHORS
@@ -2,14 +2,16 @@ Nominatim was written by:
|
|||||||
|
|
||||||
Brian Quinion
|
Brian Quinion
|
||||||
Sarah Hoffmann
|
Sarah Hoffmann
|
||||||
Marc Tobias Metten
|
|
||||||
|
|
||||||
markigail
|
|
||||||
gemo1011
|
|
||||||
IrlJidel
|
|
||||||
Frederik Ramm
|
Frederik Ramm
|
||||||
|
Michael Spreng
|
||||||
and many more.
|
Daniele Forsi
|
||||||
|
mfn
|
||||||
For a full list of contributors see
|
Grant Slater
|
||||||
https://github.com/openstreetmap/Nominatim/graphs/contributors
|
Andree Klattenhoff
|
||||||
|
IrlJidel
|
||||||
|
appelflap
|
||||||
|
b3nn0
|
||||||
|
Spin0us
|
||||||
|
Kurt Roeckx
|
||||||
|
Rodolphe Quiédeville
|
||||||
|
Marc Tobias Metten
|
||||||
|
|||||||
162
CMakeLists.txt
162
CMakeLists.txt
@@ -1,162 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# CMake Config
|
|
||||||
#
|
|
||||||
# Nominatim
|
|
||||||
#
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# Project version
|
|
||||||
#
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
project(nominatim)
|
|
||||||
|
|
||||||
set(NOMINATIM_VERSION_MAJOR 3)
|
|
||||||
set(NOMINATIM_VERSION_MINOR 2)
|
|
||||||
set(NOMINATIM_VERSION_PATCH 0)
|
|
||||||
|
|
||||||
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
|
|
||||||
|
|
||||||
add_definitions(-DNOMINATIM_VERSION="${NOMINATIM_VERSION}")
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# Find external dependencies
|
|
||||||
#
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
set(BUILD_TESTS off CACHE BOOL "Build test suite" FORCE)
|
|
||||||
set(WITH_LUA off CACHE BOOL "Build with lua support" FORCE)
|
|
||||||
|
|
||||||
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/osm2pgsql/CMakeLists.txt")
|
|
||||||
message(FATAL_ERROR "The osm2pgsql directory is empty.\
|
|
||||||
Did you forget to check out Nominatim recursively?\
|
|
||||||
\nTry updating submodules with: git submodule update --init")
|
|
||||||
endif()
|
|
||||||
add_subdirectory(osm2pgsql)
|
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
|
|
||||||
unset(PostgreSQL_TYPE_INCLUDE_DIR CACHE)
|
|
||||||
set(PostgreSQL_TYPE_INCLUDE_DIR "/usr/include/")
|
|
||||||
find_package(PostgreSQL REQUIRED)
|
|
||||||
include_directories(${PostgreSQL_INCLUDE_DIRS})
|
|
||||||
link_directories(${PostgreSQL_LIBRARY_DIRS})
|
|
||||||
|
|
||||||
find_program(PYOSMIUM pyosmium-get-changes)
|
|
||||||
if (NOT EXISTS "${PYOSMIUM}")
|
|
||||||
set(PYOSMIUM_PATH "")
|
|
||||||
message(WARNING "pyosmium-get-changes not found (required for updates)")
|
|
||||||
else()
|
|
||||||
set(PYOSMIUM_PATH "${PYOSMIUM}")
|
|
||||||
message(STATUS "Using pyosmium-get-changes at ${PYOSMIUM_PATH}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
find_program(PG_CONFIG pg_config)
|
|
||||||
execute_process(COMMAND ${PG_CONFIG} --pgxs
|
|
||||||
OUTPUT_VARIABLE PGXS
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
||||||
|
|
||||||
if (NOT EXISTS "${PGXS}")
|
|
||||||
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})
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# Setup settings and paths
|
|
||||||
#
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
set(CUSTOMFILES
|
|
||||||
settings/phrase_settings.php
|
|
||||||
website/deletable.php
|
|
||||||
website/details.php
|
|
||||||
website/hierarchy.php
|
|
||||||
website/lookup.php
|
|
||||||
website/polygons.php
|
|
||||||
website/reverse.php
|
|
||||||
website/search.php
|
|
||||||
website/status.php
|
|
||||||
utils/blocks.php
|
|
||||||
utils/country_languages.php
|
|
||||||
utils/imports.php
|
|
||||||
utils/importWikipedia.php
|
|
||||||
utils/export.php
|
|
||||||
utils/query.php
|
|
||||||
utils/server_compare.php
|
|
||||||
utils/setup.php
|
|
||||||
utils/specialphrases.php
|
|
||||||
utils/update.php
|
|
||||||
utils/warm.php
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach (cfile ${CUSTOMFILES})
|
|
||||||
configure_file(${PROJECT_SOURCE_DIR}/${cfile} ${PROJECT_BINARY_DIR}/${cfile})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
configure_file(${PROJECT_SOURCE_DIR}/settings/defaults.php ${PROJECT_BINARY_DIR}/settings/settings.php)
|
|
||||||
|
|
||||||
set(WEBPATHS css images js)
|
|
||||||
|
|
||||||
foreach (wp ${WEBPATHS})
|
|
||||||
execute_process(
|
|
||||||
COMMAND ln -sf ${PROJECT_SOURCE_DIR}/website/${wp} ${PROJECT_BINARY_DIR}/website/
|
|
||||||
)
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# Tests
|
|
||||||
#
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
include(CTest)
|
|
||||||
|
|
||||||
set(TEST_BDD db osm2pgsql api)
|
|
||||||
|
|
||||||
foreach (test ${TEST_BDD})
|
|
||||||
add_test(NAME bdd_${test}
|
|
||||||
COMMAND lettuce features/${test}
|
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests)
|
|
||||||
set_tests_properties(bdd_${test}
|
|
||||||
PROPERTIES ENVIRONMENT "NOMINATIM_DIR=${PROJECT_BINARY_DIR}")
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
add_test(NAME php
|
|
||||||
COMMAND phpunit ./
|
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests-php)
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
add_subdirectory(module)
|
|
||||||
add_subdirectory(nominatim)
|
|
||||||
add_subdirectory(docs)
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
102
CONTRIBUTING.md
102
CONTRIBUTING.md
@@ -1,102 +0,0 @@
|
|||||||
# Nominatim contribution guidelines
|
|
||||||
|
|
||||||
## Reporting Bugs
|
|
||||||
|
|
||||||
Bugs can be reported at https://github.com/openstreetmap/Nominatim/issues.
|
|
||||||
Please always open a separate issue for each problem. In particular, do
|
|
||||||
not add your bugs to closed issues. They may looks similar to you but
|
|
||||||
often are completely different from the maintainer's point of view.
|
|
||||||
|
|
||||||
### When Reporting Bad Search Results...
|
|
||||||
|
|
||||||
Please make sure to add the following information:
|
|
||||||
|
|
||||||
* the URL of the query that produces the bad result
|
|
||||||
* the result you are getting
|
|
||||||
* the expected result, preferably a link to the OSM object you want to find,
|
|
||||||
otherwise an address that is as precise as possible
|
|
||||||
|
|
||||||
To get the link to the OSM object, you can try the following:
|
|
||||||
|
|
||||||
* go to https://openstreetmap.org
|
|
||||||
* zoom to the area of the map where you expect the result and
|
|
||||||
zoom in as much as possible
|
|
||||||
* click on the question mark on the right side of the map,
|
|
||||||
then with the queston cursor on the map where your object is located
|
|
||||||
* find the object of interest in the list that appears on the left side
|
|
||||||
* click on the object and report the URL back that the browser shows
|
|
||||||
|
|
||||||
### When Reporting Problems with your Installation...
|
|
||||||
|
|
||||||
Please add the following information to your issue:
|
|
||||||
|
|
||||||
* hardware configuration: RAM size, CPUs, kind and size of disks
|
|
||||||
* Operating system (also mention if you are running on a cloud service)
|
|
||||||
* Postgres and Postgis version
|
|
||||||
* list of settings you changed in your Postgres configuration
|
|
||||||
* Nominatim version (release version or,
|
|
||||||
if you run from the git repo, the output of `git rev-parse HEAD`)
|
|
||||||
* (if applicable) exact command line of the command that was causing the issue
|
|
||||||
|
|
||||||
|
|
||||||
## Workflow for Pull Requests
|
|
||||||
|
|
||||||
We love to get pull requests from you. We operate the "Fork & Pull" model
|
|
||||||
explained at
|
|
||||||
|
|
||||||
https://help.github.com/articles/using-pull-requests
|
|
||||||
|
|
||||||
You should fork the project into your own repo, create a topic branch
|
|
||||||
there and then make one or more pull requests back to the openstreetmap repository.
|
|
||||||
Your pull requests will then be reviewed and discussed. Please be aware
|
|
||||||
that you are responsible for your pull requests. You should be prepared
|
|
||||||
to get change requests because as the maintainers we have to make sure
|
|
||||||
that your contribution fits well with the rest of the code. Please make
|
|
||||||
sure that you have time to react to these comments and amend the code or
|
|
||||||
engage in a conversion. Do not expect that others will pick up your code,
|
|
||||||
it will almost never happen.
|
|
||||||
|
|
||||||
Please open a separate pull request for each issue you want to address.
|
|
||||||
Don't mix multiple changes. In particular, don't mix style cleanups with
|
|
||||||
feature pull requests. If you plan to make larger changes, please open
|
|
||||||
an issue first or comment on the appropriate issue already existing so
|
|
||||||
that duplicate work can be avoided.
|
|
||||||
|
|
||||||
## Coding style
|
|
||||||
|
|
||||||
Nominatim historically hasn't followed a particular coding style but we
|
|
||||||
are in process of consolidating the style. The following rules apply:
|
|
||||||
|
|
||||||
* Python code uses the official Python style
|
|
||||||
* indention
|
|
||||||
* SQL use 2 spaces
|
|
||||||
* all other file types use 4 spaces
|
|
||||||
* [BSD style](https://en.wikipedia.org/wiki/Indent_style#Allman_style) for braces
|
|
||||||
* spaces
|
|
||||||
* spaces before and after equal signs and operators
|
|
||||||
* no trailing spaces
|
|
||||||
* no spaces after opening and before closing bracket
|
|
||||||
* leave out space between a function name and bracket
|
|
||||||
but add one between control statement(if, while, etc.) and bracket
|
|
||||||
* for PHP variables use CamelCase with a prefixing letter indicating the type
|
|
||||||
(i - integer, f - float, a - array, s - string, o - object)
|
|
||||||
|
|
||||||
The coding style is enforced with PHPCS and can be tested with:
|
|
||||||
|
|
||||||
```
|
|
||||||
phpcs --report-width=120 --colors .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
Before submitting a pull request make sure that the following tests pass:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd test/bdd
|
|
||||||
behave -DBUILDDIR=<builddir> db osm2pgsql
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
cd test/php
|
|
||||||
phpunit ./
|
|
||||||
```
|
|
||||||
172
ChangeLog
172
ChangeLog
@@ -1,137 +1,7 @@
|
|||||||
3.2.0
|
2.0.1
|
||||||
|
|
||||||
* complete rewrite of reverse search algorithm
|
* delete outdated entries from location_area_country
|
||||||
* add new geojson and geocodejson output formats
|
* remove remaining uses of INTEGER, to allow node ids larger than 2^31
|
||||||
* add simple export script to exprot addresses to CSV
|
|
||||||
* remove is_in terms from address computation
|
|
||||||
* remove unused search_name_country tables
|
|
||||||
* various smaller fixes to query parsing
|
|
||||||
* convert Tokens and token types to class types
|
|
||||||
* correctly handle update when boundary object type is changed
|
|
||||||
* improve debug output for /search endpoint
|
|
||||||
* update to latest osm2pgsql and leaflet.js
|
|
||||||
* overhaul of /details endpoint:
|
|
||||||
* new class parameter when using osmtype/osmid parameters
|
|
||||||
* permalink to instance-independent osmtype/osmid parameter format
|
|
||||||
* new json output format
|
|
||||||
* update CentOS vagrant machine to use SELinux
|
|
||||||
* add vagrant scripts for Ubuntu 18.04
|
|
||||||
* fix build process for BSD
|
|
||||||
* enable running the database on a different host than the setup scripts
|
|
||||||
* allow to configure use of custom PHP binaries (PHP_BIN)
|
|
||||||
* extensive coding style improvements to PHP code
|
|
||||||
* more PHP unit tests for new classes
|
|
||||||
* increase coverage for API tests
|
|
||||||
* add documentation for API
|
|
||||||
|
|
||||||
3.1.0
|
|
||||||
|
|
||||||
* rework postcode handling and introduce location_postcode table
|
|
||||||
* make setup less verbose and print a summary in the end
|
|
||||||
* setup: error out when web site user does not exist
|
|
||||||
* add more API tests to complete code coverage
|
|
||||||
* reinstate key-value amenity search (special term [key=value])
|
|
||||||
* fix detection of coordinates in query
|
|
||||||
* various smaller tweaks to ranking of search interpretations
|
|
||||||
* complete overhaul of PHP frontend code using OOP
|
|
||||||
* add address rank to details page
|
|
||||||
* update Tiger scripts for 2017 data and clean up unused code
|
|
||||||
* various bug fixes and improvements to UI
|
|
||||||
* improve reverse geocoding performance close to coasts
|
|
||||||
* more PHP style cleanup (quoting)
|
|
||||||
* allow unnamed road in reverse geocoding to avoid too far off results
|
|
||||||
* add function to recalculate counts for full-word search term
|
|
||||||
* add function to check if new updates are available
|
|
||||||
* update documentation and switch to mkdocs for generating HTML
|
|
||||||
|
|
||||||
3.0.1
|
|
||||||
|
|
||||||
* fix bug in geometry building algorithm in osm2pgsql
|
|
||||||
* fix typos in build instructions
|
|
||||||
|
|
||||||
3.0.0
|
|
||||||
|
|
||||||
* move to cmake build system
|
|
||||||
* various fixes to HTML output
|
|
||||||
* reverse endpoint now can return geometries
|
|
||||||
* add support for PHP7
|
|
||||||
* move to on-the-fly computation of interpolations
|
|
||||||
* improve handling of linked places (updating)
|
|
||||||
* improve test framework:
|
|
||||||
* replace lettuce with behave
|
|
||||||
* use smaller database for API tests
|
|
||||||
* drop support for postgres < 9.1, postgis < 2.0 and PHP < 5.4
|
|
||||||
* make external data use optional (useful for imports without US)
|
|
||||||
* detect postgres and postgis versions automatically
|
|
||||||
* clean up query logging and remove unused tables
|
|
||||||
* move installation documentation into this repo
|
|
||||||
* add self-documenting vagrant scripts
|
|
||||||
* remove --create-website, recommend to use website directory in build
|
|
||||||
* add accessor functions for URL parameters and improve erro checking
|
|
||||||
* remove IP blocking and rate-limiting code
|
|
||||||
* enable CI via travis
|
|
||||||
* reformatting for more consistent coding style
|
|
||||||
* make country search term creation part of setup
|
|
||||||
* update country names and country grid
|
|
||||||
* handle roads that cross boundaries better
|
|
||||||
* keep full information on address tags
|
|
||||||
* update to refactored osm2pgsql which use libosmium based types
|
|
||||||
* switch from osmosis to pyosmium for updates
|
|
||||||
* be more strict when matching against special search terms
|
|
||||||
* handle postcode entries with mutliple values correctly
|
|
||||||
|
|
||||||
2.5
|
|
||||||
|
|
||||||
* reverse geocoding includes looking up housenumbers from Tiger data
|
|
||||||
* added parameter to return simplified geometries
|
|
||||||
* new lookup call for getting address information for OSM objects
|
|
||||||
* new namedetails and extratags parameters that expose the name and extratags
|
|
||||||
fields of the placex table
|
|
||||||
* mobile website
|
|
||||||
* reverse web view
|
|
||||||
|
|
||||||
2.4
|
|
||||||
|
|
||||||
* drop support for postgres 8.4
|
|
||||||
* rewrite address interpolation
|
|
||||||
* switch to C++ version of osm2pgsql and rewrite tag filtering
|
|
||||||
* support for bridge:name and tunnel:name, man_made, junction
|
|
||||||
* drop way-node index (reduces database size by about 15%)
|
|
||||||
* add support for configuring tablespaces and webserver user
|
|
||||||
* better evaluation of search queries in right-to-left notation
|
|
||||||
* improve house number search for streets with many duplicate entries
|
|
||||||
* code cleanup (remove unused functions and tables)
|
|
||||||
|
|
||||||
2.3
|
|
||||||
|
|
||||||
* further improve ordering of results
|
|
||||||
* support for more lat/lon formats in search-as-reverse
|
|
||||||
* fix handling of GB postcodes
|
|
||||||
* new functional test suite
|
|
||||||
* support for waterway relations
|
|
||||||
* inherit postcodes from street to poi
|
|
||||||
* fix housenumber normalisation to find non-latin house numbers
|
|
||||||
* take viewbox into account for ordering of results
|
|
||||||
* pois may now inherit address tags from surrounding buildings
|
|
||||||
* improve what objects may participate in an address
|
|
||||||
* clean up handled class/type combinations to current OSM usage
|
|
||||||
* lots of bug fixes
|
|
||||||
|
|
||||||
2.2
|
|
||||||
|
|
||||||
* correct database rights for www-data
|
|
||||||
* add timestamps for update output
|
|
||||||
* load postgis via extension for postgis >= 2.0
|
|
||||||
* remove non-admin boundaries from addresses
|
|
||||||
* further improve ordering of results with same importance
|
|
||||||
* merge addr:postcode tags into object addresses
|
|
||||||
* include rank and importance in reverse geocode output
|
|
||||||
* replace ST_Line_Interpolate_Point with ST_LineInterpolatePoint
|
|
||||||
(for postgis >= 2.1)
|
|
||||||
* update osm2pgsql to latest version
|
|
||||||
* properly detect changes of admin_level
|
|
||||||
* remove landuses when name is removed
|
|
||||||
* smaller fixes
|
|
||||||
|
|
||||||
2.1
|
2.1
|
||||||
|
|
||||||
@@ -157,7 +27,37 @@
|
|||||||
* refactoring of front-end PHP code
|
* refactoring of front-end PHP code
|
||||||
* lots of smaller bug fixes
|
* lots of smaller bug fixes
|
||||||
|
|
||||||
2.0.1
|
2.2
|
||||||
|
|
||||||
* delete outdated entries from location_area_country
|
* correct database rights for www-data
|
||||||
* remove remaining uses of INTEGER, to allow node ids larger than 2^31
|
* add timestamps for update output
|
||||||
|
* load postgis via extension for postgis >= 2.0
|
||||||
|
* remove non-admin boundaries from addresses
|
||||||
|
* further improve ordering of results with same importance
|
||||||
|
* merge addr:postcode tags into object addresses
|
||||||
|
* include rank and importance in reverse geocode output
|
||||||
|
* replace ST_Line_Interpolate_Point with ST_LineInterpolatePoint
|
||||||
|
(for postgis >= 2.1)
|
||||||
|
* update osm2pgsql to latest version
|
||||||
|
* properly detect changes of admin_level
|
||||||
|
* remove landuses when name is removed
|
||||||
|
* smaller fixes
|
||||||
|
|
||||||
|
2.3
|
||||||
|
|
||||||
|
* further improve ordering of results
|
||||||
|
* support for more lat/lon formats in search-as-reverse
|
||||||
|
* fix handling of GB postcodes
|
||||||
|
* new functional test suite
|
||||||
|
* support for waterway relations
|
||||||
|
* inherit postcodes from street to poi
|
||||||
|
* fix housenumber normalisation to find non-latin house numbers
|
||||||
|
* take viewbox into account for ordering of results
|
||||||
|
* pois may now inherit address tags from surrounding buildings
|
||||||
|
* improve what objects may participate in an address
|
||||||
|
* clean up handled class/type combinations to current OSM usage
|
||||||
|
* lots of bug fixes
|
||||||
|
|
||||||
|
2.3.1
|
||||||
|
|
||||||
|
* fix parse error in replication state directories (fatal during setup)
|
||||||
|
|||||||
27
Makefile.am
Normal file
27
Makefile.am
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
ACLOCAL_AMFLAGS = -I osm2pgsql/m4
|
||||||
|
AUTOMAKE_OPTIONS = -Wno-portability
|
||||||
|
|
||||||
|
SUBDIRS = osm2pgsql module nominatim
|
||||||
|
|
||||||
|
NOMINATIM_SERVER ?= $(shell echo a | php -F lib/init.php -E 'echo CONST_Website_BaseURL."\n";')
|
||||||
|
NOMINATIM_DATABASE ?= $(shell echo a | php -F lib/init.php -E 'echo DB::parseDSN(CONST_Database_DSN)["database"];')
|
||||||
|
|
||||||
|
install:
|
||||||
|
@echo Nominatim needs to be executed directly from this directory. No install necessary.
|
||||||
|
|
||||||
|
test:
|
||||||
|
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} lettuce -t -Fail -t -poldi-only
|
||||||
|
|
||||||
|
test-fast:
|
||||||
|
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} NOMINATIM_REUSE_TEMPLATE=1 lettuce -t -Fail -t -poldi-only
|
||||||
|
|
||||||
|
test-db:
|
||||||
|
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} lettuce -t -Fail -t -poldi-only features/db
|
||||||
|
|
||||||
|
test-db-fast:
|
||||||
|
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} NOMINATIM_REUSE_TEMPLATE=1 lettuce -t -Fail -t -poldi-only features/db
|
||||||
|
|
||||||
|
test-api:
|
||||||
|
cd tests; NOMINATIM_SERVER=${NOMINATIM_SERVER} lettuce -t -Fail -t -poldi-only features/api
|
||||||
|
|
||||||
|
.PHONY: test test-fast test-db test-db-fast test-api
|
||||||
56
README
Normal file
56
README
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
Nominatim
|
||||||
|
=========
|
||||||
|
|
||||||
|
Nominatim (from the Latin, 'by name') is a tool to search OpenStreetMap data
|
||||||
|
by name and address (geocoding) and to generate synthetic addresses of
|
||||||
|
OSM points (reverse geocoding). An instance with up-to-date data can be found
|
||||||
|
at http://nominatim.openstreetmap.org. Nominatim is also used as one of the
|
||||||
|
sources for the Search box on the OpenStreetMap home page and powers the search
|
||||||
|
on the MapQuest Open Initiative websites.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
=============
|
||||||
|
|
||||||
|
More information about Nominatim, including usage and installation instructions,
|
||||||
|
can be found in the OSM wiki at:
|
||||||
|
|
||||||
|
http://wiki.openstreetmap.org/wiki/Nominatim
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
The following instructions is a quick guide to installation. A more detailed guide
|
||||||
|
how to set up your own instance of Nominatim can be found in the wiki:
|
||||||
|
|
||||||
|
http://wiki.openstreetmap.org/wiki/Nominatim/Installation
|
||||||
|
|
||||||
|
Installation steps:
|
||||||
|
|
||||||
|
0. If checking out from git run:
|
||||||
|
|
||||||
|
./autogen.sh
|
||||||
|
|
||||||
|
1. Compile Nominatim:
|
||||||
|
|
||||||
|
./configure [--enable-64bit-ids]
|
||||||
|
make
|
||||||
|
|
||||||
|
2. Get OSM data and import:
|
||||||
|
|
||||||
|
./utils/setup.php --osm-file <your planet file> --all
|
||||||
|
|
||||||
|
3. Point your webserver to the ./website directory.
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
|
||||||
|
The source code is available under a GPLv2 license.
|
||||||
|
|
||||||
|
Contact and Bugreports
|
||||||
|
======================
|
||||||
|
|
||||||
|
For questions you can join the geocoding mailinglist, see
|
||||||
|
http://lists.openstreetmap.org/listinfo/geocoding
|
||||||
|
|
||||||
|
Bugs may be reported on the github project site:
|
||||||
|
https://github.com/twain47/Nominatim
|
||||||
57
README.md
57
README.md
@@ -1,57 +0,0 @@
|
|||||||
[](https://travis-ci.org/openstreetmap/Nominatim)
|
|
||||||
|
|
||||||
Nominatim
|
|
||||||
=========
|
|
||||||
|
|
||||||
Nominatim (from the Latin, 'by name') is a tool to search OpenStreetMap data
|
|
||||||
by name and address (geocoding) and to generate synthetic addresses of
|
|
||||||
OSM points (reverse geocoding). An instance with up-to-date data can be found
|
|
||||||
at https://nominatim.openstreetmap.org. Nominatim is also used as one of the
|
|
||||||
sources for the Search box on the OpenStreetMap home page.
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
=============
|
|
||||||
|
|
||||||
The documentation of the latest development version is in the
|
|
||||||
`docs/` subdirectory. A HTML version can be found at
|
|
||||||
https://nominatim.org/release-docs/develop/ .
|
|
||||||
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
The latest stable release can be downloaded from https://nominatim.org.
|
|
||||||
There you can also find [installation instructions for the release](https://nominatim.org/release-docs/latest/admin/Installation).
|
|
||||||
|
|
||||||
Detailed installation instructions for the development version can be
|
|
||||||
found at [nominatim.org](https://nominatim.org/release-docs/develop/admin/Installation)
|
|
||||||
as well.
|
|
||||||
|
|
||||||
A quick summary of the necessary steps:
|
|
||||||
|
|
||||||
1. Compile Nominatim:
|
|
||||||
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make
|
|
||||||
|
|
||||||
2. Get OSM data and import:
|
|
||||||
|
|
||||||
./build/utils/setup.php --osm-file <your planet file> --all
|
|
||||||
|
|
||||||
3. Point your webserver to the ./build/website directory.
|
|
||||||
|
|
||||||
|
|
||||||
License
|
|
||||||
=======
|
|
||||||
|
|
||||||
The source code is available under a GPLv2 license.
|
|
||||||
|
|
||||||
Contact and Bug reports
|
|
||||||
======================
|
|
||||||
|
|
||||||
For questions you can join the geocoding mailinglist, see
|
|
||||||
https://lists.openstreetmap.org/listinfo/geocoding
|
|
||||||
|
|
||||||
Bugs may be reported on the github project site:
|
|
||||||
https://github.com/openstreetmap/Nominatim
|
|
||||||
186
VAGRANT.md
186
VAGRANT.md
@@ -1,186 +0,0 @@
|
|||||||
# Install Nominatim in a virtual machine for development and testing
|
|
||||||
|
|
||||||
This document describes how you can install Nominatim inside a Ubuntu 16
|
|
||||||
virtual machine on your desktop/laptop (host machine). The goal is to give
|
|
||||||
you a development environment to easily edit code and run the test suite
|
|
||||||
without affecting the rest of your system.
|
|
||||||
|
|
||||||
The installation can run largely unsupervised. You should expect 1h from
|
|
||||||
start to finish depending on how fast your computer and download speed
|
|
||||||
is.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
1. [Virtualbox](https://www.virtualbox.org/wiki/Downloads)
|
|
||||||
|
|
||||||
2. [Vagrant](https://www.vagrantup.com/downloads.html)
|
|
||||||
|
|
||||||
3. Nominatim
|
|
||||||
|
|
||||||
git clone --recursive https://github.com/openstreetmap/Nominatim.git
|
|
||||||
|
|
||||||
If you forgot `--recursive`, it you can later load the submodules using
|
|
||||||
|
|
||||||
git submodule init
|
|
||||||
git submodule update
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
1. Start the virtual machine
|
|
||||||
|
|
||||||
vagrant up ubuntu
|
|
||||||
|
|
||||||
2. Log into the virtual machine
|
|
||||||
|
|
||||||
vagrant ssh ubuntu
|
|
||||||
|
|
||||||
3. Import a small country (Monaco)
|
|
||||||
|
|
||||||
See the FAQ how to skip this step and point Nominatim to an existing database.
|
|
||||||
|
|
||||||
```
|
|
||||||
# inside the virtual machine:
|
|
||||||
cd build
|
|
||||||
wget --no-verbose --output-document=/tmp/monaco.osm.pbf http://download.geofabrik.de/europe/monaco-latest.osm.pbf
|
|
||||||
./utils/setup.php --osm-file /tmp/monaco.osm.pbf --osm2pgsql-cache 1000 --all 2>&1 | tee monaco.$$.log
|
|
||||||
```
|
|
||||||
|
|
||||||
To repeat an import you'd need to delete the database first
|
|
||||||
|
|
||||||
dropdb --if-exists nominatim
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
Vagrant maps the virtual machine's port 8089 to your host machine. Thus you can
|
|
||||||
see Nominatim in action on [locahost:8089](http://localhost:8089/nominatim/).
|
|
||||||
|
|
||||||
You edit code on your host machine in any editor you like. There is no need to
|
|
||||||
restart any software: just refresh your browser window.
|
|
||||||
|
|
||||||
Note that the webserver uses files from the /build directory. If you change
|
|
||||||
files in Nominatim/website or Nominatim/utils for example you first need to
|
|
||||||
copy them into the /build directory by running the `cmake` step from the
|
|
||||||
installation.
|
|
||||||
|
|
||||||
PHP errors are written to `/var/log/apache2/error.log`.
|
|
||||||
|
|
||||||
With `echo` and `var_dump()` you write into the output (HTML/XML/JSON) when
|
|
||||||
you either add `&debug=1` to the URL (preferred) or set
|
|
||||||
`@define('CONST_Debug', true);` in `settings/local.php`.
|
|
||||||
|
|
||||||
In the Python BDD test you can use `logger.info()` for temporary debug
|
|
||||||
statements.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Running unit tests
|
|
||||||
|
|
||||||
cd ~/Nominatim/tests/php
|
|
||||||
phpunit ./
|
|
||||||
|
|
||||||
|
|
||||||
## Running PHP code style tests
|
|
||||||
|
|
||||||
cd ~/Nominatim
|
|
||||||
phpcs --colors .
|
|
||||||
|
|
||||||
|
|
||||||
## Running functional tests
|
|
||||||
|
|
||||||
Tests in `test/bdd/db` and `test/bdd/osm2pgsql` have to pass 100%. Other
|
|
||||||
tests might require full planet-wide data. Sadly even if you have your own
|
|
||||||
planet-wide data there will be enough differences to the openstreetmap.org
|
|
||||||
installation to cause false positives in the other tests (see FAQ).
|
|
||||||
|
|
||||||
To run the full test suite
|
|
||||||
|
|
||||||
cd ~/Nominatim/test/bdd
|
|
||||||
behave -DBUILDDIR=/home/vagrant/build/ db osm2pgsql
|
|
||||||
|
|
||||||
To run a single file
|
|
||||||
|
|
||||||
behave -DBUILDDIR=/home/vagrant/build/ api/lookup/simple.feature
|
|
||||||
|
|
||||||
Or a single test by line number
|
|
||||||
|
|
||||||
behave -DBUILDDIR=/home/vagrant/build/ api/lookup/simple.feature:34
|
|
||||||
|
|
||||||
To run specific groups of tests you can add tags just before the `Scenario line`, e.g.
|
|
||||||
|
|
||||||
@bug-34
|
|
||||||
Scenario: address lookup for non-existing or invalid node, way, relation
|
|
||||||
|
|
||||||
and then
|
|
||||||
|
|
||||||
behave -DBUILDDIR=/home/vagrant/build/ --tags @bug-34
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
##### Will it run on Windows?
|
|
||||||
|
|
||||||
Yes, Vagrant and Virtualbox can be installed on MS Windows just fine. You need a 64bit
|
|
||||||
version of Windows.
|
|
||||||
|
|
||||||
|
|
||||||
##### Why Monaco, can I use another country?
|
|
||||||
|
|
||||||
Of course! The Monaco import takes less than 30 minutes and works with 2GB RAM.
|
|
||||||
|
|
||||||
##### Will the results be the same as those from nominatim.openstreetmap.org?
|
|
||||||
|
|
||||||
No. Long running Nominatim installations will differ once new import features (or
|
|
||||||
bug fixes) get added since those usually only get applied to new/changed data.
|
|
||||||
|
|
||||||
Also this document skips the optional Wikipedia data import which affects ranking
|
|
||||||
of search results. See [Nominatim installation](http://nominatim.org/release-docs/latest/Installation) for details.
|
|
||||||
|
|
||||||
##### Why Ubuntu? Can I test CentOS/Fedora/CoreOS/FreeBSD?
|
|
||||||
|
|
||||||
There is a Vagrant script for CentOS available, but the Nominatim directory
|
|
||||||
isn't symlinked/mounted to the host which makes development trickier. We used
|
|
||||||
it mainly for debugging installation with SELinux.
|
|
||||||
|
|
||||||
In general Nominatim will run in the other environments. The installation steps
|
|
||||||
are slightly different, e.g. the name of the package manager, Apache2 package
|
|
||||||
name, location of files. We chose Ubuntu because that is closest to the
|
|
||||||
nominatim.openstreetmap.org production environment.
|
|
||||||
|
|
||||||
You can configure/download other Vagrant boxes from [https://app.vagrantup.com/boxes/search](https://app.vagrantup.com/boxes/search).
|
|
||||||
|
|
||||||
##### How can I connect to an existing database?
|
|
||||||
|
|
||||||
Let's say you have a Postgres database named `nominatim_it` on server `your-server.com` and port `5432`. The Postgres username is `postgres`. You can edit `settings/local.php` and point Nominatim to it.
|
|
||||||
|
|
||||||
pgsql://postgres@your-server.com:5432/nominatim_it
|
|
||||||
|
|
||||||
No data import necessary or restarting necessary.
|
|
||||||
|
|
||||||
If the Postgres installation is behind a firewall, you can try
|
|
||||||
|
|
||||||
ssh -L 9999:localhost:5432 your-username@your-server.com
|
|
||||||
|
|
||||||
inside the virtual machine. It will map the port to `localhost:9999` and then
|
|
||||||
you edit `settings/local.php` with
|
|
||||||
|
|
||||||
@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`
|
|
||||||
|
|
||||||
|
|
||||||
##### My computer is slow and the import takes too long. Can I start the virtual machine "in the cloud"?
|
|
||||||
|
|
||||||
Yes. It's possible to start the virtual machine on [Amazon AWS (plugin)](https://github.com/mitchellh/vagrant-aws)
|
|
||||||
or [DigitalOcean (plugin)](https://github.com/smdahlen/vagrant-digitalocean).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
61
Vagrantfile
vendored
61
Vagrantfile
vendored
@@ -1,61 +0,0 @@
|
|||||||
# -*- mode: ruby -*-
|
|
||||||
# vi: set ft=ruby :
|
|
||||||
|
|
||||||
Vagrant.configure("2") do |config|
|
|
||||||
# Apache webserver
|
|
||||||
config.vm.network "forwarded_port", guest: 80, host: 8089
|
|
||||||
|
|
||||||
# If true, then any SSH connections made will enable agent forwarding.
|
|
||||||
config.ssh.forward_agent = true
|
|
||||||
|
|
||||||
checkout = "yes"
|
|
||||||
if ENV['CHECKOUT'] != 'y' then
|
|
||||||
config.vm.synced_folder ".", "/home/vagrant/Nominatim"
|
|
||||||
checkout = "no"
|
|
||||||
end
|
|
||||||
|
|
||||||
config.vm.define "ubuntu", primary: true do |sub|
|
|
||||||
sub.vm.box = "bento/ubuntu-18.04"
|
|
||||||
sub.vm.provision :shell do |s|
|
|
||||||
s.path = "vagrant/Install-on-Ubuntu-18.sh"
|
|
||||||
s.privileged = false
|
|
||||||
s.args = [checkout]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
config.vm.define "ubuntu16" do |sub|
|
|
||||||
sub.vm.box = "bento/ubuntu-16.04"
|
|
||||||
sub.vm.provision :shell do |s|
|
|
||||||
s.path = "vagrant/Install-on-Ubuntu-16.sh"
|
|
||||||
s.privileged = false
|
|
||||||
s.args = [checkout]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
config.vm.define "travis" do |sub|
|
|
||||||
sub.vm.box = "bento/ubuntu-14.04"
|
|
||||||
sub.vm.provision :shell do |s|
|
|
||||||
s.path = "vagrant/install-on-travis-ci.sh"
|
|
||||||
s.privileged = false
|
|
||||||
s.args = [checkout]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
config.vm.define "centos" do |sub|
|
|
||||||
sub.vm.box = "centos/7"
|
|
||||||
sub.vm.provision :shell do |s|
|
|
||||||
s.path = "vagrant/Install-on-Centos-7.sh"
|
|
||||||
s.privileged = false
|
|
||||||
s.args = "yes"
|
|
||||||
end
|
|
||||||
sub.vm.synced_folder ".", "/home/vagrant/Nominatim", disabled: true
|
|
||||||
sub.vm.synced_folder ".", "/vagrant", disabled: true
|
|
||||||
end
|
|
||||||
|
|
||||||
config.vm.provider "virtualbox" do |vb|
|
|
||||||
vb.gui = false
|
|
||||||
vb.memory = 2048
|
|
||||||
vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate//vagrant","0"]
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
2
autogen.sh
Executable file
2
autogen.sh
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
autoreconf -vfi
|
||||||
65
configure.ac
Normal file
65
configure.ac
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
AC_INIT(Nominatim,2.3.1)
|
||||||
|
if git rev-parse HEAD 2>/dev/null >/dev/null; then
|
||||||
|
AC_SUBST([PACKAGE_VERSION], [$PACKAGE_VERSION-git-`git rev-parse --short HEAD`])
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl Required autoconf version
|
||||||
|
AC_PREREQ(2.61)
|
||||||
|
|
||||||
|
AM_INIT_AUTOMAKE([1.9.6 dist-bzip2 std-options check-news])
|
||||||
|
|
||||||
|
dnl Additional macro definitions are in here
|
||||||
|
AC_CONFIG_MACRO_DIR([osm2pgsql/m4])
|
||||||
|
|
||||||
|
dnl Generate configuration header file
|
||||||
|
AC_CONFIG_HEADER(nominatim/config.h)
|
||||||
|
|
||||||
|
|
||||||
|
dnl Find C compiler
|
||||||
|
AC_PROG_CC
|
||||||
|
|
||||||
|
dnl Find C++ compiler
|
||||||
|
AC_PROG_CXX
|
||||||
|
|
||||||
|
dnl pthread
|
||||||
|
AX_PTHREAD([], [AC_MSG_ERROR([pthread library required])])
|
||||||
|
|
||||||
|
dnl Check for Geos library
|
||||||
|
AX_LIB_GEOS
|
||||||
|
if test "x$GEOS_VERSION" = "x"
|
||||||
|
then
|
||||||
|
AC_MSG_ERROR([required library not found]);
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl Check for Proj library
|
||||||
|
AX_LIB_PROJ
|
||||||
|
if test "$HAVE_PROJ" = "no"
|
||||||
|
then
|
||||||
|
AC_MSG_ERROR([required library not found]);
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
dnl Check for PostgresSQL client library
|
||||||
|
AX_LIB_POSTGRESQL(8.4)
|
||||||
|
if test "x$POSTGRESQL_VERSION" = "x"
|
||||||
|
then
|
||||||
|
AC_MSG_ERROR([postgresql client library not found])
|
||||||
|
fi
|
||||||
|
if test ! -f "$POSTGRESQL_PGXS"
|
||||||
|
then
|
||||||
|
AC_MSG_ERROR([postgresql server development library not found])
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl Check for bzip2 library
|
||||||
|
AX_LIB_BZIP2
|
||||||
|
if test "$HAVE_BZIP2" = "no"
|
||||||
|
then
|
||||||
|
AC_MSG_ERROR([required library not found]);
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl Check for libxml2 library
|
||||||
|
AM_PATH_XML2
|
||||||
|
|
||||||
|
AC_CONFIG_SUBDIRS([osm2pgsql])
|
||||||
|
|
||||||
|
AC_OUTPUT(Makefile nominatim/Makefile module/Makefile)
|
||||||
36
contrib/openlayers.cfg
Normal file
36
contrib/openlayers.cfg
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# This file includes a small subset of OpenLayers code, designed to be
|
||||||
|
# integrated into another application. It includes only the Layer types
|
||||||
|
# neccesary to create tiled or untiled WMS, and does not include any Controls.
|
||||||
|
# This is the result of what was at the time called "Webmap.js" at the FOSS4G
|
||||||
|
# Web Mapping BOF.
|
||||||
|
|
||||||
|
[first]
|
||||||
|
|
||||||
|
[last]
|
||||||
|
|
||||||
|
[include]
|
||||||
|
OpenLayers/Map.js
|
||||||
|
OpenLayers/Kinetic.js
|
||||||
|
OpenLayers/Geometry/MultiLineString.js
|
||||||
|
OpenLayers/Geometry/MultiPolygon.js
|
||||||
|
OpenLayers/Format/WKT.js
|
||||||
|
OpenLayers/Layer/OSM.js
|
||||||
|
OpenLayers/Layer/Vector.js
|
||||||
|
OpenLayers/Layer/SphericalMercator.js
|
||||||
|
OpenLayers/Control/Attribution.js
|
||||||
|
OpenLayers/Control/KeyboardDefaults.js
|
||||||
|
OpenLayers/Control/Navigation.js
|
||||||
|
OpenLayers/Control/MousePosition.js
|
||||||
|
OpenLayers/Control/PanZoomBar.js
|
||||||
|
OpenLayers/Control/Permalink.js
|
||||||
|
OpenLayers/Control/TouchNavigation.js
|
||||||
|
OpenLayers/Style.js
|
||||||
|
OpenLayers/Protocol/HTTP.js
|
||||||
|
OpenLayers/Projection.js
|
||||||
|
OpenLayers/Renderer/SVG.js
|
||||||
|
OpenLayers/Renderer/VML.js
|
||||||
|
OpenLayers/Renderer/Canvas.js
|
||||||
|
|
||||||
|
[exclude]
|
||||||
|
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
25524
data/country_osm_grid.sql
Normal file
25524
data/country_osm_grid.sql
Normal file
File diff suppressed because one or more lines are too long
@@ -24,3 +24,4 @@ CREATE TABLE gb_postcode (
|
|||||||
CONSTRAINT enforce_srid_geometry CHECK ((st_srid(geometry) = 4326))
|
CONSTRAINT enforce_srid_geometry CHECK ((st_srid(geometry) = 4326))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
GRANT SELECT ON TABLE gb_postcode TO "www-data";
|
||||||
|
|||||||
70498
data/us_postcode.sql
70498
data/us_postcode.sql
File diff suppressed because it is too large
Load Diff
2930
data/us_state.sql
Normal file
2930
data/us_state.sql
Normal file
File diff suppressed because one or more lines are too long
6198
data/us_statecounty.sql
Normal file
6198
data/us_statecounty.sql
Normal file
File diff suppressed because one or more lines are too long
79349
data/words.sql
79349
data/words.sql
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
|||||||
# Auto-generated vagrant install documentation
|
|
||||||
|
|
||||||
|
|
||||||
# build the actual documentation
|
|
||||||
|
|
||||||
configure_file(mkdocs.yml ../mkdocs.yml)
|
|
||||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/appendix)
|
|
||||||
|
|
||||||
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}/develop ${CMAKE_CURRENT_BINARY_DIR}/develop
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/api ${CMAKE_CURRENT_BINARY_DIR}/api
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/index.md ${CMAKE_CURRENT_BINARY_DIR}/index.md
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/extra.css ${CMAKE_CURRENT_BINARY_DIR}/extra.css
|
|
||||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Centos-7.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Centos-7.md
|
|
||||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Ubuntu-16.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Ubuntu-16.md
|
|
||||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/bash2md.sh ${PROJECT_SOURCE_DIR}/vagrant/Install-on-Ubuntu-18.sh ${CMAKE_CURRENT_BINARY_DIR}/appendix/Install-on-Ubuntu-18.md
|
|
||||||
COMMAND mkdocs build -d ${CMAKE_CURRENT_BINARY_DIR}/../site-html -f ${CMAKE_CURRENT_BINARY_DIR}/../mkdocs.yml
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
# Troubleshooting Nominatim Installations
|
|
||||||
|
|
||||||
## Installation Issues
|
|
||||||
|
|
||||||
### Can a stopped/killed import process be resumed?
|
|
||||||
|
|
||||||
"I accidentally killed the import process after it has been running for many hours. Can it be resumed?"
|
|
||||||
|
|
||||||
It is possible if the import already got to the indexing stage.
|
|
||||||
Check the last line of output that was logged before the process
|
|
||||||
was killed. If it looks like this:
|
|
||||||
|
|
||||||
|
|
||||||
Done 844 in 13 @ 64.923080 per second - Rank 26 ETA (seconds): 7.886255
|
|
||||||
|
|
||||||
then you can resume with the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./utils/setup.php --index --create-search-indices --create-country-names
|
|
||||||
```
|
|
||||||
|
|
||||||
If the reported rank is 26 or higher, you can also safely add `--index-noanalyse`.
|
|
||||||
|
|
||||||
|
|
||||||
### PHP "open_basedir restriction in effect" warnings
|
|
||||||
|
|
||||||
`PHP Warning: file_get_contents(): open_basedir restriction in effect.`
|
|
||||||
|
|
||||||
You need to adjust the [open_basedir](http://www.php.net/manual/en/ini.core.php#ini.open-basedir) setting
|
|
||||||
in your PHP configuration (`php.ini file`). By default this setting may look like this:
|
|
||||||
|
|
||||||
open_basedir = /srv/http/:/home/:/tmp/:/usr/share/pear/
|
|
||||||
|
|
||||||
Either add reported directories to the list or disable this setting temporarily by
|
|
||||||
dding ";" at the beginning of the line. Don't forget to enable this setting again
|
|
||||||
once you are done with the PHP command line operations.
|
|
||||||
|
|
||||||
|
|
||||||
### PHP timzeone warnings
|
|
||||||
|
|
||||||
The Apache log may contain lots of PHP warnings like this:
|
|
||||||
`PHP Warning: date_default_timezone_set() function.`
|
|
||||||
|
|
||||||
You should set the default time zone as instructed in the warning in
|
|
||||||
your `php.ini` file. Find the entry about timezone and set it to
|
|
||||||
something like this:
|
|
||||||
|
|
||||||
; Defines the default timezone used by the date functions
|
|
||||||
; http://php.net/date.timezone
|
|
||||||
date.timezone = 'America/Denver'
|
|
||||||
|
|
||||||
Or
|
|
||||||
|
|
||||||
```
|
|
||||||
echo "date.timezone = 'America/Denver'" > /etc/php.d/timezone.ini
|
|
||||||
```
|
|
||||||
|
|
||||||
### nominatim.so version mismatch
|
|
||||||
|
|
||||||
When running the import you may get a version mismatch:
|
|
||||||
`COPY_END for place failed: ERROR: incompatible library "/srv/Nominatim/nominatim/build/module/nominatim.so": version mismatch`
|
|
||||||
|
|
||||||
pg_config seems to use bad includes sometimes when multiple versions
|
|
||||||
of PostgreSQL are available in the system. Make sure you remove the
|
|
||||||
server development libraries (`postgresql-server-dev-9.5` on Ubuntu)
|
|
||||||
and recompile (`cmake .. && make`).
|
|
||||||
|
|
||||||
|
|
||||||
### I see the error: "function transliteration(text) does not exist"
|
|
||||||
|
|
||||||
Reinstall the nominatim functions with `setup.php --create--functions`
|
|
||||||
and check for any errors, e.g. a missing `nominatim.so` file.
|
|
||||||
|
|
||||||
|
|
||||||
### The website shows: "Could not get word tokens"
|
|
||||||
|
|
||||||
The server cannot access your database. Add `&debug=1` to your URL
|
|
||||||
to get the full error message.
|
|
||||||
|
|
||||||
|
|
||||||
### On CentOS the website shows "Could not connect to server"
|
|
||||||
|
|
||||||
`could not connect to server: No such file or directory`
|
|
||||||
|
|
||||||
On CentOS v7 the PostgreSQL server is started with `systemd`.
|
|
||||||
Check if `/usr/lib/systemd/system/httpd.service` contains a line `PrivateTmp=true`.
|
|
||||||
If so then Apache cannot see the `/tmp/.s.PGSQL.5432` file. It's a good security feature,
|
|
||||||
so use the [preferred solution](../appendix/Install-on-Centos-7/#adding-selinux-security-settings).
|
|
||||||
|
|
||||||
However, you can solve this the quick and dirty way by commenting out that line and then run
|
|
||||||
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl restart httpd
|
|
||||||
|
|
||||||
|
|
||||||
### Website reports "DB Error: insufficient permissions"
|
|
||||||
|
|
||||||
The user the webserver, e.g. Apache, runs under needs to have access to the Nominatim database. You can find the user like [this](https://serverfault.com/questions/125865/finding-out-what-user-apache-is-running-as), for default Ubuntu operating system for example it's `www-data`.
|
|
||||||
|
|
||||||
1. Repeat the `createuser` step of the installation instructions.
|
|
||||||
|
|
||||||
2. Give the user permission to existing tables
|
|
||||||
|
|
||||||
```
|
|
||||||
GRANT usage ON SCHEMA public TO "www-data";
|
|
||||||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "www-data";
|
|
||||||
```
|
|
||||||
|
|
||||||
### Website reports "Could not load library "nominatim.so"
|
|
||||||
|
|
||||||
Example error message
|
|
||||||
|
|
||||||
```
|
|
||||||
SELECT make_standard_name('3039 E MEADOWLARK LN') [nativecode=ERROR: could not
|
|
||||||
load library "/srv/nominatim/Nominatim-3.1.0/build/module/nominatim.so":
|
|
||||||
/srv/nominatim/Nominatim-3.1.0/build/module/nominatim.so: cannot open shared
|
|
||||||
object file: Permission denied
|
|
||||||
CONTEXT: PL/pgSQL function make_standard_name(text) line 5 at assignment]
|
|
||||||
```
|
|
||||||
|
|
||||||
The Postgresql database, i.e. user postgres, needs to have access to that file.
|
|
||||||
|
|
||||||
The permission need to be read & executable by everybody, e.g.
|
|
||||||
|
|
||||||
```
|
|
||||||
-rwxr-xr-x 1 nominatim nominatim 297984 build/module/nominatim.so
|
|
||||||
```
|
|
||||||
|
|
||||||
Try `chmod a+r nominatim.so; chmod a+x nominatim.so`.
|
|
||||||
|
|
||||||
When running SELinux, make sure that the
|
|
||||||
[context is set up correctly](../appendix/Install-on-Centos-7/#adding-selinux-security-settings).
|
|
||||||
|
|
||||||
### Setup.php fails with "DB Error: extension not found"
|
|
||||||
|
|
||||||
Make sure you have the Postgres extensions hstore and postgis installed.
|
|
||||||
See the installation instruction for a full list of required packages.
|
|
||||||
|
|
||||||
|
|
||||||
### Setup.php reports "Cannot redeclare getDB()"
|
|
||||||
|
|
||||||
`Cannot redeclare getDB() (previously declared in /your/path/Nominatim/lib/db.php:4)`
|
|
||||||
|
|
||||||
The message is a bit misleading as PHP needs to load the file `DB.php` and
|
|
||||||
instead re-loads Nominatim's `db.php`. To solve this make sure you
|
|
||||||
have the [Pear module 'DB'](http://pear.php.net/package/DB/) installed.
|
|
||||||
|
|
||||||
sudo pear install DB
|
|
||||||
|
|
||||||
### I forgot to delete the flatnodes file before starting an import.
|
|
||||||
|
|
||||||
That's fine. For each import the flatnodes file get overwritten.
|
|
||||||
See [https://help.openstreetmap.org/questions/52419/nominatim-flatnode-storage]()
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
|
|
||||||
## Running your own instance
|
|
||||||
|
|
||||||
### Can I import multiple countries and keep them up to date?
|
|
||||||
|
|
||||||
You should use the extracts and updates from https://download.geofabrik.de.
|
|
||||||
For the initial import, download the countries you need and merge them.
|
|
||||||
See [OSM Help](https://help.openstreetmap.org/questions/48843/merging-two-or-more-geographical-areas-to-import-two-or-more-osm-files-in-nominatim)
|
|
||||||
for examples how to do that. Use the resulting single osm file when
|
|
||||||
running `setup.php`.
|
|
||||||
|
|
||||||
For updates you need to download the change files for each country
|
|
||||||
once per day and apply them **separately** using
|
|
||||||
|
|
||||||
./utils/update.php --import-diff <filename> --index
|
|
||||||
|
|
||||||
See [this issue](https://github.com/openstreetmap/Nominatim/issues/60#issuecomment-18679446)
|
|
||||||
for a script that runs the updates using osmosis.
|
|
||||||
|
|
||||||
### Can I import negative OSM ids into Nominatim?
|
|
||||||
|
|
||||||
See [this question of Stackoverflow](https://help.openstreetmap.org/questions/64662/nominatim-flatnode-with-negative-id).
|
|
||||||
|
|
||||||
### Missing XML or text declaration
|
|
||||||
|
|
||||||
The website might show: `XML Parsing Error: XML or text declaration not at start of entity Location.`
|
|
||||||
|
|
||||||
Make sure there are no spaces at the beginning of your `settings/local.php` file.
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
# Importing and Updating the Database
|
|
||||||
|
|
||||||
The following instructions explain how to create a Nominatim database
|
|
||||||
from an OSM planet file and how to keep the database up to date. It
|
|
||||||
is assumed that you have already successfully installed the Nominatim
|
|
||||||
software itself, if not return to the [installation page](Installation.md).
|
|
||||||
|
|
||||||
## Configuration setup in settings/local.php
|
|
||||||
|
|
||||||
The Nominatim server can be customized via the file `settings/local.php`
|
|
||||||
in the build directory. Note that this is a PHP file, so it must always
|
|
||||||
start like this:
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
without any leading spaces.
|
|
||||||
|
|
||||||
There are lots of configuration settings you can tweak. Have a look
|
|
||||||
at `settings/default.php` for a full list. Most should have a sensible default.
|
|
||||||
|
|
||||||
#### Flatnode files
|
|
||||||
|
|
||||||
If you plan to import a large dataset (e.g. Europe, North America, planet),
|
|
||||||
you should also enable flatnode storage of node locations. With this
|
|
||||||
setting enabled, node coordinates are stored in a simple file instead
|
|
||||||
of the database. This will save you import time and disk storage.
|
|
||||||
Add to your `settings/local.php`:
|
|
||||||
|
|
||||||
@define('CONST_Osm2pgsql_Flatnode_File', '/path/to/flatnode.file');
|
|
||||||
|
|
||||||
Replace the second part with a suitable path on your system and make sure
|
|
||||||
the directory exists. There should be at least 40GB of free space.
|
|
||||||
|
|
||||||
## Downloading additional data
|
|
||||||
|
|
||||||
### Wikipedia rankings
|
|
||||||
|
|
||||||
Wikipedia can be used as an optional auxiliary data source to help indicate
|
|
||||||
the importance of osm features. Nominatim will work without this information
|
|
||||||
but it will improve the quality of the results if this is installed.
|
|
||||||
This data is available as a binary download:
|
|
||||||
|
|
||||||
cd $NOMINATIM_SOURCE_DIR/data
|
|
||||||
wget https://www.nominatim.org/data/wikipedia_article.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
|
|
||||||
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
|
|
||||||
the initial import of the data if you want the rankings applied to the
|
|
||||||
loaded data.
|
|
||||||
|
|
||||||
### UK postcodes
|
|
||||||
|
|
||||||
Nominatim can use postcodes from an external source to improve searches that involve a UK postcode. This data can be optionally downloaded:
|
|
||||||
|
|
||||||
cd $NOMINATIM_SOURCE_DIR/data
|
|
||||||
wget https://www.nominatim.org/data/gb_postcode_data.sql.gz
|
|
||||||
|
|
||||||
|
|
||||||
## Initial import of the data
|
|
||||||
|
|
||||||
**Important:** first try the import with a small excerpt, for example from
|
|
||||||
[Geofabrik](https://download.geofabrik.de).
|
|
||||||
|
|
||||||
Download the data to import and load the data with the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./utils/setup.php --osm-file <data file> --all [--osm2pgsql-cache 28000] 2>&1 | tee setup.log
|
|
||||||
```
|
|
||||||
|
|
||||||
The `--osm2pgsql-cache` parameter is optional but strongly recommended for
|
|
||||||
planet imports. It sets the node cache size for the osm2pgsql import part
|
|
||||||
(see `-C` parameter in osm2pgsql help). As a rule of thumb, this should be
|
|
||||||
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.
|
|
||||||
|
|
||||||
Computing word frequency for search terms can improve the performance of
|
|
||||||
forward geocoding in particular under high load as it helps Postgres' query
|
|
||||||
planner to make the right decisions. To recompute word counts run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./utils/update.php --recompute-word-counts
|
|
||||||
```
|
|
||||||
|
|
||||||
This will take a couple of hours for a full planet installation. You can
|
|
||||||
also defer that step to a later point in time when you realise that
|
|
||||||
performance becomes an issue. Just make sure that updates are stopped before
|
|
||||||
running this function.
|
|
||||||
|
|
||||||
If you want to be able to search for places by their type through
|
|
||||||
[special key phrases](https://wiki.openstreetmap.org/wiki/Nominatim/Special_Phrases)
|
|
||||||
you also need to enable these key phrases like this:
|
|
||||||
|
|
||||||
./utils/specialphrases.php --wiki-import > specialphrases.sql
|
|
||||||
psql -d nominatim -f specialphrases.sql
|
|
||||||
|
|
||||||
Note that this command downloads the phrases from the wiki link above.
|
|
||||||
|
|
||||||
|
|
||||||
## Installing Tiger housenumber data for the US
|
|
||||||
|
|
||||||
Nominatim is able to use the official TIGER address set to complement the
|
|
||||||
OSM house number data in the US. You can add TIGER data to your own Nominatim
|
|
||||||
instance by following these steps:
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
cd Nominatim/data
|
|
||||||
wget https://nominatim.org/data/tiger2017-nominatim-preprocessed.tar.gz
|
|
||||||
tar xf tiger2017-nominatim-preprocessed.tar.gz
|
|
||||||
|
|
||||||
3. Import the data into your Nominatim database:
|
|
||||||
|
|
||||||
./utils/setup.php --import-tiger-data
|
|
||||||
|
|
||||||
4. Enable use of the Tiger data in your `settings/local.php` by adding:
|
|
||||||
|
|
||||||
@define('CONST_Use_US_Tiger_Data', true);
|
|
||||||
|
|
||||||
5. Apply the new settings:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./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
|
|
||||||
|
|
||||||
There are many different possibilities to update your Nominatim database.
|
|
||||||
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`.
|
|
||||||
|
|
||||||
#### Installing the newest version of Pyosmium
|
|
||||||
|
|
||||||
It is recommended to install Pyosmium via pip. Run (as the same user who
|
|
||||||
will later run the updates):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip install --user osmium
|
|
||||||
```
|
|
||||||
|
|
||||||
Nominatim needs a tool called `pyosmium-get-updates`, which comes with
|
|
||||||
Pyosmium. You need to tell Nominatim where to find it. Add the
|
|
||||||
following line to your `settings/local.php`:
|
|
||||||
|
|
||||||
@define('CONST_Pyosmium_Binary', '/home/user/.local/bin/pyosmium-get-changes');
|
|
||||||
|
|
||||||
The path above is fine if you used the `--user` parameter with pip.
|
|
||||||
Replace `user` with your user name.
|
|
||||||
|
|
||||||
#### Setting up the update process
|
|
||||||
|
|
||||||
Next the update needs to be initialised. By default Nominatim is configured
|
|
||||||
to update using the global minutely diffs.
|
|
||||||
|
|
||||||
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
|
|
||||||
diffs for Ireland from geofabrik add the following:
|
|
||||||
|
|
||||||
// base URL of the replication service
|
|
||||||
@define('CONST_Replication_Url', 'https://download.geofabrik.de/europe/ireland-and-northern-ireland-updates');
|
|
||||||
// How often upstream publishes diffs
|
|
||||||
@define('CONST_Replication_Update_Interval', '86400');
|
|
||||||
// How long to sleep if no update found yet
|
|
||||||
@define('CONST_Replication_Recheck_Interval', '900');
|
|
||||||
|
|
||||||
To set up the update process now run the following command:
|
|
||||||
|
|
||||||
./utils/update.php --init-updates
|
|
||||||
|
|
||||||
It outputs the date where updates will start. Recheck that this date is
|
|
||||||
what you expect.
|
|
||||||
|
|
||||||
The --init-updates command needs to be rerun whenever the replication service
|
|
||||||
is changed.
|
|
||||||
|
|
||||||
#### Updating Nominatim
|
|
||||||
|
|
||||||
The following command will keep your database constantly up to date:
|
|
||||||
|
|
||||||
./utils/update.php --import-osmosis-all
|
|
||||||
|
|
||||||
(Note that even though the old name "import-osmosis-all" has been kept for compatibility reasons, Osmosis is not required to run this - it uses pyosmium behind the scenes.)
|
|
||||||
|
|
||||||
If you have imported multiple country extracts and want to keep them
|
|
||||||
up-to-date, have a look at the script in
|
|
||||||
[issue #60](https://github.com/openstreetmap/Nominatim/issues/60).
|
|
||||||
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
# Basic Installation
|
|
||||||
|
|
||||||
This page contains generic installation instructions for Nominatim and its
|
|
||||||
prerequisites. There are also step-by-step instructions available for
|
|
||||||
the following operating systems:
|
|
||||||
|
|
||||||
* [Ubuntu 18.04](../appendix/Install-on-Ubuntu-18.md)
|
|
||||||
* [Ubuntu 16.04](../appendix/Install-on-Ubuntu-16.md)
|
|
||||||
* [CentOS 7.2](../appendix/Install-on-Centos-7.md)
|
|
||||||
|
|
||||||
These OS-specific instructions can also be found in executable form
|
|
||||||
in the `vagrant/` directory.
|
|
||||||
|
|
||||||
Users have created instructions for other frameworks. We haven't tested those
|
|
||||||
and can't offer support.
|
|
||||||
|
|
||||||
* [Docker](https://github.com/mediagis/nominatim-docker)
|
|
||||||
* [Docker on Kubernetes](https://github.com/peter-evans/nominatim-k8s)
|
|
||||||
* [Ansible](https://github.com/synthesio/infra-ansible-nominatim)
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
### Software
|
|
||||||
|
|
||||||
For compiling:
|
|
||||||
|
|
||||||
* [cmake](https://cmake.org/)
|
|
||||||
* [libxml2](http://xmlsoft.org/)
|
|
||||||
* a recent C++ compiler
|
|
||||||
|
|
||||||
Nominatim comes with its own version of osm2pgsql. See the
|
|
||||||
osm2pgsql README for additional dependencies required for compiling osm2pgsql.
|
|
||||||
|
|
||||||
For running tests:
|
|
||||||
|
|
||||||
* [behave](http://pythonhosted.org/behave/)
|
|
||||||
* [Psycopg2](http://initd.org/psycopg)
|
|
||||||
* [nose](https://nose.readthedocs.io)
|
|
||||||
* [phpunit](https://phpunit.de)
|
|
||||||
|
|
||||||
For running Nominatim:
|
|
||||||
|
|
||||||
* [PostgreSQL](http://www.postgresql.org) (9.1 or later)
|
|
||||||
* [PostGIS](http://postgis.refractions.net) (2.0 or later)
|
|
||||||
* [PHP](http://php.net) (5.4 or later)
|
|
||||||
* PHP-pgsql
|
|
||||||
* PHP-intl (bundled with PHP)
|
|
||||||
* [PEAR::DB](http://pear.php.net/package/DB)
|
|
||||||
* a webserver (apache or nginx are recommended)
|
|
||||||
|
|
||||||
For running continuous updates:
|
|
||||||
|
|
||||||
* [pyosmium](http://osmcode.org/pyosmium/)
|
|
||||||
|
|
||||||
### Hardware
|
|
||||||
|
|
||||||
A minimum of 2GB of RAM is required or installation will fail. For a full
|
|
||||||
planet import 32GB of RAM or more strongly are recommended.
|
|
||||||
|
|
||||||
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
|
|
||||||
will help considerably to speed up import and queries.
|
|
||||||
|
|
||||||
On a 6-core machine with 32GB RAM and SSDs the import of a full planet takes
|
|
||||||
a bit more than 2 days. Without SSDs 7-8 days are more realistic.
|
|
||||||
|
|
||||||
|
|
||||||
## Setup of the server
|
|
||||||
|
|
||||||
### PostgreSQL tuning
|
|
||||||
|
|
||||||
You might want to tune your PostgreSQL installation so that the later steps
|
|
||||||
make best use of your hardware. You should tune the following parameters in
|
|
||||||
your `postgresql.conf` file.
|
|
||||||
|
|
||||||
shared_buffers (2GB)
|
|
||||||
maintenance_work_mem (10GB)
|
|
||||||
work_mem (50MB)
|
|
||||||
effective_cache_size (24GB)
|
|
||||||
synchronous_commit = off
|
|
||||||
checkpoint_segments = 100 # only for postgresql <= 9.4
|
|
||||||
checkpoint_timeout = 10min
|
|
||||||
checkpoint_completion_target = 0.9
|
|
||||||
|
|
||||||
The numbers in brackets behind some parameters seem to work fine for
|
|
||||||
32GB RAM machine. Adjust to your setup.
|
|
||||||
|
|
||||||
For the initial import, you should also set:
|
|
||||||
|
|
||||||
fsync = off
|
|
||||||
full_page_writes = off
|
|
||||||
|
|
||||||
Don't forget to reenable them after the initial import or you risk database
|
|
||||||
corruption. Autovacuum must not be switched off because it ensures that the
|
|
||||||
tables are frequently analysed.
|
|
||||||
|
|
||||||
### Webserver setup
|
|
||||||
|
|
||||||
The `website/` directory in the build directory contains the configured
|
|
||||||
website. Include the directory into your webbrowser to serve php files
|
|
||||||
from there.
|
|
||||||
|
|
||||||
#### Configure for use with Apache
|
|
||||||
|
|
||||||
Make sure your Apache configuration contains the required permissions for the
|
|
||||||
directory and create an alias:
|
|
||||||
|
|
||||||
<Directory "/srv/nominatim/build/website">
|
|
||||||
Options FollowSymLinks MultiViews
|
|
||||||
AddType text/html .php
|
|
||||||
DirectoryIndex search.php
|
|
||||||
Require all granted
|
|
||||||
</Directory>
|
|
||||||
Alias /nominatim /srv/nominatim/build/website
|
|
||||||
|
|
||||||
`/srv/nominatim/build` should be replaced with the location of your
|
|
||||||
build directory.
|
|
||||||
|
|
||||||
After making changes in the apache config you need to restart apache.
|
|
||||||
The website should now be available on http://localhost/nominatim.
|
|
||||||
|
|
||||||
#### Configure for use with Nginx
|
|
||||||
|
|
||||||
Use php-fpm as a deamon for serving PHP cgi. Install php-fpm together with nginx.
|
|
||||||
|
|
||||||
By default php listens on a network socket. If you want it to listen to a
|
|
||||||
Unix socket instead, change the pool configuration (`pool.d/www.conf`) as
|
|
||||||
follows:
|
|
||||||
|
|
||||||
; Comment out the tcp listener and add the unix socket
|
|
||||||
;listen = 127.0.0.1:9000
|
|
||||||
listen = /var/run/php5-fpm.sock
|
|
||||||
|
|
||||||
; Ensure that the daemon runs as the correct user
|
|
||||||
listen.owner = www-data
|
|
||||||
listen.group = www-data
|
|
||||||
listen.mode = 0666
|
|
||||||
|
|
||||||
Tell nginx that php files are special and to fastcgi_pass to the php-fpm
|
|
||||||
unix socket by adding the location definition to the default configuration.
|
|
||||||
|
|
||||||
root /srv/nominatim/build/website;
|
|
||||||
index search.php index.html;
|
|
||||||
location ~ [^/]\.php(/|$) {
|
|
||||||
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
|
||||||
if (!-f $document_root$fastcgi_script_name) {
|
|
||||||
return 404;
|
|
||||||
}
|
|
||||||
fastcgi_pass unix:/var/run/php5-fpm.sock;
|
|
||||||
fastcgi_index search.php;
|
|
||||||
include fastcgi.conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
Restart the nginx and php5-fpm services and the website should now be available
|
|
||||||
at `http://localhost/`.
|
|
||||||
|
|
||||||
|
|
||||||
Now continue with [importing the database](Import-and-Update.md).
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
# Database Migrations
|
|
||||||
|
|
||||||
This page describes database migrations necessary to update existing databases
|
|
||||||
to newer versions of Nominatim.
|
|
||||||
|
|
||||||
SQL statements should be executed from the postgres commandline. Execute
|
|
||||||
`psql nominiatim` to enter command line mode.
|
|
||||||
|
|
||||||
## 3.1.0 -> 3.2.0
|
|
||||||
|
|
||||||
### New reverse algorithm
|
|
||||||
|
|
||||||
The reverse algorithm has changed and requires new indexes. Run the following
|
|
||||||
SQL statements to create the indexes:
|
|
||||||
|
|
||||||
```
|
|
||||||
CREATE INDEX idx_placex_geometry_reverse_lookupPoint
|
|
||||||
ON placex USING gist (geometry) {ts:search-index}
|
|
||||||
WHERE (name is not null or housenumber is not null or rank_address between 26 and 27)
|
|
||||||
AND class not in ('railway','tunnel','bridge','man_made')
|
|
||||||
AND rank_address >= 26 AND indexed_status = 0 AND linked_place_id is null;
|
|
||||||
CREATE INDEX idx_placex_geometry_reverse_lookupPolygon
|
|
||||||
ON placex USING gist (geometry) {ts:search-index}
|
|
||||||
WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
|
|
||||||
AND rank_address between 4 and 25 AND type != 'postcode'
|
|
||||||
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
|
||||||
CREATE INDEX idx_placex_geometry_reverse_placeNode
|
|
||||||
ON placex USING gist (geometry) {ts:search-index}
|
|
||||||
WHERE osm_type = 'N' AND rank_search between 5 and 25
|
|
||||||
AND class = 'place' AND type != 'postcode'
|
|
||||||
AND name is not null AND indexed_status = 0 AND linked_place_id is null;
|
|
||||||
```
|
|
||||||
|
|
||||||
You also need to grant the website user access to the `country_osm_grid` table:
|
|
||||||
|
|
||||||
```
|
|
||||||
GRANT SELECT ON table country_osm_grid to "www-user";
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace the `www-user` with the user name of your website server if necessary.
|
|
||||||
|
|
||||||
Finally, you can drop the now unused indexes:
|
|
||||||
|
|
||||||
```
|
|
||||||
DROP INDEX idx_placex_reverse_geometry;
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3.0.0 -> 3.1.0
|
|
||||||
|
|
||||||
### Postcode Table
|
|
||||||
|
|
||||||
A new separate table for artificially computed postcode centroids was introduced.
|
|
||||||
Migration to the new format is possible but **not recommended**.
|
|
||||||
|
|
||||||
Create postcode table and indexes, running the following SQL statements:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE location_postcode
|
|
||||||
(place_id BIGINT, parent_place_id BIGINT, rank_search SMALLINT,
|
|
||||||
rank_address SMALLINT, indexed_status SMALLINT, indexed_date TIMESTAMP,
|
|
||||||
country_code varchar(2), postcode TEXT,
|
|
||||||
geometry GEOMETRY(Geometry, 4326));
|
|
||||||
CREATE INDEX idx_postcode_geometry ON location_postcode USING GIST (geometry);
|
|
||||||
CREATE UNIQUE INDEX idx_postcode_id ON location_postcode USING BTREE (place_id);
|
|
||||||
CREATE INDEX idx_postcode_postcode ON location_postcode USING BTREE (postcode);
|
|
||||||
GRANT SELECT ON location_postcode TO "www-data";
|
|
||||||
drop type if exists nearfeaturecentr cascade;
|
|
||||||
create type nearfeaturecentr as (
|
|
||||||
place_id BIGINT,
|
|
||||||
keywords int[],
|
|
||||||
rank_address smallint,
|
|
||||||
rank_search smallint,
|
|
||||||
distance float,
|
|
||||||
isguess boolean,
|
|
||||||
postcode TEXT,
|
|
||||||
centroid GEOMETRY
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
Add postcode column to `location_area` tables with SQL statement:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
ALTER TABLE location_area ADD COLUMN postcode TEXT;
|
|
||||||
```
|
|
||||||
|
|
||||||
Then reimport the functions:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
|
|
||||||
```
|
|
||||||
|
|
||||||
Create appropriate triggers with SQL:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TRIGGER location_postcode_before_update BEFORE UPDATE ON location_postcode
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE postcode_update();
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally populate the postcode table (will take a while):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./utils/setup.php --calculate-postcodes --index --index-noanalyse
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create a working database. You may also delete the old artificial
|
|
||||||
postcodes now. Note that this may be expensive and is not absolutely necessary.
|
|
||||||
The following SQL statement will remove them:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
DELETE FROM place_addressline a USING placex p
|
|
||||||
WHERE a.address_place_id = p.place_id and p.osm_type = 'P';
|
|
||||||
ALTER TABLE placex DISABLE TRIGGER USER;
|
|
||||||
DELETE FROM placex WHERE osm_type = 'P';
|
|
||||||
ALTER TABLE placex ENABLE TRIGGER USER;
|
|
||||||
```
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
# Place details
|
|
||||||
|
|
||||||
Lookup details about a single place by id. The default output is HTML for debugging search logic and results.
|
|
||||||
|
|
||||||
**The details page (including JSON output) exists for debugging only and must not be downloaded automatically**, see [Nominatim Usage Policy](https://operations.osmfoundation.org/policies/nominatim/).
|
|
||||||
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
The details API supports the following two request formats:
|
|
||||||
|
|
||||||
```
|
|
||||||
https://nominatim.openstreetmap.org/details?osmtype=[N|W|R]&osmid=<value>&class=<value>
|
|
||||||
```
|
|
||||||
|
|
||||||
`osmtype` and `osmid` are required parameter. The type is one of node (N), way (W)
|
|
||||||
or relation (R). The id must be a number. The `class` parameter is optional and
|
|
||||||
allows to distinguish between entries, when the corresponding OSM object has more
|
|
||||||
than one main tag. For example, when a place is tagged with `tourism=hotel` and
|
|
||||||
`amenity=restaurant`, there will be two place entries in Nominatim, one for a
|
|
||||||
restaurant, one for a hotel. You need to specify `class=tourism` or `class=amentity`
|
|
||||||
to get exactly the one you want. If there are multiple places in the database
|
|
||||||
but the `class` parameter is left out, then one of the places will be chosen
|
|
||||||
at random and displayed.
|
|
||||||
|
|
||||||
```
|
|
||||||
https://nominatim.openstreetmap.org/details?placeid=<value>
|
|
||||||
```
|
|
||||||
|
|
||||||
Placeids are assigned sequentially during Nominatim data import. The id for a place is different between Nominatim installation (servers) and changes when data gets reimported. Therefore it can't be used as permanent id and shouldn't be used in bug reports.
|
|
||||||
|
|
||||||
|
|
||||||
Additional optional parameters are explained below.
|
|
||||||
|
|
||||||
### Output format
|
|
||||||
|
|
||||||
* `format=[html|json]`
|
|
||||||
|
|
||||||
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
|
||||||
|
|
||||||
* `json_callback=<string>`
|
|
||||||
|
|
||||||
Wrap json output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
|
||||||
Only has an effect for JSON output formats.
|
|
||||||
|
|
||||||
* `pretty=[0|1]`
|
|
||||||
|
|
||||||
For JSON output will add indentation to make it more human-readable. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
### Output details
|
|
||||||
|
|
||||||
* `addressdetails=[0|1]`
|
|
||||||
|
|
||||||
Include a breakdown of the address into elements. (Default for JSON: 0, for HTML: 1)
|
|
||||||
|
|
||||||
* `keywords=[0|1]`
|
|
||||||
|
|
||||||
Include a list of name keywords and address keywords (word ids). (Default: 0)
|
|
||||||
|
|
||||||
* `linkedplaces=[0|1]`
|
|
||||||
|
|
||||||
Include details of places higher in the address hierarchy. E.g. for a street this is usually the city, state, postal code, country. (Default: 1)
|
|
||||||
|
|
||||||
* `hierarchy=[0|1]`
|
|
||||||
|
|
||||||
Include details of places lower in the address hierarchy. E.g. for a city this usually a list of streets, suburbs, rivers. (Default for JSON: 0, for HTML: 1)
|
|
||||||
|
|
||||||
* `group_hierarchy=[0|1]`
|
|
||||||
|
|
||||||
For JSON output will group the places by type. (Default: 0)
|
|
||||||
|
|
||||||
* `polygon_geojson=[0|1]`
|
|
||||||
|
|
||||||
Include geometry of result. (Default for JSON: 0, for HTML: 1)
|
|
||||||
|
|
||||||
### Language of results
|
|
||||||
|
|
||||||
* `accept-language=<browser language string>`
|
|
||||||
|
|
||||||
Preferred language order for showing result, overrides the value
|
|
||||||
specified in the "Accept-Language" HTTP header.
|
|
||||||
Either use a standard RFC2616 accept-language string or a simple
|
|
||||||
comma-separated list of language codes.
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
##### HTML
|
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407](https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407)
|
|
||||||
|
|
||||||
##### JSON
|
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407&format=json](https://nominatim.openstreetmap.org/details.php?osmtype=W&osmid=38210407&format=json)
|
|
||||||
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"place_id": 85993608,
|
|
||||||
"parent_place_id": 72765313,
|
|
||||||
"osm_type": "W",
|
|
||||||
"osm_id": 38210407,
|
|
||||||
"category": "place",
|
|
||||||
"type": "square",
|
|
||||||
"admin_level": "15",
|
|
||||||
"localname": "Pariser Platz",
|
|
||||||
"names": {
|
|
||||||
"name": "Pariser Platz",
|
|
||||||
"name:be": "Парыжская плошча",
|
|
||||||
"name:de": "Pariser Platz",
|
|
||||||
"name:es": "Plaza de París",
|
|
||||||
"name:he": "פאריזר פלאץ",
|
|
||||||
"name:ko": "파리저 광장",
|
|
||||||
"name:la": "Forum Parisinum",
|
|
||||||
"name:ru": "Парижская площадь",
|
|
||||||
"name:uk": "Паризька площа",
|
|
||||||
"name:zh": "巴黎廣場"
|
|
||||||
},
|
|
||||||
"addresstags": {
|
|
||||||
"postcode": "10117"
|
|
||||||
},
|
|
||||||
"housenumber": null,
|
|
||||||
"calculated_postcode": "10117",
|
|
||||||
"country_code": "de",
|
|
||||||
"indexed_date": "2018-08-18T17:02:45+00:00",
|
|
||||||
"importance": 0.339401620591472,
|
|
||||||
"calculated_importance": 0.339401620591472,
|
|
||||||
"extratags": {
|
|
||||||
"wikidata": "Q156716",
|
|
||||||
"wikipedia": "de:Pariser Platz"
|
|
||||||
},
|
|
||||||
"calculated_wikipedia": "de:Pariser_Platz",
|
|
||||||
"rank_address": 30,
|
|
||||||
"rank_search": 30,
|
|
||||||
"isarea": true,
|
|
||||||
"centroid": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
13.3786822618517,
|
|
||||||
52.5163654
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
13.3786822618517,
|
|
||||||
52.5163654
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# Frequently Asked Questions
|
|
||||||
|
|
||||||
## API Results
|
|
||||||
|
|
||||||
#### 1. The address of my search results contains far-away places that don't belong there.
|
|
||||||
|
|
||||||
Nominatim computes the address from two sources in the OpenStreetMap data:
|
|
||||||
from administrative boundaries and from place nodes. Boundaries are the more
|
|
||||||
useful source. They precisely describe an area. So it is very clear for
|
|
||||||
Nominatim if a point belongs to an area of not. Place nodes are more complicated.
|
|
||||||
These are only points without any precise extend. So Nominatim has to take a
|
|
||||||
guess and assume that an address belongs to the closest place nose it can find.
|
|
||||||
In an ideal world, Nominatim would not need the place nodes but there are
|
|
||||||
many places on earth where there are not precise boundaries available for
|
|
||||||
all parts that make up an address. This is in particular true for the more
|
|
||||||
local address parts, like villages and suburbs. Therefore it is not possible
|
|
||||||
to completely dismiss place nodes. And sometimes they sneak in where they
|
|
||||||
don't belong.
|
|
||||||
|
|
||||||
As a OpenStreetMap mapper, you can improve the situation in two ways: if you
|
|
||||||
see a place node for which already an administrative area exists, then you
|
|
||||||
should _link_ the two by adding the node with a 'label' role to the boundary
|
|
||||||
relation. If there is no administrative area, you can add the approximate
|
|
||||||
extend of the place and tag it place=<something> as well.
|
|
||||||
|
|
||||||
#### 2. When doing reverse search, the address details have parts that don't contain the point I was looking up.
|
|
||||||
|
|
||||||
There is a common misconception how the reverse API call works in Nominatim.
|
|
||||||
Reverse does not give you the address of the point you asked for. Reverse
|
|
||||||
returns the closest object to the point you asked for and then returns the
|
|
||||||
address of that object. Now, if you are close to a border, then the closest
|
|
||||||
object may be across that border. When Nominatim then returns the address,
|
|
||||||
contains the county/state/country across the border.
|
|
||||||
|
|
||||||
#### 3. I get different counties/states/countries when I change the zoom parameter in the reverse query. How is that possible?
|
|
||||||
|
|
||||||
This is basically the same problem as in the previous answer.
|
|
||||||
The zoom level influences at which [search rank](https://wiki.openstreetmap.org/wiki/Nominatim/Development_overview#Country_to_street_level) Nominatim starts looking
|
|
||||||
for the closest object. So the closest house number maybe on one side of the
|
|
||||||
border while the closest street is on the other. As the address details contain
|
|
||||||
the address of the closest object found, you might sometimes get one result,
|
|
||||||
sometimes the other for the closest point.
|
|
||||||
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
# Address lookup
|
|
||||||
|
|
||||||
The lookup API allows to query the address and other details of one or
|
|
||||||
multiple OSM objects like node, way or relation.
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
The lookup API has the following format:
|
|
||||||
|
|
||||||
```
|
|
||||||
https://nominatim.openstreetmap.org/lookup?osm_ids=[N|W|R]<value>,…,…,&<params>
|
|
||||||
```
|
|
||||||
|
|
||||||
`osm_ids` is mandatory and must contain a comma-separated list of OSM ids each
|
|
||||||
prefixed with its type, one of node(N), way(W) or relation(R). Up to 50 ids
|
|
||||||
can be queried at the same time.
|
|
||||||
|
|
||||||
Additional optional parameters are explained below.
|
|
||||||
|
|
||||||
### Output format
|
|
||||||
|
|
||||||
* `format=[html|xml|json|jsonv2|geojson|geocodejson]`
|
|
||||||
|
|
||||||
See [Place Output Formats](Output.md) for details on each format. (Default: xml)
|
|
||||||
|
|
||||||
* `json_callback=<string>`
|
|
||||||
|
|
||||||
Wrap json output in a callback function (JSONP) i.e. `<string>(<json>)`.
|
|
||||||
Only has an effect for JSON output formats.
|
|
||||||
|
|
||||||
### Output details
|
|
||||||
|
|
||||||
* `addressdetails=[0|1]`
|
|
||||||
|
|
||||||
Include a breakdown of the address into elements. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
* `extratags=[0|1]`
|
|
||||||
|
|
||||||
Include additional information in the result if available,
|
|
||||||
e.g. wikipedia link, opening hours. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
* `namedetails=[0|1]`
|
|
||||||
|
|
||||||
Include a list of alternative names in the results. These may include
|
|
||||||
language variants, references, operator and brand. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
### Language of results
|
|
||||||
|
|
||||||
* `accept-language=<browser language string>`
|
|
||||||
|
|
||||||
Preferred language order for showing search results, overrides the value
|
|
||||||
specified in the "Accept-Language" HTTP header.
|
|
||||||
Either use a standard RFC2616 accept-language string or a simple
|
|
||||||
comma-separated list of language codes.
|
|
||||||
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
* `email=<valid email address>`
|
|
||||||
|
|
||||||
If you are making large numbers of request please include an appropriate email
|
|
||||||
address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details.
|
|
||||||
|
|
||||||
* `debug=[0|1]`
|
|
||||||
|
|
||||||
Output assorted developer debug information. Data on internals of Nominatim's
|
|
||||||
"Search Loop" logic, and SQL queries. The output is (rough) HTML format.
|
|
||||||
This overrides the specified machine readable format. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
##### XML
|
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/lookup?osm_ids=R146656,W104393803,N240109189](https://nominatim.openstreetmap.org/lookup?osm_ids=R146656,W104393803,N240109189)
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<lookupresults timestamp="Mon, 29 Jun 15 18:01:33 +0000" attribution="Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright" querystring="R146656,W104393803,N240109189" polygon="false">
|
|
||||||
<place place_id="127761056" osm_type="relation" osm_id="146656" place_rank="16" lat="53.4791466" lon="-2.2447445" display_name="Manchester, Greater Manchester, North West England, England, United Kingdom" class="boundary" type="administrative" importance="0.704893333438333">
|
|
||||||
<city>Manchester</city>
|
|
||||||
<county>Greater Manchester</county>
|
|
||||||
<state_district>North West England</state_district>
|
|
||||||
<state>England</state>
|
|
||||||
<country>United Kingdom</country>
|
|
||||||
<country_code>gb</country_code>
|
|
||||||
</place>
|
|
||||||
<place place_id="77769745" osm_type="way" osm_id="104393803" place_rank="30" lat="52.5162024" lon="13.3777343363579" display_name="Brandenburg Gate, 1, Pariser Platz, Mitte, Berlin, 10117, Germany" class="tourism" type="attraction" importance="0.443472858361592">
|
|
||||||
<attraction>Brandenburg Gate</attraction>
|
|
||||||
<house_number>1</house_number>
|
|
||||||
<pedestrian>Pariser Platz</pedestrian>
|
|
||||||
<suburb>Mitte</suburb>
|
|
||||||
<city_district>Mitte</city_district>
|
|
||||||
<city>Berlin</city>
|
|
||||||
<state>Berlin</state>
|
|
||||||
<postcode>10117</postcode>
|
|
||||||
<country>Germany</country>
|
|
||||||
<country_code>de</country_code>
|
|
||||||
</place>
|
|
||||||
<place place_id="2570600569" osm_type="node" osm_id="240109189" place_rank="15" lat="52.5170365" lon="13.3888599" display_name="Berlin, Germany" class="place" type="city" importance="0.822149797630868">
|
|
||||||
<city>Berlin</city>
|
|
||||||
<state>Berlin</state>
|
|
||||||
<country>Germany</country>
|
|
||||||
<country_code>de</country_code>
|
|
||||||
</place>
|
|
||||||
</lookupresults>
|
|
||||||
```
|
|
||||||
|
|
||||||
##### JSON with extratags
|
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/lookup?osm_ids=W50637691&format=json](https://nominatim.openstreetmap.org/lookup?osm_ids=W50637691&format=json)
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"place_id": "84271358",
|
|
||||||
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
|
||||||
"osm_type": "way",
|
|
||||||
"osm_id": "50637691",
|
|
||||||
"lat": "52.39955055",
|
|
||||||
"lon": "13.04806574678",
|
|
||||||
"display_name": "Brandenburger Tor, Brandenburger Straße, Nördliche Innenstadt, Innenstadt, Potsdam, Brandenburg, 14467, Germany",
|
|
||||||
"class": "historic",
|
|
||||||
"type": "city_gate",
|
|
||||||
"importance": "0.221233780277011",
|
|
||||||
"address": {
|
|
||||||
"address29": "Brandenburger Tor",
|
|
||||||
"pedestrian": "Brandenburger Straße",
|
|
||||||
"suburb": "Nördliche Innenstadt",
|
|
||||||
"city": "Potsdam",
|
|
||||||
"state": "Brandenburg",
|
|
||||||
"postcode": "14467",
|
|
||||||
"country": "Germany",
|
|
||||||
"country_code": "de"
|
|
||||||
},
|
|
||||||
"extratags": {
|
|
||||||
"image": "http://commons.wikimedia.org/wiki/File:Potsdam_brandenburger_tor.jpg",
|
|
||||||
"wikidata": "Q695045",
|
|
||||||
"wikipedia": "de:Brandenburger Tor (Potsdam)",
|
|
||||||
"wheelchair": "yes",
|
|
||||||
"description": "Kleines Brandenburger Tor in Potsdam"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
# Place Output
|
|
||||||
|
|
||||||
The [\reverse](Reverse.md), [\search](Search.md) and [\lookup](Lookup.md)
|
|
||||||
API calls produce very similar output which is explained in this section.
|
|
||||||
There is one section for each format which is selectable via the `format`
|
|
||||||
parameter.
|
|
||||||
|
|
||||||
## Formats
|
|
||||||
|
|
||||||
### JSON
|
|
||||||
|
|
||||||
The JSON format returns an array of places (for search and lookup) or
|
|
||||||
a single place (for reverse) of the following format:
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"place_id": "100149",
|
|
||||||
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
|
||||||
"osm_type": "node",
|
|
||||||
"osm_id": "107775",
|
|
||||||
"boundingbox": ["51.3473219", "51.6673219", "-0.2876474", "0.0323526"],
|
|
||||||
"lat": "51.5073219",
|
|
||||||
"lon": "-0.1276474",
|
|
||||||
"display_name": "London, Greater London, England, SW1A 2DU, United Kingdom",
|
|
||||||
"class": "place",
|
|
||||||
"type": "city",
|
|
||||||
"importance": 0.9654895765402,
|
|
||||||
"icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
|
|
||||||
"address": {
|
|
||||||
"city": "London",
|
|
||||||
"state_district": "Greater London",
|
|
||||||
"state": "England",
|
|
||||||
"postcode": "SW1A 2DU",
|
|
||||||
"country": "United Kingdom",
|
|
||||||
"country_code": "gb"
|
|
||||||
},
|
|
||||||
"extratags": {
|
|
||||||
"capital": "yes",
|
|
||||||
"website": "http://www.london.gov.uk",
|
|
||||||
"wikidata": "Q84",
|
|
||||||
"wikipedia": "en:London",
|
|
||||||
"population": "8416535"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
The possible fields are:
|
|
||||||
|
|
||||||
* `place_id` - reference to the Nominatim internal database ID
|
|
||||||
* `osm_type`, `osm_id` - reference to the OSM object
|
|
||||||
* `boundingbox` - area of corner coordinates
|
|
||||||
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
|
||||||
* `display_name` - full comma-separated address
|
|
||||||
* `class`, `type` - key and value of the main OSM tag
|
|
||||||
* `importance` - computed importance rank
|
|
||||||
* `icon` - link to class icon (if available)
|
|
||||||
* `address` - dictionary of address details (only with `addressdetails=1`)
|
|
||||||
* `extratags` - dictionary with additional useful tags like website or maxspeed
|
|
||||||
(only with `extratags=1`)
|
|
||||||
* `namedetails` - dictionary with full list of available names including ref etc.
|
|
||||||
* `geojson`, `svg`, `geotext`, `geokml` - full geometry
|
|
||||||
(only with the appropriate `polygon_*` parameter)
|
|
||||||
|
|
||||||
### JSONv2
|
|
||||||
|
|
||||||
This is the same as the JSON format with two changes:
|
|
||||||
|
|
||||||
* `class` renamed to `category`
|
|
||||||
* additional field `place_rank` with the search rank of the object
|
|
||||||
|
|
||||||
### GeoJSON
|
|
||||||
|
|
||||||
This format follows the [RFC7946](http://geojson.org). Every feature includes
|
|
||||||
a bounding box (`bbox`).
|
|
||||||
|
|
||||||
The feature list has the following fields:
|
|
||||||
|
|
||||||
* `place_id` - reference to the Nominatim internal database ID
|
|
||||||
* `osm_type`, `osm_id` - reference to the OSM object
|
|
||||||
* `category`, `type` - key and value of the main OSM tag
|
|
||||||
* `display_name` - full comma-separated address
|
|
||||||
* `place_rank` - class search rank
|
|
||||||
* `importance` - computed importance rank
|
|
||||||
* `icon` - link to class icon (if available)
|
|
||||||
* `address` - dictionary of address details (only with `addressdetails=1`)
|
|
||||||
* `extratags` - dictionary with additional useful tags like website or maxspeed
|
|
||||||
(only with `extratags=1`)
|
|
||||||
* `namedetails` - dictionary with full list of available names including ref etc.
|
|
||||||
|
|
||||||
Use `polygon_geojson` to output the full geometry of the object instead
|
|
||||||
of the centroid.
|
|
||||||
|
|
||||||
### GeocodeJSON
|
|
||||||
|
|
||||||
The GeocodeJSON format follows the
|
|
||||||
[GeocodeJSON spec 0.1.0](https://github.com/geocoders/geocodejson-spec).
|
|
||||||
The following feature attributes are implemented:
|
|
||||||
|
|
||||||
* `osm_type`, `osm_id` - reference to the OSM object (unofficial extension)
|
|
||||||
* `type` - value of the main tag of the object (e.g. residential, restaurant, ...)
|
|
||||||
* `label` - full comma-separated address
|
|
||||||
* `name` - localised name of the place
|
|
||||||
* `housenumber`, `street`, `locality`, `postcode`, `city`,
|
|
||||||
`district`, `county`, `state`, `country` -
|
|
||||||
provided when it can be determined from the address
|
|
||||||
(see [this issue](https://github.com/openstreetmap/Nominatim/issues/1080) for
|
|
||||||
current limitations on the correctness of the address) and `addressdetails=1`
|
|
||||||
was given
|
|
||||||
* `admin` - list of localised names of administrative boundaries (only with `addressdetails=1`)
|
|
||||||
|
|
||||||
Use `polygon_geojson` to output the full geometry of the object instead
|
|
||||||
of the centroid.
|
|
||||||
|
|
||||||
### XML
|
|
||||||
|
|
||||||
The XML response returns one or more place objects in slightly different
|
|
||||||
formats depending on the API call.
|
|
||||||
|
|
||||||
#### Reverse
|
|
||||||
|
|
||||||
```
|
|
||||||
<reversegeocode timestamp="Sat, 11 Aug 18 11:53:21 +0000"
|
|
||||||
attribution="Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright"
|
|
||||||
querystring="lat=48.400381&lon=11.745876&zoom=5&format=xml">
|
|
||||||
<result place_id="179509537" osm_type="relation" osm_id="2145268" ref="BY"
|
|
||||||
lat="48.9467562" lon="11.4038717"
|
|
||||||
boundingbox="47.2701114,50.5647142,8.9763497,13.8396373">
|
|
||||||
Bavaria, Germany
|
|
||||||
</result>
|
|
||||||
<addressparts>
|
|
||||||
<state>Bavaria</state>
|
|
||||||
<country>Germany</country>
|
|
||||||
<country_code>de</country_code>
|
|
||||||
</addressparts>
|
|
||||||
<extratags>
|
|
||||||
<tag key="place" value="state"/>
|
|
||||||
<tag key="wikidata" value="Q980"/>
|
|
||||||
<tag key="wikipedia" value="de:Bayern"/>
|
|
||||||
<tag key="population" value="12520000"/>
|
|
||||||
<tag key="name:prefix" value="Freistaat"/>
|
|
||||||
</extratags>
|
|
||||||
</reversegeocode>
|
|
||||||
```
|
|
||||||
|
|
||||||
The attributes of the outer `reversegeocode` element return generic information
|
|
||||||
about the query, including the time when the response was sent (in UTC),
|
|
||||||
attribution to OSM and the original querystring.
|
|
||||||
|
|
||||||
The place information can be found in the `result` element. The attributes of that element contain:
|
|
||||||
|
|
||||||
* `place_id` - reference to the Nominatim internal database ID
|
|
||||||
* `osm_type`, `osm_id` - reference to the OSM object
|
|
||||||
* `ref` - content of `ref` tag if it exists
|
|
||||||
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
|
||||||
* `boundingbox` - comma-separated list of corner coordinates
|
|
||||||
|
|
||||||
The full address address of the result can be found in the content of the
|
|
||||||
`result` element as a comma-separated list.
|
|
||||||
|
|
||||||
Additional information requested with `addressdetails=1`, `extratags=1` and
|
|
||||||
`namedetails=1` can be found in extra elements.
|
|
||||||
|
|
||||||
#### Search and Lookup
|
|
||||||
|
|
||||||
```
|
|
||||||
<searchresults timestamp="Sat, 11 Aug 18 11:55:35 +0000"
|
|
||||||
attribution="Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright"
|
|
||||||
querystring="london" polygon="false" exclude_place_ids="100149"
|
|
||||||
more_url="https://nominatim.openstreetmap.org/search.php?q=london&addressdetails=1&extratags=1&exclude_place_ids=100149&format=xml&accept-language=en-US%2Cen%3Bq%3D0.7%2Cde%3Bq%3D0.3">
|
|
||||||
<place place_id="100149" osm_type="node" osm_id="107775" place_rank="15"
|
|
||||||
boundingbox="51.3473219,51.6673219,-0.2876474,0.0323526" lat="51.5073219" lon="-0.1276474"
|
|
||||||
display_name="London, Greater London, England, SW1A 2DU, United Kingdom"
|
|
||||||
class="place" type="city" importance="0.9654895765402"
|
|
||||||
icon="https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png">
|
|
||||||
<extratags>
|
|
||||||
<tag key="capital" value="yes"/>
|
|
||||||
<tag key="website" value="http://www.london.gov.uk"/>
|
|
||||||
<tag key="wikidata" value="Q84"/>
|
|
||||||
<tag key="wikipedia" value="en:London"/>
|
|
||||||
<tag key="population" value="8416535"/>
|
|
||||||
</extratags>
|
|
||||||
<city>London</city>
|
|
||||||
<state_district>Greater London</state_district>
|
|
||||||
<state>England</state>
|
|
||||||
<postcode>SW1A 2DU</postcode>
|
|
||||||
<country>United Kingdom</country>
|
|
||||||
<country_code>gb</country_code>
|
|
||||||
</place>
|
|
||||||
</searchresults>
|
|
||||||
```
|
|
||||||
|
|
||||||
The attributes of the outer `searchresults` or `lookupresults` element return
|
|
||||||
generic information about the query:
|
|
||||||
|
|
||||||
* `timestamp` - UTC time when the response was sent
|
|
||||||
* `attribution` - OSM licensing information
|
|
||||||
* `querystring` - original query
|
|
||||||
* `polygon` - true when extra geometry information was requested
|
|
||||||
* `exclude_place_ids` - IDs of places that should be ignored in a follow-up request
|
|
||||||
* `more_url` - search call that will yield additional results for the query
|
|
||||||
just sent
|
|
||||||
|
|
||||||
The place information can be found in the `place` elements, of which there may
|
|
||||||
be more than one. The attributes of that element contain:
|
|
||||||
|
|
||||||
* `place_id` - reference to the Nominatim internal database ID
|
|
||||||
* `osm_type`, `osm_id` - reference to the OSM object
|
|
||||||
* `ref` - content of `ref` tag if it exists
|
|
||||||
* `lat`, `lon` - latitude and longitude of the centroid of the object
|
|
||||||
* `boundingbox` - comma-separated list of corner coordinates
|
|
||||||
* `place_rank` - class search rank
|
|
||||||
* `display_name` - full comma-separated address
|
|
||||||
* `class`, `type` - key and value of the main OSM tag
|
|
||||||
* `importance` - computed importance rank
|
|
||||||
* `icon` - link to class icon (if available)
|
|
||||||
|
|
||||||
When `addressdetails=1` is requested, the localised address parts appear
|
|
||||||
as subelements with the type of the address part.
|
|
||||||
|
|
||||||
Additional information requested with `extratags=1` and `namedetails=1` can
|
|
||||||
be found in extra elements as sub-element of each place.
|
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
### Nominatim API
|
|
||||||
|
|
||||||
Nominatim indexes named (or numbered) features within the OpenStreetMap (OSM) dataset and a subset of other unnamed features (pubs, hotels, churches, etc).
|
|
||||||
|
|
||||||
Its API has the following endpoints for querying the data:
|
|
||||||
|
|
||||||
* __[/search](Search.md)__ - search OSM objects by name or type
|
|
||||||
* __[/reverse](Reverse.md)__ - search OSM object by their location
|
|
||||||
* __[/lookup](Lookup.md)__ - look up address details for OSM objects by their ID
|
|
||||||
* __/status__ - query the status of the server
|
|
||||||
* __/deletable__ - list objects that have been deleted in OSM but are held
|
|
||||||
back in Nominatim in case the deletion was accidental
|
|
||||||
* __/polygons__ - list of broken polygons detected by Nominatim
|
|
||||||
* __[/details](Details.md)__ - show internal details for an object (for debugging only)
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
# Reverse Geocoding
|
|
||||||
|
|
||||||
Reverse geocoding generates an address from a latitude and longitude or from
|
|
||||||
an OSM object.
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
The main format of the reverse API is
|
|
||||||
|
|
||||||
```
|
|
||||||
https://nominatim.openstreetmap.org/reverse?<query>
|
|
||||||
```
|
|
||||||
|
|
||||||
There are two ways how the requested location can be specified:
|
|
||||||
|
|
||||||
* `lat=<value>` `lon=<value>`
|
|
||||||
|
|
||||||
A geographic location to generate an address for. The coordiantes must be
|
|
||||||
in WGS84 format.
|
|
||||||
|
|
||||||
* `osm_type=[N|W|R]` `osm_id=<value>`
|
|
||||||
|
|
||||||
A specific OSM node(N), way(W) or relation(R) to return an address for.
|
|
||||||
|
|
||||||
In both cases exactly one object is returned. The two input paramters cannot
|
|
||||||
be used at the same time. Both accept the additional optional parameters listed
|
|
||||||
below.
|
|
||||||
|
|
||||||
### Output format
|
|
||||||
|
|
||||||
* `format=[xml|json|jsonv2|geojson|geocodejson]`
|
|
||||||
|
|
||||||
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
|
||||||
|
|
||||||
* `json_callback=<string>`
|
|
||||||
|
|
||||||
Wrap json output in a callback function ([JSONP](https://en.wikipedia.org/wiki/JSONP)) i.e. `<string>(<json>)`.
|
|
||||||
Only has an effect for JSON output formats.
|
|
||||||
|
|
||||||
### Output details
|
|
||||||
|
|
||||||
* `addressdetails=[0|1]`
|
|
||||||
|
|
||||||
Include a breakdown of the address into elements. (Default: 1)
|
|
||||||
|
|
||||||
|
|
||||||
* `extratags=[0|1]`
|
|
||||||
|
|
||||||
Include additional information in the result if available,
|
|
||||||
e.g. wikipedia link, opening hours. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
* `namedetails=[0|1]`
|
|
||||||
|
|
||||||
Include a list of alternative names in the results. These may include
|
|
||||||
language variants, references, operator and brand. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
### Language of results
|
|
||||||
|
|
||||||
* `accept-language=<browser language string>`
|
|
||||||
|
|
||||||
Preferred language order for showing search results, overrides the value
|
|
||||||
specified in the "Accept-Language" HTTP header.
|
|
||||||
Either use a standard RFC2616 accept-language string or a simple
|
|
||||||
comma-separated list of language codes.
|
|
||||||
|
|
||||||
### Result limitation
|
|
||||||
|
|
||||||
* `zoom=[0-18]`
|
|
||||||
|
|
||||||
Level of detail required for the address. Default: 18. This is a number that corresponds
|
|
||||||
roughly to the zoom level used in map frameworks like Leaflet.js, Openlayers etc.
|
|
||||||
In terms of address details the zoom levels are as follows:
|
|
||||||
|
|
||||||
zoom | address detail
|
|
||||||
-----|---------------
|
|
||||||
3 | country
|
|
||||||
5 | state
|
|
||||||
8 | county
|
|
||||||
10 | city
|
|
||||||
14 | suburb
|
|
||||||
16 | street
|
|
||||||
18 | building
|
|
||||||
|
|
||||||
|
|
||||||
### Polygon output
|
|
||||||
|
|
||||||
* `polygon_geojson=1`
|
|
||||||
* `polygon_kml=1`
|
|
||||||
* `polygon_svg=1`
|
|
||||||
* `polygon_text=1`
|
|
||||||
|
|
||||||
Output geometry of results as a GeoJSON, KML, SVG or WKT. Only one of these
|
|
||||||
options can be used at a time. (Default: 0)
|
|
||||||
|
|
||||||
* `polygon_threshold=0.0`
|
|
||||||
|
|
||||||
Simplify the output geometry before returning. The parameter is the
|
|
||||||
tolerance in degrees with which the geometry may differ from the original
|
|
||||||
geometry. Topology is preserved in the result. (Default: 0.0)
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
* `email=<valid email address>`
|
|
||||||
|
|
||||||
If you are making large numbers of request please include an appropriate email
|
|
||||||
address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details.
|
|
||||||
|
|
||||||
|
|
||||||
* `debug=[0|1]`
|
|
||||||
|
|
||||||
Output assorted developer debug information. Data on internals of Nominatim's
|
|
||||||
"Search Loop" logic, and SQL queries. The output is (rough) HTML format.
|
|
||||||
This overrides the specified machine readable format. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
* [https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1](https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1)
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<reversegeocode timestamp="Fri, 06 Nov 09 16:33:54 +0000" querystring="...">
|
|
||||||
<result place_id="1620612" osm_type="node" osm_id="452010817">
|
|
||||||
135, Pilkington Avenue, Wylde Green, City of Birmingham, West Midlands (county), B72, United Kingdom
|
|
||||||
</result>
|
|
||||||
<addressparts>
|
|
||||||
<house_number>135</house_number>
|
|
||||||
<road>Pilkington Avenue</road>
|
|
||||||
<village>Wylde Green</village>
|
|
||||||
<town>Sutton Coldfield</town>
|
|
||||||
<city>City of Birmingham</city>
|
|
||||||
<county>West Midlands (county)</county>
|
|
||||||
<postcode>B72</postcode>
|
|
||||||
<country>United Kingdom</country>
|
|
||||||
<country_code>gb</country_code>
|
|
||||||
</addressparts>
|
|
||||||
</reversegeocode>
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Example with `format=jsonv2`
|
|
||||||
|
|
||||||
* [https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521](https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"place_id":"134140761",
|
|
||||||
"licence":"Data © OpenStreetMap contributors, ODbL 1.0. http:\/\/www.openstreetmap.org\/copyright",
|
|
||||||
"osm_type":"way",
|
|
||||||
"osm_id":"280940520",
|
|
||||||
"lat":"-34.4391708",
|
|
||||||
"lon":"-58.7064573",
|
|
||||||
"place_rank":"26",
|
|
||||||
"category":"highway",
|
|
||||||
"type":"motorway",
|
|
||||||
"importance":"0.1",
|
|
||||||
"addresstype":"road",
|
|
||||||
"display_name":"Autopista Pedro Eugenio Aramburu, El Triángulo, Partido de Malvinas Argentinas, Buenos Aires, 1.619, Argentina",
|
|
||||||
"name":"Autopista Pedro Eugenio Aramburu",
|
|
||||||
"address":{
|
|
||||||
"road":"Autopista Pedro Eugenio Aramburu",
|
|
||||||
"village":"El Triángulo",
|
|
||||||
"state_district":"Partido de Malvinas Argentinas",
|
|
||||||
"state":"Buenos Aires",
|
|
||||||
"postcode":"1.619",
|
|
||||||
"country":"Argentina",
|
|
||||||
"country_code":"ar"
|
|
||||||
},
|
|
||||||
"boundingbox":["-34.44159","-34.4370994","-58.7086067","-58.7044712"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Example with `format=geojson`
|
|
||||||
|
|
||||||
* [https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989](https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "FeatureCollection",
|
|
||||||
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
|
||||||
"features": [
|
|
||||||
{
|
|
||||||
"type": "Feature",
|
|
||||||
"properties": {
|
|
||||||
"place_id": "18512203",
|
|
||||||
"osm_type": "node",
|
|
||||||
"osm_id": "1704756187",
|
|
||||||
"place_rank": "30",
|
|
||||||
"category": "place",
|
|
||||||
"type": "house",
|
|
||||||
"importance": "0",
|
|
||||||
"addresstype": "place",
|
|
||||||
"name": null,
|
|
||||||
"display_name": "71, Via Guglielmo Marconi, Saragozza-Porto, Bologna, BO, Emilia-Romagna, 40122, Italy",
|
|
||||||
"address": {
|
|
||||||
"house_number": "71",
|
|
||||||
"road": "Via Guglielmo Marconi",
|
|
||||||
"suburb": "Saragozza-Porto",
|
|
||||||
"city": "Bologna",
|
|
||||||
"county": "BO",
|
|
||||||
"state": "Emilia-Romagna",
|
|
||||||
"postcode": "40122",
|
|
||||||
"country": "Italy",
|
|
||||||
"country_code": "it"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bbox": [
|
|
||||||
11.3397676,
|
|
||||||
44.5014307,
|
|
||||||
11.3399676,
|
|
||||||
44.5016307
|
|
||||||
],
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
11.3398676,
|
|
||||||
44.5015307
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Example with `format=geocodejson`
|
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663](https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "FeatureCollection",
|
|
||||||
"geocoding": {
|
|
||||||
"version": "0.1.0",
|
|
||||||
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
|
||||||
"licence": "ODbL",
|
|
||||||
"query": "60.229917843587,11.16630979382"
|
|
||||||
},
|
|
||||||
"features": {
|
|
||||||
"type": "Feature",
|
|
||||||
"properties": {
|
|
||||||
"geocoding": {
|
|
||||||
"place_id": "42700574",
|
|
||||||
"osm_type": "node",
|
|
||||||
"osm_id": "3110596255",
|
|
||||||
"type": "house",
|
|
||||||
"accuracy": 0,
|
|
||||||
"label": "1, Løvenbergvegen, Mogreina, Ullensaker, Akershus, 2054, Norway",
|
|
||||||
"name": null,
|
|
||||||
"housenumber": "1",
|
|
||||||
"street": "Løvenbergvegen",
|
|
||||||
"postcode": "2054",
|
|
||||||
"county": "Akershus",
|
|
||||||
"country": "Norway",
|
|
||||||
"admin": {
|
|
||||||
"level7": "Ullensaker",
|
|
||||||
"level4": "Akershus",
|
|
||||||
"level2": "Norway"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
11.1658572,
|
|
||||||
60.2301296
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
# Search queries
|
|
||||||
|
|
||||||
The search API allows to look up a location from a textual description.
|
|
||||||
Nominatim supports structured as well as free-form search queries.
|
|
||||||
|
|
||||||
The search query may also contain
|
|
||||||
[special phrases](https://wiki.openstreetmap.org/wiki/Nominatim/Special_Phrases)
|
|
||||||
which are translated into specific OpenStreetMap (OSM) tags (e.g. Pub => `amenity=pub`).
|
|
||||||
Note that this only limits the items to be found, it's not suited to return complete
|
|
||||||
lists of OSM objects of a specific type. For those use [Overpass API](https://overpass-api.de/).
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
The search API has the following two formats:
|
|
||||||
|
|
||||||
```
|
|
||||||
https://nominatim.openstreetmap.org/search/<query>?<params>
|
|
||||||
```
|
|
||||||
|
|
||||||
This format only accepts a free-form query string where the
|
|
||||||
parts of the query are separated by slashes.
|
|
||||||
|
|
||||||
```
|
|
||||||
https://nominatim.openstreetmap.org/search?<params>
|
|
||||||
```
|
|
||||||
|
|
||||||
In this form, the query may be given through two different sets of parameters:
|
|
||||||
|
|
||||||
* `q=<query>`
|
|
||||||
|
|
||||||
Free-form query string to search for.
|
|
||||||
Free-form queries are processed first left-to-right and then right-to-left if that fails. So you may search for
|
|
||||||
[pilkington avenue, birmingham](//nominatim.openstreetmap.org/search?q=pilkington+avenue,birmingham) as well as for
|
|
||||||
[birmingham, pilkington avenue](//nominatim.openstreetmap.org/search?q=birmingham,+pilkington+avenue).
|
|
||||||
Commas are optional, but improve performance by reducing the complexity of the search.
|
|
||||||
|
|
||||||
|
|
||||||
* `street=<housenumber> <streetname>`
|
|
||||||
* `city=<city>`
|
|
||||||
* `county=<county>`
|
|
||||||
* `state=<state>`
|
|
||||||
* `country=<country>`
|
|
||||||
* `postalcode=<postalcode>`
|
|
||||||
|
|
||||||
Alternative query string format split into several parameters for structured requests.
|
|
||||||
Structured requests are faster but are less robust against alternative
|
|
||||||
OSM tagging schemas. **Do not combine with** `q=<query>` **parameter**.
|
|
||||||
|
|
||||||
All three query forms accept the additional paramters listed below.
|
|
||||||
|
|
||||||
### Output format
|
|
||||||
|
|
||||||
* `format=[html|xml|json|jsonv2|geojson|geocodejson]`
|
|
||||||
|
|
||||||
See [Place Output Formats](Output.md) for details on each format. (Default: html)
|
|
||||||
|
|
||||||
* `json_callback=<string>`
|
|
||||||
|
|
||||||
Wrap json output in a callback function ([JSONP](https://en.wikipedia.org/wiki/JSONP)) i.e. `<string>(<json>)`.
|
|
||||||
Only has an effect for JSON output formats.
|
|
||||||
|
|
||||||
### Output details
|
|
||||||
|
|
||||||
* `addressdetails=[0|1]`
|
|
||||||
|
|
||||||
Include a breakdown of the address into elements. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
* `extratags=[0|1]`
|
|
||||||
|
|
||||||
Include additional information in the result if available,
|
|
||||||
e.g. wikipedia link, opening hours. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
* `namedetails=[0|1]`
|
|
||||||
|
|
||||||
Include a list of alternative names in the results. These may include
|
|
||||||
language variants, references, operator and brand. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
### Language of results
|
|
||||||
|
|
||||||
* `accept-language=<browser language string>`
|
|
||||||
|
|
||||||
Preferred language order for showing search results, overrides the value
|
|
||||||
specified in the ["Accept-Language" HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language).
|
|
||||||
Either use a standard RFC2616 accept-language string or a simple
|
|
||||||
comma-separated list of language codes.
|
|
||||||
|
|
||||||
### Result limitation
|
|
||||||
|
|
||||||
* `countrycodes=<countrycode>[,<countrycode>][,<countrycode>]...`
|
|
||||||
|
|
||||||
Limit search results to one or more countries. `<countrycode>` must be the
|
|
||||||
ISO 3166-1alpha2 code, e.g. `gb` for the United Kingdom, `de` for Germany.
|
|
||||||
|
|
||||||
|
|
||||||
* `exclude_place_ids=<place_id,[place_id],[place_id]`
|
|
||||||
|
|
||||||
If you do not want certain OSM objects to appear in the search
|
|
||||||
result, give a comma separated list of the `place_id`s you want to skip.
|
|
||||||
This can be used to broaden search results. For example, if a previous
|
|
||||||
query only returned a few results, then including those here would cause
|
|
||||||
the search to return other, less accurate, matches (if possible).
|
|
||||||
|
|
||||||
|
|
||||||
* `limit=<integer>`
|
|
||||||
|
|
||||||
Limit the number of returned results. (Default: 10, Maximum: 50)
|
|
||||||
|
|
||||||
|
|
||||||
* `viewbox=<x1>,<y1>,<x2>,<y2>`
|
|
||||||
|
|
||||||
The preferred area to find search results. Any two corner points of the box
|
|
||||||
are accepted in any order as long as they span a real box.
|
|
||||||
|
|
||||||
|
|
||||||
* `bounded=[0|1]`
|
|
||||||
|
|
||||||
When a viewbox is given, restrict the result to items contained with that
|
|
||||||
viewbox (see above). When `viewbox` and `bounded=1` are given, an amenity
|
|
||||||
only search is allowed. In this case, give the special keyword for the
|
|
||||||
amenity in square brackets, e.g. `[pub]`. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
### Polygon output
|
|
||||||
|
|
||||||
* `polygon_geojson=1`
|
|
||||||
* `polygon_kml=1`
|
|
||||||
* `polygon_svg=1`
|
|
||||||
* `polygon_text=1`
|
|
||||||
|
|
||||||
Output geometry of results as a GeoJSON, KML, SVG or WKT. Only one of these
|
|
||||||
options can be used at a time. (Default: 0)
|
|
||||||
|
|
||||||
* `polygon_threshold=0.0`
|
|
||||||
|
|
||||||
Simplify the output geometry before returning. The parameter is the
|
|
||||||
tolerance in degrees with which the geometry may differ from the original
|
|
||||||
geometry. Topology is preserved in the result. (Default: 0.0)
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
* `email=<valid email address>`
|
|
||||||
|
|
||||||
If you are making large numbers of request please include an appropriate email
|
|
||||||
address to identify your requests. See Nominatim's [Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) for more details.
|
|
||||||
|
|
||||||
* `dedupe=[0|1]`
|
|
||||||
|
|
||||||
Sometimes you have several objects in OSM identifying the same place or
|
|
||||||
object in reality. The simplest case is a street being split in many
|
|
||||||
different OSM ways due to different characteristics. Nominatim will
|
|
||||||
attempt to detect such duplicates and only return one match unless
|
|
||||||
this parameter is set to 0. (Default: 1)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* `debug=[0|1]`
|
|
||||||
|
|
||||||
Output assorted developer debug information. Data on internals of Nominatim's
|
|
||||||
"Search Loop" logic, and SQL queries. The output is (rough) HTML format.
|
|
||||||
This overrides the specified machine readable format. (Default: 0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
|
|
||||||
##### XML with polygon points
|
|
||||||
|
|
||||||
* [https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon=1&addressdetails=1](https://nominatim.openstreetmap.org/search?q=135+pilkington+avenue,+birmingham&format=xml&polygon=1&addressdetails=1)
|
|
||||||
* [https://nominatim.openstreetmap.org/search/gb/birmingham/pilkington%20avenue/135?format=xml&polygon=1&addressdetails=1](https://nominatim.openstreetmap.org/search/gb/birmingham/pilkington%20avenue/135?format=xml&polygon=1&addressdetails=1)
|
|
||||||
* [https://nominatim.openstreetmap.org/search/135%20pilkington%20avenue,%20birmingham?format=xml&polygon=1&addressdetails=1](https://nominatim.openstreetmap.org/search/135%20pilkington%20avenue,%20birmingham?format=xml&polygon=1&addressdetails=1)
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<searchresults timestamp="Sat, 07 Nov 09 14:42:10 +0000" querystring="135 pilkington, avenue birmingham" polygon="true">
|
|
||||||
<place
|
|
||||||
place_id="1620612" osm_type="node" osm_id="452010817"
|
|
||||||
boundingbox="52.548641204834,52.5488433837891,-1.81612110137939,-1.81592094898224"
|
|
||||||
polygonpoints="[['-1.81592098644987','52.5487429714954'],['-1.81592290792183','52.5487234624632'],...]"
|
|
||||||
lat="52.5487429714954" lon="-1.81602098644987"
|
|
||||||
display_name="135, Pilkington Avenue, Wylde Green, City of Birmingham, West Midlands (county), B72, United Kingdom"
|
|
||||||
class="place" type="house">
|
|
||||||
<house_number>135</house_number>
|
|
||||||
<road>Pilkington Avenue</road>
|
|
||||||
<village>Wylde Green</village>
|
|
||||||
<town>Sutton Coldfield</town>
|
|
||||||
<city>City of Birmingham</city>
|
|
||||||
<county>West Midlands (county)</county>
|
|
||||||
<postcode>B72</postcode>
|
|
||||||
<country>United Kingdom</country>
|
|
||||||
<country_code>gb</country_code>
|
|
||||||
</place>
|
|
||||||
</searchresults>
|
|
||||||
```
|
|
||||||
|
|
||||||
##### JSON with SVG polygon
|
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/search/Unter%20den%20Linden%201%20Berlin?format=json&addressdetails=1&limit=1&polygon_svg=1](https://nominatim.openstreetmap.org/search/Unter%20den%20Linden%201%20Berlin?format=json&addressdetails=1&limit=1&polygon_svg=1)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"address": {
|
|
||||||
"city": "Berlin",
|
|
||||||
"city_district": "Mitte",
|
|
||||||
"construction": "Unter den Linden",
|
|
||||||
"continent": "European Union",
|
|
||||||
"country": "Deutschland",
|
|
||||||
"country_code": "de",
|
|
||||||
"house_number": "1",
|
|
||||||
"neighbourhood": "Scheunenviertel",
|
|
||||||
"postcode": "10117",
|
|
||||||
"public_building": "Kommandantenhaus",
|
|
||||||
"state": "Berlin",
|
|
||||||
"suburb": "Mitte"
|
|
||||||
},
|
|
||||||
"boundingbox": [
|
|
||||||
"52.5170783996582",
|
|
||||||
"52.5173187255859",
|
|
||||||
"13.3975105285645",
|
|
||||||
"13.3981599807739"
|
|
||||||
],
|
|
||||||
"class": "amenity",
|
|
||||||
"display_name": "Kommandantenhaus, 1, Unter den Linden, Scheunenviertel, Mitte, Berlin, 10117, Deutschland, European Union",
|
|
||||||
"importance": 0.73606775332943,
|
|
||||||
"lat": "52.51719785",
|
|
||||||
"licence": "Data \u00a9 OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright",
|
|
||||||
"lon": "13.3978352028938",
|
|
||||||
"osm_id": "15976890",
|
|
||||||
"osm_type": "way",
|
|
||||||
"place_id": "30848715",
|
|
||||||
"svg": "M 13.397511 -52.517283599999999 L 13.397829400000001 -52.517299800000004 13.398131599999999 -52.517315099999998 13.398159400000001 -52.517112099999999 13.3975388 -52.517080700000001 Z",
|
|
||||||
"type": "public_building"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### JSON with address details
|
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/?format=json&addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1](https://nominatim.openstreetmap.org/?format=json&addressdetails=1&q=bakery+in+berlin+wedding&format=json&limit=1)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"address": {
|
|
||||||
"bakery": "B\u00e4cker Kamps",
|
|
||||||
"city_district": "Mitte",
|
|
||||||
"continent": "European Union",
|
|
||||||
"country": "Deutschland",
|
|
||||||
"country_code": "de",
|
|
||||||
"footway": "Bahnsteig U6",
|
|
||||||
"neighbourhood": "Sprengelkiez",
|
|
||||||
"postcode": "13353",
|
|
||||||
"state": "Berlin",
|
|
||||||
"suburb": "Wedding"
|
|
||||||
},
|
|
||||||
"boundingbox": [
|
|
||||||
"52.5460929870605",
|
|
||||||
"52.5460968017578",
|
|
||||||
"13.3591794967651",
|
|
||||||
"13.3591804504395"
|
|
||||||
],
|
|
||||||
"class": "shop",
|
|
||||||
"display_name": "B\u00e4cker Kamps, Bahnsteig U6, Sprengelkiez, Wedding, Mitte, Berlin, 13353, Deutschland, European Union",
|
|
||||||
"icon": "https://nominatim.openstreetmap.org/images/mapicons/shopping_bakery.p.20.png",
|
|
||||||
"importance": 0.201,
|
|
||||||
"lat": "52.5460941",
|
|
||||||
"licence": "Data \u00a9 OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright",
|
|
||||||
"lon": "13.35918",
|
|
||||||
"osm_id": "317179427",
|
|
||||||
"osm_type": "node",
|
|
||||||
"place_id": "1453068",
|
|
||||||
"type": "bakery"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### GeoJSON
|
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/search?q=17+Strada+Pictor+Alexandru+Romano%2C+Bukarest&format=geojson](https://nominatim.openstreetmap.org/search?q=17+Strada+Pictor+Alexandru+Romano%2C+Bukarest&format=geojson)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "FeatureCollection",
|
|
||||||
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
|
||||||
"features": [
|
|
||||||
{
|
|
||||||
"type": "Feature",
|
|
||||||
"properties": {
|
|
||||||
"place_id": "35811445",
|
|
||||||
"osm_type": "node",
|
|
||||||
"osm_id": "2846295644",
|
|
||||||
"display_name": "17, Strada Pictor Alexandru Romano, Bukarest, Bucharest, Sector 2, Bucharest, 023964, Romania",
|
|
||||||
"place_rank": "30",
|
|
||||||
"category": "place",
|
|
||||||
"type": "house",
|
|
||||||
"importance": 0.62025
|
|
||||||
},
|
|
||||||
"bbox": [
|
|
||||||
26.1156689,
|
|
||||||
44.4354754,
|
|
||||||
26.1157689,
|
|
||||||
44.4355754
|
|
||||||
],
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
26.1157189,
|
|
||||||
44.4355254
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### GeocodeJSON
|
|
||||||
|
|
||||||
[https://nominatim.openstreetmap.org/search?q=%CE%91%CE%B3%CE%AF%CE%B1+%CE%A4%CF%81%CE%B9%CE%AC%CE%B4%CE%B1%2C+%CE%91%CE%B4%CF%89%CE%BD%CE%B9%CE%B4%CE%BF%CF%82%2C+Athens%2C+Greece&format=geocodejson](https://nominatim.openstreetmap.org/search?q=%CE%91%CE%B3%CE%AF%CE%B1+%CE%A4%CF%81%CE%B9%CE%AC%CE%B4%CE%B1%2C+%CE%91%CE%B4%CF%89%CE%BD%CE%B9%CE%B4%CE%BF%CF%82%2C+Athens%2C+Greece&format=geocodejson)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "FeatureCollection",
|
|
||||||
"geocoding": {
|
|
||||||
"version": "0.1.0",
|
|
||||||
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
|
|
||||||
"licence": "ODbL",
|
|
||||||
"query": "Αγία Τριάδα, Αδωνιδος, Athens, Greece"
|
|
||||||
},
|
|
||||||
"features": [
|
|
||||||
{
|
|
||||||
"type": "Feature",
|
|
||||||
"properties": {
|
|
||||||
"geocoding": {
|
|
||||||
"type": "place_of_worship",
|
|
||||||
"label": "Αγία Τριάδα, Αδωνιδος, Άγιος Νικόλαος, 5º Δημοτικό Διαμέρισμα Αθηνών, Athens, Municipality of Athens, Regional Unit of Central Athens, Region of Attica, Attica, 11472, Greece",
|
|
||||||
"name": "Αγία Τριάδα",
|
|
||||||
"admin": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"geometry": {
|
|
||||||
"type": "Point",
|
|
||||||
"coordinates": [
|
|
||||||
23.72949633941,
|
|
||||||
38.0051697
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# Extract markdown-formatted documentation from a source file
|
|
||||||
#
|
|
||||||
# Usage: bash2md.sh <infile> <outfile>
|
|
||||||
|
|
||||||
sed '/^#!/d;s:^#\( \|$\)::;s/.*#DOCS://' $1 > $2
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Basic Architecture
|
|
||||||
|
|
||||||
Nominatim provides geocoding based on OpenStreetMap data. It uses a Postgresql
|
|
||||||
database as a backend for storing the data.
|
|
||||||
|
|
||||||
There are three basic parts to Nominatim's architecture: the data import,
|
|
||||||
the address computation and the search frontend.
|
|
||||||
|
|
||||||
The __data import__ stage reads the raw OSM data and extracts all information
|
|
||||||
that is useful for geocoding. This part is done by osm2pgsql, the same tool
|
|
||||||
that can also be used to import a rendering database. It uses the special
|
|
||||||
gazetteer output plugin in `osm2pgsql/output-gazetter.[ch]pp`. The result of
|
|
||||||
the import can be found in the database table `place`.
|
|
||||||
|
|
||||||
The __address computation__ or __indexing__ stage takes the data from `place`
|
|
||||||
and adds additional information needed for geocoding. It ranks the places by
|
|
||||||
importance, links objects that belong together and computes addresses and
|
|
||||||
the search index. Most of this work is done in Pl/pqSQL via database triggers
|
|
||||||
and can be found in the file `sql/functions.sql`.
|
|
||||||
|
|
||||||
The __search frontend__ implements the actual API. It takes queries for
|
|
||||||
search and reverse geocoding queries from the user, looks up the data and
|
|
||||||
returns the results in the requested format. This part is written in PHP
|
|
||||||
and can be found in the `lib/` and `website/` directories.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.toctree-l3 {
|
|
||||||
display: none!important
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
Nominatim (from the Latin, 'by name') is a tool to search OSM data by name and address and to generate synthetic addresses of OSM points (reverse geocoding).
|
|
||||||
|
|
||||||
This guide comes in three parts:
|
|
||||||
|
|
||||||
* __[API reference](api/Overview.md)__ for users of Nominatim
|
|
||||||
* __[Administration Guide](admin/Installation.md)__ for those who want
|
|
||||||
to install their own Nominatim server
|
|
||||||
* __[Developer's Guide](develop/overview.md)__ for developers of the software
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
site_name: Nominatim Documentation
|
|
||||||
theme: readthedocs
|
|
||||||
docs_dir: ${CMAKE_CURRENT_BINARY_DIR}
|
|
||||||
site_url: http://nominatim.org
|
|
||||||
repo_url: https://github.com/openstreetmap/Nominatim
|
|
||||||
pages:
|
|
||||||
- 'Introduction' : 'index.md'
|
|
||||||
- 'API Reference':
|
|
||||||
- 'Overview': 'api/Overview.md'
|
|
||||||
- 'Search': 'api/Search.md'
|
|
||||||
- 'Reverse': 'api/Reverse.md'
|
|
||||||
- 'Address Lookup': 'api/Lookup.md'
|
|
||||||
- 'Details' : 'api/Details.md'
|
|
||||||
- 'Place Output Formats': 'api/Output.md'
|
|
||||||
- 'FAQ': 'api/Faq.md'
|
|
||||||
- 'Administration Guide':
|
|
||||||
- 'Basic Installation': 'admin/Installation.md'
|
|
||||||
- 'Importing and Updating' : 'admin/Import-and-Update.md'
|
|
||||||
- 'Migration from older Versions' : 'admin/Migration.md'
|
|
||||||
- 'Troubleshooting' : 'admin/Faq.md'
|
|
||||||
- 'Developers Guide':
|
|
||||||
- 'Overview' : 'develop/overview.md'
|
|
||||||
- 'Appendix':
|
|
||||||
- 'Installation on CentOS 7' : 'appendix/Install-on-Centos-7.md'
|
|
||||||
- 'Installation on Ubuntu 16' : 'appendix/Install-on-Ubuntu-16.md'
|
|
||||||
- 'Installation on Ubuntu 18' : 'appendix/Install-on-Ubuntu-18.md'
|
|
||||||
markdown_extensions:
|
|
||||||
- codehilite:
|
|
||||||
use_pygments: False
|
|
||||||
- toc:
|
|
||||||
permalink:
|
|
||||||
extra_css: [extra.css]
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/ClassTypes.php');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detailed list of address parts for a single result
|
|
||||||
*/
|
|
||||||
class AddressDetails
|
|
||||||
{
|
|
||||||
private $aAddressLines;
|
|
||||||
|
|
||||||
public function __construct(&$oDB, $iPlaceID, $sHousenumber, $mLangPref)
|
|
||||||
{
|
|
||||||
if (is_array($mLangPref)) {
|
|
||||||
$mLangPref = 'ARRAY['.join(',', array_map('getDBQuoted', $mLangPref)).']';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$sHousenumber) {
|
|
||||||
$sHousenumber = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sSQL = 'SELECT *,';
|
|
||||||
$sSQL .= ' get_name_by_language(name,'.$mLangPref.') as localname';
|
|
||||||
$sSQL .= ' FROM get_addressdata('.$iPlaceID.','.$sHousenumber.')';
|
|
||||||
$sSQL .= ' ORDER BY rank_address desc,isaddress DESC';
|
|
||||||
|
|
||||||
$this->aAddressLines = chksql($oDB->getAll($sSQL));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function isAddress($aLine)
|
|
||||||
{
|
|
||||||
return $aLine['isaddress'] == 't' || $aLine['type'] == 'country_code';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAddressDetails($bAll = false)
|
|
||||||
{
|
|
||||||
if ($bAll) {
|
|
||||||
return $this->aAddressLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_filter($this->aAddressLines, 'AddressDetails::isAddress');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLocaleAddress()
|
|
||||||
{
|
|
||||||
$aParts = array();
|
|
||||||
$sPrevResult = '';
|
|
||||||
|
|
||||||
foreach ($this->aAddressLines as $aLine) {
|
|
||||||
if ($aLine['isaddress'] == 't' && $sPrevResult != $aLine['localname']) {
|
|
||||||
$sPrevResult = $aLine['localname'];
|
|
||||||
$aParts[] = $sPrevResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return join(', ', $aParts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAddressNames()
|
|
||||||
{
|
|
||||||
$aAddress = array();
|
|
||||||
$aFallback = array();
|
|
||||||
|
|
||||||
foreach ($this->aAddressLines as $aLine) {
|
|
||||||
if (!self::isAddress($aLine)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$bFallback = false;
|
|
||||||
$aTypeLabel = ClassTypes\getInfo($aLine);
|
|
||||||
|
|
||||||
if ($aTypeLabel === false) {
|
|
||||||
$aTypeLabel = ClassTypes\getFallbackInfo($aLine);
|
|
||||||
$bFallback = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sName = false;
|
|
||||||
if (isset($aLine['localname']) && $aLine['localname']) {
|
|
||||||
$sName = $aLine['localname'];
|
|
||||||
} elseif (isset($aLine['housenumber']) && $aLine['housenumber']) {
|
|
||||||
$sName = $aLine['housenumber'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($sName) {
|
|
||||||
$sTypeLabel = strtolower(isset($aTypeLabel['simplelabel']) ? $aTypeLabel['simplelabel'] : $aTypeLabel['label']);
|
|
||||||
$sTypeLabel = str_replace(' ', '_', $sTypeLabel);
|
|
||||||
if (!isset($aAddress[$sTypeLabel])
|
|
||||||
|| isset($aFallback[$sTypeLabel])
|
|
||||||
|| $aLine['class'] == 'place'
|
|
||||||
) {
|
|
||||||
$aAddress[$sTypeLabel] = $sName;
|
|
||||||
if ($bFallback) {
|
|
||||||
$aFallback[$sTypeLabel] = $bFallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAdminLevels()
|
|
||||||
{
|
|
||||||
$aAddress = array();
|
|
||||||
foreach ($this->aAddressLines as $aLine) {
|
|
||||||
if (self::isAddress($aLine)
|
|
||||||
&& isset($aLine['admin_level'])
|
|
||||||
&& $aLine['admin_level'] < 15
|
|
||||||
&& !isset($aAddress['level'.$aLine['admin_level']])
|
|
||||||
) {
|
|
||||||
$aAddress['level'.$aLine['admin_level']] = $aLine['localname'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return $this->aAddressLines;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,374 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim\ClassTypes;
|
|
||||||
|
|
||||||
function getInfo($aPlace)
|
|
||||||
{
|
|
||||||
$aClassType = getList();
|
|
||||||
|
|
||||||
if (isset($aPlace['admin_level'])) {
|
|
||||||
$sName = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
|
||||||
if (isset($aClassType[$sName])) {
|
|
||||||
return $aClassType[$sName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$sName = $aPlace['class'].':'.$aPlace['type'];
|
|
||||||
if (isset($aClassType[$sName])) {
|
|
||||||
return $aClassType[$sName];
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFallbackInfo($aPlace)
|
|
||||||
{
|
|
||||||
$aClassType = getList();
|
|
||||||
|
|
||||||
$sFallback = 'boundary:administrative:'.((int)($aPlace['rank_address']/2));
|
|
||||||
if (isset($aClassType[$sFallback])) {
|
|
||||||
return $aClassType[$sFallback];
|
|
||||||
}
|
|
||||||
|
|
||||||
return array('simplelabel' => 'address'.$aPlace['rank_address']);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProperty($aPlace, $sProp, $mDefault = false)
|
|
||||||
{
|
|
||||||
$aClassType = getList();
|
|
||||||
|
|
||||||
if (isset($aPlace['admin_level'])) {
|
|
||||||
$sName = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
|
||||||
if (isset($aClassType[$sName]) && isset($aClassType[$sName][$sProp])) {
|
|
||||||
return $aClassType[$sName][$sProp];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$sName = $aPlace['class'].':'.$aPlace['type'];
|
|
||||||
if (isset($aClassType[$sName]) && isset($aClassType[$sName][$sProp])) {
|
|
||||||
return $aClassType[$sName][$sProp];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $mDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getListWithImportance()
|
|
||||||
{
|
|
||||||
static $aOrders = null;
|
|
||||||
if ($aOrders === null) {
|
|
||||||
$aOrders = getList();
|
|
||||||
$i = 1;
|
|
||||||
foreach ($aOrders as $sID => $a) {
|
|
||||||
$aOrders[$sID]['importance'] = $i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aOrders;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getList()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'boundary:administrative:1' => array('label' => 'Continent', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:2' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:country' => array('label' => 'Country', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 6, 'defdiameter' => 15),
|
|
||||||
'boundary:administrative:3' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:4' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:state' => array('label' => 'State', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 5.12),
|
|
||||||
'boundary:administrative:5' => array('label' => 'State District', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:6' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:7' => array('label' => 'County', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:county' => array('label' => 'County', 'frequency' => 108, 'icon' => 'poi_boundary_administrative', 'defzoom' => 10, 'defdiameter' => 1.28),
|
|
||||||
'boundary:administrative:8' => array('label' => 'City', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:city' => array('label' => 'City', 'frequency' => 66, 'icon' => 'poi_place_city', 'defzoom' => 12, 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:9' => array('label' => 'City District', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:10' => array('label' => 'Suburb', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:administrative:11' => array('label' => 'Neighbourhood', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:region' => array('label' => 'Region', 'frequency' => 0, 'icon' => 'poi_boundary_administrative', 'defzoom' => 8, 'defdiameter' => 0.04),
|
|
||||||
'place:island' => array('label' => 'Island', 'frequency' => 288, 'defzoom' => 11, 'defdiameter' => 0.64),
|
|
||||||
'boundary:administrative' => array('label' => 'Administrative', 'frequency' => 413, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'boundary:postal_code' => array('label' => 'Postcode', 'frequency' => 413, 'icon' => 'poi_boundary_administrative', 'defdiameter' => 0.32),
|
|
||||||
'place:town' => array('label' => 'Town', 'frequency' => 1497, 'icon' => 'poi_place_town', 'defzoom' => 14, 'defdiameter' => 0.08),
|
|
||||||
'place:village' => array('label' => 'Village', 'frequency' => 11230, 'icon' => 'poi_place_village', 'defzoom' => 15, 'defdiameter' => 0.04),
|
|
||||||
'place:hamlet' => array('label' => 'Hamlet', 'frequency' => 7075, 'icon' => 'poi_place_village', 'defzoom' => 15, 'defdiameter' => 0.04),
|
|
||||||
'place:suburb' => array('label' => 'Suburb', 'frequency' => 2528, 'icon' => 'poi_place_village', 'defdiameter' => 0.04),
|
|
||||||
'place:locality' => array('label' => 'Locality', 'frequency' => 4113, 'icon' => 'poi_place_village', 'defdiameter' => 0.02),
|
|
||||||
'landuse:farm' => array('label' => 'Farm', 'frequency' => 1201, 'defdiameter' => 0.02),
|
|
||||||
'place:farm' => array('label' => 'Farm', 'frequency' => 1162, 'defdiameter' => 0.02),
|
|
||||||
|
|
||||||
'highway:motorway_junction' => array('label' => 'Motorway Junction', 'frequency' => 1126, 'simplelabel' => 'Junction'),
|
|
||||||
'highway:motorway' => array('label' => 'Motorway', 'frequency' => 4627, 'simplelabel' => 'Road'),
|
|
||||||
'highway:trunk' => array('label' => 'Trunk', 'frequency' => 23084, 'simplelabel' => 'Road'),
|
|
||||||
'highway:primary' => array('label' => 'Primary', 'frequency' => 32138, 'simplelabel' => 'Road'),
|
|
||||||
'highway:secondary' => array('label' => 'Secondary', 'frequency' => 25807, 'simplelabel' => 'Road'),
|
|
||||||
'highway:tertiary' => array('label' => 'Tertiary', 'frequency' => 29829, 'simplelabel' => 'Road'),
|
|
||||||
'highway:residential' => array('label' => 'Residential', 'frequency' => 361498, 'simplelabel' => 'Road'),
|
|
||||||
'highway:unclassified' => array('label' => 'Unclassified', 'frequency' => 66441, 'simplelabel' => 'Road'),
|
|
||||||
'highway:living_street' => array('label' => 'Living Street', 'frequency' => 710, 'simplelabel' => 'Road'),
|
|
||||||
'highway:service' => array('label' => 'Service', 'frequency' => 9963, 'simplelabel' => 'Road'),
|
|
||||||
'highway:track' => array('label' => 'Track', 'frequency' => 2565, 'simplelabel' => 'Road'),
|
|
||||||
'highway:road' => array('label' => 'Road', 'frequency' => 591, 'simplelabel' => 'Road'),
|
|
||||||
'highway:byway' => array('label' => 'Byway', 'frequency' => 346, 'simplelabel' => 'Road'),
|
|
||||||
'highway:bridleway' => array('label' => 'Bridleway', 'frequency' => 1556),
|
|
||||||
'highway:cycleway' => array('label' => 'Cycleway', 'frequency' => 2419),
|
|
||||||
'highway:pedestrian' => array('label' => 'Pedestrian', 'frequency' => 2757),
|
|
||||||
'highway:footway' => array('label' => 'Footway', 'frequency' => 15008),
|
|
||||||
'highway:steps' => array('label' => 'Steps', 'frequency' => 444, 'simplelabel' => 'Footway'),
|
|
||||||
'highway:motorway_link' => array('label' => 'Motorway Link', 'frequency' => 795, 'simplelabel' => 'Road'),
|
|
||||||
'highway:trunk_link' => array('label' => 'Trunk Link', 'frequency' => 1258, 'simplelabel' => 'Road'),
|
|
||||||
'highway:primary_link' => array('label' => 'Primary Link', 'frequency' => 313, 'simplelabel' => 'Road'),
|
|
||||||
|
|
||||||
'landuse:industrial' => array('label' => 'Industrial', 'frequency' => 1062),
|
|
||||||
'landuse:residential' => array('label' => 'Residential', 'frequency' => 886),
|
|
||||||
'landuse:retail' => array('label' => 'Retail', 'frequency' => 754),
|
|
||||||
'landuse:commercial' => array('label' => 'Commercial', 'frequency' => 657),
|
|
||||||
|
|
||||||
'place:airport' => array('label' => 'Airport', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
|
||||||
'aeroway:aerodrome' => array('label' => 'Aerodrome', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
|
||||||
'aeroway' => array('label' => 'Aeroway', 'frequency' => 36, 'icon' => 'transport_airport2', 'defdiameter' => 0.03),
|
|
||||||
'railway:station' => array('label' => 'Station', 'frequency' => 3431, 'icon' => 'transport_train_station2', 'defdiameter' => 0.01),
|
|
||||||
'amenity:place_of_worship' => array('label' => 'Place Of Worship', 'frequency' => 9049, 'icon' => 'place_of_worship_unknown3'),
|
|
||||||
'amenity:pub' => array('label' => 'Pub', 'frequency' => 18969, 'icon' => 'food_pub'),
|
|
||||||
'amenity:bar' => array('label' => 'Bar', 'frequency' => 164, 'icon' => 'food_bar'),
|
|
||||||
'amenity:university' => array('label' => 'University', 'frequency' => 607, 'icon' => 'education_university'),
|
|
||||||
'tourism:museum' => array('label' => 'Museum', 'frequency' => 543, 'icon' => 'tourist_museum'),
|
|
||||||
'amenity:arts_centre' => array('label' => 'Arts Centre', 'frequency' => 136, 'icon' => 'tourist_art_gallery2'),
|
|
||||||
'tourism:zoo' => array('label' => 'Zoo', 'frequency' => 47, 'icon' => 'tourist_zoo'),
|
|
||||||
'tourism:theme_park' => array('label' => 'Theme Park', 'frequency' => 24, 'icon' => 'poi_point_of_interest'),
|
|
||||||
'tourism:attraction' => array('label' => 'Attraction', 'frequency' => 1463, 'icon' => 'poi_point_of_interest'),
|
|
||||||
'leisure:golf_course' => array('label' => 'Golf Course', 'frequency' => 712, 'icon' => 'sport_golf'),
|
|
||||||
'historic:castle' => array('label' => 'Castle', 'frequency' => 316, 'icon' => 'tourist_castle'),
|
|
||||||
'amenity:hospital' => array('label' => 'Hospital', 'frequency' => 879, 'icon' => 'health_hospital'),
|
|
||||||
'amenity:school' => array('label' => 'School', 'frequency' => 8192, 'icon' => 'education_school'),
|
|
||||||
'amenity:theatre' => array('label' => 'Theatre', 'frequency' => 371, 'icon' => 'tourist_theatre'),
|
|
||||||
'amenity:public_building' => array('label' => 'Public Building', 'frequency' => 985),
|
|
||||||
'amenity:library' => array('label' => 'Library', 'frequency' => 794, 'icon' => 'amenity_library'),
|
|
||||||
'amenity:townhall' => array('label' => 'Townhall', 'frequency' => 242),
|
|
||||||
'amenity:community_centre' => array('label' => 'Community Centre', 'frequency' => 157),
|
|
||||||
'amenity:fire_station' => array('label' => 'Fire Station', 'frequency' => 221, 'icon' => 'amenity_firestation3'),
|
|
||||||
'amenity:police' => array('label' => 'Police', 'frequency' => 334, 'icon' => 'amenity_police2'),
|
|
||||||
'amenity:bank' => array('label' => 'Bank', 'frequency' => 1248, 'icon' => 'money_bank2'),
|
|
||||||
'amenity:post_office' => array('label' => 'Post Office', 'frequency' => 859, 'icon' => 'amenity_post_office'),
|
|
||||||
'leisure:park' => array('label' => 'Park', 'frequency' => 2378),
|
|
||||||
'amenity:park' => array('label' => 'Park', 'frequency' => 53),
|
|
||||||
'landuse:park' => array('label' => 'Park', 'frequency' => 50),
|
|
||||||
'landuse:recreation_ground' => array('label' => 'Recreation Ground', 'frequency' => 517),
|
|
||||||
'tourism:hotel' => array('label' => 'Hotel', 'frequency' => 2150, 'icon' => 'accommodation_hotel2'),
|
|
||||||
'tourism:motel' => array('label' => 'Motel', 'frequency' => 43),
|
|
||||||
'amenity:cinema' => array('label' => 'Cinema', 'frequency' => 277, 'icon' => 'tourist_cinema'),
|
|
||||||
'tourism:artwork' => array('label' => 'Artwork', 'frequency' => 171, 'icon' => 'tourist_art_gallery2'),
|
|
||||||
'historic:archaeological_site' => array('label' => 'Archaeological Site', 'frequency' => 407, 'icon' => 'tourist_archaeological2'),
|
|
||||||
'amenity:doctors' => array('label' => 'Doctors', 'frequency' => 581, 'icon' => 'health_doctors'),
|
|
||||||
'leisure:sports_centre' => array('label' => 'Sports Centre', 'frequency' => 767, 'icon' => 'sport_leisure_centre'),
|
|
||||||
'leisure:swimming_pool' => array('label' => 'Swimming Pool', 'frequency' => 24, 'icon' => 'sport_swimming_outdoor'),
|
|
||||||
'shop:supermarket' => array('label' => 'Supermarket', 'frequency' => 2673, 'icon' => 'shopping_supermarket'),
|
|
||||||
'shop:convenience' => array('label' => 'Convenience', 'frequency' => 1469, 'icon' => 'shopping_convenience'),
|
|
||||||
'amenity:restaurant' => array('label' => 'Restaurant', 'frequency' => 3179, 'icon' => 'food_restaurant'),
|
|
||||||
'amenity:fast_food' => array('label' => 'Fast Food', 'frequency' => 2289, 'icon' => 'food_fastfood'),
|
|
||||||
'amenity:cafe' => array('label' => 'Cafe', 'frequency' => 1780, 'icon' => 'food_cafe'),
|
|
||||||
'tourism:guest_house' => array('label' => 'Guest House', 'frequency' => 223, 'icon' => 'accommodation_bed_and_breakfast'),
|
|
||||||
'amenity:pharmacy' => array('label' => 'Pharmacy', 'frequency' => 733, 'icon' => 'health_pharmacy_dispensing'),
|
|
||||||
'amenity:fuel' => array('label' => 'Fuel', 'frequency' => 1308, 'icon' => 'transport_fuel'),
|
|
||||||
'natural:peak' => array('label' => 'Peak', 'frequency' => 3212, 'icon' => 'poi_peak'),
|
|
||||||
'waterway:waterfall' => array('label' => 'Waterfall', 'frequency' => 24),
|
|
||||||
'natural:wood' => array('label' => 'Wood', 'frequency' => 1845, 'icon' => 'landuse_coniferous_and_deciduous'),
|
|
||||||
'natural:water' => array('label' => 'Water', 'frequency' => 1790),
|
|
||||||
'landuse:forest' => array('label' => 'Forest', 'frequency' => 467),
|
|
||||||
'landuse:cemetery' => array('label' => 'Cemetery', 'frequency' => 463),
|
|
||||||
'landuse:allotments' => array('label' => 'Allotments', 'frequency' => 408),
|
|
||||||
'landuse:farmyard' => array('label' => 'Farmyard', 'frequency' => 397),
|
|
||||||
'railway:rail' => array('label' => 'Rail', 'frequency' => 4894),
|
|
||||||
'waterway:canal' => array('label' => 'Canal', 'frequency' => 1723),
|
|
||||||
'waterway:river' => array('label' => 'River', 'frequency' => 4089),
|
|
||||||
'waterway:stream' => array('label' => 'Stream', 'frequency' => 2684),
|
|
||||||
'shop:bicycle' => array('label' => 'Bicycle', 'frequency' => 349, 'icon' => 'shopping_bicycle'),
|
|
||||||
'shop:clothes' => array('label' => 'Clothes', 'frequency' => 315, 'icon' => 'shopping_clothes'),
|
|
||||||
'shop:hairdresser' => array('label' => 'Hairdresser', 'frequency' => 312, 'icon' => 'shopping_hairdresser'),
|
|
||||||
'shop:doityourself' => array('label' => 'Doityourself', 'frequency' => 247, 'icon' => 'shopping_diy'),
|
|
||||||
'shop:estate_agent' => array('label' => 'Estate Agent', 'frequency' => 162, 'icon' => 'shopping_estateagent2'),
|
|
||||||
'shop:car' => array('label' => 'Car', 'frequency' => 159, 'icon' => 'shopping_car'),
|
|
||||||
'shop:garden_centre' => array('label' => 'Garden Centre', 'frequency' => 143, 'icon' => 'shopping_garden_centre'),
|
|
||||||
'shop:car_repair' => array('label' => 'Car Repair', 'frequency' => 141, 'icon' => 'shopping_car_repair'),
|
|
||||||
'shop:newsagent' => array('label' => 'Newsagent', 'frequency' => 132),
|
|
||||||
'shop:bakery' => array('label' => 'Bakery', 'frequency' => 129, 'icon' => 'shopping_bakery'),
|
|
||||||
'shop:furniture' => array('label' => 'Furniture', 'frequency' => 124),
|
|
||||||
'shop:butcher' => array('label' => 'Butcher', 'frequency' => 105, 'icon' => 'shopping_butcher'),
|
|
||||||
'shop:apparel' => array('label' => 'Apparel', 'frequency' => 98, 'icon' => 'shopping_clothes'),
|
|
||||||
'shop:electronics' => array('label' => 'Electronics', 'frequency' => 96),
|
|
||||||
'shop:department_store' => array('label' => 'Department Store', 'frequency' => 86),
|
|
||||||
'shop:books' => array('label' => 'Books', 'frequency' => 85),
|
|
||||||
'shop:yes' => array('label' => 'Shop', 'frequency' => 68),
|
|
||||||
'shop:outdoor' => array('label' => 'Outdoor', 'frequency' => 67),
|
|
||||||
'shop:mall' => array('label' => 'Mall', 'frequency' => 63),
|
|
||||||
'shop:florist' => array('label' => 'Florist', 'frequency' => 61),
|
|
||||||
'shop:charity' => array('label' => 'Charity', 'frequency' => 60),
|
|
||||||
'shop:hardware' => array('label' => 'Hardware', 'frequency' => 59),
|
|
||||||
'shop:laundry' => array('label' => 'Laundry', 'frequency' => 51, 'icon' => 'shopping_laundrette'),
|
|
||||||
'shop:shoes' => array('label' => 'Shoes', 'frequency' => 49),
|
|
||||||
'shop:beverages' => array('label' => 'Beverages', 'frequency' => 48, 'icon' => 'shopping_alcohol'),
|
|
||||||
'shop:dry_cleaning' => array('label' => 'Dry Cleaning', 'frequency' => 46),
|
|
||||||
'shop:carpet' => array('label' => 'Carpet', 'frequency' => 45),
|
|
||||||
'shop:computer' => array('label' => 'Computer', 'frequency' => 44),
|
|
||||||
'shop:alcohol' => array('label' => 'Alcohol', 'frequency' => 44, 'icon' => 'shopping_alcohol'),
|
|
||||||
'shop:optician' => array('label' => 'Optician', 'frequency' => 55, 'icon' => 'health_opticians'),
|
|
||||||
'shop:chemist' => array('label' => 'Chemist', 'frequency' => 42, 'icon' => 'health_pharmacy'),
|
|
||||||
'shop:gallery' => array('label' => 'Gallery', 'frequency' => 38, 'icon' => 'tourist_art_gallery2'),
|
|
||||||
'shop:mobile_phone' => array('label' => 'Mobile Phone', 'frequency' => 37),
|
|
||||||
'shop:sports' => array('label' => 'Sports', 'frequency' => 37),
|
|
||||||
'shop:jewelry' => array('label' => 'Jewelry', 'frequency' => 32, 'icon' => 'shopping_jewelry'),
|
|
||||||
'shop:pet' => array('label' => 'Pet', 'frequency' => 29),
|
|
||||||
'shop:beauty' => array('label' => 'Beauty', 'frequency' => 28),
|
|
||||||
'shop:stationery' => array('label' => 'Stationery', 'frequency' => 25),
|
|
||||||
'shop:shopping_centre' => array('label' => 'Shopping Centre', 'frequency' => 25),
|
|
||||||
'shop:general' => array('label' => 'General', 'frequency' => 25),
|
|
||||||
'shop:electrical' => array('label' => 'Electrical', 'frequency' => 25),
|
|
||||||
'shop:toys' => array('label' => 'Toys', 'frequency' => 23),
|
|
||||||
'shop:jeweller' => array('label' => 'Jeweller', 'frequency' => 23),
|
|
||||||
'shop:betting' => array('label' => 'Betting', 'frequency' => 23),
|
|
||||||
'shop:household' => array('label' => 'Household', 'frequency' => 21),
|
|
||||||
'shop:travel_agency' => array('label' => 'Travel Agency', 'frequency' => 21),
|
|
||||||
'shop:hifi' => array('label' => 'Hifi', 'frequency' => 21),
|
|
||||||
'amenity:shop' => array('label' => 'Shop', 'frequency' => 61),
|
|
||||||
'tourism:information' => array('label' => 'Information', 'frequency' => 224, 'icon' => 'amenity_information'),
|
|
||||||
|
|
||||||
'place:house' => array('label' => 'House', 'frequency' => 2086, 'defzoom' => 18),
|
|
||||||
'place:house_name' => array('label' => 'House', 'frequency' => 2086, 'defzoom' => 18),
|
|
||||||
'place:house_number' => array('label' => 'House Number', 'frequency' => 2086, 'defzoom' => 18),
|
|
||||||
'place:country_code' => array('label' => 'Country Code', 'frequency' => 2086, 'defzoom' => 18),
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
'leisure:pitch' => array('label' => 'Pitch', 'frequency' => 762),
|
|
||||||
'highway:unsurfaced' => array('label' => 'Unsurfaced', 'frequency' => 492),
|
|
||||||
'historic:ruins' => array('label' => 'Ruins', 'frequency' => 483, 'icon' => 'tourist_ruin'),
|
|
||||||
'amenity:college' => array('label' => 'College', 'frequency' => 473, 'icon' => 'education_school'),
|
|
||||||
'historic:monument' => array('label' => 'Monument', 'frequency' => 470, 'icon' => 'tourist_monument'),
|
|
||||||
'railway:subway' => array('label' => 'Subway', 'frequency' => 385),
|
|
||||||
'historic:memorial' => array('label' => 'Memorial', 'frequency' => 382, 'icon' => 'tourist_monument'),
|
|
||||||
'leisure:nature_reserve' => array('label' => 'Nature Reserve', 'frequency' => 342),
|
|
||||||
'leisure:common' => array('label' => 'Common', 'frequency' => 322),
|
|
||||||
'waterway:lock_gate' => array('label' => 'Lock Gate', 'frequency' => 321),
|
|
||||||
'natural:fell' => array('label' => 'Fell', 'frequency' => 308),
|
|
||||||
'amenity:nightclub' => array('label' => 'Nightclub', 'frequency' => 292),
|
|
||||||
'highway:path' => array('label' => 'Path', 'frequency' => 287),
|
|
||||||
'leisure:garden' => array('label' => 'Garden', 'frequency' => 285),
|
|
||||||
'landuse:reservoir' => array('label' => 'Reservoir', 'frequency' => 276),
|
|
||||||
'leisure:playground' => array('label' => 'Playground', 'frequency' => 264),
|
|
||||||
'leisure:stadium' => array('label' => 'Stadium', 'frequency' => 212),
|
|
||||||
'historic:mine' => array('label' => 'Mine', 'frequency' => 193, 'icon' => 'poi_mine'),
|
|
||||||
'natural:cliff' => array('label' => 'Cliff', 'frequency' => 193),
|
|
||||||
'tourism:caravan_site' => array('label' => 'Caravan Site', 'frequency' => 183, 'icon' => 'accommodation_caravan_park'),
|
|
||||||
'amenity:bus_station' => array('label' => 'Bus Station', 'frequency' => 181, 'icon' => 'transport_bus_station'),
|
|
||||||
'amenity:kindergarten' => array('label' => 'Kindergarten', 'frequency' => 179),
|
|
||||||
'highway:construction' => array('label' => 'Construction', 'frequency' => 176),
|
|
||||||
'amenity:atm' => array('label' => 'Atm', 'frequency' => 172, 'icon' => 'money_atm2'),
|
|
||||||
'amenity:emergency_phone' => array('label' => 'Emergency Phone', 'frequency' => 164),
|
|
||||||
'waterway:lock' => array('label' => 'Lock', 'frequency' => 146),
|
|
||||||
'waterway:riverbank' => array('label' => 'Riverbank', 'frequency' => 143),
|
|
||||||
'natural:coastline' => array('label' => 'Coastline', 'frequency' => 142),
|
|
||||||
'tourism:viewpoint' => array('label' => 'Viewpoint', 'frequency' => 140, 'icon' => 'tourist_view_point'),
|
|
||||||
'tourism:hostel' => array('label' => 'Hostel', 'frequency' => 140),
|
|
||||||
'tourism:bed_and_breakfast' => array('label' => 'Bed And Breakfast', 'frequency' => 140, 'icon' => 'accommodation_bed_and_breakfast'),
|
|
||||||
'railway:halt' => array('label' => 'Halt', 'frequency' => 135),
|
|
||||||
'railway:platform' => array('label' => 'Platform', 'frequency' => 134),
|
|
||||||
'railway:tram' => array('label' => 'Tram', 'frequency' => 130, 'icon' => 'transport_tram_stop'),
|
|
||||||
'amenity:courthouse' => array('label' => 'Courthouse', 'frequency' => 129, 'icon' => 'amenity_court'),
|
|
||||||
'amenity:recycling' => array('label' => 'Recycling', 'frequency' => 126, 'icon' => 'amenity_recycling'),
|
|
||||||
'amenity:dentist' => array('label' => 'Dentist', 'frequency' => 124, 'icon' => 'health_dentist'),
|
|
||||||
'natural:beach' => array('label' => 'Beach', 'frequency' => 121, 'icon' => 'tourist_beach'),
|
|
||||||
'place:moor' => array('label' => 'Moor', 'frequency' => 118),
|
|
||||||
'amenity:grave_yard' => array('label' => 'Grave Yard', 'frequency' => 110),
|
|
||||||
'waterway:drain' => array('label' => 'Drain', 'frequency' => 108),
|
|
||||||
'landuse:grass' => array('label' => 'Grass', 'frequency' => 106),
|
|
||||||
'landuse:village_green' => array('label' => 'Village Green', 'frequency' => 106),
|
|
||||||
'natural:bay' => array('label' => 'Bay', 'frequency' => 102),
|
|
||||||
'railway:tram_stop' => array('label' => 'Tram Stop', 'frequency' => 101, 'icon' => 'transport_tram_stop'),
|
|
||||||
'leisure:marina' => array('label' => 'Marina', 'frequency' => 98),
|
|
||||||
'highway:stile' => array('label' => 'Stile', 'frequency' => 97),
|
|
||||||
'natural:moor' => array('label' => 'Moor', 'frequency' => 95),
|
|
||||||
'railway:light_rail' => array('label' => 'Light Rail', 'frequency' => 91),
|
|
||||||
'railway:narrow_gauge' => array('label' => 'Narrow Gauge', 'frequency' => 90),
|
|
||||||
'natural:land' => array('label' => 'Land', 'frequency' => 86),
|
|
||||||
'amenity:village_hall' => array('label' => 'Village Hall', 'frequency' => 82),
|
|
||||||
'waterway:dock' => array('label' => 'Dock', 'frequency' => 80),
|
|
||||||
'amenity:veterinary' => array('label' => 'Veterinary', 'frequency' => 79),
|
|
||||||
'landuse:brownfield' => array('label' => 'Brownfield', 'frequency' => 77),
|
|
||||||
'leisure:track' => array('label' => 'Track', 'frequency' => 76),
|
|
||||||
'railway:historic_station' => array('label' => 'Historic Station', 'frequency' => 74),
|
|
||||||
'landuse:construction' => array('label' => 'Construction', 'frequency' => 72),
|
|
||||||
'amenity:prison' => array('label' => 'Prison', 'frequency' => 71, 'icon' => 'amenity_prison'),
|
|
||||||
'landuse:quarry' => array('label' => 'Quarry', 'frequency' => 71),
|
|
||||||
'amenity:telephone' => array('label' => 'Telephone', 'frequency' => 70),
|
|
||||||
'highway:traffic_signals' => array('label' => 'Traffic Signals', 'frequency' => 66),
|
|
||||||
'natural:heath' => array('label' => 'Heath', 'frequency' => 62),
|
|
||||||
'historic:house' => array('label' => 'House', 'frequency' => 61),
|
|
||||||
'amenity:social_club' => array('label' => 'Social Club', 'frequency' => 61),
|
|
||||||
'landuse:military' => array('label' => 'Military', 'frequency' => 61),
|
|
||||||
'amenity:health_centre' => array('label' => 'Health Centre', 'frequency' => 59),
|
|
||||||
'historic:building' => array('label' => 'Building', 'frequency' => 58),
|
|
||||||
'amenity:clinic' => array('label' => 'Clinic', 'frequency' => 57),
|
|
||||||
'highway:services' => array('label' => 'Services', 'frequency' => 56),
|
|
||||||
'amenity:ferry_terminal' => array('label' => 'Ferry Terminal', 'frequency' => 55),
|
|
||||||
'natural:marsh' => array('label' => 'Marsh', 'frequency' => 55),
|
|
||||||
'natural:hill' => array('label' => 'Hill', 'frequency' => 54),
|
|
||||||
'highway:raceway' => array('label' => 'Raceway', 'frequency' => 53),
|
|
||||||
'amenity:taxi' => array('label' => 'Taxi', 'frequency' => 47),
|
|
||||||
'amenity:take_away' => array('label' => 'Take Away', 'frequency' => 45),
|
|
||||||
'amenity:car_rental' => array('label' => 'Car Rental', 'frequency' => 44),
|
|
||||||
'place:islet' => array('label' => 'Islet', 'frequency' => 44),
|
|
||||||
'amenity:nursery' => array('label' => 'Nursery', 'frequency' => 44),
|
|
||||||
'amenity:nursing_home' => array('label' => 'Nursing Home', 'frequency' => 43),
|
|
||||||
'amenity:toilets' => array('label' => 'Toilets', 'frequency' => 38),
|
|
||||||
'amenity:hall' => array('label' => 'Hall', 'frequency' => 38),
|
|
||||||
'waterway:boatyard' => array('label' => 'Boatyard', 'frequency' => 36),
|
|
||||||
'highway:mini_roundabout' => array('label' => 'Mini Roundabout', 'frequency' => 35),
|
|
||||||
'historic:manor' => array('label' => 'Manor', 'frequency' => 35),
|
|
||||||
'tourism:chalet' => array('label' => 'Chalet', 'frequency' => 34),
|
|
||||||
'amenity:bicycle_parking' => array('label' => 'Bicycle Parking', 'frequency' => 34),
|
|
||||||
'amenity:hotel' => array('label' => 'Hotel', 'frequency' => 34),
|
|
||||||
'waterway:weir' => array('label' => 'Weir', 'frequency' => 33),
|
|
||||||
'natural:wetland' => array('label' => 'Wetland', 'frequency' => 33),
|
|
||||||
'natural:cave_entrance' => array('label' => 'Cave Entrance', 'frequency' => 32),
|
|
||||||
'amenity:crematorium' => array('label' => 'Crematorium', 'frequency' => 31),
|
|
||||||
'tourism:picnic_site' => array('label' => 'Picnic Site', 'frequency' => 31),
|
|
||||||
'landuse:wood' => array('label' => 'Wood', 'frequency' => 30),
|
|
||||||
'landuse:basin' => array('label' => 'Basin', 'frequency' => 30),
|
|
||||||
'natural:tree' => array('label' => 'Tree', 'frequency' => 30),
|
|
||||||
'leisure:slipway' => array('label' => 'Slipway', 'frequency' => 29),
|
|
||||||
'landuse:meadow' => array('label' => 'Meadow', 'frequency' => 29),
|
|
||||||
'landuse:piste' => array('label' => 'Piste', 'frequency' => 28),
|
|
||||||
'amenity:care_home' => array('label' => 'Care Home', 'frequency' => 28),
|
|
||||||
'amenity:club' => array('label' => 'Club', 'frequency' => 28),
|
|
||||||
'amenity:medical_centre' => array('label' => 'Medical Centre', 'frequency' => 27),
|
|
||||||
'historic:roman_road' => array('label' => 'Roman Road', 'frequency' => 27),
|
|
||||||
'historic:fort' => array('label' => 'Fort', 'frequency' => 26),
|
|
||||||
'railway:subway_entrance' => array('label' => 'Subway Entrance', 'frequency' => 26),
|
|
||||||
'historic:yes' => array('label' => 'Historic', 'frequency' => 25),
|
|
||||||
'highway:gate' => array('label' => 'Gate', 'frequency' => 25),
|
|
||||||
'leisure:fishing' => array('label' => 'Fishing', 'frequency' => 24),
|
|
||||||
'historic:museum' => array('label' => 'Museum', 'frequency' => 24),
|
|
||||||
'amenity:car_wash' => array('label' => 'Car Wash', 'frequency' => 24),
|
|
||||||
'railway:level_crossing' => array('label' => 'Level Crossing', 'frequency' => 23),
|
|
||||||
'leisure:bird_hide' => array('label' => 'Bird Hide', 'frequency' => 23),
|
|
||||||
'natural:headland' => array('label' => 'Headland', 'frequency' => 21),
|
|
||||||
'tourism:apartments' => array('label' => 'Apartments', 'frequency' => 21),
|
|
||||||
'amenity:shopping' => array('label' => 'Shopping', 'frequency' => 21),
|
|
||||||
'natural:scrub' => array('label' => 'Scrub', 'frequency' => 20),
|
|
||||||
'natural:fen' => array('label' => 'Fen', 'frequency' => 20),
|
|
||||||
'building:yes' => array('label' => 'Building', 'frequency' => 200),
|
|
||||||
'mountain_pass:yes' => array('label' => 'Mountain Pass', 'frequency' => 200),
|
|
||||||
|
|
||||||
'amenity:parking' => array('label' => 'Parking', 'frequency' => 3157),
|
|
||||||
'highway:bus_stop' => array('label' => 'Bus Stop', 'frequency' => 35777, 'icon' => 'transport_bus_stop2'),
|
|
||||||
'place:postcode' => array('label' => 'Postcode', 'frequency' => 27267),
|
|
||||||
'amenity:post_box' => array('label' => 'Post Box', 'frequency' => 9613),
|
|
||||||
|
|
||||||
'place:houses' => array('label' => 'Houses', 'frequency' => 85),
|
|
||||||
'railway:preserved' => array('label' => 'Preserved', 'frequency' => 227),
|
|
||||||
'waterway:derelict_canal' => array('label' => 'Derelict Canal', 'frequency' => 21),
|
|
||||||
'amenity:dead_pub' => array('label' => 'Dead Pub', 'frequency' => 20),
|
|
||||||
'railway:disused_station' => array('label' => 'Disused Station', 'frequency' => 114),
|
|
||||||
'railway:abandoned' => array('label' => 'Abandoned', 'frequency' => 641),
|
|
||||||
'railway:disused' => array('label' => 'Disused', 'frequency' => 72),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
class Debug
|
|
||||||
{
|
|
||||||
public static function newFunction($sHeading)
|
|
||||||
{
|
|
||||||
echo "<pre><h2>Debug output for $sHeading</h2></pre>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function newSection($sHeading)
|
|
||||||
{
|
|
||||||
echo "<hr><pre><h3>$sHeading</h3></pre>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function printVar($sHeading, $mVar)
|
|
||||||
{
|
|
||||||
echo '<pre><b>'.$sHeading. ':</b> ';
|
|
||||||
Debug::outputVar($mVar, str_repeat(' ', strlen($sHeading) + 3));
|
|
||||||
echo "</pre>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fmtArrayVals($aArr)
|
|
||||||
{
|
|
||||||
return array('__debug_format' => 'array_vals', 'data' => $aArr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function printDebugArray($sHeading, $oVar)
|
|
||||||
{
|
|
||||||
|
|
||||||
if ($oVar === null) {
|
|
||||||
Debug::printVar($sHeading, 'null');
|
|
||||||
} else {
|
|
||||||
Debug::printVar($sHeading, $oVar->debugInfo());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function printDebugTable($sHeading, $aVar)
|
|
||||||
{
|
|
||||||
echo '<b>'.$sHeading.":</b>\n";
|
|
||||||
echo "<table border='1'>\n";
|
|
||||||
if (!empty($aVar)) {
|
|
||||||
echo " <tr>\n";
|
|
||||||
$aKeys = array();
|
|
||||||
$aInfo = reset($aVar);
|
|
||||||
if (!is_array($aInfo)) {
|
|
||||||
$aInfo = $aInfo->debugInfo();
|
|
||||||
}
|
|
||||||
foreach ($aInfo as $sKey => $mVal) {
|
|
||||||
echo ' <th><small>'.$sKey.'</small></th>'."\n";
|
|
||||||
$aKeys[] = $sKey;
|
|
||||||
}
|
|
||||||
echo " </tr>\n";
|
|
||||||
foreach ($aVar as $oRow) {
|
|
||||||
$aInfo = $oRow;
|
|
||||||
if (!is_array($oRow)) {
|
|
||||||
$aInfo = $oRow->debugInfo();
|
|
||||||
}
|
|
||||||
echo " <tr>\n";
|
|
||||||
foreach ($aKeys as $sKey) {
|
|
||||||
echo ' <td><pre>';
|
|
||||||
if (isset($aInfo[$sKey])) {
|
|
||||||
Debug::outputVar($aInfo[$sKey], '');
|
|
||||||
}
|
|
||||||
echo '</pre></td>'."\n";
|
|
||||||
}
|
|
||||||
echo " </tr>\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo "</table>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function printGroupedSearch($aSearches, $aWordsIDs)
|
|
||||||
{
|
|
||||||
echo '<table border="1">';
|
|
||||||
echo '<tr><th>rank</th><th>Name Tokens</th><th>Name Not</th>';
|
|
||||||
echo '<th>Address Tokens</th><th>Address Not</th>';
|
|
||||||
echo '<th>country</th><th>operator</th>';
|
|
||||||
echo '<th>class</th><th>type</th><th>postcode</th><th>housenumber</th></tr>';
|
|
||||||
foreach ($aSearches as $iRank => $aRankedSet) {
|
|
||||||
foreach ($aRankedSet as $aRow) {
|
|
||||||
$aRow->dumpAsHtmlTableRow($aWordsIDs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo '</table>';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function printGroupTable($sHeading, $aVar)
|
|
||||||
{
|
|
||||||
echo '<b>'.$sHeading.":</b>\n";
|
|
||||||
echo "<table border='1'>\n";
|
|
||||||
if (!empty($aVar)) {
|
|
||||||
echo " <tr>\n";
|
|
||||||
echo ' <th><small>Group</small></th>'."\n";
|
|
||||||
$aKeys = array();
|
|
||||||
$aInfo = reset($aVar)[0];
|
|
||||||
if (!is_array($aInfo)) {
|
|
||||||
$aInfo = $aInfo->debugInfo();
|
|
||||||
}
|
|
||||||
foreach ($aInfo as $sKey => $mVal) {
|
|
||||||
echo ' <th><small>'.$sKey.'</small></th>'."\n";
|
|
||||||
$aKeys[] = $sKey;
|
|
||||||
}
|
|
||||||
echo " </tr>\n";
|
|
||||||
foreach ($aVar as $sGrpKey => $aGroup) {
|
|
||||||
foreach ($aGroup as $oRow) {
|
|
||||||
$aInfo = $oRow;
|
|
||||||
if (!is_array($oRow)) {
|
|
||||||
$aInfo = $oRow->debugInfo();
|
|
||||||
}
|
|
||||||
echo " <tr>\n";
|
|
||||||
echo ' <td><pre>'.$sGrpKey.'</pre></td>'."\n";
|
|
||||||
foreach ($aKeys as $sKey) {
|
|
||||||
echo ' <td><pre>';
|
|
||||||
if (!empty($aInfo[$sKey])) {
|
|
||||||
Debug::outputVar($aInfo[$sKey], '');
|
|
||||||
}
|
|
||||||
echo '</pre></td>'."\n";
|
|
||||||
}
|
|
||||||
echo " </tr>\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo "</table>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function printSQL($sSQL)
|
|
||||||
{
|
|
||||||
echo '<p><tt><font color="#aaa">'.$sSQL.'</font></tt></p>'."\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function outputVar($mVar, $sPreNL)
|
|
||||||
{
|
|
||||||
if (is_array($mVar) && !isset($mVar['__debug_format'])) {
|
|
||||||
$sPre = '';
|
|
||||||
foreach ($mVar as $mKey => $aValue) {
|
|
||||||
echo $sPre;
|
|
||||||
$iKeyLen = Debug::outputSimpleVar($mKey);
|
|
||||||
echo ' => ';
|
|
||||||
Debug::outputVar(
|
|
||||||
$aValue,
|
|
||||||
$sPreNL.str_repeat(' ', $iKeyLen + 4)
|
|
||||||
);
|
|
||||||
$sPre = "\n".$sPreNL;
|
|
||||||
}
|
|
||||||
} elseif (is_array($mVar) && isset($mVar['__debug_format'])) {
|
|
||||||
if (!empty($mVar['data'])) {
|
|
||||||
$sPre = '';
|
|
||||||
foreach ($mVar['data'] as $mValue) {
|
|
||||||
echo $sPre;
|
|
||||||
Debug::outputSimpleVar($mValue);
|
|
||||||
$sPre = ', ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif (is_object($mVar) && method_exists($mVar, 'debugInfo')) {
|
|
||||||
Debug::outputVar($mVar->debugInfo(), $sPreNL);
|
|
||||||
} elseif (is_a($mVar, 'stdClass')) {
|
|
||||||
Debug::outputVar(json_decode(json_encode($mVar), true), $sPreNL);
|
|
||||||
} else {
|
|
||||||
Debug::outputSimpleVar($mVar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function outputSimpleVar($mVar)
|
|
||||||
{
|
|
||||||
if (is_bool($mVar)) {
|
|
||||||
echo '<i>'.($mVar ? 'True' : 'False').'</i>';
|
|
||||||
return $mVar ? 4 : 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_string($mVar)) {
|
|
||||||
echo "'$mVar'";
|
|
||||||
return strlen($mVar) + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
echo (string)$mVar;
|
|
||||||
return strlen((string)$mVar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
class Debug
|
|
||||||
{
|
|
||||||
public static function __callStatic($name, $arguments)
|
|
||||||
{
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2032
lib/Geocode.php
2032
lib/Geocode.php
File diff suppressed because it is too large
Load Diff
@@ -1,121 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
class ParameterParser
|
|
||||||
{
|
|
||||||
private $aParams;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct($aParams = null)
|
|
||||||
{
|
|
||||||
$this->aParams = ($aParams === null) ? $_GET : $aParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBool($sName, $bDefault = false)
|
|
||||||
{
|
|
||||||
if (!isset($this->aParams[$sName]) || strlen($this->aParams[$sName]) == 0) {
|
|
||||||
return $bDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (bool) $this->aParams[$sName];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getInt($sName, $bDefault = false)
|
|
||||||
{
|
|
||||||
if (!isset($this->aParams[$sName])) {
|
|
||||||
return $bDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!preg_match('/^[+-]?[0-9]+$/', $this->aParams[$sName])) {
|
|
||||||
userError("Integer number expected for parameter '$sName'");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) $this->aParams[$sName];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFloat($sName, $bDefault = false)
|
|
||||||
{
|
|
||||||
if (!isset($this->aParams[$sName])) {
|
|
||||||
return $bDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!preg_match('/^[+-]?[0-9]*\.?[0-9]+$/', $this->aParams[$sName])) {
|
|
||||||
userError("Floating-point number expected for parameter '$sName'");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (float) $this->aParams[$sName];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getString($sName, $bDefault = false)
|
|
||||||
{
|
|
||||||
if (!isset($this->aParams[$sName]) || strlen($this->aParams[$sName]) == 0) {
|
|
||||||
return $bDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->aParams[$sName];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSet($sName, $aValues, $sDefault = false)
|
|
||||||
{
|
|
||||||
if (!isset($this->aParams[$sName]) || strlen($this->aParams[$sName]) == 0) {
|
|
||||||
return $sDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!in_array($this->aParams[$sName], $aValues)) {
|
|
||||||
userError("Parameter '$sName' must be one of: ".join(', ', $aValues));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->aParams[$sName];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStringList($sName, $aDefault = false)
|
|
||||||
{
|
|
||||||
$sValue = $this->getString($sName);
|
|
||||||
|
|
||||||
if ($sValue) {
|
|
||||||
// removes all NULL, FALSE and Empty Strings but leaves 0 (zero) values
|
|
||||||
return array_values(array_filter(explode(',', $sValue), 'strlen'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPreferredLanguages($sFallback = null)
|
|
||||||
{
|
|
||||||
if ($sFallback === null && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
|
||||||
$sFallback = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$aLanguages = array();
|
|
||||||
$sLangString = $this->getString('accept-language', $sFallback);
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
foreach ($aLanguagesParse as $iLang => $aLanguage) {
|
|
||||||
$aLanguages[$aLanguage[1]] = isset($aLanguage[5])?(float)$aLanguage[5]:1 - ($iLang/100);
|
|
||||||
if (!isset($aLanguages[$aLanguage[2]])) $aLanguages[$aLanguage[2]] = $aLanguages[$aLanguage[1]]/10;
|
|
||||||
}
|
|
||||||
arsort($aLanguages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (empty($aLanguages) && CONST_Default_Language) {
|
|
||||||
$aLanguages[CONST_Default_Language] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($aLanguages as $sLanguage => $fLanguagePref) {
|
|
||||||
$aLangPrefOrder['short_name:'.$sLanguage] = 'short_name:'.$sLanguage;
|
|
||||||
$aLangPrefOrder['name:'.$sLanguage] = 'name:'.$sLanguage;
|
|
||||||
}
|
|
||||||
$aLangPrefOrder['short_name'] = 'short_name';
|
|
||||||
$aLangPrefOrder['name'] = 'name';
|
|
||||||
$aLangPrefOrder['brand'] = 'brand';
|
|
||||||
foreach ($aLanguages as $sLanguage => $fLanguagePref) {
|
|
||||||
$aLangPrefOrder['official_name:'.$sLanguage] = 'official_name:'.$sLanguage;
|
|
||||||
}
|
|
||||||
$aLangPrefOrder['official_name'] = 'official_name';
|
|
||||||
$aLangPrefOrder['ref'] = 'ref';
|
|
||||||
$aLangPrefOrder['type'] = 'type';
|
|
||||||
return $aLangPrefOrder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
126
lib/Phrase.php
126
lib/Phrase.php
@@ -1,126 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Segment of a query string.
|
|
||||||
*
|
|
||||||
* The parts of a query strings are usually separated by commas.
|
|
||||||
*/
|
|
||||||
class Phrase
|
|
||||||
{
|
|
||||||
const MAX_DEPTH = 7;
|
|
||||||
|
|
||||||
// Complete phrase as a string.
|
|
||||||
private $sPhrase;
|
|
||||||
// Element type for structured searches.
|
|
||||||
private $sPhraseType;
|
|
||||||
// Space-separated words of the phrase.
|
|
||||||
private $aWords;
|
|
||||||
// Possible segmentations of the phrase.
|
|
||||||
private $aWordSets;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct($sPhrase, $sPhraseType)
|
|
||||||
{
|
|
||||||
$this->sPhrase = trim($sPhrase);
|
|
||||||
$this->sPhraseType = $sPhraseType;
|
|
||||||
$this->aWords = explode(' ', $this->sPhrase);
|
|
||||||
$this->aWordSets = $this->createWordSets($this->aWords, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the element type of the phrase.
|
|
||||||
*
|
|
||||||
* @return string Pharse type if the phrase comes from a structured query
|
|
||||||
* or empty string otherwise.
|
|
||||||
*/
|
|
||||||
public function getPhraseType()
|
|
||||||
{
|
|
||||||
return $this->sPhraseType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the array of possible segmentations of the phrase.
|
|
||||||
*
|
|
||||||
* @return string[][] Array of segmentations, each consisting of an
|
|
||||||
* array of terms.
|
|
||||||
*/
|
|
||||||
public function getWordSets()
|
|
||||||
{
|
|
||||||
return $this->aWordSets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the tokens from this phrase to the given list of tokens.
|
|
||||||
*
|
|
||||||
* @param string[] $aTokens List of tokens to append.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addTokens(&$aTokens)
|
|
||||||
{
|
|
||||||
foreach ($this->aWordSets as $aSet) {
|
|
||||||
foreach ($aSet as $sWord) {
|
|
||||||
$aTokens[' '.$sWord] = ' '.$sWord;
|
|
||||||
$aTokens[$sWord] = $sWord;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invert the set of possible segmentations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function invertWordSets()
|
|
||||||
{
|
|
||||||
$this->aWordSets = $this->createInverseWordSets($this->aWords, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createWordSets($aWords, $iDepth)
|
|
||||||
{
|
|
||||||
$aResult = array(array(join(' ', $aWords)));
|
|
||||||
$sFirstToken = '';
|
|
||||||
if ($iDepth < Phrase::MAX_DEPTH) {
|
|
||||||
while (count($aWords) > 1) {
|
|
||||||
$sWord = array_shift($aWords);
|
|
||||||
$sFirstToken .= ($sFirstToken?' ':'').$sWord;
|
|
||||||
$aRest = $this->createWordSets($aWords, $iDepth + 1);
|
|
||||||
foreach ($aRest as $aSet) {
|
|
||||||
$aResult[] = array_merge(array($sFirstToken), $aSet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'Type' => $this->sPhraseType,
|
|
||||||
'Phrase' => $this->sPhrase,
|
|
||||||
'Words' => $this->aWords,
|
|
||||||
'WordSets' => $this->aWordSets
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,573 +1,138 @@
|
|||||||
<?php
|
<?php
|
||||||
|
class PlaceLookup
|
||||||
namespace Nominatim;
|
{
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/AddressDetails.php');
|
|
||||||
require_once(CONST_BasePath.'/lib/Result.php');
|
|
||||||
|
|
||||||
class PlaceLookup
|
|
||||||
{
|
|
||||||
protected $oDB;
|
protected $oDB;
|
||||||
|
|
||||||
protected $aLangPrefOrderSql = "''";
|
protected $iPlaceID;
|
||||||
|
|
||||||
|
protected $aLangPrefOrder = array();
|
||||||
|
|
||||||
protected $bAddressDetails = false;
|
protected $bAddressDetails = false;
|
||||||
protected $bExtraTags = false;
|
|
||||||
protected $bNameDetails = false;
|
|
||||||
|
|
||||||
protected $bIncludePolygonAsPoints = false;
|
function PlaceLookup(&$oDB)
|
||||||
protected $bIncludePolygonAsText = false;
|
|
||||||
protected $bIncludePolygonAsGeoJSON = false;
|
|
||||||
protected $bIncludePolygonAsKML = false;
|
|
||||||
protected $bIncludePolygonAsSVG = false;
|
|
||||||
protected $fPolygonSimplificationThreshold = 0.0;
|
|
||||||
|
|
||||||
protected $sAnchorSql = null;
|
|
||||||
protected $sAddressRankListSql = null;
|
|
||||||
protected $sAllowedTypesSQLList = null;
|
|
||||||
protected $bDeDupe = true;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct(&$oDB)
|
|
||||||
{
|
{
|
||||||
$this->oDB =& $oDB;
|
$this->oDB =& $oDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doDeDupe()
|
function setLanguagePreference($aLangPrefOrder)
|
||||||
{
|
{
|
||||||
return $this->bDeDupe;
|
$this->aLangPrefOrder = $aLangPrefOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setIncludePolygonAsPoints($b = true)
|
function setIncludeAddressDetails($bAddressDetails = true)
|
||||||
{
|
{
|
||||||
$this->bIncludePolygonAsPoints = $b;
|
$this->bAddressDetails = $bAddressDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setIncludeAddressDetails($b)
|
function setPlaceID($iPlaceID)
|
||||||
{
|
{
|
||||||
$this->bAddressDetails = $b;
|
$this->iPlaceID = $iPlaceID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadParamArray($oParams, $sGeomType = null)
|
function setOSMID($sType, $iID)
|
||||||
{
|
{
|
||||||
$aLangs = $oParams->getPreferredLanguages();
|
$sSQL = "select place_id from placex where osm_type = '".pg_escape_string($sType)."' and osm_id = ".(int)$iID." order by type = 'postcode' asc";
|
||||||
$this->aLangPrefOrderSql =
|
$this->iPlaceID = $this->oDB->getOne($sSQL);
|
||||||
'ARRAY['.join(',', array_map('getDBQuoted', $aLangs)).']';
|
|
||||||
|
|
||||||
$this->bExtraTags = $oParams->getBool('extratags', false);
|
|
||||||
$this->bNameDetails = $oParams->getBool('namedetails', false);
|
|
||||||
|
|
||||||
$this->bDeDupe = $oParams->getBool('dedupe', $this->bDeDupe);
|
|
||||||
|
|
||||||
if ($sGeomType === null || $sGeomType == 'geojson') {
|
|
||||||
$this->bIncludePolygonAsGeoJSON = $oParams->getBool('polygon_geojson');
|
|
||||||
$this->bIncludePolygonAsPoints = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($oParams->getString('format', '') !== 'geojson') {
|
function lookup()
|
||||||
if ($sGeomType === null || $sGeomType == 'text') {
|
|
||||||
$this->bIncludePolygonAsText = $oParams->getBool('polygon_text');
|
|
||||||
}
|
|
||||||
if ($sGeomType === null || $sGeomType == 'kml') {
|
|
||||||
$this->bIncludePolygonAsKML = $oParams->getBool('polygon_kml');
|
|
||||||
}
|
|
||||||
if ($sGeomType === null || $sGeomType == 'svg') {
|
|
||||||
$this->bIncludePolygonAsSVG = $oParams->getBool('polygon_svg');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->fPolygonSimplificationThreshold
|
|
||||||
= $oParams->getFloat('polygon_threshold', 0.0);
|
|
||||||
|
|
||||||
$iWantedTypes =
|
|
||||||
($this->bIncludePolygonAsText ? 1 : 0) +
|
|
||||||
($this->bIncludePolygonAsGeoJSON ? 1 : 0) +
|
|
||||||
($this->bIncludePolygonAsKML ? 1 : 0) +
|
|
||||||
($this->bIncludePolygonAsSVG ? 1 : 0);
|
|
||||||
if ($iWantedTypes > CONST_PolygonOutput_MaximumTypes) {
|
|
||||||
if (CONST_PolygonOutput_MaximumTypes) {
|
|
||||||
userError('Select only '.CONST_PolygonOutput_MaximumTypes.' polgyon output option');
|
|
||||||
} else {
|
|
||||||
userError('Polygon output is disabled');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMoreUrlParams()
|
|
||||||
{
|
{
|
||||||
$aParams = array();
|
if (!$this->iPlaceID) return null;
|
||||||
|
|
||||||
if ($this->bAddressDetails) $aParams['addressdetails'] = '1';
|
$sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted", $this->aLangPrefOrder))."]";
|
||||||
if ($this->bExtraTags) $aParams['extratags'] = '1';
|
|
||||||
if ($this->bNameDetails) $aParams['namedetails'] = '1';
|
|
||||||
|
|
||||||
if ($this->bIncludePolygonAsPoints) $aParams['polygon'] = '1';
|
$sSQL = "select placex.place_id, partition, osm_type, osm_id, class, type, admin_level, housenumber, street, isin, postcode, country_code, extratags, parent_place_id, linked_place_id, rank_address, rank_search, ";
|
||||||
if ($this->bIncludePolygonAsText) $aParams['polygon_text'] = '1';
|
$sSQL .= " coalesce(importance,0.75-(rank_search::float/40)) as importance, indexed_status, indexed_date, wikipedia, calculated_country_code, ";
|
||||||
if ($this->bIncludePolygonAsGeoJSON) $aParams['polygon_geojson'] = '1';
|
$sSQL .= " get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,";
|
||||||
if ($this->bIncludePolygonAsKML) $aParams['polygon_kml'] = '1';
|
$sSQL .= " get_name_by_language(name, $sLanguagePrefArraySQL) as placename,";
|
||||||
if ($this->bIncludePolygonAsSVG) $aParams['polygon_svg'] = '1';
|
$sSQL .= " get_name_by_language(name, ARRAY['ref']) as ref,";
|
||||||
|
$sSQL .= " st_y(centroid) as lat, st_x(centroid) as lon";
|
||||||
|
$sSQL .= " from placex where place_id = ".(int)$this->iPlaceID;
|
||||||
|
$aPlace = $this->oDB->getRow($sSQL);
|
||||||
|
|
||||||
if ($this->fPolygonSimplificationThreshold > 0.0) {
|
if (!$aPlace['place_id']) return null;
|
||||||
$aParams['polygon_threshold'] = $this->fPolygonSimplificationThreshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->bDeDupe) $aParams['dedupe'] = '0';
|
|
||||||
|
|
||||||
return $aParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setAnchorSql($sPoint)
|
|
||||||
{
|
|
||||||
$this->sAnchorSql = $sPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setAddressRankList($aList)
|
|
||||||
{
|
|
||||||
$this->sAddressRankListSql = '('.join(',', $aList).')';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setAllowedTypesSQLList($sSql)
|
|
||||||
{
|
|
||||||
$this->sAllowedTypesSQLList = $sSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setLanguagePreference($aLangPrefOrder)
|
|
||||||
{
|
|
||||||
$this->aLangPrefOrderSql =
|
|
||||||
'ARRAY['.join(',', array_map('getDBQuoted', $aLangPrefOrder)).']';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addressImportanceSql($sGeometry, $sPlaceId)
|
|
||||||
{
|
|
||||||
if ($this->sAnchorSql) {
|
|
||||||
$sSQL = 'ST_Distance('.$this->sAnchorSql.','.$sGeometry.')';
|
|
||||||
} else {
|
|
||||||
$sSQL = '(SELECT max(ai_p.importance * (ai_p.rank_address + 2))';
|
|
||||||
$sSQL .= ' FROM place_addressline ai_s, placex ai_p';
|
|
||||||
$sSQL .= ' WHERE ai_s.place_id = '.$sPlaceId;
|
|
||||||
$sSQL .= ' AND ai_p.place_id = ai_s.address_place_id ';
|
|
||||||
$sSQL .= ' AND ai_s.isaddress ';
|
|
||||||
$sSQL .= ' AND ai_p.importance is not null)';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sSQL.' AS addressimportance,';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function langAddressSql($sHousenumber)
|
|
||||||
{
|
|
||||||
if ($this->bAddressDetails)
|
if ($this->bAddressDetails)
|
||||||
return ''; // langaddress will be computed from address details
|
|
||||||
|
|
||||||
return 'get_address_by_language(place_id,'.$sHousenumber.','.$this->aLangPrefOrderSql.') AS langaddress,';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function lookupOSMID($sType, $iID)
|
|
||||||
{
|
{
|
||||||
$sSQL = "select place_id from placex where osm_type = '".$sType."' and osm_id = ".$iID;
|
$aAddress = $this->getAddressNames();
|
||||||
$iPlaceID = chksql($this->oDB->getOne($sSQL));
|
$aPlace['aAddress'] = $aAddress;
|
||||||
|
|
||||||
if (!$iPlaceID) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$aResults = $this->lookup(array($iPlaceID => new Result($iPlaceID)));
|
$aClassType = getClassTypes();
|
||||||
|
$sAddressType = '';
|
||||||
return empty($aResults) ? null : reset($aResults);
|
$sClassType = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
|
||||||
}
|
if (isset($aClassType[$sClassType]) && isset($aClassType[$sClassType]['simplelabel']))
|
||||||
|
|
||||||
public function lookup($aResults, $iMinRank = 0, $iMaxRank = 30)
|
|
||||||
{
|
{
|
||||||
Debug::newFunction('Place lookup');
|
$sAddressType = $aClassType[$aClassType]['simplelabel'];
|
||||||
|
|
||||||
if (empty($aResults)) {
|
|
||||||
return array();
|
|
||||||
}
|
}
|
||||||
$aSubSelects = array();
|
else
|
||||||
|
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX);
|
|
||||||
if ($sPlaceIDs) {
|
|
||||||
Debug::printVar('Ids from placex', $sPlaceIDs);
|
|
||||||
$sSQL = 'SELECT ';
|
|
||||||
$sSQL .= ' osm_type,';
|
|
||||||
$sSQL .= ' osm_id,';
|
|
||||||
$sSQL .= ' class,';
|
|
||||||
$sSQL .= ' type,';
|
|
||||||
$sSQL .= ' admin_level,';
|
|
||||||
$sSQL .= ' rank_search,';
|
|
||||||
$sSQL .= ' rank_address,';
|
|
||||||
$sSQL .= ' min(place_id) AS place_id,';
|
|
||||||
$sSQL .= ' min(parent_place_id) AS parent_place_id,';
|
|
||||||
$sSQL .= ' -1 as housenumber,';
|
|
||||||
$sSQL .= ' country_code,';
|
|
||||||
$sSQL .= $this->langAddressSql('-1');
|
|
||||||
$sSQL .= ' get_name_by_language(name,'.$this->aLangPrefOrderSql.') AS placename,';
|
|
||||||
$sSQL .= " get_name_by_language(name, ARRAY['ref']) AS ref,";
|
|
||||||
if ($this->bExtraTags) {
|
|
||||||
$sSQL .= 'hstore_to_json(extratags)::text AS extra,';
|
|
||||||
}
|
|
||||||
if ($this->bNameDetails) {
|
|
||||||
$sSQL .= 'hstore_to_json(name)::text AS names,';
|
|
||||||
}
|
|
||||||
$sSQL .= ' avg(ST_X(centroid)) AS lon, ';
|
|
||||||
$sSQL .= ' avg(ST_Y(centroid)) AS lat, ';
|
|
||||||
$sSQL .= ' COALESCE(importance,0.75-(rank_search::float/40)) AS importance, ';
|
|
||||||
$sSQL .= $this->addressImportanceSql(
|
|
||||||
'ST_Collect(centroid)',
|
|
||||||
'min(CASE WHEN placex.rank_search < 28 THEN placex.place_id ELSE placex.parent_place_id END)'
|
|
||||||
);
|
|
||||||
$sSQL .= " (extratags->'place') AS extra_place ";
|
|
||||||
$sSQL .= ' FROM placex';
|
|
||||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
|
||||||
$sSQL .= ' AND (';
|
|
||||||
$sSQL .= " placex.rank_address between $iMinRank and $iMaxRank ";
|
|
||||||
if (14 >= $iMinRank && 14 <= $iMaxRank) {
|
|
||||||
$sSQL .= " OR (extratags->'place') = 'city'";
|
|
||||||
}
|
|
||||||
if ($this->sAddressRankListSql) {
|
|
||||||
$sSQL .= ' OR placex.rank_address in '.$this->sAddressRankListSql;
|
|
||||||
}
|
|
||||||
$sSQL .= ' ) ';
|
|
||||||
if ($this->sAllowedTypesSQLList) {
|
|
||||||
$sSQL .= 'AND placex.class in '.$this->sAllowedTypesSQLList;
|
|
||||||
}
|
|
||||||
$sSQL .= ' AND linked_place_id is null ';
|
|
||||||
$sSQL .= ' GROUP BY ';
|
|
||||||
$sSQL .= ' osm_type, ';
|
|
||||||
$sSQL .= ' osm_id, ';
|
|
||||||
$sSQL .= ' class, ';
|
|
||||||
$sSQL .= ' type, ';
|
|
||||||
$sSQL .= ' admin_level, ';
|
|
||||||
$sSQL .= ' rank_search, ';
|
|
||||||
$sSQL .= ' rank_address, ';
|
|
||||||
$sSQL .= ' housenumber,';
|
|
||||||
$sSQL .= ' country_code, ';
|
|
||||||
$sSQL .= ' importance, ';
|
|
||||||
if (!$this->bDeDupe) $sSQL .= 'place_id,';
|
|
||||||
if (!$this->bAddressDetails) $sSQL .= 'langaddress, ';
|
|
||||||
$sSQL .= ' placename, ';
|
|
||||||
$sSQL .= ' ref, ';
|
|
||||||
if ($this->bExtraTags) $sSQL .= 'extratags, ';
|
|
||||||
if ($this->bNameDetails) $sSQL .= 'name, ';
|
|
||||||
$sSQL .= " extratags->'place' ";
|
|
||||||
|
|
||||||
$aSubSelects[] = $sSQL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// postcode table
|
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_POSTCODE);
|
|
||||||
if ($sPlaceIDs) {
|
|
||||||
Debug::printVar('Ids from location_postcode', $sPlaceIDs);
|
|
||||||
$sSQL = 'SELECT';
|
|
||||||
$sSQL .= " 'P' as osm_type,";
|
|
||||||
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id = lp.parent_place_id) as osm_id,';
|
|
||||||
$sSQL .= " 'place' as class, 'postcode' as type,";
|
|
||||||
$sSQL .= ' null::smallint as admin_level, rank_search, rank_address,';
|
|
||||||
$sSQL .= ' place_id, parent_place_id,';
|
|
||||||
$sSQL .= ' -1 as housenumber,';
|
|
||||||
$sSQL .= ' country_code,';
|
|
||||||
$sSQL .= $this->langAddressSql('-1');
|
|
||||||
$sSQL .= ' postcode as placename,';
|
|
||||||
$sSQL .= ' postcode as ref,';
|
|
||||||
if ($this->bExtraTags) $sSQL .= 'null::text AS extra,';
|
|
||||||
if ($this->bNameDetails) $sSQL .= 'null::text AS names,';
|
|
||||||
$sSQL .= ' ST_x(geometry) AS lon, ST_y(geometry) AS lat,';
|
|
||||||
$sSQL .= ' (0.75-(rank_search::float/40)) AS importance, ';
|
|
||||||
$sSQL .= $this->addressImportanceSql('geometry', 'lp.parent_place_id');
|
|
||||||
$sSQL .= ' null::text AS extra_place ';
|
|
||||||
$sSQL .= 'FROM location_postcode lp';
|
|
||||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
|
||||||
$sSQL .= " AND lp.rank_address between $iMinRank and $iMaxRank";
|
|
||||||
|
|
||||||
$aSubSelects[] = $sSQL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// All other tables are rank 30 only.
|
|
||||||
if ($iMaxRank == 30) {
|
|
||||||
// TIGER table
|
|
||||||
if (CONST_Use_US_Tiger_Data) {
|
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_TIGER);
|
|
||||||
if ($sPlaceIDs) {
|
|
||||||
Debug::printVar('Ids from Tiger table', $sPlaceIDs);
|
|
||||||
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_TIGER);
|
|
||||||
// Tiger search only if a housenumber was searched and if it was found
|
|
||||||
// (realized through a join)
|
|
||||||
$sSQL = ' SELECT ';
|
|
||||||
$sSQL .= " 'T' AS osm_type, ";
|
|
||||||
$sSQL .= ' (SELECT osm_id from placex p WHERE p.place_id=blub.parent_place_id) as osm_id, ';
|
|
||||||
$sSQL .= " 'place' AS class, ";
|
|
||||||
$sSQL .= " 'house' AS type, ";
|
|
||||||
$sSQL .= ' null::smallint AS admin_level, ';
|
|
||||||
$sSQL .= ' 30 AS rank_search, ';
|
|
||||||
$sSQL .= ' 30 AS rank_address, ';
|
|
||||||
$sSQL .= ' place_id, ';
|
|
||||||
$sSQL .= ' parent_place_id, ';
|
|
||||||
$sSQL .= ' housenumber_for_place as housenumber,';
|
|
||||||
$sSQL .= " 'us' AS country_code, ";
|
|
||||||
$sSQL .= $this->langAddressSql('housenumber_for_place');
|
|
||||||
$sSQL .= ' null::text AS placename, ';
|
|
||||||
$sSQL .= ' null::text AS ref, ';
|
|
||||||
if ($this->bExtraTags) $sSQL .= 'null::text AS extra,';
|
|
||||||
if ($this->bNameDetails) $sSQL .= 'null::text AS names,';
|
|
||||||
$sSQL .= ' st_x(centroid) AS lon, ';
|
|
||||||
$sSQL .= ' st_y(centroid) AS lat,';
|
|
||||||
$sSQL .= ' -1.15 AS importance, ';
|
|
||||||
$sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id');
|
|
||||||
$sSQL .= ' null::text AS extra_place ';
|
|
||||||
$sSQL .= ' FROM (';
|
|
||||||
$sSQL .= ' SELECT place_id, '; // interpolate the Tiger housenumbers here
|
|
||||||
$sSQL .= ' ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) AS centroid, ';
|
|
||||||
$sSQL .= ' parent_place_id, ';
|
|
||||||
$sSQL .= ' housenumber_for_place';
|
|
||||||
$sSQL .= ' FROM (';
|
|
||||||
$sSQL .= ' location_property_tiger ';
|
|
||||||
$sSQL .= ' JOIN (values '.$sHousenumbers.') AS housenumbers(place_id, housenumber_for_place) USING(place_id)) ';
|
|
||||||
$sSQL .= ' WHERE ';
|
|
||||||
$sSQL .= ' housenumber_for_place >= startnumber';
|
|
||||||
$sSQL .= ' AND housenumber_for_place <= endnumber';
|
|
||||||
$sSQL .= ' ) AS blub'; //postgres wants an alias here
|
|
||||||
|
|
||||||
$aSubSelects[] = $sSQL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// osmline - interpolated housenumbers
|
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_OSMLINE);
|
|
||||||
if ($sPlaceIDs) {
|
|
||||||
Debug::printVar('Ids from interpolation', $sPlaceIDs);
|
|
||||||
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_OSMLINE);
|
|
||||||
// interpolation line search only if a housenumber was searched
|
|
||||||
// (realized through a join)
|
|
||||||
$sSQL = 'SELECT ';
|
|
||||||
$sSQL .= " 'W' AS osm_type, ";
|
|
||||||
$sSQL .= ' osm_id, ';
|
|
||||||
$sSQL .= " 'place' AS class, ";
|
|
||||||
$sSQL .= " 'house' AS type, ";
|
|
||||||
$sSQL .= ' null::smallint AS admin_level, ';
|
|
||||||
$sSQL .= ' 30 AS rank_search, ';
|
|
||||||
$sSQL .= ' 30 AS rank_address, ';
|
|
||||||
$sSQL .= ' place_id, ';
|
|
||||||
$sSQL .= ' parent_place_id, ';
|
|
||||||
$sSQL .= ' housenumber_for_place as housenumber,';
|
|
||||||
$sSQL .= ' country_code, ';
|
|
||||||
$sSQL .= $this->langAddressSql('housenumber_for_place');
|
|
||||||
$sSQL .= ' null::text AS placename, ';
|
|
||||||
$sSQL .= ' null::text AS ref, ';
|
|
||||||
if ($this->bExtraTags) $sSQL .= 'null::text AS extra, ';
|
|
||||||
if ($this->bNameDetails) $sSQL .= 'null::text AS names, ';
|
|
||||||
$sSQL .= ' st_x(centroid) AS lon, ';
|
|
||||||
$sSQL .= ' st_y(centroid) AS lat, ';
|
|
||||||
// slightly smaller than the importance for normal houses
|
|
||||||
$sSQL .= ' -0.1 AS importance, ';
|
|
||||||
$sSQL .= $this->addressImportanceSql('centroid', 'blub.parent_place_id');
|
|
||||||
$sSQL .= ' null::text AS extra_place ';
|
|
||||||
$sSQL .= ' FROM (';
|
|
||||||
$sSQL .= ' SELECT ';
|
|
||||||
$sSQL .= ' osm_id, ';
|
|
||||||
$sSQL .= ' place_id, ';
|
|
||||||
$sSQL .= ' country_code, ';
|
|
||||||
$sSQL .= ' CASE '; // interpolate the housenumbers here
|
|
||||||
$sSQL .= ' WHEN startnumber != endnumber ';
|
|
||||||
$sSQL .= ' THEN ST_LineInterpolatePoint(linegeo, (housenumber_for_place-startnumber::float)/(endnumber-startnumber)::float) ';
|
|
||||||
$sSQL .= ' ELSE ST_LineInterpolatePoint(linegeo, 0.5) ';
|
|
||||||
$sSQL .= ' END as centroid, ';
|
|
||||||
$sSQL .= ' parent_place_id, ';
|
|
||||||
$sSQL .= ' housenumber_for_place ';
|
|
||||||
$sSQL .= ' FROM (';
|
|
||||||
$sSQL .= ' location_property_osmline ';
|
|
||||||
$sSQL .= ' JOIN (values '.$sHousenumbers.') AS housenumbers(place_id, housenumber_for_place) USING(place_id)';
|
|
||||||
$sSQL .= ' ) ';
|
|
||||||
$sSQL .= ' WHERE housenumber_for_place >= 0 ';
|
|
||||||
$sSQL .= ' ) as blub'; //postgres wants an alias here
|
|
||||||
|
|
||||||
$aSubSelects[] = $sSQL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CONST_Use_Aux_Location_data) {
|
|
||||||
$sPlaceIDs = Result::joinIdsByTable($aResults, Result::TABLE_AUX);
|
|
||||||
if ($sPlaceIDs) {
|
|
||||||
$sHousenumbers = Result::sqlHouseNumberTable($aResults, Result::TABLE_AUX);
|
|
||||||
$sSQL = ' SELECT ';
|
|
||||||
$sSQL .= " 'L' AS osm_type, ";
|
|
||||||
$sSQL .= ' place_id AS osm_id, ';
|
|
||||||
$sSQL .= " 'place' AS class,";
|
|
||||||
$sSQL .= " 'house' AS type, ";
|
|
||||||
$sSQL .= ' null::smallint AS admin_level, ';
|
|
||||||
$sSQL .= ' 30 AS rank_search,';
|
|
||||||
$sSQL .= ' 30 AS rank_address, ';
|
|
||||||
$sSQL .= ' place_id,';
|
|
||||||
$sSQL .= ' parent_place_id, ';
|
|
||||||
$sSQL .= ' housenumber,';
|
|
||||||
$sSQL .= " 'us' AS country_code, ";
|
|
||||||
$sSQL .= $this->langAddressSql('-1');
|
|
||||||
$sSQL .= ' null::text AS placename, ';
|
|
||||||
$sSQL .= ' null::text AS ref, ';
|
|
||||||
if ($this->bExtraTags) $sSQL .= 'null::text AS extra, ';
|
|
||||||
if ($this->bNameDetails) $sSQL .= 'null::text AS names, ';
|
|
||||||
$sSQL .= ' ST_X(centroid) AS lon, ';
|
|
||||||
$sSQL .= ' ST_Y(centroid) AS lat, ';
|
|
||||||
$sSQL .= ' -1.10 AS importance, ';
|
|
||||||
$sSQL .= $this->addressImportanceSql(
|
|
||||||
'centroid',
|
|
||||||
'location_property_aux.parent_place_id'
|
|
||||||
);
|
|
||||||
$sSQL .= ' null::text AS extra_place ';
|
|
||||||
$sSQL .= ' FROM location_property_aux ';
|
|
||||||
$sSQL .= " WHERE place_id in ($sPlaceIDs) ";
|
|
||||||
|
|
||||||
$aSubSelects[] = $sSQL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($aSubSelects)) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$sSQL = join(' UNION ', $aSubSelects);
|
|
||||||
Debug::printSQL($sSQL);
|
|
||||||
$aPlaces = chksql($this->oDB->getAll($sSQL), 'Could not lookup place');
|
|
||||||
|
|
||||||
foreach ($aPlaces as &$aPlace) {
|
|
||||||
if ($this->bAddressDetails) {
|
|
||||||
// to get addressdetails for tiger data, the housenumber is needed
|
|
||||||
$aPlace['address'] = new AddressDetails(
|
|
||||||
$this->oDB,
|
|
||||||
$aPlace['place_id'],
|
|
||||||
$aPlace['housenumber'],
|
|
||||||
$this->aLangPrefOrderSql
|
|
||||||
);
|
|
||||||
$aPlace['langaddress'] = $aPlace['address']->getLocaleAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->bExtraTags) {
|
|
||||||
if ($aPlace['extra']) {
|
|
||||||
$aPlace['sExtraTags'] = json_decode($aPlace['extra']);
|
|
||||||
} else {
|
|
||||||
$aPlace['sExtraTags'] = (object) array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->bNameDetails) {
|
|
||||||
if ($aPlace['names']) {
|
|
||||||
$aPlace['sNameDetails'] = json_decode($aPlace['names']);
|
|
||||||
} else {
|
|
||||||
$aPlace['sNameDetails'] = (object) array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$aPlace['addresstype'] = ClassTypes\getProperty(
|
|
||||||
$aPlace,
|
|
||||||
'simplelabel',
|
|
||||||
$aPlace['class']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug::printVar('Places', $aPlaces);
|
|
||||||
|
|
||||||
return $aPlaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* returns an array which will contain the keys
|
|
||||||
* aBoundingBox
|
|
||||||
* and may also contain one or more of the keys
|
|
||||||
* asgeojson
|
|
||||||
* askml
|
|
||||||
* assvg
|
|
||||||
* astext
|
|
||||||
* lat
|
|
||||||
* lon
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public function getOutlines($iPlaceID, $fLon = null, $fLat = null, $fRadius = null, $fLonReverse = null, $fLatReverse = null)
|
|
||||||
{
|
{
|
||||||
|
$sClassType = $aPlace['class'].':'.$aPlace['type'];
|
||||||
$aOutlineResult = array();
|
if (isset($aClassType[$sClassType]) && isset($aClassType[$sClassType]['simplelabel']))
|
||||||
if (!$iPlaceID) return $aOutlineResult;
|
$sAddressType = $aClassType[$sClassType]['simplelabel'];
|
||||||
|
else $sAddressType = $aPlace['class'];
|
||||||
if (CONST_Search_AreaPolygons) {
|
|
||||||
// Get the bounding box and outline polygon
|
|
||||||
$sSQL = 'select place_id,0 as numfeatures,st_area(geometry) as area,';
|
|
||||||
if ($fLonReverse != null && $fLatReverse != null) {
|
|
||||||
$sSQL .= ' ST_Y(closest_point) as centrelat,';
|
|
||||||
$sSQL .= ' ST_X(closest_point) as centrelon,';
|
|
||||||
} else {
|
|
||||||
$sSQL .= ' ST_Y(centroid) as centrelat, ST_X(centroid) as centrelon,';
|
|
||||||
}
|
|
||||||
$sSQL .= ' ST_YMin(geometry) as minlat,ST_YMax(geometry) as maxlat,';
|
|
||||||
$sSQL .= ' ST_XMin(geometry) as minlon,ST_XMax(geometry) as maxlon';
|
|
||||||
if ($this->bIncludePolygonAsGeoJSON) $sSQL .= ',ST_AsGeoJSON(geometry) as asgeojson';
|
|
||||||
if ($this->bIncludePolygonAsKML) $sSQL .= ',ST_AsKML(geometry) as askml';
|
|
||||||
if ($this->bIncludePolygonAsSVG) $sSQL .= ',ST_AsSVG(geometry) as assvg';
|
|
||||||
if ($this->bIncludePolygonAsText || $this->bIncludePolygonAsPoints) $sSQL .= ',ST_AsText(geometry) as astext';
|
|
||||||
if ($fLonReverse != null && $fLatReverse != null) {
|
|
||||||
$sFrom = ' from (SELECT * , CASE WHEN (class = \'highway\') AND (ST_GeometryType(geometry) = \'ST_LineString\') THEN ';
|
|
||||||
$sFrom .=' ST_ClosestPoint(geometry, ST_SetSRID(ST_Point('.$fLatReverse.','.$fLonReverse.'),4326))';
|
|
||||||
$sFrom .=' ELSE centroid END AS closest_point';
|
|
||||||
$sFrom .= ' from placex where place_id = '.$iPlaceID.') as plx';
|
|
||||||
} else {
|
|
||||||
$sFrom = ' from placex where place_id = '.$iPlaceID;
|
|
||||||
}
|
|
||||||
if ($this->fPolygonSimplificationThreshold > 0) {
|
|
||||||
$sSQL .= ' from (select place_id,centroid,ST_SimplifyPreserveTopology(geometry,'.$this->fPolygonSimplificationThreshold.') as geometry'.$sFrom.') as plx';
|
|
||||||
} else {
|
|
||||||
$sSQL .= $sFrom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$aPointPolygon = chksql($this->oDB->getRow($sSQL), 'Could not get outline');
|
$aPlace['addresstype'] = $sAddressType;
|
||||||
|
|
||||||
if ($aPointPolygon['place_id']) {
|
return $aPlace;
|
||||||
if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null) {
|
|
||||||
$aOutlineResult['lat'] = $aPointPolygon['centrelat'];
|
|
||||||
$aOutlineResult['lon'] = $aPointPolygon['centrelon'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->bIncludePolygonAsGeoJSON) $aOutlineResult['asgeojson'] = $aPointPolygon['asgeojson'];
|
function getAddressDetails($bAll = false)
|
||||||
if ($this->bIncludePolygonAsKML) $aOutlineResult['askml'] = $aPointPolygon['askml'];
|
{
|
||||||
if ($this->bIncludePolygonAsSVG) $aOutlineResult['assvg'] = $aPointPolygon['assvg'];
|
if (!$this->iPlaceID) return null;
|
||||||
if ($this->bIncludePolygonAsText) $aOutlineResult['astext'] = $aPointPolygon['astext'];
|
|
||||||
if ($this->bIncludePolygonAsPoints) $aOutlineResult['aPolyPoints'] = geometryText2Points($aPointPolygon['astext'], $fRadius);
|
|
||||||
|
|
||||||
|
$sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted", $this->aLangPrefOrder))."]";
|
||||||
|
|
||||||
if (abs($aPointPolygon['minlat'] - $aPointPolygon['maxlat']) < 0.0000001) {
|
$sSQL = "select *,get_name_by_language(name,$sLanguagePrefArraySQL) as localname from get_addressdata(".$this->iPlaceID.")";
|
||||||
$aPointPolygon['minlat'] = $aPointPolygon['minlat'] - $fRadius;
|
if (!$bAll) $sSQL .= " WHERE isaddress OR type = 'country_code'";
|
||||||
$aPointPolygon['maxlat'] = $aPointPolygon['maxlat'] + $fRadius;
|
$sSQL .= " order by rank_address desc,isaddress desc";
|
||||||
|
|
||||||
|
$aAddressLines = $this->oDB->getAll($sSQL);
|
||||||
|
if (PEAR::IsError($aAddressLines))
|
||||||
|
{
|
||||||
|
var_dump($aAddressLines);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
return $aAddressLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abs($aPointPolygon['minlon'] - $aPointPolygon['maxlon']) < 0.0000001) {
|
function getAddressNames()
|
||||||
$aPointPolygon['minlon'] = $aPointPolygon['minlon'] - $fRadius;
|
{
|
||||||
$aPointPolygon['maxlon'] = $aPointPolygon['maxlon'] + $fRadius;
|
$aAddressLines = $this->getAddressDetails(false);;
|
||||||
|
|
||||||
|
$aAddress = array();
|
||||||
|
$aFallback = array();
|
||||||
|
$aClassType = getClassTypes();
|
||||||
|
foreach($aAddressLines as $aLine)
|
||||||
|
{
|
||||||
|
$bFallback = false;
|
||||||
|
$aTypeLabel = false;
|
||||||
|
if (isset($aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']])) $aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type'].':'.$aLine['admin_level']];
|
||||||
|
elseif (isset($aClassType[$aLine['class'].':'.$aLine['type']])) $aTypeLabel = $aClassType[$aLine['class'].':'.$aLine['type']];
|
||||||
|
elseif (isset($aClassType['boundary:administrative:'.((int)($aLine['rank_address']/2))]))
|
||||||
|
{
|
||||||
|
$aTypeLabel = $aClassType['boundary:administrative:'.((int)($aLine['rank_address']/2))];
|
||||||
|
$bFallback = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$aTypeLabel = array('simplelabel'=>'address'.$aLine['rank_address']);
|
||||||
|
$bFallback = true;
|
||||||
|
}
|
||||||
|
if ($aTypeLabel && ((isset($aLine['localname']) && $aLine['localname']) || (isset($aLine['housenumber']) && $aLine['housenumber'])))
|
||||||
|
{
|
||||||
|
$sTypeLabel = strtolower(isset($aTypeLabel['simplelabel'])?$aTypeLabel['simplelabel']:$aTypeLabel['label']);
|
||||||
|
$sTypeLabel = str_replace(' ','_',$sTypeLabel);
|
||||||
|
if (!isset($aAddress[$sTypeLabel]) || (isset($aFallback[$sTypeLabel]) && $aFallback[$sTypeLabel]) || $aLine['class'] == 'place')
|
||||||
|
{
|
||||||
|
$aAddress[$sTypeLabel] = $aLine['localname']?$aLine['localname']:$aLine['housenumber'];
|
||||||
|
}
|
||||||
|
$aFallback[$sTypeLabel] = $bFallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $aAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
$aOutlineResult['aBoundingBox'] = array(
|
|
||||||
(string)$aPointPolygon['minlat'],
|
|
||||||
(string)$aPointPolygon['maxlat'],
|
|
||||||
(string)$aPointPolygon['minlon'],
|
|
||||||
(string)$aPointPolygon['maxlon']
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
?>
|
||||||
|
|
||||||
// as a fallback we generate a bounding box without knowing the size of the geometry
|
|
||||||
if ((!isset($aOutlineResult['aBoundingBox'])) && isset($fLon)) {
|
|
||||||
//
|
|
||||||
if ($this->bIncludePolygonAsPoints) {
|
|
||||||
$sGeometryText = 'POINT('.$fLon.','.$fLat.')';
|
|
||||||
$aOutlineResult['aPolyPoints'] = geometryText2Points($sGeometryText, $fRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
$aBounds = array();
|
|
||||||
$aBounds['minlat'] = $fLat - $fRadius;
|
|
||||||
$aBounds['maxlat'] = $fLat + $fRadius;
|
|
||||||
$aBounds['minlon'] = $fLon - $fRadius;
|
|
||||||
$aBounds['maxlon'] = $fLon + $fRadius;
|
|
||||||
|
|
||||||
$aOutlineResult['aBoundingBox'] = array(
|
|
||||||
(string)$aBounds['minlat'],
|
|
||||||
(string)$aBounds['maxlat'],
|
|
||||||
(string)$aBounds['minlon'],
|
|
||||||
(string)$aBounds['maxlon']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return $aOutlineResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A single result of a search operation or a reverse lookup.
|
|
||||||
*
|
|
||||||
* This object only contains the id of the result. It does not yet
|
|
||||||
* have any details needed to format the output document.
|
|
||||||
*/
|
|
||||||
class Result
|
|
||||||
{
|
|
||||||
const TABLE_PLACEX = 0;
|
|
||||||
const TABLE_POSTCODE = 1;
|
|
||||||
const TABLE_OSMLINE = 2;
|
|
||||||
const TABLE_AUX = 3;
|
|
||||||
const TABLE_TIGER = 4;
|
|
||||||
|
|
||||||
/// Database table that contains the result.
|
|
||||||
public $iTable;
|
|
||||||
/// Id of the result.
|
|
||||||
public $iId;
|
|
||||||
/// House number (only for interpolation results).
|
|
||||||
public $iHouseNumber = -1;
|
|
||||||
/// Number of exact matches in address (address searches only).
|
|
||||||
public $iExactMatches = 0;
|
|
||||||
/// Subranking within the results (the higher the worse).
|
|
||||||
public $iResultRank = 0;
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'Table' => $this->iTable,
|
|
||||||
'ID' => $this->iId,
|
|
||||||
'House number' => $this->iHouseNumber,
|
|
||||||
'Exact Matches' => $this->iExactMatches,
|
|
||||||
'Result rank' => $this->iResultRank
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct($sId, $iTable = Result::TABLE_PLACEX)
|
|
||||||
{
|
|
||||||
$this->iTable = $iTable;
|
|
||||||
$this->iId = (int) $sId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function joinIdsByTable($aResults, $iTable)
|
|
||||||
{
|
|
||||||
return join(',', array_keys(array_filter(
|
|
||||||
$aResults,
|
|
||||||
function ($aValue) use ($iTable) {
|
|
||||||
return $aValue->iTable == $iTable;
|
|
||||||
}
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
public static function sqlHouseNumberTable($aResults, $iTable)
|
|
||||||
{
|
|
||||||
$sHousenumbers = '';
|
|
||||||
$sSep = '';
|
|
||||||
foreach ($aResults as $oResult) {
|
|
||||||
if ($oResult->iTable == $iTable) {
|
|
||||||
$sHousenumbers .= $sSep.'('.$oResult->iId.',';
|
|
||||||
$sHousenumbers .= $oResult->iHouseNumber.')';
|
|
||||||
$sSep = ',';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sHousenumbers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,43 @@
|
|||||||
<?php
|
<?php
|
||||||
|
class ReverseGeocode
|
||||||
namespace Nominatim;
|
{
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/Result.php');
|
|
||||||
|
|
||||||
class ReverseGeocode
|
|
||||||
{
|
|
||||||
protected $oDB;
|
protected $oDB;
|
||||||
|
|
||||||
|
protected $fLat;
|
||||||
|
protected $fLon;
|
||||||
protected $iMaxRank = 28;
|
protected $iMaxRank = 28;
|
||||||
|
|
||||||
|
protected $aLangPrefOrder = array();
|
||||||
|
|
||||||
public function __construct(&$oDB)
|
protected $bShowAddressDetails = true;
|
||||||
|
|
||||||
|
function ReverseGeocode(&$oDB)
|
||||||
{
|
{
|
||||||
$this->oDB =& $oDB;
|
$this->oDB =& $oDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setLanguagePreference($aLangPref)
|
||||||
|
{
|
||||||
|
$this->aLangPrefOrder = $aLangPref;
|
||||||
|
}
|
||||||
|
|
||||||
public function setZoom($iZoom)
|
function setIncludeAddressDetails($bAddressDetails = true)
|
||||||
|
{
|
||||||
|
$this->bAddressDetails = $bAddressDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLatLon($fLat, $fLon)
|
||||||
|
{
|
||||||
|
$this->fLat = (float)$fLat;
|
||||||
|
$this->fLon = (float)$fLon;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRank($iRank)
|
||||||
|
{
|
||||||
|
$this->iMaxRank = $iRank;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setZoom($iZoom)
|
||||||
{
|
{
|
||||||
// Zoom to rank, this could probably be calculated but a lookup gives fine control
|
// Zoom to rank, this could probably be calculated but a lookup gives fine control
|
||||||
$aZoomRank = array(
|
$aZoomRank = array(
|
||||||
@@ -44,324 +65,75 @@ class ReverseGeocode
|
|||||||
$this->iMaxRank = (isset($iZoom) && isset($aZoomRank[$iZoom]))?$aZoomRank[$iZoom]:28;
|
$this->iMaxRank = (isset($iZoom) && isset($aZoomRank[$iZoom]))?$aZoomRank[$iZoom]:28;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function lookup()
|
||||||
* Find the closest interpolation with the given search diameter.
|
|
||||||
*
|
|
||||||
* @param string $sPointSQL Reverse geocoding point as SQL
|
|
||||||
* @param float $fSearchDiam Search diameter
|
|
||||||
*
|
|
||||||
* @return Record of the interpolation or null.
|
|
||||||
*/
|
|
||||||
protected function lookupInterpolation($sPointSQL, $fSearchDiam)
|
|
||||||
{
|
{
|
||||||
$sSQL = 'SELECT place_id, parent_place_id, 30 as rank_search,';
|
$sPointSQL = 'ST_SetSRID(ST_Point('.$this->fLon.','.$this->fLat.'),4326)';
|
||||||
$sSQL .= ' ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fraction,';
|
|
||||||
$sSQL .= ' startnumber, endnumber, interpolationtype,';
|
|
||||||
$sSQL .= ' ST_Distance(linegeo,'.$sPointSQL.') as distance';
|
|
||||||
$sSQL .= ' FROM location_property_osmline';
|
|
||||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', linegeo, '.$fSearchDiam.')';
|
|
||||||
$sSQL .= ' and indexed_status = 0 and startnumber is not NULL ';
|
|
||||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
|
||||||
|
|
||||||
return chksql(
|
|
||||||
$this->oDB->getRow($sSQL),
|
|
||||||
'Could not determine closest housenumber on an osm interpolation line.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function lookupLargeArea($sPointSQL, $iMaxRank)
|
|
||||||
{
|
|
||||||
$oResult = null;
|
|
||||||
|
|
||||||
if ($iMaxRank > 4) {
|
|
||||||
$aPlace = $this->lookupPolygon($sPointSQL, $iMaxRank);
|
|
||||||
if ($aPlace) {
|
|
||||||
return new Result($aPlace['place_id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no polygon which contains the searchpoint is found,
|
|
||||||
// searches in the country_osm_grid table for a polygon.
|
|
||||||
return $this->lookupInCountry($sPointSQL, $iMaxRank);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function lookupInCountry($sPointSQL, $iMaxRank)
|
|
||||||
{
|
|
||||||
// searches for polygon in table country_osm_grid which contains the searchpoint
|
|
||||||
// and searches for the nearest place node to the searchpoint in this polygon
|
|
||||||
$sSQL = 'SELECT country_code FROM country_osm_grid';
|
|
||||||
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.') LIMIT 1';
|
|
||||||
|
|
||||||
$sCountryCode = chksql(
|
|
||||||
$this->oDB->getOne($sSQL),
|
|
||||||
'Could not determine country polygon containing the point.'
|
|
||||||
);
|
|
||||||
if ($sCountryCode) {
|
|
||||||
if ($iMaxRank > 4) {
|
|
||||||
// look for place nodes with the given country code
|
|
||||||
$sSQL = 'SELECT place_id FROM';
|
|
||||||
$sSQL .= ' (SELECT place_id, rank_search,';
|
|
||||||
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
|
||||||
$sSQL .= ' FROM placex';
|
|
||||||
$sSQL .= ' WHERE osm_type = \'N\'';
|
|
||||||
$sSQL .= ' AND country_code = \''.$sCountryCode.'\'';
|
|
||||||
$sSQL .= ' AND rank_search between 5 and ' .min(25, $iMaxRank);
|
|
||||||
$sSQL .= ' AND class = \'place\' AND type != \'postcode\'';
|
|
||||||
$sSQL .= ' AND name IS NOT NULL ';
|
|
||||||
$sSQL .= ' and indexed_status = 0 and linked_place_id is null';
|
|
||||||
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', geometry, 1.8)) p ';
|
|
||||||
$sSQL .= 'WHERE distance <= reverse_place_diameter(rank_search)';
|
|
||||||
$sSQL .= ' ORDER BY rank_search DESC, distance ASC';
|
|
||||||
$sSQL .= ' LIMIT 1';
|
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
|
||||||
$aPlace = chksql(
|
|
||||||
$this->oDB->getRow($sSQL),
|
|
||||||
'Could not determine place node.'
|
|
||||||
);
|
|
||||||
if ($aPlace) {
|
|
||||||
return new Result($aPlace['place_id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// still nothing, then return the country object
|
|
||||||
$sSQL = 'SELECT place_id, ST_distance('.$sPointSQL.', centroid) as distance';
|
|
||||||
$sSQL .= ' FROM placex';
|
|
||||||
$sSQL .= ' WHERE country_code = \''.$sCountryCode.'\'';
|
|
||||||
$sSQL .= ' AND rank_search = 4 AND rank_address = 4';
|
|
||||||
$sSQL .= ' AND class in (\'boundary\', \'place\')';
|
|
||||||
$sSQL .= ' AND linked_place_id is null';
|
|
||||||
$sSQL .= ' ORDER BY distance ASC';
|
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
|
||||||
$aPlace = chksql(
|
|
||||||
$this->oDB->getRow($sSQL),
|
|
||||||
'Could not determine place node.'
|
|
||||||
);
|
|
||||||
if ($aPlace) {
|
|
||||||
return new Result($aPlace['place_id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for areas or nodes for areas or nodes between state and suburb level.
|
|
||||||
*
|
|
||||||
* @param string $sPointSQL Search point as SQL string.
|
|
||||||
* @param int $iMaxRank Maximum address rank of the feature.
|
|
||||||
*
|
|
||||||
* @return Record of the found feature or null.
|
|
||||||
*
|
|
||||||
* Searches first for polygon that contains the search point.
|
|
||||||
* If such a polygon is found, place nodes with a higher rank are
|
|
||||||
* searched inside the polygon.
|
|
||||||
*/
|
|
||||||
protected function lookupPolygon($sPointSQL, $iMaxRank)
|
|
||||||
{
|
|
||||||
// polygon search begins at suburb-level
|
|
||||||
if ($iMaxRank > 25) $iMaxRank = 25;
|
|
||||||
// no polygon search over country-level
|
|
||||||
if ($iMaxRank < 5) $iMaxRank = 5;
|
|
||||||
// search for polygon
|
|
||||||
$sSQL = 'SELECT place_id, parent_place_id, rank_address, rank_search FROM';
|
|
||||||
$sSQL .= '(select place_id, parent_place_id, rank_address, rank_search, country_code, geometry';
|
|
||||||
$sSQL .= ' FROM placex';
|
|
||||||
$sSQL .= ' WHERE ST_GeometryType(geometry) in (\'ST_Polygon\', \'ST_MultiPolygon\')';
|
|
||||||
$sSQL .= ' AND rank_address Between 5 AND ' .$iMaxRank;
|
|
||||||
$sSQL .= ' AND geometry && '.$sPointSQL;
|
|
||||||
$sSQL .= ' AND type != \'postcode\' ';
|
|
||||||
$sSQL .= ' AND name is not null';
|
|
||||||
$sSQL .= ' AND indexed_status = 0 and linked_place_id is null';
|
|
||||||
$sSQL .= ' ORDER BY rank_address DESC LIMIT 50 ) as a';
|
|
||||||
$sSQL .= ' WHERE ST_CONTAINS(geometry, '.$sPointSQL.' )';
|
|
||||||
$sSQL .= ' ORDER BY rank_address DESC LIMIT 1';
|
|
||||||
|
|
||||||
$aPoly = chksql(
|
|
||||||
$this->oDB->getRow($sSQL),
|
|
||||||
'Could not determine polygon containing the point.'
|
|
||||||
);
|
|
||||||
if ($aPoly) {
|
|
||||||
// if a polygon is found, search for placenodes begins ...
|
|
||||||
$iParentPlaceID = $aPoly['parent_place_id'];
|
|
||||||
$iRankAddress = $aPoly['rank_address'];
|
|
||||||
$iRankSearch = $aPoly['rank_search'];
|
|
||||||
$iPlaceID = $aPoly['place_id'];
|
|
||||||
|
|
||||||
if ($iRankAddress != $iMaxRank) {
|
|
||||||
$sSQL = 'SELECT place_id FROM ';
|
|
||||||
$sSQL .= '(SELECT place_id, rank_search, country_code, geometry,';
|
|
||||||
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
|
||||||
$sSQL .= ' FROM placex';
|
|
||||||
$sSQL .= ' WHERE osm_type = \'N\'';
|
|
||||||
// using rank_search because of a better differentiation
|
|
||||||
// for place nodes at rank_address 16
|
|
||||||
$sSQL .= ' AND rank_search > '.$iRankSearch;
|
|
||||||
$sSQL .= ' AND rank_search <= '.$iMaxRank;
|
|
||||||
$sSQL .= ' AND class = \'place\'';
|
|
||||||
$sSQL .= ' AND type != \'postcode\'';
|
|
||||||
$sSQL .= ' AND name IS NOT NULL ';
|
|
||||||
$sSQL .= ' AND indexed_status = 0 AND linked_place_id is null';
|
|
||||||
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', geometry, reverse_place_diameter('.$iRankSearch.'::smallint))';
|
|
||||||
$sSQL .= ' ORDER BY distance ASC,';
|
|
||||||
$sSQL .= ' rank_address DESC';
|
|
||||||
$sSQL .= ' limit 500) as a';
|
|
||||||
$sSQL .= ' WHERE ST_CONTAINS((SELECT geometry FROM placex WHERE place_id = '.$iPlaceID.'), geometry )';
|
|
||||||
$sSQL .= ' AND distance <= reverse_place_diameter(rank_search)';
|
|
||||||
$sSQL .= ' ORDER BY distance ASC, rank_search DESC';
|
|
||||||
$sSQL .= ' LIMIT 1';
|
|
||||||
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
|
||||||
$aPlacNode = chksql(
|
|
||||||
$this->oDB->getRow($sSQL),
|
|
||||||
'Could not determine place node.'
|
|
||||||
);
|
|
||||||
if ($aPlacNode) {
|
|
||||||
return $aPlacNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aPoly;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function lookup($fLat, $fLon, $bDoInterpolation = true)
|
|
||||||
{
|
|
||||||
return $this->lookupPoint(
|
|
||||||
'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)',
|
|
||||||
$bDoInterpolation
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function lookupPoint($sPointSQL, $bDoInterpolation = true)
|
|
||||||
{
|
|
||||||
// starts if the search is on POI or street level,
|
|
||||||
// searches for the nearest POI or street,
|
|
||||||
// if a street is found and a POI is searched for,
|
|
||||||
// the nearest POI which the found street is a parent of is choosen.
|
|
||||||
$iMaxRank = $this->iMaxRank;
|
$iMaxRank = $this->iMaxRank;
|
||||||
|
|
||||||
// Find the nearest point
|
// Find the nearest point
|
||||||
$fSearchDiam = 0.006;
|
$fSearchDiam = 0.0004;
|
||||||
$oResult = null;
|
$iPlaceID = null;
|
||||||
$aPlace = null;
|
$aArea = false;
|
||||||
|
$fMaxAreaDistance = 1;
|
||||||
|
while(!$iPlaceID && $fSearchDiam < $fMaxAreaDistance)
|
||||||
|
{
|
||||||
|
$fSearchDiam = $fSearchDiam * 2;
|
||||||
|
|
||||||
// for POI or street level
|
// If we have to expand the search area by a large amount then we need a larger feature
|
||||||
if ($iMaxRank >= 26) {
|
// then there is a limit to how small the feature should be
|
||||||
$sSQL = 'select place_id,parent_place_id,rank_address,country_code,';
|
if ($fSearchDiam > 2 && $iMaxRank > 4) $iMaxRank = 4;
|
||||||
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
if ($fSearchDiam > 1 && $iMaxRank > 9) $iMaxRank = 8;
|
||||||
$sSQL .= ' FROM ';
|
if ($fSearchDiam > 0.8 && $iMaxRank > 10) $iMaxRank = 10;
|
||||||
$sSQL .= ' placex';
|
if ($fSearchDiam > 0.6 && $iMaxRank > 12) $iMaxRank = 12;
|
||||||
|
if ($fSearchDiam > 0.2 && $iMaxRank > 17) $iMaxRank = 17;
|
||||||
|
if ($fSearchDiam > 0.1 && $iMaxRank > 18) $iMaxRank = 18;
|
||||||
|
if ($fSearchDiam > 0.008 && $iMaxRank > 22) $iMaxRank = 22;
|
||||||
|
if ($fSearchDiam > 0.001 && $iMaxRank > 26) $iMaxRank = 26;
|
||||||
|
|
||||||
|
$sSQL = 'select place_id,parent_place_id,rank_search from placex';
|
||||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
|
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
|
||||||
$sSQL .= ' AND';
|
$sSQL .= ' and rank_search != 28 and rank_search >= '.$iMaxRank;
|
||||||
// only streets
|
$sSQL .= ' and (name is not null or housenumber is not null)';
|
||||||
if ($iMaxRank == 26) {
|
$sSQL .= ' and class not in (\'waterway\',\'railway\',\'tunnel\',\'bridge\')';
|
||||||
$sSQL .= ' rank_address = 26';
|
$sSQL .= ' and indexed_status = 0 ';
|
||||||
} else {
|
|
||||||
$sSQL .= ' rank_address between 26 and '.$iMaxRank;
|
|
||||||
}
|
|
||||||
$sSQL .= ' and (name is not null or housenumber is not null';
|
|
||||||
$sSQL .= ' or rank_address between 26 and 27)';
|
|
||||||
$sSQL .= ' and class not in (\'railway\',\'tunnel\',\'bridge\',\'man_made\')';
|
|
||||||
$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 ST_distance('.$sPointSQL.', geometry) ASC limit 1';
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
if (CONST_Debug) var_dump($sSQL);
|
||||||
$aPlace = chksql(
|
$aPlace = $this->oDB->getRow($sSQL);
|
||||||
$this->oDB->getRow($sSQL),
|
if (PEAR::IsError($aPlace))
|
||||||
'Could not determine closest place.'
|
{
|
||||||
);
|
failInternalError("Could not determine closest place.", $sSQL, $aPlace);
|
||||||
|
}
|
||||||
if (CONST_Debug) var_dump($aPlace);
|
|
||||||
if ($aPlace) {
|
|
||||||
$iPlaceID = $aPlace['place_id'];
|
$iPlaceID = $aPlace['place_id'];
|
||||||
$oResult = new Result($iPlaceID);
|
|
||||||
$iRankAddress = $aPlace['rank_address'];
|
|
||||||
$iParentPlaceID = $aPlace['parent_place_id'];
|
$iParentPlaceID = $aPlace['parent_place_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bDoInterpolation && $iMaxRank >= 30) {
|
// The point we found might be too small - use the address to find what it is a child of
|
||||||
$fDistance = $fSearchDiam;
|
if ($iPlaceID && $iMaxRank < 28)
|
||||||
if ($aPlace) {
|
{
|
||||||
// We can't reliably go from the closest street to an
|
if ($aPlace['rank_search'] > 28 && $iParentPlaceID)
|
||||||
// interpolation line because the closest interpolation
|
{
|
||||||
// may have a different street segments as a parent.
|
$iPlaceID = $iParentPlaceID;
|
||||||
// Therefore allow an interpolation line to take precendence
|
|
||||||
// even when the street is closer.
|
|
||||||
$fDistance = $iRankAddress < 28 ? 0.001 : $aPlace['distance'];
|
|
||||||
}
|
}
|
||||||
|
$sSQL = "select address_place_id from place_addressline where place_id = $iPlaceID order by abs(cached_rank_address - $iMaxRank) asc,cached_rank_address desc,isaddress desc,distance desc limit 1";
|
||||||
$aHouse = $this->lookupInterpolation($sPointSQL, $fDistance);
|
$iPlaceID = $this->oDB->getOne($sSQL);
|
||||||
|
if (PEAR::IsError($iPlaceID))
|
||||||
if ($aHouse) {
|
{
|
||||||
$oResult = new Result($aHouse['place_id'], Result::TABLE_OSMLINE);
|
failInternalError("Could not get parent for place.", $sSQL, $iPlaceID);
|
||||||
$oResult->iHouseNumber = closestHouseNumber($aHouse);
|
}
|
||||||
$aPlace = $aHouse;
|
if (!$iPlaceID)
|
||||||
$iRankAddress = 30;
|
{
|
||||||
|
$iPlaceID = $aPlace['place_id'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($aPlace) {
|
$oPlaceLookup = new PlaceLookup($this->oDB);
|
||||||
// if street and maxrank > streetlevel
|
$oPlaceLookup->setLanguagePreference($this->aLangPrefOrder);
|
||||||
if ($iRankAddress <= 27 && $iMaxRank > 27) {
|
$oPlaceLookup->setIncludeAddressDetails($this->bAddressDetails);
|
||||||
// find the closest object (up to a certain radius) of which the street is a parent of
|
$oPlaceLookup->setPlaceId($iPlaceID);
|
||||||
$sSQL = ' select place_id,';
|
|
||||||
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
|
|
||||||
$sSQL .= ' FROM ';
|
|
||||||
$sSQL .= ' placex';
|
|
||||||
// radius ?
|
|
||||||
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, 0.001)';
|
|
||||||
$sSQL .= ' AND parent_place_id = '.$iPlaceID;
|
|
||||||
$sSQL .= ' and rank_address != 28';
|
|
||||||
$sSQL .= ' and (name is not null or housenumber is not null)';
|
|
||||||
$sSQL .= ' and class not in (\'railway\',\'tunnel\',\'bridge\',\'man_made\')';
|
|
||||||
$sSQL .= ' and indexed_status = 0 and linked_place_id is null';
|
|
||||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
|
||||||
$aStreet = chksql(
|
|
||||||
$this->oDB->getRow($sSQL),
|
|
||||||
'Could not determine closest place.'
|
|
||||||
);
|
|
||||||
if ($aStreet) {
|
|
||||||
if (CONST_Debug) var_dump($aStreet);
|
|
||||||
$oResult = new Result($aStreet['place_id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the US we can check TIGER data for nearest housenumber
|
return $oPlaceLookup->lookup();
|
||||||
if (CONST_Use_US_Tiger_Data
|
|
||||||
&& $iRankAddress <= 27
|
|
||||||
&& $aPlace['country_code'] == 'us'
|
|
||||||
&& $this->iMaxRank >= 28
|
|
||||||
) {
|
|
||||||
$sSQL = 'SELECT place_id,parent_place_id,30 as rank_search,';
|
|
||||||
$sSQL .= 'ST_LineLocatePoint(linegeo,'.$sPointSQL.') as fraction,';
|
|
||||||
$sSQL .= 'ST_distance('.$sPointSQL.', linegeo) as distance,';
|
|
||||||
$sSQL .= 'startnumber,endnumber,interpolationtype';
|
|
||||||
$sSQL .= ' FROM location_property_tiger WHERE parent_place_id = '.$oResult->iId;
|
|
||||||
$sSQL .= ' AND ST_DWithin('.$sPointSQL.', linegeo, 0.001)';
|
|
||||||
$sSQL .= ' ORDER BY distance ASC limit 1';
|
|
||||||
if (CONST_Debug) var_dump($sSQL);
|
|
||||||
$aPlaceTiger = chksql(
|
|
||||||
$this->oDB->getRow($sSQL),
|
|
||||||
'Could not determine closest Tiger place.'
|
|
||||||
);
|
|
||||||
if ($aPlaceTiger) {
|
|
||||||
if (CONST_Debug) var_dump('found Tiger housenumber', $aPlaceTiger);
|
|
||||||
$oResult = new Result($aPlaceTiger['place_id'], Result::TABLE_TIGER);
|
|
||||||
$oResult->iHouseNumber = closestHouseNumber($aPlaceTiger);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
?>
|
||||||
// if no POI or street is found ...
|
|
||||||
$oResult = $this->lookupLargeArea($sPointSQL, 25);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// lower than street level ($iMaxRank < 26 )
|
|
||||||
$oResult = $this->lookupLargeArea($sPointSQL, $iMaxRank);
|
|
||||||
}
|
|
||||||
return $oResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,284 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/lib.php');
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collection of search constraints that are independent of the
|
|
||||||
* actual interpretation of the search query.
|
|
||||||
*
|
|
||||||
* The search context is shared between all SearchDescriptions. This
|
|
||||||
* object mainly serves as context provider for the database queries.
|
|
||||||
* Therefore most data is directly cached as SQL statements.
|
|
||||||
*/
|
|
||||||
class SearchContext
|
|
||||||
{
|
|
||||||
/// Search radius around a given Near reference point.
|
|
||||||
private $fNearRadius = false;
|
|
||||||
/// True if search must be restricted to viewbox only.
|
|
||||||
public $bViewboxBounded = false;
|
|
||||||
|
|
||||||
/// Reference point for search (as SQL).
|
|
||||||
public $sqlNear = '';
|
|
||||||
/// Viewbox selected for search (as SQL).
|
|
||||||
public $sqlViewboxSmall = '';
|
|
||||||
/// Viewbox with a larger buffer around (as SQL).
|
|
||||||
public $sqlViewboxLarge = '';
|
|
||||||
/// Reference along a route (as SQL).
|
|
||||||
public $sqlViewboxCentre = '';
|
|
||||||
/// List of countries to restrict search to (as SQL).
|
|
||||||
public $sqlCountryList = '';
|
|
||||||
/// List of place IDs to exclude (as SQL).
|
|
||||||
private $sqlExcludeList = '';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a reference point is defined.
|
|
||||||
*
|
|
||||||
* @return bool True if a reference point is defined.
|
|
||||||
*/
|
|
||||||
public function hasNearPoint()
|
|
||||||
{
|
|
||||||
return $this->fNearRadius !== false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get radius around reference point.
|
|
||||||
*
|
|
||||||
* @return float Search radius around reference point.
|
|
||||||
*/
|
|
||||||
public function nearRadius()
|
|
||||||
{
|
|
||||||
return $this->fNearRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set search reference point in WGS84.
|
|
||||||
*
|
|
||||||
* If set, then only places around this point will be taken into account.
|
|
||||||
*
|
|
||||||
* @param float $fLat Latitude of point.
|
|
||||||
* @param float $fLon Longitude of point.
|
|
||||||
* @param float $fRadius Search radius around point.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setNearPoint($fLat, $fLon, $fRadius = 0.1)
|
|
||||||
{
|
|
||||||
$this->fNearRadius = $fRadius;
|
|
||||||
$this->sqlNear = 'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the search is geographically restricted.
|
|
||||||
*
|
|
||||||
* Searches are restricted if a reference point is given or if
|
|
||||||
* a bounded viewbox is set.
|
|
||||||
*
|
|
||||||
* @return bool True, if the search is geographically bounded.
|
|
||||||
*/
|
|
||||||
public function isBoundedSearch()
|
|
||||||
{
|
|
||||||
return $this->hasNearPoint() || ($this->sqlViewboxSmall && $this->bViewboxBounded);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set rectangular viewbox.
|
|
||||||
*
|
|
||||||
* The viewbox may be bounded which means that no search results
|
|
||||||
* must be outside the viewbox.
|
|
||||||
*
|
|
||||||
* @param float[4] $aViewBox Coordinates of the viewbox.
|
|
||||||
* @param bool $bBounded True if the viewbox is bounded.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setViewboxFromBox(&$aViewBox, $bBounded)
|
|
||||||
{
|
|
||||||
$this->bViewboxBounded = $bBounded;
|
|
||||||
$this->sqlViewboxCentre = '';
|
|
||||||
|
|
||||||
$this->sqlViewboxSmall = sprintf(
|
|
||||||
'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
|
|
||||||
$aViewBox[0],
|
|
||||||
$aViewBox[1],
|
|
||||||
$aViewBox[2],
|
|
||||||
$aViewBox[3]
|
|
||||||
);
|
|
||||||
|
|
||||||
$fHeight = abs($aViewBox[0] - $aViewBox[2]);
|
|
||||||
$fWidth = abs($aViewBox[1] - $aViewBox[3]);
|
|
||||||
|
|
||||||
$this->sqlViewboxLarge = sprintf(
|
|
||||||
'ST_SetSRID(ST_MakeBox2D(ST_Point(%F,%F),ST_Point(%F,%F)),4326)',
|
|
||||||
max($aViewBox[0], $aViewBox[2]) + $fHeight,
|
|
||||||
max($aViewBox[1], $aViewBox[3]) + $fWidth,
|
|
||||||
min($aViewBox[0], $aViewBox[2]) - $fHeight,
|
|
||||||
min($aViewBox[1], $aViewBox[3]) - $fWidth
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set viewbox along a route.
|
|
||||||
*
|
|
||||||
* The viewbox may be bounded which means that no search results
|
|
||||||
* must be outside the viewbox.
|
|
||||||
*
|
|
||||||
* @param object $oDB DB connection to use for computing the box.
|
|
||||||
* @param string[] $aRoutePoints List of x,y coordinates along a route.
|
|
||||||
* @param float $fRouteWidth Buffer around the route to use.
|
|
||||||
* @param bool $bBounded True if the viewbox bounded.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setViewboxFromRoute(&$oDB, $aRoutePoints, $fRouteWidth, $bBounded)
|
|
||||||
{
|
|
||||||
$this->bViewboxBounded = $bBounded;
|
|
||||||
$this->sqlViewboxCentre = "ST_SetSRID('LINESTRING(";
|
|
||||||
$sSep = '';
|
|
||||||
foreach ($aRoutePoints as $aPoint) {
|
|
||||||
$fPoint = (float)$aPoint;
|
|
||||||
$this->sqlViewboxCentre .= $sSep.$fPoint;
|
|
||||||
$sSep = ($sSep == ' ') ? ',' : ' ';
|
|
||||||
}
|
|
||||||
$this->sqlViewboxCentre .= ")'::geometry,4326)";
|
|
||||||
|
|
||||||
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/69).')';
|
|
||||||
$sGeom = chksql($oDB->getOne('select '.$sSQL), 'Could not get small viewbox');
|
|
||||||
$this->sqlViewboxSmall = "'".$sGeom."'::geometry";
|
|
||||||
|
|
||||||
$sSQL = 'ST_BUFFER('.$this->sqlViewboxCentre.','.($fRouteWidth/30).')';
|
|
||||||
$sGeom = chksql($oDB->getOne('select '.$sSQL), 'Could not get large viewbox');
|
|
||||||
$this->sqlViewboxLarge = "'".$sGeom."'::geometry";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set list of excluded place IDs.
|
|
||||||
*
|
|
||||||
* @param integer[] $aExcluded List of IDs.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setExcludeList($aExcluded)
|
|
||||||
{
|
|
||||||
$this->sqlExcludeList = ' not in ('.join(',', $aExcluded).')';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set list of countries to restrict search to.
|
|
||||||
*
|
|
||||||
* @param string[] $aCountries List of two-letter lower-case country codes.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setCountryList($aCountries)
|
|
||||||
{
|
|
||||||
$this->sqlCountryList = '('.join(',', array_map('addQuotes', $aCountries)).')';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract a reference point from a query string.
|
|
||||||
*
|
|
||||||
* @param string $sQuery Query to scan.
|
|
||||||
*
|
|
||||||
* @return string The remaining query string.
|
|
||||||
*/
|
|
||||||
public function setNearPointFromQuery($sQuery)
|
|
||||||
{
|
|
||||||
$aResult = parseLatLon($sQuery);
|
|
||||||
|
|
||||||
if ($aResult !== false
|
|
||||||
&& $aResult[1] <= 90.1
|
|
||||||
&& $aResult[1] >= -90.1
|
|
||||||
&& $aResult[2] <= 180.1
|
|
||||||
&& $aResult[2] >= -180.1
|
|
||||||
) {
|
|
||||||
$this->setNearPoint($aResult[1], $aResult[2]);
|
|
||||||
$sQuery = trim(str_replace($aResult[0], ' ', $sQuery));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an SQL snipped for computing the distance from the reference point.
|
|
||||||
*
|
|
||||||
* @param string $sObj SQL variable name to compute the distance from.
|
|
||||||
*
|
|
||||||
* @return string An SQL string.
|
|
||||||
*/
|
|
||||||
public function distanceSQL($sObj)
|
|
||||||
{
|
|
||||||
return 'ST_Distance('.$this->sqlNear.", $sObj)";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an SQL snipped for checking if something is within range of the
|
|
||||||
* reference point.
|
|
||||||
*
|
|
||||||
* @param string $sObj SQL variable name to compute if it is within range.
|
|
||||||
*
|
|
||||||
* @return string An SQL string.
|
|
||||||
*/
|
|
||||||
public function withinSQL($sObj)
|
|
||||||
{
|
|
||||||
return sprintf('ST_DWithin(%s, %s, %F)', $sObj, $this->sqlNear, $this->fNearRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an SQL snipped of the importance factor of the viewbox.
|
|
||||||
*
|
|
||||||
* The importance factor is computed by checking if an object is within
|
|
||||||
* the viewbox and/or the extended version of the viewbox.
|
|
||||||
*
|
|
||||||
* @param string $sObj SQL variable name of object to weight the importance
|
|
||||||
*
|
|
||||||
* @return string SQL snipped of the factor with a leading multiply sign.
|
|
||||||
*/
|
|
||||||
public function viewboxImportanceSQL($sObj)
|
|
||||||
{
|
|
||||||
$sSQL = '';
|
|
||||||
|
|
||||||
if ($this->sqlViewboxSmall) {
|
|
||||||
$sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxSmall, $sObj) THEN 1 ELSE 0.5 END";
|
|
||||||
}
|
|
||||||
if ($this->sqlViewboxLarge) {
|
|
||||||
$sSQL = " * CASE WHEN ST_Contains($this->sqlViewboxLarge, $sObj) THEN 1 ELSE 0.5 END";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sSQL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SQL snipped checking if a place ID should be excluded.
|
|
||||||
*
|
|
||||||
* @param string $sVariable SQL variable name of place ID to check,
|
|
||||||
* potentially prefixed with more SQL.
|
|
||||||
*
|
|
||||||
* @return string SQL snippet.
|
|
||||||
*/
|
|
||||||
public function excludeSQL($sVariable)
|
|
||||||
{
|
|
||||||
if ($this->sqlExcludeList) {
|
|
||||||
return $sVariable.$this->sqlExcludeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'Near radius' => $this->fNearRadius,
|
|
||||||
'Near point (SQL)' => $this->sqlNear,
|
|
||||||
'Bounded viewbox' => $this->bViewboxBounded,
|
|
||||||
'Viewbox (SQL, small)' => $this->sqlViewboxSmall,
|
|
||||||
'Viewbox (SQL, large)' => $this->sqlViewboxLarge,
|
|
||||||
'Viewbox (SQL, centre)' => $this->sqlViewboxCentre,
|
|
||||||
'Countries (SQL)' => $this->sqlCountryList,
|
|
||||||
'Excluded IDs (SQL)' => $this->sqlExcludeList
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Operators describing special searches.
|
|
||||||
*/
|
|
||||||
abstract class Operator
|
|
||||||
{
|
|
||||||
/// No operator selected.
|
|
||||||
const NONE = 0;
|
|
||||||
/// Search for POI of the given type.
|
|
||||||
const TYPE = 1;
|
|
||||||
/// Search for POIs near the given place.
|
|
||||||
const NEAR = 2;
|
|
||||||
/// Search for POIS in the given place.
|
|
||||||
const IN = 3;
|
|
||||||
/// Search for POIS named as given.
|
|
||||||
const NAME = 4;
|
|
||||||
/// Search for postcodes.
|
|
||||||
const POSTCODE = 5;
|
|
||||||
|
|
||||||
private static $aConstantNames = null;
|
|
||||||
|
|
||||||
|
|
||||||
public static function toString($iOperator)
|
|
||||||
{
|
|
||||||
if ($iOperator == Operator::NONE) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Operator::$aConstantNames === null) {
|
|
||||||
$oReflector = new \ReflectionClass('Nominatim\Operator');
|
|
||||||
$aConstants = $oReflector->getConstants();
|
|
||||||
|
|
||||||
Operator::$aConstantNames = array();
|
|
||||||
foreach ($aConstants as $sName => $iValue) {
|
|
||||||
Operator::$aConstantNames[$iValue] = $sName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Operator::$aConstantNames[$iOperator];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use PEAR;
|
|
||||||
|
|
||||||
class Status
|
|
||||||
{
|
|
||||||
protected $oDB;
|
|
||||||
|
|
||||||
public function __construct(&$oDB)
|
|
||||||
{
|
|
||||||
$this->oDB =& $oDB;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function status()
|
|
||||||
{
|
|
||||||
if (!$this->oDB || PEAR::isError($this->oDB)) {
|
|
||||||
throw new Exception('No database', 700);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sStandardWord = $this->oDB->getOne("SELECT make_standard_name('a')");
|
|
||||||
if (PEAR::isError($sStandardWord)) {
|
|
||||||
throw new Exception('Module failed', 701);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($sStandardWord != 'a') {
|
|
||||||
throw new Exception('Module call failed', 702);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code, ';
|
|
||||||
$sSQL .= "operator, search_name_count FROM word WHERE word_token IN (' a')";
|
|
||||||
$iWordID = $this->oDB->getOne($sSQL);
|
|
||||||
if (PEAR::isError($iWordID)) {
|
|
||||||
throw new Exception('Query failed', 703);
|
|
||||||
}
|
|
||||||
if (!$iWordID) {
|
|
||||||
throw new Exception('No value', 704);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function dataDate()
|
|
||||||
{
|
|
||||||
$sSQL = 'SELECT EXTRACT(EPOCH FROM lastimportdate) FROM import_status LIMIT 1';
|
|
||||||
$iDataDateEpoch = $this->oDB->getOne($sSQL);
|
|
||||||
|
|
||||||
if (PEAR::isError($iDataDateEpoch)) {
|
|
||||||
throw Exception('Data date query failed '.$iDataDateEpoch->getMessage(), 705);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $iDataDateEpoch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim\Token;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A country token.
|
|
||||||
*/
|
|
||||||
class Country
|
|
||||||
{
|
|
||||||
/// Database word id, if available.
|
|
||||||
public $iId;
|
|
||||||
/// Two-letter country code (lower-cased).
|
|
||||||
public $sCountryCode;
|
|
||||||
|
|
||||||
public function __construct($iId, $sCountryCode)
|
|
||||||
{
|
|
||||||
$this->iId = $iId;
|
|
||||||
$this->sCountryCode = $sCountryCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'ID' => $this->iId,
|
|
||||||
'Type' => 'country',
|
|
||||||
'Info' => $this->sCountryCode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim\Token;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A house number token.
|
|
||||||
*/
|
|
||||||
class HouseNumber
|
|
||||||
{
|
|
||||||
/// Database word id, if available.
|
|
||||||
public $iId;
|
|
||||||
/// Normalized house number.
|
|
||||||
public $sToken;
|
|
||||||
|
|
||||||
public function __construct($iId, $sToken)
|
|
||||||
{
|
|
||||||
$this->iId = $iId;
|
|
||||||
$this->sToken = $sToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'ID' => $this->iId,
|
|
||||||
'Type' => 'house number',
|
|
||||||
'Info' => array('nr' => $this->sToken)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim;
|
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/TokenCountry.php');
|
|
||||||
require_once(CONST_BasePath.'/lib/TokenHousenumber.php');
|
|
||||||
require_once(CONST_BasePath.'/lib/TokenPostcode.php');
|
|
||||||
require_once(CONST_BasePath.'/lib/TokenSpecialTerm.php');
|
|
||||||
require_once(CONST_BasePath.'/lib/TokenWord.php');
|
|
||||||
require_once(CONST_BasePath.'/lib/SpecialSearchOperator.php');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves information about the tokens that appear in a search query.
|
|
||||||
*
|
|
||||||
* Tokens are sorted by their normalized form, the token word. There are different
|
|
||||||
* kinds of tokens, represented by different Token* classes. Note that
|
|
||||||
* tokens do not have a common base class. All tokens need to have a field
|
|
||||||
* with the word id that points to an entry in the `word` database table
|
|
||||||
* but otherwise the information saved about a token can be very different.
|
|
||||||
*
|
|
||||||
* There are two different kinds of token words: full words and partial terms.
|
|
||||||
*
|
|
||||||
* Full words start with a space. They represent a complete name of a place.
|
|
||||||
* All special tokens are normally full words.
|
|
||||||
*
|
|
||||||
* Partial terms have no space at the beginning. They may represent a part of
|
|
||||||
* a name of a place (e.g. in the name 'World Trade Center' a partial term
|
|
||||||
* would be 'Trade' or 'Trade Center'). They are only used in TokenWord.
|
|
||||||
*/
|
|
||||||
class TokenList
|
|
||||||
{
|
|
||||||
// List of list of tokens indexed by their word_token.
|
|
||||||
private $aTokens = array();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return total number of tokens.
|
|
||||||
*
|
|
||||||
* @return Integer
|
|
||||||
*/
|
|
||||||
public function count()
|
|
||||||
{
|
|
||||||
return count($this->aTokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there are tokens for the given token word.
|
|
||||||
*
|
|
||||||
* @param string $sWord Token word to look for.
|
|
||||||
*
|
|
||||||
* @return bool True if there is one or more token for the token word.
|
|
||||||
*/
|
|
||||||
public function contains($sWord)
|
|
||||||
{
|
|
||||||
return isset($this->aTokens[$sWord]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the list of tokens for the given token word.
|
|
||||||
*
|
|
||||||
* @param string $sWord Token word to look for.
|
|
||||||
*
|
|
||||||
* @return object[] Array of tokens for the given token word or an
|
|
||||||
* empty array if no tokens could be found.
|
|
||||||
*/
|
|
||||||
public function get($sWord)
|
|
||||||
{
|
|
||||||
return isset($this->aTokens[$sWord]) ? $this->aTokens[$sWord] : array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add token information from the word table in the database.
|
|
||||||
*
|
|
||||||
* @param object $oDB Database connection.
|
|
||||||
* @param string[] $aTokens List of tokens to look up in the database.
|
|
||||||
* @param string[] $aCountryCodes List of country restrictions.
|
|
||||||
* @param string $sNormQuery Normalized query string.
|
|
||||||
* @param object $oNormalizer Normalizer function to use on tokens.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addTokensFromDB(&$oDB, &$aTokens, &$aCountryCodes, $sNormQuery, $oNormalizer)
|
|
||||||
{
|
|
||||||
// Check which tokens we have, get the ID numbers
|
|
||||||
$sSQL = 'SELECT word_id, word_token, word, class, type, country_code,';
|
|
||||||
$sSQL .= ' operator, coalesce(search_name_count, 0) as count';
|
|
||||||
$sSQL .= ' FROM word WHERE word_token in (';
|
|
||||||
$sSQL .= join(',', array_map('getDBQuoted', $aTokens)).')';
|
|
||||||
|
|
||||||
Debug::printSQL($sSQL);
|
|
||||||
|
|
||||||
$aDBWords = chksql($oDB->getAll($sSQL), 'Could not get word tokens.');
|
|
||||||
|
|
||||||
foreach ($aDBWords as $aWord) {
|
|
||||||
$oToken = null;
|
|
||||||
$iId = (int) $aWord['word_id'];
|
|
||||||
|
|
||||||
if ($aWord['class']) {
|
|
||||||
// Special terms need to appear in their normalized form.
|
|
||||||
if ($aWord['word']) {
|
|
||||||
$sNormWord = $aWord['word'];
|
|
||||||
if ($oNormalizer != null) {
|
|
||||||
$sNormWord = $oNormalizer->transliterate($aWord['word']);
|
|
||||||
}
|
|
||||||
if (strpos($sNormQuery, $sNormWord) === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($aWord['class'] == 'place' && $aWord['type'] == 'house') {
|
|
||||||
$oToken = new Token\HouseNumber($iId, trim($aWord['word_token']));
|
|
||||||
} elseif ($aWord['class'] == 'place' && $aWord['type'] == 'postcode') {
|
|
||||||
if ($aWord['word']
|
|
||||||
&& pg_escape_string($aWord['word']) == $aWord['word']
|
|
||||||
) {
|
|
||||||
$oToken = new Token\Postcode(
|
|
||||||
$iId,
|
|
||||||
$aWord['word'],
|
|
||||||
$aWord['country_code']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// near and in operator the same at the moment
|
|
||||||
$oToken = new Token\SpecialTerm(
|
|
||||||
$iId,
|
|
||||||
$aWord['class'],
|
|
||||||
$aWord['type'],
|
|
||||||
$aWord['operator'] ? Operator::NEAR : Operator::NONE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} elseif ($aWord['country_code']) {
|
|
||||||
// Filter country tokens that do not match restricted countries.
|
|
||||||
if (!$aCountryCodes
|
|
||||||
|| in_array($aWord['country_code'], $aCountryCodes)
|
|
||||||
) {
|
|
||||||
$oToken = new Token\Country($iId, $aWord['country_code']);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$oToken = new Token\Word(
|
|
||||||
$iId,
|
|
||||||
$aWord['word_token'][0] != ' ',
|
|
||||||
(int) $aWord['count']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($oToken) {
|
|
||||||
$this->addToken($aWord['word_token'], $oToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new token for the given word.
|
|
||||||
*
|
|
||||||
* @param string $sWord Word the token describes.
|
|
||||||
* @param object $oToken Token object to add.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addToken($sWord, $oToken)
|
|
||||||
{
|
|
||||||
if (isset($this->aTokens[$sWord])) {
|
|
||||||
$this->aTokens[$sWord][] = $oToken;
|
|
||||||
} else {
|
|
||||||
$this->aTokens[$sWord] = array($oToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugTokenByWordIdList()
|
|
||||||
{
|
|
||||||
$aWordsIDs = array();
|
|
||||||
foreach ($this->aTokens as $sToken => $aWords) {
|
|
||||||
foreach ($aWords as $aToken) {
|
|
||||||
if ($aToken->iId !== null) {
|
|
||||||
$aWordsIDs[$aToken->iId] =
|
|
||||||
'#'.$sToken.'('.$aToken->iId.')#';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aWordsIDs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return $this->aTokens;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim\Token;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A postcode token.
|
|
||||||
*/
|
|
||||||
class Postcode
|
|
||||||
{
|
|
||||||
/// Database word id, if available.
|
|
||||||
public $iId;
|
|
||||||
/// Full nomralized postcode (upper cased).
|
|
||||||
public $sPostcode;
|
|
||||||
// Optional country code the postcode belongs to (currently unused).
|
|
||||||
public $sCountryCode;
|
|
||||||
|
|
||||||
public function __construct($iId, $sPostcode, $sCountryCode = '')
|
|
||||||
{
|
|
||||||
$this->iId = $iId;
|
|
||||||
$this->sPostcode = $sPostcode;
|
|
||||||
$this->sCountryCode = empty($sCountryCode) ? '' : $sCountryCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'ID' => $this->iId,
|
|
||||||
'Type' => 'postcode',
|
|
||||||
'Info' => $this->sPostcode.'('.$this->sCountryCode.')'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim\Token;
|
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/SpecialSearchOperator.php');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A word token describing a place type.
|
|
||||||
*/
|
|
||||||
class SpecialTerm
|
|
||||||
{
|
|
||||||
/// Database word id, if applicable.
|
|
||||||
public $iId;
|
|
||||||
/// Class (or OSM tag key) of the place to look for.
|
|
||||||
public $sClass;
|
|
||||||
/// Type (or OSM tag value) of the place to look for.
|
|
||||||
public $sType;
|
|
||||||
/// Relationship of the operator to the object (see Operator class).
|
|
||||||
public $iOperator;
|
|
||||||
|
|
||||||
public function __construct($iID, $sClass, $sType, $iOperator)
|
|
||||||
{
|
|
||||||
$this->iId = $iID;
|
|
||||||
$this->sClass = $sClass;
|
|
||||||
$this->sType = $sType;
|
|
||||||
$this->iOperator = $iOperator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'ID' => $this->iId,
|
|
||||||
'Type' => 'special term',
|
|
||||||
'Info' => array(
|
|
||||||
'class' => $this->sClass,
|
|
||||||
'type' => $this->sType,
|
|
||||||
'operator' => \Nominatim\Operator::toString($this->iOperator)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Nominatim\Token;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A standard word token.
|
|
||||||
*/
|
|
||||||
class Word
|
|
||||||
{
|
|
||||||
/// Database word id, if applicable.
|
|
||||||
public $iId;
|
|
||||||
/// If true, the word may represent only part of a place name.
|
|
||||||
public $bPartial;
|
|
||||||
/// Number of appearances in the database.
|
|
||||||
public $iSearchNameCount;
|
|
||||||
|
|
||||||
public function __construct($iId, $bPartial, $iSearchNameCount)
|
|
||||||
{
|
|
||||||
$this->iId = $iId;
|
|
||||||
$this->bPartial = $bPartial;
|
|
||||||
$this->iSearchNameCount = $iSearchNameCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function debugInfo()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'ID' => $this->iId,
|
|
||||||
'Type' => 'word',
|
|
||||||
'Info' => array(
|
|
||||||
'partial' => $this->bPartial,
|
|
||||||
'count' => $this->iSearchNameCount
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
193
lib/cmd.php
193
lib/cmd.php
@@ -1,13 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnknown = false)
|
||||||
function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnknown = false)
|
{
|
||||||
{
|
|
||||||
$aQuick = array();
|
$aQuick = array();
|
||||||
$aCounts = array();
|
$aCounts = array();
|
||||||
|
|
||||||
foreach ($aSpec as $aLine) {
|
foreach($aSpec as $aLine)
|
||||||
if (is_array($aLine)) {
|
{
|
||||||
|
if (is_array($aLine))
|
||||||
|
{
|
||||||
if ($aLine[0]) $aQuick['--'.$aLine[0]] = $aLine;
|
if ($aLine[0]) $aQuick['--'.$aLine[0]] = $aLine;
|
||||||
if ($aLine[1]) $aQuick['-'.$aLine[1]] = $aLine;
|
if ($aLine[1]) $aQuick['-'.$aLine[1]] = $aLine;
|
||||||
$aCounts[$aLine[0]] = 0;
|
$aCounts[$aLine[0]] = 0;
|
||||||
@@ -16,30 +17,35 @@ function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnkn
|
|||||||
|
|
||||||
$aResult = array();
|
$aResult = array();
|
||||||
$bUnknown = false;
|
$bUnknown = false;
|
||||||
$iSize = count($aArg);
|
$iSize = sizeof($aArg);
|
||||||
for ($i = 1; $i < $iSize; $i++) {
|
for ($i = 1; $i < $iSize; $i++)
|
||||||
if (isset($aQuick[$aArg[$i]])) {
|
{
|
||||||
|
if (isset($aQuick[$aArg[$i]]))
|
||||||
|
{
|
||||||
$aLine = $aQuick[$aArg[$i]];
|
$aLine = $aQuick[$aArg[$i]];
|
||||||
$aCounts[$aLine[0]]++;
|
$aCounts[$aLine[0]]++;
|
||||||
$xVal = null;
|
$xVal = null;
|
||||||
if ($aLine[4] == $aLine[5]) {
|
if ($aLine[4] == $aLine[5])
|
||||||
if ($aLine[4]) {
|
{
|
||||||
|
if ($aLine[4])
|
||||||
|
{
|
||||||
$xVal = array();
|
$xVal = array();
|
||||||
for ($n = $aLine[4]; $i < $iSize && $n; $n--) {
|
for($n = $aLine[4]; $i < $iSize && $n; $n--)
|
||||||
|
{
|
||||||
$i++;
|
$i++;
|
||||||
if ($i >= $iSize || $aArg[$i][0] == '-') showUsage($aSpec, $bExitOnError, 'Parameter of \''.$aLine[0].'\' is missing');
|
if ($i >= $iSize || $aArg[$i][0] == '-') showUsage($aSpec, $bExitOnError, 'Parameter of \''.$aLine[0].'\' is missing');
|
||||||
|
|
||||||
switch ($aLine[6]) {
|
switch ($aLine[6])
|
||||||
|
{
|
||||||
case 'realpath':
|
case 'realpath':
|
||||||
$xVal[] = realpath($aArg[$i]);
|
$xVal[] = realpath($aArg[$i]);
|
||||||
break;
|
break;
|
||||||
case 'realdir':
|
case 'realdir':
|
||||||
$sPath = realpath(dirname($aArg[$i]));
|
$sPath = realpath(dirname($aArg[$i]));
|
||||||
if ($sPath) {
|
if ($sPath)
|
||||||
$xVal[] = $sPath . '/' . basename($aArg[$i]);
|
$xVal[] = $sPath . '/' . basename($aArg[$i]);
|
||||||
} else {
|
else
|
||||||
$xVal[] = $sPath;
|
$xVal[] = $sPath;
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'bool':
|
case 'bool':
|
||||||
$xVal[] = (bool)$aArg[$i];
|
$xVal[] = (bool)$aArg[$i];
|
||||||
@@ -56,20 +62,29 @@ function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnkn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($aLine[4] == 1) $xVal = $xVal[0];
|
if ($aLine[4] == 1) $xVal = $xVal[0];
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
$xVal = true;
|
$xVal = true;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fail('Variable numbers of params not yet supported');
|
fail('Variable numbers of params not yet supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($aLine[3] > 1) {
|
if ($aLine[3] > 1)
|
||||||
|
{
|
||||||
if (!array_key_exists($aLine[0], $aResult)) $aResult[$aLine[0]] = array();
|
if (!array_key_exists($aLine[0], $aResult)) $aResult[$aLine[0]] = array();
|
||||||
$aResult[$aLine[0]][] = $xVal;
|
$aResult[$aLine[0]][] = $xVal;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
$aResult[$aLine[0]] = $xVal;
|
$aResult[$aLine[0]] = $xVal;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
$bUnknown = $aArg[$i];
|
$bUnknown = $aArg[$i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,11 +92,14 @@ function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnkn
|
|||||||
if (array_key_exists('help', $aResult)) showUsage($aSpec);
|
if (array_key_exists('help', $aResult)) showUsage($aSpec);
|
||||||
if ($bUnknown && $bExitOnUnknown) showUsage($aSpec, $bExitOnError, 'Unknown option \''.$bUnknown.'\'');
|
if ($bUnknown && $bExitOnUnknown) showUsage($aSpec, $bExitOnError, 'Unknown option \''.$bUnknown.'\'');
|
||||||
|
|
||||||
foreach ($aSpec as $aLine) {
|
foreach($aSpec as $aLine)
|
||||||
if (is_array($aLine)) {
|
{
|
||||||
|
if (is_array($aLine))
|
||||||
|
{
|
||||||
if ($aCounts[$aLine[0]] < $aLine[2]) showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is missing');
|
if ($aCounts[$aLine[0]] < $aLine[2]) showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is missing');
|
||||||
if ($aCounts[$aLine[0]] > $aLine[3]) showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is pressent too many times');
|
if ($aCounts[$aLine[0]] > $aLine[3]) showUsage($aSpec, $bExitOnError, 'Option \''.$aLine[0].'\' is pressent too many times');
|
||||||
switch ($aLine[6]) {
|
switch ($aLine[6])
|
||||||
|
{
|
||||||
case 'bool':
|
case 'bool':
|
||||||
if (!array_key_exists($aLine[0], $aResult))
|
if (!array_key_exists($aLine[0], $aResult))
|
||||||
$aResult[$aLine[0]] = false;
|
$aResult[$aLine[0]] = false;
|
||||||
@@ -90,133 +108,38 @@ function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnkn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $bUnknown;
|
return $bUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showUsage($aSpec, $bExit = false, $sError = false)
|
function showUsage($aSpec, $bExit = false, $sError = false)
|
||||||
{
|
{
|
||||||
if ($sError) {
|
if ($sError)
|
||||||
|
{
|
||||||
echo basename($_SERVER['argv'][0]).': '.$sError."\n";
|
echo basename($_SERVER['argv'][0]).': '.$sError."\n";
|
||||||
echo 'Try `'.basename($_SERVER['argv'][0]).' --help` for more information.'."\n";
|
echo 'Try `'.basename($_SERVER['argv'][0]).' --help` for more information.'."\n";
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
echo 'Usage: '.basename($_SERVER['argv'][0])."\n";
|
echo "Usage: ".basename($_SERVER['argv'][0])."\n";
|
||||||
$bFirst = true;
|
$bFirst = true;
|
||||||
foreach ($aSpec as $aLine) {
|
foreach($aSpec as $aLine)
|
||||||
if (is_array($aLine)) {
|
{
|
||||||
if ($bFirst) {
|
if (is_array($aLine))
|
||||||
|
{
|
||||||
|
if ($bFirst)
|
||||||
|
{
|
||||||
$bFirst = false;
|
$bFirst = false;
|
||||||
echo "\n";
|
echo "\n";
|
||||||
}
|
}
|
||||||
$aNames = array();
|
$aNames = array();
|
||||||
if ($aLine[1]) $aNames[] = '-'.$aLine[1];
|
if ($aLine[1]) $aNames[] = '-'.$aLine[1];
|
||||||
if ($aLine[0]) $aNames[] = '--'.$aLine[0];
|
if ($aLine[0]) $aNames[] = '--'.$aLine[0];
|
||||||
$sName = join(', ', $aNames);
|
$sName = join(', ',$aNames);
|
||||||
echo ' '.$sName.str_repeat(' ', 30-strlen($sName)).$aLine[7]."\n";
|
echo ' '.$sName.str_repeat(' ',30-strlen($sName)).$aLine[7]."\n";
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
echo $aLine."\n";
|
echo $aLine."\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo "\n";
|
echo "\n";
|
||||||
exit;
|
exit;
|
||||||
}
|
|
||||||
|
|
||||||
function chksql($oSql, $sMsg = false)
|
|
||||||
{
|
|
||||||
if (PEAR::isError($oSql)) {
|
|
||||||
fail($sMsg || $oSql->getMessage(), $oSql->userinfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $oSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
function info($sMsg)
|
|
||||||
{
|
|
||||||
echo date('Y-m-d H:i:s == ').$sMsg."\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$aWarnings = array();
|
|
||||||
|
|
||||||
|
|
||||||
function warn($sMsg)
|
|
||||||
{
|
|
||||||
$GLOBALS['aWarnings'][] = $sMsg;
|
|
||||||
echo date('Y-m-d H:i:s == ').'WARNING: '.$sMsg."\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function repeatWarnings()
|
|
||||||
{
|
|
||||||
foreach ($GLOBALS['aWarnings'] as $sMsg) {
|
|
||||||
echo ' * ',$sMsg."\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function runSQLScript($sScript, $bfatal = true, $bVerbose = false, $bIgnoreErrors = false)
|
|
||||||
{
|
|
||||||
// Convert database DSN to psql parameters
|
|
||||||
$aDSNInfo = DB::parseDSN(CONST_Database_DSN);
|
|
||||||
if (!isset($aDSNInfo['port']) || !$aDSNInfo['port']) $aDSNInfo['port'] = 5432;
|
|
||||||
$sCMD = 'psql -p '.$aDSNInfo['port'].' -d '.$aDSNInfo['database'];
|
|
||||||
if (isset($aDSNInfo['hostspec']) && $aDSNInfo['hostspec']) {
|
|
||||||
$sCMD .= ' -h ' . $aDSNInfo['hostspec'];
|
|
||||||
}
|
|
||||||
if (isset($aDSNInfo['username']) && $aDSNInfo['username']) {
|
|
||||||
$sCMD .= ' -U ' . $aDSNInfo['username'];
|
|
||||||
}
|
|
||||||
$aProcEnv = null;
|
|
||||||
if (isset($aDSNInfo['password']) && $aDSNInfo['password']) {
|
|
||||||
$aProcEnv = array_merge(array('PGPASSWORD' => $aDSNInfo['password']), $_ENV);
|
|
||||||
}
|
|
||||||
if (!$bVerbose) {
|
|
||||||
$sCMD .= ' -q';
|
|
||||||
}
|
|
||||||
if ($bfatal && !$bIgnoreErrors) {
|
|
||||||
$sCMD .= ' -v ON_ERROR_STOP=1';
|
|
||||||
}
|
|
||||||
$aDescriptors = array(
|
|
||||||
0 => array('pipe', 'r'),
|
|
||||||
1 => STDOUT,
|
|
||||||
2 => STDERR
|
|
||||||
);
|
|
||||||
$ahPipes = null;
|
|
||||||
$hProcess = @proc_open($sCMD, $aDescriptors, $ahPipes, null, $aProcEnv);
|
|
||||||
if (!is_resource($hProcess)) {
|
|
||||||
fail('unable to start pgsql');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$bVerbose) {
|
|
||||||
fwrite($ahPipes[0], 'set client_min_messages to WARNING;');
|
|
||||||
}
|
|
||||||
|
|
||||||
while (strlen($sScript)) {
|
|
||||||
$iWritten = fwrite($ahPipes[0], $sScript);
|
|
||||||
if ($iWritten <= 0) break;
|
|
||||||
$sScript = substr($sScript, $iWritten);
|
|
||||||
}
|
|
||||||
fclose($ahPipes[0]);
|
|
||||||
$iReturn = proc_close($hProcess);
|
|
||||||
if ($bfatal && $iReturn > 0) {
|
|
||||||
fail("pgsql returned with error code ($iReturn)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function runWithEnv($sCmd, $aEnv)
|
|
||||||
{
|
|
||||||
$aFDs = array(
|
|
||||||
0 => array('pipe', 'r'),
|
|
||||||
1 => STDOUT,
|
|
||||||
2 => STDERR
|
|
||||||
);
|
|
||||||
$aPipes = null;
|
|
||||||
$hProc = @proc_open($sCmd, $aFDs, $aPipes, null, $aEnv);
|
|
||||||
if (!is_resource($hProc)) {
|
|
||||||
fail('unable to run command:' . $sCmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($aPipes[0]); // no stdin
|
|
||||||
|
|
||||||
$iStat = proc_close($hProc);
|
|
||||||
return $iStat;
|
|
||||||
}
|
|
||||||
|
|||||||
47
lib/db.php
47
lib/db.php
@@ -1,43 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
require_once('DB.php');
|
||||||
|
|
||||||
require_once('DB.php');
|
function &getDB($bNew = false, $bPersistent = false)
|
||||||
|
{
|
||||||
|
|
||||||
function &getDB($bNew = false, $bPersistent = false)
|
|
||||||
{
|
|
||||||
// Get the database object
|
// Get the database object
|
||||||
$oDB = chksql(
|
$oDB =& DB::connect(CONST_Database_DSN.($bNew?'?new_link=true':''), $bPersistent);
|
||||||
DB::connect(CONST_Database_DSN.($bNew?'?new_link=true':''), $bPersistent),
|
if (PEAR::IsError($oDB))
|
||||||
'Failed to establish database connection'
|
{
|
||||||
);
|
var_dump(CONST_Database_DSN);
|
||||||
|
var_Dump($oDB);
|
||||||
|
fail($oDB->getMessage());
|
||||||
|
}
|
||||||
$oDB->setFetchMode(DB_FETCHMODE_ASSOC);
|
$oDB->setFetchMode(DB_FETCHMODE_ASSOC);
|
||||||
$oDB->query("SET DateStyle TO 'sql,european'");
|
$oDB->query("SET DateStyle TO 'sql,european'");
|
||||||
$oDB->query("SET client_encoding TO 'utf-8'");
|
$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;
|
return $oDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDBQuoted($s)
|
function getDBQuoted($s)
|
||||||
{
|
{
|
||||||
return "'".pg_escape_string($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]);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,28 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if (file_exists(getenv('NOMINATIM_SETTINGS')))
|
||||||
require_once('init.php');
|
{
|
||||||
require_once('cmd.php');
|
require_once(getenv('NOMINATIM_SETTINGS'));
|
||||||
require_once('DebugNone.php');
|
|
||||||
|
|
||||||
// handle http proxy when using file_get_contents
|
|
||||||
if (CONST_HTTP_Proxy) {
|
|
||||||
$proxy = 'tcp://' . CONST_HTTP_Proxy_Host . ':' . CONST_HTTP_Proxy_Port;
|
|
||||||
$aHeaders = array();
|
|
||||||
if (CONST_HTTP_Proxy_Login != null && CONST_HTTP_Proxy_Login != '' && CONST_HTTP_Proxy_Password != null && CONST_HTTP_Proxy_Password != '') {
|
|
||||||
$auth = base64_encode(CONST_HTTP_Proxy_Login . ':' . CONST_HTTP_Proxy_Password);
|
|
||||||
$aHeaders = array("Proxy-Authorization: Basic $auth");
|
|
||||||
}
|
}
|
||||||
$aContext = array(
|
|
||||||
'http' => array(
|
require_once('init.php');
|
||||||
'proxy' => $proxy,
|
require_once('cmd.php');
|
||||||
'request_fulluri' => true,
|
|
||||||
'header' => $aHeaders
|
|
||||||
),
|
|
||||||
'https' => array(
|
|
||||||
'proxy' => $proxy,
|
|
||||||
'request_fulluri' => true,
|
|
||||||
'header' => $aHeaders
|
|
||||||
)
|
|
||||||
);
|
|
||||||
stream_context_set_default($aContext);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,101 +1,64 @@
|
|||||||
<?php
|
<?php
|
||||||
|
require_once('init.php');
|
||||||
|
|
||||||
require_once('init.php');
|
if (CONST_NoAccessControl)
|
||||||
require_once('ParameterParser.php');
|
{
|
||||||
require_once(CONST_Debug ? 'DebugHtml.php' : 'DebugNone.php');
|
header("Access-Control-Allow-Origin: *");
|
||||||
|
header("Access-Control-Allow-Methods: OPTIONS,GET");
|
||||||
|
if (!empty($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
|
||||||
|
{
|
||||||
|
header("Access-Control-Allow-Headers: ".$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') exit;
|
||||||
|
|
||||||
/***************************************************************************
|
if (CONST_ClosedForIndexing && strpos(CONST_ClosedForIndexingExceptionIPs, ','.$_SERVER["REMOTE_ADDR"].',') === false)
|
||||||
*
|
{
|
||||||
* Error handling functions
|
echo "Closed for re-indexing...";
|
||||||
*
|
exit;
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
$aBucketKeys = array();
|
||||||
|
|
||||||
function chksql($oSql, $sMsg = 'Database request failed')
|
if (isset($_SERVER["HTTP_REFERER"])) $aBucketKeys[] = str_replace('www.','',strtolower(parse_url($_SERVER["HTTP_REFERER"], PHP_URL_HOST)));
|
||||||
{
|
if (isset($_SERVER["REMOTE_ADDR"])) $aBucketKeys[] = $_SERVER["REMOTE_ADDR"];
|
||||||
if (!PEAR::isError($oSql)) return $oSql;
|
if (isset($_GET["email"])) $aBucketKeys[] = $_GET["email"];
|
||||||
|
|
||||||
|
$fBucketVal = doBucket($aBucketKeys,
|
||||||
|
(defined('CONST_ConnectionBucket_PageType')?constant('CONST_ConnectionBucket_Cost_'.CONST_ConnectionBucket_PageType):1) + user_busy_cost(),
|
||||||
|
CONST_ConnectionBucket_LeakRate, CONST_ConnectionBucket_BlockLimit);
|
||||||
|
|
||||||
|
if ($fBucketVal > CONST_ConnectionBucket_WaitLimit && $fBucketVal < CONST_ConnectionBucket_BlockLimit)
|
||||||
|
{
|
||||||
|
$m = getBucketMemcache();
|
||||||
|
$iCurrentSleeping = $m->increment('sleepCounter');
|
||||||
|
if (false === $iCurrentSleeping)
|
||||||
|
{
|
||||||
|
$m->add('sleepCounter', 0);
|
||||||
|
$iCurrentSleeping = $m->increment('sleepCounter');
|
||||||
|
}
|
||||||
|
if ($iCurrentSleeping >= CONST_ConnectionBucket_MaxSleeping || isBucketSleeping($aBucketKeys))
|
||||||
|
{
|
||||||
|
// Too many threads sleeping already. This becomes a hard block.
|
||||||
|
$fBucketVal = doBucket($aBucketKeys, CONST_ConnectionBucket_BlockLimit, CONST_ConnectionBucket_LeakRate, CONST_ConnectionBucket_BlockLimit);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setBucketSleeping($aBucketKeys, true);
|
||||||
|
sleep(($fBucketVal - CONST_ConnectionBucket_WaitLimit)/CONST_ConnectionBucket_LeakRate);
|
||||||
|
$fBucketVal = doBucket($aBucketKeys, CONST_ConnectionBucket_LeakRate, CONST_ConnectionBucket_LeakRate, CONST_ConnectionBucket_BlockLimit);
|
||||||
|
setBucketSleeping($aBucketKeys, false);
|
||||||
|
}
|
||||||
|
$m->decrement('sleepCounter');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos(CONST_BlockedIPs, ','.$_SERVER["REMOTE_ADDR"].',') !== false || $fBucketVal >= CONST_ConnectionBucket_BlockLimit)
|
||||||
|
{
|
||||||
|
header("HTTP/1.0 429 Too Many Requests");
|
||||||
|
echo "Your IP has been blocked. \n";
|
||||||
|
echo CONST_BlockMessage;
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
header('HTTP/1.0 500 Internal Server Error');
|
|
||||||
header('Content-type: text/html; charset=utf-8');
|
header('Content-type: text/html; charset=utf-8');
|
||||||
|
|
||||||
$sSqlError = $oSql->getMessage();
|
|
||||||
|
|
||||||
echo <<<INTERNALFAIL
|
|
||||||
<html>
|
|
||||||
<head><title>Internal Server Error</title></head>
|
|
||||||
<body>
|
|
||||||
<h1>Internal Server Error</h1>
|
|
||||||
<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;
|
|
||||||
|
|
||||||
if (CONST_Debug) {
|
|
||||||
var_dump($oSql);
|
|
||||||
} else {
|
|
||||||
echo "<pre>\n".$oSql->getUserInfo().'</pre>';
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* HTTP Reply header setup
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (CONST_NoAccessControl) {
|
|
||||||
header('Access-Control-Allow-Origin: *');
|
|
||||||
header('Access-Control-Allow-Methods: OPTIONS,GET');
|
|
||||||
if (!empty($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
|
||||||
header('Access-Control-Allow-Headers: '.$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') exit;
|
|
||||||
|
|
||||||
if (CONST_Debug) header('Content-type: text/html; charset=utf-8');
|
|
||||||
|
|||||||
13
lib/init.php
13
lib/init.php
@@ -1,9 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once(CONST_BasePath.'/lib/lib.php');
|
@define('CONST_BasePath', dirname(dirname(__FILE__)));
|
||||||
require_once(CONST_BasePath.'/lib/db.php');
|
|
||||||
|
|
||||||
if (get_magic_quotes_gpc()) {
|
require_once(CONST_BasePath.'/settings/settings.php');
|
||||||
|
require_once(CONST_BasePath.'/lib/lib.php');
|
||||||
|
require_once(CONST_BasePath.'/lib/leakybucket.php');
|
||||||
|
require_once(CONST_BasePath.'/lib/db.php');
|
||||||
|
|
||||||
|
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";
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|||||||
168
lib/leakybucket.php
Normal file
168
lib/leakybucket.php
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function getBucketMemcache()
|
||||||
|
{
|
||||||
|
static $m;
|
||||||
|
|
||||||
|
if (!CONST_ConnectionBucket_MemcacheServerAddress) return null;
|
||||||
|
if (!isset($m))
|
||||||
|
{
|
||||||
|
$m = new Memcached();
|
||||||
|
$m->addServer(CONST_ConnectionBucket_MemcacheServerAddress, CONST_ConnectionBucket_MemcacheServerPort);
|
||||||
|
}
|
||||||
|
return $m;
|
||||||
|
}
|
||||||
|
|
||||||
|
function doBucket($asKey, $iRequestCost, $iLeakPerSecond, $iThreshold)
|
||||||
|
{
|
||||||
|
$m = getBucketMemcache();
|
||||||
|
if (!$m) return 0;
|
||||||
|
|
||||||
|
$iMaxVal = 0;
|
||||||
|
$t = time();
|
||||||
|
|
||||||
|
foreach($asKey as $sKey)
|
||||||
|
{
|
||||||
|
$aCurrentBlock = $m->get($sKey);
|
||||||
|
if (!$aCurrentBlock)
|
||||||
|
{
|
||||||
|
$aCurrentBlock = array($iRequestCost, $t, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// add RequestCost
|
||||||
|
// remove leak * the time since the last request
|
||||||
|
$aCurrentBlock[0] += $iRequestCost - ($t - $aCurrentBlock[1])*$iLeakPerSecond;
|
||||||
|
$aCurrentBlock[1] = $t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aCurrentBlock[0] <= 0)
|
||||||
|
{
|
||||||
|
$m->delete($sKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we have hit the threshold stop and record this to the block list
|
||||||
|
if ($aCurrentBlock[0] >= $iThreshold)
|
||||||
|
{
|
||||||
|
$aCurrentBlock[0] = $iThreshold;
|
||||||
|
|
||||||
|
// Make up to 10 attempts to record this to memcache (with locking to prevent conflicts)
|
||||||
|
$i = 10;
|
||||||
|
for($i = 0; $i < 10; $i++)
|
||||||
|
{
|
||||||
|
$aBlockedList = $m->get('blockedList', null, $hCasToken);
|
||||||
|
if (!$aBlockedList)
|
||||||
|
{
|
||||||
|
$aBlockedList = array();
|
||||||
|
$m->add('blockedList', $aBlockedList);
|
||||||
|
$aBlockedList = $m->get('blockedList', null, $hCasToken);
|
||||||
|
}
|
||||||
|
if (!isset($aBlockedList[$sKey]))
|
||||||
|
{
|
||||||
|
$aBlockedList[$sKey] = array(1, $t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$aBlockedList[$sKey][0]++;
|
||||||
|
$aBlockedList[$sKey][1] = $t;
|
||||||
|
}
|
||||||
|
if (sizeof($aBlockedList) > CONST_ConnectionBucket_MaxBlockList)
|
||||||
|
{
|
||||||
|
uasort($aBlockedList, 'byValue1');
|
||||||
|
$aBlockedList = array_slice($aBlockedList, 0, CONST_ConnectionBucket_MaxBlockList);
|
||||||
|
}
|
||||||
|
$x = $m->cas($hCasToken, 'blockedList', $aBlockedList);
|
||||||
|
if ($x) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only keep in memcache until the time it would have expired (to avoid clutering memcache)
|
||||||
|
$m->set($sKey, $aCurrentBlock, $t + 1 + $aCurrentBlock[0]/$iLeakPerSecond);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bucket result in the largest bucket we find
|
||||||
|
$iMaxVal = max($iMaxVal, $aCurrentBlock[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $iMaxVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBucketSleeping($asKey)
|
||||||
|
{
|
||||||
|
$m = getBucketMemcache();
|
||||||
|
if (!$m) return false;
|
||||||
|
|
||||||
|
foreach($asKey as $sKey)
|
||||||
|
{
|
||||||
|
$aCurrentBlock = $m->get($sKey);
|
||||||
|
if ($aCurrentBlock[2]) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBucketSleeping($asKey, $bVal)
|
||||||
|
{
|
||||||
|
$m = getBucketMemcache();
|
||||||
|
if (!$m) return false;
|
||||||
|
|
||||||
|
$iMaxVal = 0;
|
||||||
|
$t = time();
|
||||||
|
|
||||||
|
foreach($asKey as $sKey)
|
||||||
|
{
|
||||||
|
$aCurrentBlock = $m->get($sKey);
|
||||||
|
$aCurrentBlock[2] = $bVal;
|
||||||
|
$m->set($sKey, $aCurrentBlock, $t + 1 + $aCurrentBlock[0]/CONST_ConnectionBucket_LeakRate);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function byValue1($a, $b)
|
||||||
|
{
|
||||||
|
if ($a[1] == $b[1])
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ($a[1] > $b[1]) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function byLastBlockTime($a, $b)
|
||||||
|
{
|
||||||
|
if ($a['lastBlockTimestamp'] == $b['lastBlockTimestamp'])
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ($a['lastBlockTimestamp'] > $b['lastBlockTimestamp']) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBucketBlocks()
|
||||||
|
{
|
||||||
|
$m = getBucketMemcache();
|
||||||
|
if (!$m) return null;
|
||||||
|
$t = time();
|
||||||
|
$aBlockedList = $m->get('blockedList', null, $hCasToken);
|
||||||
|
if (!$aBlockedList) $aBlockedList = array();
|
||||||
|
foreach($aBlockedList as $sKey => $aDetails)
|
||||||
|
{
|
||||||
|
$aCurrentBlock = $m->get($sKey);
|
||||||
|
if (!$aCurrentBlock) $aCurrentBlock = array(0, $t);
|
||||||
|
$iCurrentBucketSize = max(0, $aCurrentBlock[0] - ($t - $aCurrentBlock[1])*CONST_ConnectionBucket_LeakRate);
|
||||||
|
$aBlockedList[$sKey] = array(
|
||||||
|
'totalBlocks' => $aDetails[0],
|
||||||
|
'lastBlockTimestamp' => $aDetails[1],
|
||||||
|
'isSleeping' => (isset($aCurrentBlock[2])?$aCurrentBlock[2]:false),
|
||||||
|
'currentBucketSize' => $iCurrentBucketSize,
|
||||||
|
'currentlyBlocked' => $iCurrentBucketSize + (CONST_ConnectionBucket_Cost_Reverse) >= CONST_ConnectionBucket_BlockLimit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
uasort($aBlockedList, 'byLastBlockTime');
|
||||||
|
return $aBlockedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearBucketBlocks()
|
||||||
|
{
|
||||||
|
$m = getBucketMemcache();
|
||||||
|
if (!$m) return false;
|
||||||
|
$m->delete('blockedList');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
1092
lib/lib.php
1092
lib/lib.php
File diff suppressed because it is too large
Load Diff
110
lib/log.php
110
lib/log.php
@@ -1,57 +1,69 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
function logStart(&$oDB, $sType = '', $sQuery = '', $aLanguageList = array())
|
||||||
function logStart(&$oDB, $sType = '', $sQuery = '', $aLanguageList = array())
|
{
|
||||||
{
|
$aStartTime = explode('.',microtime(true));
|
||||||
$fStartTime = microtime(true);
|
|
||||||
$aStartTime = explode('.', $fStartTime);
|
|
||||||
if (!isset($aStartTime[1])) $aStartTime[1] = '0';
|
if (!isset($aStartTime[1])) $aStartTime[1] = '0';
|
||||||
|
|
||||||
$sOutputFormat = '';
|
$sOutputFormat = '';
|
||||||
if (isset($_GET['format'])) $sOutputFormat = $_GET['format'];
|
if (isset($_GET['format'])) $sOutputFormat = $_GET['format'];
|
||||||
|
|
||||||
if ($sType == 'reverse') {
|
|
||||||
$sOutQuery = (isset($_GET['lat'])?$_GET['lat']:'').'/';
|
|
||||||
if (isset($_GET['lon'])) $sOutQuery .= $_GET['lon'];
|
|
||||||
if (isset($_GET['zoom'])) $sOutQuery .= '/'.$_GET['zoom'];
|
|
||||||
} else {
|
|
||||||
$sOutQuery = $sQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
$hLog = array(
|
$hLog = array(
|
||||||
date('Y-m-d H:i:s', $aStartTime[0]).'.'.$aStartTime[1],
|
date('Y-m-d H:i:s',$aStartTime[0]).'.'.$aStartTime[1],
|
||||||
$_SERVER['REMOTE_ADDR'],
|
$_SERVER["REMOTE_ADDR"],
|
||||||
$_SERVER['QUERY_STRING'],
|
$_SERVER['QUERY_STRING'],
|
||||||
$sOutQuery,
|
$sQuery
|
||||||
$sType,
|
|
||||||
$fStartTime
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (CONST_Log_DB) {
|
if (CONST_Log_DB)
|
||||||
if (isset($_GET['email']))
|
{
|
||||||
$sUserAgent = $_GET['email'];
|
// Log
|
||||||
elseif (isset($_SERVER['HTTP_REFERER']))
|
if ($sType == 'search')
|
||||||
$sUserAgent = $_SERVER['HTTP_REFERER'];
|
{
|
||||||
elseif (isset($_SERVER['HTTP_USER_AGENT']))
|
$oDB->query('insert into query_log values ('.getDBQuoted($hLog[0]).','.getDBQuoted($hLog[3]).','.getDBQuoted($hLog[1]).')');
|
||||||
$sUserAgent = $_SERVER['HTTP_USER_AGENT'];
|
}
|
||||||
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)';
|
||||||
$sSQL .= ' values ('.getDBQuoted($sType).','.getDBQuoted($hLog[0]).','.getDBQuoted($hLog[2]);
|
$sSQL .= ' values ('.getDBQuoted($sType).','.getDBQuoted($hLog[0]).','.getDBQuoted($hLog[2]);
|
||||||
$sSQL .= ','.getDBQuoted($hLog[1]).','.getDBQuoted($sUserAgent).','.getDBQuoted(join(',', $aLanguageList)).','.getDBQuoted($sOutputFormat).','.getDBQuoted($hLog[3]).')';
|
$sSQL .= ','.getDBQuoted($hLog[1]).','.getDBQuoted($_SERVER['HTTP_USER_AGENT']).','.getDBQuoted(join(',',$aLanguageList)).','.getDBQuoted($sOutputFormat).')';
|
||||||
$oDB->query($sSQL);
|
$oDB->query($sSQL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CONST_Log_File && CONST_Log_File_ReverseLog != '')
|
||||||
|
{
|
||||||
|
if ($sType == 'reverse')
|
||||||
|
{
|
||||||
|
$aStartTime = explode('.',$hLog[0]);
|
||||||
|
file_put_contents(CONST_Log_File_ReverseLog,
|
||||||
|
$aStartTime[0].','.$aStartTime[1].','.
|
||||||
|
php_uname('n').','.
|
||||||
|
'"'.addslashes(isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'').'",'.
|
||||||
|
'"'.addslashes($hLog[1]).'",'.
|
||||||
|
$_GET['lat'].','.
|
||||||
|
$_GET['lon'].','.
|
||||||
|
$_GET['zoom'].','.
|
||||||
|
'"'.addslashes($_SERVER['HTTP_USER_AGENT']).'",'.
|
||||||
|
'"'.addslashes($sOutputFormat).'"'."\n",
|
||||||
|
FILE_APPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $hLog;
|
return $hLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
function logEnd(&$oDB, $hLog, $iNumResults)
|
function logEnd(&$oDB, $hLog, $iNumResults)
|
||||||
{
|
{
|
||||||
$fEndTime = microtime(true);
|
$aEndTime = explode('.',microtime(true));
|
||||||
|
|
||||||
if (CONST_Log_DB) {
|
|
||||||
$aEndTime = explode('.', $fEndTime);
|
|
||||||
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];
|
||||||
|
|
||||||
|
if (CONST_Log_DB)
|
||||||
|
{
|
||||||
|
$sSQL = 'update query_log set endtime = '.getDBQuoted($sEndTime).', results = '.$iNumResults;
|
||||||
|
$sSQL .= ' where starttime = '.getDBQuoted($hLog[0]);
|
||||||
|
$sSQL .= ' and ipaddress = '.getDBQuoted($hLog[1]);
|
||||||
|
$sSQL .= ' and query = '.getDBQuoted($hLog[3]);
|
||||||
|
$oDB->query($sSQL);
|
||||||
|
|
||||||
$sSQL = 'update new_query_log set endtime = '.getDBQuoted($sEndTime).', results = '.$iNumResults;
|
$sSQL = 'update new_query_log set endtime = '.getDBQuoted($sEndTime).', results = '.$iNumResults;
|
||||||
$sSQL .= ' where starttime = '.getDBQuoted($hLog[0]);
|
$sSQL .= ' where starttime = '.getDBQuoted($hLog[0]);
|
||||||
@@ -60,15 +72,19 @@ function logEnd(&$oDB, $hLog, $iNumResults)
|
|||||||
$oDB->query($sSQL);
|
$oDB->query($sSQL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONST_Log_File) {
|
if (CONST_Log_File && CONST_Log_File_SearchLog != '')
|
||||||
$aOutdata = sprintf(
|
{
|
||||||
"[%s] %.4f %d %s \"%s\"\n",
|
$aStartTime = explode('.',$hLog[0]);
|
||||||
$hLog[0],
|
file_put_contents(CONST_Log_File_SearchLog,
|
||||||
$fEndTime-$hLog[5],
|
$aStartTime[0].','.$aStartTime[1].','.
|
||||||
$iNumResults,
|
php_uname('n').','.
|
||||||
$hLog[4],
|
'"'.addslashes(isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'').'",'.
|
||||||
$hLog[2]
|
'"'.addslashes($hLog[1]).'",'.
|
||||||
);
|
'"'.addslashes($hLog[3]).'",'.
|
||||||
file_put_contents(CONST_Log_File, $aOutdata, FILE_APPEND | LOCK_EX);
|
'"'.addslashes($_SERVER['HTTP_USER_AGENT']).'",'.
|
||||||
|
'"'.addslashes((isset($_GET['format']))?$_GET['format']:'').'",'.
|
||||||
|
$iNumResults."\n",
|
||||||
|
FILE_APPEND);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
function formatOSMType($sType, $bIncludeExternal = true)
|
|
||||||
{
|
|
||||||
if ($sType == 'N') return 'node';
|
|
||||||
if ($sType == 'W') return 'way';
|
|
||||||
if ($sType == 'R') return 'relation';
|
|
||||||
|
|
||||||
if (!$bIncludeExternal) return '';
|
|
||||||
|
|
||||||
if ($sType == 'T') return 'way';
|
|
||||||
if ($sType == 'I') return 'way';
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function osmLink($aFeature, $sRefText = false)
|
|
||||||
{
|
|
||||||
$sOSMType = formatOSMType($aFeature['osm_type'], false);
|
|
||||||
if ($sOSMType) {
|
|
||||||
return '<a href="//www.openstreetmap.org/'.$sOSMType.'/'.$aFeature['osm_id'].'">'.$sOSMType.' '.($sRefText?$sRefText:$aFeature['osm_id']).'</a>';
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function wikipediaLink($aFeature)
|
|
||||||
{
|
|
||||||
if ($aFeature['wikipedia']) {
|
|
||||||
list($sLanguage, $sArticle) = explode(':', $aFeature['wikipedia']);
|
|
||||||
return '<a href="https://'.$sLanguage.'.wikipedia.org/wiki/'.urlencode($sArticle).'" target="_blank">'.$aFeature['wikipedia'].'</a>';
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function detailsLink($aFeature, $sTitle = false)
|
|
||||||
{
|
|
||||||
if (!$aFeature['place_id']) return '';
|
|
||||||
|
|
||||||
return '<a href="details.php?place_id='.$aFeature['place_id'].'">'.($sTitle?$sTitle:$aFeature['place_id']).'</a>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function detailsPermaLink($aFeature, $sRefText = false)
|
|
||||||
{
|
|
||||||
$sOSMType = formatOSMType($aFeature['osm_type'], false);
|
|
||||||
|
|
||||||
if ($sOSMType) {
|
|
||||||
$sLabel = $sRefText ? $sRefText : $sOSMType.' '.$aFeature['osm_id'];
|
|
||||||
return '<a href="details.php?osmtype='.$aFeature['osm_type'].'&osmid='.$aFeature['osm_id'].'&class='.$aFeature['class'].'">'.$sLabel.'</a>';
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// https://github.com/geocoders/geocodejson-spec/
|
|
||||||
|
|
||||||
$aFilteredPlaces = array();
|
|
||||||
|
|
||||||
if (empty($aPlace)) {
|
|
||||||
if (isset($sError))
|
|
||||||
$aFilteredPlaces['error'] = $sError;
|
|
||||||
else $aFilteredPlaces['error'] = 'Unable to geocode';
|
|
||||||
javascript_renderData($aFilteredPlaces);
|
|
||||||
} else {
|
|
||||||
$aFilteredPlaces = array(
|
|
||||||
'type' => 'Feature',
|
|
||||||
'properties' => array(
|
|
||||||
'geocoding' => array()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isset($aPlace['place_id'])) $aFilteredPlaces['properties']['geocoding']['place_id'] = $aPlace['place_id'];
|
|
||||||
$sOSMType = formatOSMType($aPlace['osm_type']);
|
|
||||||
if ($sOSMType) {
|
|
||||||
$aFilteredPlaces['properties']['geocoding']['osm_type'] = $sOSMType;
|
|
||||||
$aFilteredPlaces['properties']['geocoding']['osm_id'] = $aPlace['osm_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['geocoding']['type'] = $aPlace['type'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['geocoding']['accuracy'] = (int) $fDistance;
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['geocoding']['label'] = $aPlace['langaddress'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['geocoding']['name'] = $aPlace['placename'];
|
|
||||||
|
|
||||||
if (isset($aPlace['address'])) {
|
|
||||||
$aFieldMappings = array(
|
|
||||||
'house_number' => 'housenumber',
|
|
||||||
'road' => 'street',
|
|
||||||
'locality' => 'locality',
|
|
||||||
'postcode' => 'postcode',
|
|
||||||
'city' => 'city',
|
|
||||||
'district' => 'district',
|
|
||||||
'county' => 'county',
|
|
||||||
'state' => 'state',
|
|
||||||
'country' => 'country'
|
|
||||||
);
|
|
||||||
|
|
||||||
$aAddressNames = $aPlace['address']->getAddressNames();
|
|
||||||
foreach ($aFieldMappings as $sFrom => $sTo) {
|
|
||||||
if (isset($aAddressNames[$sFrom])) {
|
|
||||||
$aFilteredPlaces['properties']['geocoding'][$sTo] = $aAddressNames[$sFrom];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['geocoding']['admin']
|
|
||||||
= $aPlace['address']->getAdminLevels();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['asgeojson'])) {
|
|
||||||
$aFilteredPlaces['geometry'] = json_decode($aPlace['asgeojson']);
|
|
||||||
} else {
|
|
||||||
$aFilteredPlaces['geometry'] = array(
|
|
||||||
'type' => 'Point',
|
|
||||||
'coordinates' => array(
|
|
||||||
(float) $aPlace['lon'],
|
|
||||||
(float) $aPlace['lat']
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
javascript_renderData(array(
|
|
||||||
'type' => 'FeatureCollection',
|
|
||||||
'geocoding' => array(
|
|
||||||
'version' => '0.1.0',
|
|
||||||
'attribution' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
|
||||||
'licence' => 'ODbL',
|
|
||||||
'query' => $sQuery
|
|
||||||
),
|
|
||||||
'features' => array($aFilteredPlaces)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$aFilteredPlaces = array();
|
|
||||||
|
|
||||||
if (empty($aPlace)) {
|
|
||||||
if (isset($sError))
|
|
||||||
$aFilteredPlaces['error'] = $sError;
|
|
||||||
else $aFilteredPlaces['error'] = 'Unable to geocode';
|
|
||||||
javascript_renderData($aFilteredPlaces);
|
|
||||||
} else {
|
|
||||||
$aFilteredPlaces = array(
|
|
||||||
'type' => 'Feature',
|
|
||||||
'properties' => array()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isset($aPlace['place_id'])) $aFilteredPlaces['properties']['place_id'] = $aPlace['place_id'];
|
|
||||||
$sOSMType = formatOSMType($aPlace['osm_type']);
|
|
||||||
if ($sOSMType) {
|
|
||||||
$aFilteredPlaces['properties']['osm_type'] = $sOSMType;
|
|
||||||
$aFilteredPlaces['properties']['osm_id'] = $aPlace['osm_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['place_rank'] = $aPlace['rank_search'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['category'] = $aPlace['class'];
|
|
||||||
$aFilteredPlaces['properties']['type'] = $aPlace['type'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['importance'] = $aPlace['importance'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['addresstype'] = strtolower($aPlace['addresstype']);
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['name'] = $aPlace['placename'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['properties']['display_name'] = $aPlace['langaddress'];
|
|
||||||
|
|
||||||
if (isset($aPlace['address'])) {
|
|
||||||
$aFilteredPlaces['properties']['address'] = $aPlace['address']->getAddressNames();
|
|
||||||
}
|
|
||||||
if (isset($aPlace['sExtraTags'])) $aFilteredPlaces['properties']['extratags'] = $aPlace['sExtraTags'];
|
|
||||||
if (isset($aPlace['sNameDetails'])) $aFilteredPlaces['properties']['namedetails'] = $aPlace['sNameDetails'];
|
|
||||||
|
|
||||||
if (isset($aPlace['aBoundingBox'])) {
|
|
||||||
$aFilteredPlaces['bbox'] = array(
|
|
||||||
(float) $aPlace['aBoundingBox'][2], // minlon
|
|
||||||
(float) $aPlace['aBoundingBox'][0], // minlat
|
|
||||||
(float) $aPlace['aBoundingBox'][3], // maxlon
|
|
||||||
(float) $aPlace['aBoundingBox'][1] // maxlat
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['asgeojson'])) {
|
|
||||||
$aFilteredPlaces['geometry'] = json_decode($aPlace['asgeojson']);
|
|
||||||
} else {
|
|
||||||
$aFilteredPlaces['geometry'] = array(
|
|
||||||
'type' => 'Point',
|
|
||||||
'coordinates' => array(
|
|
||||||
(float) $aPlace['lon'],
|
|
||||||
(float) $aPlace['lat']
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
javascript_renderData(array(
|
|
||||||
'type' => 'FeatureCollection',
|
|
||||||
'licence' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
|
||||||
'features' => array($aFilteredPlaces)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
<?php
|
|
||||||
header("content-type: text/html; charset=UTF-8");
|
|
||||||
?>
|
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-header.php'); ?>
|
|
||||||
<link href="css/common.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="css/search.css" rel="stylesheet" type="text/css" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body id="reverse-page">
|
|
||||||
|
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-top-navigation.php'); ?>
|
|
||||||
|
|
||||||
<form class="form-inline" role="search" accept-charset="UTF-8" action="<?php echo CONST_Website_BaseURL; ?>reverse.php">
|
|
||||||
<div class="form-group">
|
|
||||||
<input name="format" type="hidden" value="html">
|
|
||||||
lat
|
|
||||||
<input name="lat" type="text" class="form-control input-sm" placeholder="latitude" value="<?php echo $fLat; ?>" >
|
|
||||||
<a href="#" class="btn btn-default btn-xs" id="switch-coords" title="switch lat and lon"><></a>
|
|
||||||
lon
|
|
||||||
<input name="lon" type="text" class="form-control input-sm" placeholder="longitude" value="<?php echo $fLon; ?>" >
|
|
||||||
max zoom
|
|
||||||
|
|
||||||
<select name="zoom" class="form-control input-sm">
|
|
||||||
<option value="" <?php if ($iZoom === false) echo 'selected="selected"' ?> >--</option>
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$aZoomLevels = array(
|
|
||||||
0 => "Continent / Sea",
|
|
||||||
1 => "",
|
|
||||||
2 => "",
|
|
||||||
3 => "Country",
|
|
||||||
4 => "",
|
|
||||||
5 => "State",
|
|
||||||
6 => "Region",
|
|
||||||
7 => "",
|
|
||||||
8 => "County",
|
|
||||||
9 => "",
|
|
||||||
10 => "City",
|
|
||||||
11 => "",
|
|
||||||
12 => "Town / Village",
|
|
||||||
13 => "",
|
|
||||||
14 => "Suburb",
|
|
||||||
15 => "",
|
|
||||||
16 => "Street",
|
|
||||||
17 => "",
|
|
||||||
18 => "Building",
|
|
||||||
19 => "",
|
|
||||||
20 => "",
|
|
||||||
21 => "",
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach($aZoomLevels as $iZoomLevel => $sLabel)
|
|
||||||
{
|
|
||||||
$bSel = $iZoom === $iZoomLevel;
|
|
||||||
echo '<option value="'.$iZoomLevel.'"'.($bSel?' selected="selected"':'').'>'.$iZoomLevel.' '.$sLabel.'</option>'."\n";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group search-button-group">
|
|
||||||
<button type="submit" class="btn btn-primary btn-sm">Search</button>
|
|
||||||
</div>
|
|
||||||
<div class="search-type-link">
|
|
||||||
<a href="<?php echo CONST_Website_BaseURL; ?>search.php">forward search</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
<div id="content">
|
|
||||||
|
|
||||||
<?php if (count($aPlace)>0) { ?>
|
|
||||||
|
|
||||||
<div id="searchresults" class="sidebar">
|
|
||||||
<?php
|
|
||||||
$aResult = $aPlace;
|
|
||||||
|
|
||||||
echo '<div class="result" data-position="0">';
|
|
||||||
|
|
||||||
echo (isset($aResult['icon'])?'<img alt="icon" src="'.$aResult['icon'].'"/>':'');
|
|
||||||
echo ' <span class="name">'.htmlspecialchars($aResult['langaddress']).'</span>';
|
|
||||||
if (isset($aResult['label']))
|
|
||||||
echo ' <span class="type">('.$aResult['label'].')</span>';
|
|
||||||
else if ($aResult['type'] == 'yes')
|
|
||||||
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['class'])).')</span>';
|
|
||||||
else
|
|
||||||
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['type'])).')</span>';
|
|
||||||
echo '<p>'.$aResult['lat'].','.$aResult['lon'].'</p>';
|
|
||||||
echo ' <a class="btn btn-default btn-xs details" href="details.php?place_id='.$aResult['place_id'].'">details</a>';
|
|
||||||
echo '</div>';
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php } else { ?>
|
|
||||||
|
|
||||||
<div id="intro" class="sidebar">
|
|
||||||
Search for coordinates or click anywhere on the map.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php } ?>
|
|
||||||
|
|
||||||
<div id="map-wrapper">
|
|
||||||
<div id="map-position">
|
|
||||||
<div id="map-position-inner"></div>
|
|
||||||
<div id="map-position-close"><a href="#">hide</a></div>
|
|
||||||
</div>
|
|
||||||
<div id="map"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div> <!-- /content -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$aNominatimMapInit = array(
|
|
||||||
'zoom' => $iZoom !== false ? $iZoom : CONST_Default_Zoom,
|
|
||||||
'lat' => $fLat !== false ? $fLat : CONST_Default_Lat,
|
|
||||||
'lon' => $fLon !== false ? $fLon : CONST_Default_Lon,
|
|
||||||
'tile_url' => $sTileURL,
|
|
||||||
'tile_attribution' => $sTileAttribution
|
|
||||||
);
|
|
||||||
echo 'var nominatim_map_init = ' . json_encode($aNominatimMapInit, JSON_PRETTY_PRINT) . ';';
|
|
||||||
|
|
||||||
echo 'var nominatim_results = ' . json_encode([$aPlace], JSON_PRETTY_PRINT) . ';';
|
|
||||||
?>
|
|
||||||
</script>
|
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,62 +1,28 @@
|
|||||||
<?php
|
<?php
|
||||||
|
$aFilteredPlaces = array();
|
||||||
|
|
||||||
$aFilteredPlaces = array();
|
if (!sizeof($aPlace))
|
||||||
|
{
|
||||||
if (empty($aPlace)) {
|
|
||||||
if (isset($sError))
|
if (isset($sError))
|
||||||
$aFilteredPlaces['error'] = $sError;
|
$aFilteredPlaces['error'] = $sError;
|
||||||
else $aFilteredPlaces['error'] = 'Unable to geocode';
|
else
|
||||||
} else {
|
$aFilteredPlaces['error'] = 'Unable to geocode';
|
||||||
if (isset($aPlace['place_id'])) $aFilteredPlaces['place_id'] = $aPlace['place_id'];
|
}
|
||||||
$aFilteredPlaces['licence'] = 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright';
|
else
|
||||||
$sOSMType = formatOSMType($aPlace['osm_type']);
|
{
|
||||||
if ($sOSMType) {
|
if ($aPlace['place_id']) $aFilteredPlaces['place_id'] = $aPlace['place_id'];
|
||||||
|
$aFilteredPlaces['licence'] = "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright";
|
||||||
|
$sOSMType = ($aPlace['osm_type'] == 'N'?'node':($aPlace['osm_type'] == 'W'?'way':($aPlace['osm_type'] == 'R'?'relation':'')));
|
||||||
|
if ($sOSMType)
|
||||||
|
{
|
||||||
$aFilteredPlaces['osm_type'] = $sOSMType;
|
$aFilteredPlaces['osm_type'] = $sOSMType;
|
||||||
$aFilteredPlaces['osm_id'] = $aPlace['osm_id'];
|
$aFilteredPlaces['osm_id'] = $aPlace['osm_id'];
|
||||||
}
|
}
|
||||||
if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat'];
|
if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat'];
|
||||||
if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon'];
|
if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon'];
|
||||||
|
|
||||||
if ($sOutputFormat == 'jsonv2' || $sOutputFormat == 'geojson') {
|
|
||||||
$aFilteredPlaces['place_rank'] = $aPlace['rank_search'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['category'] = $aPlace['class'];
|
|
||||||
$aFilteredPlaces['type'] = $aPlace['type'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['importance'] = $aPlace['importance'];
|
|
||||||
|
|
||||||
$aFilteredPlaces['addresstype'] = strtolower($aPlace['addresstype']);
|
|
||||||
|
|
||||||
$aFilteredPlaces['name'] = $aPlace['placename'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$aFilteredPlaces['display_name'] = $aPlace['langaddress'];
|
$aFilteredPlaces['display_name'] = $aPlace['langaddress'];
|
||||||
|
if ($bShowAddressDetails) $aFilteredPlaces['address'] = $aPlace['aAddress'];
|
||||||
if (isset($aPlace['address'])) {
|
|
||||||
$aFilteredPlaces['address'] = $aPlace['address']->getAddressNames();
|
|
||||||
}
|
|
||||||
if (isset($aPlace['sExtraTags'])) $aFilteredPlaces['extratags'] = $aPlace['sExtraTags'];
|
|
||||||
if (isset($aPlace['sNameDetails'])) $aFilteredPlaces['namedetails'] = $aPlace['sNameDetails'];
|
|
||||||
|
|
||||||
if (isset($aPlace['aBoundingBox'])) {
|
|
||||||
$aFilteredPlaces['boundingbox'] = $aPlace['aBoundingBox'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['asgeojson'])) {
|
javascript_renderData($aFilteredPlaces);
|
||||||
$aFilteredPlaces['geojson'] = json_decode($aPlace['asgeojson']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['assvg'])) {
|
|
||||||
$aFilteredPlaces['svg'] = $aPlace['assvg'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['astext'])) {
|
|
||||||
$aFilteredPlaces['geotext'] = $aPlace['astext'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['askml'])) {
|
|
||||||
$aFilteredPlaces['geokml'] = $aPlace['askml'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
javascript_renderData($aFilteredPlaces);
|
|
||||||
|
|||||||
38
lib/template/address-jsonv2.php
Normal file
38
lib/template/address-jsonv2.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
$aFilteredPlaces = array();
|
||||||
|
|
||||||
|
if (!sizeof($aPlace))
|
||||||
|
{
|
||||||
|
if (isset($sError))
|
||||||
|
$aFilteredPlaces['error'] = $sError;
|
||||||
|
else
|
||||||
|
$aFilteredPlaces['error'] = 'Unable to geocode';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($aPlace['place_id']) $aFilteredPlaces['place_id'] = $aPlace['place_id'];
|
||||||
|
$aFilteredPlaces['licence'] = "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright";
|
||||||
|
$sOSMType = ($aPlace['osm_type'] == 'N'?'node':($aPlace['osm_type'] == 'W'?'way':($aPlace['osm_type'] == 'R'?'relation':'')));
|
||||||
|
if ($sOSMType)
|
||||||
|
{
|
||||||
|
$aFilteredPlaces['osm_type'] = $sOSMType;
|
||||||
|
$aFilteredPlaces['osm_id'] = $aPlace['osm_id'];
|
||||||
|
}
|
||||||
|
if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat'];
|
||||||
|
if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['place_rank'] = $aPlace['rank_search'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['category'] = $aPlace['class'];
|
||||||
|
$aFilteredPlaces['type'] = $aPlace['type'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['importance'] = $aPlace['importance'];
|
||||||
|
|
||||||
|
$aFilteredPlaces['addresstype'] = strtolower($aPlace['addresstype']);
|
||||||
|
|
||||||
|
$aFilteredPlaces['display_name'] = $aPlace['langaddress'];
|
||||||
|
$aFilteredPlaces['name'] = $aPlace['placename'];
|
||||||
|
if ($bShowAddressDetails && $aPlace['aAddress'] && sizeof($aPlace['aAddress'])) $aFilteredPlaces['address'] = $aPlace['aAddress'];
|
||||||
|
}
|
||||||
|
|
||||||
|
javascript_renderData($aFilteredPlaces);
|
||||||
@@ -1,87 +1,45 @@
|
|||||||
<?php
|
<?php
|
||||||
header('content-type: text/xml; charset=UTF-8');
|
header("content-type: text/xml; charset=UTF-8");
|
||||||
|
|
||||||
echo '<';
|
echo "<";
|
||||||
echo '?xml version="1.0" encoding="UTF-8" ?';
|
echo "?xml version=\"1.0\" encoding=\"UTF-8\" ?";
|
||||||
echo ">\n";
|
echo ">\n";
|
||||||
|
|
||||||
echo '<reversegeocode';
|
echo "<reversegeocode";
|
||||||
echo " timestamp='".date(DATE_RFC822)."'";
|
echo " timestamp='".date(DATE_RFC822)."'";
|
||||||
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
|
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
|
||||||
echo " querystring='".htmlspecialchars($_SERVER['QUERY_STRING'], ENT_QUOTES)."'";
|
echo " querystring='".htmlspecialchars($_SERVER['QUERY_STRING'], ENT_QUOTES)."'";
|
||||||
echo ">\n";
|
echo ">\n";
|
||||||
|
|
||||||
if (empty($aPlace)) {
|
if (!sizeof($aPlace))
|
||||||
|
{
|
||||||
if (isset($sError))
|
if (isset($sError))
|
||||||
echo "<error>$sError</error>";
|
echo "<error>$sError</error>";
|
||||||
else echo '<error>Unable to geocode</error>';
|
else
|
||||||
} else {
|
echo "<error>Unable to geocode</error>";
|
||||||
echo '<result';
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "<result";
|
||||||
if ($aPlace['place_id']) echo ' place_id="'.$aPlace['place_id'].'"';
|
if ($aPlace['place_id']) echo ' place_id="'.$aPlace['place_id'].'"';
|
||||||
$sOSMType = formatOSMType($aPlace['osm_type']);
|
$sOSMType = ($aPlace['osm_type'] == 'N'?'node':($aPlace['osm_type'] == 'W'?'way':($aPlace['osm_type'] == 'R'?'relation':'')));
|
||||||
if ($sOSMType) echo ' osm_type="'.$sOSMType.'"'.' osm_id="'.$aPlace['osm_id'].'"';
|
if ($sOSMType) echo ' osm_type="'.$sOSMType.'"'.' osm_id="'.$aPlace['osm_id'].'"';
|
||||||
if ($aPlace['ref']) echo ' ref="'.htmlspecialchars($aPlace['ref']).'"';
|
if ($aPlace['ref']) echo ' ref="'.htmlspecialchars($aPlace['ref']).'"';
|
||||||
if (isset($aPlace['lat'])) echo ' lat="'.htmlspecialchars($aPlace['lat']).'"';
|
if (isset($aPlace['lat'])) echo ' lat="'.htmlspecialchars($aPlace['lat']).'"';
|
||||||
if (isset($aPlace['lon'])) echo ' lon="'.htmlspecialchars($aPlace['lon']).'"';
|
if (isset($aPlace['lon'])) echo ' lon="'.htmlspecialchars($aPlace['lon']).'"';
|
||||||
if (isset($aPlace['aBoundingBox'])) {
|
echo ">".htmlspecialchars($aPlace['langaddress'])."</result>";
|
||||||
echo ' boundingbox="';
|
|
||||||
echo join(',', $aPlace['aBoundingBox']);
|
|
||||||
echo '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['asgeojson'])) {
|
if ($bShowAddressDetails) {
|
||||||
echo ' geojson=\'';
|
echo "<addressparts>";
|
||||||
echo $aPlace['asgeojson'];
|
foreach($aPlace['aAddress'] as $sKey => $sValue)
|
||||||
echo '\'';
|
{
|
||||||
}
|
$sKey = str_replace(' ','_',$sKey);
|
||||||
|
|
||||||
if (isset($aPlace['assvg'])) {
|
|
||||||
echo ' geosvg=\'';
|
|
||||||
echo $aPlace['assvg'];
|
|
||||||
echo '\'';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['astext'])) {
|
|
||||||
echo ' geotext=\'';
|
|
||||||
echo $aPlace['astext'];
|
|
||||||
echo '\'';
|
|
||||||
}
|
|
||||||
echo '>'.htmlspecialchars($aPlace['langaddress']).'</result>';
|
|
||||||
|
|
||||||
if (isset($aPlace['address'])) {
|
|
||||||
echo '<addressparts>';
|
|
||||||
foreach ($aPlace['address']->getAddressNames() as $sKey => $sValue) {
|
|
||||||
$sKey = str_replace(' ', '_', $sKey);
|
|
||||||
echo "<$sKey>";
|
echo "<$sKey>";
|
||||||
echo htmlspecialchars($sValue);
|
echo htmlspecialchars($sValue);
|
||||||
echo "</$sKey>";
|
echo "</$sKey>";
|
||||||
}
|
}
|
||||||
echo '</addressparts>';
|
echo "</addressparts>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPlace['sExtraTags'])) {
|
echo "</reversegeocode>";
|
||||||
echo '<extratags>';
|
|
||||||
foreach ($aPlace['sExtraTags'] as $sKey => $sValue) {
|
|
||||||
echo '<tag key="'.htmlspecialchars($sKey).'" value="'.htmlspecialchars($sValue).'"/>';
|
|
||||||
}
|
|
||||||
echo '</extratags>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['sNameDetails'])) {
|
|
||||||
echo '<namedetails>';
|
|
||||||
foreach ($aPlace['sNameDetails'] as $sKey => $sValue) {
|
|
||||||
echo '<name desc="'.htmlspecialchars($sKey).'">';
|
|
||||||
echo htmlspecialchars($sValue);
|
|
||||||
echo '</name>';
|
|
||||||
}
|
|
||||||
echo '</namedetails>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPlace['askml'])) {
|
|
||||||
echo "\n<geokml>";
|
|
||||||
echo $aPlace['askml'];
|
|
||||||
echo '</geokml>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
echo '</reversegeocode>';
|
|
||||||
|
|||||||
@@ -1,122 +1,180 @@
|
|||||||
<?php
|
<?php
|
||||||
header("content-type: text/html; charset=UTF-8");
|
header("content-type: text/html; charset=UTF-8");
|
||||||
?>
|
?>
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-header.php'); ?>
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<link href="css/common.css" rel="stylesheet" type="text/css" />
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<link href="css/details.css" rel="stylesheet" type="text/css" />
|
<head>
|
||||||
</head>
|
<title>OpenStreetMap Nominatim: <?php echo $aPointDetails['localname'];?></title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
<?php
|
margin:0px;
|
||||||
|
padding:16px;
|
||||||
function osmMapUrl($aFeature)
|
background:#ffffff;
|
||||||
{
|
height: 100%;
|
||||||
if (isset($sFeature['error_x']) && isset($sFeature['error_y']))
|
font: normal 12px/15px arial,sans-serif;
|
||||||
{
|
}
|
||||||
$sBaseUrl = '//www.openstreetmap.org/';
|
.line{
|
||||||
$sOSMType = formatOSMType($aFeature['osm_type'], false);
|
margin-left:20px;
|
||||||
if ($sOSMType)
|
}
|
||||||
{
|
.name{
|
||||||
$sBaseUrl += $sOSMType.'/'.$aFeature['osm_id'];
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
.notused{
|
||||||
return '<a href="'.$sBaseUrl.'?mlat='.$aFeature['error_y'].'&mlon='.$aFeature['error_x'].'">view on osm.org</a>';
|
color:#ddd;
|
||||||
}
|
}
|
||||||
return '';
|
.noname{
|
||||||
}
|
color:#800;
|
||||||
|
}
|
||||||
function josm_edit_url($aFeature)
|
#map {
|
||||||
{
|
width:500px;
|
||||||
$fWidth = 0.0002;
|
height:500px;
|
||||||
$sLon = $aFeature['error_x'];
|
border: 2px solid #666;
|
||||||
$sLat = $aFeature['error_y'];
|
float: right;
|
||||||
|
}
|
||||||
if (isset($sLat))
|
</style>
|
||||||
{
|
<script src="js/OpenLayers.js"></script>
|
||||||
return "http://localhost:8111/load_and_zoom?left=".($sLon-$fWidth)."&right=".($sLon+$fWidth)."&top=".($sLat+$fWidth)."&bottom=".($sLat-$fWidth);
|
<script src="js/tiles.js"></script>
|
||||||
}
|
|
||||||
|
|
||||||
$sOSMType = formatOSMType($aFeature['osm_type'], false);
|
|
||||||
if ($sOSMType)
|
|
||||||
{
|
|
||||||
return 'http://localhost:8111/import?url=http://www.openstreetmap.org/api/0.6/'.$sOSMType.'/'.$aFeature['osm_id'].'/full';
|
|
||||||
// Should be better to load by object id - but this doesn't seem to zoom correctly
|
|
||||||
// return " <a href=\"http://localhost:8111/load_object?new_layer=true&objects=".strtolower($aFeature['osm_type']).$sOSMID."\" target=\"josm\">Remote Control (JOSM / Merkaartor)</a>";
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function potlach_edit_url($aFeature)
|
|
||||||
{
|
|
||||||
$fWidth = 0.0002;
|
|
||||||
$sLat = $aFeature['error_y'];
|
|
||||||
$sLon = $aFeature['error_x'];
|
|
||||||
|
|
||||||
if (isset($sLat))
|
|
||||||
{
|
|
||||||
return "//www.openstreetmap.org/edit?editor=potlatch2&bbox=".($sLon-$fWidth).",".($sLat-$fWidth).",".($sLon+$fWidth).",".($sLat+$fWidth);
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<body id="details-page">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
|
|
||||||
|
|
||||||
<h1><?php echo $aPointDetails['localname'] ?></h1>
|
|
||||||
<div class="locationdetails">
|
|
||||||
<h2 class="bg-danger">This object has an invalid geometry.</h2>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Type: <span class="type"><?php echo $aPointDetails['class'].':'.$aPointDetails['type'];?></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
OSM: <span class="label"><?php echo osmLink($aPointDetails); ?><span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<h4>Error</h4>
|
|
||||||
<p>
|
|
||||||
<?php echo $aPointDetails['errormessage']?$aPointDetails['errormessage']:'unknown'; ?>
|
|
||||||
</p>
|
|
||||||
<?php echo osmMapUrl($aPointDetails); ?>
|
|
||||||
|
|
||||||
<h4>Edit</h4>
|
|
||||||
<ul>
|
|
||||||
<?php if (josm_edit_url($aPointDetails)) { ?>
|
|
||||||
<li><a href="<?php echo josm_edit_url($aPointDetails); ?>" target="josm">Remote Control (JOSM / Merkaartor)</a></li>
|
|
||||||
<?php } ?>
|
|
||||||
<?php if (potlach_edit_url($aPointDetails)) { ?>
|
|
||||||
<li><a href="<?php echo potlach_edit_url($aPointDetails); ?>" target="potlatch2">Potlatch 2</a></li>
|
|
||||||
<?php } ?>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div id="map"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
var nominatim_result = {
|
var map;
|
||||||
outlinestring: '<?php echo $aPointDetails['outlinestring'];?>',
|
|
||||||
lon: <?php echo isset($aPointDetails['error_x']) ? $aPointDetails['error_x'] : 0; ?>,
|
function init() {
|
||||||
lat: <?php echo isset($aPointDetails['error_y']) ? $aPointDetails['error_y'] : 0; ?>
|
map = new OpenLayers.Map ("map", {
|
||||||
|
controls:[
|
||||||
|
new OpenLayers.Control.Permalink(),
|
||||||
|
new OpenLayers.Control.Navigation(),
|
||||||
|
new OpenLayers.Control.PanZoomBar(),
|
||||||
|
new OpenLayers.Control.MouseDefaults(),
|
||||||
|
new OpenLayers.Control.MousePosition(),
|
||||||
|
new OpenLayers.Control.Attribution()],
|
||||||
|
maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
|
||||||
|
maxResolution: 156543.0399,
|
||||||
|
numZoomLevels: 19,
|
||||||
|
units: 'm',
|
||||||
|
projection: new OpenLayers.Projection("EPSG:900913"),
|
||||||
|
displayProjection: new OpenLayers.Projection("EPSG:4326")
|
||||||
|
} );
|
||||||
|
map.addLayer(new OpenLayers.Layer.OSM.<?php echo CONST_Tile_Default;?>("Default"));
|
||||||
|
|
||||||
|
var layer_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']);
|
||||||
|
layer_style.fillOpacity = 0.2;
|
||||||
|
layer_style.graphicOpacity = 0.2;
|
||||||
|
|
||||||
|
vectorLayer = new OpenLayers.Layer.Vector("Points", {style: layer_style});
|
||||||
|
map.addLayer(vectorLayer);
|
||||||
|
|
||||||
|
var proj_EPSG4326 = new OpenLayers.Projection("EPSG:4326");
|
||||||
|
var proj_map = map.getProjectionObject();
|
||||||
|
|
||||||
|
freader = new OpenLayers.Format.WKT({
|
||||||
|
'internalProjection': proj_map,
|
||||||
|
'externalProjection': proj_EPSG4326
|
||||||
|
});
|
||||||
|
|
||||||
|
var bounds;
|
||||||
|
<?php if ($aPointDetails['prevgeom']) { ?>
|
||||||
|
var feature = freader.read('<?php echo $aPointDetails['prevgeom'];?>');
|
||||||
|
if (feature) {
|
||||||
|
bounds = feature.geometry.getBounds();
|
||||||
|
|
||||||
|
}
|
||||||
|
feature.style = {
|
||||||
|
strokeColor: "#777777",
|
||||||
|
fillColor: "#F0F0F0",
|
||||||
|
strokeWidth: 2,
|
||||||
|
strokeOpacity: 0.75,
|
||||||
|
fillOpacity: 0.75,
|
||||||
|
strokeDashstyle: "longdash"
|
||||||
};
|
};
|
||||||
|
vectorLayer.addFeatures([feature]);
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php if ($aPointDetails['newgeom']) { ?>
|
||||||
|
feature = freader.read('<?php echo $aPointDetails['newgeom'];?>');
|
||||||
|
if (feature) {
|
||||||
|
if (!bounds) {
|
||||||
|
bounds = feature.geometry.getBounds();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bounds.extend(feature.geometry.getBounds());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
feature.style = {
|
||||||
|
strokeColor: "#75ADFF",
|
||||||
|
fillColor: "#FFF7F0",
|
||||||
|
strokeWidth: 2,
|
||||||
|
strokeOpacity: 0.75,
|
||||||
|
fillOpacity: 0.75
|
||||||
|
};
|
||||||
|
vectorLayer.addFeatures([feature]);
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php if (isset($aPointDetails['error_x'])) { ?>
|
||||||
|
var pt = new OpenLayers.Geometry.Point(<?php echo $aPointDetails['error_x'].','.$aPointDetails['error_y'];?>);
|
||||||
|
pt = pt.transform(proj_EPSG4326, proj_map);
|
||||||
|
feature = new OpenLayers.Feature.Vector(pt, null,
|
||||||
|
{
|
||||||
|
graphicName : "x",
|
||||||
|
fillColor: "#FF0000",
|
||||||
|
graphic : true,
|
||||||
|
pointRadius: 6
|
||||||
|
});
|
||||||
|
vectorLayer.addFeatures([feature]);
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
|
||||||
|
map.zoomToExtent(bounds);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="init();">
|
||||||
|
<div id="map"></div>
|
||||||
|
<h1><?php echo $aPointDetails['localname'] ?></h1>
|
||||||
|
<div class="locationdetails">
|
||||||
|
<div>Type: <span class="type"><?php echo $aPointDetails['class'].':'.$aPointDetails['type'];?></span></div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$sOSMType = ($aPointDetails['osm_type'] == 'N'?'node':($aPointDetails['osm_type'] == 'W'?'way': ($aPointDetails['osm_type'] == 'R'?'relation':'')));
|
||||||
|
if ($sOSMType) echo ' <div>OSM: <span class="osm"><span class="label"></span>'.$sOSMType.' <a href="http://www.openstreetmap.org/browse/'.$sOSMType.'/'.$aPointDetails['osm_id'].'">'. $aPointDetails['osm_id'].'</a></span></div>';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<p>This object has an invalid geometry.</p>
|
||||||
|
<p><b>Details:</b> <?php
|
||||||
|
|
||||||
|
$sVal = $aPointDetails['errormessage']?$aPointDetails['errormessage']:' ';
|
||||||
|
$sOSMType = ($aPointDetails['osm_type'] == 'N'?'node':($aPointDetails['osm_type'] == 'W'?'way':($aPointDetails['osm_type'] == 'R'?'relation':'')));
|
||||||
|
$sOSMID = $aPointDetails['osm_id'];
|
||||||
|
|
||||||
|
if (isset($aPointDetails['error_x']))
|
||||||
|
{
|
||||||
|
$sLat = $aPointDetails['error_y'];
|
||||||
|
$sLon = $aPointDetails['error_x'];
|
||||||
|
echo "<a href=\"http://www.openstreetmap.org/?lat=".$sLat."&lon=".$sLon."&zoom=18&layers=M&".$sOSMType."=".$sOSMID."\">".$sVal."</a>";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo $sVal;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<p><b>Edit:</b> in <?php
|
||||||
|
if (isset($aPointDetails['error_x']))
|
||||||
|
{
|
||||||
|
$fWidth = 0.0002;
|
||||||
|
echo " <a href=\"http://localhost:8111/load_and_zoom?left=".($sLon-$fWidth)."&right=".($sLon+$fWidth)."&top=".($sLat+$fWidth)."&bottom=".($sLat-$fWidth)."\" target=\"josm\">Remote Control (JOSM / Merkaartor)</a>";
|
||||||
|
echo " | <a href=\"http://www.openstreetmap.org/edit?editor=potlatch2&bbox=".($sLon-$fWidth).",".($sLat-$fWidth).",".($sLon+$fWidth).",".($sLat+$fWidth)."\" target=\"potlatch2\">Potlatch 2</a>";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo " <a href=\"http://localhost:8111/import?url=http://www.openstreetmap.org/api/0.6/".$sOSMType.'/'.$sOSMID."/full\" target=\"josm\">Remote Control (JOSM / Merkaartor)</a>";
|
||||||
|
// Should be better to load by object id - but this doesn't seem to zoom correctly
|
||||||
|
//echo " <a href=\"http://localhost:8111/load_object?new_layer=true&objects=".strtolower($aPointDetails['osm_type']).$sOSMID."\" target=\"josm\">Remote Control (JOSM / Merkaartor)</a>";
|
||||||
|
}
|
||||||
|
|
||||||
|
?></p>
|
||||||
|
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -1,254 +1,226 @@
|
|||||||
<?php
|
<?php
|
||||||
header("content-type: text/html; charset=UTF-8");
|
header("content-type: text/html; charset=UTF-8");
|
||||||
?>
|
?>
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-header.php'); ?>
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<link href="css/common.css" rel="stylesheet" type="text/css" />
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>OpenStreetMap Nominatim: <?php echo $aPointDetails['localname'];?></title>
|
||||||
<link href="css/details.css" rel="stylesheet" type="text/css" />
|
<link href="css/details.css" rel="stylesheet" type="text/css" />
|
||||||
</head>
|
<script src="js/OpenLayers.js" type="text/javascript"></script>
|
||||||
|
<script src="js/tiles.js" type="text/javascript"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var map;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
map = new OpenLayers.Map ("map", {
|
||||||
|
controls:[
|
||||||
|
new OpenLayers.Control.Permalink(),
|
||||||
|
new OpenLayers.Control.Navigation(),
|
||||||
|
new OpenLayers.Control.PanZoomBar(),
|
||||||
|
new OpenLayers.Control.MousePosition(),
|
||||||
|
new OpenLayers.Control.Attribution()],
|
||||||
|
maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
|
||||||
|
maxResolution: 156543.0399,
|
||||||
|
numZoomLevels: 19,
|
||||||
|
units: 'm',
|
||||||
|
projection: new OpenLayers.Projection("EPSG:900913"),
|
||||||
|
displayProjection: new OpenLayers.Projection("EPSG:4326")
|
||||||
|
} );
|
||||||
|
map.addLayer(new OpenLayers.Layer.OSM.<?php echo CONST_Tile_Default;?>("Default"));
|
||||||
|
|
||||||
|
var layer_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']);
|
||||||
|
layer_style.fillOpacity = 0.2;
|
||||||
|
layer_style.graphicOpacity = 0.2;
|
||||||
|
|
||||||
|
vectorLayer = new OpenLayers.Layer.Vector("Points", {style: layer_style});
|
||||||
|
map.addLayer(vectorLayer);
|
||||||
|
|
||||||
|
var proj_EPSG4326 = new OpenLayers.Projection("EPSG:4326");
|
||||||
|
var proj_map = map.getProjectionObject();
|
||||||
|
|
||||||
|
freader = new OpenLayers.Format.WKT({
|
||||||
|
'internalProjection': proj_map,
|
||||||
|
'externalProjection': proj_EPSG4326
|
||||||
|
});
|
||||||
|
|
||||||
|
var feature = freader.read('<?php echo $aPointDetails['outlinestring'];?>');
|
||||||
|
var featureCentre = freader.read('POINT(<?php echo $aPointDetails['lon'];?> <?php echo $aPointDetails['lat'];?>)');
|
||||||
|
if (feature) {
|
||||||
|
map.zoomToExtent(feature.geometry.getBounds());
|
||||||
|
feature.style = {
|
||||||
|
strokeColor: "#75ADFF",
|
||||||
|
fillColor: "#F0F7FF",
|
||||||
|
strokeWidth: <?php echo ($aPointDetails['isarea']=='t'?'2':'5');?>,
|
||||||
|
strokeOpacity: 0.75,
|
||||||
|
fillOpacity: 0.75,
|
||||||
|
pointRadius: 50
|
||||||
|
};
|
||||||
|
|
||||||
|
<?php if ($aPointDetails['isarea']=='t') {?>
|
||||||
|
featureCentre.style = {
|
||||||
|
strokeColor: "#008800",
|
||||||
|
fillColor: "#338833",
|
||||||
|
strokeWidth: <?php echo ($aPointDetails['isarea']=='t'?'2':'5');?>,
|
||||||
|
strokeOpacity: 0.75,
|
||||||
|
fillOpacity: 0.75,
|
||||||
|
pointRadius: 8
|
||||||
|
};
|
||||||
|
vectorLayer.addFeatures([feature,featureCentre]);
|
||||||
|
<?php } else { ?>
|
||||||
|
vectorLayer.addFeatures([feature]);
|
||||||
|
<?php } ?>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="init();">
|
||||||
|
<div id="map"></div>
|
||||||
<?php
|
<?php
|
||||||
|
echo '<h1>';
|
||||||
function headline($sTitle)
|
if ($aPointDetails['icon'])
|
||||||
{
|
{
|
||||||
echo "<tr class='all-columns'><td colspan='6'><h2>".$sTitle."</h2></td></tr>\n";
|
echo '<img style="float:right;margin-right:40px;" src="'.CONST_Website_BaseURL.'images/mapicons/'.$aPointDetails['icon'].'.n.32.png'.'" alt="'.$aPointDetails['icon'].'" />';
|
||||||
}
|
}
|
||||||
|
echo $aPointDetails['localname']."</h1>\n";
|
||||||
function headline3($sTitle)
|
echo '<div class="locationdetails">';
|
||||||
|
echo ' <div>Name: ';
|
||||||
|
foreach($aPointDetails['aNames'] as $sKey => $sValue)
|
||||||
{
|
{
|
||||||
echo "<tr class='all-columns'><td colspan='6'><h3>".$sTitle."</h3></td></tr>\n";
|
echo ' <div class="line"><span class="name">'.$sValue.'</span> ('.$sKey.')</div>';
|
||||||
}
|
}
|
||||||
|
echo ' </div>';
|
||||||
|
echo ' <div>Type: <span class="type">'.$aPointDetails['class'].':'.$aPointDetails['type'].'</span></div>';
|
||||||
function format_distance($fDistance)
|
echo ' <div>Last Updated: <span class="type">'.$aPointDetails['indexed_date'].'</span></div>';
|
||||||
{
|
echo ' <div>Admin Level: <span class="adminlevel">'.$aPointDetails['admin_level'].'</span></div>';
|
||||||
// $fDistance is in meters
|
echo ' <div>Rank: <span class="rankaddress">'.$aPointDetails['rank_search_label'].'</span></div>';
|
||||||
if ($fDistance < 1)
|
if ($aPointDetails['calculated_importance']) echo ' <div>Importance: <span class="rankaddress">'.$aPointDetails['calculated_importance'].($aPointDetails['importance']?'':' (estimated)').'</span></div>';
|
||||||
{
|
echo ' <div>Coverage: <span class="area">'.($aPointDetails['isarea']=='t'?'Polygon':'Point').'</span></div>';
|
||||||
return '0';
|
echo ' <div>Centre Point: <span class="area">'.$aPointDetails['lat'].','.$aPointDetails['lon'].'</span></div>';
|
||||||
}
|
$sOSMType = ($aPointDetails['osm_type'] == 'N'?'node':($aPointDetails['osm_type'] == 'W'?'way':($aPointDetails['osm_type'] == 'R'?'relation':'')));
|
||||||
elseif ($fDistance < 1000)
|
if ($sOSMType) echo ' <div>OSM: <span class="osm">'.$sOSMType.' <a href="http://www.openstreetmap.org/browse/'.$sOSMType.'/'.$aPointDetails['osm_id'].'">'.$aPointDetails['osm_id'].'</a></span></div>';
|
||||||
{
|
|
||||||
return'<abbr class="distance" title="'.$fDistance.'">~'.(round($fDistance,0)).' m</abbr>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return'<abbr class="distance" title="'.$fDistance.'">~'.(round($fDistance/1000,1)).' km</abbr>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function kv($sKey,$sValue)
|
|
||||||
{
|
|
||||||
echo ' <tr><td>' . $sKey . '</td><td>'.$sValue.'</td></tr>'. "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function hash_to_subtable($aAssociatedList)
|
|
||||||
{
|
|
||||||
$sHTML = '';
|
|
||||||
foreach ($aAssociatedList as $sKey => $sValue) {
|
|
||||||
$sHTML = $sHTML.' <div class="line"><span class="name">'.$sValue.'</span> ('.$sKey.')</div>'."\n";
|
|
||||||
}
|
|
||||||
return $sHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
function map_icon($sIcon)
|
|
||||||
{
|
|
||||||
if ($sIcon){
|
|
||||||
echo '<img id="mapicon" src="'.CONST_Website_BaseURL.'images/mapicons/'.$sIcon.'.n.32.png'.'" alt="'.$sIcon.'" />';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function _one_row($aAddressLine){
|
|
||||||
$bNotUsed = (isset($aAddressLine['isaddress']) && $aAddressLine['isaddress'] == 'f');
|
|
||||||
|
|
||||||
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>' . $aAddressLine['class'].':'.$aAddressLine['type'] . "</td>\n";
|
|
||||||
echo ' <td>' . osmLink($aAddressLine) . "</td>\n";
|
|
||||||
echo ' <td>' . (isset($aAddressLine['rank_address']) ? $aAddressLine['rank_address'] : '') . "</td>\n";
|
|
||||||
echo ' <td>' . ($aAddressLine['admin_level'] < 15 ? $aAddressLine['admin_level'] : '') . "</td>\n";
|
|
||||||
echo ' <td>' . format_distance($aAddressLine['distance'])."</td>\n";
|
|
||||||
echo ' <td>' . detailsLink($aAddressLine,'details >') . "</td>\n";
|
|
||||||
echo "</tr>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
function _one_keyword_row($keyword_token,$word_id){
|
|
||||||
echo "<tr>\n";
|
|
||||||
echo '<td>';
|
|
||||||
// mark partial tokens (those starting with a space) with a star for readability
|
|
||||||
echo ($keyword_token[0]==' '?'*':'');
|
|
||||||
echo $keyword_token;
|
|
||||||
if (isset($word_id))
|
|
||||||
{
|
|
||||||
echo '</td><td>word id: '.$word_id;
|
|
||||||
}
|
|
||||||
echo "</td></tr>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<body id="details-page">
|
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-top-navigation.php'); ?>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<h1>
|
|
||||||
<?php echo $aPointDetails['localname'] ?>
|
|
||||||
<small><?php echo detailsPermaLink($aPointDetails, 'link to this page') ?></small>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-2 text-right">
|
|
||||||
<?php map_icon($aPointDetails['icon']) ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<table id="locationdetails" class="table table-striped">
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
kv('Name' , hash_to_subtable($aPointDetails['aNames']) );
|
|
||||||
kv('Type' , $aPointDetails['class'].':'.$aPointDetails['type'] );
|
|
||||||
kv('Last Updated' , (new DateTime('@'.$aPointDetails['indexed_epoch']))->format(DateTime::RFC822) );
|
|
||||||
kv('Admin Level' , $aPointDetails['admin_level'] );
|
|
||||||
kv('Rank' , $aPointDetails['rank_search_label'] );
|
|
||||||
if ($aPointDetails['calculated_importance']) {
|
|
||||||
kv('Importance' , $aPointDetails['calculated_importance'].($aPointDetails['importance']?'':' (estimated)') );
|
|
||||||
}
|
|
||||||
kv('Coverage' , ($aPointDetails['isarea']=='t'?'Polygon':'Point') );
|
|
||||||
kv('Centre Point' , $aPointDetails['lat'].','.$aPointDetails['lon'] );
|
|
||||||
kv('OSM' , osmLink($aPointDetails) );
|
|
||||||
if ($aPointDetails['wikipedia'])
|
if ($aPointDetails['wikipedia'])
|
||||||
{
|
{
|
||||||
kv('Wikipedia Calculated' , wikipediaLink($aPointDetails) );
|
list($sWikipediaLanguage,$sWikipediaArticle) = explode(':',$aPointDetails['wikipedia']);
|
||||||
|
echo ' <div>Wikipedia Calculated: <span class="wikipedia"><a href="http://'.$sWikipediaLanguage.'.wikipedia.org/wiki/'.urlencode($sWikipediaArticle).'">'.$aPointDetails['wikipedia'].'</a></span></div>';
|
||||||
}
|
}
|
||||||
|
echo ' <div>Extra Tags: ';
|
||||||
kv('Computed Postcode', $aPointDetails['postcode']);
|
foreach($aPointDetails['aExtraTags'] as $sKey => $sValue)
|
||||||
kv('Address Tags' , hash_to_subtable($aPointDetails['aAddressTags']) );
|
{
|
||||||
kv('Extra Tags' , hash_to_subtable($aPointDetails['aExtraTags']) );
|
echo ' <div class="line"><span class="name">'.$sValue.'</span> ('.$sKey.')</div>';
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div id="map"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
|
|
||||||
<h2>Address</h2>
|
|
||||||
|
|
||||||
<table id="address" class="table table-striped table-responsive">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Local name</td>
|
|
||||||
<td>Type</td>
|
|
||||||
<td>OSM</td>
|
|
||||||
<td>Address rank</td>
|
|
||||||
<td>Admin level</td>
|
|
||||||
<td>Distance</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
foreach ($aAddressLines as $aAddressLine) {
|
|
||||||
_one_row($aAddressLine);
|
|
||||||
}
|
}
|
||||||
?>
|
echo ' </div>';
|
||||||
|
echo "</div>\n";
|
||||||
|
|
||||||
|
echo "<h2>Address</h2>\n";
|
||||||
|
echo '<div class="address">';
|
||||||
|
$iPrevRank = 1000000;
|
||||||
|
$sPrevLocalName = '';
|
||||||
|
foreach($aAddressLines as $aAddressLine)
|
||||||
|
{
|
||||||
|
$sOSMType = ($aAddressLine['osm_type'] == 'N'?'node':($aAddressLine['osm_type'] == 'W'?'way':($aAddressLine['osm_type'] == 'R'?'relation':'')));
|
||||||
|
|
||||||
<?php
|
echo '<div class="line'.($aAddressLine['isaddress']=='f'?' notused':'').'">';
|
||||||
|
if (!($iPrevRank<=$aAddressLine['rank_address'] || $sPrevLocalName == $aAddressLine['localname']))
|
||||||
|
{
|
||||||
|
$iPrevRank = $aAddressLine['rank_address'];
|
||||||
|
$sPrevLocalName = $aAddressLine['localname'];
|
||||||
|
}
|
||||||
|
echo '<span class="name">'.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'<span class="noname">No Name</span>').'</span>';
|
||||||
|
echo ' (';
|
||||||
|
echo '<span class="type"><span class="label">Type: </span>'.$aAddressLine['class'].':'.$aAddressLine['type'].'</span>';
|
||||||
|
if ($sOSMType) echo ', <span class="osm">'.$sOSMType.' <a href="http://www.openstreetmap.org/browse/'.$sOSMType.'/'.$aAddressLine['osm_id'].'">'.$aAddressLine['osm_id'].'</a></span>';
|
||||||
|
if (isset($aAddressLine['admin_level'])) echo ', <span class="adminlevel">'.$aAddressLine['admin_level'].'</span>';
|
||||||
|
if (isset($aAddressLine['rank_search_label'])) echo ', <span class="rankaddress">'.$aAddressLine['rank_search_label'].'</span>';
|
||||||
|
// echo ', <span class="area">'.($aAddressLine['fromarea']=='t'?'Polygon':'Point').'</span>';
|
||||||
|
echo ', <span class="distance">'.$aAddressLine['distance'].'</span>';
|
||||||
|
echo ' <a href="details.php?place_id='.$aAddressLine['place_id'].'">GOTO</a>';
|
||||||
|
echo ')';
|
||||||
|
echo "</div>\n";
|
||||||
|
}
|
||||||
|
echo "</div>\n";
|
||||||
|
|
||||||
if ($aLinkedLines)
|
if ($aLinkedLines)
|
||||||
{
|
{
|
||||||
headline('Linked Places');
|
echo "<h2>Linked Places</h2>\n";
|
||||||
foreach ($aLinkedLines as $aAddressLine) {
|
echo '<div class="linked">';
|
||||||
_one_row($aAddressLine);
|
foreach($aLinkedLines as $aAddressLine)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($bIncludeKeywords)
|
|
||||||
{
|
{
|
||||||
headline('Name Keywords');
|
$sOSMType = ($aAddressLine['osm_type'] == 'N'?'node':($aAddressLine['osm_type'] == 'W'?'way':($aAddressLine['osm_type'] == 'R'?'relation':'')));
|
||||||
if ($aPlaceSearchNameKeywords) {
|
|
||||||
foreach ($aPlaceSearchNameKeywords as $aRow) {
|
echo '<div class="line">';
|
||||||
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
|
echo '<span class="name">'.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'<span class="noname">No Name</span>').'</span>';
|
||||||
|
echo ' (';
|
||||||
|
echo '<span class="type"><span class="label">Type: </span>'.$aAddressLine['class'].':'.$aAddressLine['type'].'</span>';
|
||||||
|
if ($sOSMType) echo ', <span class="osm">'.$sOSMType.' <a href="http://www.openstreetmap.org/browse/'.$sOSMType.'/'.$aAddressLine['osm_id'].'">'.$aAddressLine['osm_id'].'</a></span>';
|
||||||
|
echo ', <span class="adminlevel">'.$aAddressLine['admin_level'].'</span>';
|
||||||
|
if (isset($aAddressLine['rank_search_label'])) echo ', <span class="rankaddress">'.$aAddressLine['rank_search_label'].'</span>';
|
||||||
|
// echo ', <span class="area">'.($aAddressLine['fromarea']=='t'?'Polygon':'Point').'</span>';
|
||||||
|
echo ', <span class="distance">'.$aAddressLine['distance'].'</span>';
|
||||||
|
echo ' <a href="details.php?place_id='.$aAddressLine['place_id'].'">GOTO</a>';
|
||||||
|
echo ')';
|
||||||
|
echo "</div>\n";
|
||||||
}
|
}
|
||||||
|
echo "</div>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
headline('Address Keywords');
|
if ($aPlaceSearchNameKeywords)
|
||||||
if ($aPlaceSearchAddressKeywords) {
|
|
||||||
foreach ($aPlaceSearchAddressKeywords as $aRow) {
|
|
||||||
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($aHierarchyLines))
|
|
||||||
{
|
{
|
||||||
headline('Parent Of');
|
echo '<h2>Name Keywords</h2>';
|
||||||
|
foreach($aPlaceSearchNameKeywords as $aRow)
|
||||||
|
{
|
||||||
|
echo '<div>'.$aRow['word_token']."</div>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aPlaceSearchAddressKeywords)
|
||||||
|
{
|
||||||
|
echo '<h2>Address Keywords</h2>';
|
||||||
|
foreach($aPlaceSearchAddressKeywords as $aRow)
|
||||||
|
{
|
||||||
|
echo '<div>'.($aRow['word_token'][0]==' '?'*':'').$aRow['word_token'].'('.$aRow['word_id'].')'."</div>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sizeof($aParentOfLines))
|
||||||
|
{
|
||||||
|
echo "<h2>Parent Of:</h2>\n<div>\n";
|
||||||
|
|
||||||
$aGroupedAddressLines = array();
|
$aGroupedAddressLines = array();
|
||||||
foreach ($aHierarchyLines as $aAddressLine) {
|
foreach($aParentOfLines as $aAddressLine)
|
||||||
if ($aAddressLine['type'] == 'yes') $sType = $aAddressLine['class'];
|
{
|
||||||
else $sType = $aAddressLine['type'];
|
if (!isset($aGroupedAddressLines[$aAddressLine['type']])) $aGroupedAddressLines[$aAddressLine['type']] = array();
|
||||||
|
$aGroupedAddressLines[$aAddressLine['type']][] = $aAddressLine;
|
||||||
if (!isset($aGroupedAddressLines[$sType]))
|
|
||||||
$aGroupedAddressLines[$sType] = array();
|
|
||||||
$aGroupedAddressLines[$sType][] = $aAddressLine;
|
|
||||||
}
|
}
|
||||||
foreach ($aGroupedAddressLines as $sGroupHeading => $aHierarchyLines) {
|
foreach($aGroupedAddressLines as $sGroupHeading => $aParentOfLines)
|
||||||
|
{
|
||||||
$sGroupHeading = ucwords($sGroupHeading);
|
$sGroupHeading = ucwords($sGroupHeading);
|
||||||
headline3($sGroupHeading);
|
echo "<h3>$sGroupHeading</h3>\n";
|
||||||
|
foreach($aParentOfLines as $aAddressLine)
|
||||||
|
{
|
||||||
|
$aAddressLine['localname'] = $aAddressLine['localname']?$aAddressLine['localname']:$aAddressLine['housenumber'];
|
||||||
|
$sOSMType = ($aAddressLine['osm_type'] == 'N'?'node':($aAddressLine['osm_type'] == 'W'?'way':($aAddressLine['osm_type'] == 'R'?'relation':'')));
|
||||||
|
|
||||||
foreach ($aHierarchyLines as $aAddressLine) {
|
echo '<div class="line">';
|
||||||
_one_row($aAddressLine);
|
echo '<span class="name">'.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'<span class="noname">No Name</span>').'</span>';
|
||||||
|
echo ' (';
|
||||||
|
echo '<span class="area">'.($aAddressLine['isarea']=='t'?'Polygon':'Point').'</span>';
|
||||||
|
echo ', <span class="distance">~'.(round($aAddressLine['distance']*69,1)).' miles</span>';
|
||||||
|
if ($sOSMType) echo ', <span class="osm">'.$sOSMType.' <a href="http://www.openstreetmap.org/browse/'.$sOSMType.'/'.$aAddressLine['osm_id'].'">'.$aAddressLine['osm_id'].'</a></span>';
|
||||||
|
echo ', <a href="details.php?place_id='.$aAddressLine['place_id'].'">GOTO</a>';
|
||||||
|
echo ')';
|
||||||
|
echo "</div>\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (count($aHierarchyLines) >= 500) {
|
if (sizeof($aParentOfLines) >= 500) {
|
||||||
echo '<p>There are more child objects which are not shown.</p>';
|
echo '<p>There are more child objects which are not shown.</p>';
|
||||||
}
|
}
|
||||||
|
echo '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "</table>\n";
|
// echo '<h2>Other Parts:</h2>';
|
||||||
|
// echo '<h2>Linked To:</h2>';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
</div>
|
</body>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$aNominatimMapInit = array(
|
|
||||||
'tile_url' => $sTileURL,
|
|
||||||
'tile_attribution' => $sTileAttribution
|
|
||||||
);
|
|
||||||
echo 'var nominatim_map_init = ' . json_encode($aNominatimMapInit, JSON_PRETTY_PRINT) . ';';
|
|
||||||
|
|
||||||
$aPlace = array(
|
|
||||||
'asgeojson' => $aPointDetails['asgeojson'],
|
|
||||||
'lon' => $aPointDetails['lon'],
|
|
||||||
'lat' => $aPointDetails['lat'],
|
|
||||||
);
|
|
||||||
echo 'var nominatim_result = ' . json_encode($aPlace, JSON_PRETTY_PRINT) . ';';
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$aPlaceDetails = array();
|
|
||||||
|
|
||||||
$aPlaceDetails['place_id'] = (int) $aPointDetails['place_id'];
|
|
||||||
$aPlaceDetails['parent_place_id'] = (int) $aPointDetails['parent_place_id'];
|
|
||||||
|
|
||||||
$aPlaceDetails['osm_type'] = $aPointDetails['osm_type'];
|
|
||||||
$aPlaceDetails['osm_id'] = (int) $aPointDetails['osm_id'];
|
|
||||||
|
|
||||||
$aPlaceDetails['category'] = $aPointDetails['class'];
|
|
||||||
$aPlaceDetails['type'] = $aPointDetails['type'];
|
|
||||||
$aPlaceDetails['admin_level'] = $aPointDetails['admin_level'];
|
|
||||||
|
|
||||||
$aPlaceDetails['localname'] = $aPointDetails['localname'];
|
|
||||||
$aPlaceDetails['names'] = $aPointDetails['aNames'];
|
|
||||||
|
|
||||||
$aPlaceDetails['addresstags'] = $aPointDetails['aAddressTags'];
|
|
||||||
$aPlaceDetails['housenumber'] = $aPointDetails['housenumber'];
|
|
||||||
$aPlaceDetails['calculated_postcode'] = $aPointDetails['postcode'];
|
|
||||||
$aPlaceDetails['country_code'] = $aPointDetails['country_code'];
|
|
||||||
|
|
||||||
$aPlaceDetails['indexed_date'] = (new DateTime('@'.$aPointDetails['indexed_epoch']))->format(DateTime::RFC3339);
|
|
||||||
$aPlaceDetails['importance'] = (float) $aPointDetails['importance'];
|
|
||||||
$aPlaceDetails['calculated_importance'] = (float) $aPointDetails['calculated_importance'];
|
|
||||||
|
|
||||||
$aPlaceDetails['extratags'] = $aPointDetails['aExtraTags'];
|
|
||||||
$aPlaceDetails['calculated_wikipedia'] = $aPointDetails['wikipedia'];
|
|
||||||
if ($aPointDetails['icon']) {
|
|
||||||
$aPlaceDetails['icon'] = CONST_Website_BaseURL.'images/mapicons/'.$aPointDetails['icon'].'.n.32.png';
|
|
||||||
}
|
|
||||||
|
|
||||||
$aPlaceDetails['rank_address'] = (int) $aPointDetails['rank_address'];
|
|
||||||
$aPlaceDetails['rank_search'] = (int) $aPointDetails['rank_search'];
|
|
||||||
|
|
||||||
$aPlaceDetails['isarea'] = ($aPointDetails['isarea'] == 't');
|
|
||||||
$aPlaceDetails['centroid'] = array(
|
|
||||||
'type' => 'Point',
|
|
||||||
'coordinates' => array( (float) $aPointDetails['lon'], (float) $aPointDetails['lat'] )
|
|
||||||
);
|
|
||||||
|
|
||||||
$aPlaceDetails['geometry'] = json_decode($aPointDetails['asgeojson']);
|
|
||||||
|
|
||||||
$funcMapAddressLine = function ($aFull) {
|
|
||||||
$aMapped = array(
|
|
||||||
'localname' => $aFull['localname'],
|
|
||||||
'place_id' => isset($aFull['place_id']) ? (int) $aFull['place_id'] : null,
|
|
||||||
'osm_id' => isset($aFull['osm_id']) ? (int) $aFull['osm_id'] : null,
|
|
||||||
'osm_type' => isset($aFull['osm_type']) ? $aFull['osm_type'] : null,
|
|
||||||
'class' => $aFull['class'],
|
|
||||||
'type' => $aFull['type'],
|
|
||||||
'admin_level' => isset($aFull['admin_level']) ? (int) $aFull['admin_level'] : null,
|
|
||||||
'rank_address' => $aFull['rank_address'] ? (int) $aFull['rank_address'] : null,
|
|
||||||
'distance' => (float) $aFull['distance']
|
|
||||||
);
|
|
||||||
|
|
||||||
return $aMapped;
|
|
||||||
};
|
|
||||||
|
|
||||||
$funcMapKeyword = function ($aFull) {
|
|
||||||
$aMapped = array(
|
|
||||||
'id' => (int) $aFull['word_id'],
|
|
||||||
'token' => $aFull['word_token']
|
|
||||||
);
|
|
||||||
return $aMapped;
|
|
||||||
};
|
|
||||||
|
|
||||||
if ($aAddressLines) {
|
|
||||||
$aPlaceDetails['address'] = array_map($funcMapAddressLine, $aAddressLines);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($aLinkedLines) {
|
|
||||||
$aPlaceDetails['linked_places'] = array_map($funcMapAddressLine, $aLinkedLines);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($bIncludeKeywords) {
|
|
||||||
$aPlaceDetails['keywords'] = array();
|
|
||||||
|
|
||||||
if ($aPlaceSearchNameKeywords) {
|
|
||||||
$aPlaceDetails['keywords']['name'] = array_map($funcMapKeyword, $aPlaceSearchNameKeywords);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($aPlaceSearchAddressKeywords) {
|
|
||||||
$aPlaceDetails['keywords']['address'] = array_map($funcMapKeyword, $aPlaceSearchAddressKeywords);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($bIncludeHierarchy) {
|
|
||||||
if ($bGroupHierarchy) {
|
|
||||||
$aPlaceDetails['hierarchy'] = array();
|
|
||||||
foreach ($aHierarchyLines as $aAddressLine) {
|
|
||||||
if ($aAddressLine['type'] == 'yes') $sType = $aAddressLine['class'];
|
|
||||||
else $sType = $aAddressLine['type'];
|
|
||||||
|
|
||||||
if (!isset($aPlaceDetails['hierarchy'][$sType]))
|
|
||||||
$aPlaceDetails['hierarchy'][$sType] = array();
|
|
||||||
$aPlaceDetails['hierarchy'][$sType][] = $funcMapAddressLine($aAddressLine);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$aPlaceDetails['hierarchy'] = array_map($funcMapAddressLine, $aHierarchyLines);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
javascript_renderData($aPlaceDetails);
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<footer>
|
|
||||||
<p class="disclaimer">
|
|
||||||
Addresses and postcodes are approximate
|
|
||||||
</p>
|
|
||||||
<p class="copyright">
|
|
||||||
© <a href="https://osm.org/copyright">OpenStreetMap</a> contributors
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script src="js/jquery.min.js"></script>
|
|
||||||
<script src="js/bootstrap.min.js"></script>
|
|
||||||
<script src="js/leaflet.min.js"></script>
|
|
||||||
<script src="js/Control.Minimap.min.js"></script>
|
|
||||||
<script src="js/url-search-params.js"></script>
|
|
||||||
<script src="js/nominatim-ui.js"></script>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>OpenStreetMap Nominatim: Search</title>
|
|
||||||
<meta content="IE=edge" http-equiv="x-ua-compatible" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<base href="<?php echo CONST_Website_BaseURL;?>" />
|
|
||||||
<link href="nominatim.xml" rel="search" title="Nominatim Search" type="application/opensearchdescription+xml" />
|
|
||||||
<link href="css/leaflet.css" rel="stylesheet" />
|
|
||||||
<link href="css/Control.Minimap.min.css" rel="stylesheet" />
|
|
||||||
<link href="css/bootstrap-theme.min.css" rel="stylesheet" />
|
|
||||||
<link href="css/bootstrap.min.css" rel="stylesheet" />
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<header class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-4">
|
|
||||||
<div class="brand">
|
|
||||||
<a href="<?php echo CONST_Website_BaseURL;?>">
|
|
||||||
<img alt="logo" src="images/osm_logo.120px.png" width="30" height="30"/>
|
|
||||||
<h1>Nominatim</h1>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="last-updated" class="col-xs-4 text-center">
|
|
||||||
<?php if (isset($sDataDate)){ ?>
|
|
||||||
Data last updated:
|
|
||||||
<br>
|
|
||||||
<?php echo $sDataDate; ?>
|
|
||||||
<?php } ?>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-4 text-right">
|
|
||||||
<div class="btn-group">
|
|
||||||
<button class="dropdown-toggle btn btn-sm btn-default" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
|
||||||
About & Help <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-right">
|
|
||||||
<li><a href="https://wiki.openstreetmap.org/wiki/Nominatim" target="_blank">Documentation</a></li>
|
|
||||||
<li><a href="https://wiki.openstreetmap.org/wiki/Nominatim/FAQ" target="_blank">FAQ</a></li>
|
|
||||||
<li role="separator" class="divider"></li>
|
|
||||||
<li><a href="#" class="" data-toggle="modal" data-target="#report-modal">Report problem with results</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="modal fade" id="report-modal">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
|
||||||
<h4 class="modal-title">Report a problem</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/report-errors.php'); ?>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">OK</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<h2>Welcome to Nominatim</h2>
|
|
||||||
|
|
||||||
<p>Nominatim is a search engine for <a href="https://www.openstreetmap.org">OpenStreetMap</a>
|
|
||||||
data. This is the debugging interface. You may search for a name or address (forward search) or
|
|
||||||
look up data by its geographic coordinate (reverse search). Each result comes with a
|
|
||||||
link to a details page where you can inspect what data about the object is saved in
|
|
||||||
the database and investigate how the address of the object has been computed.</p>
|
|
||||||
|
|
||||||
For more information visit the <a href="https://wiki.openstreetmap.org/wiki/Nominatim">Nominatim wiki page</a>.
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<p>
|
|
||||||
Before reporting problems please read the <a target="_blank" href="https://wiki.openstreetmap.org/wiki/Nominatim">user documentation</a>
|
|
||||||
and
|
|
||||||
<a target="_blank" href="https://wiki.openstreetmap.org/wiki/Nominatim/FAQ">FAQ</a>.
|
|
||||||
|
|
||||||
If your problem relates to the address of a particular search result please use the 'details' link
|
|
||||||
to check how the address was generated before reporting a problem.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Use <a target="_blank" href="https://github.com/openstreetmap/nominatim/issues">Nominatim issues on github</a>
|
|
||||||
to report problems.
|
|
||||||
<!-- You can search for existing bug reports
|
|
||||||
<a href="https://trac.openstreetmap.org/query?status=new&status=assigned&status=reopened&component=nominatim&order=priority">here</a>.</p>
|
|
||||||
-->
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Please ensure that you include a full description of the problem, including the search
|
|
||||||
query that you used, the problem with the result and, if the problem relates to missing data,
|
|
||||||
the osm type (node, way, relation) and id of the item that is missing.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Problems that contain enough detail are likely to get looked at before ones that require
|
|
||||||
significant research.
|
|
||||||
</p>
|
|
||||||
@@ -1,37 +1,42 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$aOutput = array();
|
$aOutput = array();
|
||||||
$aOutput['licence'] = 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright';
|
$aOutput['licence'] = "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright";
|
||||||
$aOutput['batch'] = array();
|
$aOutput['batch'] = array();
|
||||||
|
|
||||||
foreach ($aBatchResults as $aSearchResults) {
|
foreach($aBatchResults as $aSearchResults)
|
||||||
|
{
|
||||||
if (!$aSearchResults) $aSearchResults = array();
|
if (!$aSearchResults) $aSearchResults = array();
|
||||||
$aFilteredPlaces = array();
|
$aFilteredPlaces = array();
|
||||||
foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
foreach($aSearchResults as $iResNum => $aPointDetails)
|
||||||
|
{
|
||||||
$aPlace = array(
|
$aPlace = array(
|
||||||
'place_id'=>$aPointDetails['place_id'],
|
'place_id'=>$aPointDetails['place_id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
$sOSMType = ($aPointDetails['osm_type'] == 'N'?'node':($aPointDetails['osm_type'] == 'W'?'way':($aPointDetails['osm_type'] == 'R'?'relation':'')));
|
||||||
if ($sOSMType) {
|
if ($sOSMType)
|
||||||
|
{
|
||||||
$aPlace['osm_type'] = $sOSMType;
|
$aPlace['osm_type'] = $sOSMType;
|
||||||
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['aBoundingBox'])) {
|
if (isset($aPointDetails['aBoundingBox']))
|
||||||
|
{
|
||||||
$aPlace['boundingbox'] = array(
|
$aPlace['boundingbox'] = array(
|
||||||
$aPointDetails['aBoundingBox'][0],
|
$aPointDetails['aBoundingBox'][0],
|
||||||
$aPointDetails['aBoundingBox'][1],
|
$aPointDetails['aBoundingBox'][1],
|
||||||
$aPointDetails['aBoundingBox'][2],
|
$aPointDetails['aBoundingBox'][2],
|
||||||
$aPointDetails['aBoundingBox'][3]
|
$aPointDetails['aBoundingBox'][3]);
|
||||||
);
|
|
||||||
|
|
||||||
if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons) {
|
if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons)
|
||||||
|
{
|
||||||
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['zoom'])) {
|
if (isset($aPointDetails['zoom']))
|
||||||
|
{
|
||||||
$aPlace['zoom'] = $aPointDetails['zoom'];
|
$aPlace['zoom'] = $aPointDetails['zoom'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,33 +50,39 @@ foreach ($aBatchResults as $aSearchResults) {
|
|||||||
|
|
||||||
$aPlace['importance'] = $aPointDetails['importance'];
|
$aPlace['importance'] = $aPointDetails['importance'];
|
||||||
|
|
||||||
if (isset($aPointDetails['icon'])) {
|
if (isset($aPointDetails['icon']))
|
||||||
|
{
|
||||||
$aPlace['icon'] = $aPointDetails['icon'];
|
$aPlace['icon'] = $aPointDetails['icon'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['address'])) {
|
if (isset($aPointDetails['address']) && sizeof($aPointDetails['address'])>0)
|
||||||
$aPlace['address'] = $aPointDetails['address']->getAddressNames();
|
{
|
||||||
|
$aPlace['address'] = $aPointDetails['address'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['asgeojson'])) {
|
if (isset($aPointDetails['asgeojson']))
|
||||||
|
{
|
||||||
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['assvg'])) {
|
if (isset($aPointDetails['assvg']))
|
||||||
|
{
|
||||||
$aPlace['svg'] = $aPointDetails['assvg'];
|
$aPlace['svg'] = $aPointDetails['assvg'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['astext'])) {
|
if (isset($aPointDetails['astext']))
|
||||||
|
{
|
||||||
$aPlace['geotext'] = $aPointDetails['astext'];
|
$aPlace['geotext'] = $aPointDetails['astext'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['askml'])) {
|
if (isset($aPointDetails['askml']))
|
||||||
|
{
|
||||||
$aPlace['geokml'] = $aPointDetails['askml'];
|
$aPlace['geokml'] = $aPointDetails['askml'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$aFilteredPlaces[] = $aPlace;
|
$aFilteredPlaces[] = $aPlace;
|
||||||
}
|
}
|
||||||
$aOutput['batch'][] = $aFilteredPlaces;
|
$aOutput['batch'][] = $aFilteredPlaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
javascript_renderData($aOutput, array('geojson'));
|
javascript_renderData($aOutput, array('geojson'));
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$aFilteredPlaces = array();
|
|
||||||
foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
|
||||||
$aPlace = array(
|
|
||||||
'type' => 'Feature',
|
|
||||||
'properties' => array(
|
|
||||||
'geocoding' => array()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isset($aPointDetails['place_id'])) $aPlace['properties']['geocoding']['place_id'] = $aPointDetails['place_id'];
|
|
||||||
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
|
||||||
if ($sOSMType) {
|
|
||||||
$aPlace['properties']['geocoding']['osm_type'] = $sOSMType;
|
|
||||||
$aPlace['properties']['geocoding']['osm_id'] = $aPointDetails['osm_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$aPlace['properties']['geocoding']['type'] = $aPointDetails['type'];
|
|
||||||
|
|
||||||
$aPlace['properties']['geocoding']['label'] = $aPointDetails['langaddress'];
|
|
||||||
|
|
||||||
$aPlace['properties']['geocoding']['name'] = $aPointDetails['placename'];
|
|
||||||
|
|
||||||
if (isset($aPointDetails['address'])) {
|
|
||||||
$aFieldMappings = array(
|
|
||||||
'house_number' => 'housenumber',
|
|
||||||
'road' => 'street',
|
|
||||||
'locality' => 'locality',
|
|
||||||
'postcode' => 'postcode',
|
|
||||||
'city' => 'city',
|
|
||||||
'district' => 'district',
|
|
||||||
'county' => 'county',
|
|
||||||
'state' => 'state',
|
|
||||||
'country' => 'country'
|
|
||||||
);
|
|
||||||
|
|
||||||
$aAddrNames = $aPointDetails['address']->getAddressNames();
|
|
||||||
foreach ($aFieldMappings as $sFrom => $sTo) {
|
|
||||||
if (isset($aAddrNames[$sFrom])) {
|
|
||||||
$aPlace['properties']['geocoding'][$sTo] = $aAddrNames[$sFrom];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$aPlace['properties']['geocoding']['admin']
|
|
||||||
= $aPointDetails['address']->getAdminLevels();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['asgeojson'])) {
|
|
||||||
$aPlace['geometry'] = json_decode($aPointDetails['asgeojson']);
|
|
||||||
} else {
|
|
||||||
$aPlace['geometry'] = array(
|
|
||||||
'type' => 'Point',
|
|
||||||
'coordinates' => array(
|
|
||||||
(float) $aPointDetails['lon'],
|
|
||||||
(float) $aPointDetails['lat']
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$aFilteredPlaces[] = $aPlace;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
javascript_renderData(array(
|
|
||||||
'type' => 'FeatureCollection',
|
|
||||||
'geocoding' => array(
|
|
||||||
'version' => '0.1.0',
|
|
||||||
'attribution' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
|
||||||
'licence' => 'ODbL',
|
|
||||||
'query' => $sQuery
|
|
||||||
),
|
|
||||||
'features' => $aFilteredPlaces
|
|
||||||
));
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$aFilteredPlaces = array();
|
|
||||||
foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
|
||||||
$aPlace = array(
|
|
||||||
'type' => 'Feature',
|
|
||||||
'properties' => array(
|
|
||||||
'place_id'=>$aPointDetails['place_id'],
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
|
||||||
if ($sOSMType) {
|
|
||||||
$aPlace['properties']['osm_type'] = $sOSMType;
|
|
||||||
$aPlace['properties']['osm_id'] = $aPointDetails['osm_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['aBoundingBox'])) {
|
|
||||||
$aPlace['bbox'] = array(
|
|
||||||
(float) $aPointDetails['aBoundingBox'][2], // minlon
|
|
||||||
(float) $aPointDetails['aBoundingBox'][0], // minlat
|
|
||||||
(float) $aPointDetails['aBoundingBox'][3], // maxlon
|
|
||||||
(float) $aPointDetails['aBoundingBox'][1] // maxlat
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['zoom'])) {
|
|
||||||
$aPlace['properties']['zoom'] = $aPointDetails['zoom'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$aPlace['properties']['display_name'] = $aPointDetails['name'];
|
|
||||||
|
|
||||||
$aPlace['properties']['place_rank'] = $aPointDetails['rank_search'];
|
|
||||||
$aPlace['properties']['category'] = $aPointDetails['class'];
|
|
||||||
|
|
||||||
$aPlace['properties']['type'] = $aPointDetails['type'];
|
|
||||||
|
|
||||||
$aPlace['properties']['importance'] = $aPointDetails['importance'];
|
|
||||||
|
|
||||||
if (isset($aPointDetails['icon']) && $aPointDetails['icon']) {
|
|
||||||
$aPlace['properties']['icon'] = $aPointDetails['icon'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['address'])) {
|
|
||||||
$aPlace['properties']['address'] = $aPointDetails['address']->getAddressNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aPointDetails['asgeojson'])) {
|
|
||||||
$aPlace['geometry'] = json_decode($aPointDetails['asgeojson']);
|
|
||||||
} else {
|
|
||||||
$aPlace['geometry'] = array(
|
|
||||||
'type' => 'Point',
|
|
||||||
'coordinates' => array(
|
|
||||||
(float) $aPointDetails['lon'],
|
|
||||||
(float) $aPointDetails['lat']
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (isset($aPointDetails['sExtraTags'])) $aPlace['properties']['extratags'] = $aPointDetails['sExtraTags'];
|
|
||||||
if (isset($aPointDetails['sNameDetails'])) $aPlace['properties']['namedetails'] = $aPointDetails['sNameDetails'];
|
|
||||||
|
|
||||||
$aFilteredPlaces[] = $aPlace;
|
|
||||||
}
|
|
||||||
|
|
||||||
javascript_renderData(array(
|
|
||||||
'type' => 'FeatureCollection',
|
|
||||||
'licence' => 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
|
||||||
'features' => $aFilteredPlaces
|
|
||||||
));
|
|
||||||
@@ -1,114 +1,298 @@
|
|||||||
<?php
|
<?php
|
||||||
header("content-type: text/html; charset=UTF-8");
|
header("content-type: text/html; charset=UTF-8");
|
||||||
?>
|
?>
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-header.php'); ?>
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<link href="css/common.css" rel="stylesheet" type="text/css" />
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>OpenStreetMap Nominatim: Search</title>
|
||||||
|
|
||||||
|
<base href="<?php echo CONST_Website_BaseURL;?>" />
|
||||||
|
<link href="nominatim.xml" rel="search" title="Nominatim Search" type="application/opensearchdescription+xml" />
|
||||||
<link href="css/search.css" rel="stylesheet" type="text/css" />
|
<link href="css/search.css" rel="stylesheet" type="text/css" />
|
||||||
|
|
||||||
|
<script src="js/OpenLayers.js" type="text/javascript"></script>
|
||||||
|
<script src="js/tiles.js" type="text/javascript"></script>
|
||||||
|
<script src="js/prototype-1.6.0.3.js" type="text/javascript"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var map;
|
||||||
|
|
||||||
|
function handleResize()
|
||||||
|
{
|
||||||
|
if ($('searchresults'))
|
||||||
|
{
|
||||||
|
var viewwidth = ((document.documentElement.clientWidth > 0?document.documentElement.clientWidth:document.documentElement.offsetWidth) - 200) + 'px';
|
||||||
|
$('map').style.width = viewwidth;
|
||||||
|
$('report').style.width = viewwidth;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$('map').style.width = ((document.documentElement.clientWidth > 0?document.documentElement.clientWidth:document.documentElement.offsetWidth) - 0) + 'px';
|
||||||
|
$('map').style.left = '0px';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($('map')) $('map').style.height = ((document.documentElement.clientHeight > 0?document.documentElement.clientHeight:document.documentElement.offsetHeight) - 38) + 'px';
|
||||||
|
if ($('searchresults')) $('searchresults').style.height = ((document.documentElement.clientHeight > 0?document.documentElement.clientHeight:document.documentElement.offsetHeight) - 38) + 'px';
|
||||||
|
if ($('report')) $('report').style.height = ((document.documentElement.clientHeight > 0?document.documentElement.clientHeight:document.documentElement.offsetHeight) - 38) + 'px';
|
||||||
|
}
|
||||||
|
window.onresize = handleResize;
|
||||||
|
|
||||||
|
function panToLatLon(lat,lon) {
|
||||||
|
var lonLat = new OpenLayers.LonLat(lon, lat).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
|
||||||
|
map.panTo(lonLat, <?php echo $iZoom ?>);
|
||||||
|
}
|
||||||
|
|
||||||
|
function panToLatLonZoom(lat, lon, zoom) {
|
||||||
|
var lonLat = new OpenLayers.LonLat(lon, lat).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
|
||||||
|
if (zoom != map.getZoom())
|
||||||
|
map.setCenter(lonLat, zoom);
|
||||||
|
else
|
||||||
|
map.panTo(lonLat, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function panToLatLonBoundingBox(lat,lon,minlat,maxlat,minlon,maxlon,wkt) {
|
||||||
|
vectorLayer.destroyFeatures();
|
||||||
|
var proj_EPSG4326 = new OpenLayers.Projection("EPSG:4326");
|
||||||
|
var proj_map = map.getProjectionObject();
|
||||||
|
map.zoomToExtent(new OpenLayers.Bounds(minlon,minlat,maxlon,maxlat).transform(proj_EPSG4326, proj_map));
|
||||||
|
var lonLat = new OpenLayers.LonLat(lon, lat).transform(proj_EPSG4326, proj_map);
|
||||||
|
map.panTo(lonLat, <?php echo $iZoom ?>);
|
||||||
|
|
||||||
|
if (wkt)
|
||||||
|
{
|
||||||
|
var freader = new OpenLayers.Format.WKT({
|
||||||
|
'internalProjection': proj_map,
|
||||||
|
'externalProjection': proj_EPSG4326
|
||||||
|
});
|
||||||
|
|
||||||
|
var feature = freader.read(wkt);
|
||||||
|
if (feature)
|
||||||
|
{
|
||||||
|
feature.style = {
|
||||||
|
strokeColor: "#75ADFF",
|
||||||
|
fillColor: "#F0F7FF",
|
||||||
|
strokeWidth: 2,
|
||||||
|
strokeOpacity: 0.75,
|
||||||
|
fillOpacity: 0.75,
|
||||||
|
pointRadius: 100
|
||||||
|
};
|
||||||
|
vectorLayer.addFeatures([feature]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function round(v,n)
|
||||||
|
{
|
||||||
|
n = Math.pow(10,n);
|
||||||
|
return Math.round(v*n)/n;
|
||||||
|
}
|
||||||
|
function floor(v,n)
|
||||||
|
{
|
||||||
|
n = Math.pow(10,n);
|
||||||
|
return Math.floor(v*n)/n;
|
||||||
|
}
|
||||||
|
function ceil(v,n)
|
||||||
|
{
|
||||||
|
n = Math.pow(10,n);
|
||||||
|
return Math.ceil(v*n)/n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapEventMove() {
|
||||||
|
var proj = new OpenLayers.Projection("EPSG:4326");
|
||||||
|
var bounds = map.getExtent();
|
||||||
|
bounds = bounds.transform(map.getProjectionObject(), proj);
|
||||||
|
$('viewbox').value = floor(bounds.left,2)+','+ceil(bounds.top,2)+','+ceil(bounds.right,2)+','+floor(bounds.bottom,2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
handleResize();
|
||||||
|
map = new OpenLayers.Map ("map", {
|
||||||
|
controls:[
|
||||||
|
new OpenLayers.Control.Navigation(),
|
||||||
|
new OpenLayers.Control.PanZoomBar(),
|
||||||
|
new OpenLayers.Control.MousePosition(),
|
||||||
|
new OpenLayers.Control.Attribution()],
|
||||||
|
maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
|
||||||
|
maxResolution: 156543.0399,
|
||||||
|
numZoomLevels: 19,
|
||||||
|
units: 'm',
|
||||||
|
projection: new OpenLayers.Projection("EPSG:900913"),
|
||||||
|
displayProjection: new OpenLayers.Projection("EPSG:4326"),
|
||||||
|
eventListeners: {
|
||||||
|
"moveend": mapEventMove
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
map.addLayer(new OpenLayers.Layer.OSM.<?php echo CONST_Tile_Default;?>("Default"));
|
||||||
|
|
||||||
|
var layer_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']);
|
||||||
|
layer_style.fillOpacity = 0.2;
|
||||||
|
layer_style.graphicOpacity = 1;
|
||||||
|
vectorLayer = new OpenLayers.Layer.Vector("Points", {style: layer_style});
|
||||||
|
map.addLayer(vectorLayer);
|
||||||
|
|
||||||
|
// var lonLat = new OpenLayers.LonLat(<?php echo $fLon ?>, <?php echo $fLat ?>).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
|
||||||
|
// map.setCenter (lonLat, <?php echo $iZoom ?>);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setfocus(field_id) {
|
||||||
|
$(field_id).focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="search-page">
|
<body onload="setfocus('q');">
|
||||||
|
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-top-navigation.php'); ?>
|
<div id="seachheaderfade1"></div><div id="seachheaderfade2"></div><div id="seachheaderfade3"></div><div id="seachheaderfade4"></div>
|
||||||
|
|
||||||
<form class="form-inline" role="search" accept-charset="UTF-8" action="<?php echo CONST_Website_BaseURL; ?>search.php">
|
<div id="seachheader">
|
||||||
<div class="form-group">
|
<form accept-charset="UTF-8" action="<?php echo CONST_Website_BaseURL; ?>search.php" method="get">
|
||||||
<input id="q" name="q" type="text" class="form-control input-sm" placeholder="Search" value="<?php echo htmlspecialchars($sQuery); ?>" >
|
<table border="0" width="100%" summary="header">
|
||||||
</div>
|
<tr>
|
||||||
<div class="form-group search-button-group">
|
<td valign="middle" style="width:30px;"><img alt="logo" src="images/logo.gif" /></td>
|
||||||
<button type="submit" class="btn btn-primary btn-sm">Search</button>
|
<td valign="middle" style="width:400px;"><input id="q" name="q" value="<?php echo htmlspecialchars($sQuery);
|
||||||
<?php if (CONST_Search_AreaPolygons) { ?>
|
?>" style="width:270px;" /><input type="text" id="viewbox" style="width:120px;" name="viewbox" /></td>
|
||||||
<input type="hidden" value="1" name="polygon_geojson" />
|
<td style="width:80px;"><input type="submit" value="Search"/></td>
|
||||||
<?php } ?>
|
<?php if (CONST_Search_AreaPolygons) { ?> <td style="width:100px;"><input type="checkbox" value="1" name="polygon" <?php if ($bAsText) echo "checked='checked'"; ?>/> Highlight</td>
|
||||||
<input type="hidden" name="viewbox" value="<?php if (isset($aMoreParams['viewbox'])) echo ($aMoreParams['viewbox']); ?>" />
|
<td style="text-align:right;">Data: <?php echo $sDataDate; ?></td>
|
||||||
<div class="checkbox-inline">
|
<td style="text-align:right;">
|
||||||
<input type="checkbox" id="use_viewbox" <?php if (isset($aMoreParams['viewbox'])) echo "checked='checked'"; ?>>
|
<a href="http://wiki.openstreetmap.org/wiki/Nominatim" target="_blank">Documentation</a> | <a href="http://wiki.openstreetmap.org/wiki/Nominatim/FAQ"
|
||||||
<label for="use_viewbox">apply viewbox</label>
|
target="_blank">FAQ</a></td>
|
||||||
</div>
|
|
||||||
</div>
|
<?php } ?> <td style="text-align:right;"><?php if ($sQuery) { ?><input type="button" value="Report Problem With Results" onclick="$('report').style.visibility=($('report').style.visibility=='hidden'?'visible':'hidden')"/><?php } ?></td>
|
||||||
<div class="search-type-link">
|
</tr>
|
||||||
<a id="switch-to-reverse" href="<?php echo CONST_Website_BaseURL; ?>reverse.php?format=html">reverse search</a>
|
</table>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
<div id="content">
|
if ($sQuery)
|
||||||
|
{
|
||||||
<?php if ($sQuery) { ?>
|
?>
|
||||||
|
<div id="searchresultsfade1"></div><div id="searchresultsfade2"></div><div id="searchresultsfade3"></div><div id="searchresultsfade4"></div>
|
||||||
<div id="searchresults" class="sidebar">
|
<div id="searchresults">
|
||||||
<?php
|
<?php
|
||||||
$i = 0;
|
if ($sSuggestionURL)
|
||||||
|
{
|
||||||
|
echo '<div class="more"><b>Suggest: </b><a href="'.$sSuggestionURL.'"><b>'.$sSuggestion.'</b></a></div>';
|
||||||
|
}
|
||||||
foreach($aSearchResults as $iResNum => $aResult)
|
foreach($aSearchResults as $iResNum => $aResult)
|
||||||
{
|
{
|
||||||
|
if ($aResult['aBoundingBox'])
|
||||||
echo '<div class="result" data-position=' . $i . '>';
|
{
|
||||||
|
echo '<div class="result" onClick=\'panToLatLonBoundingBox('.$aResult['lat'].', '.$aResult['lon'];
|
||||||
|
echo ', '.$aResult['aBoundingBox'][0];
|
||||||
|
echo ', '.$aResult['aBoundingBox'][1];
|
||||||
|
echo ', '.$aResult['aBoundingBox'][2];
|
||||||
|
echo ', '.$aResult['aBoundingBox'][3];
|
||||||
|
if (isset($aResult['astext'])) echo ', "'.$aResult['astext'].'"';
|
||||||
|
echo ");'>\n";
|
||||||
|
}
|
||||||
|
elseif (isset($aResult['zoom']))
|
||||||
|
{
|
||||||
|
echo '<div class="result" onClick="panToLatLonZoom('.$aResult['lat'].', '.$aResult['lon'].', '.$aResult['zoom'].');">';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo '<div class="result" onClick="panToLatLon('.$aResult['lat'].', '.$aResult['lon'].');">';
|
||||||
|
}
|
||||||
|
|
||||||
echo (isset($aResult['icon'])?'<img alt="icon" src="'.$aResult['icon'].'"/>':'');
|
echo (isset($aResult['icon'])?'<img alt="icon" src="'.$aResult['icon'].'"/>':'');
|
||||||
echo ' <span class="name">'.htmlspecialchars($aResult['name']).'</span>';
|
echo ' <span class="name">'.$aResult['name'].'</span>';
|
||||||
// echo ' <span class="latlon">'.round($aResult['lat'],3).','.round($aResult['lon'],3).'</span>';
|
echo ' <span class="latlon">'.round($aResult['lat'],3).','.round($aResult['lon'],3).'</span>';
|
||||||
// echo ' <span class="place_id">'.$aResult['place_id'].'</span>';
|
echo ' <span class="place_id">'.$aResult['place_id'].'</span>';
|
||||||
if (isset($aResult['label']))
|
if (isset($aResult['label']))
|
||||||
echo ' <span class="type">('.$aResult['label'].')</span>';
|
echo ' <span class="type">('.$aResult['label'].')</span>';
|
||||||
else if ($aResult['type'] == 'yes')
|
|
||||||
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['class'])).')</span>';
|
|
||||||
else
|
else
|
||||||
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['type'])).')</span>';
|
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['type'])).')</span>';
|
||||||
echo ' <a class="btn btn-default btn-xs details" href="details.php?place_id='.$aResult['place_id'].'">details</a>';
|
echo ' <span class="details">(<a href="details.php?place_id='.$aResult['place_id'].'">details</a>)</span>';
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
$i = $i+1;
|
|
||||||
}
|
}
|
||||||
if (!empty($aSearchResults) && $sMoreURL)
|
if (sizeof($aSearchResults))
|
||||||
{
|
{
|
||||||
echo '<div class="more"><a class="btn btn-primary" href="'.htmlentities($sMoreURL).'">Search for more results</a></div>';
|
if ($sMoreURL)
|
||||||
|
{
|
||||||
|
echo '<div class="more"><a href="'.htmlentities($sMoreURL).'">Search for more results</a></div>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
echo '<div class="noresults">No search results found</div>';
|
echo '<div class="noresults">No search results found</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
<div class="disclaimer">Addresses and postcodes are approximate
|
||||||
|
<input type="button" value="Report Problem" onclick="$('report').style.visibility=($('report').style.visibility=='hidden'?'visible':'hidden')"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php } else { ?>
|
|
||||||
|
|
||||||
<div id="intro" class="sidebar">
|
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/introduction.php'); ?>
|
|
||||||
</div>
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
<?php } ?>
|
|
||||||
|
|
||||||
<div id="map-wrapper">
|
|
||||||
<div id="map-position">
|
|
||||||
<div id="map-position-inner"></div>
|
|
||||||
<div id="map-position-close"><a href="#">hide</a></div>
|
|
||||||
</div>
|
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
|
<div id="report" style="visibility:hidden;"><div style="width:600px;margin:auto;margin-top:60px;">
|
||||||
|
<h2>Report a problem</h2>
|
||||||
|
<p>Before reporting problems please read the <a href="http://wiki.openstreetmap.org/wiki/Nominatim">user documentation</a> and <a
|
||||||
|
href="http://wiki.openstreetmap.org/wiki/Nominatim/FAQ">FAQ</a>. If your problem relates to the address of a particular search result please use the 'details' link
|
||||||
|
to check how the address was generated before reporting a problem.</p>
|
||||||
|
<p>Please use <a href="http://trac.openstreetmap.org/newticket?component=nominatim">trac.openstreetmap.org</a> to report problems
|
||||||
|
making sure to set
|
||||||
|
the component to 'nominatim'. You can search for existing bug reports <a href="http://trac.openstreetmap.org/query?status=new&status=assigned&status=reopened&component=nominatim&order=priority">here</a>.</p>
|
||||||
|
<p>Please ensure that you include a full description of the problem, including the search query that you used, the problem with the result and, if
|
||||||
|
the problem relates to missing data, the osm id of the item that is missing. Problems that contain enough detail are likely to get looked at before ones that
|
||||||
|
require significant research!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div> <!-- /content -->
|
<!--
|
||||||
|
<p>Please use this form to report problems with the search results. Of particular interest are items missing, but please also use this form to
|
||||||
|
report any other problems.</p>
|
||||||
|
<p>If your problem relates to the address of a particular search result please use the 'details' link to check how the address was generated before
|
||||||
|
reporting a problem.</p>
|
||||||
|
<p>If you are reporting a missing result please (if possible) include the OSM ID of the item you where expecting (i.e. node 422162)</p>
|
||||||
|
<form method="post">
|
||||||
|
<table>
|
||||||
|
<tr><th>Your Query:</th><td><input type="hidden" name="report:query" value="<?php echo htmlspecialchars($sQuery); ?>" style="width:500px;"><?php echo htmlspecialchars($sQuery); ?></td></tr>
|
||||||
|
<tr><th>Your Email Address(opt):</th><td><input type="text" name="report:email" value="" style="width:500px;"></td></tr>
|
||||||
|
<tr><th>Description of Problem:</th><td><textarea name="report:description" style="width:500px;height:200px;"></textarea></td></tr>
|
||||||
|
<tr><td colspan="2" class="button"><input type="button" value="Cancel" onclick="$('report').style.visibility='hidden'"><input type="submit" value="Report"></td></tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
<h2>Known Problems</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Countries where missed out of the index</li>
|
||||||
|
<li>Area Polygons relate to the search area - not the address area which would make more sense</li>
|
||||||
|
</ul>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
<?php
|
init();
|
||||||
|
<?php
|
||||||
$aNominatimMapInit = array(
|
foreach($aSearchResults as $iResNum => $aResult)
|
||||||
'zoom' => CONST_Default_Zoom,
|
{
|
||||||
'lat' => CONST_Default_Lat,
|
if ($aResult['aBoundingBox'])
|
||||||
'lon' => CONST_Default_Lon,
|
{
|
||||||
'tile_url' => CONST_Map_Tile_URL,
|
echo 'panToLatLonBoundingBox('.$aResult['lat'].', '.$aResult['lon'];
|
||||||
'tile_attribution' => CONST_Map_Tile_Attribution
|
echo ', '.$aResult['aBoundingBox'][0];
|
||||||
);
|
echo ', '.$aResult['aBoundingBox'][1];
|
||||||
echo 'var nominatim_map_init = ' . json_encode($aNominatimMapInit, JSON_PRETTY_PRINT) . ';';
|
echo ', '.$aResult['aBoundingBox'][2];
|
||||||
|
echo ', '.$aResult['aBoundingBox'][3];
|
||||||
echo 'var nominatim_results = ' . json_encode($aSearchResults, JSON_PRETTY_PRINT) . ';';
|
if (isset($aResult['astext'])) echo ", '".$aResult['astext']."'";
|
||||||
?>
|
echo ');'."\n";
|
||||||
</script>
|
}
|
||||||
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
|
else
|
||||||
|
{
|
||||||
|
echo 'panToLatLonZoom('.$fLat.', '.$fLon.', '.$iZoom.');'."\n";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!sizeof($aSearchResults))
|
||||||
|
{
|
||||||
|
echo 'panToLatLonZoom('.$fLat.', '.$fLon.', '.$iZoom.');'."\n";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,73 +1,80 @@
|
|||||||
<?php
|
<?php
|
||||||
|
header("content-type: application/json; charset=UTF-8");
|
||||||
|
|
||||||
$aFilteredPlaces = array();
|
$aFilteredPlaces = array();
|
||||||
foreach ($aSearchResults as $iResNum => $aPointDetails) {
|
foreach($aSearchResults as $iResNum => $aPointDetails)
|
||||||
|
{
|
||||||
$aPlace = array(
|
$aPlace = array(
|
||||||
'place_id'=>$aPointDetails['place_id'],
|
'place_id'=>$aPointDetails['place_id'],
|
||||||
'licence'=>'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
|
'licence'=>"Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright",
|
||||||
);
|
);
|
||||||
|
|
||||||
$sOSMType = formatOSMType($aPointDetails['osm_type']);
|
$sOSMType = ($aPointDetails['osm_type'] == 'N'?'node':($aPointDetails['osm_type'] == 'W'?'way':($aPointDetails['osm_type'] == 'R'?'relation':'')));
|
||||||
if ($sOSMType) {
|
if ($sOSMType)
|
||||||
|
{
|
||||||
$aPlace['osm_type'] = $sOSMType;
|
$aPlace['osm_type'] = $sOSMType;
|
||||||
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['aBoundingBox'])) {
|
if (isset($aPointDetails['aBoundingBox']))
|
||||||
$aPlace['boundingbox'] = $aPointDetails['aBoundingBox'];
|
{
|
||||||
|
$aPlace['boundingbox'] = array(
|
||||||
|
$aPointDetails['aBoundingBox'][0],
|
||||||
|
$aPointDetails['aBoundingBox'][1],
|
||||||
|
$aPointDetails['aBoundingBox'][2],
|
||||||
|
$aPointDetails['aBoundingBox'][3]);
|
||||||
|
|
||||||
if (isset($aPointDetails['aPolyPoints'])) {
|
if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons)
|
||||||
|
{
|
||||||
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['zoom'])) {
|
if (isset($aPointDetails['zoom']))
|
||||||
|
{
|
||||||
$aPlace['zoom'] = $aPointDetails['zoom'];
|
$aPlace['zoom'] = $aPointDetails['zoom'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$aPlace['lat'] = $aPointDetails['lat'];
|
$aPlace['lat'] = $aPointDetails['lat'];
|
||||||
$aPlace['lon'] = $aPointDetails['lon'];
|
$aPlace['lon'] = $aPointDetails['lon'];
|
||||||
|
|
||||||
$aPlace['display_name'] = $aPointDetails['name'];
|
$aPlace['display_name'] = $aPointDetails['name'];
|
||||||
|
|
||||||
if ($sOutputFormat == 'jsonv2' || $sOutputFormat == 'geojson') {
|
|
||||||
$aPlace['place_rank'] = $aPointDetails['rank_search'];
|
|
||||||
$aPlace['category'] = $aPointDetails['class'];
|
|
||||||
} else {
|
|
||||||
$aPlace['class'] = $aPointDetails['class'];
|
$aPlace['class'] = $aPointDetails['class'];
|
||||||
}
|
|
||||||
$aPlace['type'] = $aPointDetails['type'];
|
$aPlace['type'] = $aPointDetails['type'];
|
||||||
|
|
||||||
$aPlace['importance'] = $aPointDetails['importance'];
|
$aPlace['importance'] = $aPointDetails['importance'];
|
||||||
|
|
||||||
if (isset($aPointDetails['icon']) && $aPointDetails['icon']) {
|
if (isset($aPointDetails['icon']) && $aPointDetails['icon'])
|
||||||
|
{
|
||||||
$aPlace['icon'] = $aPointDetails['icon'];
|
$aPlace['icon'] = $aPointDetails['icon'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['address'])) {
|
if (isset($aPointDetails['address']))
|
||||||
$aPlace['address'] = $aPointDetails['address']->getAddressNames();
|
{
|
||||||
|
$aPlace['address'] = $aPointDetails['address'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['asgeojson'])) {
|
if (isset($aPointDetails['asgeojson']))
|
||||||
|
{
|
||||||
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['assvg'])) {
|
if (isset($aPointDetails['assvg']))
|
||||||
|
{
|
||||||
$aPlace['svg'] = $aPointDetails['assvg'];
|
$aPlace['svg'] = $aPointDetails['assvg'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['astext'])) {
|
if (isset($aPointDetails['astext']))
|
||||||
|
{
|
||||||
$aPlace['geotext'] = $aPointDetails['astext'];
|
$aPlace['geotext'] = $aPointDetails['astext'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['askml'])) {
|
if (isset($aPointDetails['askml']))
|
||||||
|
{
|
||||||
$aPlace['geokml'] = $aPointDetails['askml'];
|
$aPlace['geokml'] = $aPointDetails['askml'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aPointDetails['sExtraTags'])) $aPlace['extratags'] = $aPointDetails['sExtraTags'];
|
|
||||||
if (isset($aPointDetails['sNameDetails'])) $aPlace['namedetails'] = $aPointDetails['sNameDetails'];
|
|
||||||
|
|
||||||
$aFilteredPlaces[] = $aPlace;
|
$aFilteredPlaces[] = $aPlace;
|
||||||
}
|
}
|
||||||
|
|
||||||
javascript_renderData($aFilteredPlaces);
|
javascript_renderData($aFilteredPlaces);
|
||||||
|
|||||||
79
lib/template/search-jsonv2.php
Normal file
79
lib/template/search-jsonv2.php
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
$aFilteredPlaces = array();
|
||||||
|
foreach($aSearchResults as $iResNum => $aPointDetails)
|
||||||
|
{
|
||||||
|
$aPlace = array(
|
||||||
|
'place_id'=>$aPointDetails['place_id'],
|
||||||
|
'licence'=>"Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright",
|
||||||
|
);
|
||||||
|
|
||||||
|
$sOSMType = ($aPointDetails['osm_type'] == 'N'?'node':($aPointDetails['osm_type'] == 'W'?'way':($aPointDetails['osm_type'] == 'R'?'relation':'')));
|
||||||
|
if ($sOSMType)
|
||||||
|
{
|
||||||
|
$aPlace['osm_type'] = $sOSMType;
|
||||||
|
$aPlace['osm_id'] = $aPointDetails['osm_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['aBoundingBox']))
|
||||||
|
{
|
||||||
|
$aPlace['boundingbox'] = array(
|
||||||
|
$aPointDetails['aBoundingBox'][0],
|
||||||
|
$aPointDetails['aBoundingBox'][1],
|
||||||
|
$aPointDetails['aBoundingBox'][2],
|
||||||
|
$aPointDetails['aBoundingBox'][3]);
|
||||||
|
|
||||||
|
if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons)
|
||||||
|
{
|
||||||
|
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['zoom']))
|
||||||
|
{
|
||||||
|
$aPlace['zoom'] = $aPointDetails['zoom'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aPlace['lat'] = $aPointDetails['lat'];
|
||||||
|
$aPlace['lon'] = $aPointDetails['lon'];
|
||||||
|
$aPlace['display_name'] = $aPointDetails['name'];
|
||||||
|
$aPlace['place_rank'] = $aPointDetails['rank_search'];
|
||||||
|
|
||||||
|
$aPlace['category'] = $aPointDetails['class'];
|
||||||
|
$aPlace['type'] = $aPointDetails['type'];
|
||||||
|
|
||||||
|
$aPlace['importance'] = $aPointDetails['importance'];
|
||||||
|
|
||||||
|
if (isset($aPointDetails['icon']))
|
||||||
|
{
|
||||||
|
$aPlace['icon'] = $aPointDetails['icon'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['address']) && sizeof($aPointDetails['address'])>0)
|
||||||
|
{
|
||||||
|
$aPlace['address'] = $aPointDetails['address'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['asgeojson']))
|
||||||
|
{
|
||||||
|
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['assvg']))
|
||||||
|
{
|
||||||
|
$aPlace['svg'] = $aPointDetails['assvg'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['astext']))
|
||||||
|
{
|
||||||
|
$aPlace['geotext'] = $aPointDetails['astext'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($aPointDetails['askml']))
|
||||||
|
{
|
||||||
|
$aPlace['geokml'] = $aPointDetails['askml'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aFilteredPlaces[] = $aPlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
javascript_renderData($aFilteredPlaces, array('geojson'));
|
||||||
@@ -1,63 +1,77 @@
|
|||||||
<?php
|
<?php
|
||||||
header('content-type: text/xml; charset=UTF-8');
|
header("content-type: text/xml; charset=UTF-8");
|
||||||
|
|
||||||
echo '<';
|
echo "<";
|
||||||
echo '?xml version="1.0" encoding="UTF-8" ?';
|
echo "?xml version=\"1.0\" encoding=\"UTF-8\" ?";
|
||||||
echo ">\n";
|
echo ">\n";
|
||||||
|
|
||||||
echo '<';
|
echo "<searchresults";
|
||||||
echo (isset($sXmlRootTag)?$sXmlRootTag:'searchresults');
|
echo " timestamp='".date(DATE_RFC822)."'";
|
||||||
echo " timestamp='".date(DATE_RFC822)."'";
|
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
|
||||||
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
|
echo " querystring='".htmlspecialchars($sQuery, ENT_QUOTES)."'";
|
||||||
echo " querystring='".htmlspecialchars($sQuery, ENT_QUOTES)."'";
|
if ($sViewBox) echo " viewbox='".htmlspecialchars($sViewBox, ENT_QUOTES)."'";
|
||||||
if (isset($aMoreParams['viewbox'])) echo " viewbox='".htmlspecialchars($aMoreParams['viewbox'], ENT_QUOTES)."'";
|
echo " polygon='".($bShowPolygons?'true':'false')."'";
|
||||||
echo " polygon='".(isset($aMoreParams['polygon'])?'true':'false')."'";
|
if (sizeof($aExcludePlaceIDs))
|
||||||
if (isset($aMoreParams['exclude_place_ids'])) {
|
{
|
||||||
echo " exclude_place_ids='".htmlspecialchars($aMoreParams['exclude_place_ids'])."'";
|
echo " exclude_place_ids='".htmlspecialchars(join(',',$aExcludePlaceIDs))."'";
|
||||||
}
|
}
|
||||||
echo " more_url='".htmlspecialchars($sMoreURL)."'";
|
if ($sMoreURL)
|
||||||
echo ">\n";
|
{
|
||||||
|
echo " more_url='".htmlspecialchars($sMoreURL)."'";
|
||||||
|
}
|
||||||
|
echo ">\n";
|
||||||
|
|
||||||
foreach ($aSearchResults as $iResNum => $aResult) {
|
foreach($aSearchResults as $iResNum => $aResult)
|
||||||
|
{
|
||||||
echo "<place place_id='".$aResult['place_id']."'";
|
echo "<place place_id='".$aResult['place_id']."'";
|
||||||
$sOSMType = formatOSMType($aResult['osm_type']);
|
$sOSMType = ($aResult['osm_type'] == 'N'?'node':($aResult['osm_type'] == 'W'?'way':($aResult['osm_type'] == 'R'?'relation':'')));
|
||||||
if ($sOSMType) {
|
if ($sOSMType)
|
||||||
|
{
|
||||||
echo " osm_type='$sOSMType'";
|
echo " osm_type='$sOSMType'";
|
||||||
echo " osm_id='".$aResult['osm_id']."'";
|
echo " osm_id='".$aResult['osm_id']."'";
|
||||||
}
|
}
|
||||||
echo " place_rank='".$aResult['rank_search']."'";
|
echo " place_rank='".$aResult['rank_search']."'";
|
||||||
|
|
||||||
if (isset($aResult['aBoundingBox'])) {
|
if (isset($aResult['aBoundingBox']))
|
||||||
|
{
|
||||||
echo ' boundingbox="';
|
echo ' boundingbox="';
|
||||||
echo join(',', $aResult['aBoundingBox']);
|
echo $aResult['aBoundingBox'][0];
|
||||||
|
echo ','.$aResult['aBoundingBox'][1];
|
||||||
|
echo ','.$aResult['aBoundingBox'][2];
|
||||||
|
echo ','.$aResult['aBoundingBox'][3];
|
||||||
echo '"';
|
echo '"';
|
||||||
|
|
||||||
if (isset($aResult['aPolyPoints'])) {
|
if ($bShowPolygons && isset($aResult['aPolyPoints']))
|
||||||
|
{
|
||||||
echo ' polygonpoints=\'';
|
echo ' polygonpoints=\'';
|
||||||
echo json_encode($aResult['aPolyPoints']);
|
echo json_encode($aResult['aPolyPoints']);
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['asgeojson'])) {
|
if (isset($aResult['asgeojson']))
|
||||||
|
{
|
||||||
echo ' geojson=\'';
|
echo ' geojson=\'';
|
||||||
echo $aResult['asgeojson'];
|
echo $aResult['asgeojson'];
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['assvg'])) {
|
if (isset($aResult['assvg']))
|
||||||
|
{
|
||||||
echo ' geosvg=\'';
|
echo ' geosvg=\'';
|
||||||
echo $aResult['assvg'];
|
echo $aResult['assvg'];
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['astext'])) {
|
if (isset($aResult['astext']))
|
||||||
|
{
|
||||||
echo ' geotext=\'';
|
echo ' geotext=\'';
|
||||||
echo $aResult['astext'];
|
echo $aResult['astext'];
|
||||||
echo '\'';
|
echo '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['zoom'])) {
|
if (isset($aResult['zoom']))
|
||||||
|
{
|
||||||
echo " zoom='".$aResult['zoom']."'";
|
echo " zoom='".$aResult['zoom']."'";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,67 +82,43 @@ foreach ($aSearchResults as $iResNum => $aResult) {
|
|||||||
echo " class='".htmlspecialchars($aResult['class'])."'";
|
echo " class='".htmlspecialchars($aResult['class'])."'";
|
||||||
echo " type='".htmlspecialchars($aResult['type'], ENT_QUOTES)."'";
|
echo " type='".htmlspecialchars($aResult['type'], ENT_QUOTES)."'";
|
||||||
echo " importance='".htmlspecialchars($aResult['importance'])."'";
|
echo " importance='".htmlspecialchars($aResult['importance'])."'";
|
||||||
if (isset($aResult['icon']) && $aResult['icon']) {
|
if (isset($aResult['icon']) && $aResult['icon'])
|
||||||
|
{
|
||||||
echo " icon='".htmlspecialchars($aResult['icon'], ENT_QUOTES)."'";
|
echo " icon='".htmlspecialchars($aResult['icon'], ENT_QUOTES)."'";
|
||||||
}
|
}
|
||||||
|
|
||||||
$bHasDelim = false;
|
if (isset($aResult['address']) || isset($aResult['askml']))
|
||||||
|
{
|
||||||
if (isset($aResult['askml'])) {
|
echo ">";
|
||||||
if (!$bHasDelim) {
|
|
||||||
$bHasDelim = true;
|
|
||||||
echo '>';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($aResult['askml']))
|
||||||
|
{
|
||||||
echo "\n<geokml>";
|
echo "\n<geokml>";
|
||||||
echo $aResult['askml'];
|
echo $aResult['askml'];
|
||||||
echo '</geokml>';
|
echo "</geokml>";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($aResult['sExtraTags'])) {
|
if (isset($aResult['address']))
|
||||||
if (!$bHasDelim) {
|
{
|
||||||
$bHasDelim = true;
|
|
||||||
echo '>';
|
|
||||||
}
|
|
||||||
echo "\n<extratags>";
|
|
||||||
foreach ($aResult['sExtraTags'] as $sKey => $sValue) {
|
|
||||||
echo '<tag key="'.htmlspecialchars($sKey).'" value="'.htmlspecialchars($sValue).'"/>';
|
|
||||||
}
|
|
||||||
echo '</extratags>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aResult['sNameDetails'])) {
|
|
||||||
if (!$bHasDelim) {
|
|
||||||
$bHasDelim = true;
|
|
||||||
echo '>';
|
|
||||||
}
|
|
||||||
echo "\n<namedetails>";
|
|
||||||
foreach ($aResult['sNameDetails'] as $sKey => $sValue) {
|
|
||||||
echo '<name desc="'.htmlspecialchars($sKey).'">';
|
|
||||||
echo htmlspecialchars($sValue);
|
|
||||||
echo '</name>';
|
|
||||||
}
|
|
||||||
echo '</namedetails>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aResult['address'])) {
|
|
||||||
if (!$bHasDelim) {
|
|
||||||
$bHasDelim = true;
|
|
||||||
echo '>';
|
|
||||||
}
|
|
||||||
echo "\n";
|
echo "\n";
|
||||||
foreach ($aResult['address']->getAddressNames() as $sKey => $sValue) {
|
foreach($aResult['address'] as $sKey => $sValue)
|
||||||
$sKey = str_replace(' ', '_', $sKey);
|
{
|
||||||
|
$sKey = str_replace(' ','_',$sKey);
|
||||||
echo "<$sKey>";
|
echo "<$sKey>";
|
||||||
echo htmlspecialchars($sValue);
|
echo htmlspecialchars($sValue);
|
||||||
echo "</$sKey>";
|
echo "</$sKey>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bHasDelim) {
|
if (isset($aResult['address']) || isset($aResult['askml']))
|
||||||
echo '</place>';
|
{
|
||||||
} else {
|
echo "</place>";
|
||||||
echo '/>';
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "/>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
echo '</' . (isset($sXmlRootTag)?$sXmlRootTag:'searchresults') . '>';
|
echo "</searchresults>";
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
# just use the pgxs makefile
|
|
||||||
ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/dummy
|
|
||||||
COMMAND PGXS=${PGXS} PG_CONFIG=${PG_CONFIG} MODSRCDIR=${CMAKE_CURRENT_SOURCE_DIR} $(MAKE) -f ${CMAKE_CURRENT_SOURCE_DIR}/Makefile
|
|
||||||
COMMENT "Running external makefile ${PGXS}"
|
|
||||||
)
|
|
||||||
|
|
||||||
ADD_CUSTOM_TARGET( nominatim_lib ALL
|
|
||||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/dummy
|
|
||||||
)
|
|
||||||
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
MODULES = nominatim
|
MODULES = nominatim
|
||||||
PG_CPPFLAGS = -I$(MODSRCDIR)
|
PGXS := @POSTGRESQL_PGXS@
|
||||||
|
PG_CONFIG := @PG_CONFIG@
|
||||||
include $(PGXS)
|
include $(PGXS)
|
||||||
|
|
||||||
VPATH = $(MODSRCDIR)
|
|
||||||
|
|
||||||
all:
|
all:
|
||||||
chmod 755 nominatim.so
|
chmod 755 nominatim.so
|
||||||
|
|
||||||
@@ -157,18 +157,17 @@ transliteration( PG_FUNCTION_ARGS )
|
|||||||
PG_RETURN_TEXT_P(result);
|
PG_RETURN_TEXT_P(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set isspace=1 if the replacement _only_ adds a space before the search string. I.e. to == " " + from
|
|
||||||
void str_replace(char* buffer, int* len, int* changes, char* from, int fromlen, char* to, int tolen, int isspace)
|
void str_replace(char* buffer, int* len, int* changes, char* from, int fromlen, char* to, int tolen, int isspace)
|
||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
// Search string is too long to be present
|
// Search string is too long to be pressent
|
||||||
if (fromlen > *len) return;
|
if (fromlen > *len) return;
|
||||||
|
|
||||||
p = strstr(buffer, from);
|
p = strstr(buffer, from);
|
||||||
while(p)
|
while(p)
|
||||||
{
|
{
|
||||||
if (!isspace || (p > buffer && *(p-1) != ' '))
|
if (!isspace || *(p-1) != ' ')
|
||||||
{
|
{
|
||||||
(*changes)++;
|
(*changes)++;
|
||||||
if (tolen != fromlen) memmove(p+tolen, p+fromlen, *len-(p-buffer)+1);
|
if (tolen != fromlen) memmove(p+tolen, p+fromlen, *len-(p-buffer)+1);
|
||||||
@@ -231,7 +230,7 @@ gettokenstring( PG_FUNCTION_ARGS )
|
|||||||
sourcedata = (unsigned char *)VARDATA(source);
|
sourcedata = (unsigned char *)VARDATA(source);
|
||||||
sourcedatalength = VARSIZE(source) - VARHDRSZ;
|
sourcedatalength = VARSIZE(source) - VARHDRSZ;
|
||||||
|
|
||||||
// Buffer for doing the replace in - string could get slightly longer (double is massive overkill)
|
// Buffer for doing the replace in - string could get slightly longer (double is mastive overkill)
|
||||||
buffer = (char *)palloc((sourcedatalength*2)*sizeof(char));
|
buffer = (char *)palloc((sourcedatalength*2)*sizeof(char));
|
||||||
memcpy(buffer+1, sourcedata, sourcedatalength);
|
memcpy(buffer+1, sourcedata, sourcedatalength);
|
||||||
buffer[0] = 32;
|
buffer[0] = 32;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,12 +0,0 @@
|
|||||||
add_executable(nominatim export.c geometry.cpp import.c index.c input.c nominatim.c postgresql.c sprompt.c)
|
|
||||||
|
|
||||||
CHECK_SYMBOL_EXISTS(bswap_32 "byteswap.h" HAVE_BYTESWAP)
|
|
||||||
CHECK_SYMBOL_EXISTS(bswap32 "sys/endian.h" HAVE_SYS_ENDIAN)
|
|
||||||
|
|
||||||
target_compile_definitions(nominatim
|
|
||||||
PRIVATE HAVE_BYTESWAP=$<BOOL:${HAVE_BYTESWAP}>
|
|
||||||
PRIVATE HAVE_SYS_ENDIAN=$<BOOL:${HAVE_SYS_ENDIAN}>
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(nominatim ${LIBXML2_LIBRARIES} ${ZLIB_LIBRARIES} ${BZIP2_LIBRARIES} ${PostgreSQL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
|
|
||||||
9
nominatim/Makefile.am
Normal file
9
nominatim/Makefile.am
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
bin_PROGRAMS = nominatim
|
||||||
|
|
||||||
|
nominatim_SOURCES = export.c geometry.cpp import.c index.c input.c nominatim.c postgresql.c sprompt.c
|
||||||
|
|
||||||
|
AM_CFLAGS = @PTHREAD_CFLAGS@ @POSTGRESQL_CFLAGS@ @XML_CPPFLAGS@ @BZIP2_CFLAGS@ @GEOS_CFLAGS@ @PROJ_CFLAGS@ -DVERSION='"@PACKAGE_VERSION@"'
|
||||||
|
AM_CPPFLAGS = @PTHREAD_CFLAGS@ @POSTGRESQL_CFLAGS@ @XML_CPPFLAGS@ @BZIP2_CFLAGS@ @GEOS_CFLAGS@ @PROJ_CFLAGS@
|
||||||
|
|
||||||
|
nominatim_LDADD = @PTHREAD_CFLAGS@ @POSTGRESQL_LDFLAGS@ @POSTGRESQL_LIBS@ @XML_LIBS@ @BZIP2_LDFLAGS@ @BZIP2_LIBS@ @GEOS_LDFLAGS@ @GEOS_LIBS@ @PROJ_LDFLAGS@ @PROJ_LIBS@ -lz
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ void nominatim_exportCreatePreparedQueries(PGconn * conn)
|
|||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT8;
|
pg_prepare_params[0] = PG_OID_INT8;
|
||||||
res = PQprepare(conn, "placex_details",
|
res = PQprepare(conn, "placex_details",
|
||||||
"select placex.osm_type, placex.osm_id, placex.class, placex.type, placex.name, placex.housenumber, placex.country_code, ST_AsText(placex.geometry), placex.admin_level, placex.rank_address, placex.rank_search, placex.parent_place_id, parent.osm_type, parent.osm_id, placex.indexed_status, placex.linked_place_id from placex left outer join placex as parent on (placex.parent_place_id = parent.place_id) where placex.place_id = $1",
|
"select placex.osm_type, placex.osm_id, placex.class, placex.type, placex.name, placex.housenumber, placex.country_code, ST_AsText(placex.geometry), placex.admin_level, placex.rank_address, placex.rank_search, placex.parent_place_id, parent.osm_type, parent.osm_id, placex.indexed_status from placex left outer join placex as parent on (placex.parent_place_id = parent.place_id) where placex.place_id = $1",
|
||||||
1, pg_prepare_params);
|
1, pg_prepare_params);
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
{
|
{
|
||||||
@@ -417,7 +417,6 @@ void nominatim_exportPlace(uint64_t place_id, PGconn * conn,
|
|||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_place_id", BAD_CAST PQgetvalue(querySet.res, 0, 11));
|
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_place_id", BAD_CAST PQgetvalue(querySet.res, 0, 11));
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_type", BAD_CAST PQgetvalue(querySet.res, 0, 12));
|
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_type", BAD_CAST PQgetvalue(querySet.res, 0, 12));
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_id", BAD_CAST PQgetvalue(querySet.res, 0, 13));
|
xmlTextWriterWriteAttribute(writer, BAD_CAST "parent_id", BAD_CAST PQgetvalue(querySet.res, 0, 13));
|
||||||
xmlTextWriterWriteAttribute(writer, BAD_CAST "linked_place_id", BAD_CAST PQgetvalue(querySet.res, 0, 15));
|
|
||||||
|
|
||||||
if (PQntuples(querySet.resNames))
|
if (PQntuples(querySet.resNames))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -486,7 +486,7 @@ void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
|
|||||||
}
|
}
|
||||||
paramValues[7] = (const char *)featureExtraTagString;
|
paramValues[7] = (const char *)featureExtraTagString;
|
||||||
|
|
||||||
if (xmlStrlen(feature.parentPlaceID) == 0)
|
if (strlen(feature.parentPlaceID) == 0)
|
||||||
paramValues[8] = "0";
|
paramValues[8] = "0";
|
||||||
else
|
else
|
||||||
paramValues[8] = (const char *)feature.parentPlaceID;
|
paramValues[8] = (const char *)feature.parentPlaceID;
|
||||||
@@ -541,10 +541,10 @@ void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
|
|||||||
|
|
||||||
if (featureNameLines)
|
if (featureNameLines)
|
||||||
{
|
{
|
||||||
if (xmlStrlen(feature.parentPlaceID) > 0 && featureAddressLines == 0)
|
if (strlen(feature.parentPlaceID) > 0 && featureAddressLines == 0)
|
||||||
{
|
{
|
||||||
paramValues[0] = (const char *)place_id;
|
paramValues[0] = (const char *)place_id;
|
||||||
paramValues[1] = (const char *)feature.parentPlaceID;
|
paramValues[1] = feature.parentPlaceID;
|
||||||
if (verbose) fprintf(stderr, "search_name_from_parent_insert: INSERT %s %s\n", paramValues[0], paramValues[1]);
|
if (verbose) fprintf(stderr, "search_name_from_parent_insert: INSERT %s %s\n", paramValues[0], paramValues[1]);
|
||||||
res = PQexecPrepared(conn, "search_name_from_parent_insert", 2, paramValues, NULL, NULL, 0);
|
res = PQexecPrepared(conn, "search_name_from_parent_insert", 2, paramValues, NULL, NULL, 0);
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
* triggers indexing (reparenting etc.) through setting resetting indexed_status: update placex/osmline set indexed_status = 0 where indexed_status > 0
|
|
||||||
* triggers placex_update and osmline_update
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -21,21 +19,24 @@
|
|||||||
|
|
||||||
extern int verbose;
|
extern int verbose;
|
||||||
|
|
||||||
void run_indexing(int rank, int interpolation, PGconn *conn, int num_threads,
|
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile)
|
||||||
struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|
||||||
{
|
{
|
||||||
int tuples, count, sleepcount;
|
struct index_thread_data * thread_data;
|
||||||
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
int tuples, count, sleepcount;
|
||||||
|
|
||||||
time_t rankStartTime;
|
time_t rankStartTime;
|
||||||
int rankTotalTuples;
|
int rankTotalTuples;
|
||||||
int rankCountTuples;
|
int rankCountTuples;
|
||||||
float rankPerSecond;
|
float rankPerSecond;
|
||||||
|
|
||||||
|
PGconn *conn;
|
||||||
|
PGresult * res;
|
||||||
PGresult * resSectors;
|
PGresult * resSectors;
|
||||||
PGresult * resPlaces;
|
PGresult * resPlaces;
|
||||||
PGresult * resNULL;
|
PGresult * resNULL;
|
||||||
|
|
||||||
|
int rank;
|
||||||
int i;
|
int i;
|
||||||
int iSector;
|
int iSector;
|
||||||
int iResult;
|
int iResult;
|
||||||
@@ -50,6 +51,93 @@ struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|||||||
xmlTextWriterPtr writer;
|
xmlTextWriterPtr writer;
|
||||||
pthread_mutex_t writer_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t writer_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
Oid pg_prepare_params[2];
|
||||||
|
|
||||||
|
conn = PQconnectdb(conninfo);
|
||||||
|
if (PQstatus(conn) != CONNECTION_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pg_prepare_params[0] = PG_OID_INT4;
|
||||||
|
res = PQprepare(conn, "index_sectors",
|
||||||
|
"select geometry_sector,count(*) from placex where rank_search = $1 and indexed_status > 0 group by geometry_sector order by geometry_sector",
|
||||||
|
1, pg_prepare_params);
|
||||||
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed preparing index_sectors: %s\n", PQerrorMessage(conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
pg_prepare_params[0] = PG_OID_INT4;
|
||||||
|
res = PQprepare(conn, "index_nosectors",
|
||||||
|
"select 0::integer,count(*) from placex where rank_search = $1 and indexed_status > 0",
|
||||||
|
1, pg_prepare_params);
|
||||||
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed preparing index_sectors: %s\n", PQerrorMessage(conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
pg_prepare_params[0] = PG_OID_INT4;
|
||||||
|
pg_prepare_params[1] = PG_OID_INT4;
|
||||||
|
res = PQprepare(conn, "index_sector_places",
|
||||||
|
"select place_id from placex where rank_search = $1 and geometry_sector = $2 and indexed_status > 0",
|
||||||
|
2, pg_prepare_params);
|
||||||
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed preparing index_sector_places: %s\n", PQerrorMessage(conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
pg_prepare_params[0] = PG_OID_INT4;
|
||||||
|
res = PQprepare(conn, "index_nosector_places",
|
||||||
|
"select place_id from placex where rank_search = $1 and indexed_status > 0 order by geometry_sector",
|
||||||
|
1, pg_prepare_params);
|
||||||
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed preparing index_nosector_places: %s\n", PQerrorMessage(conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
// Build the data for each thread
|
||||||
|
thread_data = (struct index_thread_data *)malloc(sizeof(struct index_thread_data)*num_threads);
|
||||||
|
for (i = 0; i < num_threads; i++)
|
||||||
|
{
|
||||||
|
thread_data[i].conn = PQconnectdb(conninfo);
|
||||||
|
if (PQstatus(thread_data[i].conn) != CONNECTION_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(thread_data[i].conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pg_prepare_params[0] = PG_OID_INT8;
|
||||||
|
res = PQprepare(thread_data[i].conn, "index_placex",
|
||||||
|
"update placex set indexed_status = 0 where place_id = $1",
|
||||||
|
1, pg_prepare_params);
|
||||||
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed preparing index_placex: %s\n", PQerrorMessage(conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
/*res = PQexec(thread_data[i].conn, "set enable_seqscan = false");
|
||||||
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed disabling sequential scan: %s\n", PQerrorMessage(conn));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
PQclear(res);*/
|
||||||
|
|
||||||
|
nominatim_exportCreatePreparedQueries(thread_data[i].conn);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the output file
|
// Create the output file
|
||||||
writer = NULL;
|
writer = NULL;
|
||||||
if (structuredoutputfile)
|
if (structuredoutputfile)
|
||||||
@@ -57,15 +145,11 @@ struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|||||||
writer = nominatim_exportXMLStart(structuredoutputfile);
|
writer = nominatim_exportXMLStart(structuredoutputfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interpolation)
|
fprintf(stderr, "Starting indexing rank (%i to %i) using %i threads\n", rank_min, rank_max, num_threads);
|
||||||
{
|
|
||||||
fprintf(stderr, "Starting interpolation lines (location_property_osmline)\n");
|
for (rank = rank_min; rank <= rank_max; rank++)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Starting rank %d\n", rank);
|
fprintf(stderr, "Starting rank %d\n", rank);
|
||||||
}
|
|
||||||
|
|
||||||
rankCountTuples = 0;
|
rankCountTuples = 0;
|
||||||
rankPerSecond = 0;
|
rankPerSecond = 0;
|
||||||
|
|
||||||
@@ -73,15 +157,11 @@ struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|||||||
paramValues[0] = (char *)¶mRank;
|
paramValues[0] = (char *)¶mRank;
|
||||||
paramLengths[0] = sizeof(paramRank);
|
paramLengths[0] = sizeof(paramRank);
|
||||||
paramFormats[0] = 1;
|
paramFormats[0] = 1;
|
||||||
|
// if (rank < 16)
|
||||||
if (interpolation)
|
// resSectors = PQexecPrepared(conn, "index_nosectors", 1, paramValues, paramLengths, paramFormats, 1);
|
||||||
{
|
// else
|
||||||
resSectors = PQexecPrepared(conn, "index_sectors_osmline", 0, NULL, 0, NULL, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resSectors = PQexecPrepared(conn, "index_sectors", 1, paramValues, paramLengths, paramFormats, 1);
|
resSectors = PQexecPrepared(conn, "index_sectors", 1, paramValues, paramLengths, paramFormats, 1);
|
||||||
}
|
|
||||||
if (PQresultStatus(resSectors) != PGRES_TUPLES_OK)
|
if (PQresultStatus(resSectors) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "index_sectors: SELECT failed: %s", PQerrorMessage(conn));
|
fprintf(stderr, "index_sectors: SELECT failed: %s", PQerrorMessage(conn));
|
||||||
@@ -140,41 +220,20 @@ struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|||||||
|
|
||||||
// Get all the place_id's for this sector
|
// Get all the place_id's for this sector
|
||||||
paramRank = PGint32(rank);
|
paramRank = PGint32(rank);
|
||||||
|
paramValues[0] = (char *)¶mRank;
|
||||||
|
paramLengths[0] = sizeof(paramRank);
|
||||||
|
paramFormats[0] = 1;
|
||||||
paramSector = PGint32(sector);
|
paramSector = PGint32(sector);
|
||||||
if (rankTotalTuples-rankCountTuples < num_threads*1000)
|
|
||||||
{
|
|
||||||
// no sectors
|
|
||||||
if (interpolation)
|
|
||||||
{
|
|
||||||
iResult = PQsendQueryPrepared(conn, "index_nosector_places_osmline", 0, NULL, 0, NULL, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
paramValues[0] = (char *)¶mRank;
|
|
||||||
paramLengths[0] = sizeof(paramRank);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
iResult = PQsendQueryPrepared(conn, "index_nosector_places", 1, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (interpolation)
|
|
||||||
{
|
|
||||||
iResult = PQsendQueryPrepared(conn, "index_sector_places_osmline", 1, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
paramValues[0] = (char *)¶mSector;
|
|
||||||
paramLengths[0] = sizeof(paramSector);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
paramValues[0] = (char *)¶mRank;
|
|
||||||
paramLengths[0] = sizeof(paramRank);
|
|
||||||
paramFormats[0] = 1;
|
|
||||||
paramValues[1] = (char *)¶mSector;
|
paramValues[1] = (char *)¶mSector;
|
||||||
paramLengths[1] = sizeof(paramSector);
|
paramLengths[1] = sizeof(paramSector);
|
||||||
paramFormats[1] = 1;
|
paramFormats[1] = 1;
|
||||||
iResult = PQsendQueryPrepared(conn, "index_sector_places", 2, paramValues, paramLengths, paramFormats, 1);
|
if (rankTotalTuples-rankCountTuples < num_threads*1000)
|
||||||
|
{
|
||||||
|
iResult = PQsendQueryPrepared(conn, "index_nosector_places", 1, paramValues, paramLengths, paramFormats, 1);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iResult = PQsendQueryPrepared(conn, "index_sector_places", 2, paramValues, paramLengths, paramFormats, 1);
|
||||||
}
|
}
|
||||||
if (!iResult)
|
if (!iResult)
|
||||||
{
|
{
|
||||||
@@ -183,6 +242,7 @@ struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iSector > 0)
|
if (iSector > 0)
|
||||||
{
|
{
|
||||||
count = 0;
|
count = 0;
|
||||||
@@ -200,14 +260,6 @@ struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|||||||
thread_data[i].count_mutex = &count_mutex;
|
thread_data[i].count_mutex = &count_mutex;
|
||||||
thread_data[i].writer = writer;
|
thread_data[i].writer = writer;
|
||||||
thread_data[i].writer_mutex = &writer_mutex;
|
thread_data[i].writer_mutex = &writer_mutex;
|
||||||
if (interpolation)
|
|
||||||
{
|
|
||||||
thread_data[i].table = 0; // use interpolations table
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
thread_data[i].table = 1; // use placex table
|
|
||||||
}
|
|
||||||
pthread_create(&thread_data[i].thread, NULL, &nominatim_indexThread, (void *)&thread_data[i]);
|
pthread_create(&thread_data[i].thread, NULL, &nominatim_indexThread, (void *)&thread_data[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,18 +270,10 @@ struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|||||||
usleep(1000);
|
usleep(1000);
|
||||||
|
|
||||||
// Aim for one update per second
|
// Aim for one update per second
|
||||||
if (sleepcount++ > 1000)
|
if (sleepcount++ > 500)
|
||||||
{
|
{
|
||||||
rankPerSecond = ((float)rankCountTuples + (float)count) / MAX(difftime(time(0), rankStartTime),1);
|
rankPerSecond = ((float)rankCountTuples + (float)count) / MAX(difftime(time(0), rankStartTime),1);
|
||||||
if(interpolation)
|
|
||||||
{
|
|
||||||
fprintf(stderr, " Done %i in %i @ %f per second - Interpolation lines ETA (seconds): %f\n", (rankCountTuples + count), (int)(difftime(time(0), rankStartTime)), rankPerSecond, ((float)(rankTotalTuples - (rankCountTuples + count)))/rankPerSecond);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, " Done %i in %i @ %f per second - Rank %i ETA (seconds): %f\n", (rankCountTuples + count), (int)(difftime(time(0), rankStartTime)), rankPerSecond, rank, ((float)(rankTotalTuples - (rankCountTuples + count)))/rankPerSecond);
|
fprintf(stderr, " Done %i in %i @ %f per second - Rank %i ETA (seconds): %f\n", (rankCountTuples + count), (int)(difftime(time(0), rankStartTime)), rankPerSecond, rank, ((float)(rankTotalTuples - (rankCountTuples + count)))/rankPerSecond);
|
||||||
}
|
|
||||||
|
|
||||||
sleepcount = 0;
|
sleepcount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,189 +299,16 @@ struct index_thread_data * thread_data, const char *structuredoutputfile)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Finished rank
|
// Finished rank
|
||||||
fprintf(stderr, "\r Done %i in %i @ %f per second - FINISHED\n\n", rankCountTuples, (int)(difftime(time(0), rankStartTime)), rankPerSecond);
|
fprintf(stderr, "\r Done %i in %i @ %f per second - FINISHED \n\n", rankCountTuples, (int)(difftime(time(0), rankStartTime)), rankPerSecond);
|
||||||
|
|
||||||
PQclear(resSectors);
|
PQclear(resSectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile)
|
if (writer)
|
||||||
{
|
|
||||||
struct index_thread_data *thread_data;
|
|
||||||
|
|
||||||
PGconn *conn;
|
|
||||||
PGresult *res;
|
|
||||||
int num_rows = 0, status_code = 0;
|
|
||||||
int db_has_locale = 0;
|
|
||||||
char *result_string = NULL;
|
|
||||||
|
|
||||||
int rank;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
xmlTextWriterPtr writer;
|
|
||||||
pthread_mutex_t writer_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
Oid pg_prepare_params[2];
|
|
||||||
|
|
||||||
conn = PQconnectdb(conninfo);
|
|
||||||
if (PQstatus(conn) != CONNECTION_OK)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
|
nominatim_exportXMLEnd(writer);
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res = PQexec(conn, "SHOW lc_messages");
|
|
||||||
status_code = PQresultStatus(res);
|
|
||||||
if (status_code != PGRES_TUPLES_OK && status_code != PGRES_SINGLE_TUPLE) {
|
|
||||||
fprintf(stderr, "Failed determining database locale: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
num_rows = PQntuples(res);
|
|
||||||
if (num_rows > 0)
|
|
||||||
{
|
|
||||||
result_string = PQgetvalue(res, 0, 0);
|
|
||||||
if (result_string && (strlen(result_string) > 0) && (strcasecmp(result_string, "C") != 0))
|
|
||||||
{
|
|
||||||
// non-default locale if the result exists, is non-empty, and is not "C"
|
|
||||||
db_has_locale = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_sectors",
|
|
||||||
"select geometry_sector,count(*) from placex where rank_search = $1 and indexed_status > 0 group by geometry_sector order by geometry_sector",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sectors: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
res = PQprepare(conn, "index_sectors_osmline",
|
|
||||||
"select geometry_sector,count(*) from location_property_osmline where indexed_status > 0 group by geometry_sector order by geometry_sector",
|
|
||||||
0, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sectors: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_nosectors",
|
|
||||||
"select 0::integer,count(*) from placex where rank_search = $1 and indexed_status > 0",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sectors: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
pg_prepare_params[1] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_sector_places",
|
|
||||||
"select place_id from placex where rank_search = $1 and geometry_sector = $2 and indexed_status > 0",
|
|
||||||
2, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sector_places: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_nosector_places",
|
|
||||||
"select place_id from placex where rank_search = $1 and indexed_status > 0 order by geometry_sector",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_nosector_places: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT4;
|
|
||||||
res = PQprepare(conn, "index_sector_places_osmline",
|
|
||||||
"select place_id from location_property_osmline where geometry_sector = $1 and indexed_status > 0",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_sector_places: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
res = PQprepare(conn, "index_nosector_places_osmline",
|
|
||||||
"select place_id from location_property_osmline where indexed_status > 0 order by geometry_sector",
|
|
||||||
0, NULL);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_nosector_places: %s\n", PQerrorMessage(conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
// Build the data for each thread
|
|
||||||
thread_data = (struct index_thread_data *)malloc(sizeof(struct index_thread_data)*num_threads);
|
|
||||||
for (i = 0; i < num_threads; i++)
|
|
||||||
{
|
|
||||||
thread_data[i].conn = PQconnectdb(conninfo);
|
|
||||||
if (PQstatus(thread_data[i].conn) != CONNECTION_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(thread_data[i].conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT8;
|
|
||||||
res = PQprepare(thread_data[i].conn, "index_placex",
|
|
||||||
"update placex set indexed_status = 0 where place_id = $1",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_placex: %s\n", PQerrorMessage(thread_data[i].conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
pg_prepare_params[0] = PG_OID_INT8;
|
|
||||||
res = PQprepare(thread_data[i].conn, "index_osmline",
|
|
||||||
"update location_property_osmline set indexed_status = 0 where place_id = $1",
|
|
||||||
1, pg_prepare_params);
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed preparing index_osmline: %s\n", PQerrorMessage(thread_data[i].conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
if (db_has_locale)
|
|
||||||
{
|
|
||||||
// Make sure the error message is not localized as we parse it later.
|
|
||||||
res = PQexec(thread_data[i].conn, "SET lc_messages TO 'C'");
|
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to set langauge: %s\n", PQerrorMessage(thread_data[i].conn));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
}
|
|
||||||
nominatim_exportCreatePreparedQueries(thread_data[i].conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Starting indexing rank (%i to %i) using %i threads\n", rank_min, rank_max, num_threads);
|
|
||||||
|
|
||||||
for (rank = rank_min; rank <= rank_max; rank++)
|
|
||||||
{
|
|
||||||
// OSMLINE: do reindexing (=> reparenting) for interpolation lines at rank 30, but before all other objects of rank 30
|
|
||||||
// reason: houses (rank 30) depend on the updated interpolation line, when reparenting (see placex_update in functions.sql)
|
|
||||||
if (rank == 30)
|
|
||||||
{
|
|
||||||
run_indexing(rank, 1, conn, num_threads, thread_data, structuredoutputfile);
|
|
||||||
}
|
|
||||||
run_indexing(rank, 0, conn, num_threads, thread_data, structuredoutputfile);
|
|
||||||
}
|
|
||||||
// Close all connections
|
// Close all connections
|
||||||
for (i = 0; i < num_threads; i++)
|
for (i = 0; i < num_threads; i++)
|
||||||
{
|
{
|
||||||
@@ -459,9 +330,6 @@ void *nominatim_indexThread(void * thread_data_in)
|
|||||||
uint64_t paramPlaceID;
|
uint64_t paramPlaceID;
|
||||||
uint64_t place_id;
|
uint64_t place_id;
|
||||||
time_t updateStartTime;
|
time_t updateStartTime;
|
||||||
unsigned table;
|
|
||||||
|
|
||||||
table = thread_data->table;
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
@@ -493,41 +361,20 @@ void *nominatim_indexThread(void * thread_data_in)
|
|||||||
paramValues[0] = (char *)¶mPlaceID;
|
paramValues[0] = (char *)¶mPlaceID;
|
||||||
paramLengths[0] = sizeof(paramPlaceID);
|
paramLengths[0] = sizeof(paramPlaceID);
|
||||||
paramFormats[0] = 1;
|
paramFormats[0] = 1;
|
||||||
if (table == 1) // table=1 for placex
|
|
||||||
{
|
|
||||||
res = PQexecPrepared(thread_data->conn, "index_placex", 1, paramValues, paramLengths, paramFormats, 1);
|
res = PQexecPrepared(thread_data->conn, "index_placex", 1, paramValues, paramLengths, paramFormats, 1);
|
||||||
}
|
|
||||||
else // table=0 for osmline
|
|
||||||
{
|
|
||||||
res = PQexecPrepared(thread_data->conn, "index_osmline", 1, paramValues, paramLengths, paramFormats, 1);
|
|
||||||
}
|
|
||||||
if (PQresultStatus(res) == PGRES_COMMAND_OK)
|
if (PQresultStatus(res) == PGRES_COMMAND_OK)
|
||||||
done = 1;
|
done = 1;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!strncmp(PQerrorMessage(thread_data->conn), "ERROR: deadlock detected", 25))
|
if (!strncmp(PQerrorMessage(thread_data->conn), "ERROR: deadlock detected", 25))
|
||||||
{
|
|
||||||
if (table == 1)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "index_placex: UPDATE failed - deadlock, retrying (%ld)\n", place_id);
|
fprintf(stderr, "index_placex: UPDATE failed - deadlock, retrying (%ld)\n", place_id);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_osmline: UPDATE failed - deadlock, retrying (%ld)\n", place_id);
|
|
||||||
}
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
sleep(rand() % 10);
|
sleep(rand() % 10);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
if (table == 1)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "index_placex: UPDATE failed: %s", PQerrorMessage(thread_data->conn));
|
fprintf(stderr, "index_placex: UPDATE failed: %s", PQerrorMessage(thread_data->conn));
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "index_osmline: UPDATE failed: %s", PQerrorMessage(thread_data->conn));
|
|
||||||
}
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ struct index_thread_data
|
|||||||
pthread_mutex_t * count_mutex;
|
pthread_mutex_t * count_mutex;
|
||||||
xmlTextWriterPtr writer;
|
xmlTextWriterPtr writer;
|
||||||
pthread_mutex_t * writer_mutex;
|
pthread_mutex_t * writer_mutex;
|
||||||
unsigned table;
|
|
||||||
};
|
};
|
||||||
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile);
|
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile);
|
||||||
void *nominatim_indexThread(void * thread_data_in);
|
void *nominatim_indexThread(void * thread_data_in);
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
PGconn *conn;
|
PGconn *conn;
|
||||||
|
|
||||||
fprintf(stderr, "nominatim version %s\n\n", NOMINATIM_VERSION);
|
fprintf(stderr, "nominatim version %s\n\n", VERSION);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user