Skip to content

Commit aa159f8

Browse files
committed
creation of a backend for osm2pgsql specific felx output
1 parent 512d0a0 commit aa159f8

11 files changed

Lines changed: 427 additions & 5 deletions

File tree

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ Underpass-API aim to be a [Overpass-API](https://github.com/drolbr/Overpass-API)
66

77
## Prepare the data & Run the server
88

9-
Folow the instruction of one of the backends:
10-
* [Postgres+PostGIS / Osmosis](backends/postgres_osmosis/README.md), Osmosis schema
9+
### With docker (recommended)
10+
11+
Follow the instruction of one of the backends:
1112
* [DuckDB+Spatial / QuackOSM](backends/duckdb_quackosm/README.md), Quackosm schema
13+
* [Postgres+PostGIS / Osmosis](backends/postgres_osmosis/README.md), Osmosis schema
14+
* [Postgres+PostGIS / Osm2pgsql](backends/postgres_osm2pgsql/README.md), Osm2pgsql schema using a specific `flex output`
1215

1316
## Query
1417

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Postgres/PostGIS, Osm2pgsql schema
2+
3+
Prepare Docker
4+
```sh
5+
docker compose --profile '*' build
6+
```
7+
8+
## Prepare the data
9+
10+
Create you database with osm2pgsql, using the script `geometries-alone.lua` in this folder.
11+
12+
Example command :
13+
14+
```
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
16+
```
17+
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.
19+
20+
If your database was created "outside" docker, you will have to modify `docker-compose.yaml` to:
21+
- delete services `osm2pgsql` and `postgress`
22+
- in service api : delete reference `depends on: -postgres` and set your `DATABASE_URL: postgres://user:pw@host:5432/database`
23+
24+
## Run the server
25+
26+
Run the HTTP server
27+
```
28+
docker compose up
29+
```
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
version: "3.3"
2+
3+
services:
4+
osm2pgsql:
5+
profiles: [tools]
6+
build:
7+
context: docker/osm2pgsql
8+
environment:
9+
DATABASE_URL: postgresql://postgres@postgres:5432/postgres
10+
volumes:
11+
- ../../data:/data
12+
depends_on:
13+
- postgres
14+
15+
postgres:
16+
image: postgis/postgis:15-3.4
17+
shm_size: 1g
18+
environment:
19+
POSTGRES_HOST_AUTH_METHOD: trust
20+
volumes:
21+
- ./docker/postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
22+
- pgdata:/var/lib/postgresql/data
23+
restart: unless-stopped
24+
25+
api:
26+
extends:
27+
file: ../../docker-compose-base.yaml
28+
service: api
29+
environment:
30+
BACKEND: PostgresOsm2pgsql
31+
DATABASE_URL: postgresql://postgres@postgres:5432/postgres
32+
volumes:
33+
- .:/srv/app/backends/postgres_osm2pgsql
34+
depends_on:
35+
- postgres
36+
37+
volumes:
38+
pgdata:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM debian:12
2+
3+
RUN apt update -y && apt install -y \
4+
osm2pgsql \
5+
postgresql-client
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DROP SCHEMA IF EXISTS tiger CASCADE;
2+
DROP EXTENSION IF EXISTS postgis_tiger_geocoder;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CREATE EXTENSION IF NOT EXISTS htsore;
2+
3+
-- Same as ->, for code compatibility with json
4+
CREATE OPERATOR ->> (
5+
LEFTARG = hstore,
6+
RIGHTARG = text,
7+
PROCEDURE = fetchval
8+
);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CREATE EXTENSION IF NOT EXISTS postgis;
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
-- This is a very simple Lua config for the Flex output
2+
-- which only stores the geometries (not even the tags)
3+
-- for use with Underpass-API to mimics Overpass
4+
5+
local tables = {}
6+
7+
tables.points = osm2pgsql.define_table({
8+
name = 'points',
9+
ids = { type = 'node', id_column = 'id' },
10+
columns = {
11+
-- { column = 'tags', type = 'jsonb' },
12+
{ column = 'geom', type = 'point', projection = 4326, not_null = true }
13+
}})
14+
15+
tables.lines = osm2pgsql.define_table({
16+
name = 'lines',
17+
ids = { type = 'way', id_column = 'id' },
18+
columns = {
19+
-- { column = 'tags', type = 'jsonb' },
20+
{ column = 'geom', type = 'multilinestring', projection = 4326, not_null = true }
21+
}})
22+
23+
tables.areas = osm2pgsql.define_table({
24+
name = 'areas',
25+
ids = { type = 'area', id_column = 'id' },
26+
columns = {
27+
-- { column = 'tags', type = 'jsonb' },
28+
{ column = 'geom', type = 'geometry', projection = 4326, not_null = true },
29+
-- In this column we'll put the true area calculated on the spheroid
30+
{ column = 'area', type = 'real' }
31+
}})
32+
33+
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.
49+
local function has_area_tags(tags)
50+
if tags.area == 'yes' or tags.area == 'true' or tags.area == '1' then
51+
return true
52+
end
53+
if tags.area == 'no' or tags.area == 'false' or tags.area == '0' then
54+
return false
55+
end
56+
57+
return tags.aeroway
58+
or tags.amenity
59+
or tags.building
60+
or tags.harbour
61+
or tags.historic
62+
or tags.landuse
63+
or tags.leisure
64+
or tags.man_made
65+
or tags.military
66+
or tags.natural
67+
or tags.office
68+
or tags.place
69+
or tags.power
70+
or tags.public_transport
71+
or tags.shop
72+
or tags.sport
73+
or tags.tourism
74+
or tags.water
75+
or tags.waterway
76+
or tags.wetland
77+
or tags['abandoned:aeroway']
78+
or tags['abandoned:amenity']
79+
or tags['abandoned:building']
80+
or tags['abandoned:landuse']
81+
or tags['abandoned:power']
82+
or tags['area:highway']
83+
end
84+
85+
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
95+
})
96+
97+
end
98+
99+
function osm2pgsql.process_way(object)
100+
if clean_tags(object.tags) then
101+
return
102+
end
103+
104+
-- A closed way that also has the right tags for an area is a polygon.
105+
if object.is_closed and has_area_tags(object.tags) then
106+
-- Creating the polygon geometry takes time, so we do it once here
107+
-- and later store it in the table and use it to calculate the area.
108+
local geom = object:as_polygon()
109+
tables.areas:insert({
110+
geom = geom,
111+
area = geom:spherical_area() -- calculate "real" area in spheroid
112+
})
113+
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()
118+
})
119+
end
120+
end
121+
122+
function osm2pgsql.process_relation(object)
123+
if clean_tags(object.tags) then
124+
return
125+
end
126+
127+
local relation_type = object:grab_tag('type')
128+
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
138+
if relation_type == 'multipolygon' or relation_type == 'boundary' then
139+
local geom = object:as_multipolygon()
140+
tables.areas:insert({
141+
geom = geom,
142+
area = geom:spherical_area()
143+
})
144+
end
145+
end
146+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# frozen_string_literal: true
2+
3+
require 'pg'
4+
require 'overpass_parser/sql_dialect/postgres'
5+
6+
class PostgresOsm2pgsql
7+
def initialize
8+
9+
@@con = PG.connect(ENV['DATABASE_URL'])
10+
@@con.query(File.read(File.dirname(__FILE__) + '/view.sql'))
11+
@dialect = OverpassParser::SqlDialect::Postgres.new(postgres_escape_literal: ->(s) { @@con.escape_literal(s) })
12+
end
13+
14+
def exec(query)
15+
request = OverpassParser.parse(query)
16+
sql = request.to_sql(@dialect)
17+
puts sql
18+
result = @@con.exec(sql)
19+
[sql, result.collect { |row| row['j'].gsub('+00:00', 'Z') }]
20+
end
21+
end

0 commit comments

Comments
 (0)