Compare commits

..

10 Commits

Author SHA1 Message Date
Sarah Hoffmann
50c3890985 adapt to release 4.3.4 2023-11-17 17:06:43 +01:00
Sarah Hoffmann
f145b466b9 add hint about migrating to 4.2.3 2023-04-24 11:37:17 +02:00
Sarah Hoffmann
928e56f668 adapt to release 4.2.3 2023-04-11 16:27:55 +02:00
Sarah Hoffmann
bfa5f44bf1 adapt to release 4.2.2 2023-03-22 21:00:37 +01:00
Sarah Hoffmann
3757b9f04a use canonical url for nominatim.org 2023-03-22 20:58:45 +01:00
Frederik Ramm
debcf9d54e Fix typo in NOMINATIM_LOG_FILE (#2919)
* fix typo in docs (NOMINATIM_LOG_FILE uses s not ms)
2023-03-22 20:57:30 +01:00
Sarah Hoffmann
738603ad66 add FAQ about finding bad postcodes 2023-03-22 20:57:22 +01:00
Sarah Hoffmann
f8a055b366 adapt to release 4.2.1 2023-02-20 20:34:47 +01:00
Sarah Hoffmann
d71b07d19e fix internal links 2023-02-04 21:11:49 +01:00
Sarah Hoffmann
64eaa6e272 adapt to 4.2.0 release 2022-11-24 10:58:56 +01:00
45 changed files with 334 additions and 958 deletions

View File

@@ -15,9 +15,7 @@ runs:
- name: Remove existing PostgreSQL - name: Remove existing PostgreSQL
run: | run: |
sudo apt-get purge -yq postgresql* sudo apt-get purge -yq postgresql*
sudo apt install curl ca-certificates gnupg sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/apt.postgresql.org.gpg >/dev/null
sudo sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
sudo apt-get update -qq sudo apt-get update -qq
shell: bash shell: bash

View File

@@ -37,8 +37,13 @@ jobs:
needs: create-archive needs: create-archive
strategy: strategy:
matrix: matrix:
ubuntu: [20, 22] ubuntu: [18, 20, 22]
include: include:
- ubuntu: 18
postgresql: 9.6
postgis: 2.5
pytest: pytest
php: 7.2
- ubuntu: 20 - ubuntu: 20
postgresql: 13 postgresql: 13
postgis: 3 postgis: 3
@@ -64,10 +69,8 @@ jobs:
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
tools: phpunit:9, phpcs, composer tools: phpunit, phpcs, composer
ini-values: opcache.jit=disable ini-values: opcache.jit=disable
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4
with: with:
@@ -97,22 +100,18 @@ jobs:
- name: Install latest pylint/mypy - name: Install latest pylint/mypy
run: pip3 install -U pylint mypy types-PyYAML types-jinja2 types-psycopg2 types-psutil types-requests typing-extensions run: pip3 install -U pylint mypy types-PyYAML types-jinja2 types-psycopg2 types-psutil types-requests typing-extensions
if: matrix.ubuntu == 22
- name: PHP linting - name: PHP linting
run: phpcs --report-width=120 . run: phpcs --report-width=120 .
working-directory: Nominatim working-directory: Nominatim
if: matrix.ubuntu == 22
- name: Python linting - name: Python linting
run: pylint nominatim run: pylint nominatim
working-directory: Nominatim working-directory: Nominatim
if: matrix.ubuntu == 22
- name: Python static typechecking - name: Python static typechecking
run: mypy --strict nominatim run: mypy --strict nominatim
working-directory: Nominatim working-directory: Nominatim
if: matrix.ubuntu == 22
- name: PHP unit tests - name: PHP unit tests

View File

@@ -13,6 +13,6 @@ ignored-classes=NominatimArgs,closing
# 'too-many-ancestors' is triggered already by deriving from UserDict # 'too-many-ancestors' is triggered already by deriving from UserDict
# 'not-context-manager' disabled because it causes false positives once # 'not-context-manager' disabled because it causes false positives once
# typed Python is enabled. See also https://github.com/PyCQA/pylint/issues/5273 # typed Python is enabled. See also https://github.com/PyCQA/pylint/issues/5273
disable=too-few-public-methods,duplicate-code,too-many-ancestors,bad-option-value,no-self-use,not-context-manager,use-dict-literal disable=too-few-public-methods,duplicate-code,too-many-ancestors,bad-option-value,no-self-use,not-context-manager
good-names=i,x,y,m,fd,db,cc good-names=i,x,y,m,fd,db,cc

View File

@@ -20,7 +20,7 @@ project(nominatim)
set(NOMINATIM_VERSION_MAJOR 4) set(NOMINATIM_VERSION_MAJOR 4)
set(NOMINATIM_VERSION_MINOR 2) set(NOMINATIM_VERSION_MINOR 2)
set(NOMINATIM_VERSION_PATCH 4) set(NOMINATIM_VERSION_PATCH 0)
set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}") set(NOMINATIM_VERSION "${NOMINATIM_VERSION_MAJOR}.${NOMINATIM_VERSION_MINOR}.${NOMINATIM_VERSION_PATCH}")
@@ -269,12 +269,6 @@ install(FILES settings/env.defaults
settings/import-address.style settings/import-address.style
settings/import-full.style settings/import-full.style
settings/import-extratags.style settings/import-extratags.style
settings/import-admin.lua
settings/import-street.lua
settings/import-address.lua
settings/import-full.lua
settings/import-extratags.lua
settings/flex-base.lua
settings/icu_tokenizer.yaml settings/icu_tokenizer.yaml
settings/country_settings.yaml settings/country_settings.yaml
DESTINATION ${NOMINATIM_CONFIGDIR}) DESTINATION ${NOMINATIM_CONFIGDIR})

View File

@@ -1,33 +1,3 @@
4.2.4
* fix a potential SQL injection in 'nominatim admin --collect-os-info'
* fix compatibility issue with PostGIS 3.4
4.2.3
* fix deletion handling for 'nominatim add-data'
* adapt place_force_delete() to new deletion handling
* flex style: avoid dropping of postcode areas
* fix update errors on address interpolation handling
4.2.2
* extend flex-style library to fully support all default styles
* fix handling of Hebrew aleph
* do not assign postcodes to rivers
* fix string matching in PHP code
* update osm2pgsql (various updates to flex)
* fix slow query when deleting places on update
* fix CLI details query
* fix recalculation of importance values
* fix polygon simplification in reverse results
* add class/type information to reverse geocodejson result
* minor improvements to default tokenizer configuration
* various smaller fixes to documentation
4.2.1
* fix XSS vulnerability in debug view
4.2.0 4.2.0
* add experimental support for osm2pgsql flex style * add experimental support for osm2pgsql flex style
@@ -51,10 +21,6 @@
* typing fixes to work with latest type annotations from typeshed * typing fixes to work with latest type annotations from typeshed
* smaller improvements to documentation (thanks to @mausch) * smaller improvements to documentation (thanks to @mausch)
4.1.1
* fix XSS vulnerability in debug view
4.1.0 4.1.0
* switch to ICU tokenizer as default * switch to ICU tokenizer as default
@@ -91,10 +57,6 @@
* add setup instructions for updates and systemd * add setup instructions for updates and systemd
* drop support for PostgreSQL 9.5 * drop support for PostgreSQL 9.5
4.0.2
* fix XSS vulnerability in debug view
4.0.1 4.0.1
* fix initialisation error in replication script * fix initialisation error in replication script
@@ -133,10 +95,6 @@
* add testing of installation scripts via CI * add testing of installation scripts via CI
* drop support for Python < 3.6 and Postgresql < 9.5 * drop support for Python < 3.6 and Postgresql < 9.5
3.7.3
* fix XSS vulnerability in debug view
3.7.2 3.7.2
* fix database check for reverse-only imports * fix database check for reverse-only imports

View File

