The GeoPandas osm
module makes it easy to dynamically query and analyze arbitrary OpenStreetMap (OSM) data. It's a powerful combination.
This example finds the number of traffic lights that are along an arbitrary running route, using just a few lines of code. Note that OpenStreetMap doesn't have lots of traffic lights, but they are pretty good around Cambridge, MA where this example is located.
I used RunKeeper to download a recorded run from my phone. You can find the raw file here:
I removed a bunch of random points near the start and end so you can't exactly figure out where I live :)
import numpy as np
import geopandas as gpd
import geopandas.io.osm as osm
First load up the GPX file. Specifying the tracks
layer loads the data as a single row containing a LineString
or MultiLineString
. For other uses you can specify layer=track_points
to have a row for each recorded point.
df = gpd.read_file('RK_gpx_2014-07-10_2054.gpx', layer='tracks')
df
Now load all the OpenStreetMap traffic lights in the route's bounding box. Traffic lights in OSM are represented as nodes where the highway
tag is traffic_signals
. The query_osm
function takes care of formulating the query and returning a GeoDataFrame ready to go with all the points.
df_lights = osm.query_osm('node', bbox=df.total_bounds, tags='highway=traffic_signals')
df_lights
Project both GeoDataFrames to the Massachusetts state plane (EPSG:26986) (Hopefully in the future GeoPandas will have geography methods and we can skip the projections).
Then just compute the distance from the lights to the line using distance()
.
epsg = 26986 # MA mainland state plane
df_p = df.to_crs(epsg=epsg)
df_lights_p = df_lights.to_crs(epsg=epsg)
df_lights['d'] = df_lights_p.distance(df_p['geometry'].iloc[0])
df_lights['d']
Now visualize the data. I'm using geojsonio.py which opens http://geojsonio.io. We can take advantage of simplestyle-spec by setting a few properties on the GeoDataFrame to control plotting behavior
Use the marker-color
property - traffic lights that are close to the route (< 20 meters) are green and the others will be red. The light at Massachusetts Ave. & Landsdowne St. is a false negative due to noise in the GPS trace.
You can click on each marker and inspect the d
property to see how far it is from the route.
# Shouldn't need the 'set_geometry()' call below, this is a problem in GeoPandas
combined = df.append(df_lights).set_geometry('geometry')
combined['marker-color'] = np.where(combined['d'] <= 20, '#4daf4a', '#e41a1c')
import geojsonio
geojsonio.embed(combined.to_json(na='drop'))