Sunday, June 26, 2011

Routing with OpenStreetMap data in GRASS GIS

A couple of month ago I got the task to calculate for each village in Switzerland the distance to the intra-Swiss language border. I thought it's a good opportunity to get used to the routing capabilities in GRASS GIS using OpenStreetMap data. Despite following the example of the v.net.path module, I miserably failed. Whatever I tried, I got the warnings
WARNING: Point with category [2] is not reachable from point with category [1]
WARNING: 1 destination(s) unreachable (including points out of threshold)
Finally I solved the task the easy way with v.distance using linear distance.

Now a new GIS task that requires routing came up in our office, and I took this opportunity for a new attempt to create a routable network with OpenStreetMap data in GRASS GIS.
I skipped the conversion from OpenStreetMap data to GIS data in downloading a ready-made road Shapefile from openstreetmap.la and imported it to GRASS GIS. I used the v.net module to connect the start and end points to the network and tried to calculate the shortest path with v.net.path. Again I got the "not reachable" warnings resulting in an empty output map. I checked the topology, everything seemed to be fine, exactly what I expected since OpenStreetMap data are topological. Nevertheless I tried the break operation from v.clean which breaks lines at each intersection:
v.clean input=osm_roads output=osm_roads_cleaned tool=break,rmdupl
And: it does the trick! I got rid of the "not reachable" warnings and was able to calculate the shortest path between two points after building a network with v.net:
echo "1 1 2" | v.net.path -g input=osm_net output=short_dist
In the next step I wanted to calculate the length for each line using v.to.db when I encountered another problem: the length was computed for each original line (before the break up with v.clean) since the segments that were created with v.clean had still the same category. I tried to figure out a "GRASS way" of solving this, but I couldn't. Any help is very appreciated.

Finally I used the following workaround with ogr2ogr, exporting the vector map, dropping the category column and re-import it again in order to get new unique categories for each feature:
ogr2ogr -select "osm_id,highway,oneway,surface,name,name_lo,name_en" -lco ENCODING=UTF-8 tmp.shp $GISDBASE/$LOCATION_NAME/$MAPSET/vector/osm_roads_cleaned/head
Adding the length for each feature with:
v.db.addcol map=osm_net columns='length DOUBLE PRECISION'
v.to.db map=osm_net option=length units=meters columns=length
Now with the correct length for each line it is possible to refine the routing using road speed limits and oneways to get a more lifelike model.

A screenshot using only distance: