project(nominatim)
set(NOMINATIM_VERSION_MAJOR 3)
-set(NOMINATIM_VERSION_MINOR 3)
+set(NOMINATIM_VERSION_MINOR 4)
set(NOMINATIM_VERSION_PATCH 0)
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
+3.4.0
+
+ * increase required version for PostgreSQL(9.3), PostGIS(2.2) and PHP(7.0)
+ * better error reporting for out-of-memory errors
+ * exclude postcode ranges separated by colon from centre point calculation
+ * update osm2pgsql, better handling of imports without flatnode file
+ * switch to more efficient algorithm for word set computation
+ * use only boundries for country and state parts of addresses
+ * improve updates of addresses with housenumbers and interpolations
+ * remove country from place_addressline table and use country_code instead
+ * optimise indexes on search_name partition tables
+ * improve searching of attached streets for large objects like airports
+ * drop support for python 2
+ * new scripts for importing Wikidata for importance
+ * create and drop indexes concurrently to not clash with auto vacuum
+ * various documentation improvements
+
+
3.3.0
* zoom 17 in reverse now zooms in on minor streets
Wikidata
---
-This script downloads and processes Wikidata to enrich the previously created Wekipedia tables for use in Nominatim.
+This script downloads and processes Wikidata to enrich the previously created Wikipedia tables for use in Nominatim.
#### Import & Process Wikidata
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_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/styles.css ${CMAKE_CURRENT_BINARY_DIR}/styles.css
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/data-sources/overview.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/overview.md
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/us-tiger/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/US-Tiger.md
COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_SOURCE_DIR}/data-sources/gb-postcodes/README.md ${CMAKE_CURRENT_BINARY_DIR}/data-sources/GB-Postcodes.md
SQL statements should be executed from the PostgreSQL commandline. Execute
`psql nominatim` to enter command line mode.
-## 3.3.0 -> master
+## 3.3.0 -> 3.4.0
### Reorganisation of location_area_country table
* `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.
+are accepted in any order as long as they span a real box. `x` is longitude,
+`y` is latitude.
* `bounded=[0|1]`
# Documentation Pages
-The [Nominatim documentation](https://nominatim.org/release-docs/develop/) is built using the [MkDocs](https://www.mkdocs.org/) static site generation framework. The master branch is automatically deployed every night on under [https://nominatim.org/release-docs/develop/]()
+The [Nominatim documentation](https://nominatim.org/release-docs/develop/) is built using the [MkDocs](https://www.mkdocs.org/) static site generation framework. The master branch is automatically deployed every night on under [https://nominatim.org/release-docs/develop/](https://nominatim.org/release-docs/develop/)
-To preview local changes:
+To preview local changes, first install MkDocs
-1. Install MkDocs
+```
+pip3 install --user mkdocs
+```
- ```
- pip3 install --user mkdocs
- ```
+Then go to the build directory and run
-2. In build directory run
+```
+make doc
+INFO - Cleaning site directory
+INFO - Building documentation to directory: /home/vagrant/build/site-html
+```
- ```
- make doc
- INFO - Cleaning site directory
- INFO - Building documentation to directory: /home/vagrant/build/site-html
- ```
+This runs `mkdocs build` plus extra transformation of some files and adds
+symlinks (see `CMakeLists.txt` for the exact steps).
- This runs `mkdocs build` plus extra transformion of some files and adds symlinks (see `CMakeLists.txt` for the exact steps).
+Now you can start webserver for local testing
+```
+build> mkdocs serve
+[server:296] Serving on http://127.0.0.1:8000
+[handlers:62] Start watching changes
+```
-3. Start webserver for local testing
+If you develop inside a Vagrant virtual machine:
- ```
- mkdocs serve
- [server:296] Serving on http://127.0.0.1:8000
- [handlers:62] Start watching changes
- ```
-
- If you develop inside a Vagrant virtual machine:
- * add port forwarding to your Vagrantfile, e.g. `config.vm.network "forwarded_port", guest: 8000, host: 8000`
- * use `mkdocs serve --dev-addr 0.0.0.0:8000` because the default localhost
- IP does not get forwarded.
+ * add port forwarding to your Vagrantfile,
+ e.g. `config.vm.network "forwarded_port", guest: 8000, host: 8000`
+ * use `mkdocs serve --dev-addr 0.0.0.0:8000` because the default localhost
+ IP does not get forwarded.
--- /dev/null
+# Postcodes in Nominatim
+
+The blog post [Nominatim and Postcodes](https://www.openstreetmap.org/user/lonvia/diary/43143)
+describes the handling implemented since Nominatim 3.1.
+
+Postcode centroids (aka 'calculated postcodes') are generated by looking at all
+postcodes of a country, grouping them and calculating the geometric centroid.
+There is currently no logic to deal with extreme outliers (typos or other
+mistakes in OSM data). There is also no check if a postcodes adheres to a
+country's format, e.g. if Swiss postcodes are 4 digits.
+
+
+## Regular updating calculated postcodes
+
+The script to rerun the calculation is
+`build/utils/update.php --calculate-postcodes`
+and runs once per night on nominatim.openstreetmap.org.
+
+
+## Finding places that share a specific postcode
+
+In the Nominatim database run
+
+```sql
+SELECT osm_type, osm_id, class, type,
+ st_x(centroid) as lon, st_y(centroid) at lat
+FROM placex
+WHERE country_code='fr'
+ AND upper(trim (both ' ' from address->'postcode')) = '33210';
+```
+
+Alternatively on [Overpass](https://overpass-turbo.eu/) run the following query
+
+```
+[out:json][timeout:250];
+area["name"="France"]->.boundaryarea;
+(
+nwr(area.boundaryarea)["addr:postcode"="33210"];
+);
+out body;
+>;
+out skel qt;
+```
- 'Overview' : 'develop/overview.md'
- 'OSM Data Import' : 'develop/Import.md'
- 'Place Ranking' : 'develop/Ranking.md'
+ - 'Postcodes' : 'develop/Postcodes.md'
- 'Documentation' : 'develop/Documentation.md'
- 'External Data Sources':
- 'Overview' : 'data-sources/overview.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
+ - codehilite
- toc:
permalink:
-extra_css: [extra.css]
+extra_css: [extra.css, styles.css]
--- /dev/null
+.codehilite .hll { background-color: #ffffcc }
+.codehilite { background: #f0f0f0; }
+.codehilite .c { color: #60a0b0; font-style: italic } /* Comment */
+.codehilite .err { /* border: 1px solid #FF0000 */ } /* Error */
+.codehilite .k { color: #007020; font-weight: bold } /* Keyword */
+.codehilite .o { color: #666666 } /* Operator */
+.codehilite .ch { color: #60a0b0; font-style: italic } /* Comment.Hashbang */
+.codehilite .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */
+.codehilite .cp { color: #007020 } /* Comment.Preproc */
+.codehilite .cpf { color: #60a0b0; font-style: italic } /* Comment.PreprocFile */
+.codehilite .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */
+.codehilite .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */
+.codehilite .gd { color: #A00000 } /* Generic.Deleted */
+.codehilite .ge { font-style: italic } /* Generic.Emph */
+.codehilite .gr { color: #FF0000 } /* Generic.Error */
+.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.codehilite .gi { color: #00A000 } /* Generic.Inserted */
+.codehilite .go { color: #888888 } /* Generic.Output */
+.codehilite .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.codehilite .gs { font-weight: bold } /* Generic.Strong */
+.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.codehilite .gt { color: #0044DD } /* Generic.Traceback */
+.codehilite .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
+.codehilite .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
+.codehilite .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
+.codehilite .kp { color: #007020 } /* Keyword.Pseudo */
+.codehilite .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
+.codehilite .kt { color: #902000 } /* Keyword.Type */
+.codehilite .m { color: #40a070 } /* Literal.Number */
+.codehilite .s { color: #4070a0 } /* Literal.String */
+.codehilite .na { color: #4070a0 } /* Name.Attribute */
+.codehilite .nb { color: #007020 } /* Name.Builtin */
+.codehilite .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
+.codehilite .no { color: #60add5 } /* Name.Constant */
+.codehilite .nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.codehilite .ni { color: #d55537; font-weight: bold } /* Name.Entity */
+.codehilite .ne { color: #007020 } /* Name.Exception */
+.codehilite .nf { color: #06287e } /* Name.Function */
+.codehilite .nl { color: #002070; font-weight: bold } /* Name.Label */
+.codehilite .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.codehilite .nt { color: #062873; font-weight: bold } /* Name.Tag */
+.codehilite .nv { color: #bb60d5 } /* Name.Variable */
+.codehilite .ow { color: #007020; font-weight: bold } /* Operator.Word */
+.codehilite .w { color: #bbbbbb } /* Text.Whitespace */
+.codehilite .mb { color: #40a070 } /* Literal.Number.Bin */
+.codehilite .mf { color: #40a070 } /* Literal.Number.Float */
+.codehilite .mh { color: #40a070 } /* Literal.Number.Hex */
+.codehilite .mi { color: #40a070 } /* Literal.Number.Integer */
+.codehilite .mo { color: #40a070 } /* Literal.Number.Oct */
+.codehilite .sa { color: #4070a0 } /* Literal.String.Affix */
+.codehilite .sb { color: #4070a0 } /* Literal.String.Backtick */
+.codehilite .sc { color: #4070a0 } /* Literal.String.Char */
+.codehilite .dl { color: #4070a0 } /* Literal.String.Delimiter */
+.codehilite .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
+.codehilite .s2 { color: #4070a0 } /* Literal.String.Double */
+.codehilite .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
+.codehilite .sh { color: #4070a0 } /* Literal.String.Heredoc */
+.codehilite .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
+.codehilite .sx { color: #c65d09 } /* Literal.String.Other */
+.codehilite .sr { color: #235388 } /* Literal.String.Regex */
+.codehilite .s1 { color: #4070a0 } /* Literal.String.Single */
+.codehilite .ss { color: #517918 } /* Literal.String.Symbol */
+.codehilite .bp { color: #007020 } /* Name.Builtin.Pseudo */
+.codehilite .fm { color: #06287e } /* Name.Function.Magic */
+.codehilite .vc { color: #bb60d5 } /* Name.Variable.Class */
+.codehilite .vg { color: #bb60d5 } /* Name.Variable.Global */
+.codehilite .vi { color: #bb60d5 } /* Name.Variable.Instance */
+.codehilite .vm { color: #bb60d5 } /* Name.Variable.Magic */
+.codehilite .il { color: #40a070 } /* Literal.Number.Integer.Long */
try {
$stmt = $this->getQueryStatement($sSQL, $aInputVars, $sErrMessage);
- while ($val = $stmt->fetchColumn(0)) { // returns first column or false
+ while (($val = $stmt->fetchColumn(0)) !== false) { // returns first column or false
$aVals[] = $val;
}
} catch (\PDOException $e) {
}
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['short_name:'.$sLanguage] = 'short_name:'.$sLanguage;
}
$aLangPrefOrder['official_name'] = 'official_name';
+ $aLangPrefOrder['short_name'] = 'short_name';
$aLangPrefOrder['ref'] = 'ref';
$aLangPrefOrder['type'] = 'type';
return $aLangPrefOrder;
exit(1);
}
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_name.sql');
- $this->pgsqlRunScriptFile(CONST_BasePath.'/data/country_osm_grid.sql.gz');
+ $this->pgsqlRunScriptFile(CONST_ExtraDataPath.'/country_osm_grid.sql.gz');
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/gb_postcode_table.sql');
$this->pgsqlRunScriptFile(CONST_BasePath.'/data/us_postcode_table.sql');
{
info('Import Tiger data');
+ $aFilenames = glob(CONST_Tiger_Data_Path.'/*.sql');
+ info('Found '.count($aFilenames).' SQL files in path '.CONST_Tiger_Data_Path);
+ if (empty($aFilenames)) return;
+
$sTemplate = file_get_contents(CONST_BasePath.'/sql/tiger_import_start.sql');
$sTemplate = str_replace('{www-user}', CONST_Database_Web_User, $sTemplate);
$sTemplate = $this->replaceTablespace(
pg_ping($aDBInstances[$i]);
}
- foreach (glob(CONST_Tiger_Data_Path.'/*.sql') as $sFile) {
+ foreach ($aFilenames as $sFile) {
echo $sFile.': ';
$hFile = fopen($sFile, 'r');
$sSQL = fgets($hFile, 100000);
}
foreach ($aDropTables as $sDrop) {
if ($this->bVerbose) echo "Dropping table $sDrop\n";
- $this->oDB->exec("DROP TABLE $sDrop CASCADE");
- // ignore warnings/errors as they might be caused by a table having
- // been deleted already by CASCADE
+ $this->oDB->exec("DROP TABLE IF EXISTS $sDrop CASCADE");
}
if (!is_null(CONST_Osm2pgsql_Flatnode_File) && CONST_Osm2pgsql_Flatnode_File) {
--- /dev/null
+<?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/details.css" rel="stylesheet" type="text/css" />
+</head>
+
+
+<body id="details-index-page">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-12">
+
+ <h1>Show details for place</h1>
+
+ <div class="search-form">
+ <h4>Search by place id</h4>
+
+ <form class="form-inline" action="details.php">
+ <input type="edit" class="form-control input-sm" pattern="^[0-9]+$" name="place_id" placeholder="12345" />
+ <input type="submit" class="btn btn-primary btn-sm" value="Show" />
+ </form>
+ </div>
+
+ <div class="search-form">
+ <h4>Search by OSM type and OSM id</h4>
+
+ <form id="form-by-type-and-id" class="form-inline" action="details.php">
+ <input type="edit" class="form-control input-sm" pattern="^[NWR][0-9]+$" placeholder="N123 or W123 or R123" />
+ <input type="hidden" name="osmtype" />
+ <input type="hidden" name="osmid" />
+ <input type="submit" class="btn btn-primary btn-sm" value="Show" />
+ </form>
+ </div>
+
+ <div class="search-form">
+ <h4>Search by openstreetmap.org URL</h4>
+
+ <form id="form-by-osm-url" class="form-inline" action="details.php">
+ <input type="edit" class="form-control input-sm" pattern=".*openstreetmap.*" placeholder="https://www.openstreetmap.org/relation/123" />
+ <input type="hidden" name="osmtype" />
+ <input type="hidden" name="osmid" />
+ <input type="submit" class="btn btn-primary btn-sm" value="Show" />
+ </form>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+
+ <?php include(CONST_BasePath.'/lib/template/includes/html-footer.php'); ?>
+</body>
+</html>
-Subproject commit 90e17f0e8c793487ca39cbf95501cd9c5daa33e2
+Subproject commit 8d9087f1111f4a062158e8e6b10bfbceed90899b
"islet" : [20, 0],
"mountain_pass" : [20, 0],
"neighbourhood" : 22,
+ "quarter" : 22,
+ "city_block" : 22,
"houses" : [28, 0]
},
"boundary" : {
-- postcode table
IF for_place_id IS NULL THEN
- SELECT parent_place_id, country_code, rank_address, postcode, 'place', 'postcode'
+ SELECT parent_place_id, country_code, rank_search, postcode, 'place', 'postcode'
FROM location_postcode
WHERE place_id = in_place_id
INTO for_place_id, searchcountrycode, searchrankaddress, searchpostcode,
-- Indices used only during search and update.
-- These indices are created only after the indexing process is done.
-CREATE INDEX idx_word_word_id on word USING BTREE (word_id) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_word_word_id on word USING BTREE (word_id) {ts:search-index};
-CREATE INDEX idx_place_addressline_address_place_id on place_addressline USING BTREE (address_place_id) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_place_addressline_address_place_id on place_addressline USING BTREE (address_place_id) {ts:search-index};
-DROP INDEX IF EXISTS idx_placex_rank_search;
-CREATE INDEX idx_placex_rank_search ON placex USING BTREE (rank_search) {ts:search-index};
-CREATE INDEX idx_placex_rank_address ON placex USING BTREE (rank_address) {ts:search-index};
-CREATE INDEX idx_placex_pendingsector ON placex USING BTREE (rank_search,geometry_sector) {ts:address-index} where indexed_status > 0;
-CREATE INDEX idx_placex_parent_place_id ON placex USING BTREE (parent_place_id) {ts:search-index} where parent_place_id IS NOT NULL;
+DROP INDEX CONCURRENTLY IF EXISTS idx_placex_rank_search;
+CREATE INDEX CONCURRENTLY idx_placex_rank_search ON placex USING BTREE (rank_search) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_placex_rank_address ON placex USING BTREE (rank_address) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_placex_pendingsector ON placex USING BTREE (rank_search,geometry_sector) {ts:address-index} where indexed_status > 0;
+CREATE INDEX CONCURRENTLY idx_placex_parent_place_id ON placex USING BTREE (parent_place_id) {ts:search-index} where parent_place_id IS NOT NULL;
-CREATE INDEX idx_placex_geometry_reverse_lookupPoint
+CREATE INDEX CONCURRENTLY 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
+CREATE INDEX CONCURRENTLY 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
+CREATE INDEX CONCURRENTLY 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'
GRANT SELECT ON table country_osm_grid to "{www-user}";
-CREATE INDEX idx_location_area_country_place_id ON location_area_country USING BTREE (place_id) {ts:address-index};
+CREATE INDEX CONCURRENTLY idx_location_area_country_place_id ON location_area_country USING BTREE (place_id) {ts:address-index};
-CREATE INDEX idx_osmline_parent_place_id ON location_property_osmline USING BTREE (parent_place_id) {ts:search-index};
-CREATE INDEX idx_osmline_parent_osm_id ON location_property_osmline USING BTREE (osm_id) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_osmline_parent_place_id ON location_property_osmline USING BTREE (parent_place_id) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_osmline_parent_osm_id ON location_property_osmline USING BTREE (osm_id) {ts:search-index};
-DROP INDEX IF EXISTS place_id_idx;
-CREATE UNIQUE INDEX idx_place_osm_unique on place using btree(osm_id,osm_type,class,type) {ts:address-index};
+DROP INDEX CONCURRENTLY IF EXISTS place_id_idx;
+CREATE UNIQUE INDEX CONCURRENTLY idx_place_osm_unique on place using btree(osm_id,osm_type,class,type) {ts:address-index};
-CREATE UNIQUE INDEX idx_postcode_id ON location_postcode USING BTREE (place_id) {ts:search-index};
-CREATE INDEX idx_postcode_postcode ON location_postcode USING BTREE (postcode) {ts:search-index};
+CREATE UNIQUE INDEX CONCURRENTLY idx_postcode_id ON location_postcode USING BTREE (place_id) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_postcode_postcode ON location_postcode USING BTREE (postcode) {ts:search-index};
-- Indices used for /search API.
-- These indices are created only after the indexing process is done.
-CREATE INDEX idx_search_name_nameaddress_vector ON search_name USING GIN (nameaddress_vector) WITH (fastupdate = off) {ts:search-index};
-CREATE INDEX idx_search_name_name_vector ON search_name USING GIN (name_vector) WITH (fastupdate = off) {ts:search-index};
-CREATE INDEX idx_search_name_centroid ON search_name USING GIST (centroid) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_search_name_nameaddress_vector ON search_name USING GIN (nameaddress_vector) WITH (fastupdate = off) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_search_name_name_vector ON search_name USING GIN (name_vector) WITH (fastupdate = off) {ts:search-index};
+CREATE INDEX CONCURRENTLY idx_search_name_centroid ON search_name USING GIST (centroid) {ts:search-index};
{
$oParams = new ParameterParser(array('accept-language' => ''));
$this->assertSame(array(
- 'short_name:default' => 'short_name:default',
'name:default' => 'name:default',
- 'short_name' => 'short_name',
'name' => 'name',
'brand' => 'brand',
'official_name:default' => 'official_name:default',
+ 'short_name:default' => 'short_name:default',
'official_name' => 'official_name',
+ 'short_name' => 'short_name',
'ref' => 'ref',
'type' => 'type'
), $oParams->getPreferredLanguages('default'));
$oParams = new ParameterParser(array('accept-language' => 'de,en'));
$this->assertSame(array(
- 'short_name:de' => 'short_name:de',
'name:de' => 'name:de',
- 'short_name:en' => 'short_name:en',
'name:en' => 'name:en',
- 'short_name' => 'short_name',
'name' => 'name',
'brand' => 'brand',
'official_name:de' => 'official_name:de',
+ 'short_name:de' => 'short_name:de',
'official_name:en' => 'official_name:en',
+ 'short_name:en' => 'short_name:en',
'official_name' => 'official_name',
+ 'short_name' => 'short_name',
'ref' => 'ref',
'type' => 'type'
), $oParams->getPreferredLanguages('default'));
$oParams = new ParameterParser(array('accept-language' => 'fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3'));
$this->assertSame(array(
- 'short_name:fr-ca' => 'short_name:fr-ca',
'name:fr-ca' => 'name:fr-ca',
- 'short_name:fr' => 'short_name:fr',
'name:fr' => 'name:fr',
- 'short_name:en-ca' => 'short_name:en-ca',
'name:en-ca' => 'name:en-ca',
- 'short_name:en' => 'short_name:en',
'name:en' => 'name:en',
- 'short_name' => 'short_name',
'name' => 'name',
'brand' => 'brand',
'official_name:fr-ca' => 'official_name:fr-ca',
+ 'short_name:fr-ca' => 'short_name:fr-ca',
'official_name:fr' => 'official_name:fr',
+ 'short_name:fr' => 'short_name:fr',
'official_name:en-ca' => 'official_name:en-ca',
+ 'short_name:en-ca' => 'short_name:en-ca',
'official_name:en' => 'official_name:en',
+ 'short_name:en' => 'short_name:en',
'official_name' => 'official_name',
+ 'short_name' => 'short_name',
'ref' => 'ref',
'type' => 'type',
), $oParams->getPreferredLanguages('default'));
$oParams = new ParameterParser(array('accept-language' => 'ja_rm,zh_pinyin'));
$this->assertSame(array(
- 'short_name:ja_rm' => 'short_name:ja_rm',
'name:ja_rm' => 'name:ja_rm',
- 'short_name:zh_pinyin' => 'short_name:zh_pinyin',
'name:zh_pinyin' => 'name:zh_pinyin',
- 'short_name:ja' => 'short_name:ja',
'name:ja' => 'name:ja',
- 'short_name:zh' => 'short_name:zh',
'name:zh' => 'name:zh',
- 'short_name' => 'short_name',
'name' => 'name',
'brand' => 'brand',
'official_name:ja_rm' => 'official_name:ja_rm',
+ 'short_name:ja_rm' => 'short_name:ja_rm',
'official_name:zh_pinyin' => 'official_name:zh_pinyin',
+ 'short_name:zh_pinyin' => 'short_name:zh_pinyin',
'official_name:ja' => 'official_name:ja',
+ 'short_name:ja' => 'short_name:ja',
'official_name:zh' => 'official_name:zh',
+ 'short_name:zh' => 'short_name:zh',
'official_name' => 'official_name',
+ 'short_name' => 'short_name',
'ref' => 'ref',
'type' => 'type',
), $oParams->getPreferredLanguages('default'));
foreach (explode(',', $sLanguageIn) as $sLanguage) {
$sURL = 'https://wiki.openstreetmap.org/wiki/Special:Export/Nominatim/Special_Phrases/'.strtoupper($sLanguage);
$sWikiPageXML = file_get_contents($sURL);
- if (preg_match_all('#\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([\\-YN])#', $sWikiPageXML, $aMatches, PREG_SET_ORDER)) {
- foreach ($aMatches as $aMatch) {
- $sLabel = trim($aMatch[1]);
- if ($oNormalizer !== null) {
- $sTrans = pg_escape_string($oNormalizer->transliterate($sLabel));
- } else {
- $sTrans = null;
- }
- $sClass = trim($aMatch[2]);
- $sType = trim($aMatch[3]);
- // hack around a bug where building=yes was imported with
- // quotes into the wiki
- $sType = preg_replace('/"/', '', $sType);
- // sanity check, in case somebody added garbage in the wiki
- if (preg_match('/^\\w+$/', $sClass) < 1
- || preg_match('/^\\w+$/', $sType) < 1
- ) {
- trigger_error("Bad class/type for language $sLanguage: $sClass=$sType");
- exit;
- }
- // blacklisting: disallow certain class/type combinations
- if (isset($aTagsBlacklist[$sClass]) && in_array($sType, $aTagsBlacklist[$sClass])) {
- // fwrite(STDERR, "Blacklisted: ".$sClass."/".$sType."\n");
- continue;
- }
- // whitelisting: if class is in whitelist, allow only tags in the list
- if (isset($aTagsWhitelist[$sClass]) && !in_array($sType, $aTagsWhitelist[$sClass])) {
- // fwrite(STDERR, "Non-Whitelisted: ".$sClass."/".$sType."\n");
- continue;
- }
- $aPairs[$sClass.'|'.$sType] = array($sClass, $sType);
-
- switch (trim($aMatch[4])) {
- case 'near':
- echo "select getorcreate_amenityoperator(make_standard_name('".pg_escape_string($sLabel)."'), '$sTrans', '$sClass', '$sType', 'near');\n";
- break;
- case 'in':
- echo "select getorcreate_amenityoperator(make_standard_name('".pg_escape_string($sLabel)."'), '$sTrans', '$sClass', '$sType', 'in');\n";
- break;
- default:
- echo "select getorcreate_amenity(make_standard_name('".pg_escape_string($sLabel)."'), '$sTrans', '$sClass', '$sType');\n";
- break;
- }
+
+ if (!preg_match_all(
+ '#\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([\\-YN])#',
+ $sWikiPageXML,
+ $aMatches,
+ PREG_SET_ORDER
+ )) {
+ continue;
+ }
+
+ foreach ($aMatches as $aMatch) {
+ $sLabel = trim($aMatch[1]);
+ if ($oNormalizer !== null) {
+ $sTrans = pg_escape_string($oNormalizer->transliterate($sLabel));
+ } else {
+ $sTrans = null;
+ }
+ $sClass = trim($aMatch[2]);
+ $sType = trim($aMatch[3]);
+ // hack around a bug where building=yes was imported with
+ // quotes into the wiki
+ $sType = preg_replace('/("|")/', '', $sType);
+ // sanity check, in case somebody added garbage in the wiki
+ if (preg_match('/^\\w+$/', $sClass) < 1
+ || preg_match('/^\\w+$/', $sType) < 1
+ ) {
+ trigger_error("Bad class/type for language $sLanguage: $sClass=$sType");
+ exit;
+ }
+ // blacklisting: disallow certain class/type combinations
+ if (isset($aTagsBlacklist[$sClass]) && in_array($sType, $aTagsBlacklist[$sClass])) {
+ // fwrite(STDERR, "Blacklisted: ".$sClass."/".$sType."\n");
+ continue;
+ }
+ // whitelisting: if class is in whitelist, allow only tags in the list
+ if (isset($aTagsWhitelist[$sClass]) && !in_array($sType, $aTagsWhitelist[$sClass])) {
+ // fwrite(STDERR, "Non-Whitelisted: ".$sClass."/".$sType."\n");
+ continue;
+ }
+ $aPairs[$sClass.'|'.$sType] = array($sClass, $sType);
+
+ switch (trim($aMatch[4])) {
+ case 'near':
+ printf(
+ "SELECT getorcreate_amenityoperator(make_standard_name('%s'), '%s', '%s', '%s', 'near');\n",
+ pg_escape_string($sLabel),
+ $sTrans,
+ $sClass,
+ $sType
+ );
+ break;
+ case 'in':
+ printf(
+ "SELECT getorcreate_amenityoperator(make_standard_name('%s'), '%s', '%s', '%s', 'in');\n",
+ pg_escape_string($sLabel),
+ $sTrans,
+ $sClass,
+ $sType
+ );
+ break;
+ default:
+ printf(
+ "SELECT getorcreate_amenity(make_standard_name('%s'), '%s', '%s', '%s');\n",
+ pg_escape_string($sLabel),
+ $sTrans,
+ $sClass,
+ $sType
+ );
+ break;
}
}
}
- echo 'create index idx_placex_classtype on placex (class, type);';
+ echo 'CREATE INDEX idx_placex_classtype ON placex (class, type);';
foreach ($aPairs as $aPair) {
- echo 'create table place_classtype_'.pg_escape_string($aPair[0]).'_'.pg_escape_string($aPair[1]);
- if (CONST_Tablespace_Aux_Data)
- echo ' tablespace '.CONST_Tablespace_Aux_Data;
- echo ' as select place_id as place_id,st_centroid(geometry) as centroid from placex where ';
- echo "class = '".pg_escape_string($aPair[0])."' and type = '".pg_escape_string($aPair[1])."'";
- echo ";\n";
-
- echo 'CREATE INDEX idx_place_classtype_'.pg_escape_string($aPair[0]).'_'.pg_escape_string($aPair[1]).'_centroid ';
- echo 'ON place_classtype_'.pg_escape_string($aPair[0]).'_'.pg_escape_string($aPair[1]).' USING GIST (centroid)';
- if (CONST_Tablespace_Aux_Index)
- echo ' tablespace '.CONST_Tablespace_Aux_Index;
- echo ";\n";
-
- echo 'CREATE INDEX idx_place_classtype_'.pg_escape_string($aPair[0]).'_'.pg_escape_string($aPair[1]).'_place_id ';
- echo 'ON place_classtype_'.pg_escape_string($aPair[0]).'_'.pg_escape_string($aPair[1]).' USING btree(place_id)';
- if (CONST_Tablespace_Aux_Index)
- echo ' tablespace '.CONST_Tablespace_Aux_Index;
- echo ";\n";
-
- echo 'GRANT SELECT ON place_classtype_'.pg_escape_string($aPair[0]).'_'.pg_escape_string($aPair[1]).' TO "'.CONST_Database_Web_User."\";\n";
+ $sql_tablespace = CONST_Tablespace_Aux_Data ? ' TABLESPACE '.CONST_Tablespace_Aux_Data : '';
+
+ printf(
+ 'CREATE TABLE place_classtype_%s_%s'
+ . $sql_tablespace
+ . ' AS'
+ . ' SELECT place_id AS place_id,st_centroid(geometry) AS centroid FROM placex'
+ . " WHERE class = '%s' AND type = '%s'"
+ . ";\n",
+ pg_escape_string($aPair[0]),
+ pg_escape_string($aPair[1]),
+ pg_escape_string($aPair[0]),
+ pg_escape_string($aPair[1])
+ );
+
+ printf(
+ 'CREATE INDEX idx_place_classtype_%s_%s_centroid'
+ . ' ON place_classtype_%s_%s USING GIST (centroid)'
+ . $sql_tablespace
+ . ";\n",
+ pg_escape_string($aPair[0]),
+ pg_escape_string($aPair[1]),
+ pg_escape_string($aPair[0]),
+ pg_escape_string($aPair[1])
+ );
+
+ printf(
+ 'CREATE INDEX idx_place_classtype_%s_%s_place_id'
+ . ' ON place_classtype_%s_%s USING btree(place_id)'
+ . $sql_tablespace
+ . ";\n",
+ pg_escape_string($aPair[0]),
+ pg_escape_string($aPair[1]),
+ pg_escape_string($aPair[0]),
+ pg_escape_string($aPair[1])
+ );
+
+ printf(
+ 'GRANT SELECT ON place_classtype_%s_%s TO "%s"'
+ . ";\n",
+ pg_escape_string($aPair[0]),
+ pg_escape_string($aPair[1]),
+ CONST_Database_Web_User
+ );
}
- echo 'drop index idx_placex_classtype;';
+ echo 'DROP INDEX idx_placex_classtype;';
}
sudo yum install -y epel-release
+# More repositories for postgresql 11 (CentOS default 'postgresql' is 9.2), postgis
+# and llvm-toolset (https://github.com/theory/pg-semver/issues/35)
+
+ sudo yum install -y https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
+ sudo yum install -y centos-release-scl-rh
+
# Now you can install all packages needed for Nominatim:
#DOCS: :::sh
- sudo yum install -y postgresql-server postgresql-contrib postgresql-devel \
- postgis postgis-utils \
+
+ sudo yum install -y postgresql11-server postgresql11-contrib postgresql11-devel \
+ postgis25_11 postgis25_11-utils \
wget git cmake make gcc gcc-c++ libtool policycoreutils-python \
+ devtoolset-7 llvm-toolset-7 \
php-pgsql php php-intl libpqxx-devel \
proj-epsg bzip2-devel proj-devel libxml2-devel boost-devel \
expat-devel zlib-devel
# CentOS does not automatically create a database cluster. Therefore, start
# with initializing the database, then enable the server to start at boot:
- sudo postgresql-setup initdb
- sudo systemctl enable postgresql
+ echo 'PATH=$PATH:/usr/pgsql-11/bin' > .bash_profile
+ source .bash_profile
+
+ sudo /usr/pgsql-11/bin/postgresql-11-setup initdb
+ sudo systemctl enable postgresql-11
#
# Next tune the postgresql configuration, which is located in
#
# Now start the postgresql service after updating this config file.
- sudo systemctl restart postgresql
+ sudo systemctl restart postgresql-11
#
# Finally, we need to add two postgres users: one for the user that does
# download the country grid:
if [ ! -f data/country_osm_grid.sql.gz ]; then #DOCS: :::sh
- wget -O data/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz
+ wget --no-verbose -O data/country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz
fi #DOCS:
# The code must be built in a separate directory. Create this directory,
margin: 10px 0;
}
+#details-index-page .search-form {
+ padding: 20px 10px;
+ margin: 2em 0;
+}
+#details-index-page .search-form h4 {
+ margin-top: 0;
+}
+#details-index-page .search-form .form-control{
+ width: 30em;
+}
+
footer {
text-align: center;
padding: 2em 0;
$sLanguagePrefArraySQL = $oDB->getArraySQL($oDB->getDBQuotedList($aLangPrefOrder));
+if ($sOutputFormat == 'html' && !$sPlaceId && !$sOsmType) {
+ include(CONST_BasePath.'/lib/template/details-index-html.php');
+ exit;
+}
+
if ($sOsmType && $iOsmId > 0) {
$sSQL = 'SELECT place_id FROM placex WHERE osm_type = :type AND osm_id = :id';
// osm_type and osm_id are not unique enough
});
+jQuery(document).ready(function(){
+
+ if ( !$('#details-index-page').length ){ return; }
+
+ $('#form-by-type-and-id,#form-by-osm-url').on('submit', function(e){
+ e.preventDefault();
+
+ var val = $(this).find('input[type=edit]').val();
+ var matches = val.match(/^\s*([NWR])(\d+)\s*$/i);
+
+ if (!matches) {
+ matches = val.match(/\/(relation|way|node)\/(\d+)\s*$/);
+ }
+
+ if (matches) {
+ $(this).find('input[name=osmtype]').val(matches[1].charAt(0).toUpperCase());
+ $(this).find('input[name=osmid]').val(matches[2]);
+ $(this).get(0).submit();
+ } else {
+ alert('invalid input');
+ }
+ });
+});
+
jQuery(document).ready(function(){
if ( !$('#details-page').length ){ return; }
if (!$oGeocode->getQueryString()
&& isset($_SERVER['PATH_INFO'])
+ && strlen($_SERVER['PATH_INFO']) > 0
&& $_SERVER['PATH_INFO'][0] == '/'
) {
$sQuery = substr(rawurldecode($_SERVER['PATH_INFO']), 1);