Compare commits

..

4 Commits

Author SHA1 Message Date
Sarah Hoffmann
7eb263a3a4 only check for postgres major and minor version
fixes #192
2014-11-28 23:02:53 +01:00
Sarah Hoffmann
ed4e46c3d9 remove debug output 2014-11-28 23:02:44 +01:00
Sarah Hoffmann
a7ef9426d3 prepare 2.3.1 release 2014-11-27 22:37:05 +01:00
Sarah Hoffmann
0bc81fe59c more tolerant regexp for parsing replication state directories
Apache 2.4 has changed the date format, so that the current regexp
doesn't match anymore, so be more tolerant with the date format.
Also force less fancy output formatting without tables.
2014-11-27 22:33:41 +01:00
348 changed files with 141234 additions and 97437 deletions

24
.gitignore vendored
View File

@@ -1,11 +1,31 @@
*.log
*.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
.deps/
autom4te.cache/
config.*
configure
Makefile
Makefile.in
stamp-h1
missing
INSTALL
aclocal.m4
depcomp
install-sh
compile
data/wiki_import.sql
data/wiki_specialphrases.sql
data/osmosischange.osc
.vagrant

View File

@@ -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
View File

@@ -2,14 +2,16 @@ Nominatim was written by:
Brian Quinion
Sarah Hoffmann
Marc Tobias Metten
markigail
gemo1011
IrlJidel
Frederik Ramm
and many more.
For a full list of contributors see
https://github.com/openstreetmap/Nominatim/graphs/contributors
Michael Spreng
Daniele Forsi
mfn
Grant Slater
Andree Klattenhoff
IrlJidel
appelflap
b3nn0
Spin0us
Kurt Roeckx
Rodolphe Quiédeville
Marc Tobias Metten

View File

@@ -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 1)
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)
#-----------------------------------------------------------------------------

View File

@@ -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 ./
```

14
COPYING
View File

@@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -56,7 +56,7 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it

175
ChangeLog
View File

@@ -1,140 +1,7 @@
3.2.1
* security fix: fix possible SQL injection via details API
2.0.1
3.2.0
* complete rewrite of reverse search algorithm
* add new geojson and geocodejson output formats
* add simple export script to exprot addresses to CSV
* remove is_in terms from address computation
* remove unused search_name_country tables
* various smaller fixes to query parsing
* convert Tokens and token types to class types
* correctly handle update when boundary object type is changed
* improve debug output for /search endpoint
* update to latest osm2pgsql and leaflet.js
* overhaul of /details endpoint:
* new class parameter when using osmtype/osmid parameters
* permalink to instance-independent osmtype/osmid parameter format
* new json output format
* update CentOS vagrant machine to use SELinux
* add vagrant scripts for Ubuntu 18.04
* fix build process for BSD
* enable running the database on a different host than the setup scripts
* allow to configure use of custom PHP binaries (PHP_BIN)
* extensive coding style improvements to PHP code
* more PHP unit tests for new classes
* increase coverage for API tests
* add documentation for API
3.1.0
* 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
* delete outdated entries from location_area_country
* remove remaining uses of INTEGER, to allow node ids larger than 2^31
2.1
@@ -160,7 +27,37 @@
* refactoring of front-end PHP code
* lots of smaller bug fixes
2.0.1
2.2
* delete outdated entries from location_area_country
* remove remaining uses of INTEGER, to allow node ids larger than 2^31
* 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.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
View 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

0
NEWS Normal file
View File

56
README Normal file
View 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

View File

@@ -1,57 +0,0 @@
[![Build Status](https://travis-ci.org/openstreetmap/Nominatim.svg?branch=master)](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

View File

@@ -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
View File

@@ -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
View File

@@ -0,0 +1,2 @@
#!/bin/sh
autoreconf -vfi

65
configure.ac Normal file
View 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
View 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

File diff suppressed because one or more lines are too long

View File

@@ -24,3 +24,4 @@ CREATE TABLE gb_postcode (
CONSTRAINT enforce_srid_geometry CHECK ((st_srid(geometry) = 4326))
);
GRANT SELECT ON TABLE gb_postcode TO "www-data";

File diff suppressed because it is too large Load Diff

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

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -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
)

View File

@@ -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.

View 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).

View File

@@ -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).

View File

@@ -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;
```

View File

@@ -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
]
}
}
```

View File

@@ -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.

View File

@@ -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"
}
}
]
```

View File

@@ -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.

View File

@@ -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)

View File

@@ -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
]
}
}
}
```

View File

@@ -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
]
}
}
]
}
```

View File

@@ -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

View File

@@ -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.

View File

@@ -1,3 +0,0 @@
.toctree-l3 {
display: none!important
}

View File

@@ -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

View File

@@ -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]

View File

@@ -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;
}
}

View File

@@ -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),
);
}

View File

@@ -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);
}
}

View File

@@ -1,11 +0,0 @@
<?php
namespace Nominatim;
class Debug
{
public static function __callStatic($name, $arguments)
{
// nothing
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}

View File

@@ -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
);
}
}

View File

@@ -1,573 +1,138 @@
<?php
namespace Nominatim;
require_once(CONST_BasePath.'/lib/AddressDetails.php');
require_once(CONST_BasePath.'/lib/Result.php');
class PlaceLookup
{
protected $oDB;
protected $aLangPrefOrderSql = "''";
protected $bAddressDetails = false;
protected $bExtraTags = false;
protected $bNameDetails = false;
protected $bIncludePolygonAsPoints = false;
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;
}
public function doDeDupe()
{
return $this->bDeDupe;
}
public function setIncludePolygonAsPoints($b = true)
{
$this->bIncludePolygonAsPoints = $b;
}
public function setIncludeAddressDetails($b)
{
$this->bAddressDetails = $b;
}
public function loadParamArray($oParams, $sGeomType = null)
{
$aLangs = $oParams->getPreferredLanguages();
$this->aLangPrefOrderSql =
'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') {
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->bAddressDetails) $aParams['addressdetails'] = '1';
if ($this->bExtraTags) $aParams['extratags'] = '1';
if ($this->bNameDetails) $aParams['namedetails'] = '1';
if ($this->bIncludePolygonAsPoints) $aParams['polygon'] = '1';
if ($this->bIncludePolygonAsText) $aParams['polygon_text'] = '1';
if ($this->bIncludePolygonAsGeoJSON) $aParams['polygon_geojson'] = '1';
if ($this->bIncludePolygonAsKML) $aParams['polygon_kml'] = '1';
if ($this->bIncludePolygonAsSVG) $aParams['polygon_svg'] = '1';
if ($this->fPolygonSimplificationThreshold > 0.0) {
$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)
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;
$iPlaceID = chksql($this->oDB->getOne($sSQL));
if (!$iPlaceID) {
return null;
}
$aResults = $this->lookup(array($iPlaceID => new Result($iPlaceID)));
return empty($aResults) ? null : reset($aResults);
}
public function lookup($aResults, $iMinRank = 0, $iMaxRank = 30)
{
Debug::newFunction('Place lookup');
if (empty($aResults)) {
return array();
}
$aSubSelects = array();
$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)
{
$aOutlineResult = array();
if (!$iPlaceID) return $aOutlineResult;
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');
if ($aPointPolygon['place_id']) {
if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null) {
$aOutlineResult['lat'] = $aPointPolygon['centrelat'];
$aOutlineResult['lon'] = $aPointPolygon['centrelon'];
}
if ($this->bIncludePolygonAsGeoJSON) $aOutlineResult['asgeojson'] = $aPointPolygon['asgeojson'];
if ($this->bIncludePolygonAsKML) $aOutlineResult['askml'] = $aPointPolygon['askml'];
if ($this->bIncludePolygonAsSVG) $aOutlineResult['assvg'] = $aPointPolygon['assvg'];
if ($this->bIncludePolygonAsText) $aOutlineResult['astext'] = $aPointPolygon['astext'];
if ($this->bIncludePolygonAsPoints) $aOutlineResult['aPolyPoints'] = geometryText2Points($aPointPolygon['astext'], $fRadius);
if (abs($aPointPolygon['minlat'] - $aPointPolygon['maxlat']) < 0.0000001) {
$aPointPolygon['minlat'] = $aPointPolygon['minlat'] - $fRadius;
$aPointPolygon['maxlat'] = $aPointPolygon['maxlat'] + $fRadius;
}
if (abs($aPointPolygon['minlon'] - $aPointPolygon['maxlon']) < 0.0000001) {
$aPointPolygon['minlon'] = $aPointPolygon['minlon'] - $fRadius;
$aPointPolygon['maxlon'] = $aPointPolygon['maxlon'] + $fRadius;
}
$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;
}
}
class PlaceLookup
{
protected $oDB;
protected $iPlaceID;
protected $aLangPrefOrder = array();
protected $bAddressDetails = false;
function PlaceLookup(&$oDB)
{
$this->oDB =& $oDB;
}
function setLanguagePreference($aLangPrefOrder)
{
$this->aLangPrefOrder = $aLangPrefOrder;
}
function setIncludeAddressDetails($bAddressDetails = true)
{
$this->bAddressDetails = $bAddressDetails;
}
function setPlaceID($iPlaceID)
{
$this->iPlaceID = $iPlaceID;
}
function setOSMID($sType, $iID)
{
$sSQL = "select place_id from placex where osm_type = '".pg_escape_string($sType)."' and osm_id = ".(int)$iID." order by type = 'postcode' asc";
$this->iPlaceID = $this->oDB->getOne($sSQL);
}
function lookup()
{
if (!$this->iPlaceID) return null;
$sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted", $this->aLangPrefOrder))."]";
$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, ";
$sSQL .= " coalesce(importance,0.75-(rank_search::float/40)) as importance, indexed_status, indexed_date, wikipedia, calculated_country_code, ";
$sSQL .= " get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,";
$sSQL .= " get_name_by_language(name, $sLanguagePrefArraySQL) as placename,";
$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 (!$aPlace['place_id']) return null;
if ($this->bAddressDetails)
{
$aAddress = $this->getAddressNames();
$aPlace['aAddress'] = $aAddress;
}
$aClassType = getClassTypes();
$sAddressType = '';
$sClassType = $aPlace['class'].':'.$aPlace['type'].':'.$aPlace['admin_level'];
if (isset($aClassType[$sClassType]) && isset($aClassType[$sClassType]['simplelabel']))
{
$sAddressType = $aClassType[$aClassType]['simplelabel'];
}
else
{
$sClassType = $aPlace['class'].':'.$aPlace['type'];
if (isset($aClassType[$sClassType]) && isset($aClassType[$sClassType]['simplelabel']))
$sAddressType = $aClassType[$sClassType]['simplelabel'];
else $sAddressType = $aPlace['class'];
}
$aPlace['addresstype'] = $sAddressType;
return $aPlace;
}
function getAddressDetails($bAll = false)
{
if (!$this->iPlaceID) return null;
$sLanguagePrefArraySQL = "ARRAY[".join(',',array_map("getDBQuoted", $this->aLangPrefOrder))."]";
$sSQL = "select *,get_name_by_language(name,$sLanguagePrefArraySQL) as localname from get_addressdata(".$this->iPlaceID.")";
if (!$bAll) $sSQL .= " WHERE isaddress OR type = 'country_code'";
$sSQL .= " order by rank_address desc,isaddress desc";
$aAddressLines = $this->oDB->getAll($sSQL);
if (PEAR::IsError($aAddressLines))
{
var_dump($aAddressLines);
exit;
}
return $aAddressLines;
}
function getAddressNames()
{
$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;
}
}
?>

