Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
networkx-osm import open street map data as a networkx graph
"""
Read graphs in Open Street Maps osm format
Based on osm.py from brianw's osmgeocode
http://github.com/brianw/osmgeocode, which is based on osm.py from
comes from Graphserver:
http://github.com/bmander/graphserver/tree/master 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( "http://api.openstreetmap.org/api/0.6/map?bbox=%f,%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.
Parameters
----------
filename_or_stream : filename or stream object
Returns
-------
G : Graph
Examples
--------
>>> 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:
continue
G.add_path(w.nds, id=w.id, 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):
self.id = id
self.lon = lon
self.lat = lat
self.tags = {}
class Way:
def __init__(self, id, osm):
self.osm = osm
self.id = 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 = []
i=0
for slice in slices:
littleway = copy.copy( self )
littleway.id += "-%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):
@classmethod
def setDocumentLocator(self,loc):
pass
@classmethod
def startDocument(self):
pass
@classmethod
def endDocument(self):
pass
@classmethod
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'] )
@classmethod
def endElement(self,name):
if name=='node':
nodes[self.currElem.id] = self.currElem
elif name=='way':
ways[self.currElem.id] = self.currElem
@classmethod
def characters(self, chars):
pass
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[way.id]
else:
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.id] = split_way
self.ways = new_ways
@aflaxman

This comment has been minimized.

Copy link
Owner Author

@aflaxman aflaxman commented Jul 27, 2012

The api for OSM has been updated since I used this last, 0.5 has been advanced to 0.6

Fortunately, the way to get map data has not changed: http://wiki.openstreetmap.org/wiki/API_changes_between_v0.5_and_v0.6

So making this work again was as simple as updating the url. It should work for you now. Please let the NetworkX developers know that you would like this included in their package: https://networkx.lanl.gov/trac/ticket/309

@rajanski

This comment has been minimized.

Copy link

@rajanski rajanski commented Jun 16, 2015

Hi I revised your very helpful code above in order to also write each node's lat/lon coordinates into the resulting graph, see https://gist.github.com/rajanski/ccf65d4f5106c2cdc70e

The question is how can i further expand your script to also write the tags of the ways as an attribute to the edges of the graph?

@rajanski

This comment has been minimized.

@rajanski

This comment has been minimized.

Copy link

@rajanski rajanski commented Jul 2, 2015

Hi as asked here https://help.openstreetmap.org/questions/19213/how-can-i-convert-an-osm-xml-file-into-a-graph-representation as well, I wonder why we need to split all ways (section betweeen lines 144 and 159) ?
If i outcomment that section and run the script I get a fine graph anyway...

@rajanski

This comment has been minimized.

Copy link

@rajanski rajanski commented Sep 14, 2015

See edited version enabling directionality https://gist.github.com/rajanski/ccf65d4f5106c2cdc70e

@Tofull

This comment has been minimized.

Copy link

@Tofull 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 : https://gist.github.com/Tofull/49fbb9f3661e376d2fe08c2e9d64320e

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