Created
January 26, 2010 23:34
-
-
Save aflaxman/287370 to your computer and use it in GitHub Desktop.
networkx-osm import open street map data as a networkx graph
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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 |
See edited version enabling directionality https://gist.github.com/rajanski/ccf65d4f5106c2cdc70e
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
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...