View File

@@ -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;
}
}

View File

@@ -1,367 +1,139 @@
<?php
class ReverseGeocode
{
protected $oDB;
namespace Nominatim;
protected $fLat;
protected $fLon;
protected $iMaxRank = 28;
require_once(CONST_BasePath.'/lib/Result.php');
protected $aLangPrefOrder = array();
class ReverseGeocode
{
protected $oDB;
protected $iMaxRank = 28;
protected $bShowAddressDetails = true;
function ReverseGeocode(&$oDB)
{
$this->oDB =& $oDB;
}
public function __construct(&$oDB)
{
$this->oDB =& $oDB;
}
function setLanguagePreference($aLangPref)
{
$this->aLangPrefOrder = $aLangPref;
}
function setIncludeAddressDetails($bAddressDetails = true)
{
$this->bAddressDetails = $bAddressDetails;
}
public function setZoom($iZoom)
{
// Zoom to rank, this could probably be calculated but a lookup gives fine control
$aZoomRank = array(
0 => 2, // Continent / Sea
1 => 2,
2 => 2,
3 => 4, // Country
4 => 4,
5 => 8, // State
6 => 10, // Region
7 => 10,
8 => 12, // County
9 => 12,
10 => 17, // City
11 => 17,
12 => 18, // Town / Village
13 => 18,
14 => 22, // Suburb
15 => 22,
16 => 26, // Street, TODO: major street?
17 => 26,
18 => 30, // or >, Building
19 => 30, // or >, Building
);
$this->iMaxRank = (isset($iZoom) && isset($aZoomRank[$iZoom]))?$aZoomRank[$iZoom]:28;
}
function setLatLon($fLat, $fLon)
{
$this->fLat = (float)$fLat;
$this->fLon = (float)$fLon;
}
/**
* 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,';
$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';
function setRank($iRank)
{
$this->iMaxRank = $iRank;
}
return chksql(
$this->oDB->getRow($sSQL),
'Could not determine closest housenumber on an osm interpolation line.'
);
}
function setZoom($iZoom)
{
// Zoom to rank, this could probably be calculated but a lookup gives fine control
$aZoomRank = array(
0 => 2, // Continent / Sea
1 => 2,
2 => 2,
3 => 4, // Country
4 => 4,
5 => 8, // State
6 => 10, // Region
7 => 10,
8 => 12, // County
9 => 12,
10 => 17, // City
11 => 17,
12 => 18, // Town / Village
13 => 18,
14 => 22, // Suburb
15 => 22,
16 => 26, // Street, TODO: major street?
17 => 26,
18 => 30, // or >, Building
19 => 30, // or >, Building
);
$this->iMaxRank = (isset($iZoom) && isset($aZoomRank[$iZoom]))?$aZoomRank[$iZoom]:28;
}
protected function lookupLargeArea($sPointSQL, $iMaxRank)
{
$oResult = null;
function lookup()
{
$sPointSQL = 'ST_SetSRID(ST_Point('.$this->fLon.','.$this->fLat.'),4326)';
$iMaxRank = $this->iMaxRank;
if ($iMaxRank > 4) {
$aPlace = $this->lookupPolygon($sPointSQL, $iMaxRank);
if ($aPlace) {
return new Result($aPlace['place_id']);
}
}
// Find the nearest point
$fSearchDiam = 0.0004;
$iPlaceID = null;
$aArea = false;
$fMaxAreaDistance = 1;
while(!$iPlaceID && $fSearchDiam < $fMaxAreaDistance)
{
$fSearchDiam = $fSearchDiam * 2;
// If no polygon which contains the searchpoint is found,
// searches in the country_osm_grid table for a polygon.
return $this->lookupInCountry($sPointSQL, $iMaxRank);
}
// If we have to expand the search area by a large amount then we need a larger feature
// then there is a limit to how small the feature should be
if ($fSearchDiam > 2 && $iMaxRank > 4) $iMaxRank = 4;
if ($fSearchDiam > 1 && $iMaxRank > 9) $iMaxRank = 8;
if ($fSearchDiam > 0.8 && $iMaxRank > 10) $iMaxRank = 10;
if ($fSearchDiam > 0.6 && $iMaxRank > 12) $iMaxRank = 12;
if ($fSearchDiam > 0.2 && $iMaxRank > 17) $iMaxRank = 17;
if ($fSearchDiam > 0.1 && $iMaxRank > 18) $iMaxRank = 18;
if ($fSearchDiam > 0.008 && $iMaxRank > 22) $iMaxRank = 22;
if ($fSearchDiam > 0.001 && $iMaxRank > 26) $iMaxRank = 26;
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';
$sSQL = 'select place_id,parent_place_id,rank_search from placex';
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
$sSQL .= ' and rank_search != 28 and rank_search >= '.$iMaxRank;
$sSQL .= ' and (name is not null or housenumber is not null)';
$sSQL .= ' and class not in (\'waterway\',\'railway\',\'tunnel\',\'bridge\')';
$sSQL .= ' and indexed_status = 0 ';
$sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') ';
$sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
$sSQL .= ' ORDER BY ST_distance('.$sPointSQL.', geometry) ASC limit 1';
if (CONST_Debug) var_dump($sSQL);
$aPlace = $this->oDB->getRow($sSQL);
if (PEAR::IsError($aPlace))
{
failInternalError("Could not determine closest place.", $sSQL, $aPlace);
}
$iPlaceID = $aPlace['place_id'];
$iParentPlaceID = $aPlace['parent_place_id'];
}
$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';
// The point we found might be too small - use the address to find what it is a child of
if ($iPlaceID && $iMaxRank < 28)
{
if ($aPlace['rank_search'] > 28 && $iParentPlaceID)
{
$iPlaceID = $iParentPlaceID;
}
$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";
$iPlaceID = $this->oDB->getOne($sSQL);
if (PEAR::IsError($iPlaceID))
{
failInternalError("Could not get parent for place.", $sSQL, $iPlaceID);
}
if (!$iPlaceID)
{
$iPlaceID = $aPlace['place_id'];
}
}
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']);
}
}
$oPlaceLookup = new PlaceLookup($this->oDB);
$oPlaceLookup->setLanguagePreference($this->aLangPrefOrder);
$oPlaceLookup->setIncludeAddressDetails($this->bAddressDetails);
$oPlaceLookup->setPlaceId($iPlaceID);
// 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;
// Find the nearest point
$fSearchDiam = 0.006;
$oResult = null;
$aPlace = null;
// for POI or street level
if ($iMaxRank >= 26) {
$sSQL = 'select place_id,parent_place_id,rank_address,country_code,';
$sSQL .= ' ST_distance('.$sPointSQL.', geometry) as distance';
$sSQL .= ' FROM ';
$sSQL .= ' placex';
$sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
$sSQL .= ' AND';
// only streets
if ($iMaxRank == 26) {
$sSQL .= ' rank_address = 26';
} 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 .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
$sSQL .= ' ORDER BY distance ASC limit 1';
if (CONST_Debug) var_dump($sSQL);
$aPlace = chksql(
$this->oDB->getRow($sSQL),
'Could not determine closest place.'
);
if (CONST_Debug) var_dump($aPlace);
if ($aPlace) {
$iPlaceID = $aPlace['place_id'];
$oResult = new Result($iPlaceID);
$iRankAddress = $aPlace['rank_address'];
$iParentPlaceID = $aPlace['parent_place_id'];
}
if ($bDoInterpolation && $iMaxRank >= 30) {
$fDistance = $fSearchDiam;
if ($aPlace) {
// We can't reliably go from the closest street to an
// interpolation line because the closest interpolation
// may have a different street segments as a parent.
// Therefore allow an interpolation line to take precendence
// even when the street is closer.
$fDistance = $iRankAddress < 28 ? 0.001 : $aPlace['distance'];
}
$aHouse = $this->lookupInterpolation($sPointSQL, $fDistance);
if ($aHouse) {
$oResult = new Result($aHouse['place_id'], Result::TABLE_OSMLINE);
$oResult->iHouseNumber = closestHouseNumber($aHouse);
$aPlace = $aHouse;
$iRankAddress = 30;
}
}
if ($aPlace) {
// if street and maxrank > streetlevel
if ($iRankAddress <= 27 && $iMaxRank > 27) {
// find the closest object (up to a certain radius) of which the street is a parent of
$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
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;
}
}
return $oPlaceLookup->lookup();
}
}
?>

View File

@@ -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

View File

@@ -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];
}
}

View File

@@ -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;
}
}

View File

@@ -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
);
}
}

View File

@@ -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)
);
}
}

View File

@@ -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;
}
}

View File

@@ -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.')'
);
}
}

View File

@@ -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)
)
);
}
}

View File

@@ -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
)
);
}
}

View File

@@ -1,222 +1,145 @@
<?php
function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnknown = false)
{
$aQuick = array();
$aCounts = array();
function getCmdOpt($aArg, $aSpec, &$aResult, $bExitOnError = false, $bExitOnUnknown = false)
{
$aQuick = array();
$aCounts = array();
foreach($aSpec as $aLine)
{
if (is_array($aLine))
{
if ($aLine[0]) $aQuick['--'.$aLine[0]] = $aLine;
if ($aLine[1]) $aQuick['-'.$aLine[1]] = $aLine;
$aCounts[$aLine[0]] = 0;
}
}
foreach ($aSpec as $aLine) {
if (is_array($aLine)) {
if ($aLine[0]) $aQuick['--'.$aLine[0]] = $aLine;
if ($aLine[1]) $aQuick['-'.$aLine[1]] = $aLine;
$aCounts[$aLine[0]] = 0;
}
}
$aResult = array();
$bUnknown = false;
$iSize = sizeof($aArg);
for ($i = 1; $i < $iSize; $i++)
{
if (isset($aQuick[$aArg[$i]]))
{
$aLine = $aQuick[$aArg[$i]];
$aCounts[$aLine[0]]++;
$xVal = null;
if ($aLine[4] == $aLine[5])
{
if ($aLine[4])
{
$xVal = array();
for($n = $aLine[4]; $i < $iSize && $n; $n--)
{
$i++;
if ($i >= $iSize || $aArg[$i][0] == '-') showUsage($aSpec, $bExitOnError, 'Parameter of \''.$aLine[0].'\' is missing');
$aResult = array();
$bUnknown = false;
$iSize = count($aArg);
for ($i = 1; $i < $iSize; $i++) {
if (isset($aQuick[$aArg[$i]])) {
$aLine = $aQuick[$aArg[$i]];
$aCounts[$aLine[0]]++;
$xVal = null;
if ($aLine[4] == $aLine[5]) {
if ($aLine[4]) {
$xVal = array();
for ($n = $aLine[4]; $i < $iSize && $n; $n--) {
$i++;
if ($i >= $iSize || $aArg[$i][0] == '-') showUsage($aSpec, $bExitOnError, 'Parameter of \''.$aLine[0].'\' is missing');
switch ($aLine[6])
{
case 'realpath':
$xVal[] = realpath($aArg[$i]);
break;
case 'realdir':
$sPath = realpath(dirname($aArg[$i]));
if ($sPath)
$xVal[] = $sPath . '/' . basename($aArg[$i]);
else
$xVal[] = $sPath;
break;
case 'bool':
$xVal[] = (bool)$aArg[$i];
break;
case 'int':
$xVal[] = (int)$aArg[$i];
break;
case 'float':
$xVal[] = (float)$aArg[$i];
break;
default:
$xVal[] = $aArg[$i];
break;
}
}
if ($aLine[4] == 1) $xVal = $xVal[0];
}
else
{
$xVal = true;
}
}
else
{
fail('Variable numbers of params not yet supported');
}
switch ($aLine[6]) {
case 'realpath':
$xVal[] = realpath($aArg[$i]);
break;
case 'realdir':
$sPath = realpath(dirname($aArg[$i]));
if ($sPath) {
$xVal[] = $sPath . '/' . basename($aArg[$i]);
} else {
$xVal[] = $sPath;
}
break;
case 'bool':
$xVal[] = (bool)$aArg[$i];
break;
case 'int':
$xVal[] = (int)$aArg[$i];
break;
case 'float':
$xVal[] = (float)$aArg[$i];
break;
default:
$xVal[] = $aArg[$i];
break;
}
}
if ($aLine[4] == 1) $xVal = $xVal[0];
} else {
$xVal = true;
}
} else {
fail('Variable numbers of params not yet supported');
}
if ($aLine[3] > 1)
{
if (!array_key_exists($aLine[0], $aResult)) $aResult[$aLine[0]] = array();
$aResult[$aLine[0]][] = $xVal;
}
else
{
$aResult[$aLine[0]] = $xVal;
}
}
else
{
$bUnknown = $aArg[$i];
}
}
if ($aLine[3] > 1) {
if (!array_key_exists($aLine[0], $aResult)) $aResult[$aLine[0]] = array();
$aResult[$aLine[0]][] = $xVal;
} else {
$aResult[$aLine[0]] = $xVal;
}
} else {
$bUnknown = $aArg[$i];
}
}
if (array_key_exists('help', $aResult)) showUsage($aSpec);
if ($bUnknown && $bExitOnUnknown) showUsage($aSpec, $bExitOnError, 'Unknown option \''.$bUnknown.'\'');
if (array_key_exists('help', $aResult)) showUsage($aSpec);
if ($bUnknown && $bExitOnUnknown) showUsage($aSpec, $bExitOnError, 'Unknown option \''.$bUnknown.'\'');
foreach($aSpec as $aLine)
{
if (is_array($aLine))
{
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');
switch ($aLine[6])
{
case 'bool':
if (!array_key_exists($aLine[0], $aResult))
$aResult[$aLine[0]] = false;
break;
}
}
}
return $bUnknown;
}
foreach ($aSpec as $aLine) {
if (is_array($aLine)) {
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');
switch ($aLine[6]) {
case 'bool':
if (!array_key_exists($aLine[0], $aResult))
$aResult[$aLine[0]] = false;
break;
}
}
}
return $bUnknown;
}
function showUsage($aSpec, $bExit = false, $sError = false)
{
if ($sError) {
echo basename($_SERVER['argv'][0]).': '.$sError."\n";
echo 'Try `'.basename($_SERVER['argv'][0]).' --help` for more information.'."\n";
exit;
}
echo 'Usage: '.basename($_SERVER['argv'][0])."\n";
$bFirst = true;
foreach ($aSpec as $aLine) {
if (is_array($aLine)) {
if ($bFirst) {
$bFirst = false;
echo "\n";
}
$aNames = array();
if ($aLine[1]) $aNames[] = '-'.$aLine[1];
if ($aLine[0]) $aNames[] = '--'.$aLine[0];
$sName = join(', ', $aNames);
echo ' '.$sName.str_repeat(' ', 30-strlen($sName)).$aLine[7]."\n";
} else {
echo $aLine."\n";
}
}
echo "\n";
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;
}
function showUsage($aSpec, $bExit = false, $sError = false)
{
if ($sError)
{
echo basename($_SERVER['argv'][0]).': '.$sError."\n";
echo 'Try `'.basename($_SERVER['argv'][0]).' --help` for more information.'."\n";
exit;
}
echo "Usage: ".basename($_SERVER['argv'][0])."\n";
$bFirst = true;
foreach($aSpec as $aLine)
{
if (is_array($aLine))
{
if ($bFirst)
{
$bFirst = false;
echo "\n";
}
$aNames = array();
if ($aLine[1]) $aNames[] = '-'.$aLine[1];
if ($aLine[0]) $aNames[] = '--'.$aLine[0];
$sName = join(', ',$aNames);
echo ' '.$sName.str_repeat(' ',30-strlen($sName)).$aLine[7]."\n";
}
else
{
echo $aLine."\n";
}
}
echo "\n";
exit;
}

View File

@@ -1,43 +1,24 @@
<?php
require_once('DB.php');
require_once('DB.php');
function &getDB($bNew = false, $bPersistent = false)
{
// Get the database object
$oDB =& DB::connect(CONST_Database_DSN.($bNew?'?new_link=true':''), $bPersistent);
if (PEAR::IsError($oDB))
{
var_dump(CONST_Database_DSN);
var_Dump($oDB);
fail($oDB->getMessage());
}
$oDB->setFetchMode(DB_FETCHMODE_ASSOC);
$oDB->query("SET DateStyle TO 'sql,european'");
$oDB->query("SET client_encoding TO 'utf-8'");
return $oDB;
}
function getDBQuoted($s)
{
return "'".pg_escape_string($s)."'";
}
function &getDB($bNew = false, $bPersistent = false)
{
// Get the database object
$oDB = chksql(
DB::connect(CONST_Database_DSN.($bNew?'?new_link=true':''), $bPersistent),
'Failed to establish database connection'
);
$oDB->setFetchMode(DB_FETCHMODE_ASSOC);
$oDB->query("SET DateStyle TO 'sql,european'");
$oDB->query("SET client_encoding TO 'utf-8'");
$iMaxExecution = ini_get('max_execution_time') * 1000;
if ($iMaxExecution > 0) $oDB->query("SET statement_timeout TO $iMaxExecution");
return $oDB;
}
function getDBQuoted($s)
{
return "'".pg_escape_string($s)."'";
}
function getArraySQL($a)
{
return 'ARRAY['.join(',', $a).']';
}
function getPostgresVersion(&$oDB)
{
$sVersionString = $oDB->getOne('select version()');
preg_match('#PostgreSQL ([0-9]+)[.]([0-9]+)[^0-9]#', $sVersionString, $aMatches);
return (float) ($aMatches[1].'.'.$aMatches[2]);
}
function getPostgisVersion(&$oDB)
{
$sVersionString = $oDB->getOne('select postgis_full_version()');
preg_match('#POSTGIS="([0-9]+)[.]([0-9]+)[.]([0-9]+)( r([0-9]+))?"#', $sVersionString, $aMatches);
return (float) ($aMatches[1].'.'.$aMatches[2]);
}

View File

@@ -1,28 +1,8 @@
<?php
if (file_exists(getenv('NOMINATIM_SETTINGS')))
{
require_once(getenv('NOMINATIM_SETTINGS'));
}
require_once('init.php');
require_once('cmd.php');
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(
'proxy' => $proxy,
'request_fulluri' => true,
'header' => $aHeaders
),
'https' => array(
'proxy' => $proxy,
'request_fulluri' => true,
'header' => $aHeaders
)
);
stream_context_set_default($aContext);
}
require_once('init.php');
require_once('cmd.php');

View File

@@ -1,101 +1,64 @@
<?php
require_once('init.php');
require_once('init.php');
require_once('ParameterParser.php');
require_once(CONST_Debug ? 'DebugHtml.php' : 'DebugNone.php');
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;
/***************************************************************************
*
* Error handling functions
*
*/
if (CONST_ClosedForIndexing && strpos(CONST_ClosedForIndexingExceptionIPs, ','.$_SERVER["REMOTE_ADDR"].',') === false)
{
echo "Closed for re-indexing...";
exit;
}
$aBucketKeys = array();
function chksql($oSql, $sMsg = 'Database request failed')
{
if (!PEAR::isError($oSql)) return $oSql;
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 (isset($_GET["email"])) $aBucketKeys[] = $_GET["email"];
header('HTTP/1.0 500 Internal Server Error');
header('Content-type: text/html; charset=utf-8');
$fBucketVal = doBucket($aBucketKeys,
(defined('CONST_ConnectionBucket_PageType')?constant('CONST_ConnectionBucket_Cost_'.CONST_ConnectionBucket_PageType):1) + user_busy_cost(),
CONST_ConnectionBucket_LeakRate, CONST_ConnectionBucket_BlockLimit);
$sSqlError = $oSql->getMessage();
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');
}
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 (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;
}
if (CONST_Debug) {
var_dump($oSql);
} else {
echo "<pre>\n".$oSql->getUserInfo().'</pre>';
}
header('Content-type: text/html; charset=utf-8');
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');

View File

@@ -1,9 +1,14 @@
<?php
require_once(CONST_BasePath.'/lib/lib.php');
require_once(CONST_BasePath.'/lib/db.php');
@define('CONST_BasePath', dirname(dirname(__FILE__)));
if (get_magic_quotes_gpc()) {
echo "Please disable magic quotes in your php.ini configuration\n";
exit;
}
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";
exit;
}

168
lib/leakybucket.php Normal file
View 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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,74 +1,90 @@
<?php
function logStart(&$oDB, $sType = '', $sQuery = '', $aLanguageList = array())
{
$aStartTime = explode('.',microtime(true));
if (!isset($aStartTime[1])) $aStartTime[1] = '0';
function logStart(&$oDB, $sType = '', $sQuery = '', $aLanguageList = array())
{
$fStartTime = microtime(true);
$aStartTime = explode('.', $fStartTime);
if (!isset($aStartTime[1])) $aStartTime[1] = '0';
$sOutputFormat = '';
if (isset($_GET['format'])) $sOutputFormat = $_GET['format'];
$sOutputFormat = '';
if (isset($_GET['format'])) $sOutputFormat = $_GET['format'];
$hLog = array(
date('Y-m-d H:i:s',$aStartTime[0]).'.'.$aStartTime[1],
$_SERVER["REMOTE_ADDR"],
$_SERVER['QUERY_STRING'],
$sQuery
);
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;
}
if (CONST_Log_DB)
{
// Log
if ($sType == 'search')
{
$oDB->query('insert into query_log values ('.getDBQuoted($hLog[0]).','.getDBQuoted($hLog[3]).','.getDBQuoted($hLog[1]).')');
}
$hLog = array(
date('Y-m-d H:i:s', $aStartTime[0]).'.'.$aStartTime[1],
$_SERVER['REMOTE_ADDR'],
$_SERVER['QUERY_STRING'],
$sOutQuery,
$sType,
$fStartTime
);
$sSQL = 'insert into new_query_log (type,starttime,query,ipaddress,useragent,language,format)';
$sSQL .= ' values ('.getDBQuoted($sType).','.getDBQuoted($hLog[0]).','.getDBQuoted($hLog[2]);
$sSQL .= ','.getDBQuoted($hLog[1]).','.getDBQuoted($_SERVER['HTTP_USER_AGENT']).','.getDBQuoted(join(',',$aLanguageList)).','.getDBQuoted($sOutputFormat).')';
$oDB->query($sSQL);
}
if (CONST_Log_DB) {
if (isset($_GET['email']))
$sUserAgent = $_GET['email'];
elseif (isset($_SERVER['HTTP_REFERER']))
$sUserAgent = $_SERVER['HTTP_REFERER'];
elseif (isset($_SERVER['HTTP_USER_AGENT']))
$sUserAgent = $_SERVER['HTTP_USER_AGENT'];
else $sUserAgent = '';
$sSQL = 'insert into new_query_log (type,starttime,query,ipaddress,useragent,language,format,searchterm)';
$sSQL .= ' values ('.getDBQuoted($sType).','.getDBQuoted($hLog[0]).','.getDBQuoted($hLog[2]);
$sSQL .= ','.getDBQuoted($hLog[1]).','.getDBQuoted($sUserAgent).','.getDBQuoted(join(',', $aLanguageList)).','.getDBQuoted($sOutputFormat).','.getDBQuoted($hLog[3]).')';
$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)
{
$fEndTime = microtime(true);
function logEnd(&$oDB, $hLog, $iNumResults)
{
$aEndTime = explode('.',microtime(true));
if (!$aEndTime[1]) $aEndTime[1] = '0';
$sEndTime = date('Y-m-d H:i:s',$aEndTime[0]).'.'.$aEndTime[1];
if (CONST_Log_DB) {
$aEndTime = explode('.', $fEndTime);
if (!$aEndTime[1]) $aEndTime[1] = '0';
$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 .= ' where starttime = '.getDBQuoted($hLog[0]);
$sSQL .= ' and ipaddress = '.getDBQuoted($hLog[1]);
$sSQL .= ' and query = '.getDBQuoted($hLog[2]);
$oDB->query($sSQL);
}
$sSQL = 'update new_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[2]);
$oDB->query($sSQL);
}
if (CONST_Log_File) {
$aOutdata = sprintf(
"[%s] %.4f %d %s \"%s\"\n",
$hLog[0],
$fEndTime-$hLog[5],
$iNumResults,
$hLog[4],
$hLog[2]
);
file_put_contents(CONST_Log_File, $aOutdata, FILE_APPEND | LOCK_EX);
}
}
if (CONST_Log_File && CONST_Log_File_SearchLog != '')
{
$aStartTime = explode('.',$hLog[0]);
file_put_contents(CONST_Log_File_SearchLog,
$aStartTime[0].','.$aStartTime[1].','.
php_uname('n').','.
'"'.addslashes(isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'').'",'.
'"'.addslashes($hLog[1]).'",'.
'"'.addslashes($hLog[3]).'",'.
'"'.addslashes($_SERVER['HTTP_USER_AGENT']).'",'.
'"'.addslashes((isset($_GET['format']))?$_GET['format']:'').'",'.
$iNumResults."\n",
FILE_APPEND);
}
}

View File

@@ -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 '';
}

