Digital Geography

3. June 2015

Routing in QGIS … with Google

Routing in QGIS was, as far as I know, always dependent on an available network. Either you had some database which was pgrouting enabled, or you had some network and used this via the roadgraph plugin. I would like to show you, how to do routing and path finding via googlemaps and import the path into QGIS. Big advantage: You don’t even have to think about a network…

Current Solutions

My current knowledge of routing possibilities in QGIS is quite limited. I only know these three:
  • pgrouting
  • roadgraph plugin
  • points to route
So let’s have a closer look:

pgrouting

pgrouting is not a routing method. Instead it is an extension for postgresql like postgis. It requires you not only to have access to a postgresql database which you can administrate but also to have a transportation layer. Once you have it imported into the database you can use pgrouting algorithms on this network. Anita Graser covered the whole process in a blog post 2 years ago. pgrouting is probably the most versatile approach regarding routing in QGIS.

pgrouting example from Anita’s blog

roadgraph plugin

The roadgraph plugin makes it a bit easier to work with points and routes. All you need is a local shapefile which serves as a network. Once you have this, the plugin is able to snap o the network, calculate travel times (if you have the data), lengths and the shortest path from one way to another. Keep in mind that we don’t talk about real-life routing as this plugin doesn’t know any traffic lights ;-).

points to route

points to route is quite comparable to the roadgraph plugin but offers less more possibilities or outputs. It simply uses a polyline layer as a network and creates routes for a set of input points along the network.

Routing using GoogleMaps API

The primary problems on the above mentioned solutions are:
  • You need a periodic update of your network layer
  • acquire a network layer
  • prob. install a postgres DB
Therefore I asked myself whether it is possible to use the GoogleMaps API for routing. And hell yeah it is working. You’ll need an API key and the right APIs activated:
  1. log in at the Google dev page.
  2. Create a new project.
  3. Choose “Enable an API”.
  4. Browse for the API and activate the following APIs: Directions API
  5. Click on Credentials.
  6. Click Create new Key for a new public key.
  7. Select “Server Key”.
  8. Click Create.
unfortunately you will also need to “install” a module in your current Python environment to get access to the API. The module is called googlemaps can be obtained via github. It was easy to install via command line on Ubuntu:
sudo pip install googlemaps
On Windows I was using the OSGeo4W Shell and:
pip install googlemaps
Once you got this, the finding of a direction is quite easy from the python coding in the qgis python console:
import googlemaps
from datetime import datetime
now = datetime.now()
gmaps = googlemaps.Client(key="put YOUR key here!")
directions_result = gmaps.directions("Dessau, Germany", "Berlin, Germany",mode="driving",departure_time=now)
point_str = str(directions_result[0]["overview_polyline"]["points"])
The result is an ugly list of dictionaries. Especially the rout is not given as a pair of nice X-Y coordinates. Instead it is a encoded polyline as Google names it. The good thing: we can handle it!

Importing the route into QGIS

