Your own little OpenStreetMap database
“The Wikipedia of Maps”
OSM is a dataset, which is…
The OSM dataset has:

OSM data is made up of:
Let’s look at an example!
{
"elements": [
{
"type": "node",
"id": 6650982353,
"lat": 41.6620246,
"lon": -88.5328721,
"tags": {
"direction": "forward",
"highway": "stop"
}
},
{
"type": "node",
"id": 235170089,
"lat": 41.6630576,
"lon": -88.5327984,
"tags": {
"railway": "crossing",
"crossing:bell": "yes"
}
},
{
"type": "node",
"id": 235170094,
"lat": 41.6637334,
"lon": -88.5327199
}
]
}{
"elements": [
{
"type": "way",
"id": 21814608,
"nodes": [
6650982353,
235170089,
235170094
],
"tags": {
"highway": "tertiary",
"name": "Lew Street"
}
},
{
"type": "node",
"id": 6650982353,
"lat": 41.6620246,
"lon": -88.5328721,
"tags": {
"direction": "forward",
"highway": "stop"
}
},
{
"type": "node",
"id": 235170089,
"lat": 41.6630576,
"lon": -88.5327984,
"tags": {
"railway": "crossing",
"crossing:bell": "yes"
}
},
{
"type": "node",
"id": 235170094,
"lat": 41.6637334,
"lon": -88.5327199
}
]
}{
"elements": [
{
"type": "relation",
"id": 10004268,
"members": [
{
"type": "way",
"ref": 21814608,
"role": ""
}
],
"tags": {
"name": "Plano City Road 1",
"network": "US:IL:Plano",
"type": "route",
"route": "road"
}
},
{
"type": "way",
"id": 21814608,
"nodes": [
6650982353,
235170089,
235170094,
235170095,
6650982353
],
"tags": {
"barrier": "wall"
}
}
]
}{
"elements": [
{
"type": "relation",
"id": 10004268,
"members": [
{
"type": "way",
"ref": 21814608,
"role": "outer"
},
{
"type": "way",
"ref": 21814722,
"role": "inner"
},
{
"type": "way",
"ref": 21815110,
"role": "inner"
},
{
"type": "way",
"ref": 21814600,
"role": "inner"
}
],
"tags": {
"building": "yes"
}
}
]
}A “swiss army knife” for OSM data.
Audience
Participation!
imposmMapping OSM data to tables is done using a YAML config file.
highways-mapping.yml
And prepping for future updates!
imposm import \
-config /app/imposm-scenarios/city-all-config.json \
-read /app/data/city-extract.osm.pbf \
-deployproduction -optimize -write -overwritecache \
-diffosm2pgsqlTake the plunge!
The osm2pgsql maintainers strongly encourage using the flex output, especially if you’re just starting out using it. The default output does not always take advantage of new capabilities.
Mapping to tables is done with a Lua file.
pois.lua
local pois = osm2pgsql.define_table({
name = 'pois',
ids = { type = 'any', type_column = 'osm_type', id_column = 'osm_id' },
columns = {
{ column = 'name' },
{ column = 'class', not_null = true },
{ column = 'subclass' },
{ column = 'tags', type = 'jsonb'},
{ column = 'geom', type = 'point', not_null = true },
}
})
function process_poi(object, geom)
local a = {
name = object.tags.name,
geom = geom,
tags = object.tags
}
if object.tags.amenity then
a.class = 'amenity'
a.subclass = object.tags.amenity
elseif object.tags.shop then
a.class = 'shop'
a.subclass = object.tags.shop
else
return
end
pois:insert(a)
end
function osm2pgsql.process_node(object)
process_poi(object, object:as_point())
end
function osm2pgsql.process_way(object)
if object.is_closed then
process_poi(object, object:as_polygon():centroid())
end
endroutes.lua
local ways = osm2pgsql.define_table({
name = 'routes',
ids = { type = 'way', id_column = 'osm_id' },
columns = {
{ column = 'id', sql_type = 'serial', create_only = true },
{ column = 'ref' },
{ column = 'name' },
{ column = 'network' },
{ column = 'tags', type = 'jsonb' },
{ column = 'the_geom', type = 'multilinestring', not_null = true}
}
})
function osm2pgsql.process_relation(object)
if object.tags.route == 'road' then
ways:insert({
tags = object.tags,
the_geom = object:as_multilinestring():line_merge(),
ref = object.tags.ref,
name = object.tags.name,
network = object.tags.network
})
end
endAll nodes, ways, and some relations1 are dumped into tables with all their tags.
>> data accessorJust re-run the import steps whenever a new extract is available from the original source!
For GeoFabrik, this means your data is only a day or so behind the main OSM dataset, depending on:
wget or curl command to download extract fileimposmAs long as you included -diff in the original import command, updating with imposm simple:
Use imposm run along with the same properties or config file as before.
osm2pgsqlUpdating with osm2pgsql needs a replication URL. It works great with the diffs at planet.osm.org.
To limit the changes to your area of interest is possible, but requires some custom coding to work.
If “live” data is needed, probably just use imposm.
Using the column ->> 'key' syntax returns the value for the specified key.
cycling_infrastructure.sql
Honestly, once you write the query, you can use the results for anything that you would a normal table, including:
It was noted earlier, but if you’re going to be providing map layers, services, etc., directly from this database, consider writing your query to a Materialized View for a performance boost.
Just don’t forget to refresh them now and then.
Questions?