Skip to content

Commit b064726

Browse files
committed
rewrite flex.lua and view.sql to solve index issue with negative ids
1 parent 5ede8af commit b064726

File tree

3 files changed

+88
-116
lines changed

3 files changed

+88
-116
lines changed

backends/postgres_osm2pgsql/README.md

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,56 @@ docker compose --profile '*' build
77

88
## Prepare the data
99

10-
Create you database with osm2pgsql, using the script `geometries-alone.lua` in this folder.
10+
### 1. View for a new, dedicated, updatable and complete DB
1111

12-
Example command :
12+
This view is configured to be a true alternative to overpass, in the sense that there is no filtering on the elements included in the DB. They are all included in the DB with all their tags, whereas osm2pgsql is usually configured not to include tags or elements that are useless for generating tiles.
1313

14+
Warning : this wiew will not work on an existing osm2pgsql database (see below). It is specific for a database created with the lua script above and the `-s (--slim)` option.
15+
16+
Create you database with osm2pgsql, using the script `geometries-alone.lua` available in this folder.
17+
18+
Example command:
19+
20+
```
21+
osm2pgsql -U user -d database -c -s --flat-nodes FILE --middle-with-nodes -x -O flex -S geometries-alone.lua extract.osm.pbf
22+
```
23+
24+
Explanation of the command:
25+
- `-c -s -x`: create an updatable table including metadata
26+
- `--flat-nodes FILE --middle-with-nodes`: use the file `FILE` to store nodes (to reduce the size of the DB), but nodes with tags are also stored in the database (so that filtering by tag will be possible on nodes)
27+
- `-O flex -S geometries-alone.lua`: use flex output mode with specific `.lua` file
28+
29+
One finished, add index for tags:
1430
```
15-
osm2pgsql -U user -d database -c -s --flat-nodes DB-flat-nodes --middle-with-nodes -x -O flex -S geometries-alone.lua extract.osm.pbf
31+
CREATE INDEX nodes_tags_idx ON planet_osm_nodes USING GIN (tags);
32+
CREATE INDEX ways_tags_idx ON planet_osm_ways USING GIN (tags);
33+
CREATE INDEX rels_tags_idx ON planet_osm_rels USING GIN (tags);
1634
```
1735

18-
Warning : this backend will not work on a existing osm2pgsql database. it is specific for a database created with the lua script above and the `-s (--slim)` option.
36+
Use `osm2pgsql-replication` to update the DB.
37+
38+
Sizing
39+
- 57GB for France (29GB for middle tables (metadata+tags) + 2GB for tag index + 26GB for output tables (geometries))
1940

2041
If your database was created "outside" docker, you will have to modify `docker-compose.yaml` to:
2142
- delete services `osm2pgsql` and `postgress`
2243
- in service api : delete reference `depends on: -postgres` and set your `DATABASE_URL: postgres://user:pw@host:5432/database`
2344

45+
Explanation of the `.lua` script and principle of the DB structure:
46+
- `-s` option creates `middle` tables which include all raw OSM elements (`planet_osm_nodes`, `planet_osm_ways`, `planet_osm_rels`) with all their tags. This view uses these 3 tables to get tags (no need to duplicate them in `output` tables), but we need to manually index them at the beginning.
47+
- `-x` option add columns for metadata in these tables, and table `planet_osm_users` for usernames.
48+
- so only the geometries are missing. The `.lua` script of the `flex output` creates 3 additionnal `output` tables: `nodes_geom`, `ways_geom` and `rels_geom` with the id and the geometry.
49+
- I had to use tables by element type instead of geometry type (as usual for osm2pgsql) since the negative id used for relations in area type table is problematic for the join with the `planet_osm_rels` table which uses normal positive id (join is slow since indexing is not working).
50+
- For a list of expected areas (as usual in osm2pgsql), the lua script creates polygon-like geometries (instead of line-like) and adds the area size in a 3rd column (it will be usefull to get areas).
51+
52+
### 2. View for an existing DB created for cartocss
53+
54+
not written yet
55+
56+
### 3. View for a simple static DB (without synchronisation)
57+
58+
not written yet
59+
2460
## Run the server
2561

2662
Run the HTTP server
Lines changed: 28 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,34 @@
11
-- This is a very simple Lua config for the Flex output
22
-- which only stores the geometries (not even the tags)
3-
-- for use with Underpass-API to mimics Overpass
3+
-- for use with Underpass-API to mimic Overpass
44