View File

@@ -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)
));
}

View File

@@ -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)
));
}

View File

@@ -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">&lt;&gt;</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>

View File

@@ -1,62 +1,28 @@
<?php
$aFilteredPlaces = array();
$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['display_name'] = $aPlace['langaddress'];
if ($bShowAddressDetails) $aFilteredPlaces['address'] = $aPlace['aAddress'];
}
if (empty($aPlace)) {
if (isset($sError))
$aFilteredPlaces['error'] = $sError;
else $aFilteredPlaces['error'] = 'Unable to geocode';
} else {
if (isset($aPlace['place_id'])) $aFilteredPlaces['place_id'] = $aPlace['place_id'];
$aFilteredPlaces['licence'] = 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright';
$sOSMType = formatOSMType($aPlace['osm_type']);
if ($sOSMType) {
$aFilteredPlaces['osm_type'] = $sOSMType;
$aFilteredPlaces['osm_id'] = $aPlace['osm_id'];
}
if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat'];
if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon'];
javascript_renderData($aFilteredPlaces);
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'];
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'])) {
$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);

View 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);

View File

@@ -1,87 +1,45 @@
<?php
header('content-type: text/xml; charset=UTF-8');
header("content-type: text/xml; charset=UTF-8");
echo '<';
echo '?xml version="1.0" encoding="UTF-8" ?';
echo ">\n";
echo "<";
echo "?xml version=\"1.0\" encoding=\"UTF-8\" ?";
echo ">\n";
echo '<reversegeocode';
echo " timestamp='".date(DATE_RFC822)."'";
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
echo " querystring='".htmlspecialchars($_SERVER['QUERY_STRING'], ENT_QUOTES)."'";
echo ">\n";
echo "<reversegeocode";
echo " timestamp='".date(DATE_RFC822)."'";
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
echo " querystring='".htmlspecialchars($_SERVER['QUERY_STRING'], ENT_QUOTES)."'";
echo ">\n";
if (empty($aPlace)) {
if (isset($sError))
echo "<error>$sError</error>";
else echo '<error>Unable to geocode</error>';
} else {
echo '<result';
if ($aPlace['place_id']) echo ' place_id="'.$aPlace['place_id'].'"';
$sOSMType = formatOSMType($aPlace['osm_type']);
if ($sOSMType) echo ' osm_type="'.$sOSMType.'"'.' osm_id="'.$aPlace['osm_id'].'"';
if ($aPlace['ref']) echo ' ref="'.htmlspecialchars($aPlace['ref']).'"';
if (isset($aPlace['lat'])) echo ' lat="'.htmlspecialchars($aPlace['lat']).'"';
if (isset($aPlace['lon'])) echo ' lon="'.htmlspecialchars($aPlace['lon']).'"';
if (isset($aPlace['aBoundingBox'])) {
echo ' boundingbox="';
echo join(',', $aPlace['aBoundingBox']);
echo '"';
}
if (!sizeof($aPlace))
{
if (isset($sError))
echo "<error>$sError</error>";
else
echo "<error>Unable to geocode</error>";
}
else
{
echo "<result";
if ($aPlace['place_id']) echo ' place_id="'.$aPlace['place_id'].'"';
$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 ($aPlace['ref']) echo ' ref="'.htmlspecialchars($aPlace['ref']).'"';
if (isset($aPlace['lat'])) echo ' lat="'.htmlspecialchars($aPlace['lat']).'"';
if (isset($aPlace['lon'])) echo ' lon="'.htmlspecialchars($aPlace['lon']).'"';
echo ">".htmlspecialchars($aPlace['langaddress'])."</result>";
if (isset($aPlace['asgeojson'])) {
echo ' geojson=\'';
echo $aPlace['asgeojson'];
echo '\'';
}
if ($bShowAddressDetails) {
echo "<addressparts>";
foreach($aPlace['aAddress'] as $sKey => $sValue)
{
$sKey = str_replace(' ','_',$sKey);
echo "<$sKey>";
echo htmlspecialchars($sValue);
echo "</$sKey>";
}
echo "</addressparts>";
}
}
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 htmlspecialchars($sValue);
echo "</$sKey>";
}
echo '</addressparts>';
}
if (isset($aPlace['sExtraTags'])) {
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>';
echo "</reversegeocode>";

View File

@@ -1,122 +1,180 @@
<?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'); ?>
<link href="css/common.css" rel="stylesheet" type="text/css" />
<link href="css/details.css" rel="stylesheet" type="text/css" />
</head>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>OpenStreetMap Nominatim: <?php echo $aPointDetails['localname'];?></title>
<style>
body {
margin:0px;
padding:16px;
background:#ffffff;
height: 100%;
font: normal 12px/15px arial,sans-serif;
}
.line{
margin-left:20px;
}
.name{
font-weight: bold;
}
.notused{
color:#ddd;
}
.noname{
color:#800;
}
#map {
width:500px;
height:500px;
border: 2px solid #666;
float: right;
}
</style>
<script src="js/OpenLayers.js"></script>
<script src="js/tiles.js"></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.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"));
<?php
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();
function osmMapUrl($aFeature)
{
if (isset($sFeature['error_x']) && isset($sFeature['error_y']))
{
$sBaseUrl = '//www.openstreetmap.org/';
$sOSMType = formatOSMType($aFeature['osm_type'], false);
if ($sOSMType)
{
$sBaseUrl += $sOSMType.'/'.$aFeature['osm_id'];
}
feature.style = {
strokeColor: "#777777",
fillColor: "#F0F0F0",
strokeWidth: 2,
strokeOpacity: 0.75,
fillOpacity: 0.75,
strokeDashstyle: "longdash"
};
vectorLayer.addFeatures([feature]);
<?php } ?>
return '<a href="'.$sBaseUrl.'?mlat='.$aFeature['error_y'].'&mlon='.$aFeature['error_x'].'">view on osm.org</a>';
}
return '';
}
<?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 } ?>
function josm_edit_url($aFeature)
{
$fWidth = 0.0002;
$sLon = $aFeature['error_x'];
$sLat = $aFeature['error_y'];
if (isset($sLat))
{
return "http://localhost:8111/load_and_zoom?left=".($sLon-$fWidth)."&right=".($sLon+$fWidth)."&top=".($sLat+$fWidth)."&bottom=".($sLat-$fWidth);
}
$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 '';
}
<?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>
</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>';
?>
<body id="details-page">
<div class="container">
<div class="row">
<div class="col-md-6">
<p>This object has an invalid geometry.</p>
<p><b>Details:</b> <?php
$sVal = $aPointDetails['errormessage']?$aPointDetails['errormessage']:'&nbsp;';
$sOSMType = ($aPointDetails['osm_type'] == 'N'?'node':($aPointDetails['osm_type'] == 'W'?'way':($aPointDetails['osm_type'] == 'R'?'relation':'')));
$sOSMID = $aPointDetails['osm_id'];
<h1><?php echo $aPointDetails['localname'] ?></h1>
<div class="locationdetails">
<h2 class="bg-danger">This object has an invalid geometry.</h2>
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>";
}
<div>
Type: <span class="type"><?php echo $aPointDetails['class'].':'.$aPointDetails['type'];?></span>
</div>
?></p>
<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">
var nominatim_result = {
outlinestring: '<?php echo $aPointDetails['outlinestring'];?>',
lon: <?php echo isset($aPointDetails['error_x']) ? $aPointDetails['error_x'] : 0; ?>,
lat: <?php echo isset($aPointDetails['error_y']) ? $aPointDetails['error_y'] : 0; ?>
};
</script>
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
</body>
</body>
</html>

