osmgraph
========
Create [networkx](https://networkx.github.io/) graphs from OpenStreetMap (OSM)
data. `osmgraph` uses
[imposm-parser](https://github.com/omniscale/imposm-parser) for parsing
OpenStreetMap XML (including bz2) and PBF
files and [osmqa-parser](https://github.com/mapkin/osmqa-parser) for parsing
[OSM QA tiles](http://osmlab.github.io/osm-qa-tiles/).
Usage
-----
```
>>> import osmgraph
>>> g = osmgraph.parse_file(filename)
```
Install [osmqa-parser](https://github.com/mapkin/osmqa-parser) to import a graph from a vector tile. Read the [documentation](https://github.com/mapkin/osmqa-parser#data-model-caveats) to understand the caveats associated with forming a network from OSM QA tiles.
```
### Load data from mbtiles or other source
>>> g = osmgraph.parse_qa_tile(x, y, zoom, data)
```
Graph Structure
---------------
`osmgraph` parses OSM data to create a networkx [directed graph](https://networkx.readthedocs.org/en/stable/reference/classes.digraph.html). OSM nodes correspond directly to the nodes in the directed graph. The OSM tags become attributes of the node. Additionally `osmgraph` adds a `coordinate` attribute containing the (lon, lat) tuple of the node's coordinates.
For example:
```
g = osmgraph.parse_file('boston_massachusetts.osm.bz2')
```
Given the following XML node:
```
```
```
>>> g.node[665539692]
{'coordinate': (-71.0207486, 42.3971185), 'railway': 'level_crossing'}
```
Similarly, the nodes comprising an OSM way form the graph's edges. The way's attributes are duplicated across the edges. For example, given the following XML way:
```
```
```
>>> g[61448456][1102764005]
{'attribution': 'Office of Geographic and Environmental Information (MassGIS)',
'condition': 'fair',
'highway': 'primary',
'massgis:way_id': '134349',
'name': 'North Washington Street',
'oneway': 'yes',
'source': 'massgis_import_v0.1_20071008193615',
'width': '30.2'}
```
Ways that are not oneway roads will have edges in both directions.
Notes
-----
`osmgraph` loads the entire graph in memory. You should be careful how much
data is being loaded. All parsing functions accept a `ways_tag_filter` and
`nodes_tag_filter` arguments. These are functions that accept a dictionary
of node or way tags. They should manipulate the dictionary in place to drop
unused tags.
For example, if we only care about nodes containing a traffic light.
```
def traffic_lights_filter(tags):
if tags.get('highway') != 'traffic_signals':
tags.clear()
g = osmgraph.parse_file(filename, nodes_tag_filter=traffic_lights_filter)
```
Example: Build a Cheapo Router
-----------------------------------
Parse some OSM data, add a `length` property to each edge using
[geog](https://github.com/jwass/geog), use networkx's builtin shortest path
algorithm to find the shortest path between two nodes, use [geojsonio.py](https://github.com/jwass/geojsonio.py) to show the line on [geojson.io](https://geojson.io)
```
import geog
import networkx as nx
import osmgraph
# By default any way with a highway tag will be loaded
g = osmgraph.parse_file('boston_massachusetts.osm.bz2') # or .osm or .pbf
for n1, n2 in g.edges_iter():
c1, c2 = osmgraph.tools.coordinates(g, (n1, n2))
g[n1][n2]['length'] = geog.distance(c1, c2)
import random
start = random.choice(g.nodes())
end = random.choice(g.nodes())
path = nx.shortest_path(g, start, end, 'length')
coords = osmgraph.tools.coordinates(g, path)
# Find the sequence of roads to get from start to end
edge_names = [g[n1][n2].get('name') for n1, n2 in osmgraph.tools.pairwise(path)]
import itertools
names = [k for k, v in itertools.groupby(edge_names)]
print(names)
['North Harvard Street',
'Franklin Street',
'Lincoln Street',
None,
'Cambridge Street',
'Gordon Street',
'Warren Street',
'Commonwealth Avenue']
# Visualize the path using geojsonio.py
import geojsonio
import json
geojsonio.display(json.dumps({'type': 'LineString', 'coordinates': coords}))
```
![Route Line](doc/images/router_line_example.jpg)
See Also
--------
* [networkx](https://networkx.github.io)
* [OSM QA Tiles](https://osmlab.github.io/osm-qa-tiles/)
* [osmqa-parser](https://github.com/mapkin/osmqa-parser/)
* [imposm.parser](https://github.com/omniscale/imposm-paser)
* [tile-reduce](https://github.com/mapbox/tile-reduce)
* [tile-reduce-py](https://github.com/jwass/tile-reduce-py)