Skip to content

Commit

Permalink
Function that connects nodes and their distances
Browse files Browse the repository at this point in the history
Connect adjacent intersection nodes as pairs and calculate
the distance from pair to pair.
  • Loading branch information
jiffyclub committed Dec 19, 2014
1 parent 55675f9 commit e8ed6c6
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 17 deletions.
68 changes: 68 additions & 0 deletions pandana/loaders/osm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
Tools for creating Pandana networks from Open Street Map.
"""
from itertools import islice, izip

import pandas as pd
import requests

from ..utils import great_circle_dist as gcd

uninteresting_tags = {
'source',
'source_ref',
Expand Down Expand Up @@ -190,3 +194,67 @@ def intersection_nodes(waynodes):
"""
counts = waynodes.node_id.value_counts()
return set(counts[counts > 1].index.values)


def node_pairs(nodes, ways, waynodes, two_way=True):
"""
Create a table of node pairs with the distances between them.
Parameters
----------
nodes : pandas.DataFrame
Must have 'lat' and 'lon' columns.
ways : pandas.DataFrame
Table of way metadata.
waynodes : pandas.DataFrame
Table linking way IDs to node IDs. Way IDs should be in the index,
with a column called 'node_ids'.
two_way : bool, optional
Whether the routes are two-way. If True, node pairs will only
occur once.
Returns
-------
pairs : pandas.DataFrame
Will have columns of 'from_id', 'to_id', and 'distance'.
The index will be a MultiIndex of (from id, to id).
The distance metric is in meters.
"""
pairwise = lambda l: izip(islice(l, 0, len(l)), islice(l, 1, None))
intersections = intersection_nodes(waynodes)
waymap = waynodes.groupby(level=0, sort=False)
pairs = []

for id, row in ways.iterrows():
nodes_in_way = waymap.get_group(id).node_id.values
nodes_in_way = filter(lambda x: x in intersections, nodes_in_way)

if len(nodes_in_way) < 2:
# no nodes to connect in this way
continue

for from_node, to_node in pairwise(nodes_in_way):
fn = nodes.loc[from_node]
tn = nodes.loc[to_node]

distance = gcd(fn.lat, fn.lon, tn.lat, tn.lon)

pairs.append({
'from_id': from_node,
'to_id': to_node,
'distance': distance
})

if not two_way:
pairs.append({
'from_id': to_node,
'to_id': from_node,
'distance': distance
})

pairs = pd.DataFrame.from_records(pairs)
pairs.index = pd.MultiIndex.from_arrays(
[pairs.from_id.values, pairs.to_id.values])

return pairs
93 changes: 76 additions & 17 deletions pandana/loaders/tests/test_osm.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,50 @@
import numpy.testing as npt
import pandas.util.testing as pdt
import pytest

from pandana.loaders import osm


@pytest.fixture(scope='module')
def bbox():
def bbox1():
# Intersection of Telegraph and Haste in Berkeley
# Sample query: http://overpass-turbo.eu/s/6AK
return 37.8659303546, -122.2588003879, 37.8661598571, -122.2585062512


@pytest.fixture(scope='module')
def query_data(bbox):
return osm.make_osm_query(*bbox)
def bbox2():
# Telegraph Channing to Durant in Berkeley
# Sample query: http://overpass-turbo.eu/s/6B0
return 37.8668405874, -122.2590948685, 37.8679028054, -122.2586363885


@pytest.fixture(scope='module')
def dataframes(query_data):
return osm.parse_osm_query(query_data)
def query_data1(bbox1):
return osm.make_osm_query(*bbox1)


def test_make_osm_query(query_data):
assert isinstance(query_data, dict)
assert len(query_data['elements']) == 24
@pytest.fixture(scope='module')
def query_data2(bbox2):
return osm.make_osm_query(*bbox2)


@pytest.fixture(scope='module')
def dataframes1(query_data1):
return osm.parse_osm_query(query_data1)


@pytest.fixture(scope='module')
def dataframes2(query_data2):
return osm.parse_osm_query(query_data2)


def test_make_osm_query(query_data1):
assert isinstance(query_data1, dict)
assert len(query_data1['elements']) == 24
assert len(
[e for e in query_data['elements'] if e['type'] == 'node']) == 22
assert len([e for e in query_data['elements'] if e['type'] == 'way']) == 2
[e for e in query_data1['elements'] if e['type'] == 'node']) == 22
assert len([e for e in query_data1['elements'] if e['type'] == 'way']) == 2


def test_process_node():
Expand Down Expand Up @@ -89,25 +108,65 @@ def test_process_way():
assert waynodes == expected_waynodes


def test_parse_osm_query(dataframes):
nodes, ways, waynodes = dataframes
def test_parse_osm_query(dataframes1):
nodes, ways, waynodes = dataframes1

assert len(nodes) == 22
assert len(ways) == 2
assert len(waynodes.index.unique()) == 2


def test_ways_in_bbox(bbox, dataframes):
nodes, ways, waynodes = osm.ways_in_bbox(*bbox)
exp_nodes, exp_ways, exp_waynodes = dataframes
def test_ways_in_bbox(bbox1, dataframes1):
nodes, ways, waynodes = osm.ways_in_bbox(*bbox1)
exp_nodes, exp_ways, exp_waynodes = dataframes1

pdt.assert_frame_equal(nodes, exp_nodes)
pdt.assert_frame_equal(ways, exp_ways)
pdt.assert_frame_equal(waynodes, exp_waynodes)


def test_intersection_nodes(dataframes):
_, _, waynodes = dataframes
def test_intersection_nodes1(dataframes1):
_, _, waynodes = dataframes1
intersections = osm.intersection_nodes(waynodes)

assert intersections == {53041093}


def test_intersection_nodes2(dataframes2):
_, _, waynodes = dataframes2
intersections = osm.intersection_nodes(waynodes)

assert intersections == {53099275, 53063555}


def test_node_pairs_two_way(dataframes2):
nodes, ways, waynodes = dataframes2
pairs = osm.node_pairs(nodes, ways, waynodes)

assert len(pairs) == 1

fn = 53063555
tn = 53099275

pair = pairs.loc[(fn, tn)]

assert pair.from_id == fn
assert pair.to_id == tn
npt.assert_allclose(pair.distance, 101.20535797547758)


def test_node_pairs_one_way(dataframes2):
nodes, ways, waynodes = dataframes2
pairs = osm.node_pairs(nodes, ways, waynodes, two_way=False)

assert len(pairs) == 2

n1 = 53063555
n2 = 53099275

for p1, p2 in [(n1, n2), (n2, n1)]:
pair = pairs.loc[(p1, p2)]

assert pair.from_id == p1
assert pair.to_id == p2
npt.assert_allclose(pair.distance, 101.20535797547758)

0 comments on commit e8ed6c6

Please sign in to comment.