View File

@@ -1,254 +1,226 @@
<?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'); ?>
<link href="css/common.css" rel="stylesheet" type="text/css" />
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<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" />
</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
echo '<h1>';
if ($aPointDetails['icon'])
{
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";
echo '<div class="locationdetails">';
echo ' <div>Name: ';
foreach($aPointDetails['aNames'] as $sKey => $sValue)
{
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>';
echo ' <div>Last Updated: <span class="type">'.$aPointDetails['indexed_date'].'</span></div>';
echo ' <div>Admin Level: <span class="adminlevel">'.$aPointDetails['admin_level'].'</span></div>';
echo ' <div>Rank: <span class="rankaddress">'.$aPointDetails['rank_search_label'].'</span></div>';
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>';
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':'')));
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>';
if ($aPointDetails['wikipedia'])
{
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: ';
foreach($aPointDetails['aExtraTags'] as $sKey => $sValue)
{
echo ' <div class="line"><span class="name">'.$sValue.'</span> ('.$sKey.')</div>';
}
echo ' </div>';
echo "</div>\n";
function headline($sTitle)
{
echo "<tr class='all-columns'><td colspan='6'><h2>".$sTitle."</h2></td></tr>\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':'')));
function headline3($sTitle)
{
echo "<tr class='all-columns'><td colspan='6'><h3>".$sTitle."</h3></td></tr>\n";
}
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)
{
echo "<h2>Linked Places</h2>\n";
echo '<div class="linked">';
foreach($aLinkedLines as $aAddressLine)
{
$sOSMType = ($aAddressLine['osm_type'] == 'N'?'node':($aAddressLine['osm_type'] == 'W'?'way':($aAddressLine['osm_type'] == 'R'?'relation':'')));
function format_distance($fDistance)
{
// $fDistance is in meters
if ($fDistance < 1)
{
return '0';
}
elseif ($fDistance < 1000)
{
return'<abbr class="distance" title="'.$fDistance.'">~'.(round($fDistance,0)).' m</abbr>';
}
else
{
return'<abbr class="distance" title="'.$fDistance.'">~'.(round($fDistance/1000,1)).' km</abbr>';
}
}
echo '<div class="line">';
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";
}
function kv($sKey,$sValue)
{
echo ' <tr><td>' . $sKey . '</td><td>'.$sValue.'</td></tr>'. "\n";
}
if ($aPlaceSearchNameKeywords)
{
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";
}
}
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;
}
if (sizeof($aParentOfLines))
{
echo "<h2>Parent Of:</h2>\n<div>\n";
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 &gt;') . "</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";
}
$aGroupedAddressLines = array();
foreach($aParentOfLines as $aAddressLine)
{
if (!isset($aGroupedAddressLines[$aAddressLine['type']])) $aGroupedAddressLines[$aAddressLine['type']] = array();
$aGroupedAddressLines[$aAddressLine['type']][] = $aAddressLine;
}
foreach($aGroupedAddressLines as $sGroupHeading => $aParentOfLines)
{
$sGroupHeading = ucwords($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':'')));
echo '<div class="line">';
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)).'&nbsp;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 (sizeof($aParentOfLines) >= 500) {
echo '<p>There are more child objects which are not shown.</p>';
}
echo '</div>';
}
// echo '<h2>Other Parts:</h2>';
// echo '<h2>Linked To:</h2>';
?>
<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'])
{
kv('Wikipedia Calculated' , wikipediaLink($aPointDetails) );
}
kv('Computed Postcode', $aPointDetails['postcode']);
kv('Address Tags' , hash_to_subtable($aPointDetails['aAddressTags']) );
kv('Extra Tags' , hash_to_subtable($aPointDetails['aExtraTags']) );
?>
</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);
}
?>
<?php
if ($aLinkedLines)
{
headline('Linked Places');
foreach ($aLinkedLines as $aAddressLine) {
_one_row($aAddressLine);
}
}
if ($bIncludeKeywords)
{
headline('Name Keywords');
if ($aPlaceSearchNameKeywords) {
foreach ($aPlaceSearchNameKeywords as $aRow) {
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
}
}
headline('Address Keywords');
if ($aPlaceSearchAddressKeywords) {
foreach ($aPlaceSearchAddressKeywords as $aRow) {
_one_keyword_row($aRow['word_token'], $aRow['word_id']);
}
}
}
if (!empty($aHierarchyLines))
{
headline('Parent Of');
$aGroupedAddressLines = array();
foreach ($aHierarchyLines as $aAddressLine) {
if ($aAddressLine['type'] == 'yes') $sType = $aAddressLine['class'];
else $sType = $aAddressLine['type'];
if (!isset($aGroupedAddressLines[$sType]))
$aGroupedAddressLines[$sType] = array();
$aGroupedAddressLines[$sType][] = $aAddressLine;
}
foreach ($aGroupedAddressLines as $sGroupHeading => $aHierarchyLines) {
$sGroupHeading = ucwords($sGroupHeading);
headline3($sGroupHeading);
foreach ($aHierarchyLines as $aAddressLine) {
_one_row($aAddressLine);
}
}
if (count($aHierarchyLines) >= 500) {
echo '<p>There are more child objects which are not shown.</p>';
}
}
echo "</table>\n";
?>
</div>
</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>
</body>
</html>