First we need to decode the polyline string into X and Y coordinate pairs as this string representing our polyline is damn ugly:
u’mak{HixkiAf@_\\bBeIzDqHbQyXzBgOe@uKcG{]_EiJOmDkAoAcMuHiBcDi@aFl@kQpAuWOcRZmHO}@wAeGoU_NmIaGe@qAl@wCdBqIaDaAmFYcTcIeETmFkCaAmB[oC_ExAe[nJiT|BaPbB}Ft@cI~DmIfBiNx@sOj@iNWcL_CwMgGaNcGaUgEmA}FeCs\\EkQeA}G}GeMoEqJwBqJwDeHyDy_@\\aaAhD}`ArDcQvNmYrDgTKiOiJqe@mMcs@}Cqr@iDgM{LgRiEqN{@oMcAuaBaAeqBhEkbBzAqZrPatAxCuTpATpBbC_@lDqAH_SmOyr@yk@kUkX}Qqa@{Qie@cSyg@qN_[iKsNcWgSsImDiuAoe@s{Ase@cTmImUuRaSe\\qTkm@iVir@cJu\\cKol@eVytAcXygAg]}sAsR}f@}mActBif@on@gWy[{KmTmRql@uKu]c\\udAwG_N_JsLoMgNiJkOqHuVcGaa@iF}QgIcOa\\uZuIqNgVws@eJgX{MuXeOgRyS_O{~@sh@sf@cZwRaPq_@u`@a}@msAqQc\\{Mi\\eUug@eq@}fAyPwXoIyQiUmr@oc@gsAyJuSaTuYav@o~@epAg{AsaA{kAyPuUkPu`@yJ_f@eMa{@i^abCgJq]{GiP_Yob@ig@as@yL_XwJm_@iPkiAkIs]cKgW{SuZi|@_aAoRySyb@we@ef@_i@wQoQwQyJuQcEuUOoUdFmR`J}y@la@cI~C{NvCyWAyWqF{d@{KkqAwZ}JeE}FaI{Hi_@yfAsyHcCsa@r@iuCLck@f@mTzBiRhO}w@tHif@\\eNs@uR_F}VmFiVeAuO`AkhAl@qi@|Aco@tBs_@{AoN}GuO_dAkm@kUaNgY}Yan@up@}J_UmH_^w^kmBaFoO{ImOqGsGiNuGyJeAmNDiiAx@gKo@cHcDyF_G_J{P{MiRoGgDsFaAy_@PmSBkHiAac@mUqN_MyFgMeDoOcTevAeCiLgKgWy]mc@au@_{@qGwCcIw@_S`C}ZbEoLdCsE~EsEnIuFpE}G\\uVyDkNmDoIcJ}[oc@op@o}@odA{vAkw@ifAwcA_pAij@ys@_l@ax@i^kh@kBcJ]sOkAmKsB_EgC]}C`@kEwEsFwDuKeAk[gQ}HyBqByACcAMmEbDo@`Do@pD{@L}G}@{_@uAie@mCuaAeBao@n@gCHkHkBmHgIcyClAaEKsE{AiCi@gKiEccB{@_[NqRdBwA~CAYgJ{AuOa@uNnDkAIcBq@mWWsJ{@HkC`@wC^w@@[wEk@oM}A_b@cMs_@’
The directions API has a very special way of providing route information as they encode the polyline. Therefore github user signed0 created a decode function and we will use the script to decode the result. We will concentrate on the overview route which has not all the points of your route but gives a first impression:
coord_chunks = [[]]
for char in point_str:
	value = ord(char) - 63
	split_after = not (value & 0x20)
	value &= 0x1F
	coord_chunks[-1].append(value)
	if split_after:
		coord_chunks.append([])

del coord_chunks[-1]
coords = []
for coord_chunk in coord_chunks:
	coord = 0
	for i, chunk in enumerate(coord_chunk):
		coord |= chunk << (i * 5)
	if coord & 0x1:
		coord = ~coord #invert
	coord >>= 1
	coord /= 100000.0
	coords.append(coord)

points = []
prev_x = 0
prev_y = 0
for i in xrange(0, len(coords) - 1, 2):
	if coords[i] == 0 and coords[i + 1] == 0:
		continue
	prev_x += coords[i + 1]
	prev_y += coords[i]
	points.append((round(prev_x, 6), round(prev_y, 6)))

print points
Now we have a smooth list of coordinates. We will convert this into vertices of a new polyline shapefile:
from PyQt4.QtCore import QVariant
layer = QgsVectorLayer('LineString', 'route', "memory") 
pr = layer.dataProvider() 
pr.addAttributes([QgsField("attribution", QVariant.String)])
layer.updateFields()
fet = QgsFeature() 
seg=[]
for i in range(0,len(points)): 
	seg.append(QgsPoint(points[i][0],points[i][1])) 

fet.setGeometry(QgsGeometry.fromPolyline(seg)) 
fet.setAttributes(["route provided by google maps api"])
pr.addFeatures([fet])
layer.updateExtents() #update it 
QgsMapLayerRegistry.instance().addMapLayer(layer)

route given by GoogleMaps API in QGIS

Limitations

As the API operates with a key you have some limitations: You can only call the API 2500 times a day and you need to give attribute to the source of the route / vertices what so ever. But I think this is a better burden than keeping your network in a db up-to-date or fetching GB of data as you need a bigger transportation layer… You can download the script here.
  • And don’t forget about the Google Maps API terms of use, that you must show a route retrieved with Directions API on Google Maps!

    • so I am a lucky guy who used googlemaps map as a basemap in my image 😉

      • Yes, you’re lucky 😉
        I already saw, that it’s a Google Maps background, so I wasn’t sure this was on purpose or a coincidence.

  • Benjamin Schmid-Lanz

    Nice work! Can you just tell me how to get the resolution up? So not the “overview” you are speaking about.