55
local tables = {}
66

7-
tables.points = osm2pgsql.define_table({
8-
name = 'points',
9-
ids = { type = 'node', id_column = 'id' },
7+
tables.nodes_geom = osm2pgsql.define_table({
8+
name = 'nodes_geom',
9+
ids = { type = 'node', id_column = 'id', create_index='unique' },
1010
columns = {
11-
-- { column = 'tags', type = 'jsonb' },
1211
{ column = 'geom', type = 'point', projection = 4326, not_null = true }
1312
}})
1413

15-
tables.lines = osm2pgsql.define_table({
16-
name = 'lines',
17-
ids = { type = 'way', id_column = 'id' },
14+
tables.ways_geom = osm2pgsql.define_table({
15+
name = 'ways_geom',
16+
ids = { type = 'way', id_column = 'id', create_index='unique' },
1817
columns = {
19-
-- { column = 'tags', type = 'jsonb' },
20-
{ column = 'geom', type = 'multilinestring', projection = 4326, not_null = true }
18+
{ column = 'geom', type = 'geometry', projection = 4326, not_null = true },
19+
{ column = 'area', type = 'real' }
2120
}})
2221

23-
tables.polygons = osm2pgsql.define_table({
24-
name = 'polygons',
25-
ids = { type = 'area', id_column = 'id' },
22+
tables.rels_geom = osm2pgsql.define_table({
23+
name = 'rels_geom',
24+
ids = { type = 'relation', id_column = 'id', create_index='unique' },
2625
columns = {
27-
-- { column = 'tags', type = 'jsonb' },
2826
{ column = 'geom', type = 'geometry', projection = 4326, not_null = true },
29-
-- In this column we'll put the true area calculated on the spheroid
3027
{ column = 'area', type = 'real' }
3128
}})
3229

3330

34-
-- Helper function to remove some of the tags we usually are not interested in.
35-
-- Returns true if there are no tags left.
36-
37-
-- modifié : retourne vrai si aucun tag
38-
local function clean_tags(tags)
39-
-- tags.odbl = nil
40-
-- tags.created_by = nil
41-
-- tags.source = nil
42-
-- tags['source:ref'] = nil
43-
44-
return next(tags) == nil
45-
end
46-
47-
-- Helper function that looks at the tags and decides if this is possibly
48-
-- an area.
31+
-- Helper function that looks at the tags and decides if this is possibly an area
4932
local function has_area_tags(tags)
5033
if tags.area == 'yes' or tags.area == 'true' or tags.area == '1' then
5134
return true
@@ -82,65 +65,49 @@ local function has_area_tags(tags)
8265
or tags['area:highway']
8366
end
8467

68+
-- Store geometry of nodes (so that they can be indexed)
8569
function osm2pgsql.process_node(object)
86-
if clean_tags(object.tags) then
87-
return
88-
end
89-
90-
local geom = object:as_point()
91-
92-
tables.points:insert({
93-
-- tags = object.tags,
94-
geom = geom -- the point will be automatically be projected to 3857
70+
tables.nodes_geom:insert({
71+
geom = object:as_point()
9572
})
96-
9773
end
9874

75+
9976
function osm2pgsql.process_way(object)
100-
if clean_tags(object.tags) then
101-
return
102-
end
10377

10478
-- A closed way that also has the right tags for an area is a polygon.
10579
if object.is_closed and has_area_tags(object.tags) then
10680
-- Creating the polygon geometry takes time, so we do it once here
10781
-- and later store it in the table and use it to calculate the area.
10882
local geom = object:as_polygon()
109-
tables.polygons:insert({
83+
tables.ways_geom:insert({
11084
geom = geom,
11185
area = geom:spherical_area() -- calculate "real" area in spheroid
11286
})
11387
else
114-
-- modif : on enregistre la géométrie directement en multilinestring
115-
-- en mergeant les lignes le plus possible
116-
tables.lines:insert({
117-
geom = object:as_multilinestring():line_merge()
88+
-- Store way as line
89+
tables.ways_geom:insert({
90+
geom = object:as_linestring()
11891
})
11992
end
12093
end
12194

12295
function osm2pgsql.process_relation(object)
123-
if clean_tags(object.tags) then
124-
return
125-
end
12696

12797
local relation_type = object:grab_tag('type')
12898

129-
-- Store route relations as multilinestrings
130-
if relation_type == 'route' or relation_type == 'associatedStreet' or relation_type == 'public_transport' or relation_type == 'waterway' then
131-
tables.lines:insert({
132-
geom = object:as_multilinestring():line_merge()
133-
})
134-
return
135-
end
136-
137-
-- Store multipolygon and boundary relations as polygons
99+
-- Store multipolygon and boundary relations as multipolygons, with their area
138100
if relation_type == 'multipolygon' or relation_type == 'boundary' then
139101
local geom = object:as_multipolygon()
140-
tables.polygons:insert({
102+
tables.rels_geom:insert({
141103
geom = geom,
142104
area = geom:spherical_area()
143105
})
106+
else
107+
-- Store other relations as geometryCollection
108+
tables.rels_geom:insert({
109+
geom = object:as_geometrycollection()
110+
})
144111
end
145112
end
146113

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,18 @@
1-
/************** ABOUT TABLES *****************/
1+
/************** WARNING *****************/
22
/*
3-
- without the --slim option, only "output" tables are created (PREFIX_point PREFIX_line and PREFIX_polygon in the case of the deprecated pgsql output) and they contain elements filtered based on their tags (using list and parameters in .style file). These tables also contain the geometry.
4-
- with --slim, 3 other "middle" tables containing ALL the raw OSM elements are created : _nodes, _ways, _rels
3+
This view is specific for a new database created with flex output geometries-alone.lua
54
*/
65

7-
/************** ABOUT GEOMETRY *****************/
8-
/*
9-
Underpass as Overpass uses SRID 4326 whereas osm2pgsql uses by default SRID 3857
10-
To make Underpass works on your osm2pgsl database, you have to CREATE it with option --proj=4326 (for the case of deprecated pgsql output) or define that the tables will use SRID 4326 (for the case of the modern flex output)
11-
If you want to deploy Underpass on an existing osm2pgsl database, you will have to modify both this backend and overpass_parser-rb, so that the parser transforms bbox and areas and others in SRID 3857 to allow comparison with the geom in your DB
12-
*/
136

14-
/************** ABOUT TAGS COLUMNS *****************/
7+
/************** ABOUT TABLES *****************/
158
/*
16-
- tags in "middle" tables _nodes, _ways, _rels are identical to the ones in OSM (they are "raw data" tables)
17-
- but tags in "output" tables are always (slightly) different depending on the hstore options:
18-
# With --hstore any tags without a column will be added to the hstore column.
19-
# With --hstore-all all tags are added to the hstore column unless they appear in the style file with a delete flag
9+
The flex output lua script creates:
10+
- the 3 normal "middle" tables planet_osm_nodes, _ ways and _rels which include (all ans synchronised) OSM elements with (all) their tags and metadata. (tags need to be indexed after DB creation). And also _users table
11+
- 3 additionnal "output" tables to store the geometries using SRID 4326 (to be compatible with overpass). 1 table per element type : nodes_geom, ways_geom and rels_geom
12+
- the views join middle table (tags) with the corresponding output table (geometry) and also with users table (username)
2013
*/
2114

22-
/************** ABOUT METADATA *****************/
23-
/* adding metadata to the DB requires -x option */
24-
/* without it, all the CAST(tags->...) will return NULL */
25-
26-
27-
/******** UNION LINES AND POLYGONS **********/
28-
CREATE OR REPLACE TEMP VIEW lines_and_polygons AS
29-
SELECT id, geom, NULL::real as area FROM lines
30-
UNION ALL
31-
SELECT * FROM polygons
32-
;
33-
34-
3515
/************** NODES *****************/
36-
/* requires database creation with either :
37-
- option --slim without --flat-nodes
38-
- options --slim --flat-nodes FILE --middle-with-nodes */
3916
CREATE OR REPLACE TEMP VIEW node AS
4017
SELECT
4118
n.id AS id,
@@ -44,7 +21,7 @@ SELECT
4421
n.changeset_id AS changeset,
4522
n.user_id AS uid,
4623
u.name AS user,
47-
/* if you did not use --extra-attributes, replace above lines by
24+
/* if you did not use -x to get metadata, replace above lines by
4825
NULL::integer AS version,
4926
NULL::timestamp without time zone AS created,
5027
NULL::bigint AS changeset,
@@ -54,15 +31,14 @@ SELECT
5431
n.tags AS tags,
5532
NULL::bigint[] AS nodes,
5633
NULL::jsonb AS members,
57-
p.geom AS geom,
34+
g.geom AS geom,
5835
NULL::real AS area,
5936
'n' AS osm_type
6037
FROM planet_osm_nodes as n
6138
LEFT JOIN planet_osm_users AS u ON n.user_id = u.id /* also remove this line */
62-
LEFT JOIN points AS p ON n.id = p.id
39+
LEFT JOIN nodes_geom AS g ON n.id = g.id
6340
;
6441

65-
6642
/************** WAYS *****************/
6743
CREATE OR REPLACE TEMP VIEW way AS
6844
SELECT
@@ -72,7 +48,7 @@ SELECT
7248
w.changeset_id AS changeset,
7349
w.user_id AS uid,
7450
u.name AS user,
75-
/* if you did not use --extra-attributes, replace above lines by
51+
/* if you did not use -x to get metadata, replace above lines by
7652
NULL::integer AS version,
7753
NULL::timestamp without time zone AS created,
7854
NULL::bigint AS changeset,
@@ -82,17 +58,15 @@ SELECT
8258
w.tags as tags,
8359
w.nodes AS nodes,
8460
NULL::jsonb AS members,
85-
lp.geom AS geom,
86-
lp.area AS area,
61+
g.geom AS geom,
62+
g.area AS area,
8763
'w' AS osm_type
8864
FROM planet_osm_ways AS w
8965
LEFT JOIN planet_osm_users AS u ON w.user_id = u.id /* also remove this line */
90-
LEFT JOIN lines_and_polygons AS lp ON w.id = lp.id
66+
LEFT JOIN ways_geom AS g ON w.id = g.id
9167
;
9268

93-
9469
/************** RELATIONS *****************/
95-
/* complete version (based on table _rels) if you used --slim */
9670
CREATE OR REPLACE TEMP VIEW relation AS
9771
SELECT
9872
r.id AS id,
@@ -101,7 +75,7 @@ SELECT
10175
r.changeset_id AS changeset,
10276
r.user_id AS uid,
10377
u.name AS user,
104-
/* if you did not use --extra-attributes, replace above lines by
78+
/* if you did not use -x to get metadata, replace above lines by
10579
NULL::integer AS version,
10680
NULL::timestamp without time zone AS created,
10781
NULL::bigint AS changeset,
@@ -111,15 +85,14 @@ SELECT
11185
r.tags as tags,
11286
NULL::bigint[] AS nodes,
11387
r.members AS members,
114-
lp.geom AS geom,
115-
lp.area AS area,
88+
g.geom AS geom,
89+
g.area AS area,
11690
'r' AS osm_type
11791
FROM planet_osm_rels AS r
11892
LEFT JOIN planet_osm_users AS u ON r.user_id = u.id /* also remove this line */
119-
LEFT JOIN lines_and_polygons AS lp ON r.id = -lp.id
93+
LEFT JOIN rels_geom AS g ON r.id = g.id
12094
;
12195

122-
12396
/************** NWR *****************/
12497
CREATE OR REPLACE TEMP VIEW nwr AS
12598
SELECT * FROM node
@@ -129,11 +102,11 @@ UNION ALL
129102
SELECT * FROM relation
130103
;
131104

132-
/************** AREA *****************/
105+
/************** AREAS *****************/
133106
CREATE OR REPLACE TEMP VIEW area AS
134107
SELECT
135108
CASE
136-
WHEN osm_type='r' THEN 3600000000+id /* transform if of relations to be consistent with overpass */
109+
WHEN osm_type='r' THEN 3600000000+id /* transform id of relations to be consistent with overpass */
137110
ELSE id
138111
END AS id,
139112
version,
@@ -147,10 +120,6 @@ SELECT
147120
geom,
148121
area,
149122
REPLACE(osm_type, 'w', 'a') AS osm_type
150-
/*CASE
151-
WHEN osm_type='r' THEN 'a' /* transform r in a for underpass ? */
152-
ELSE osm_type
153-
END AS osm_type,*/
154123
FROM nwr
155124
WHERE area IS NOT NULL
156125
;

0 commit comments

Comments
 (0)