View File

@@ -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);

View File

@@ -1,15 +0,0 @@
<footer>
<p class="disclaimer">
Addresses and postcodes are approximate
</p>
<p class="copyright">
&copy; <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>

View File

@@ -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" />

View File

@@ -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 &amp; 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">&times;</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>

View File

@@ -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>.

View File

@@ -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&amp;status=assigned&amp;status=reopened&amp;component=nominatim&amp;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>

View File

@@ -1,77 +1,88 @@
<?php
$aOutput = array();
$aOutput['licence'] = 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright';
$aOutput['batch'] = array();
$aOutput = array();
$aOutput['licence'] = "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright";
$aOutput['batch'] = array();
foreach ($aBatchResults as $aSearchResults) {
if (!$aSearchResults) $aSearchResults = array();
$aFilteredPlaces = array();
foreach ($aSearchResults as $iResNum => $aPointDetails) {
$aPlace = array(
'place_id'=>$aPointDetails['place_id'],
);
foreach($aBatchResults as $aSearchResults)
{
if (!$aSearchResults) $aSearchResults = array();
$aFilteredPlaces = array();
foreach($aSearchResults as $iResNum => $aPointDetails)
{
$aPlace = array(
'place_id'=>$aPointDetails['place_id'],
);
$sOSMType = formatOSMType($aPointDetails['osm_type']);
if ($sOSMType) {
$aPlace['osm_type'] = $sOSMType;
$aPlace['osm_id'] = $aPointDetails['osm_id'];
}
$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['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['aPolyPoints']) && $bShowPolygons)
{
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
}
}
if (isset($aPointDetails['zoom'])) {
$aPlace['zoom'] = $aPointDetails['zoom'];
}
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['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['category'] = $aPointDetails['class'];
$aPlace['type'] = $aPointDetails['type'];
$aPlace['importance'] = $aPointDetails['importance'];
$aPlace['importance'] = $aPointDetails['importance'];
if (isset($aPointDetails['icon'])) {
$aPlace['icon'] = $aPointDetails['icon'];
}
if (isset($aPointDetails['icon']))
{
$aPlace['icon'] = $aPointDetails['icon'];
}
if (isset($aPointDetails['address'])) {
$aPlace['address'] = $aPointDetails['address']->getAddressNames();
}
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['asgeojson']))
{
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
}
if (isset($aPointDetails['assvg'])) {
$aPlace['svg'] = $aPointDetails['assvg'];
}
if (isset($aPointDetails['assvg']))
{
$aPlace['svg'] = $aPointDetails['assvg'];
}
if (isset($aPointDetails['astext'])) {
$aPlace['geotext'] = $aPointDetails['astext'];
}
if (isset($aPointDetails['astext']))
{
$aPlace['geotext'] = $aPointDetails['astext'];
}
if (isset($aPointDetails['askml'])) {
$aPlace['geokml'] = $aPointDetails['askml'];
}
if (isset($aPointDetails['askml']))
{
$aPlace['geokml'] = $aPointDetails['askml'];
}
$aFilteredPlaces[] = $aPlace;
}
$aOutput['batch'][] = $aFilteredPlaces;
}
$aFilteredPlaces[] = $aPlace;
}
$aOutput['batch'][] = $aFilteredPlaces;
}
javascript_renderData($aOutput, array('geojson'));
javascript_renderData($aOutput, array('geojson'));

View File

@@ -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
));

View File

@@ -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
));

View File

