Compare commits

..

21 Commits

Author SHA1 Message Date
Sarah Hoffmann
212b69799c adapt docs for release 2025-10-29 11:08:42 +01:00
Sarah Hoffmann
9a13b62fb9 prepare release 5.2.0 2025-10-29 10:01:30 +01:00
Sarah Hoffmann
3ecda751c4 Merge pull request #3861 from lonvia/force-extra-tags
Force inclusion of extra tags when Nominatim internally depends on them
2025-10-29 08:51:40 +01:00
Sarah Hoffmann
5d4c29b84b force inclusion of extratags used directly by Nominatim 2025-10-28 17:20:17 +01:00
Sarah Hoffmann
f1fbc04f33 harmonize use of callback with set_entrance_filter
All other functions except a simple function, so do this here as well.
2025-10-28 14:33:45 +01:00
Sarah Hoffmann
353c985b9f Merge pull request #3859 from lonvia/fix-entrance-addresses
Move entrances to a separate table
2025-10-24 13:38:21 +02:00
Sarah Hoffmann
2dda9079f0 add BDD tests for importing into the new place_entrance table 2025-10-24 10:52:25 +02:00
Sarah Hoffmann
4c91a0bc8d fix syntax in presets 2025-10-24 09:43:06 +02:00
Sarah Hoffmann
31c8ec6db0 add documentation for entrance table configuration 2025-10-23 20:53:59 +02:00
Sarah Hoffmann
e2330ff4c1 add migration for separate entrance table 2025-10-23 17:25:20 +02:00
Sarah Hoffmann
589825d37e adapt tests for extra place_entrance table 2025-10-23 17:25:20 +02:00
Sarah Hoffmann
a93113bc44 use extra place_entrance table 2025-10-23 17:25:20 +02:00
Sarah Hoffmann
b042eca382 move entrances into extra table 2025-10-23 17:25:20 +02:00
Sarah Hoffmann
d202a8f7d8 Merge pull request #3857 from lonvia/leisure-garden
Be more conservative when including leisure=garden/commons
2025-10-22 14:03:19 +02:00
Sarah Hoffmann
af6386bd68 move some leisure features into manmade layer 2025-10-22 11:27:25 +02:00
Sarah Hoffmann
862bfdf6fb correct default values for layer on reverse 2025-10-22 11:27:25 +02:00
Sarah Hoffmann
28029edc8b exclude unamed gardens and commons
These are mostly private gardens and small streches of green
not of interest for a general search.

ddd
2025-10-22 11:26:58 +02:00
Sarah Hoffmann
d6e9196177 Merge pull request #3855 from hasandiwan/master
force layer to be address
2025-10-22 11:24:40 +02:00
Hasan Diwan
e0a750e089 force layer to be address 2025-10-22 07:55:10 +00:00
Sarah Hoffmann
93b2a0f194 update CI to test against PostgreSQL 18 2025-10-20 18:50:30 +02:00
Sarah Hoffmann
aa3fce6852 correct default setting for addressdetails parameter in lookup
Fixes #3850.
2025-10-11 09:20:10 +02:00
31 changed files with 675 additions and 192 deletions

View File

@@ -47,7 +47,7 @@ jobs:
python: '3.9'
- flavour: ubuntu-24
ubuntu: 24
postgresql: 17
postgresql: 18
lua: '5.3'
dependencies: apt
python: 'builtin'
@@ -326,7 +326,7 @@ jobs:
- uses: ./Nominatim/.github/actions/setup-postgresql
with:
postgresql-version: 17
postgresql-version: 18
- name: Install Python dependencies
run: |

View File

@@ -1,3 +1,39 @@
5.2.0
* increase minimum required Python to 3.9
* index and output entrances of buildings and areas (thanks @emlove)
* name tags used for creating display names are now configurable
(thanks @astridx)
* new pattern-replacement query preprocessor (thanks @TuringVerified)
* special phrases can now be filtered by presence of tags (thanks @anqixxx)
* lua import style now always includes tags required by Nominatim
* improved query time reporting and logging
* improve word matching for languages with no word boundaries
* POIs with addresses inherited from surrounding building are no
longer returned in the address layer
* avoid creating a directory for the tokenizer when not needed
* replace behave with pytest-bdd for BDD testing
* refactoring and performance improvements to query parsing
* various smaller updates to styles
* remove English as default language for South Korea
* remove Japanese word variants
* updated country names for Norwegians (thanks @Johannes-Andersen)
* remove support for deprecated osm2pgsql gazetteer style
* fix updating of importances (also needs to update search_name table)
* fix query for deletable endpoint to use index again
* fix reindexing of contained places when a boundary is deleted and reinstated
* fix difference computation error when updating postcodes
* bracket handling sanitizer no longer strips bracket terms in the middle of
name
* reduce precision of stored coordinates to 7-digits everywhere
* avoid ST_Relate as it seems buggy on some systems
* remove setting for logging queries in DB, no longer functional
* postcode updates no longer require a project directory (needed for tests)
* refactor locale handling code (thanks @anqixxx)
* code updates for newer Python (thanks @emmanuel-ferdman)
* better test coverage (thanks @asharmalik19)
* various fixes and improvements to documentation
(thanks @anqixxx, @dave-meyer, @hasandiwan)
5.1.0
* replace datrie with simple internal trie implementation
* add pattern-based postcode parser for queries,

View File