@@ -1,6 +1,6 @@
# Install Nominatim in a virtual machine for development and testing # Install Nominatim in a virtual machine for development and testing
This document describes how you can install Nominatim inside a Ubuntu 22 This document describes how you can install Nominatim inside a Ubuntu 16
virtual machine on your desktop/laptop (host machine). The goal is to give virtual machine on your desktop/laptop (host machine). The goal is to give
you a development environment to easily edit code and run the test suite you a development environment to easily edit code and run the test suite
without affecting the rest of your system. without affecting the rest of your system.
@@ -69,7 +69,8 @@ installation.
PHP errors are written to `/var/log/apache2/error.log`. PHP errors are written to `/var/log/apache2/error.log`.
With `echo` and `var_dump()` you write into the output (HTML/XML/JSON) when With `echo` and `var_dump()` you write into the output (HTML/XML/JSON) when
you either add `&debug=1` to the URL. 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 In the Python BDD test you can use `logger.info()` for temporary debug
statements. statements.
@@ -129,10 +130,6 @@ and then
Yes, Vagrant and Virtualbox can be installed on MS Windows just fine. You need a 64bit Yes, Vagrant and Virtualbox can be installed on MS Windows just fine. You need a 64bit
version of Windows. version of Windows.
##### Will it run on Apple Silicon?
You might need to replace Virtualbox with [Parallels](https://www.parallels.com/products/desktop/).
There is no free/open source version of Parallels.
##### Why Monaco, can I use another country? ##### Why Monaco, can I use another country?
@@ -144,12 +141,11 @@ No. Long running Nominatim installations will differ once new import features (o
bug fixes) get added since those usually only get applied to new/changed data. bug fixes) get added since those usually only get applied to new/changed data.
Also this document skips the optional Wikipedia data import which affects ranking Also this document skips the optional Wikipedia data import which affects ranking
of search results. See [Nominatim installation](https://nominatim.org/release-docs/latest/admin/Installation) of search results. See [Nominatim installation](https://nominatim.org/release-docs/latest/admin/Installation) for details.
for details.
##### Why Ubuntu? Can I test CentOS/Fedora/CoreOS/FreeBSD? ##### Why Ubuntu? Can I test CentOS/Fedora/CoreOS/FreeBSD?
There used to be a Vagrant script for CentOS available, but the Nominatim directory 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 isn't symlinked/mounted to the host which makes development trickier. We used
it mainly for debugging installation with SELinux. it mainly for debugging installation with SELinux.
@@ -158,17 +154,14 @@ are slightly different, e.g. the name of the package manager, Apache2 package
name, location of files. We chose Ubuntu because that is closest to the name, location of files. We chose Ubuntu because that is closest to the
nominatim.openstreetmap.org production environment. nominatim.openstreetmap.org production environment.
You can configure/download other Vagrant boxes from You can configure/download other Vagrant boxes from [https://app.vagrantup.com/boxes/search](https://app.vagrantup.com/boxes/search).
[https://app.vagrantup.com/boxes/search](https://app.vagrantup.com/boxes/search).
##### How can I connect to an existing database? ##### How can I connect to an existing database?
Let's say you have a Postgres database named `nominatim_it` on server `your-server.com` 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.
and port `5432`. The Postgres username is `postgres`. You can edit the `.env` in your
project directory and point Nominatim to it.
NOMINATIM_DATABASE_DSN="pgsql:host=your-server.com;port=5432;user=postgres;dbname=nominatim_it
pgsql:host=your-server.com;port=5432;user=postgres;dbname=nominatim_it
No data import or restarting necessary. No data import or restarting necessary.
If the Postgres installation is behind a firewall, you can try If the Postgres installation is behind a firewall, you can try
@@ -176,12 +169,11 @@ If the Postgres installation is behind a firewall, you can try
ssh -L 9999:localhost:5432 your-username@your-server.com ssh -L 9999:localhost:5432 your-username@your-server.com
inside the virtual machine. It will map the port to `localhost:9999` and then inside the virtual machine. It will map the port to `localhost:9999` and then
you edit `.env` file with you edit `settings/local.php` with
NOMINATIM_DATABASE_DSN="pgsql:host=localhost;port=9999;user=postgres;dbname=nominatim_it" @define('CONST_Database_DSN', 'pgsql:host=localhost;port=9999;user=postgres;dbname=nominatim_it');
To access postgres directly remember to specify the hostname, To access postgres directly remember to specify the hostname, e.g. `psql --host localhost --port 9999 nominatim_it`
e.g. `psql --host localhost --port 9999 nominatim_it`
##### My computer is slow and the import takes too long. Can I start the virtual machine "in the cloud"? ##### My computer is slow and the import takes too long. Can I start the virtual machine "in the cloud"?

View File

@@ -15,6 +15,14 @@ breaking changes. **Please read them before running the migration.**
If you are migrating from a version <3.6, then you still have to follow If you are migrating from a version <3.6, then you still have to follow
the manual migration steps up to 3.6. the manual migration steps up to 3.6.
## 4.2.2 -> 4.2.3
### Update interpolation functions
When updating to this release, you need to run `nominatim refresh --functions`
after updating and before restarting updates. Otherwise you may see an error
`Splitting of Point geometries is unsupported` or similar.
## 4.0.0 -> 4.1.0 ## 4.0.0 -> 4.1.0
### ICU tokenizer is the new default ### ICU tokenizer is the new default

View File

@@ -1,7 +1,3 @@
.toctree-l3 {
display: none!important
}
table { table {
margin-bottom: 12pt margin-bottom: 12pt
} }

View File

@@ -1,4 +1,4 @@
site_name: Nominatim Documentation site_name: Nominatim 4.2.4
theme: readthedocs theme: readthedocs
docs_dir: ${CMAKE_CURRENT_BINARY_DIR} docs_dir: ${CMAKE_CURRENT_BINARY_DIR}
site_url: https://nominatim.org site_url: https://nominatim.org

View File

@@ -135,7 +135,7 @@ class Debug
public static function printSQL($sSQL) public static function printSQL($sSQL)
{ {
echo '<p><tt><font color="#aaa">'.htmlspecialchars($sSQL, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401).'</font></tt></p>'."\n"; echo '<p><tt><font color="#aaa">'.$sSQL.'</font></tt></p>'."\n";
} }
private static function outputVar($mVar, $sPreNL) private static function outputVar($mVar, $sPreNL)
@@ -178,12 +178,11 @@ class Debug
} }
if (is_string($mVar)) { if (is_string($mVar)) {
$sOut = "'$mVar'"; echo "'$mVar'";
} else { return strlen($mVar) + 2;
$sOut = (string)$mVar;
} }
echo htmlspecialchars($sOut, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401); echo (string)$mVar;
return strlen($sOut); return strlen((string)$mVar);
} }
} }

View File