@@ -1,114 +1,298 @@
<?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'); ?>
<link href="css/common.css" rel="stylesheet" type="text/css" />
<link href="css/search.css" rel="stylesheet" type="text/css" />
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<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" />
<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>
<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 class="form-group">
<input id="q" name="q" type="text" class="form-control input-sm" placeholder="Search" value="<?php echo htmlspecialchars($sQuery); ?>" >
</div>
<div class="form-group search-button-group">
<button type="submit" class="btn btn-primary btn-sm">Search</button>
<?php if (CONST_Search_AreaPolygons) { ?>
<input type="hidden" value="1" name="polygon_geojson" />
<?php } ?>
<input type="hidden" name="viewbox" value="<?php if (isset($aMoreParams['viewbox'])) echo ($aMoreParams['viewbox']); ?>" />
<div class="checkbox-inline">
<input type="checkbox" id="use_viewbox" <?php if (isset($aMoreParams['viewbox'])) echo "checked='checked'"; ?>>
<label for="use_viewbox">apply viewbox</label>
</div>
</div>
<div class="search-type-link">
<a id="switch-to-reverse" href="<?php echo CONST_Website_BaseURL; ?>reverse.php?format=html">reverse search</a>
</div>
</form>
<div id="seachheader">
<form accept-charset="UTF-8" action="<?php echo CONST_Website_BaseURL; ?>search.php" method="get">
<table border="0" width="100%" summary="header">
<tr>
<td valign="middle" style="width:30px;"><img alt="logo" src="images/logo.gif" /></td>
<td valign="middle" style="width:400px;"><input id="q" name="q" value="<?php echo htmlspecialchars($sQuery);
?>" style="width:270px;" /><input type="text" id="viewbox" style="width:120px;" name="viewbox" /></td>
<td style="width:80px;"><input type="submit" value="Search"/></td>
<?php if (CONST_Search_AreaPolygons) { ?> <td style="width:100px;"><input type="checkbox" value="1" name="polygon" <?php if ($bAsText) echo "checked='checked'"; ?>/> Highlight</td>
<td style="text-align:right;">Data: <?php echo $sDataDate; ?></td>
<td style="text-align:right;">
<a href="http://wiki.openstreetmap.org/wiki/Nominatim" target="_blank">Documentation</a> | <a href="http://wiki.openstreetmap.org/wiki/Nominatim/FAQ"
target="_blank">FAQ</a></td>
<?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>
</tr>
</table>
</form>
</div>
<div id="content">
<?php
if ($sQuery)
{
?>
<div id="searchresultsfade1"></div><div id="searchresultsfade2"></div><div id="searchresultsfade3"></div><div id="searchresultsfade4"></div>
<div id="searchresults">
<?php
if ($sSuggestionURL)
{
echo '<div class="more"><b>Suggest: </b><a href="'.$sSuggestionURL.'"><b>'.$sSuggestion.'</b></a></div>';
}
foreach($aSearchResults as $iResNum => $aResult)
{
if ($aResult['aBoundingBox'])
{
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'].');">';
}
<?php if ($sQuery) { ?>
echo (isset($aResult['icon'])?'<img alt="icon" src="'.$aResult['icon'].'"/>':'');
echo ' <span class="name">'.$aResult['name'].'</span>';
echo ' <span class="latlon">'.round($aResult['lat'],3).','.round($aResult['lon'],3).'</span>';
echo ' <span class="place_id">'.$aResult['place_id'].'</span>';
if (isset($aResult['label']))
echo ' <span class="type">('.$aResult['label'].')</span>';
else
echo ' <span class="type">('.ucwords(str_replace('_',' ',$aResult['type'])).')</span>';
echo ' <span class="details">(<a href="details.php?place_id='.$aResult['place_id'].'">details</a>)</span>';
echo '</div>';
}
if (sizeof($aSearchResults))
{
if ($sMoreURL)
{
echo '<div class="more"><a href="'.htmlentities($sMoreURL).'">Search for more results</a></div>';
}
}
else
{
echo '<div class="noresults">No search results found</div>';
}
<div id="searchresults" class="sidebar">
<?php
$i = 0;
foreach($aSearchResults as $iResNum => $aResult)
{
?>
<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
}
?>
echo '<div class="result" data-position=' . $i . '>';
<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&amp;status=assigned&amp;status=reopened&amp;component=nominatim&amp;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>
echo (isset($aResult['icon'])?'<img alt="icon" src="'.$aResult['icon'].'"/>':'');
echo ' <span class="name">'.htmlspecialchars($aResult['name']).'</span>';
// echo ' <span class="latlon">'.round($aResult['lat'],3).','.round($aResult['lon'],3).'</span>';
// echo ' <span class="place_id">'.$aResult['place_id'].'</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 ' <a class="btn btn-default btn-xs details" href="details.php?place_id='.$aResult['place_id'].'">details</a>';
echo '</div>';
$i = $i+1;
}
if (!empty($aSearchResults) && $sMoreURL)
{
echo '<div class="more"><a class="btn btn-primary" href="'.htmlentities($sMoreURL).'">Search for more results</a></div>';
}
else
{
echo '<div class="noresults">No search results found</div>';
}
?>
</div>
<?php } else { ?>
<div id="intro" class="sidebar">
<?php include(CONST_BasePath.'/lib/template/includes/introduction.php'); ?>
</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' => CONST_Default_Zoom,
'lat' => CONST_Default_Lat,
'lon' => CONST_Default_Lon,
'tile_url' => CONST_Map_Tile_URL,
'tile_attribution' => CONST_Map_Tile_Attribution
);
echo 'var nominatim_map_init = ' . json_encode($aNominatimMapInit, JSON_PRETTY_PRINT) . ';';
echo 'var nominatim_results = ' . json_encode($aSearchResults, JSON_PRETTY_PRINT) . ';';
?>
</script>
<?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
<!--
<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">
init();
<?php
foreach($aSearchResults as $iResNum => $aResult)
{
if ($aResult['aBoundingBox'])
{
echo '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";
}
else
{
echo 'panToLatLonZoom('.$fLat.', '.$fLon.', '.$iZoom.');'."\n";
}
break;
}
if (!sizeof($aSearchResults))
{
echo 'panToLatLonZoom('.$fLat.', '.$fLon.', '.$iZoom.');'."\n";
}
?>
</script>
</body>
</html>

View File

@@ -1,73 +1,80 @@
<?php
header("content-type: application/json; charset=UTF-8");
$aFilteredPlaces = array();
foreach ($aSearchResults as $iResNum => $aPointDetails) {
$aPlace = array(
'place_id'=>$aPointDetails['place_id'],
'licence'=>'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
);
$sOSMType = formatOSMType($aPointDetails['osm_type']);
if ($sOSMType) {
$aPlace['osm_type'] = $sOSMType;
$aPlace['osm_id'] = $aPointDetails['osm_id'];
}
$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",
);
if (isset($aPointDetails['aBoundingBox'])) {
$aPlace['boundingbox'] = $aPointDetails['aBoundingBox'];
$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['aPolyPoints'])) {
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
}
}
if (isset($aPointDetails['aBoundingBox']))
{
$aPlace['boundingbox'] = array(
$aPointDetails['aBoundingBox'][0],
$aPointDetails['aBoundingBox'][1],
$aPointDetails['aBoundingBox'][2],
$aPointDetails['aBoundingBox'][3]);
if (isset($aPointDetails['zoom'])) {
$aPlace['zoom'] = $aPointDetails['zoom'];
}
if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons)
{
$aPlace['polygonpoints'] = $aPointDetails['aPolyPoints'];
}
}
$aPlace['lat'] = $aPointDetails['lat'];
$aPlace['lon'] = $aPointDetails['lon'];
if (isset($aPointDetails['zoom']))
{
$aPlace['zoom'] = $aPointDetails['zoom'];
}
$aPlace['display_name'] = $aPointDetails['name'];
$aPlace['lat'] = $aPointDetails['lat'];
$aPlace['lon'] = $aPointDetails['lon'];
$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['type'] = $aPointDetails['type'];
$aPlace['class'] = $aPointDetails['class'];
$aPlace['type'] = $aPointDetails['type'];
$aPlace['importance'] = $aPointDetails['importance'];
$aPlace['importance'] = $aPointDetails['importance'];
if (isset($aPointDetails['icon']) && $aPointDetails['icon']) {
$aPlace['icon'] = $aPointDetails['icon'];
}
if (isset($aPointDetails['icon']) && $aPointDetails['icon'])
{
$aPlace['icon'] = $aPointDetails['icon'];
}
if (isset($aPointDetails['address'])) {
$aPlace['address'] = $aPointDetails['address']->getAddressNames();
}
if (isset($aPointDetails['address']))
{
$aPlace['address'] = $aPointDetails['address'];
}
if (isset($aPointDetails['asgeojson'])) {
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
}
if (isset($aPointDetails['asgeojson']))
{
$aPlace['geojson'] = json_decode($aPointDetails['asgeojson']);
}
if (isset($aPointDetails['assvg'])) {
$aPlace['svg'] = $aPointDetails['assvg'];
}
if (isset($aPointDetails['assvg']))
{
$aPlace['svg'] = $aPointDetails['assvg'];
}
if (isset($aPointDetails['astext'])) {
$aPlace['geotext'] = $aPointDetails['astext'];
}
if (isset($aPointDetails['astext']))
{
$aPlace['geotext'] = $aPointDetails['astext'];
}
if (isset($aPointDetails['askml'])) {
$aPlace['geokml'] = $aPointDetails['askml'];
}
if (isset($aPointDetails['askml']))
{
$aPlace['geokml'] = $aPointDetails['askml'];
}
if (isset($aPointDetails['sExtraTags'])) $aPlace['extratags'] = $aPointDetails['sExtraTags'];
if (isset($aPointDetails['sNameDetails'])) $aPlace['namedetails'] = $aPointDetails['sNameDetails'];
$aFilteredPlaces[] = $aPlace;
}
$aFilteredPlaces[] = $aPlace;
}
javascript_renderData($aFilteredPlaces);
javascript_renderData($aFilteredPlaces);

View 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'));

View File

