Copenhagen’s bike route planner I Bike CPH will use OSRM’s all improved ability to integrate external data to find better directions for its users. Here is a walkthrough of how to use the Lua interface of OSRM to fine tune routing with external data sources. While bike routes can be computed merely based on network topology and lengths of road segments, external data is crucial to deliver great directions, Emil Tin of I Bike CPH says:

The ability to factor in data about parks, hills, pollution, etc when calculating routes open up some fantastic possibilities, and allows us to offer even better bicycle routing in our I Bike CPH service.

Emil Tin, project manager of I Bike CPH

An I Bike CPH route generated by OSRM

Project OSRM is a stack of high-performance tools to extract, prepare and serve routing information from OpenStreetMap data. Routing suggestions for bicycles or pedestrians can be vastly improved by considering data other than the mere network of roads and paths. Here is an overview of how to get started with the new Lua interface.

For the following example, I assume a current OSRM setup as described on the OSRM Wiki with further data in a SQL database. In this case it is the same OpenStreetMap data imported into a PostgreSQL/PostGIS database with Imposm. External data support is only available in the the development branch, so install from there.

Connecting to a Database

It is easy to incorporate external data sources into an OSRM profile. Technically speaking, such a profile is a Lua script that decides which nodes and ways to use from the OpenStreetMap data. There are plenty of modules for LUA that can be loaded during runtime. LUARocks is an easy-to-use management tool to build and install these modules. It is available on all major Linux distributions and also on Mac OS X through the homebrew package management system. The following steps work for OS X, and are easily transferable to Linux.

Installing dependencies

First, we install LuaRocks, which is as easy as

$ brew install luarocks

Second, the database driver is installed by executing luarocks in a brew shell:

$ brew sh
$ luarocks install luasql-postgres

And that’s it.

Running queries during extraction

Each profile script is devided into several sections. A global section is used to initialize a global environment. We connect to the database at this point as this code is run before any OpenStreetMap data is actually parsed.

    lua_sql = require "luasql.postgres"
    sql_env = assert( lua_sql.postgres() )
    sql_con = assert( sql_env:connect("imposm", "user", "password") )

We’ll use this connection in the subsequent parsing steps. Each OSM way is parsed in a function with the name way_function and it is also the place our database queries are run from. For this simple example, we put a penalty on routing over any OSM way that passes by an industrial landuse within 100 meters distance:

function way_function (way)
  local highway = way.tags:Find("highway")
  if (not highway or highway=='') then
      return 0
  end

  way.name = way.tags:Find("name")
  way.direction = Way.bidirectional

  -- query PostGIS for information about the way
  local sql_query = " " ..
    "SELECT SUM(SQRT(area.area)) AS val " ..
    "FROM osm_ways way " ..
    "LEFT JOIN osm_landusages area ON ST_DWithin(way.geometry, area.geometry, 100) " ..
    "WHERE area.type IN ('industrial') AND way.osm_id=" .. way.id .. " " ..
    "GROUP BY way.id " ..
    "LIMIT 1"

  local cursor = assert( sql_con:execute(sql_query) )
  local row = cursor:fetch( {}, "a" )
  way.speed = 20.0
  if row then
    local val = tonumber(row.val)
    if val > 10 then
      way.speed = way.speed / math.log10( val )
    end
  end
  way.type = 1
  return 1
end

We take the square root of the area to get a rough estimate on how long the side of an area is. This is just accurate enough for this example. Now, penalizing the way makes it more expensive to pass through any industrial zone. Having an index on the OSM id in the SQL database is crucial for efficient processing of our queries. The following screen shot gives a good example. Note how the biking route keeps a distance to the industrial zone by passing it on its northern side while still being reasonable.

An example of where I Bike CPH will pick a longer, but more favorable route for cycling

We are excited to see the potential use cases of this feature: routing through parks, closer to coffee shops, through populated areas, or avoiding pollution - there is no limit to this list.