@@ -874,7 +874,7 @@ class Geocode
$iCountWords = 0; $iCountWords = 0;
$sAddress = $aResult['langaddress']; $sAddress = $aResult['langaddress'];
foreach ($aRecheckWords as $i => $sWord) { foreach ($aRecheckWords as $i => $sWord) {
if (grapheme_stripos($sAddress, $sWord)!==false) { if (stripos($sAddress, $sWord)!==false) {
$iCountWords++; $iCountWords++;
if (preg_match('/(^|,)\s*'.preg_quote($sWord, '/').'\s*(,|$)/', $sAddress)) { if (preg_match('/(^|,)\s*'.preg_quote($sWord, '/').'\s*(,|$)/', $sAddress)) {
$iCountWords += 0.1; $iCountWords += 0.1;

View File

@@ -524,7 +524,12 @@ class PlaceLookup
// Get the bounding box and outline polygon // Get the bounding box and outline polygon
$sSQL = 'select place_id,0 as numfeatures,st_area(geometry) as area,'; $sSQL = 'select place_id,0 as numfeatures,st_area(geometry) as area,';
$sSQL .= ' ST_Y(centroid) as centrelat, ST_X(centroid) as centrelon,'; if ($fLonReverse != null && $fLatReverse != null) {
$sSQL .= ' ST_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_YMin(geometry) as minlat,ST_YMax(geometry) as maxlat,';
$sSQL .= ' ST_XMin(geometry) as minlon,ST_XMax(geometry) as maxlon'; $sSQL .= ' ST_XMin(geometry) as minlon,ST_XMax(geometry) as maxlon';
if ($this->bIncludePolygonAsGeoJSON) { if ($this->bIncludePolygonAsGeoJSON) {
@@ -539,21 +544,19 @@ class PlaceLookup
if ($this->bIncludePolygonAsText) { if ($this->bIncludePolygonAsText) {
$sSQL .= ',ST_AsText(geometry) as astext'; $sSQL .= ',ST_AsText(geometry) as astext';
} }
$sSQL .= ' FROM (SELECT place_id';
if ($fLonReverse != null && $fLatReverse != null) { if ($fLonReverse != null && $fLatReverse != null) {
$sSQL .= ',CASE WHEN (class = \'highway\') AND (ST_GeometryType(geometry) = \'ST_LineString\') THEN '; $sFrom = ' from (SELECT * , CASE WHEN (class = \'highway\') AND (ST_GeometryType(geometry) = \'ST_LineString\') THEN ';
$sSQL .=' ST_ClosestPoint(geometry, ST_SetSRID(ST_Point('.$fLatReverse.','.$fLonReverse.'),4326))'; $sFrom .=' ST_ClosestPoint(geometry, ST_SetSRID(ST_Point('.$fLatReverse.','.$fLonReverse.'),4326))';
$sSQL .=' ELSE centroid END AS centroid'; $sFrom .=' ELSE centroid END AS closest_point';
$sFrom .= ' from placex where place_id = '.$iPlaceID.') as plx';
} else { } else {
$sSQL .= ',centroid'; $sFrom = ' from placex where place_id = '.$iPlaceID;
} }
if ($this->fPolygonSimplificationThreshold > 0) { if ($this->fPolygonSimplificationThreshold > 0) {
$sSQL .= ',ST_SimplifyPreserveTopology(geometry,'.$this->fPolygonSimplificationThreshold.') as geometry'; $sSQL .= ' from (select place_id,centroid,ST_SimplifyPreserveTopology(geometry,'.$this->fPolygonSimplificationThreshold.') as geometry'.$sFrom.') as plx';
} else { } else {
$sSQL .= ',geometry'; $sSQL .= $sFrom;
} }
$sSQL .= ' FROM placex where place_id = '.$iPlaceID.') as plx';
$aPointPolygon = $this->oDB->getRow($sSQL, null, 'Could not get outline'); $aPointPolygon = $this->oDB->getRow($sSQL, null, 'Could not get outline');

View File

@@ -36,9 +36,6 @@ if (empty($aPlace)) {
$aFilteredPlaces['properties']['geocoding']['osm_id'] = $aPlace['osm_id']; $aFilteredPlaces['properties']['geocoding']['osm_id'] = $aPlace['osm_id'];
} }
$aFilteredPlaces['properties']['geocoding']['osm_key'] = $aPlace['class'];
$aFilteredPlaces['properties']['geocoding']['osm_value'] = $aPlace['type'];
$aFilteredPlaces['properties']['geocoding']['type'] = addressRankToGeocodeJsonType($aPlace['rank_address']); $aFilteredPlaces['properties']['geocoding']['type'] = addressRankToGeocodeJsonType($aPlace['rank_address']);
$aFilteredPlaces['properties']['geocoding']['accuracy'] = (int) $fDistance; $aFilteredPlaces['properties']['geocoding']['accuracy'] = (int) $fDistance;

View File

@@ -164,7 +164,7 @@ DECLARE
newend INTEGER; newend INTEGER;
moddiff SMALLINT; moddiff SMALLINT;
linegeo GEOMETRY; linegeo GEOMETRY;
splitpoint FLOAT; splitline GEOMETRY;
sectiongeo GEOMETRY; sectiongeo GEOMETRY;
postcode TEXT; postcode TEXT;
stepmod SMALLINT; stepmod SMALLINT;
@@ -223,27 +223,15 @@ BEGIN
FROM placex, generate_series(1, array_upper(waynodes, 1)) nodeidpos FROM placex, generate_series(1, array_upper(waynodes, 1)) nodeidpos
WHERE osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT WHERE osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT
and address is not NULL and address ? 'housenumber' and address is not NULL and address ? 'housenumber'
and ST_Distance(NEW.linegeo, geometry) < 0.0005
ORDER BY nodeidpos ORDER BY nodeidpos
LOOP LOOP
{% if debug %}RAISE WARNING 'processing point % (%)', nextnode.hnr, ST_AsText(nextnode.geometry);{% endif %} {% if debug %}RAISE WARNING 'processing point % (%)', nextnode.hnr, ST_AsText(nextnode.geometry);{% endif %}
IF linegeo is null THEN IF linegeo is null THEN
linegeo := NEW.linegeo; linegeo := NEW.linegeo;
ELSE ELSE
splitpoint := ST_LineLocatePoint(linegeo, nextnode.geometry); splitline := ST_Split(ST_Snap(linegeo, nextnode.geometry, 0.0005), nextnode.geometry);
IF splitpoint = 0 THEN sectiongeo := ST_GeometryN(splitline, 1);
-- Corner case where the splitpoint falls on the first point linegeo := ST_GeometryN(splitline, 2);
-- and thus would not return a geometry. Skip that section.
sectiongeo := NULL;
ELSEIF splitpoint = 1 THEN
-- Point is at the end of the line.
sectiongeo := linegeo;
linegeo := NULL;
ELSE
-- Split the line.
sectiongeo := ST_LineSubstring(linegeo, 0, splitpoint);
linegeo := ST_LineSubstring(linegeo, splitpoint, 1);
END IF;
END IF; END IF;
IF prevnode.hnr is not null IF prevnode.hnr is not null
@@ -251,9 +239,6 @@ BEGIN
-- regularly mapped housenumbers. -- regularly mapped housenumbers.
-- (Conveniently also fails if one of the house numbers is not a number.) -- (Conveniently also fails if one of the house numbers is not a number.)
and abs(prevnode.hnr - nextnode.hnr) > NEW.step and abs(prevnode.hnr - nextnode.hnr) > NEW.step
-- If the interpolation geometry is broken or two nodes are at the
-- same place, then splitting might produce a point. Ignore that.
and ST_GeometryType(sectiongeo) = 'ST_LineString'
THEN THEN
IF prevnode.hnr < nextnode.hnr THEN IF prevnode.hnr < nextnode.hnr THEN
startnumber := prevnode.hnr; startnumber := prevnode.hnr;
@@ -315,12 +300,12 @@ BEGIN
NEW.address, postcode, NEW.address, postcode,
NEW.country_code, NEW.geometry_sector, 0); NEW.country_code, NEW.geometry_sector, 0);
END IF; END IF;
END IF;
-- early break if we are out of line string, -- early break if we are out of line string,
-- might happen when a line string loops back on itself -- might happen when a line string loops back on itself
IF linegeo is null or ST_GeometryType(linegeo) != 'ST_LineString' THEN IF ST_GeometryType(linegeo) != 'ST_LineString' THEN
RETURN NEW; RETURN NEW;
END IF;
END IF; END IF;
prevnode := nextnode; prevnode := nextnode;

View File

@@ -384,19 +384,7 @@ BEGIN
-- Mark for delete in the placex table -- Mark for delete in the placex table
UPDATE placex SET indexed_status = 100 FROM place_to_be_deleted UPDATE placex SET indexed_status = 100 FROM place_to_be_deleted
WHERE placex.osm_type = 'N' and place_to_be_deleted.osm_type = 'N' WHERE placex.osm_type = place_to_be_deleted.osm_type
and placex.osm_id = place_to_be_deleted.osm_id
and placex.class = place_to_be_deleted.class
and placex.type = place_to_be_deleted.type
and not deferred;
UPDATE placex SET indexed_status = 100 FROM place_to_be_deleted
WHERE placex.osm_type = 'W' and place_to_be_deleted.osm_type = 'W'
and placex.osm_id = place_to_be_deleted.osm_id
and placex.class = place_to_be_deleted.class
and placex.type = place_to_be_deleted.type
and not deferred;
UPDATE placex SET indexed_status = 100 FROM place_to_be_deleted
WHERE placex.osm_type = 'R' and place_to_be_deleted.osm_type = 'R'
and placex.osm_id = place_to_be_deleted.osm_id and placex.osm_id = place_to_be_deleted.osm_id
and placex.class = place_to_be_deleted.class and placex.class = place_to_be_deleted.class
and placex.type = place_to_be_deleted.type and placex.type = place_to_be_deleted.type

View File

@@ -1120,7 +1120,7 @@ BEGIN
ELSE ELSE
-- No linked place? As a last resort check if the boundary is tagged with -- No linked place? As a last resort check if the boundary is tagged with
-- a place type and adapt the rank address. -- a place type and adapt the rank address.
IF NEW.rank_address between 4 and 25 and NEW.extratags ? 'place' THEN IF NEW.rank_address > 0 and NEW.extratags ? 'place' THEN
SELECT address_rank INTO place_address_level SELECT address_rank INTO place_address_level
FROM compute_place_rank(NEW.country_code, 'A', 'place', FROM compute_place_rank(NEW.country_code, 'A', 'place',
NEW.extratags->'place', 0::SMALLINT, False, null); NEW.extratags->'place', 0::SMALLINT, False, null);
@@ -1230,11 +1230,7 @@ BEGIN
{% endif %} {% endif %}
END IF; END IF;
IF NEW.postcode is null AND NEW.rank_search > 8 IF NEW.postcode is null AND NEW.rank_search > 8 THEN
AND (NEW.rank_address > 0
OR ST_GeometryType(NEW.geometry) not in ('ST_LineString','ST_MultiLineString')
OR ST_Length(NEW.geometry) < 0.02)
THEN
NEW.postcode := get_nearest_postcode(NEW.country_code, NEW.geometry); NEW.postcode := get_nearest_postcode(NEW.country_code, NEW.geometry);
END IF; END IF;

View File

@@ -273,8 +273,8 @@ BEGIN
END IF; END IF;
RETURN ST_Envelope(ST_Collect( RETURN ST_Envelope(ST_Collect(
ST_Project(geom::geography, radius, 0.785398)::geometry, ST_Project(geom, radius, 0.785398)::geometry,
ST_Project(geom::geography, radius, 3.9269908)::geometry)); ST_Project(geom, radius, 3.9269908)::geometry));
END; END;
$$ $$
LANGUAGE plpgsql IMMUTABLE; LANGUAGE plpgsql IMMUTABLE;
@@ -429,10 +429,9 @@ BEGIN
SELECT osm_type, osm_id, class, type FROM placex WHERE place_id = placeid INTO osmtype, osmid, pclass, ptype; SELECT osm_type, osm_id, class, type FROM placex WHERE place_id = placeid INTO osmtype, osmid, pclass, ptype;
DELETE FROM import_polygon_delete where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype; DELETE FROM import_polygon_delete where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype;
DELETE FROM import_polygon_error where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype; DELETE FROM import_polygon_error where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype;
-- force delete by directly entering it into the to-be-deleted table -- force delete from place/placex by making it a very small geometry
INSERT INTO place_to_be_deleted (osm_type, osm_id, class, type, deferred) UPDATE place set geometry = ST_SetSRID(ST_Point(0,0), 4326) where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype;
VALUES(osmtype, osmid, pclass, ptype, false); DELETE FROM place where osm_type = osmtype and osm_id = osmid and class = pclass and type = ptype;
PERFORM flush_deleted_places();
RETURN TRUE; RETURN TRUE;
END; END;

View File

@@ -76,25 +76,21 @@ class UpdateAddData:
osm2pgsql_params = args.osm2pgsql_options(default_cache=1000, default_threads=1) osm2pgsql_params = args.osm2pgsql_options(default_cache=1000, default_threads=1)
if args.file or args.diff: if args.file or args.diff:
return add_osm_data.add_data_from_file(args.config.get_libpq_dsn(), return add_osm_data.add_data_from_file(cast(str, args.file or args.diff),
cast(str, args.file or args.diff),
osm2pgsql_params) osm2pgsql_params)
if args.node: if args.node:
return add_osm_data.add_osm_object(args.config.get_libpq_dsn(), return add_osm_data.add_osm_object('node', args.node,
'node', args.node,
args.use_main_api, args.use_main_api,
osm2pgsql_params) osm2pgsql_params)
if args.way: if args.way:
return add_osm_data.add_osm_object(args.config.get_libpq_dsn(), return add_osm_data.add_osm_object('way', args.way,
'way', args.way,
args.use_main_api, args.use_main_api,
osm2pgsql_params) osm2pgsql_params)
if args.relation: if args.relation:
return add_osm_data.add_osm_object(args.config.get_libpq_dsn(), return add_osm_data.add_osm_object('relation', args.relation,
'relation', args.relation,
args.use_main_api, args.use_main_api,
osm2pgsql_params) osm2pgsql_params)

View File

@@ -248,9 +248,9 @@ class APIDetails:
if args.node: if args.node:
params = dict(osmtype='N', osmid=args.node) params = dict(osmtype='N', osmid=args.node)
elif args.way: elif args.way:
params = dict(osmtype='W', osmid=args.way) params = dict(osmtype='W', osmid=args.node)
elif args.relation: elif args.relation:
params = dict(osmtype='R', osmid=args.relation) params = dict(osmtype='R', osmid=args.node)
else: else:
params = dict(place_id=args.place_id) params = dict(place_id=args.place_id)
if args.object_class: if args.object_class:

View File

@@ -69,8 +69,8 @@ class DBConnection:
self.current_params: Optional[Sequence[Any]] = None self.current_params: Optional[Sequence[Any]] = None
self.ignore_sql_errors = ignore_sql_errors self.ignore_sql_errors = ignore_sql_errors
self.conn: Optional['psycopg2._psycopg.connection'] = None self.conn: Optional['psycopg2.connection'] = None
self.cursor: Optional['psycopg2._psycopg.cursor'] = None self.cursor: Optional['psycopg2.cursor'] = None
self.connect(cursor_factory=cursor_factory) self.connect(cursor_factory=cursor_factory)
def close(self) -> None: def close(self) -> None:
@@ -78,7 +78,7 @@ class DBConnection:
""" """
if self.conn is not None: if self.conn is not None:
if self.cursor is not None: if self.cursor is not None:
self.cursor.close() self.cursor.close() # type: ignore[no-untyped-call]
self.cursor = None self.cursor = None
self.conn.close() self.conn.close()

View File

@@ -31,7 +31,7 @@ class Cursor(psycopg2.extras.DictCursor):
""" Query execution that logs the SQL query when debugging is enabled. """ Query execution that logs the SQL query when debugging is enabled.
""" """
if LOG.isEnabledFor(logging.DEBUG): if LOG.isEnabledFor(logging.DEBUG):
LOG.debug(self.mogrify(query, args).decode('utf-8')) LOG.debug(self.mogrify(query, args).decode('utf-8')) # type: ignore[no-untyped-call]
super().execute(query, args) super().execute(query, args)

View File

@@ -118,4 +118,4 @@ class CopyBuffer:
""" """
if self.buffer.tell() > 0: if self.buffer.tell() > 0:
self.buffer.seek(0) self.buffer.seek(0)
cur.copy_from(self.buffer, table, columns=columns) cur.copy_from(self.buffer, table, columns=columns) # type: ignore[no-untyped-call]

View File

@@ -12,34 +12,23 @@ from pathlib import Path
import logging import logging
import urllib import urllib
from nominatim.db.connection import connect
from nominatim.tools.exec_utils import run_osm2pgsql, get_url from nominatim.tools.exec_utils import run_osm2pgsql, get_url
LOG = logging.getLogger() LOG = logging.getLogger()
def _run_osm2pgsql(dsn: str, options: MutableMapping[str, Any]) -> None: def add_data_from_file(fname: str, options: MutableMapping[str, Any]) -> int:
run_osm2pgsql(options)
# Handle deletions
with connect(dsn) as conn:
with conn.cursor() as cur:
cur.execute('SELECT flush_deleted_places()')
conn.commit()
def add_data_from_file(dsn: str, fname: str, options: MutableMapping[str, Any]) -> int:
""" Adds data from a OSM file to the database. The file may be a normal """ Adds data from a OSM file to the database. The file may be a normal
OSM file or a diff file in all formats supported by libosmium. OSM file or a diff file in all formats supported by libosmium.
""" """
options['import_file'] = Path(fname) options['import_file'] = Path(fname)
options['append'] = True options['append'] = True
_run_osm2pgsql(dsn, options) run_osm2pgsql(options)
# No status update. We don't know where the file came from. # No status update. We don't know where the file came from.
return 0 return 0
def add_osm_object(dsn: str, osm_type: str, osm_id: int, use_main_api: bool, def add_osm_object(osm_type: str, osm_id: int, use_main_api: bool,
options: MutableMapping[str, Any]) -> int: options: MutableMapping[str, Any]) -> int:
""" Add or update a single OSM object from the latest version of the """ Add or update a single OSM object from the latest version of the
API. API.
@@ -62,6 +51,6 @@ def add_osm_object(dsn: str, osm_type: str, osm_id: int, use_main_api: bool,
options['append'] = True options['append'] = True
options['import_data'] = get_url(base_url).encode('utf-8') options['import_data'] = get_url(base_url).encode('utf-8')
_run_osm2pgsql(dsn, options) run_osm2pgsql(options)
return 0 return 0

View File

@@ -12,13 +12,14 @@ import os
import subprocess import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple, Union from typing import List, Optional, Tuple, Union, cast
import psutil import psutil
from psycopg2.extensions import make_dsn, parse_dsn from psycopg2.extensions import make_dsn, parse_dsn
from nominatim.config import Configuration from nominatim.config import Configuration
from nominatim.db.connection import connect from nominatim.db.connection import connect
from nominatim.typing import DictCursorResults
from nominatim.version import version_str from nominatim.version import version_str
@@ -106,15 +107,15 @@ def report_system_information(config: Configuration) -> None:
postgresql_ver: str = convert_version(conn.server_version_tuple()) postgresql_ver: str = convert_version(conn.server_version_tuple())
with conn.cursor() as cur: with conn.cursor() as cur:
num = cur.scalar("SELECT count(*) FROM pg_catalog.pg_database WHERE datname=%s", cur.execute(f"""
(parse_dsn(config.get_libpq_dsn())['dbname'], )) SELECT datname FROM pg_catalog.pg_database
nominatim_db_exists = num == 1 if isinstance(num, int) else False WHERE datname='{parse_dsn(config.get_libpq_dsn())['dbname']}'""")
nominatim_db_exists = cast(Optional[DictCursorResults], cur.fetchall())
if nominatim_db_exists: if nominatim_db_exists:
with connect(config.get_libpq_dsn()) as conn: with connect(config.get_libpq_dsn()) as conn:
postgis_ver: str = convert_version(conn.postgis_version_tuple()) postgis_ver: str = convert_version(conn.postgis_version_tuple())
else: else:
postgis_ver = "Unable to connect to database" postgis_ver = "Unable to connect to database"
postgresql_config: str = get_postgresql_config(int(float(postgresql_ver))) postgresql_config: str = get_postgresql_config(int(float(postgresql_ver)))

View File

@@ -118,7 +118,7 @@ def run_osm2pgsql(options: Mapping[str, Any]) -> None:
cmd = [str(options['osm2pgsql']), cmd = [str(options['osm2pgsql']),
'--hstore', '--latlon', '--slim', '--hstore', '--latlon', '--slim',
'--log-progress', 'true', '--log-progress', 'true',
'--number-processes', '1' if options['append'] else str(options['threads']), '--number-processes', str(options['threads']),
'--cache', str(options['osm2pgsql_cache']), '--cache', str(options['osm2pgsql_cache']),
'--style', str(options['osm2pgsql_style']) '--style', str(options['osm2pgsql_style'])
] ]

View File

@@ -176,7 +176,7 @@ def recompute_importance(conn: Connection) -> None:
cur.execute(""" cur.execute("""
UPDATE placex SET (wikipedia, importance) = UPDATE placex SET (wikipedia, importance) =
(SELECT wikipedia, importance (SELECT wikipedia, importance
FROM compute_importance(extratags, country_code, rank_search, centroid)) FROM compute_importance(extratags, country_code, osm_type, osm_id, centroid))
""") """)
cur.execute(""" cur.execute("""
UPDATE placex s SET wikipedia = d.wikipedia, importance = d.importance UPDATE placex s SET wikipedia = d.wikipedia, importance = d.importance

View File

@@ -2,7 +2,7 @@
# #
# This file is part of Nominatim. (https://nominatim.org) # This file is part of Nominatim. (https://nominatim.org)
# #
# Copyright (C) 2023 by the Nominatim developer community. # Copyright (C) 2022 by the Nominatim developer community.
# For a full list of authors see the git log. # For a full list of authors see the git log.
""" """
Version information for Nominatim. Version information for Nominatim.
@@ -25,7 +25,7 @@ from typing import Optional, Tuple
# patch level when cherry-picking the commit with the migration. # patch level when cherry-picking the commit with the migration.
# #
# Released versions always have a database patch level of 0. # Released versions always have a database patch level of 0.
NOMINATIM_VERSION = (4, 2, 4, 0) NOMINATIM_VERSION = (4, 2, 0, 0)
POSTGRESQL_REQUIRED_VERSION = (9, 6) POSTGRESQL_REQUIRED_VERSION = (9, 6)
POSTGIS_REQUIRED_VERSION = (2, 2) POSTGIS_REQUIRED_VERSION = (2, 2)

View File

@@ -1,19 +1,9 @@
-- Core functions for Nominatim import flex style. -- Core functions for Nominatim import flex style.
-- --
local module = {}
local PRE_DELETE = nil
local PRE_EXTRAS = nil
local MAIN_KEYS = nil
local NAMES = nil
local ADDRESS_TAGS = nil
local SAVE_EXTRA_MAINS = false
local POSTCODE_FALLBACK = true
-- The single place table. -- The single place table.
local place_table = osm2pgsql.define_table{ place_table = osm2pgsql.define_table{
name = "place", name = "place",
ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' }, ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
columns = { columns = {
@@ -24,25 +14,7 @@ local place_table = osm2pgsql.define_table{
{ column = 'address', type = 'hstore' }, { column = 'address', type = 'hstore' },
{ column = 'extratags', type = 'hstore' }, { column = 'extratags', type = 'hstore' },
{ column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true }, { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true },
}, }
indexes = {}
}
------------ Geometry functions for relations ---------------------
function module.relation_as_multipolygon(o)
return o:as_multipolygon()
end
function module.relation_as_multiline(o)
return o:as_multilinestring():line_merge()
end
module.RELATION_TYPES = {
multipolygon = module.relation_as_multipolygon,
boundary = module.relation_as_multipolygon,
waterway = module.relation_as_multiline
} }
------------- Place class ------------------------------------------ ------------- Place class ------------------------------------------
@@ -71,17 +43,6 @@ function Place.new(object, geom_func)
return self return self
end end
function Place:clean(data)
for k, v in pairs(self.object.tags) do
if data.delete ~= nil and data.delete(k, v) then
self.object.tags[k] = nil
elseif data.extra ~= nil and data.extra(k, v) then
self.extratags[k] = v
self.object.tags[k] = nil
end
end
end
function Place:delete(data) function Place:delete(data)
if data.match ~= nil then if data.match ~= nil then
for k, v in pairs(self.object.tags) do for k, v in pairs(self.object.tags) do
@@ -108,37 +69,31 @@ function Place:grab_extratags(data)
return count return count
end end
local function strip_address_prefix(k) function Place:grab_address(data)
if k:sub(1, 5) == 'addr:' then
return k:sub(6)
end
if k:sub(1, 6) == 'is_in:' then
return k:sub(7)
end
return k
end
function Place:grab_address_parts(data)
local count = 0 local count = 0
if data.groups ~= nil then if data.match ~= nil then
for k, v in pairs(self.object.tags) do for k, v in pairs(self.object.tags) do
local atype = data.groups(k, v) if data.match(k, v) then
if atype ~= nil then
if atype == 'main' then
self.has_name = true
self.address[strip_address_prefix(k)] = v
count = count + 1
elseif atype == 'extra' then
self.address[strip_address_prefix(k)] = v
else
self.address[atype] = v
end
self.object.tags[k] = nil self.object.tags[k] = nil
if data.include_on_name == true then
self.has_name = true
end
if data.out_key ~= nil then
self.address[data.out_key] = v
return 1
end
if k:sub(1, 5) == 'addr:' then
self.address[k:sub(6)] = v
elseif k:sub(1, 6) == 'is_in:' then
self.address[k:sub(7)] = v
else
self.address[k] = v
end
count = count + 1
end end
end end
end end
@@ -146,30 +101,36 @@ function Place:grab_address_parts(data)
return count return count
end end
function Place:set_address(key, value)
self.address[key] = value
end
function Place:grab_name_parts(data) function Place:grab_name(data)
local fallback = nil local count = 0
if data.groups ~= nil then if data.match ~= nil then
for k, v in pairs(self.object.tags) do for k, v in pairs(self.object.tags) do
local atype = data.groups(k, v) if data.match(k, v) then
if atype ~= nil then
self.names[k] = v
self.object.tags[k] = nil self.object.tags[k] = nil
if atype == 'main' then self.names[k] = v
if data.include_on_name ~= false then
self.has_name = true self.has_name = true
elseif atype == 'house' then
self.has_name = true
fallback = {'place', 'house', 'always'}
end end
count = count + 1
end end
end end
end end
return fallback return count
end end
function Place:grab_tag(key)
return self.object:grab_tag(key)
end
function Place:tags()
return self.object.tags
end
function Place:write_place(k, v, mtype, save_extra_mains) function Place:write_place(k, v, mtype, save_extra_mains)
if mtype == nil then if mtype == nil then
@@ -223,9 +184,9 @@ function Place:write_row(k, v, save_extra_mains)
return 0 return 0
end end
if save_extra_mains ~= nil then if save_extra_mains then
for extra_k, extra_v in pairs(self.object.tags) do for extra_k, extra_v in pairs(self.object.tags) do
if extra_k ~= k and save_extra_mains(extra_k, extra_v) then if extra_k ~= k then
self.extratags[extra_k] = extra_v self.extratags[extra_k] = extra_v
end end
end end
@@ -243,9 +204,7 @@ function Place:write_row(k, v, save_extra_mains)
if save_extra_mains then if save_extra_mains then
for k, v in pairs(self.object.tags) do for k, v in pairs(self.object.tags) do
if save_extra_mains(k, v) then self.extratags[k] = nil
self.extratags[k] = nil
end
end end
end end
@@ -255,7 +214,7 @@ function Place:write_row(k, v, save_extra_mains)
end end
function module.tag_match(data) function tag_match(data)
if data == nil or next(data) == nil then if data == nil or next(data) == nil then
return nil return nil
end end
@@ -317,72 +276,17 @@ function module.tag_match(data)
end end
function module.tag_group(data)
if data == nil or next(data) == nil then
return nil
end
local fullmatches = {}
local key_prefixes = {}
local key_suffixes = {}
for group, tags in pairs(data) do
for _, key in pairs(tags) do
if key:sub(1, 1) == '*' then
if #key > 1 then
if key_suffixes[#key - 1] == nil then
key_suffixes[#key - 1] = {}
end
key_suffixes[#key - 1][key:sub(2)] = group
end
elseif key:sub(#key, #key) == '*' then
if key_prefixes[#key - 1] == nil then
key_prefixes[#key - 1] = {}
end
key_prefixes[#key - 1][key:sub(1, #key - 1)] = group
else
fullmatches[key] = group
end
end
end
return function (k, v)
local val = fullmatches[k]
if val ~= nil then
return val
end
for slen, slist in pairs(key_suffixes) do
if #k >= slen then
val = slist[k:sub(-slen)]
if val ~= nil then
return val
end
end
end
for slen, slist in pairs(key_prefixes) do
if #k >= slen then
val = slist[k:sub(1, slen)]
if val ~= nil then
return val
end
end
end
end
end
-- Process functions for all data types -- Process functions for all data types
function module.process_node(object) function osm2pgsql.process_node(object)
local function geom_func(o) local function geom_func(o)
return o:as_point() return o:as_point()
end end
module.process_tags(Place.new(object, geom_func)) process_tags(Place.new(object, geom_func))
end end
function module.process_way(object) function osm2pgsql.process_way(object)
local function geom_func(o) local function geom_func(o)
local geom = o:as_polygon() local geom = o:as_polygon()
@@ -394,24 +298,30 @@ function module.process_way(object)
return geom return geom
end end
module.process_tags(Place.new(object, geom_func)) process_tags(Place.new(object, geom_func))
end end
function module.process_relation(object) function relation_as_multipolygon(o)
local geom_func = module.RELATION_TYPES[object.tags.type] return o:as_multipolygon()
end
function relation_as_multiline(o)
return o:as_multilinestring():line_merge()
end
function osm2pgsql.process_relation(object)
local geom_func = RELATION_TYPES[object.tags.type]
if geom_func ~= nil then if geom_func ~= nil then
module.process_tags(Place.new(object, geom_func)) process_tags(Place.new(object, geom_func))
end end
end end
-- The process functions are used by default by osm2pgsql. function process_tags(o)
osm2pgsql.process_node = module.process_node local fallback
osm2pgsql.process_way = module.process_way
osm2pgsql.process_relation = module.process_relation
function module.process_tags(o) o:delete{match = PRE_DELETE}
o:clean{delete = PRE_DELETE, extra = PRE_EXTRAS} o:grab_extratags{match = PRE_EXTRAS}
-- Exception for boundary/place double tagging -- Exception for boundary/place double tagging
if o.object.tags.boundary == 'administrative' then if o.object.tags.boundary == 'administrative' then
@@ -420,91 +330,54 @@ function module.process_tags(o)
end} end}
end end
-- name keys
local fallback = o:grab_name_parts{groups=NAMES}
-- address keys -- address keys
if o:grab_address_parts{groups=ADDRESS_TAGS} > 0 and fallback == nil then o:grab_address{match=COUNTRY_TAGS, out_key='country'}
fallback = {'place', 'house', 'always'}
end
if o.address.country ~= nil and #o.address.country ~= 2 then if o.address.country ~= nil and #o.address.country ~= 2 then
o.address['country'] = nil o.address['country'] = nil
end end
if POSTCODE_FALLBACK and fallback == nil and o.address.postcode ~= nil then if o:grab_name{match=HOUSENAME_TAGS} > 0 then
fallback = {'place', 'postcode', 'always'} fallback = {'place', 'house'}
end
if o:grab_address{match=HOUSENUMBER_TAGS, include_on_name = true} > 0 and fallback == nil then
fallback = {'place', 'house'}
end
if o:grab_address{match=POSTCODES, out_key='postcode'} > 0 and fallback == nil then
fallback = {'place', 'postcode'}
end end
if o.address.interpolation ~= nil then local is_interpolation = o:grab_address{match=INTERPOLATION_TAGS} > 0
o:grab_address{match=ADDRESS_TAGS}
if is_interpolation then
o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS) o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS)
return return
end end
o:clean{delete = POST_DELETE} -- name keys
o:grab_name{match = NAMES}
o:grab_name{match = REFS, include_on_name = false}
o:delete{match = POST_DELETE}
o:grab_extratags{match = POST_EXTRAS}
-- collect main keys -- collect main keys
for k, v in pairs(o.object.tags) do local num_mains = 0
local ktype = MAIN_KEYS[k] for k, v in pairs(o:tags()) do
if ktype == 'fallback' then num_mains = num_mains + o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
if o.has_name then end
fallback = {k, v, 'named'}
if num_mains == 0 then
for tag, mtype in pairs(MAIN_FALLBACK_KEYS) do
if o:write_place(tag, nil, mtype, SAVE_EXTRA_MAINS) > 0 then
return
end end
elseif ktype ~= nil then
o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
end end
end
if fallback ~= nil and o.num_entries == 0 then if fallback ~= nil then
o:write_place(fallback[1], fallback[2], fallback[3], SAVE_EXTRA_MAINS) o:write_place(fallback[1], fallback[2], 'always', SAVE_EXTRA_MAINS)
end
end
--------- Convenience functions for simple style configuration -----------------
function module.set_prefilters(data)
PRE_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
PRE_EXTRAS = module.tag_match{keys = data.extratag_keys,
tags = data.extratag_tags}
end
function module.set_main_tags(data)
MAIN_KEYS = data
end
function module.set_name_tags(data)
NAMES = module.tag_group(data)
end
function module.set_address_tags(data)
if data.postcode_fallback ~= nil then
POSTCODE_FALLBACK = data.postcode_fallback
data.postcode_fallback = nil
end
ADDRESS_TAGS = module.tag_group(data)
end
function module.set_unused_handling(data)
if data.extra_keys == nil and data.extra_tags == nil then
POST_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
SAVE_EXTRA_MAINS = function() return true end
elseif data.delete_keys == nil and data.delete_tags == nil then
POST_DELETE = nil
SAVE_EXTRA_MAINS = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
else
error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
end
end
function set_relation_types(data)
module.RELATION_TYPES = {}
for k, v in data do
if v == 'multipolygon' then
module.RELATION_TYPES[k] = module.relation_as_multipolygon
elseif v == 'multiline' then
module.RELATION_TYPES[k] = module.relation_as_multiline
end end
end end
end end
return module

View File

@@ -24,7 +24,6 @@ transliteration:
- ":: lower ()" - ":: lower ()"
- "[^a-z0-9[:Space:]] >" - "[^a-z0-9[:Space:]] >"
- ":: NFC ()" - ":: NFC ()"
- "[:Space:]+ > ' '"
sanitizers: sanitizers:
- step: clean-housenumbers - step: clean-housenumbers
filter-kind: filter-kind:
@@ -38,7 +37,6 @@ sanitizers:
default-pattern: "[A-Z0-9- ]{3,12}" default-pattern: "[A-Z0-9- ]{3,12}"
- step: clean-tiger-tags - step: clean-tiger-tags
- step: split-name-list - step: split-name-list
delimiters: ;
- step: strip-brace-terms - step: strip-brace-terms
- step: tag-analyzer-by-language - step: tag-analyzer-by-language
filter-kind: [".*name.*"] filter-kind: [".*name.*"]

View File

@@ -1,67 +0,0 @@
flex = require('flex-base')
flex.set_main_tags{
highway = {'always',
street_lamp = 'named',
traffic_signals = 'named',
service = 'named',
cycleway = 'named',
path = 'named',
footway = 'named',
steps = 'named',
bridleway = 'named',
track = 'named',
motorway_link = 'named',
trunk_link = 'named',
primary_link = 'named',
secondary_link = 'named',
tertiary_link = 'named'},
boundary = {administrative = 'named',
postal_code = 'always'},
landuse = 'fallback',
place = 'always'
}
flex.set_prefilters{delete_keys = {'building', 'source',
'source', '*source', 'type',
'is_in:postcode', '*:wikidata', '*:wikipedia',
'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
'name:etymology', 'name:signed', 'name:botanical',
'addr:street:name', 'addr:street:type'},
delete_tags = {highway = {'no', 'turning_circle', 'mini_roundabout',
'noexit', 'crossing', 'give_way', 'stop'},
landuse = {'cemetry', 'no'},
boundary = {'place'}},
extratag_keys = {'wikipedia', 'wikipedia:*', 'wikidata', 'capital', 'area'}
}
flex.set_name_tags{main = {'name', 'name:*',
'int_name', 'int_name:*',
'nat_name', 'nat_name:*',
'reg_name', 'reg_name:*',
'loc_name', 'loc_name:*',
'old_name', 'old_name:*',
'alt_name', 'alt_name:*', 'alt_name_*',
'official_name', 'official_name:*',
'place_name', 'place_name:*',
'short_name', 'short_name:*'},
extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
'loc_ref', 'old_ref',
'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'},
house = {'addr:housename'}
}
flex.set_address_tags{main = {'addr:housenumber',
'addr:conscriptionnumber',
'addr:streetnumber'},
extra = {'addr:*', 'is_in:*', 'tiger:county'},
postcode = {'postal_code', 'postcode', 'addr:postcode',
'tiger:zip_left', 'tiger:zip_right'},
country = {'country_code', 'ISO3166-1',
'addr:country_code', 'is_in:country_code',
'addr:country', 'is_in:country'},
interpolation = {'addr:interpolation'}
}
flex.set_unused_handling{extra_keys = {'place'}}

View File

@@ -1,44 +0,0 @@
local flex = require('flex-base')
flex.set_main_tags{
boundary = {administrative = 'named'},
landuse = 'fallback',
place = 'always'
}
flex.set_prefilters{delete_keys = {'building', 'source', 'highway',
'addr:housenumber', 'addr:street', 'addr:city',
'source', '*source', 'type',
'is_in:postcode', '*:wikidata', '*:wikipedia',
'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
'name:etymology', 'name:signed', 'name:botanical',
'addr:street:name', 'addr:street:type'},
delete_tags = {landuse = {'cemetry', 'no'},
boundary = {'place'}},
extratag_keys = {'wikipedia', 'wikipedia:*', 'wikidata', 'capital'}
}
flex.set_name_tags{main = {'name', 'name:*',
'int_name', 'int_name:*',
'nat_name', 'nat_name:*',
'reg_name', 'reg_name:*',
'loc_name', 'loc_name:*',
'old_name', 'old_name:*',
'alt_name', 'alt_name:*', 'alt_name_*',
'official_name', 'official_name:*',
'place_name', 'place_name:*',
'short_name', 'short_name:*'},
extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
'loc_ref', 'old_ref',
'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'}
}
flex.set_address_tags{extra = {'addr:*', 'is_in:*'},
postcode = {'postal_code', 'postcode', 'addr:postcode'},
country = {'country_code', 'ISO3166-1',
'addr:country_code', 'is_in:country_code',
'addr:country', 'is_in:country'},
postcode_fallback = false
}
flex.set_unused_handling{extra_keys = {'place'}}

View File

@@ -1,9 +1,13 @@
flex = require('flex-base') require('flex-base')
flex.set_main_tags{ RELATION_TYPES = {
building = 'fallback', multipolygon = relation_as_multipolygon,
boundary = relation_as_multipolygon,
waterway = relation_as_multiline
}
MAIN_KEYS = {
emergency = 'always', emergency = 'always',
healthcare = 'fallback',
historic = 'always', historic = 'always',
military = 'always', military = 'always',
natural = 'named', natural = 'named',
@@ -27,13 +31,11 @@ flex.set_main_tags{
man_made = 'always', man_made = 'always',
aerialway = 'always', aerialway = 'always',
boundary = {'named', boundary = {'named',
postal_code = 'always'}, postal_code = 'named'},
aeroway = 'always', aeroway = 'always',
amenity = 'always', amenity = 'always',
club = 'always', club = 'always',
craft = 'always', craft = 'always',
junction = 'fallback',
landuse = 'fallback',
leisure = 'always', leisure = 'always',
office = 'always', office = 'always',
mountain_pass = 'always', mountain_pass = 'always',
@@ -45,43 +47,55 @@ flex.set_main_tags{
place = 'always' place = 'always'
} }
flex.set_prefilters{delete_keys = {'note', 'note:*', 'source', '*source', 'attribution', MAIN_FALLBACK_KEYS = {
'comment', 'fixme', 'FIXME', 'created_by', 'NHD:*', building = 'named',
'nhd:*', 'gnis:*', 'geobase:*', 'KSJ2:*', 'yh:*', landuse = 'named',
'osak:*', 'naptan:*', 'CLC:*', 'import', 'it:fvg:*', junction = 'named',
'type', 'lacounty:*', 'ref:ruian:*', 'building:ruian:type', healthcare = 'named'
'ref:linz:*', 'is_in:postcode'}, }
delete_tags = {emergency = {'yes', 'no', 'fire_hydrant'},
historic = {'yes', 'no'},
military = {'yes', 'no'}, PRE_DELETE = tag_match{keys = {'note', 'note:*', 'source', 'source*', 'attribution',
natural = {'yes', 'no', 'coastline'}, 'comment', 'fixme', 'FIXME', 'created_by', 'NHD:*',
highway = {'no', 'turning_circle', 'mini_roundabout', 'nhd:*', 'gnis:*', 'geobase:*', 'KSJ2:*', 'yh:*',
'noexit', 'crossing', 'give_way', 'stop'}, 'osak:*', 'naptan:*', 'CLC:*', 'import', 'it:fvg:*',
railway = {'level_crossing', 'no', 'rail'}, 'type', 'lacounty:*', 'ref:ruian:*', 'building:ruian:type',
man_made = {'survey_point', 'cutline'}, 'ref:linz:*', 'is_in:postcode'},
aerialway = {'pylon', 'no'}, tags = {emergency = {'yes', 'no', 'fire_hydrant'},
aeroway = {'no'}, historic = {'yes', 'no'},
amenity = {'no'}, military = {'yes', 'no'},
club = {'no'}, natural = {'yes', 'no', 'coastline'},
craft = {'no'}, highway = {'no', 'turning_circle', 'mini_roundabout',
leisure = {'no'}, 'noexit', 'crossing', 'give_way', 'stop'},
office = {'no'}, railway = {'level_crossing', 'no', 'rail'},
mountain_pass = {'no'}, man_made = {'survey_point', 'cutline'},
shop = {'no'}, aerialway = {'pylon', 'no'},
tourism = {'yes', 'no'}, aeroway = {'no'},
bridge = {'no'}, amenity = {'no'},
tunnel = {'no'}, club = {'no'},
waterway = {'riverbank'}, craft = {'no'},
building = {'no'}, leisure = {'no'},
boundary = {'place'}}, office = {'no'},
extratag_keys = {'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*', mountain_pass = {'no'},
shop = {'no'},
tourism = {'yes', 'no'},
bridge = {'no'},
tunnel = {'no'},
waterway = {'riverbank'},
building = {'no'},
boundary = {'place'}}
}
POST_DELETE = tag_match{keys = {'tiger:*'}}
PRE_EXTRAS = tag_match{keys = {'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
'name:etymology', 'name:signed', 'name:botanical', 'name:etymology', 'name:signed', 'name:botanical',
'wikidata', '*:wikidata', 'wikidata', '*:wikidata',
'*:wikipedia', 'brand:wikipedia:*',
'addr:street:name', 'addr:street:type'} 'addr:street:name', 'addr:street:type'}
} }
flex.set_name_tags{main = {'name', 'name:*',
NAMES = tag_match{keys = {'name', 'name:*',
'int_name', 'int_name:*', 'int_name', 'int_name:*',
'nat_name', 'nat_name:*', 'nat_name', 'nat_name:*',
'reg_name', 'reg_name:*', 'reg_name', 'reg_name:*',
@@ -90,24 +104,26 @@ flex.set_name_tags{main = {'name', 'name:*',
'alt_name', 'alt_name:*', 'alt_name_*', 'alt_name', 'alt_name:*', 'alt_name_*',
'official_name', 'official_name:*', 'official_name', 'official_name:*',
'place_name', 'place_name:*', 'place_name', 'place_name:*',
'short_name', 'short_name:*', 'brand'}, 'short_name', 'short_name:*', 'brand'}}
extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
'loc_ref', 'old_ref',
'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'},
house = {'addr:housename'}
}
flex.set_address_tags{main = {'addr:housenumber', REFS = tag_match{keys = {'ref', 'int_ref', 'nat_ref', 'reg_ref', 'loc_ref', 'old_ref',
'addr:conscriptionnumber', 'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'}}
'addr:streetnumber'},
extra = {'addr:*', 'is_in:*', 'tiger:county'}, POSTCODES = tag_match{keys = {'postal_code', 'postcode', 'addr:postcode',
postcode = {'postal_code', 'postcode', 'addr:postcode', 'tiger:zip_left', 'tiger:zip_right'}}
'tiger:zip_left', 'tiger:zip_right'},
country = {'country_code', 'ISO3166-1', COUNTRY_TAGS = tag_match{keys = {'country_code', 'ISO3166-1',
'addr:country_code', 'is_in:country_code', 'addr:country_code', 'is_in:country_code',
'addr:country', 'is_in:country'}, 'addr:country', 'is_in:country'}}
interpolation = {'addr:interpolation'}
}
HOUSENAME_TAGS = tag_match{keys = {'addr:housename'}}
HOUSENUMBER_TAGS = tag_match{keys = {'addr:housenumber', 'addr:conscriptionnumber',
'addr:streetnumber'}}
INTERPOLATION_TAGS = tag_match{keys = {'addr:interpolation'}}
ADDRESS_TAGS = tag_match{keys = {'addr:*', 'is_in:*', 'tiger:county'}}
SAVE_EXTRA_MAINS = true
flex.set_unused_handling{delete_keys = {'tiger:*'}}

View File

@@ -1,113 +0,0 @@
flex = require('flex-base')
flex.set_main_tags{
building = 'fallback',
emergency = 'always',
healthcare = 'fallback',
historic = 'always',
military = 'always',
natural = 'named',
landuse = 'named',
highway = {'always',
street_lamp = 'named',
traffic_signals = 'named',
service = 'named',
cycleway = 'named',
path = 'named',
footway = 'named',
steps = 'named',
bridleway = 'named',
track = 'named',
motorway_link = 'named',
trunk_link = 'named',
primary_link = 'named',
secondary_link = 'named',
tertiary_link = 'named'},
railway = 'named',
man_made = 'always',
aerialway = 'always',
boundary = {'named',
postal_code = 'always'},
aeroway = 'always',
amenity = 'always',
club = 'always',
craft = 'always',
junction = 'fallback',
landuse = 'fallback',
leisure = 'always',
office = 'always',
mountain_pass = 'always',
shop = 'always',
tourism = 'always',
bridge = 'named_with_key',
tunnel = 'named_with_key',
waterway = 'named',
place = 'always'
}
flex.set_prefilters{delete_keys = {'note', 'note:*', 'source', '*source', 'attribution',
'comment', 'fixme', 'FIXME', 'created_by', 'NHD:*',
'nhd:*', 'gnis:*', 'geobase:*', 'KSJ2:*', 'yh:*',
'osak:*', 'naptan:*', 'CLC:*', 'import', 'it:fvg:*',
'type', 'lacounty:*', 'ref:ruian:*', 'building:ruian:type',
'ref:linz:*', 'is_in:postcode',
'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
'name:etymology', 'name:signed', 'name:botanical',
'*:wikidata', '*:wikipedia', 'brand:wikipedia:*',
'addr:street:name', 'addr:street:type'},
delete_tags = {emergency = {'yes', 'no', 'fire_hydrant'},
historic = {'yes', 'no'},
military = {'yes', 'no'},
natural = {'yes', 'no', 'coastline'},
highway = {'no', 'turning_circle', 'mini_roundabout',
'noexit', 'crossing', 'give_way', 'stop'},
railway = {'level_crossing', 'no', 'rail'},
man_made = {'survey_point', 'cutline'},
aerialway = {'pylon', 'no'},
aeroway = {'no'},
amenity = {'no'},
club = {'no'},
craft = {'no'},
leisure = {'no'},
office = {'no'},
mountain_pass = {'no'},
shop = {'no'},
tourism = {'yes', 'no'},
bridge = {'no'},
tunnel = {'no'},
waterway = {'riverbank'},
building = {'no'},
boundary = {'place'}},
extra_keys = {'wikidata', 'wikipedia', 'wikipedia:*'}
}
flex.set_name_tags{main = {'name', 'name:*',
'int_name', 'int_name:*',
'nat_name', 'nat_name:*',
'reg_name', 'reg_name:*',
'loc_name', 'loc_name:*',
'old_name', 'old_name:*',
'alt_name', 'alt_name:*', 'alt_name_*',
'official_name', 'official_name:*',
'place_name', 'place_name:*',
'short_name', 'short_name:*', 'brand'},
extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
'loc_ref', 'old_ref',
'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'},
house = {'addr:housename'}
}
flex.set_address_tags{main = {'addr:housenumber',
'addr:conscriptionnumber',
'addr:streetnumber'},
extra = {'addr:*', 'is_in:*', 'tiger:county'},
postcode = {'postal_code', 'postcode', 'addr:postcode',
'tiger:zip_left', 'tiger:zip_right'},
country = {'country_code', 'ISO3166-1',
'addr:country_code', 'is_in:country_code',
'addr:country', 'is_in:country'},
interpolation = {'addr:interpolation'}
}
flex.set_unused_handling{extra_keys = {'place'}}

View File

@@ -1,67 +0,0 @@
flex = require('flex-base')
flex.set_main_tags{
highway = {'always',
street_lamp = 'named',
traffic_signals = 'named',
service = 'named',
cycleway = 'named',
path = 'named',
footway = 'named',
steps = 'named',
bridleway = 'named',
track = 'named',
motorway_link = 'named',
trunk_link = 'named',
primary_link = 'named',
secondary_link = 'named',
tertiary_link = 'named'},
boundary = {administrative = 'named',
postal_code = 'always'},
landuse = 'fallback',
place = 'always'
}
flex.set_prefilters{delete_keys = {'building', 'source',
'addr:housenumber', 'addr:street',
'source', '*source', 'type',
'is_in:postcode', '*:wikidata', '*:wikipedia',
'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
'name:etymology', 'name:signed', 'name:botanical',
'addr:street:name', 'addr:street:type'},
delete_tags = {highway = {'no', 'turning_circle', 'mini_roundabout',
'noexit', 'crossing', 'give_way', 'stop'},
landuse = {'cemetry', 'no'},
boundary = {'place'}},
extratag_keys = {'wikipedia', 'wikipedia:*', 'wikidata', 'capital', 'area'}
}
flex.set_name_tags{main = {'name', 'name:*',
'int_name', 'int_name:*',
'nat_name', 'nat_name:*',
'reg_name', 'reg_name:*',
'loc_name', 'loc_name:*',
'old_name', 'old_name:*',
'alt_name', 'alt_name:*', 'alt_name_*',
'official_name', 'official_name:*',
'place_name', 'place_name:*',
'short_name', 'short_name:*'},
extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
'loc_ref', 'old_ref',
'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'}
}
flex.set_address_tags{main = {'addr:housenumber',
'addr:conscriptionnumber',
'addr:streetnumber'},
extra = {'addr:*', 'is_in:*', 'tiger:county'},
postcode = {'postal_code', 'postcode', 'addr:postcode',
'tiger:zip_left', 'tiger:zip_right'},
country = {'country_code', 'ISO3166-1',
'addr:country_code', 'is_in:country_code',
'addr:country', 'is_in:country'},
interpolation = {'addr:interpolation'},
postcode_fallback = false
}
flex.set_unused_handling{extra_keys = {'place'}}

View File

@@ -1,9 +1,12 @@
all: bdd php python all: bdd php
no-test-db: bdd-no-test-db php no-test-db: bdd-no-test-db php
bdd: bdd:
cd bdd && behave -DREMOVE_TEMPLATE=1 cd bdd && behave -DREMOVE_TEMPLATE=1
icu:
cd bdd && behave -DREMOVE_TEMPLATE=1 -DTOKENIZER=icu
php: php:
cd php && phpunit ./ cd php && phpunit ./
@@ -11,4 +14,4 @@ python:
pytest python pytest python
.PHONY: bdd php no-test-db python .PHONY: bdd php no-test-db

View File

@@ -29,14 +29,14 @@ Feature: Import of address interpolations
| N2 | place | house | 8 | | N2 | place | house | 8 |
And the places And the places
| osm | class | type | addr+interpolation | geometry | | osm | class | type | addr+interpolation | geometry |
| W1 | place | houses | even | 2,1 | | W1 | place | houses | even | 1,2 |
And the ways And the ways
| id | nodes | | id | nodes |
| 1 | 2,1 | | 1 | 2,1 |
When importing When importing
Then W1 expands to interpolation Then W1 expands to interpolation
| start | end | geometry | | start | end | geometry |
| 4 | 6 | 9,8 | | 4 | 6 | 8,9 |
Scenario: Simple odd two point interpolation Scenario: Simple odd two point interpolation
Given the grid with origin 1,1 Given the grid with origin 1,1
@@ -341,7 +341,7 @@ Feature: Import of address interpolations
Then W1 expands to interpolation Then W1 expands to interpolation
| start | end | geometry | | start | end | geometry |
| 4 | 4 | 144.963016 -37.762946 | | 4 | 4 | 144.963016 -37.762946 |
| 8 | 8 | 144.96314407 -37.762223692 | | 8 | 8 | 144.963144 -37.7622237 |
Scenario: Place with missing address information Scenario: Place with missing address information
Given the grid Given the grid
@@ -456,69 +456,3 @@ Feature: Import of address interpolations
| foo | | foo |
| x | | x |
| 12-2 | | 12-2 |
Scenario: Interpolation line where points have been moved (Github #3022)
Given the 0.00001 grid
| 1 | | | | | | | | 2 | 3 | 9 | | | | | | | | 4 |
Given the places
| osm | class | type | housenr | geometry |
| N1 | place | house | 2 | 1 |
| N2 | place | house | 18 | 3 |
| N3 | place | house | 24 | 9 |
| N4 | place | house | 42 | 4 |
And the places
| osm | class | type | addr+interpolation | geometry |
| W1 | place | houses | even | 1,2,3,4 |
And the ways
| id | nodes |
| 1 | 1,2,3,4 |
When importing
Then W1 expands to interpolation
| start | end |
| 4 | 16 |
| 20 | 22 |
| 26 | 40 |
Scenario: Interpolation line with duplicated points
Given the grid
| 7 | 10 | 8 | 11 | 9 |
Given the places
| osm | class | type | housenr | geometry |
| N1 | place | house | 2 | 7 |
| N2 | place | house | 6 | 8 |
| N3 | place | house | 10 | 8 |
| N4 | place | house | 14 | 9 |
And the places
| osm | class | type | addr+interpolation | geometry |
| W1 | place | houses | even | 7,8,8,9 |
And the ways
| id | nodes |
| 1 | 1,2,3,4 |
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 4 | 4 | 10 |
| 12 | 12 | 11 |
Scenario: Interpolaton line with broken way geometry (Github #2986)
Given the grid
| 1 | 8 | 10 | 11 | 9 | 2 | 3 | 4 |
Given the places
| osm | class | type | housenr |
| N1 | place | house | 2 |
| N2 | place | house | 8 |
| N3 | place | house | 12 |
| N4 | place | house | 14 |
And the places
| osm | class | type | addr+interpolation | geometry |
| W1 | place | houses | even | 8,9 |
And the ways
| id | nodes |
| 1 | 1,8,9,2,3,4 |
When importing
Then W1 expands to interpolation
| start | end | geometry |
| 4 | 6 | 10,11 |

View File

@@ -255,15 +255,3 @@ Feature: Rank assignment
| W1 | R10 | True | 18 | | W1 | R10 | True | 18 |
| W1 | R2 | True | 16 | | W1 | R2 | True | 16 |
| W1 | N9 | False | 18 | | W1 | N9 | False | 18 |
Scenario: POI nodes with place tags
Given the places
| osm | class | type | name | extratags |
| N23 | amenity | playground | AB | "place": "city" |
| N23 | place | city | AB | "amenity": "playground" |
When importing
Then placex contains exactly
| object | rank_search | rank_address |
| N23:amenity | 30 | 30 |
| N23:place | 16 | 16 |

View File

@@ -2,26 +2,21 @@
Feature: Creation of search terms Feature: Creation of search terms
Tests that search_name table is filled correctly Tests that search_name table is filled correctly
Scenario: Semicolon-separated names appear as separate full names Scenario Outline: Comma- and semicolon separated names appear as full names
Given the places Given the places
| osm | class | type | name+alt_name | | osm | class | type | name+alt_name |
| N1 | place | city | New York; Big Apple | | N1 | place | city | New York<sep>Big Apple |
When importing When importing
Then search_name contains Then search_name contains
| object | name_vector | | object | name_vector |
| N1 | #New York, #Big Apple | | N1 | #New York, #Big Apple |
@fail-legacy Examples:
Scenario: Comma-separated names appear as a single full name | sep |
Given the places | , |
| osm | class | type | name+alt_name | | ; |
| N1 | place | city | New York, Big Apple |
When importing
Then search_name contains
| object | name_vector |
| N1 | #New York Big Apple |
Scenario: Name parts before brackets appear as full names Scenario Outline: Name parts before brackets appear as full names
Given the places Given the places
| osm | class | type | name+name | | osm | class | type | name+name |
| N1 | place | city | Halle (Saale) | | N1 | place | city | Halle (Saale) |

View File

@@ -101,19 +101,6 @@ Feature: Tag evaluation
| N6003 | shop | - | | N6003 | shop | - |
Scenario: Postcode areas
When loading osm data
"""
n1 x12.36853 y51.50618
n2 x12.36853 y51.42362
n3 x12.63666 y51.42362
n4 x12.63666 y51.50618
w1 Tboundary=postal_code,ref=3456 Nn1,n2,n3,n4,n1
"""
Then place contains exactly
| object | class | type | name |
| W1 | boundary | postal_code | 'ref': '3456' |
Scenario: Main with extra Scenario: Main with extra
When loading osm data When loading osm data
""" """
@@ -135,10 +122,10 @@ Feature: Tag evaluation
n8003 Tshop=shoes,name:source=survey n8003 Tshop=shoes,name:source=survey
""" """
Then place contains exactly Then place contains exactly
| object | class | name | extratags | | object | class | extratags |
| N8001 | shop | - | 'xx': 'yy' | | N8001 | shop | 'xx': 'yy' |
| N8002 | shop | - | 'ele': '234' | | N8002 | shop | 'ele': '234' |
| N8003 | shop | - | - | | N8003 | shop | - |
Scenario: Admin levels Scenario: Admin levels

View File

@@ -37,14 +37,14 @@ class DebugTest extends \PHPUnit\Framework\TestCase
<pre><b>Var1:</b> <i>True</i></pre> <pre><b>Var1:</b> <i>True</i></pre>
<pre><b>Var2:</b> <i>False</i></pre> <pre><b>Var2:</b> <i>False</i></pre>
<pre><b>Var3:</b> 0</pre> <pre><b>Var3:</b> 0</pre>
<pre><b>Var4:</b> &#039;String&#039;</pre> <pre><b>Var4:</b> 'String'</pre>
<pre><b>Var5:</b> 0 => &#039;one&#039; <pre><b>Var5:</b> 0 => 'one'
1 => &#039;two&#039; 1 => 'two'
2 => &#039;three&#039;</pre> 2 => 'three'</pre>
<pre><b>Var6:</b> &#039;key&#039; => &#039;value&#039; <pre><b>Var6:</b> 'key' => 'value'
&#039;key2&#039; => &#039;value2&#039;</pre> 'key2' => 'value2'</pre>
<pre><b>Var7:</b> me as string</pre> <pre><b>Var7:</b> me as string</pre>
<pre><b>Var8:</b> &#039;value&#039;, &#039;value2&#039;</pre> <pre><b>Var8:</b> 'value', 'value2'</pre>
EOT EOT
); );
@@ -64,10 +64,10 @@ EOT
public function testDebugArray() public function testDebugArray()
{ {
$this->expectOutputString(<<<EOT $this->expectOutputString(<<<EOT
<pre><b>Arr0:</b> &#039;null&#039;</pre> <pre><b>Arr0:</b> 'null'</pre>
<pre><b>Arr1:</b> &#039;key1&#039; => &#039;val1&#039; <pre><b>Arr1:</b> 'key1' => 'val1'
&#039;key2&#039; => &#039;val2&#039; 'key2' => 'val2'
&#039;key3&#039; => &#039;val3&#039;</pre> 'key3' => 'val3'</pre>
EOT EOT
); );
@@ -93,12 +93,12 @@ EOT
<th><small>1</small></th> <th><small>1</small></th>
</tr> </tr>
<tr> <tr>
<td><pre>&#039;one&#039;</pre></td> <td><pre>'one'</pre></td>
<td><pre>&#039;two&#039;</pre></td> <td><pre>'two'</pre></td>
</tr> </tr>
<tr> <tr>
<td><pre>&#039;three&#039;</pre></td> <td><pre>'three'</pre></td>
<td><pre>&#039;four&#039;</pre></td> <td><pre>'four'</pre></td>
</tr> </tr>
</table> </table>
<b>Table4:</b> <b>Table4:</b>
@@ -109,9 +109,9 @@ EOT
<th><small>key3</small></th> <th><small>key3</small></th>
</tr> </tr>
<tr> <tr>
<td><pre>&#039;val1&#039;</pre></td> <td><pre>'val1'</pre></td>
<td><pre>&#039;val2&#039;</pre></td> <td><pre>'val2'</pre></td>
<td><pre>&#039;val3&#039;</pre></td> <td><pre>'val3'</pre></td>
</tr> </tr>
</table> </table>
@@ -147,18 +147,18 @@ EOT
</tr> </tr>
<tr> <tr>
<td><pre>group1</pre></td> <td><pre>group1</pre></td>
<td><pre>&#039;val1&#039;</pre></td> <td><pre>'val1'</pre></td>
<td><pre>&#039;val2&#039;</pre></td> <td><pre>'val2'</pre></td>
</tr> </tr>
<tr> <tr>
<td><pre>group1</pre></td> <td><pre>group1</pre></td>
<td><pre>&#039;one&#039;</pre></td> <td><pre>'one'</pre></td>
<td><pre>&#039;two&#039;</pre></td> <td><pre>'two'</pre></td>
</tr> </tr>
<tr> <tr>
<td><pre>group2</pre></td> <td><pre>group2</pre></td>
<td><pre>&#039;val1&#039;</pre></td> <td><pre>'val1'</pre></td>
<td><pre>&#039;val2&#039;</pre></td> <td><pre>'val2'</pre></td>
</tr> </tr>
</table> </table>
<b>Table4:</b> <b>Table4:</b>
@@ -171,15 +171,15 @@ EOT
</tr> </tr>
<tr> <tr>
<td><pre>group1</pre></td> <td><pre>group1</pre></td>
<td><pre>&#039;val1&#039;</pre></td> <td><pre>'val1'</pre></td>
<td><pre>&#039;val2&#039;</pre></td> <td><pre>'val2'</pre></td>
<td><pre>&#039;val3&#039;</pre></td> <td><pre>'val3'</pre></td>
</tr> </tr>
<tr> <tr>
<td><pre>group1</pre></td> <td><pre>group1</pre></td>
<td><pre>&#039;val1&#039;</pre></td> <td><pre>'val1'</pre></td>
<td><pre>&#039;val2&#039;</pre></td> <td><pre>'val2'</pre></td>
<td><pre>&#039;val3&#039;</pre></td> <td><pre>'val3'</pre></td>
</tr> </tr>
</table> </table>

View File

@@ -24,14 +24,10 @@ class CaptureGetUrl:
return '<xml></xml>' return '<xml></xml>'
@pytest.fixture(autouse=True) def test_import_osm_file_simple(table_factory, osm2pgsql_options, capfd):
def setup_delete_postprocessing(temp_db_cursor): table_factory('place', content=((1, ), ))
temp_db_cursor.execute("""CREATE OR REPLACE FUNCTION flush_deleted_places()
RETURNS INTEGER AS $$ SELECT 1 $$ LANGUAGE SQL""")
def test_import_osm_file_simple(dsn, table_factory, osm2pgsql_options, capfd): assert add_osm_data.add_data_from_file(Path('change.osm'), osm2pgsql_options) == 0
assert add_osm_data.add_data_from_file(dsn, Path('change.osm'), osm2pgsql_options) == 0
captured = capfd.readouterr() captured = capfd.readouterr()
assert '--append' in captured.out assert '--append' in captured.out
@@ -45,11 +41,11 @@ def test_import_osm_file_simple(dsn, table_factory, osm2pgsql_options, capfd):
@pytest.mark.parametrize("osm_type", ['node', 'way', 'relation']) @pytest.mark.parametrize("osm_type", ['node', 'way', 'relation'])
@pytest.mark.parametrize("main_api,url", [(True, 'https://www.openstreetmap.org/api'), @pytest.mark.parametrize("main_api,url", [(True, 'https://www.openstreetmap.org/api'),
(False, 'https://overpass-api.de/api/interpreter?')]) (False, 'https://overpass-api.de/api/interpreter?')])
def test_import_osm_object_main_api(dsn, osm2pgsql_options, monkeypatch, def test_import_osm_object_main_api(osm2pgsql_options, monkeypatch, capfd,
capfd, osm_type, main_api, url): osm_type, main_api, url):
get_url_mock = CaptureGetUrl(monkeypatch) get_url_mock = CaptureGetUrl(monkeypatch)
add_osm_data.add_osm_object(dsn, osm_type, 4536, main_api, osm2pgsql_options) add_osm_data.add_osm_object(osm_type, 4536, main_api, osm2pgsql_options)
captured = capfd.readouterr() captured = capfd.readouterr()
assert get_url_mock.url.startswith(url) assert get_url_mock.url.startswith(url)

View File

@@ -48,7 +48,7 @@ def test_refresh_import_wikipedia(dsn, src_dir, table_factory, temp_db_cursor, r
def test_recompute_importance(placex_table, table_factory, temp_db_conn, temp_db_cursor): def test_recompute_importance(placex_table, table_factory, temp_db_conn, temp_db_cursor):
temp_db_cursor.execute("""CREATE OR REPLACE FUNCTION compute_importance(extratags HSTORE, temp_db_cursor.execute("""CREATE OR REPLACE FUNCTION compute_importance(extratags HSTORE,
country_code varchar(2), country_code varchar(2),
rank_search SMALLINT, osm_type varchar(1), osm_id BIGINT,
centroid GEOMETRY, centroid GEOMETRY,
OUT importance FLOAT, OUT importance FLOAT,
OUT wikipedia TEXT) OUT wikipedia TEXT)

View File

@@ -28,7 +28,7 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
postgresql-10-postgis-2.4 \ postgresql-10-postgis-2.4 \
postgresql-contrib-10 postgresql-10-postgis-scripts \ postgresql-contrib-10 postgresql-10-postgis-scripts \
php-cli php-pgsql php-intl libicu-dev python3-pip \ php-cli php-pgsql php-intl libicu-dev python3-pip \
python3-psutil python3-jinja2 python3-yaml python3-icu git python3-psutil python3-jinja2 python3-yaml python3-icu
# Some of the Python packages that come with Ubuntu 18.04 are too old, so # Some of the Python packages that come with Ubuntu 18.04 are too old, so
# install the latest version from pip: # install the latest version from pip:
@@ -105,25 +105,18 @@ fi #DOCS:
# #
if [ "x$1" == "xyes" ]; then #DOCS: :::sh if [ "x$1" == "xyes" ]; then #DOCS: :::sh
cd $USERHOME cd $USERHOME
git clone --recursive https://github.com/openstreetmap/Nominatim.git wget https://nominatim.org/release/Nominatim-4.2.4.tar.bz2
cd Nominatim tar xf Nominatim-4.2.4.tar.bz2
else #DOCS: else #DOCS:
cd $USERHOME/Nominatim #DOCS: cd $USERHOME/Nominatim #DOCS:
fi #DOCS: fi #DOCS:
# When installing the latest source from github, you also need to
# download the country grid:
if [ ! -f data/country_osm_grid.sql.gz ]; then #DOCS: :::sh
wget -O data/country_osm_grid.sql.gz https://nominatim.org/data/country_grid.sql.gz
fi #DOCS:
# The code must be built in a separate directory. Create this directory, # The code must be built in a separate directory. Create this directory,
# then configure and build Nominatim in there: # then configure and build Nominatim in there:
mkdir $USERHOME/build mkdir $USERHOME/build
cd $USERHOME/build cd $USERHOME/build
cmake $USERHOME/Nominatim cmake $USERHOME/Nominatim-4.2.4
make make
sudo make install sudo make install

View File

@@ -28,7 +28,7 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
postgresql-contrib-12 postgresql-12-postgis-3-scripts \ postgresql-contrib-12 postgresql-12-postgis-3-scripts \
php-cli php-pgsql php-intl libicu-dev python3-dotenv \ php-cli php-pgsql php-intl libicu-dev python3-dotenv \
python3-psycopg2 python3-psutil python3-jinja2 \ python3-psycopg2 python3-psutil python3-jinja2 \
python3-icu python3-datrie python3-yaml git python3-icu python3-datrie python3-yaml
# #
# System Configuration # System Configuration
@@ -99,25 +99,18 @@ fi #DOCS:
# #
if [ "x$1" == "xyes" ]; then #DOCS: :::sh if [ "x$1" == "xyes" ]; then #DOCS: :::sh
cd $USERHOME cd $USERHOME
git clone --recursive https://github.com/openstreetmap/Nominatim.git wget https://nominatim.org/release/Nominatim-4.2.4.tar.bz2
cd Nominatim tar xf Nominatim-4.2.4.tar.bz2
else #DOCS: else #DOCS:
cd $USERHOME/Nominatim #DOCS: cd $USERHOME/Nominatim #DOCS:
fi #DOCS: fi #DOCS:
# When installing the latest source from github, you also need to
# download the country grid:
if [ ! -f data/country_osm_grid.sql.gz ]; then #DOCS: :::sh
wget -O data/country_osm_grid.sql.gz https://nominatim.org/data/country_grid.sql.gz
fi #DOCS:
# The code must be built in a separate directory. Create this directory, # The code must be built in a separate directory. Create this directory,
# then configure and build Nominatim in there: # then configure and build Nominatim in there:
mkdir $USERHOME/build mkdir $USERHOME/build
cd $USERHOME/build cd $USERHOME/build
cmake $USERHOME/Nominatim cmake $USERHOME/Nominatim-4.2.4
make make
sudo make install sudo make install

View File

@@ -28,7 +28,7 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
postgresql-contrib-14 postgresql-14-postgis-3-scripts \ postgresql-contrib-14 postgresql-14-postgis-3-scripts \
php-cli php-pgsql php-intl libicu-dev python3-dotenv \ php-cli php-pgsql php-intl libicu-dev python3-dotenv \
python3-psycopg2 python3-psutil python3-jinja2 \ python3-psycopg2 python3-psutil python3-jinja2 \
python3-icu python3-datrie git python3-icu python3-datrie
# #
# System Configuration # System Configuration
@@ -99,25 +99,18 @@ fi #DOCS:
# #
if [ "x$1" == "xyes" ]; then #DOCS: :::sh if [ "x$1" == "xyes" ]; then #DOCS: :::sh
cd $USERHOME cd $USERHOME
git clone --recursive https://github.com/openstreetmap/Nominatim.git wget https://nominatim.org/release/Nominatim-4.2.4.tar.bz2
cd Nominatim tar xf Nominatim-4.2.4.tar.bz2
else #DOCS: else #DOCS:
cd $USERHOME/Nominatim #DOCS: cd $USERHOME/Nominatim #DOCS:
fi #DOCS: fi #DOCS:
# When installing the latest source from github, you also need to
# download the country grid:
if [ ! -f data/country_osm_grid.sql.gz ]; then #DOCS: :::sh
wget -O data/country_osm_grid.sql.gz https://nominatim.org/data/country_grid.sql.gz
fi #DOCS:
# The code must be built in a separate directory. Create this directory, # The code must be built in a separate directory. Create this directory,
# then configure and build Nominatim in there: # then configure and build Nominatim in there:
mkdir $USERHOME/build mkdir $USERHOME/build
cd $USERHOME/build cd $USERHOME/build
cmake $USERHOME/Nominatim cmake $USERHOME/Nominatim-4.2.4
make make
sudo make install sudo make install