Skip to content

Instantly share code, notes, and snippets.

Created January 26, 2010 23:34
Show Gist options
  • Save aflaxman/287370 to your computer and use it in GitHub Desktop.
Save aflaxman/287370 to your computer and use it in GitHub Desktop.
networkx-osm import open street map data as a networkx graph
Read graphs in Open Street Maps osm format
Based on from brianw's osmgeocode, which is based on from
comes from Graphserver: and is copyright (c)
2007, Brandon Martin-Anderson under the BSD License
import xml.sax
import copy
import networkx
def download_osm(left,bottom,right,top):
""" Return a filehandle to the downloaded data."""
from urllib import urlopen
fp = urlopen( ",%f,%f,%f"%(left,bottom,right,top) )
return fp
def read_osm(filename_or_stream, only_roads=True):
"""Read graph in OSM format from file specified by name or by stream object.
filename_or_stream : filename or stream object
G : Graph
>>> G=nx.read_osm(nx.download_osm(-122.33,47.60,-122.31,47.61))
>>> plot([G.node[n]['data'].lat for n in G], [G.node[n]['data'].lon for n in G], ',')
osm = OSM(filename_or_stream)
G = networkx.Graph()
for w in osm.ways.itervalues():
if only_roads and 'highway' not in w.tags:
G.add_path(w.nds,, data=w)
for n_id in G.nodes_iter():
n = osm.nodes[n_id]
G.node[n_id] = dict(data=n)
return G
class Node:
def __init__(self, id, lon, lat): = id
self.lon = lon = lat
self.tags = {}
class Way:
def __init__(self, id, osm):
self.osm = osm = id
self.nds = []
self.tags = {}
def split(self, dividers):
# slice the node-array using this nifty recursive function
def slice_array(ar, dividers):
for i in range(1,len(ar)-1):
if dividers[ar[i]]>1:
#print "slice at %s"%ar[i]
left = ar[:i+1]
right = ar[i:]
rightsliced = slice_array(right, dividers)
return [left]+rightsliced
return [ar]
slices = slice_array(self.nds, dividers)
# create a way object for each node-array slice
ret = []
for slice in slices:
littleway = copy.copy( self ) += "-%d"%i
littleway.nds = slice
ret.append( littleway )
i += 1
return ret
class OSM:
def __init__(self, filename_or_stream):
""" File can be either a filename or stream/file object."""
nodes = {}
ways = {}
superself = self
class OSMHandler(xml.sax.ContentHandler):
def setDocumentLocator(self,loc):
def startDocument(self):
def endDocument(self):
def startElement(self, name, attrs):
if name=='node':
self.currElem = Node(attrs['id'], float(attrs['lon']), float(attrs['lat']))
elif name=='way':
self.currElem = Way(attrs['id'], superself)
elif name=='tag':
self.currElem.tags[attrs['k']] = attrs['v']
elif name=='nd':
self.currElem.nds.append( attrs['ref'] )
def endElement(self,name):
if name=='node':
nodes[] = self.currElem
elif name=='way':
ways[] = self.currElem
def characters(self, chars):
xml.sax.parse(filename_or_stream, OSMHandler)
self.nodes = nodes
self.ways = ways
#count times each node is used
node_histogram = dict.fromkeys( self.nodes.keys(), 0 )
for way in self.ways.values():
if len(way.nds) < 2: #if a way has only one node, delete it out of the osm collection
del self.ways[]
for node in way.nds:
node_histogram[node] += 1
#use that histogram to split all ways, replacing the member set of ways
new_ways = {}
for id, way in self.ways.iteritems():
split_ways = way.split(node_histogram)
for split_way in split_ways:
new_ways[] = split_way
self.ways = new_ways
Copy link

See edited version enabling directionality

Copy link

Tofull commented Mar 16, 2017

Hi !
Thanks for your code. I used it on a scholar project.

Here is the list of things I added

  • Python3.6 compatibility
  • Cache for avoiding to download again the same osm tiles
  • Distance computation to estimate length of each ways (useful to compute the shortest path)
  • Directional Graph (to consider oneway road on the shortest path computation)

My edited version :

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment