An osm2pgsql database and PostGIS is well-suited for geospatial analysis using OpenStreetMap data where topology is not a consideration.
PostGIS provides an extensive number of geometry functions and a full description of how to perform analysis with them is beyond the scope of a readme, but a simple example of finding the total road lengths by classification for a municipality should help.
To start with, we'll download the data for the region as an extract from Geofabrik and import it with osm2pgsql.
osm2pgsql --database gis --number-processes 4 --multi-geometry british-columbia-latest.osm.pbf
--multi-geometry
(-G
) is necessary for most analysis as it prevents
MULTIPOLYGONs from being split into multiple POLYGONs, a step that is
normally used to increase rendering speed
but increases the complexity of analysis SQL.
Loading should take about 10 minutes, depending on computer speed. Once this
is done we'll open a PostgreSQL terminal with psql -d gis
, although a GUI
like pgadmin or any standard tool could be used instead.
To start, we'll create a partial index to speed up highway queries.
CREATE INDEX planet_osm_line_highways_index ON planet_osm_line USING GiST (way) WHERE (highway IS NOT NULL);
We'll first find the ID of the polygon we want
gis=# SELECT osm_id FROM planet_osm_polygon
WHERE boundary='administrative' AND admin_level='8' AND name='New Westminster';
osm_id
----------
-1377803
The negative sign tells us that the geometry is from a relation, and checking on the OpenStreetMap site confirms which it is.
We want to find all the roads in the city and get the length of the portion in
the city, sorted by road classification. Roads are in the planet_osm_line
table, not the planet_osm_roads
table which is only has a subset of data
for low-zoom rendering.
gis=# SELECT
round(SUM(
ST_Length(ST_Transform(
ST_Intersection(way, (SELECT way FROM planet_osm_polygon WHERE osm_id=-1377803))
,4326)::geography)
)) AS "distance (meters)", highway AS "highway type"
FROM planet_osm_line
WHERE highway IS NOT NULL
AND ST_Intersects(way, (SELECT way FROM planet_osm_polygon WHERE osm_id=-1377803))
GROUP BY highway
ORDER BY "distance (meters)" DESC
LIMIT 10;
distance (meters) | highway type
-------------------+---------------
138122 | residential
79519 | service
51890 | footway
25610 | tertiary
23434 | secondary
14900 | cycleway
6468 | primary
5217 | motorway
4389 | motorway_link
3728 | track
The ST_Transform(...,4326)::geography
is necessary because the data was
imported in Mercator. This step could have been avoided by importing in a local
projection like a suitable UTM projection.
More complicated analysises can be completed, but this simple example shows how to use the tables and put conditions on the columns.