@@ -9,11 +9,11 @@ versions.
| Version | End of support for security updates |
| ------- | ----------------------------------- |
| 5.2.x | 2027-10-29 |
| 5.1.x | 2027-04-01 |
| 5.0.x | 2027-02-06 |
| 4.5.x | 2026-09-12 |
| 4.4.x | 2026-03-07 |
| 4.3.x | 2025-09-07 |
## Reporting a Vulnerability
@@ -32,8 +32,7 @@ description of the nature and severity of the issue. **
Patches for identified security issues are applied to all affected versions and
new minor versions are released. At the same time we release a statement at
the [Nominatim blog](https://nominatim.org/blog/) describing the nature of the
incident. Announcements will also be published at the
[geocoding mailinglist](https://lists.openstreetmap.org/listinfo/geocoding).
incident.
## List of Previous Incidents

View File

@@ -110,14 +110,17 @@ Then you can install Nominatim with:
pip install nominatim-db nominatim-api
## Downloading and building Nominatim
## Downloading and building Nominatim from source
The following instructions are only relevant, if you want to build and
install Nominatim **from source**.
### Downloading the latest release
You can download the [latest release from nominatim.org](https://nominatim.org/downloads/).
The release contains all necessary files. Just unpack it.
### Downloading the latest development version
### Downloading the source for the latest development version
If you want to install latest development version from github:
@@ -131,7 +134,7 @@ The development version does not include the country grid. Download it separatel
wget -O Nominatim/data/country_osm_grid.sql.gz https://nominatim.org/data/country_grid.sql.gz
```
### Building Nominatim
### Building Nominatim from source
Nominatim is easiest to run from its own virtual environment. To create one, run:

View File

@@ -17,6 +17,18 @@ breaking changes. **Please read them before running the migration.**
and migrate to 4.3 first. Then you can migrate to the current
version. It is strongly recommended to do a reimport instead.
## 5.1.0 -> 5.2.0
### Lua import style: required extratags removed
Tags that are required by Nominatim as extratags are now always included
independent of what is defined in the style. The line
flex.add_for_extratags('required')
is no longer required in custom styles and will throw an error. Simply
remove the line from your style.
## 4.5.0 -> 5.0.0
### PHP frontend removed

View File

@@ -49,7 +49,7 @@ Only has an effect for JSON output formats.
| Parameter | Value | Default |
|-----------| ----- | ------- |
| addressdetails | 0 or 1 | 0 |
| addressdetails | 0 or 1 | 1 |
When set to 1, include a breakdown of the address into elements.
The exact content of the address breakdown depends on the output format.

View File

@@ -152,7 +152,7 @@ In terms of address details the zoom levels are as follows:
| Parameter | Value | Default |
|-----------| ----- | ------- |
| layer | comma-separated list of: `address`, `poi`, `railway`, `natural`, `manmade` | _unset_ (no restriction) |
| layer | comma-separated list of: `address`, `poi`, `railway`, `natural`, `manmade` | `address,poi` |
The layer filter allows to select places by themes.
@@ -218,7 +218,7 @@ This overrides the specified machine readable format.
## Examples
* [https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1](https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1)
* [https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1&layer=address](https://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1&layer=address)
```xml
<reversegeocode timestamp="Fri, 06 Nov 09 16:33:54 +0000" querystring="...">
@@ -241,7 +241,7 @@ This overrides the specified machine readable format.
##### Example with `format=jsonv2`
* [https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521](https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521)
* [https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521&layer=address](https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=-34.44076&lon=-58.70521&layer=address)
```json
{
@@ -273,7 +273,7 @@ This overrides the specified machine readable format.
##### Example with `format=geojson`
* [https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989](https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989)
* [https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989&layer=address](https://nominatim.openstreetmap.org/reverse?format=geojson&lat=44.50155&lon=11.33989&layer=address)
```json
{
@@ -325,7 +325,7 @@ This overrides the specified machine readable format.
##### Example with `format=geocodejson`
[https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663](https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663)
[https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663&layer=address](https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663&layer=address)
```json
{

View File

@@ -68,15 +68,16 @@ When Nominatim processes an OSM object, it looks for four kinds of tags:
The _main tags_ classify what kind of place the OSM object represents. One
OSM object can have more than one main tag. In such case one database entry
is created for each main tag. _Name tags_ represent searchable names of the
place. _Address tags_ are used to compute the address hierarchy of the place.
place. _Address tags_ are used to compute the address information of the place.
Address tags are used for searching and for creating a display name of the place.
_Extra tags_ are any tags that are not directly related to search but
contain interesting additional information.
contain interesting additional information. These are just saved in the database
and may be returned with the result [on request](../api/Search.md#output-details).
!!! danger
Some tags in the extratags category are used by Nominatim to better
classify the place. You want to make sure these are always present
in custom styles.
classify the place. These tags will always be added, independent of
any settings in the style.
Configuring the style means deciding which key and/or key/value is used
in which category.
@@ -266,11 +267,7 @@ in turn take precedence over prefix matches.
##### Presets
| Name | Description |
| :----- | :---------- |
| required | Tags that Nominatim will use for various computations when present in extratags. Always include these. |
In addition, all [presets from ignored tags](#presets_1) are accepted.
Accepts all [presets from ignored tags](#presets_1).
### General pre-filtering
@@ -426,6 +423,56 @@ is added for extratags.
already delete the tiger tags with `set_prefilters()` because that
would remove tiger:county before the address tags are processed.
## Filling additional tables
Most of the OSM objects are saved in the main `place` table for further
processing. In addition to that, there are some smaller tables that save
specialised information. The content of these tables can be customized as
well.
### Entrance table
The table `place_entrance` saves information about OSM nodes that represent
an entrance. This data is later mingled with buildings and other areas and
can be returned [on request](../api/Search.md#output-details). The table
saves the type of entrance as well as a set of custom extra tags.
The function `set_entrance_filter()` can be used to customize the table's
content.
When called without any parameter, then filling the entrance table will be
disabled. When called with a preset name, the appropriate preset will be
applied.
To create a custom configuration, call the function
with a table with the following fields:
* __main_tags__ is a list of tags that mark an entrance node. The value of the
first tag found in the list will be used as the entrance type.
* __extra_include__ is an optional list of tags to be added to the extratags
for this entrance. When left out, all tags except for the ones defined
in 'main_tags' will be included. To disable saving of extra tags, set
this to the empty list.
* __extra_exclude__ defines an optional list of tags to drop before including
the remaining tags as extratags. Note that the tags defined in 'main_tags'
will always be excluded, independently of this setting.
To have even more fine-grained control over the output, you can also hand
in a callback for processing entrance information. The callback function
receives a single parameter, the
[osm2pgsql object](https://osm2pgsql.org/doc/manual.html#processing-callbacks).
This object itself must not be modified. The callback should return either
`nil` when the object is not an entrance. Or it returns a table with a
mandatory `entrance` field containing a string with the type of entrance
and an optional `extratags` field with a simple key-value table of extra
information.
##### Presets
| Name | Description |
| :----- | :---------- |
| default | Standard configuration used with `full` and `extratags` styles. |
## Customizing osm2pgsql callbacks
osm2pgsql expects the flex style to implement three callbacks, one process

View File

@@ -29,7 +29,9 @@ local NAME_FILTER = nil
local ADDRESS_TAGS = {}
local ADDRESS_FILTER = nil
local EXTRATAGS_FILTER
local REQUIRED_EXTRATAGS_FILTER
local POSTCODE_FALLBACK = true
local ENTRANCE_FUNCTION = nil
-- This file can also be directly require'd instead of running it under
-- the themepark framework. In that case the first parameter is usually
@@ -40,37 +42,51 @@ if type(themepark) ~= 'table' then
themepark = nil
end
-- The single place table.
local place_table_definition = {
name = "place",
ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
columns = {
{ column = 'class', type = 'text', not_null = true },
{ column = 'type', type = 'text', not_null = true },
{ column = 'admin_level', type = 'smallint' },
{ column = 'name', type = 'hstore' },
{ column = 'address', type = 'hstore' },
{ column = 'extratags', type = 'hstore' },
{ column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true },
-- The place tables carry the raw OSM information.
local table_definitions = {
place = {
ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
columns = {
{ column = 'class', type = 'text', not_null = true },
{ column = 'type', type = 'text', not_null = true },
{ column = 'admin_level', type = 'smallint' },
{ column = 'name', type = 'hstore' },
{ column = 'address', type = 'hstore' },
{ column = 'extratags', type = 'hstore' },
{ column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true },
},
indexes = {}
},
data_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_DATA"),
index_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_INDEX"),
indexes = {}
place_entrance = {
ids = { type = 'node', id_column = 'osm_id' },
columns = {
{ column = 'type', type = 'text', not_null = true },
{ column = 'extratags', type = 'hstore' },
{ column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true }
},
indexes = {}
}
}
local insert_row
local insert_row = {}
local script_path = debug.getinfo(1, "S").source:match("@?(.*/)")
local PRESETS = loadfile(script_path .. 'presets.lua')()
if themepark then
themepark:add_table(place_table_definition)
insert_row = function(columns)
themepark:insert('place', columns, {}, {})
end
else
local place_table = osm2pgsql.define_table(place_table_definition)
insert_row = function(columns)
place_table:insert(columns)
for table_name, table_definition in pairs(table_definitions) do
table_definition.name = table_name
table_definition.data_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_DATA")
table_definition.index_tablespace = os.getenv("NOMINATIM_TABLESPACE_PLACE_INDEX")
if themepark then
themepark:add_table(table_definition)
insert_row[table_name] = function(columns)
themepark:insert(table_name, columns, {}, {})
end
else
local place_table = osm2pgsql.define_table(table_definition)
insert_row[table_name] = function(columns)
place_table:insert(columns)
end
end
end
@@ -150,24 +166,6 @@ local function address_fallback(place)
return place:clone{names=names}
end
--------- Built-in extratags transformation functions ---------------
local function default_extratags_filter(p, k)
-- Default handling is to copy over place tag for boundaries.
-- Nominatim needs this.
if k ~= 'boundary' or p.intags.place == nil then
return p.extratags
end
local extra = { place = p.intags.place }
for kin, vin in pairs(p.extratags) do
extra[kin] = vin
end
return extra
end
EXTRATAGS_FILTER = default_extratags_filter
----------------- other helper functions -----------------------------
local function lookup_prefilter_classification(k, v)
@@ -429,18 +427,25 @@ function Place:write_row(k, v)
return 0
end
local extratags = EXTRATAGS_FILTER(self, k, v)
if not (extratags and next(extratags)) then
extratags = nil
end
local extra = EXTRATAGS_FILTER(self, k, v) or {}
insert_row{
for tk, tv in pairs(self.object.tags) do
if REQUIRED_EXTRATAGS_FILTER(tk, tv) and extra[tk] == nil then
extra[tk] = tv
end
end
if extra and next(extra) == nil then
extra = nil
end
insert_row.place{
class = k,
type = v,
admin_level = self.admin_level,
name = next(self.names) and self.names,
address = next(self.address) and self.address,
extratags = extratags,
extratags = extra,
geometry = self.geometry
}
@@ -593,6 +598,16 @@ end
-- Process functions for all data types
function module.process_node(object)
if ENTRANCE_FUNCTION ~= nil then
local entrance_info = ENTRANCE_FUNCTION(object)
if entrance_info ~= nil then
insert_row.place_entrance{
type = entrance_info.entrance,
extratags = entrance_info.extratags,
geometry = object:as_point()
}
end
end
local function geom_func(o)
return o:as_point()
@@ -687,6 +702,15 @@ function module.process_tags(o)
end
end
--------- Extratags post-processing functions ---------------
local function default_extratags_filter(p, k)
return p.extratags
end
EXTRATAGS_FILTER = default_extratags_filter
REQUIRED_EXTRATAGS_FILTER = module.tag_match(PRESETS.EXTRATAGS)
--------- Convenience functions for simple style configuration -----------------
function module.set_prefilters(data)
@@ -717,7 +741,7 @@ end
function module.add_for_extratags(data)
if type(data) == 'string' then
local preset = data
data = PRESETS.EXTRATAGS[data] or PRESETS.IGNORE_KEYS[data]
data = PRESETS.IGNORE_KEYS[data]
if data == nil then
error('Unknown preset for extratags: ' .. preset)
end
@@ -917,6 +941,99 @@ function module.set_relation_types(data)
end
end
function module.set_entrance_filter(data)
if data == nil or type(data) == 'function' then
ENTRANCE_FUNCTION = data
return nil
end
if type(data) == 'string' then
local preset = data
data = PRESETS.ENTRANCE_TABLE[data]
if data == nil then
error('Unknown preset for entrance table: ' .. preset)
end
end
ENTRANCE_FUNCTION = nil
if data.main_tags ~= nil and next(data.main_tags) ~= nil then
if data.extra_include ~= nil and next(data.extra_include) == nil then
-- shortcut: no extra tags requested
ENTRANCE_FUNCTION = function(o)
for _, v in ipairs(data.main_tags) do
if o.tags[v] ~= nil then
return {entrance = o.tags[v]}
end
end
return nil
end
else
if data.extra_include ~= nil then
local tags = {}
for _, v in pairs(data.extra_include) do
tags[v] = true
end
if data.extra_exclude ~= nil then
for _, v in pairs(data.extra_exclude) do
tags[v] = nil
end
end
for _, v in pairs(data.main_tags) do
tags[v] = nil
end
ENTRANCE_FUNCTION = function(o)
for _, v in ipairs(data.main_tags) do
if o.tags[v] ~= nil then
local entrance = o.tags[v]
local extra = {}
for k, v in pairs(tags) do
extra[k] = o.tags[k]
end
if next(extra) == nil then
extra = nil
end
return {entrance = entrance, extratags = extra}
end
end
return nil
end
else
local notags = {}
if data.extra_exclude ~= nil then
for _, v in pairs(data.extra_exclude) do
notags[v] = 1
end
end
for _, v in pairs(data.main_tags) do
notags[v] = 1
end
ENTRANCE_FUNCTION = function(o)
for _, v in ipairs(data.main_tags) do
if o.tags[v] ~= nil then
local entrance = o.tags[v]
local extra = {}
for k, v in pairs(o.tags) do
if notags[k] ~= 1 then
extra[k] = v
end
end
if next(extra) == nil then
extra = nil
end
return {entrance = entrance, extratags = extra}
end
end
return nil
end
end
end
end
end
function module.get_taginfo()
return {main = MAIN_KEYS, name = NAMES, address = ADDRESS_TAGS}

View File

@@ -172,10 +172,6 @@ module.MAIN_TAGS_POIS = function (group)
no = group,
yes = group,
fire_hydrant = group},
entrance = {'always',
no = group},
["routing:entrance"] = {exclude_when_key_present('entrance'),
no = group},
healthcare = {'fallback',
yes = group,
no = group},
@@ -204,6 +200,8 @@ module.MAIN_TAGS_POIS = function (group)
leisure = {'always',
nature_reserve = 'fallback',
swimming_pool = 'named',
garden = 'named',
common = 'named',
no = group},
lock = {yes = lock_transform},
man_made = {pier = 'always',
@@ -377,10 +375,15 @@ module.IGNORE_KEYS.address = {'addr:street:*', 'addr:city:*', 'addr:district:*',
'addr:province:*', 'addr:subdistrict:*', 'addr:place:*',
'addr:TW:dataset'}
-- Extra tags (prefiltered away)
-- INTERNAL: Required extra tags
module.EXTRATAGS = {}
module.EXTRATAGS = {keys = {'wikipedia', 'wikipedia:*', 'wikidata', 'capital'}}
module.EXTRATAGS.required = {'wikipedia', 'wikipedia:*', 'wikidata', 'capital'}
-- Defaults for the entrance table
module.ENTRANCE_TABLE = {}
module.ENTRANCE_TABLE.default = {main_tags = {'entrance', 'routing:entrance'},
extra_exclude = module.IGNORE_KEYS.metatags}
return module

View File

@@ -11,7 +11,6 @@ flex.set_address_tags('core')
flex.modify_address_tags('houses')
flex.ignore_keys('metatags')
flex.add_for_extratags('required')
if cfg.with_extratags then
flex.set_unused_handling{delete_keys = {'tiger:*'}}

View File

@@ -8,7 +8,6 @@ flex.set_address_tags('core')
flex.set_postcode_fallback(false)
flex.ignore_keys('metatags')
flex.add_for_extratags('required')
if cfg.with_extratags then
flex.set_unused_handling{delete_keys = {'tiger:*'}}

View File

@@ -20,7 +20,6 @@ flex.set_address_tags('core')
flex.modify_address_tags('houses')
flex.ignore_keys('metatags')
flex.add_for_extratags('required')
if cfg.with_extratags then
flex.set_unused_handling{delete_keys = {'tiger:*'}}
@@ -30,3 +29,5 @@ else
flex.ignore_keys('name')
flex.ignore_keys('address')
end
flex.set_entrance_filter('default')

View File

@@ -10,7 +10,6 @@ flex.set_address_tags('core')
flex.set_postcode_fallback(false)
flex.ignore_keys('metatags')
flex.add_for_extratags('required')
if cfg.with_extratags then
flex.set_unused_handling{delete_keys = {'tiger:*'}}

View File

@@ -683,11 +683,6 @@ DECLARE
BEGIN
{% if debug %}RAISE WARNING '% % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type;{% endif %}
IF NEW.class IN ('routing:entrance', 'entrance') THEN
-- We don't need entrance nodes in the placex table.
RETURN NULL;
END IF;
NEW.place_id := nextval('seq_place');
NEW.indexed_status := 1; --STATUS_NEW

View File

@@ -634,10 +634,8 @@ DECLARE
BEGIN
osm_ids := '{}';
FOR entrance in SELECT osm_id, type, geometry, extratags
FROM place
WHERE osm_type = 'N'
AND osm_id IN (SELECT unnest(nodes) FROM planet_osm_ways WHERE id=osmid)
AND class IN ('routing:entrance', 'entrance')
FROM place_entrance
WHERE osm_id IN (SELECT unnest(nodes) FROM planet_osm_ways WHERE id=osmid)
LOOP
osm_ids := array_append(osm_ids, entrance.osm_id);
INSERT INTO placex_entrance (place_id, osm_id, type, location, extratags)

View File

@@ -1,4 +1,4 @@
site_name: Nominatim Manual
site_name: Nominatim 5.2.0 Manual
theme:
font: false
name: material

View File

@@ -58,7 +58,10 @@
"" : [24, 0]
},
"leisure" : {
"park" : [24, 0]
"park" : [24, 0],
"nature_reserve" : [24, 0],
"garden": [25, 0],
"common": [25, 0]
},
"natural" : {
"peak" : [18, 0],

View File

@@ -2,10 +2,10 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Version information for the Nominatim API.
"""
NOMINATIM_API_VERSION = '5.1.0'
NOMINATIM_API_VERSION = '5.2.0'

View File

@@ -120,26 +120,39 @@ def create_postcode_parent_index(conn: Connection, **_: Any) -> None:
@_migration(5, 1, 99, 0)
def create_placex_entrance_table(conn: Connection, config: Configuration, **_: Any) -> None:
""" Add the placex_entrance table to store entrance nodes
""" Add the placex_entrance table to store linked-up entrance nodes
"""
sqlp = SQLPreprocessor(conn, config)
sqlp.run_string(conn, """
-- Table to store location of entrance nodes
DROP TABLE IF EXISTS placex_entrance;
CREATE TABLE placex_entrance (
place_id BIGINT NOT NULL,
osm_id BIGINT NOT NULL,
type TEXT NOT NULL,
location GEOMETRY(Point, 4326) NOT NULL,
extratags HSTORE
);
CREATE UNIQUE INDEX idx_placex_entrance_place_id_osm_id ON placex_entrance
USING BTREE (place_id, osm_id) {{db.tablespace.search_index}};
GRANT SELECT ON placex_entrance TO "{{config.DATABASE_WEBUSER}}" ;
if not table_exists(conn, 'placex_entrance'):
sqlp = SQLPreprocessor(conn, config)
sqlp.run_string(conn, """
-- Table to store location of entrance nodes
CREATE TABLE placex_entrance (
place_id BIGINT NOT NULL,
osm_id BIGINT NOT NULL,
type TEXT NOT NULL,
location GEOMETRY(Point, 4326) NOT NULL,
extratags HSTORE
);
CREATE UNIQUE INDEX idx_placex_entrance_place_id_osm_id ON placex_entrance
USING BTREE (place_id, osm_id) {{db.tablespace.search_index}};
GRANT SELECT ON placex_entrance TO "{{config.DATABASE_WEBUSER}}" ;
""")
-- Create an index on the place table for lookups to populate the entrance
-- table
CREATE INDEX IF NOT EXISTS idx_placex_entrance_lookup ON place
USING BTREE (osm_id)
WHERE class IN ('routing:entrance', 'entrance');
""")
@_migration(5, 1, 99, 1)
def create_place_entrance_table(conn: Connection, config: Configuration, **_: Any) -> None:
""" Add the place_entrance table to store incomming entrance nodes
"""
if not table_exists(conn, 'place_entrance'):
with conn.cursor() as cur:
cur.execute("""
-- Table to store location of entrance nodes
CREATE TABLE place_entrance (
osm_id BIGINT NOT NULL,
type TEXT NOT NULL,
extratags HSTORE,
geometry GEOMETRY(Point, 4326) NOT NULL
);
CREATE UNIQUE INDEX place_entrance_osm_id_idx ON place_entrance
USING BTREE (osm_id);
""")

View File

@@ -2,7 +2,7 @@
#
# This file is part of Nominatim. (https://nominatim.org)
#
# Copyright (C) 2024 by the Nominatim developer community.
# Copyright (C) 2025 by the Nominatim developer community.
# For a full list of authors see the git log.
"""
Version information for Nominatim.
@@ -55,7 +55,7 @@ def parse_version(version: str) -> NominatimVersion:
return NominatimVersion(*[int(x) for x in parts[:2] + parts[2].split('-')])
NOMINATIM_VERSION = parse_version('5.1.99-0')
NOMINATIM_VERSION = parse_version('5.2.0-0')
POSTGRESQL_REQUIRED_VERSION = (12, 0)
POSTGIS_REQUIRED_VERSION = (3, 0)

View File

@@ -8,8 +8,10 @@ Feature: Entrance nodes are recorded
Given the places
| osm | class | type | geometry | extratags |
| W1 | building | yes | (1,2,3,4,1) | |
| N1 | entrance | main | 1 | 'wheelchair': 'yes' |
| N2 | entrance | yes | 3 | |
And the entrances
| osm | type | geometry | extratags |
| N1 | main | 1 | 'wheelchair': 'yes' |
| N2 | yes | 3 | |
And the ways
| id | nodes |
| 1 | 1,2,3,4,1 |

View File

@@ -18,7 +18,7 @@ Feature: Querying fo postcode variants
| 10 | | | | 11 |
And the places
| osm | class | type | name | addr+postcode | geometry |
| W1 | highway | path | De Weide | 3993 DX | 10,11 |
| W1 | highway | path | De Weide | <postcode> | 10,11 |
When importing
When geocoding "3993 DX"
Then result 0 contains

View File

@@ -17,10 +17,12 @@ Feature: Entrance nodes are recorded
| W1 | 1 |
Then placex_entrance contains exactly
| place_id | osm_id | type | location!wkt | extratags |
When updating places
| osm | class | type | geometry |
| N1 | entrance | main | 1 |
| W1 | building | yes | (1,2,3,4,1) |
When updating entrances
| osm | type | geometry |
| N1 | main | 1 |
And updating places
| osm | class | type | geometry |
| W1 | building | yes | (1,2,3,4,1) |
Then placex contains exactly
| object | place_id |
| W1 | 1 |
@@ -46,13 +48,15 @@ Feature: Entrance nodes are recorded
| W1 | 2 |
Then placex_entrance contains exactly
| place_id | osm_id | type | location!wkt | extratags |
When updating places
When marking for delete N1
And updating entrances
| osm | type | geometry |
| N1 | main | 1 |
And updating places
| osm | class | type | geometry |
| N1 | entrance | main | 1 |
| W1 | building | yes | (1,2,3,4,1) |
Then placex contains exactly
| object | place_id |
| N1 | 1 |
| W1 | 2 |
And placex_entrance contains exactly
| place_id | osm_id | type | location!wkt | extratags |
@@ -64,8 +68,10 @@ Feature: Entrance nodes are recorded
| 4 | 3 |
Given the places
| osm | class | type | geometry |
| N1 | entrance | main | 1 |
| W1 | building | yes | (1,2,3,4,1) |
And the entrances
| osm | type | geometry |
| N1 | main | 1 |
And the ways
| id | nodes |
| 1 | 1, 2, 3, 4, 1 |
@@ -79,7 +85,7 @@ Feature: Entrance nodes are recorded
When marking for delete N1
And updating places
| osm | class | type | geometry |
| W1 | building | yes | (2,3,4,2) |
| W1 | building | yes | (1,2,3,4,1) |
Then placex contains exactly
| object | place_id |
| W1 | 1 |
@@ -92,9 +98,11 @@ Feature: Entrance nodes are recorded
| 4 | 3 |
Given the places
| osm | class | type | geometry |
| N1 | entrance | main | 1 |
| N3 | entrance | yes | 3 |
| W1 | building | yes | (1,2,3,4,1) |
Given the entrances
| osm | type | geometry |
| N1 | main | 1 |
| N3 | yes | 3 |
And the ways
| id | nodes |
| 1 | 1, 2, 3, 4, 1 |
@@ -109,7 +117,7 @@ Feature: Entrance nodes are recorded
When marking for delete N1
And updating places
| osm | class | type | geometry |
| W1 | building | yes | (2,3,4,2) |
| W1 | building | yes | (1,2,3,4,1) |
Then placex contains exactly
| object | place_id |
| W1 | 1 |

View File

@@ -0,0 +1,124 @@
Feature: Import of entrance objects by osm2pgsql
Testing of correct setup of the entrance table
Scenario: Import simple entrance
When loading osm data
"""
n1 Tshop=sweets,entrance=yes,access=public x4.5 y-4
n2 Trouting:entrance=main x66.1 y0.1
n3 Tentrance=main,routing:entrance=foot x1 y2
n4 Thighway=bus_stop
"""
Then place contains exactly
| object | class | type |
| N1 | shop | sweets |
| N4 | highway | bus_stop |
And place_entrance contains exactly
| osm_id | type | extratags!dict | geometry!wkt |
| 1 | yes | 'shop': 'sweets', 'access': 'public' | 4.5 -4 |
| 2 | main | - | 66.1 0.1 |
| 3 | main | - | 1 2 |
Scenario: Addresses and entrance information can exist on the same node
When loading osm data
"""
n1 Taddr:housenumber=10,addr:street=North,entrance=main
"""
Then place contains exactly
| object | class | type | address+housenumber |
| N1 | place | house | 10 |
And place_entrance contains exactly
| osm_id | type |
| 1 | main |
Scenario Outline: Entrance import can be disabled
Given the lua style file
"""
local flex = require('import-full')
flex.set_entrance_filter<param>
"""
When loading osm data
"""
n1 Tentrance=yes,access=public
n2 Trouting:entrance=main
"""
Then place contains exactly
| object |
And place_entrance contains exactly
| osm_id |
Examples:
| param |
| () |
| (nil) |
| {} |
| {include={'access'}} |
| {main_tags={}} |
Scenario: Entrance import can have custom main tags
Given the lua style file
"""
local flex = require('import-full')
flex.set_entrance_filter{main_tags = {'door'}}
"""
When loading osm data
"""
n1 Tentrance=yes,access=public
n2 Tdoor=foot,entrance=yes
"""
Then place contains exactly
| object |
And place_entrance contains exactly
| osm_id | type | extratags!dict |
| 2 | foot | 'entrance': 'yes' |
Scenario: Entrance import can have custom extra tags included
Given the lua style file
"""
local flex = require('import-full')
flex.set_entrance_filter{main_tags = {'entrance'},
extra_include = {'access'}}
"""
When loading osm data
"""
n1 Tentrance=yes,access=public,shop=newspaper
n2 Tentrance=yes,shop=sweets
"""
Then place_entrance contains exactly
| osm_id | type | extratags!dict |
| 1 | yes | 'access': 'public' |
| 2 | yes | - |
Scenario: Entrance import can have custom extra tags excluded
Given the lua style file
"""
local flex = require('import-full')
flex.set_entrance_filter{main_tags = {'entrance', 'door'},
extra_exclude = {'shop'}}
"""
When loading osm data
"""
n1 Tentrance=yes,access=public,shop=newspaper
n2 Tentrance=yes,door=yes,shop=sweets
"""
Then place_entrance contains exactly
| osm_id | type | extratags!dict |
| 1 | yes | 'access': 'public' |
| 2 | yes | - |
Scenario: Entrance import can have a custom function
Given the lua style file
"""
local flex = require('import-full')
flex.set_entrance_filter(function(object)
return {entrance='always', extratags = {ref = '1'}}
end)
"""
When loading osm data
"""
n1 Tentrance=yes,access=public,shop=newspaper
n2 Tshop=sweets
"""
Then place_entrance contains exactly
| osm_id | type | extratags!dict |
| 1 | always | 'ref': '1' |
| 2 | always | 'ref': '1' |

View File

@@ -40,3 +40,26 @@ Feature: Import of simple objects by osm2pgsql
Then place contains exactly
| object | class | type |
| N1 | place | house |
Scenario Outline: Tags used by Nominatim internally are always imported
Given the lua style file
"""
local flex = require('import-<style>')
"""
When loading osm data
"""
n1 Tboundary=administrative,place=city,name=Foo,wikipedia:de=Foo
n2 Tplace=hamlet,wikidata=Q1234321,name=Bar
"""
Then place contains exactly
| object | class | extratags!dict |
| N1 | boundary | 'place': 'city', 'wikipedia:de': 'Foo' |
| N2 | place | 'wikidata': 'Q1234321' |
Examples:
| style |
| admin |
| street |
| address |
| full |
| extratags |

View File

@@ -0,0 +1,106 @@
Feature: Update of entrance objects by osm2pgsql
Testing of correct update of the entrance table
Scenario: A new entrance is added
When loading osm data
"""
n1 Tshop=shoes
"""
Then place_entrance contains exactly
| osm_id |
When updating osm data
"""
n2 Tentrance=yes
"""
Then place_entrance contains exactly
| osm_id | type |
| 2 | yes |
Scenario: An existing entrance is deleted
When loading osm data
"""
n1 Tentrance=yes
"""
Then place_entrance contains exactly
| osm_id | type |
| 1 | yes |
When updating osm data
"""
n1 dD
"""
Then place_entrance contains exactly
| osm_id |
Scenario: An existing node becomes an entrance
When loading osm data
"""
n1 Tshop=sweets
"""
Then place_entrance contains exactly
| osm_id | type |
And place contains exactly
| object | class |
| N1 | shop |
When updating osm data
"""
n1 Tshop=sweets,entrance=yes
"""
Then place_entrance contains exactly
| osm_id | type |
| 1 | yes |
And place contains exactly
| object | class |
| N1 | shop |
Scenario: An existing entrance tag is removed
When loading osm data
"""
n1 Tshop=sweets,entrance=yes
"""
Then place_entrance contains exactly
| osm_id | type |
| 1 | yes |
And place contains exactly
| object | class |
| N1 | shop |
When updating osm data
"""
n1 Tshop=sweets
"""
Then place_entrance contains exactly
| osm_id | type |
And place contains exactly
| object | class |
| N1 | shop |
Scenario: Extratags are added to an entrance
When loading osm data
"""
n1 Tentrance=yes
"""
Then place_entrance contains exactly
| osm_id | type | extratags |
| 1 | yes | - |
When updating osm data
"""
n1 Tentrance=yes,access=yes
"""
Then place_entrance contains exactly
| osm_id | type | extratags!dict |
| 1 | yes | 'access': 'yes' |
Scenario: Extratags are deleted from an entrance
When loading osm data
"""
n1 Tentrance=yes,access=yes
"""
Then place_entrance contains exactly
| osm_id | type | extratags!dict |
| 1 | yes | 'access': 'yes' |
When updating osm data
"""
n1 Tentrance=yes
"""
Then place_entrance contains exactly
| osm_id | type | extratags |
| 1 | yes | - |

View File

@@ -82,6 +82,21 @@ def import_places(db_conn, named, datatable, node_grid):
PlaceColumn(node_grid).add_row(datatable[0], row, named is not None).db_insert(cur)
@given(step_parse('the entrances'), target_fixture=None)
def import_place_entrances(db_conn, datatable, node_grid):
""" Insert todo rows into the place_entrance table.
"""
with db_conn.cursor() as cur:
for row in datatable[1:]:
data = PlaceColumn(node_grid).add_row(datatable[0], row, False)
assert data.columns['osm_type'] == 'N'
cur.execute("""INSERT INTO place_entrance (osm_id, type, extratags, geometry)
VALUES (%s, %s, %s, {})""".format(data.get_wkt()),
(data.columns['osm_id'], data.columns['type'],
data.columns.get('extratags')))
@given('the ways', target_fixture=None)
def import_ways(db_conn, datatable):
""" Import raw ways into the osm2pgsql way middle table.
@@ -151,6 +166,23 @@ def do_update(db_conn, update_config, node_grid, datatable):
return _collect_place_ids(db_conn)
@when('updating entrances', target_fixture=None)
def update_place_entrances(db_conn, datatable, node_grid):
""" Insert todo rows into the place_entrance table.
"""
with db_conn.cursor() as cur:
for row in datatable[1:]:
data = PlaceColumn(node_grid).add_row(datatable[0], row, False)
assert data.columns['osm_type'] == 'N'
cur.execute("DELETE FROM place_entrance WHERE osm_id = %s",
(data.columns['osm_id'],))
cur.execute("""INSERT INTO place_entrance (osm_id, type, extratags, geometry)
VALUES (%s, %s, %s, {})""".format(data.get_wkt()),
(data.columns['osm_id'], data.columns['type'],
data.columns.get('extratags')))
@when('updating postcodes')
def do_postcode_update(update_config):
""" Recompute the postcode centroids.
@@ -168,6 +200,9 @@ def do_delete_place(db_conn, update_config, node_grid, otype, oid):
cur.execute('DELETE FROM place WHERE osm_type = %s and osm_id = %s',
(otype, oid))
cur.execute('SELECT flush_deleted_places()')
if otype == 'N':
cur.execute('DELETE FROM place_entrance WHERE osm_id = %s',
(oid, ))
db_conn.commit()
cli.nominatim(['index', '-q'], update_config.environ)

View File

@@ -118,6 +118,17 @@ class PlaceColumn:
else:
self.columns[column] = {key: value}
def get_wkt(self):
if self.columns['osm_type'] == 'N' and self.geometry is None:
pt = self.grid.get(str(self.columns['osm_id'])) if self.grid else None
if pt is None:
pt = (random.uniform(-180, 180), random.uniform(-90, 90))
return "ST_SetSRID(ST_Point({}, {}), 4326)".format(*pt)
assert self.geometry is not None, "Geometry missing"
return self.geometry
def db_delete(self, cursor):
""" Issue a delete for the given OSM object.
"""
@@ -127,17 +138,8 @@ class PlaceColumn:
def db_insert(self, cursor):
""" Insert the collected data into the database.
"""
if self.columns['osm_type'] == 'N' and self.geometry is None:
pt = self.grid.get(str(self.columns['osm_id'])) if self.grid else None
if pt is None:
pt = (random.uniform(-180, 180), random.uniform(-90, 90))
self.geometry = "ST_SetSRID(ST_Point({}, {}), 4326)".format(*pt)
else:
assert self.geometry is not None, "Geometry missing"
query = 'INSERT INTO place ({}, geometry) values({}, {})'.format(
','.join(self.columns.keys()),
','.join(['%s' for x in range(len(self.columns))]),
self.geometry)
self.get_wkt())
cursor.execute(query, list(self.columns.values()))

View File

@@ -25,7 +25,7 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
libbz2-dev libpq-dev liblua5.3-dev lua5.3 lua-dkjson \
nlohmann-json3-dev postgresql-14-postgis-3 \
postgresql-contrib-14 postgresql-14-postgis-3-scripts \
libicu-dev virtualenv git
libicu-dev virtualenv
#
# System Configuration
@@ -97,23 +97,6 @@ fi #DOCS:
# Building and Configuration
# --------------------------
#
# Get the source code from Github and change into the source directory
#
if [ "x$1" == "xyes" ]; then #DOCS: :::sh
cd $USERHOME
git clone https://github.com/osm-search/Nominatim.git
cd Nominatim
else #DOCS:
cd $USERHOME/Nominatim #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:
# Nominatim needs osm2pgsql >= 1.8. The version that comes with Ubuntu is
# too old. Download and compile your own:
@@ -124,7 +107,6 @@ fi #DOCS:
cmake ../osm2pgsql
make
sudo make install
cd $USERHOME/Nominatim
# Nominatim should be installed in a separate Python virtual environment.
# Create the virtual environment:
@@ -137,8 +119,7 @@ fi #DOCS:
# Now install Nominatim using pip:
cd $USERHOME/Nominatim
$USERHOME/nominatim-venv/bin/pip install packaging/nominatim-db
$USERHOME/nominatim-venv/bin/pip install nominatim-db
# Nominatim is now ready to use. You can continue with
# [importing a database from OSM data](../admin/Import.md). If you want to set up
@@ -154,9 +135,7 @@ fi #DOCS:
# To install all packages, run:
#DOCS:```sh
$USERHOME/nominatim-venv/bin/pip install falcon uvicorn gunicorn
cd $USERHOME/Nominatim
$USERHOME/nominatim-venv/bin/pip install packaging/nominatim-api
$USERHOME/nominatim-venv/bin/pip install falcon uvicorn gunicorn nominatim-api
#DOCS:```

View File

@@ -21,7 +21,7 @@ export DEBIAN_FRONTEND=noninteractive #DOCS:
# Now you can install all packages needed for Nominatim:
sudo apt-get install -y osm2pgsql postgresql-postgis postgresql-postgis-scripts \
pkg-config libicu-dev virtualenv git
pkg-config libicu-dev virtualenv
#
@@ -94,23 +94,6 @@ fi #DOCS:
# Building and Configuration
# --------------------------
#
# Get the source code from Github and change into the source directory
#
if [ "x$1" == "xyes" ]; then #DOCS: :::sh
cd $USERHOME
git clone https://github.com/osm-search/Nominatim.git
cd Nominatim
else #DOCS:
cd $USERHOME/Nominatim #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:
# Nominatim should be installed in a separate Python virtual environment.
# Create the virtual environment:
@@ -122,8 +105,7 @@ fi #DOCS:
# Now install Nominatim using pip:
cd $USERHOME/Nominatim
$USERHOME/nominatim-venv/bin/pip install packaging/nominatim-db
$USERHOME/nominatim-venv/bin/pip install nominatim-db
# Nominatim is now ready to use. The nominatim binary is available at
# `$USERHOME/venv/bin/nominatim`. If you want to have 'nominatim' in your
@@ -147,9 +129,7 @@ fi #DOCS:
# To install all packages, run:
#DOCS:```sh
$USERHOME/nominatim-venv/bin/pip install falcon uvicorn gunicorn
cd $USERHOME/Nominatim
$USERHOME/nominatim-venv/bin/pip install packaging/nominatim-api
$USERHOME/nominatim-venv/bin/pip install falcon uvicorn gunicorn nominatim-api
#DOCS:```
# Next you need to create a systemd job that runs Nominatim on gunicorn.