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:

2 comments:

  1. Adrian, for the category problem you could try the following within GRASS:

    # first, delete all categories:
    v.category in=short_dist out=short_dist_new option=del

    # Then delete the old table:
    v.db.droptable short_dist_new -f

    # And add new categories for the lines:
    v.category in=short_dist_new out=short_dist_new2 option=add

    # Finally, create a table with the line-lengths:
    v.db.addtable short_dist_new2
    v.db.addcol short_dist_new2 columns="length double precision"
    v.to.db short_dist_new2 type=line option=length unit=meters columns=length


    Stefan

    ReplyDelete
  2. Stefan, thank you for your hints.

    I've tried it and it works to assign new categories to the geometries, but I'm losing the attributes. Thus it is no longer possible to do routing based on travel time, which depends on the speed derived from the "highway" class. Routing based only on distance should work.

    ReplyDelete