@@ -1,134 +1,124 @@
<?php
header('content-type: text/xml; charset=UTF-8');
header("content-type: text/xml; charset=UTF-8");
echo '<';
echo '?xml version="1.0" encoding="UTF-8" ?';
echo ">\n";
echo "<";
echo "?xml version=\"1.0\" encoding=\"UTF-8\" ?";
echo ">\n";
echo '<';
echo (isset($sXmlRootTag)?$sXmlRootTag:'searchresults');
echo " timestamp='".date(DATE_RFC822)."'";
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
echo " querystring='".htmlspecialchars($sQuery, ENT_QUOTES)."'";
if (isset($aMoreParams['viewbox'])) echo " viewbox='".htmlspecialchars($aMoreParams['viewbox'], ENT_QUOTES)."'";
echo " polygon='".(isset($aMoreParams['polygon'])?'true':'false')."'";
if (isset($aMoreParams['exclude_place_ids'])) {
echo " exclude_place_ids='".htmlspecialchars($aMoreParams['exclude_place_ids'])."'";
}
echo " more_url='".htmlspecialchars($sMoreURL)."'";
echo ">\n";
echo "<searchresults";
echo " timestamp='".date(DATE_RFC822)."'";
echo " attribution='Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'";
echo " querystring='".htmlspecialchars($sQuery, ENT_QUOTES)."'";
if ($sViewBox) echo " viewbox='".htmlspecialchars($sViewBox, ENT_QUOTES)."'";
echo " polygon='".($bShowPolygons?'true':'false')."'";
if (sizeof($aExcludePlaceIDs))
{
echo " exclude_place_ids='".htmlspecialchars(join(',',$aExcludePlaceIDs))."'";
}
if ($sMoreURL)
{
echo " more_url='".htmlspecialchars($sMoreURL)."'";
}
echo ">\n";
foreach ($aSearchResults as $iResNum => $aResult) {
echo "<place place_id='".$aResult['place_id']."'";
$sOSMType = formatOSMType($aResult['osm_type']);
if ($sOSMType) {
echo " osm_type='$sOSMType'";
echo " osm_id='".$aResult['osm_id']."'";
}
echo " place_rank='".$aResult['rank_search']."'";
foreach($aSearchResults as $iResNum => $aResult)
{
echo "<place place_id='".$aResult['place_id']."'";
$sOSMType = ($aResult['osm_type'] == 'N'?'node':($aResult['osm_type'] == 'W'?'way':($aResult['osm_type'] == 'R'?'relation':'')));
if ($sOSMType)
{
echo " osm_type='$sOSMType'";
echo " osm_id='".$aResult['osm_id']."'";
}
echo " place_rank='".$aResult['rank_search']."'";
if (isset($aResult['aBoundingBox'])) {
echo ' boundingbox="';
echo join(',', $aResult['aBoundingBox']);
echo '"';
if (isset($aResult['aBoundingBox']))
{
echo ' boundingbox="';
echo $aResult['aBoundingBox'][0];
echo ','.$aResult['aBoundingBox'][1];
echo ','.$aResult['aBoundingBox'][2];
echo ','.$aResult['aBoundingBox'][3];
echo '"';
if (isset($aResult['aPolyPoints'])) {
echo ' polygonpoints=\'';
echo json_encode($aResult['aPolyPoints']);
echo '\'';
}
}
if ($bShowPolygons && isset($aResult['aPolyPoints']))
{
echo ' polygonpoints=\'';
echo json_encode($aResult['aPolyPoints']);
echo '\'';
}
}
if (isset($aResult['asgeojson'])) {
echo ' geojson=\'';
echo $aResult['asgeojson'];
echo '\'';
}
if (isset($aResult['asgeojson']))
{
echo ' geojson=\'';
echo $aResult['asgeojson'];
echo '\'';
}
if (isset($aResult['assvg'])) {
echo ' geosvg=\'';
echo $aResult['assvg'];
echo '\'';
}
if (isset($aResult['assvg']))
{
echo ' geosvg=\'';
echo $aResult['assvg'];
echo '\'';
}
if (isset($aResult['astext'])) {
echo ' geotext=\'';
echo $aResult['astext'];
echo '\'';
}
if (isset($aResult['astext']))
{
echo ' geotext=\'';
echo $aResult['astext'];
echo '\'';
}
if (isset($aResult['zoom'])) {
echo " zoom='".$aResult['zoom']."'";
}
if (isset($aResult['zoom']))
{
echo " zoom='".$aResult['zoom']."'";
}
echo " lat='".$aResult['lat']."'";
echo " lon='".$aResult['lon']."'";
echo " display_name='".htmlspecialchars($aResult['name'], ENT_QUOTES)."'";
echo " lat='".$aResult['lat']."'";
echo " lon='".$aResult['lon']."'";
echo " display_name='".htmlspecialchars($aResult['name'], ENT_QUOTES)."'";
echo " class='".htmlspecialchars($aResult['class'])."'";
echo " type='".htmlspecialchars($aResult['type'], ENT_QUOTES)."'";
echo " importance='".htmlspecialchars($aResult['importance'])."'";
if (isset($aResult['icon']) && $aResult['icon']) {
echo " icon='".htmlspecialchars($aResult['icon'], ENT_QUOTES)."'";
}
echo " class='".htmlspecialchars($aResult['class'])."'";
echo " type='".htmlspecialchars($aResult['type'], ENT_QUOTES)."'";
echo " importance='".htmlspecialchars($aResult['importance'])."'";
if (isset($aResult['icon']) && $aResult['icon'])
{
echo " icon='".htmlspecialchars($aResult['icon'], ENT_QUOTES)."'";
}
$bHasDelim = false;
if (isset($aResult['address']) || isset($aResult['askml']))
{
echo ">";
}
if (isset($aResult['askml'])) {
if (!$bHasDelim) {
$bHasDelim = true;
echo '>';
}
echo "\n<geokml>";
echo $aResult['askml'];
echo '</geokml>';
}
if (isset($aResult['askml']))
{
echo "\n<geokml>";
echo $aResult['askml'];
echo "</geokml>";
}
if (isset($aResult['sExtraTags'])) {
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['address']))
{
echo "\n";
foreach($aResult['address'] as $sKey => $sValue)
{
$sKey = str_replace(' ','_',$sKey);
echo "<$sKey>";
echo htmlspecialchars($sValue);
echo "</$sKey>";
}
}
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";
foreach ($aResult['address']->getAddressNames() as $sKey => $sValue) {
$sKey = str_replace(' ', '_', $sKey);
echo "<$sKey>";
echo htmlspecialchars($sValue);
echo "</$sKey>";
}
}
if ($bHasDelim) {
echo '</place>';
} else {
echo '/>';
}
}
echo '</' . (isset($sXmlRootTag)?$sXmlRootTag:'searchresults') . '>';
if (isset($aResult['address']) || isset($aResult['askml']))
{
echo "</place>";
}
else
{
echo "/>";
}
}
echo "</searchresults>";

View File

@@ -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
)

View File

@@ -1,9 +1,8 @@
MODULES = nominatim
PG_CPPFLAGS = -I$(MODSRCDIR)
PGXS := @POSTGRESQL_PGXS@
PG_CONFIG := @PG_CONFIG@
include $(PGXS)
VPATH = $(MODSRCDIR)
all:
chmod 755 nominatim.so

View File

@@ -157,18 +157,17 @@ transliteration( PG_FUNCTION_ARGS )
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)
{
char *p;
// Search string is too long to be present
// Search string is too long to be pressent
if (fromlen > *len) return;
p = strstr(buffer, from);
while(p)
{
if (!isspace || (p > buffer && *(p-1) != ' '))
if (!isspace || *(p-1) != ' ')
{
(*changes)++;
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);
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));
memcpy(buffer+1, sourcedata, sourcedatalength);
buffer[0] = 32;

File diff suppressed because one or more lines are too long

View File

@@ -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
View 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

View File

@@ -152,7 +152,7 @@ void nominatim_exportCreatePreparedQueries(PGconn * conn)
pg_prepare_params[0] = PG_OID_INT8;
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);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
@@ -355,7 +355,7 @@ void nominatim_exportFreeQueries(struct export_data * querySet)
/*
* Requirements: the prepared queries must exist
*/
void nominatim_exportPlace(uint64_t place_id, PGconn * conn,
void nominatim_exportPlace(uint64_t place_id, PGconn * conn,
xmlTextWriterPtr writer, pthread_mutex_t * writer_mutex, struct export_data * prevQuerySet)
{
struct export_data querySet;
@@ -387,7 +387,7 @@ void nominatim_exportPlace(uint64_t place_id, PGconn * conn,
{
// Add
if (writer_mutex) pthread_mutex_lock( writer_mutex );
nominatim_exportStartMode(writer, 1);
nominatim_exportStartMode(writer, 1);
}
else
{
@@ -396,14 +396,14 @@ void nominatim_exportPlace(uint64_t place_id, PGconn * conn,
// TODO: detect changes
if (writer_mutex) pthread_mutex_lock( writer_mutex );
nominatim_exportStartMode(writer, 2);
nominatim_exportStartMode(writer, 2);
}
}
else
{
// Add
if (writer_mutex) pthread_mutex_lock( writer_mutex );
nominatim_exportStartMode(writer, 1);
nominatim_exportStartMode(writer, 1);
}
xmlTextWriterStartElement(writer, BAD_CAST "feature");
@@ -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_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 "linked_place_id", BAD_CAST PQgetvalue(querySet.res, 0, 15));
if (PQntuples(querySet.resNames))
{

View File

@@ -486,7 +486,7 @@ void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
}
paramValues[7] = (const char *)featureExtraTagString;
if (xmlStrlen(feature.parentPlaceID) == 0)
if (strlen(feature.parentPlaceID) == 0)
paramValues[8] = "0";
else
paramValues[8] = (const char *)feature.parentPlaceID;
@@ -541,10 +541,10 @@ void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
if (featureNameLines)
{
if (xmlStrlen(feature.parentPlaceID) > 0 && featureAddressLines == 0)
if (strlen(feature.parentPlaceID) > 0 && featureAddressLines == 0)
{
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]);
res = PQexecPrepared(conn, "search_name_from_parent_insert", 2, paramValues, NULL, NULL, 0);
if (PQresultStatus(res) != PGRES_COMMAND_OK)

View File

@@ -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>
@@ -21,258 +19,34 @@
extern int verbose;
void run_indexing(int rank, int interpolation, PGconn *conn, int num_threads,
struct index_thread_data * thread_data, const char *structuredoutputfile)
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile)
{
int tuples, count, sleepcount;
struct index_thread_data * thread_data;
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
int tuples, count, sleepcount;
time_t rankStartTime;
int rankTotalTuples;
int rankCountTuples;
float rankPerSecond;
PGconn *conn;
PGresult * res;
PGresult * resSectors;
PGresult * resPlaces;
PGresult * resNULL;
int rank;
int i;
int iSector;
int iResult;
const char *paramValues[2];
int paramLengths[2];
int paramFormats[2];
uint32_t paramRank;
uint32_t paramSector;
uint32_t sector;
xmlTextWriterPtr writer;
pthread_mutex_t writer_mutex = PTHREAD_MUTEX_INITIALIZER;
// Create the output file
writer = NULL;
if (structuredoutputfile)
{
writer = nominatim_exportXMLStart(structuredoutputfile);
}
if (interpolation)
{
fprintf(stderr, "Starting interpolation lines (location_property_osmline)\n");
}
else
{
fprintf(stderr, "Starting rank %d\n", rank);
}
rankCountTuples = 0;
rankPerSecond = 0;
paramRank = PGint32(rank);
paramValues[0] = (char *)&paramRank;
paramLengths[0] = sizeof(paramRank);
paramFormats[0] = 1;
if (interpolation)
{
resSectors = PQexecPrepared(conn, "index_sectors_osmline", 0, NULL, 0, NULL, 1);
}
else
{
resSectors = PQexecPrepared(conn, "index_sectors", 1, paramValues, paramLengths, paramFormats, 1);
}
if (PQresultStatus(resSectors) != PGRES_TUPLES_OK)
{
fprintf(stderr, "index_sectors: SELECT failed: %s", PQerrorMessage(conn));
PQclear(resSectors);
exit(EXIT_FAILURE);
}
if (PQftype(resSectors, 0) != PG_OID_INT4)
{
fprintf(stderr, "Sector value has unexpected type\n");
PQclear(resSectors);
exit(EXIT_FAILURE);
}
if (PQftype(resSectors, 1) != PG_OID_INT8)
{
fprintf(stderr, "Sector value has unexpected type\n");
PQclear(resSectors);
exit(EXIT_FAILURE);
}
rankTotalTuples = 0;
for (iSector = 0; iSector < PQntuples(resSectors); iSector++)
{
rankTotalTuples += PGint64(*((uint64_t *)PQgetvalue(resSectors, iSector, 1)));
}
rankStartTime = time(0);
for (iSector = 0; iSector <= PQntuples(resSectors); iSector++)
{
if (iSector > 0)
{
resPlaces = PQgetResult(conn);
if (PQresultStatus(resPlaces) != PGRES_TUPLES_OK)
{
fprintf(stderr, "index_sector_places: SELECT failed: %s", PQerrorMessage(conn));
PQclear(resPlaces);
exit(EXIT_FAILURE);
}
if (PQftype(resPlaces, 0) != PG_OID_INT8)
{
fprintf(stderr, "Place_id value has unexpected type\n");
PQclear(resPlaces);
exit(EXIT_FAILURE);
}
resNULL = PQgetResult(conn);
if (resNULL != NULL)
{
fprintf(stderr, "Unexpected non-null response\n");
exit(EXIT_FAILURE);
}
}
if (iSector < PQntuples(resSectors))
{
sector = PGint32(*((uint32_t *)PQgetvalue(resSectors, iSector, 0)));
// fprintf(stderr, "\n Starting sector %d size %ld\n", sector, PGint64(*((uint64_t *)PQgetvalue(resSectors, iSector, 1))));
// Get all the place_id's for this sector
paramRank = PGint32(rank);
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 *)&paramRank;
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 *)&paramSector;
paramLengths[0] = sizeof(paramSector);
paramFormats[0] = 1;
}
else
{
paramValues[0] = (char *)&paramRank;
paramLengths[0] = sizeof(paramRank);
paramFormats[0] = 1;
paramValues[1] = (char *)&paramSector;
paramLengths[1] = sizeof(paramSector);
paramFormats[1] = 1;
iResult = PQsendQueryPrepared(conn, "index_sector_places", 2, paramValues, paramLengths, paramFormats, 1);
}
}
if (!iResult)
{
fprintf(stderr, "index_sector_places: SELECT failed: %s", PQerrorMessage(conn));
PQclear(resPlaces);
exit(EXIT_FAILURE);
}
}
if (iSector > 0)
{
count = 0;
rankPerSecond = 0;
tuples = PQntuples(resPlaces);
if (tuples > 0)
{
// Spawn threads
for (i = 0; i < num_threads; i++)
{
thread_data[i].res = resPlaces;
thread_data[i].tuples = tuples;
thread_data[i].count = &count;
thread_data[i].count_mutex = &count_mutex;
thread_data[i].writer = writer;
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]);
}
// Monitor threads to give user feedback
sleepcount = 0;
while (count < tuples)
{
usleep(1000);
// Aim for one update per second
if (sleepcount++ > 1000)
{
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);
}
sleepcount = 0;
}
}
// Wait for everything to finish
for (i = 0; i < num_threads; i++)
{
pthread_join(thread_data[i].thread, NULL);
}
rankCountTuples += tuples;
}
// Finished sector
rankPerSecond = (float)rankCountTuples / MAX(difftime(time(0), rankStartTime),1);
fprintf(stderr, " Done %i in %i @ %f per second - ETA (seconds): %f\n", rankCountTuples, (int)(difftime(time(0), rankStartTime)), rankPerSecond, ((float)(rankTotalTuples - rankCountTuples))/rankPerSecond);
PQclear(resPlaces);
}
if (rankTotalTuples-rankCountTuples < num_threads*20 && iSector < PQntuples(resSectors))
{
iSector = PQntuples(resSectors) - 1;
}
}
// Finished rank
fprintf(stderr, "\r Done %i in %i @ %f per second - FINISHED\n\n", rankCountTuples, (int)(difftime(time(0), rankStartTime)), rankPerSecond);
PQclear(resSectors);
}
void nominatim_index(int rank_min, int rank_max, int num_threads, const char *conninfo, const char *structuredoutputfile)
{
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;
@@ -286,23 +60,6 @@ void nominatim_index(int rank_min, int rank_max, int num_threads, const char *co
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",
@@ -313,16 +70,6 @@ void nominatim_index(int rank_min, int rank_max, int num_threads, const char *co
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",
@@ -357,28 +104,7 @@ void nominatim_index(int rank_min, int rank_max, int num_threads, const char *co
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++)
@@ -396,54 +122,199 @@ void nominatim_index(int rank_min, int rank_max, int num_threads, const char *co
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));
fprintf(stderr, "Failed preparing index_placex: %s\n", PQerrorMessage(conn));
exit(EXIT_FAILURE);
}
PQclear(res);
if (db_has_locale)
/*res = PQexec(thread_data[i].conn, "set enable_seqscan = false");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
// 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);
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
writer = NULL;
if (structuredoutputfile)
{
writer = nominatim_exportXMLStart(structuredoutputfile);
}
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)
fprintf(stderr, "Starting rank %d\n", rank);
rankCountTuples = 0;
rankPerSecond = 0;
paramRank = PGint32(rank);
paramValues[0] = (char *)&paramRank;
paramLengths[0] = sizeof(paramRank);
paramFormats[0] = 1;
// if (rank < 16)
// resSectors = PQexecPrepared(conn, "index_nosectors", 1, paramValues, paramLengths, paramFormats, 1);
// else
resSectors = PQexecPrepared(conn, "index_sectors", 1, paramValues, paramLengths, paramFormats, 1);
if (PQresultStatus(resSectors) != PGRES_TUPLES_OK)
{
run_indexing(rank, 1, conn, num_threads, thread_data, structuredoutputfile);
fprintf(stderr, "index_sectors: SELECT failed: %s", PQerrorMessage(conn));
PQclear(resSectors);
exit(EXIT_FAILURE);
}
run_indexing(rank, 0, conn, num_threads, thread_data, structuredoutputfile);
if (PQftype(resSectors, 0) != PG_OID_INT4)
{
fprintf(stderr, "Sector value has unexpected type\n");
PQclear(resSectors);
exit(EXIT_FAILURE);
}
if (PQftype(resSectors, 1) != PG_OID_INT8)
{
fprintf(stderr, "Sector value has unexpected type\n");
PQclear(resSectors);
exit(EXIT_FAILURE);
}
rankTotalTuples = 0;
for (iSector = 0; iSector < PQntuples(resSectors); iSector++)
{
rankTotalTuples += PGint64(*((uint64_t *)PQgetvalue(resSectors, iSector, 1)));
}
rankStartTime = time(0);
for (iSector = 0; iSector <= PQntuples(resSectors); iSector++)
{
if (iSector > 0)
{
resPlaces = PQgetResult(conn);
if (PQresultStatus(resPlaces) != PGRES_TUPLES_OK)
{
fprintf(stderr, "index_sector_places: SELECT failed: %s", PQerrorMessage(conn));
PQclear(resPlaces);
exit(EXIT_FAILURE);
}
if (PQftype(resPlaces, 0) != PG_OID_INT8)
{
fprintf(stderr, "Place_id value has unexpected type\n");
PQclear(resPlaces);
exit(EXIT_FAILURE);
}
resNULL = PQgetResult(conn);
if (resNULL != NULL)
{
fprintf(stderr, "Unexpected non-null response\n");
exit(EXIT_FAILURE);
}
}
if (iSector < PQntuples(resSectors))
{
sector = PGint32(*((uint32_t *)PQgetvalue(resSectors, iSector, 0)));
// fprintf(stderr, "\n Starting sector %d size %ld\n", sector, PGint64(*((uint64_t *)PQgetvalue(resSectors, iSector, 1))));
// Get all the place_id's for this sector
paramRank = PGint32(rank);
paramValues[0] = (char *)&paramRank;
paramLengths[0] = sizeof(paramRank);
paramFormats[0] = 1;
paramSector = PGint32(sector);
paramValues[1] = (char *)&paramSector;
paramLengths[1] = sizeof(paramSector);
paramFormats[1] = 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)
{
fprintf(stderr, "index_sector_places: SELECT failed: %s", PQerrorMessage(conn));
PQclear(resPlaces);
exit(EXIT_FAILURE);
}
}
if (iSector > 0)
{
count = 0;
rankPerSecond = 0;
tuples = PQntuples(resPlaces);
if (tuples > 0)
{
// Spawn threads
for (i = 0; i < num_threads; i++)
{
thread_data[i].res = resPlaces;
thread_data[i].tuples = tuples;
thread_data[i].count = &count;
thread_data[i].count_mutex = &count_mutex;
thread_data[i].writer = writer;
thread_data[i].writer_mutex = &writer_mutex;
pthread_create(&thread_data[i].thread, NULL, &nominatim_indexThread, (void *)&thread_data[i]);
}
// Monitor threads to give user feedback
sleepcount = 0;
while (count < tuples)
{
usleep(1000);
// Aim for one update per second
if (sleepcount++ > 500)
{
rankPerSecond = ((float)rankCountTuples + (float)count) / MAX(difftime(time(0), rankStartTime),1);
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;
}
}
// Wait for everything to finish
for (i = 0; i < num_threads; i++)
{
pthread_join(thread_data[i].thread, NULL);
}
rankCountTuples += tuples;
}
// Finished sector
rankPerSecond = (float)rankCountTuples / MAX(difftime(time(0), rankStartTime),1);
fprintf(stderr, " Done %i in %i @ %f per second - ETA (seconds): %f\n", rankCountTuples, (int)(difftime(time(0), rankStartTime)), rankPerSecond, ((float)(rankTotalTuples - rankCountTuples))/rankPerSecond);
PQclear(resPlaces);
}
if (rankTotalTuples-rankCountTuples < num_threads*20 && iSector < PQntuples(resSectors))
{
iSector = PQntuples(resSectors) - 1;
}
}
// Finished rank
fprintf(stderr, "\r Done %i in %i @ %f per second - FINISHED \n\n", rankCountTuples, (int)(difftime(time(0), rankStartTime)), rankPerSecond);
PQclear(resSectors);
}
// Close all connections
for (i = 0; i < num_threads; i++)
{
PQfinish(thread_data[i].conn);
}
PQfinish(conn);
if (writer)
{
nominatim_exportXMLEnd(writer);
}
// Close all connections
for (i = 0; i < num_threads; i++)
{
PQfinish(thread_data[i].conn);
}
PQfinish(conn);
}
void *nominatim_indexThread(void * thread_data_in)
@@ -453,15 +324,12 @@ void *nominatim_indexThread(void * thread_data_in)
PGresult *res;
const char *paramValues[1];
const char *paramValues[1];
int paramLengths[1];
int paramFormats[1];
uint64_t paramPlaceID;
uint64_t place_id;
time_t updateStartTime;
unsigned table;
table = thread_data->table;
uint64_t place_id;
time_t updateStartTime;
while (1)
{
@@ -480,58 +348,37 @@ void *nominatim_indexThread(void * thread_data_in)
if (verbose) fprintf(stderr, " Processing place_id %ld\n", place_id);
updateStartTime = time(0);
int done = 0;
int done = 0;
if (thread_data->writer)
{
nominatim_exportPlaceQueries(place_id, thread_data->conn, &querySet);
}
while(!done)
{
paramPlaceID = PGint64(place_id);
paramValues[0] = (char *)&paramPlaceID;
paramLengths[0] = sizeof(paramPlaceID);
paramFormats[0] = 1;
if (table == 1) // table=1 for placex
{
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)
done = 1;
else
{
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);
}
else
{
fprintf(stderr, "index_osmline: UPDATE failed - deadlock, retrying (%ld)\n", place_id);
}
PQclear(res);
sleep(rand() % 10);
}
else
{
if (table == 1)
{
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);
exit(EXIT_FAILURE);
}
}
while(!done)
{
paramPlaceID = PGint64(place_id);
paramValues[0] = (char *)&paramPlaceID;
paramLengths[0] = sizeof(paramPlaceID);
paramFormats[0] = 1;
res = PQexecPrepared(thread_data->conn, "index_placex", 1, paramValues, paramLengths, paramFormats, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
done = 1;
else
{
if (!strncmp(PQerrorMessage(thread_data->conn), "ERROR: deadlock detected", 25))
{
fprintf(stderr, "index_placex: UPDATE failed - deadlock, retrying (%ld)\n", place_id);
PQclear(res);
sleep(rand() % 10);
}
else
{
fprintf(stderr, "index_placex: UPDATE failed: %s", PQerrorMessage(thread_data->conn));
PQclear(res);
exit(EXIT_FAILURE);
}
}
}
PQclear(res);
if (difftime(time(0), updateStartTime) > 1) fprintf(stderr, " Slow place_id %ld\n", place_id);

View File

@@ -14,7 +14,6 @@ struct index_thread_data
pthread_mutex_t * count_mutex;
xmlTextWriterPtr writer;
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_indexThread(void * thread_data_in);

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