From 384e26bc31553691103f0d14916fc74bf9edb1bf Mon Sep 17 00:00:00 2001 From: waddell Date: Mon, 7 Nov 2016 14:26:10 -0800 Subject: [PATCH 01/42] Fixed broken link to paper to point to TRB online copy. --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 2f5e8b87..fab8d2cf 100644 --- a/README.rst +++ b/README.rst @@ -66,7 +66,7 @@ i.e. ANN (http://www.cs.umd.edu/~mount/ANN/). Academic Literature ------------------- -I'm currently working on getting a `complete description of the -methodology `__ -published in an academic journal. Please cite this paper when referring +A `complete description of the +methodology `__ +was presented at the Transportation Research Board Annual Conference in 2012. Please cite this paper when referring to the methodology implemented by this library. From 02ae14ab530fb5cba99c992167fb709f850ee719 Mon Sep 17 00:00:00 2001 From: waddell Date: Mon, 7 Nov 2016 14:28:56 -0800 Subject: [PATCH 02/42] Fixed typo in link to paper. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index fab8d2cf..50f75661 100644 --- a/README.rst +++ b/README.rst @@ -67,6 +67,6 @@ Academic Literature ------------------- A `complete description of the -methodology `__ +methodology `__ was presented at the Transportation Research Board Annual Conference in 2012. Please cite this paper when referring to the methodology implemented by this library. From ef4ab65f7093e3bb8b4dd0581112804dd3789e61 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Tue, 8 Nov 2016 10:58:23 -0800 Subject: [PATCH 03/42] Fixed failed Travis build by updating tests in test_osm.py to account for updated OSM data that is being pulled for the tests. Changes include 4 tests: test_node_pairs_one_way(dataframes2), test_parse_network_osm_query(dataframes1), test_make_osm_query(query_data1), test_node_pairs_two_way(dataframes2). --- pandana/loaders/tests/test_osm.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pandana/loaders/tests/test_osm.py b/pandana/loaders/tests/test_osm.py index 76444b60..4e810b04 100644 --- a/pandana/loaders/tests/test_osm.py +++ b/pandana/loaders/tests/test_osm.py @@ -52,10 +52,9 @@ def dataframes2(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_data1['elements'] if e['type'] == 'node']) == 22 - assert len([e for e in query_data1['elements'] if e['type'] == 'way']) == 2 + assert len(query_data1['elements']) == 42 + assert len([e for e in query_data1['elements'] if e['type'] == 'node']) == 39 + assert len([e for e in query_data1['elements'] if e['type'] == 'way']) == 3 def test_process_node(): @@ -122,9 +121,9 @@ def test_process_way(): def test_parse_network_osm_query(dataframes1): nodes, ways, waynodes = dataframes1 - assert len(nodes) == 22 - assert len(ways) == 2 - assert len(waynodes.index.unique()) == 2 + assert len(nodes) == 39 + assert len(ways) == 3 + assert len(waynodes.index.unique()) == 3 def test_parse_network_osm_query_raises(): @@ -181,7 +180,7 @@ def test_node_pairs_two_way(dataframes2): assert pair.from_id == fn assert pair.to_id == tn - npt.assert_allclose(pair.distance, 101.20535797547758) + npt.assert_allclose(pair.distance, 101.48279182499789) def test_node_pairs_one_way(dataframes2): @@ -198,7 +197,7 @@ def test_node_pairs_one_way(dataframes2): assert pair.from_id == p1 assert pair.to_id == p2 - npt.assert_allclose(pair.distance, 101.20535797547758) + npt.assert_allclose(pair.distance, 101.48279182499789) @skipiftravis From a6d2e92b17b06e263ef8741a8bf80ab837dd30d0 Mon Sep 17 00:00:00 2001 From: Paul Waddell Date: Wed, 9 Nov 2016 21:05:41 -0800 Subject: [PATCH 04/42] Updated license --- LICENSE | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 722f25ef..dce41ce7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,8 @@ GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 - Copyright (C) Autodesk + Copyright (C) UrbanSim Inc. + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. From 7667cb0e32dd4bdd2ae38ab221539ccde6dd77c5 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Thu, 10 Nov 2016 16:01:47 -0800 Subject: [PATCH 05/42] changed author name from Autodesk to UrbanSim Inc. throughout --- docs/conf.py | 8 ++++---- docs/installation.rst | 2 +- docs/tutorial.rst | 2 +- setup.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index b95f7a98..ac97b091 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ # General information about the project. project = u'pandana' -copyright = u'2015, Autodesk' +copyright = u'2015, UrbanSim Inc.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -204,7 +204,7 @@ # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'pandana.tex', u'pandana Documentation', - u'Autodesk', 'manual'), + u'UrbanSim Inc.', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -234,7 +234,7 @@ # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pandana', u'pandana Documentation', - [u'Autodesk'], 1) + [u'UrbanSim Inc.'], 1) ] # If true, show URL addresses after external links. @@ -248,7 +248,7 @@ # dir menu entry, description, category) texinfo_documents = [ ('index', 'pandana', u'pandana Documentation', - u'Autodesk', 'pandana', 'One line description of project.', + u'UrbanSim Inc.', 'pandana', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/installation.rst b/docs/installation.rst index f857c07b..c1347eca 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -29,7 +29,7 @@ conda ~~~~~ Pandana and some of its dependencies are hosted on -`Autodesks's Anaconda repository `__. +`UrbanSim Inc's Anaconda repository `__. To add this as a default installation channel for conda run this code in a terminal:: diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 84c2a84f..7e127017 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -206,7 +206,7 @@ happen in the notebook) is also available. Note that these have a bounding box for reducing the display window. Although the underlying library is computing values for all nodes in the region, it is extremely difficult to visualize this much data using -matplotlib. The GeoCanvas tool by Autodesk is expressly designed to join +matplotlib. The GeoCanvas tool by UrbanSim Inc. is expressly designed to join indicators at the node level to shapes of parcels and produces a much more professional output map. For quick interactive checking of results, the bounding box can be used to reduce the number of points that are shown, diff --git a/setup.py b/setup.py index f9eabf36..086d50c2 100644 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ def run_tests(self): setup( packages=packages, name='pandana', - author='Autodesk', + author='UrbanSim Inc.', version=version, license='AGPL', description=('Pandas Network Analysis - ' From 27188dedac66897382dde99fe3e7bc6c32fe2986 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Fri, 3 Mar 2017 12:48:37 -0800 Subject: [PATCH 06/42] swapped out pandana.loaders.osm network functions with new osmnet repo function calls. removed tests affiliated with removed functions and updated docs to reflect new function --- docs/loaders.rst | 6 +- pandana/loaders/osm.py | 304 ++++-------------------- pandana/loaders/tests/test_osm.py | 149 +----------- pandana/tests/test_great_circle_dist.py | 16 -- pandana/utils.py | 38 --- setup.py | 3 +- 6 files changed, 51 insertions(+), 465 deletions(-) delete mode 100644 pandana/tests/test_great_circle_dist.py delete mode 100644 pandana/utils.py diff --git a/docs/loaders.rst b/docs/loaders.rst index c0e6e314..66106afe 100644 --- a/docs/loaders.rst +++ b/docs/loaders.rst @@ -11,10 +11,10 @@ OpenStreetMap ------------- A :py:class:`~pandana.network.Network` is created from OpenStreetMap using -the :py:func:`~pandana.loaders.osm.network_from_bbox` function:: +the :py:func:`~pandana.loaders.osm.pdna_network_from_bbox` function:: from pandana.loaders import osm - network = osm.network_from_bbox(37.859, -122.282, 37.881, -122.252) + network = osm.pdna_network_from_bbox(37.859, -122.282, 37.881, -122.252) By default the generated network contains only walkable routes, specify ``type='drive'`` to get driveable routes. @@ -71,7 +71,7 @@ then exclude those nodes when saving to HDF5:: OpenStreetMap API ----------------- -.. autofunction:: pandana.loaders.osm.network_from_bbox +.. autofunction:: pandana.loaders.osm.pdna_network_from_bbox .. autofunction:: pandana.loaders.osm.node_query diff --git a/pandana/loaders/osm.py b/pandana/loaders/osm.py index 3d5af822..b147cbf1 100644 --- a/pandana/loaders/osm.py +++ b/pandana/loaders/osm.py @@ -1,104 +1,78 @@ """ -Tools for creating Pandana networks from Open Street Map. +Tools for creating Pandana networks from OpenStreetMap. """ -from itertools import islice, izip import pandas as pd import requests +from osmnet.load import network_from_bbox from .. import Network -from ..utils import great_circle_dist as gcd - -uninteresting_tags = { - 'source', - 'source_ref', - 'source:ref', - 'history', - 'attribution', - 'created_by', - 'tiger:tlid', - 'tiger:upload_uuid', -} - - -def build_network_osm_query( - lat_min, lng_min, lat_max, lng_max, network_type='walk'): + + +def pdna_network_from_bbox( + lat_min=None, lng_min=None, lat_max=None, lng_max=None, bbox=None, network_type='walk', two_way=True, + timeout=180, memory=None, max_query_area_size=50*1000*50*1000): """ - Construct an OSM way query for a bounding box. + Make a Pandana network from a bounding lat/lon box request to the Overpass API Parameters ---------- lat_min, lng_min, lat_max, lng_max : float + bbox : tuple + Bounding box formatted as a 4 element tuple: (lng_max, lat_min, lng_min, lat_max) network_type : {'walk', 'drive'}, optional Specify whether the network will be used for walking or driving. A value of 'walk' attempts to exclude things like freeways, while a value of 'drive' attempts to exclude things like bike and walking paths. + two_way : bool, optional + Whether the routes are two-way. If True, node pairs will only + occur once. + timeout : int, optional + the timeout interval for requests and to pass to Overpass API + memory : int, optional + server memory allocation size for the query, in bytes. If none, server will use its default allocation size + max_query_area_size : float, optional + max area for any part of the geometry, in the units the geometry is in Returns ------- - query : str - - """ - query_fmt = ( - '[out:json];' - '(' - ' way' - ' ["highway"]' - ' {filters}' - ' ({lat_min},{lng_min},{lat_max},{lng_max});' - ' >;' # the '>' makes it recurse so we get ways and way nodes - ');' - 'out;') - - if network_type == 'walk': - filters = '["highway"!~"motor"]' - elif network_type == 'drive': - filters = '["highway"!~"foot|cycle"]' - else: - raise ValueError('Invalid network_type argument') - - return query_fmt.format( - lat_min=lat_min, lng_min=lng_min, lat_max=lat_max, lng_max=lng_max, - filters=filters) - + network : pandana.Network -def make_osm_query(query): """ - Make a request to OSM and return the parsed JSON. - Parameters - ---------- - query : str - A string in the Overpass QL format. + nodes, edges = network_from_bbox(lat_min=lat_min, lng_min=lng_min, lat_max=lat_max, lng_max=lng_max, bbox=bbox, network_type=network_type, + two_way=two_way, timeout=timeout, memory=memory, max_query_area_size=max_query_area_size) - Returns - ------- - data : dict - - """ - osm_url = 'http://www.overpass-api.de/api/interpreter' - req = requests.get(osm_url, params={'data': query}) - req.raise_for_status() - - return req.json() + return Network( + nodes['x'], nodes['y'], + edges['from'], edges['to'], edges[['distance']]) def process_node(e): """ Process a node element entry into a dict suitable for going into a Pandas DataFrame. - Parameters ---------- e : dict - Returns ------- node : dict - """ + + uninteresting_tags = { + 'source', + 'source_ref', + 'source:ref', + 'history', + 'attribution', + 'created_by', + 'tiger:tlid', + 'tiger:upload_uuid', + } + node = { 'id': e['id'], 'lat': e['lat'], @@ -113,213 +87,25 @@ def process_node(e): return node -def process_way(e): +def make_osm_query(query): """ - Process a way element entry into a list of dicts suitable for going into - a Pandas DataFrame. + Make a request to OSM and return the parsed JSON. Parameters ---------- - e : dict + query : str + A string in the Overpass QL format. Returns ------- - way : dict - waynodes : list of dict - - """ - way = { - 'id': e['id'] - } - - if 'tags' in e: - for t, v in e['tags'].items(): - if t not in uninteresting_tags: - way[t] = v - - waynodes = [] - - for n in e['nodes']: - waynodes.append({'way_id': e['id'], 'node_id': n}) - - return way, waynodes - - -def parse_network_osm_query(data): - """ - Convert OSM query data to DataFrames of ways and way-nodes. - - Parameters - ---------- data : dict - Result of an OSM query. - - Returns - ------- - nodes, ways, waynodes : pandas.DataFrame - - """ - if len(data['elements']) == 0: - raise RuntimeError('OSM query results contain no data.') - - nodes = [] - ways = [] - waynodes = [] - - for e in data['elements']: - if e['type'] == 'node': - nodes.append(process_node(e)) - elif e['type'] == 'way': - w, wn = process_way(e) - ways.append(w) - waynodes.extend(wn) - - return ( - pd.DataFrame.from_records(nodes, index='id'), - pd.DataFrame.from_records(ways, index='id'), - pd.DataFrame.from_records(waynodes, index='way_id')) - -def ways_in_bbox(lat_min, lng_min, lat_max, lng_max, network_type='walk'): """ - Get DataFrames of OSM data in a bounding box. - - Parameters - ---------- - lat_min, lng_min, lat_max, lng_max : float - network_type : {'walk', 'drive'}, optional - Specify whether the network will be used for walking or driving. - A value of 'walk' attempts to exclude things like freeways, - while a value of 'drive' attempts to exclude things like - bike and walking paths. - - Returns - ------- - nodes, ways, waynodes : pandas.DataFrame - - """ - return parse_network_osm_query(make_osm_query(build_network_osm_query( - lat_min, lng_min, lat_max, lng_max, network_type=network_type))) - - -def intersection_nodes(waynodes): - """ - Returns a set of all the nodes that appear in 2 or more ways. - - Parameters - ---------- - waynodes : pandas.DataFrame - Mapping of way IDs to node IDs as returned by `ways_in_bbox`. - - Returns - ------- - intersections : set - Node IDs that appear in 2 or more ways. - - """ - 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. - - """ - def pairwise(l): - return 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 - - -def network_from_bbox( - lat_min, lng_min, lat_max, lng_max, network_type='walk', two_way=True): - """ - Make a Pandana network from a bounding lat/lon box. - - Parameters - ---------- - lat_min, lng_min, lat_max, lng_max : float - network_type : {'walk', 'drive'}, optional - Specify whether the network will be used for walking or driving. - A value of 'walk' attempts to exclude things like freeways, - while a value of 'drive' attempts to exclude things like - bike and walking paths. - two_way : bool, optional - Whether the routes are two-way. If True, node pairs will only - occur once. - - Returns - ------- - network : pandana.Network - - """ - nodes, ways, waynodes = ways_in_bbox( - lat_min, lng_min, lat_max, lng_max, network_type) - pairs = node_pairs(nodes, ways, waynodes, two_way=two_way) - - # make the unique set of nodes that ended up in pairs - node_ids = sorted( - set(pairs['from_id'].unique()).union(set(pairs['to_id'].unique()))) - nodes = nodes.loc[node_ids] + osm_url = 'http://www.overpass-api.de/api/interpreter' + req = requests.get(osm_url, params={'data': query}) + req.raise_for_status() - return Network( - nodes['lon'], nodes['lat'], - pairs['from_id'], pairs['to_id'], pairs[['distance']]) + return req.json() def build_node_query(lat_min, lng_min, lat_max, lng_max, tags=None): diff --git a/pandana/loaders/tests/test_osm.py b/pandana/loaders/tests/test_osm.py index 4e810b04..23c99ae8 100644 --- a/pandana/loaders/tests/test_osm.py +++ b/pandana/loaders/tests/test_osm.py @@ -1,5 +1,3 @@ -import numpy.testing as npt -import pandas.util.testing as pdt import pytest import pandana @@ -21,35 +19,11 @@ def bbox2(): return 37.8668405874, -122.2590948685, 37.8679028054, -122.2586363885 -@pytest.fixture(scope='module') -def bbox3(): - # West Berkeley including highway 80, frontage roads, and foot paths - # Sample query: http://overpass-turbo.eu/s/6VE - return ( - 37.85225504880375, -122.30295896530151, - 37.85776128099243, - 122.2954273223877) - - @pytest.fixture(scope='module') def query_data1(bbox1): return osm.make_osm_query(osm.build_network_osm_query(*bbox1)) -@pytest.fixture(scope='module') -def query_data2(bbox2): - return osm.make_osm_query(osm.build_network_osm_query(*bbox2)) - - -@pytest.fixture(scope='module') -def dataframes1(query_data1): - return osm.parse_network_osm_query(query_data1) - - -@pytest.fixture(scope='module') -def dataframes2(query_data2): - return osm.parse_network_osm_query(query_data2) - - def test_make_osm_query(query_data1): assert isinstance(query_data1, dict) assert len(query_data1['elements']) == 42 @@ -79,130 +53,9 @@ def test_process_node(): assert osm.process_node(test_node) == expected -def test_process_way(): - test_way = { - "type": "way", - "id": 188434143, - "timestamp": "2014-01-04T22:18:14Z", - "version": 2, - "changeset": 19814115, - "user": "dchiles", - "uid": 153669, - "nodes": [ - 53020977, - 53041093, - ], - "tags": { - 'source': 'source', - "addr:city": "Berkeley", - "highway": "secondary", - "name": "Telegraph Avenue", - } - } - - expected_way = { - 'id': test_way['id'], - 'addr:city': test_way['tags']['addr:city'], - 'highway': test_way['tags']['highway'], - 'name': test_way['tags']['name'] - } - - expected_waynodes = [ - {'way_id': test_way['id'], 'node_id': test_way['nodes'][0]}, - {'way_id': test_way['id'], 'node_id': test_way['nodes'][1]} - ] - - way, waynodes = osm.process_way(test_way) - - assert way == expected_way - assert waynodes == expected_waynodes - - -def test_parse_network_osm_query(dataframes1): - nodes, ways, waynodes = dataframes1 - - assert len(nodes) == 39 - assert len(ways) == 3 - assert len(waynodes.index.unique()) == 3 - - -def test_parse_network_osm_query_raises(): - data = osm.make_osm_query(osm.build_network_osm_query( - 37.8, -122.252, 37.8, -122.252)) - with pytest.raises(RuntimeError): - osm.parse_network_osm_query(data) - - -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) - - -@pytest.mark.parametrize( - 'network_type, noset', - [('walk', {'motorway', 'motorway_link'}), - ('drive', {'footway', 'cycleway'})]) -def test_ways_in_bbox_walk_network(bbox3, network_type, noset): - nodes, ways, waynodes = osm.ways_in_bbox(*bbox3, network_type=network_type) - - for _, way in ways.iterrows(): - assert way['highway'] not in noset - - -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.48279182499789) - - -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.48279182499789) - - @skipiftravis def test_network_from_bbox(bbox2): - net = osm.network_from_bbox(*bbox2) + net = osm.pdna_network_from_bbox(*bbox2) assert isinstance(net, pandana.Network) diff --git a/pandana/tests/test_great_circle_dist.py b/pandana/tests/test_great_circle_dist.py deleted file mode 100644 index c34b35b7..00000000 --- a/pandana/tests/test_great_circle_dist.py +++ /dev/null @@ -1,16 +0,0 @@ -import numpy.testing as npt - -from pandana.utils import great_circle_dist as gcd - - -def test_gcd(): - # tested against geopy - # https://geopy.readthedocs.org/en/latest/#module-geopy.distance - lat1 = 41.49008 - lon1 = -71.312796 - lat2 = 41.499498 - lon2 = -81.695391 - - expected = 864456.76162966 - - npt.assert_allclose(gcd(lat1, lon1, lat2, lon2), expected) diff --git a/pandana/utils.py b/pandana/utils.py deleted file mode 100644 index 97086573..00000000 --- a/pandana/utils.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import division - -import math - - -def great_circle_dist(lat1, lon1, lat2, lon2): - """ - Get the distance (in meters) between two lat/lon points - via the Haversine formula. - - Parameters - ---------- - lat1, lon1, lat2, lon2 : float - Latitude and longitude in degrees. - - Returns - ------- - dist : float - Distance in meters. - - """ - radius = 6372795 # meters - - lat1 = math.radians(lat1) - lon1 = math.radians(lon1) - lat2 = math.radians(lat2) - lon2 = math.radians(lon2) - - dlat = lat2 - lat1 - dlon = lon2 - lon1 - - # formula from: - # http://en.wikipedia.org/wiki/Haversine_formula#The_haversine_formula - a = math.pow(math.sin(dlat / 2), 2) - b = math.cos(lat1) * math.cos(lat2) * math.pow(math.sin(dlon / 2), 2) - d = 2 * radius * math.asin(math.sqrt(a + b)) - - return d diff --git a/setup.py b/setup.py index 086d50c2..3a900c05 100644 --- a/setup.py +++ b/setup.py @@ -120,7 +120,8 @@ def run_tests(self): 'numpy>=1.8.0', 'pandas>=0.13.1', 'requests>=2.0', - 'tables>=3.1.0' + 'tables>=3.1.0', + 'osmnet>=0.1a', ], tests_require=['pytest'], cmdclass={'test': PyTest}, From c82e7108a85e11b94161daa502c6462b38187a3d Mon Sep 17 00:00:00 2001 From: sablanchard Date: Tue, 7 Mar 2017 15:17:44 -0800 Subject: [PATCH 07/42] fixed travis.yaml to install osmnet --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d4428e3f..025138e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ install: - | conda create -q -c synthicity -n test-environment python=$TRAVIS_PYTHON_VERSION basemap matplotlib numpy pandas pip pytables requests - source activate test-environment +- pip install https://github.com/UDST/osmnet/archive/master.zip - pip install pytest-cov coveralls pep8 - python setup.py install script: From 15356a836b647a3ae90851ebb46e020f18a5f0ca Mon Sep 17 00:00:00 2001 From: sablanchard Date: Wed, 8 Mar 2017 14:37:49 -0800 Subject: [PATCH 08/42] fixed osm test --- pandana/loaders/tests/test_osm.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pandana/loaders/tests/test_osm.py b/pandana/loaders/tests/test_osm.py index 23c99ae8..a2859e74 100644 --- a/pandana/loaders/tests/test_osm.py +++ b/pandana/loaders/tests/test_osm.py @@ -19,18 +19,6 @@ def bbox2(): return 37.8668405874, -122.2590948685, 37.8679028054, -122.2586363885 -@pytest.fixture(scope='module') -def query_data1(bbox1): - return osm.make_osm_query(osm.build_network_osm_query(*bbox1)) - - -def test_make_osm_query(query_data1): - assert isinstance(query_data1, dict) - assert len(query_data1['elements']) == 42 - assert len([e for e in query_data1['elements'] if e['type'] == 'node']) == 39 - assert len([e for e in query_data1['elements'] if e['type'] == 'way']) == 3 - - def test_process_node(): test_node = { 'id': 'id', From 362d7e51c728879636013c88c3c26d4f17b81cb9 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Fri, 10 Mar 2017 10:38:13 -0800 Subject: [PATCH 09/42] fixed formatting --- pandana/loaders/osm.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pandana/loaders/osm.py b/pandana/loaders/osm.py index b147cbf1..ac315e21 100644 --- a/pandana/loaders/osm.py +++ b/pandana/loaders/osm.py @@ -12,7 +12,7 @@ def pdna_network_from_bbox( lat_min=None, lng_min=None, lat_max=None, lng_max=None, bbox=None, network_type='walk', two_way=True, - timeout=180, memory=None, max_query_area_size=50*1000*50*1000): + timeout=180, memory=None, max_query_area_size=50 * 1000 * 50 * 1000): """ Make a Pandana network from a bounding lat/lon box request to the Overpass API @@ -42,8 +42,9 @@ def pdna_network_from_bbox( """ - nodes, edges = network_from_bbox(lat_min=lat_min, lng_min=lng_min, lat_max=lat_max, lng_max=lng_max, bbox=bbox, network_type=network_type, - two_way=two_way, timeout=timeout, memory=memory, max_query_area_size=max_query_area_size) + nodes, edges = network_from_bbox(lat_min=lat_min, lng_min=lng_min, lat_max=lat_max, lng_max=lng_max, + bbox=bbox, network_type=network_type, two_way=two_way, timeout=timeout, + memory=memory, max_query_area_size=max_query_area_size) return Network( nodes['x'], nodes['y'], From f39ec9a2d48609e1285d0d338d36da2fbc0ac705 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Fri, 10 Mar 2017 13:07:22 -0800 Subject: [PATCH 10/42] debug travis test --- pandana/network.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandana/network.py b/pandana/network.py index 023712be..ba8dde7f 100644 --- a/pandana/network.py +++ b/pandana/network.py @@ -603,6 +603,8 @@ def nearest_pois(self, distance, category, num_pois=1, max_distance=None, # thinking. it's complicated on the inside, but quite # intuitive to the user I think s = df2[col] + print(category) + print(s) df2[col] = self.poi_category_indexes[category].values[s] df2[col][s == -1] = np.nan From 8a179acacd152b285bbb88d82452862ed303f8eb Mon Sep 17 00:00:00 2001 From: Fletcher Foti Date: Fri, 10 Mar 2017 14:29:07 -0800 Subject: [PATCH 11/42] should allow poit queries on multiple graphs --- pandana/network.py | 6 +++--- pandana/tests/test_pandana.py | 28 +++++++++++++++++++++++++++- src/pyaccesswrap.cpp | 12 ++++++------ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/pandana/network.py b/pandana/network.py index 023712be..145879dd 100644 --- a/pandana/network.py +++ b/pandana/network.py @@ -482,7 +482,7 @@ def init_pois(self, num_categories, max_dist, max_pois): self.num_poi_categories = num_categories self.max_pois = max_pois - _pyaccess.initialize_pois(num_categories, max_dist, max_pois) + _pyaccess.initialize_pois(num_categories, max_dist, max_pois, self.graph_no) def set_pois(self, category, x_col, y_col): """ @@ -515,7 +515,7 @@ def set_pois(self, category, x_col, y_col): self.poi_category_indexes[category] = xys.index _pyaccess.initialize_category(self.poi_category_names.index(category), - xys.astype('float32')) + xys.astype('float32'), self.graph_no) def nearest_pois(self, distance, category, num_pois=1, max_distance=None, imp_name=None, include_poi_ids=False): @@ -602,7 +602,7 @@ def nearest_pois(self, distance, category, num_pois=1, max_distance=None, # initialized as a pandas series - this really is pandas-like # thinking. it's complicated on the inside, but quite # intuitive to the user I think - s = df2[col] + s = df2[col].astype('int') df2[col] = self.poi_category_indexes[category].values[s] df2[col][s == -1] = np.nan diff --git a/pandana/tests/test_pandana.py b/pandana/tests/test_pandana.py index 69bc7de5..59247586 100644 --- a/pandana/tests/test_pandana.py +++ b/pandana/tests/test_pandana.py @@ -33,6 +33,24 @@ def fin(): return net +# initialize a second network +@pytest.fixture(scope="module") +def second_sample_osm(request): + store = pd.HDFStore( + os.path.join(os.path.dirname(__file__), 'osm_sample.h5'), "r") + nodes, edges = store.nodes, store.edges + net = pdna.Network(nodes.x, nodes.y, edges["from"], edges.to, + edges[["weight"]]) + + net.precompute(2000) + + def fin(): + store.close() + request.addfinalizer(fin) + + return net + + def random_node_ids(net, ssize): return pd.Series(np.random.choice(net.node_ids, ssize)) @@ -184,8 +202,9 @@ def test_plot(sample_osm): sample_osm.plot(s) -def test_pois(sample_osm): +def test_pois(sample_osm, second_sample_osm): net = sample_osm + net2 = second_sample_osm ssize = 50 np.random.seed(0) @@ -212,6 +231,13 @@ def test_pois(sample_osm): with pytest.raises(AssertionError): net.nearest_pois(2000, "restaurants", num_pois=11) + # make sure poi searches work on second graph + net2.init_pois(num_categories=1, max_dist=2000, max_pois=10) + + net2.set_pois("restaurants", x, y) + + print net2.nearest_pois(2000, "restaurants", num_pois=10) + def test_pois_indexes(sample_osm): net = sample_osm diff --git a/src/pyaccesswrap.cpp b/src/pyaccesswrap.cpp index 88da371b..ef0eb865 100644 --- a/src/pyaccesswrap.cpp +++ b/src/pyaccesswrap.cpp @@ -84,11 +84,11 @@ create_graph(PyObject *self, PyObject *args) static PyObject * initialize_pois(PyObject *self, PyObject *args) { - int nc, mi; + int nc, mi, gno; double md; - if (!PyArg_ParseTuple(args, "idi", &nc, &md, &mi)) return NULL; + if (!PyArg_ParseTuple(args, "idii", &nc, &md, &mi, &gno)) return NULL; - std::shared_ptr sa = sas[0]; + std::shared_ptr sa = sas[gno]; sa->initializePOIs(nc,md,mi); @@ -99,11 +99,11 @@ initialize_pois(PyObject *self, PyObject *args) static PyObject * initialize_category(PyObject *self, PyObject *args) { - int id; + int id, gno; PyObject *input1; - if (!PyArg_ParseTuple(args, "iO", &id, &input1)) return NULL; + if (!PyArg_ParseTuple(args, "iOi", &id, &input1, &gno)) return NULL; - std::shared_ptr sa = sas[0]; + std::shared_ptr sa = sas[gno]; PyArrayObject *pyo; pyo = (PyArrayObject*)PyArray_ContiguousFromObject(input1, From 17a0f2d1e7bd00932cc9f5ce898c3f6a61df6822 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Fri, 10 Mar 2017 14:30:40 -0800 Subject: [PATCH 12/42] debug travis test and add logs to git ignore --- .gitignore | 3 +++ pandana/network.py | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index fa0da1f0..8f21bdc5 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ coverage.xml # Sphinx documentation docs/_build/ + +# OSMnet logs +logs diff --git a/pandana/network.py b/pandana/network.py index ba8dde7f..b92e0ab8 100644 --- a/pandana/network.py +++ b/pandana/network.py @@ -602,9 +602,7 @@ def nearest_pois(self, distance, category, num_pois=1, max_distance=None, # initialized as a pandas series - this really is pandas-like # thinking. it's complicated on the inside, but quite # intuitive to the user I think - s = df2[col] - print(category) - print(s) + s = df2[col].astype('int') df2[col] = self.poi_category_indexes[category].values[s] df2[col][s == -1] = np.nan From ad961cfcff942a3ce87e5572a65fbdc67a2a493c Mon Sep 17 00:00:00 2001 From: sablanchard Date: Fri, 10 Mar 2017 14:37:42 -0800 Subject: [PATCH 13/42] fixed pep8 compliance --- pandana/loaders/osm.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pandana/loaders/osm.py b/pandana/loaders/osm.py index ac315e21..4748b29e 100644 --- a/pandana/loaders/osm.py +++ b/pandana/loaders/osm.py @@ -11,16 +11,19 @@ def pdna_network_from_bbox( - lat_min=None, lng_min=None, lat_max=None, lng_max=None, bbox=None, network_type='walk', two_way=True, + lat_min=None, lng_min=None, lat_max=None, lng_max=None, bbox=None, + network_type='walk', two_way=True, timeout=180, memory=None, max_query_area_size=50 * 1000 * 50 * 1000): """ - Make a Pandana network from a bounding lat/lon box request to the Overpass API + Make a Pandana network from a bounding lat/lon box + request to the Overpass API Parameters ---------- lat_min, lng_min, lat_max, lng_max : float bbox : tuple - Bounding box formatted as a 4 element tuple: (lng_max, lat_min, lng_min, lat_max) + Bounding box formatted as a 4 element tuple: + (lng_max, lat_min, lng_min, lat_max) network_type : {'walk', 'drive'}, optional Specify whether the network will be used for walking or driving. A value of 'walk' attempts to exclude things like freeways, @@ -32,7 +35,8 @@ def pdna_network_from_bbox( timeout : int, optional the timeout interval for requests and to pass to Overpass API memory : int, optional - server memory allocation size for the query, in bytes. If none, server will use its default allocation size + server memory allocation size for the query, in bytes. + If none, server will use its default allocation size max_query_area_size : float, optional max area for any part of the geometry, in the units the geometry is in @@ -42,9 +46,12 @@ def pdna_network_from_bbox( """ - nodes, edges = network_from_bbox(lat_min=lat_min, lng_min=lng_min, lat_max=lat_max, lng_max=lng_max, - bbox=bbox, network_type=network_type, two_way=two_way, timeout=timeout, - memory=memory, max_query_area_size=max_query_area_size) + nodes, edges = network_from_bbox(lat_min=lat_min, lng_min=lng_min, + lat_max=lat_max, lng_max=lng_max, + bbox=bbox, network_type=network_type, + two_way=two_way, timeout=timeout, + memory=memory, + max_query_area_size=max_query_area_size) return Network( nodes['x'], nodes['y'], From 1086e2d8e43bb5f6db1301a608b9154b68c899fd Mon Sep 17 00:00:00 2001 From: Fletcher Foti Date: Fri, 10 Mar 2017 14:37:44 -0800 Subject: [PATCH 14/42] try to reserve multiple graphs first --- pandana/tests/test_pandana.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandana/tests/test_pandana.py b/pandana/tests/test_pandana.py index 59247586..72efe030 100644 --- a/pandana/tests/test_pandana.py +++ b/pandana/tests/test_pandana.py @@ -14,6 +14,7 @@ multiple graphs one way streets ''' +pdna.reserve_num_graphs(2) @pytest.fixture(scope="module") From 7fe1e8fc1835463a91b810797d25aba31bfe7f33 Mon Sep 17 00:00:00 2001 From: Fletcher Foti Date: Fri, 10 Mar 2017 15:10:05 -0800 Subject: [PATCH 15/42] this might work on travis it certainly works locally --- conftest.py | 2 +- pandana/tests/test_pandana.py | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/conftest.py b/conftest.py index 6cb22e8c..b6fb046f 100644 --- a/conftest.py +++ b/conftest.py @@ -25,6 +25,6 @@ if os.environ.get('TRAVIS') == 'true': num_networks_tested = 1 else: - num_networks_tested = 5 + num_networks_tested = 6 pdna.reserve_num_graphs(num_networks_tested) diff --git a/pandana/tests/test_pandana.py b/pandana/tests/test_pandana.py index 72efe030..36761b98 100644 --- a/pandana/tests/test_pandana.py +++ b/pandana/tests/test_pandana.py @@ -5,6 +5,7 @@ import pandas as pd import pytest from pandas.util import testing as pdt +from pandana.testing import skipiftravis import pandana.network as pdna @@ -14,7 +15,6 @@ multiple graphs one way streets ''' -pdna.reserve_num_graphs(2) @pytest.fixture(scope="module") @@ -203,9 +203,8 @@ def test_plot(sample_osm): sample_osm.plot(s) -def test_pois(sample_osm, second_sample_osm): +def test_pois(sample_osm): net = sample_osm - net2 = second_sample_osm ssize = 50 np.random.seed(0) @@ -232,15 +231,6 @@ def test_pois(sample_osm, second_sample_osm): with pytest.raises(AssertionError): net.nearest_pois(2000, "restaurants", num_pois=11) - # make sure poi searches work on second graph - net2.init_pois(num_categories=1, max_dist=2000, max_pois=10) - - net2.set_pois("restaurants", x, y) - - print net2.nearest_pois(2000, "restaurants", num_pois=10) - - -def test_pois_indexes(sample_osm): net = sample_osm x, y = random_x_y(sample_osm, 100) x.index = ['lab%d' % i for i in range(len(x))] @@ -250,3 +240,19 @@ def test_pois_indexes(sample_osm): d = net.nearest_pois(2000, "restaurants", num_pois=10, include_poi_ids=True) + + +@skipiftravis +def test_pois(second_sample_osm): + net2 = second_sample_osm + + ssize = 50 + np.random.seed(0) + x, y = random_x_y(second_sample_osm, ssize) + + # make sure poi searches work on second graph + net2.init_pois(num_categories=1, max_dist=2000, max_pois=10) + + net2.set_pois("restaurants", x, y) + + print net2.nearest_pois(2000, "restaurants", num_pois=10) \ No newline at end of file From a16b67ce93e25e7a53e71272be78bbdbf10043f6 Mon Sep 17 00:00:00 2001 From: Fletcher Foti Date: Fri, 10 Mar 2017 15:15:12 -0800 Subject: [PATCH 16/42] pep8 --- pandana/tests/test_pandana.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandana/tests/test_pandana.py b/pandana/tests/test_pandana.py index 36761b98..4b9de920 100644 --- a/pandana/tests/test_pandana.py +++ b/pandana/tests/test_pandana.py @@ -255,4 +255,4 @@ def test_pois(second_sample_osm): net2.set_pois("restaurants", x, y) - print net2.nearest_pois(2000, "restaurants", num_pois=10) \ No newline at end of file + print net2.nearest_pois(2000, "restaurants", num_pois=10) From 1dde6a98c7c7a02a932156ad2afbc48100c5811a Mon Sep 17 00:00:00 2001 From: Fletcher Foti Date: Fri, 10 Mar 2017 15:22:45 -0800 Subject: [PATCH 17/42] rename 2nd test so as to fix coverage drop --- pandana/tests/test_pandana.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandana/tests/test_pandana.py b/pandana/tests/test_pandana.py index 4b9de920..eff7e488 100644 --- a/pandana/tests/test_pandana.py +++ b/pandana/tests/test_pandana.py @@ -243,7 +243,7 @@ def test_pois(sample_osm): @skipiftravis -def test_pois(second_sample_osm): +def test_pois2(second_sample_osm): net2 = second_sample_osm ssize = 50 From 15ba67d7868909aa4ec05c88a6c44574175450cf Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Mon, 20 Mar 2017 17:31:20 -0300 Subject: [PATCH 18/42] Changes to support Python3 This includes the changes in the way the C extensions are declared and changes in Python itself. --- docs/conf.py | 16 +++---- examples/simple_example.py | 2 +- pandana/__init__.py | 2 +- pandana/loaders/osm.py | 2 +- pandana/loaders/tests/test_pandash5.py | 2 +- pandana/network.py | 4 +- pandana/tests/test_pandana.py | 2 +- src/pyaccesswrap.cpp | 64 ++++++++++++++++++++++++-- 8 files changed, 76 insertions(+), 18 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index ac97b091..11f0b65e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,8 +49,8 @@ master_doc = 'index' # General information about the project. -project = u'pandana' -copyright = u'2015, UrbanSim Inc.' +project = 'pandana' +copyright = '2015, UrbanSim Inc.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -203,8 +203,8 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'pandana.tex', u'pandana Documentation', - u'UrbanSim Inc.', 'manual'), + ('index', 'pandana.tex', 'pandana Documentation', + 'UrbanSim Inc.', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -233,8 +233,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'pandana', u'pandana Documentation', - [u'UrbanSim Inc.'], 1) + ('index', 'pandana', 'pandana Documentation', + ['UrbanSim Inc.'], 1) ] # If true, show URL addresses after external links. @@ -247,8 +247,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'pandana', u'pandana Documentation', - u'UrbanSim Inc.', 'pandana', 'One line description of project.', + ('index', 'pandana', 'pandana Documentation', + 'UrbanSim Inc.', 'pandana', 'One line description of project.', 'Miscellaneous'), ] diff --git a/examples/simple_example.py b/examples/simple_example.py index 7ded02c7..0fedf87b 100644 --- a/examples/simple_example.py +++ b/examples/simple_example.py @@ -14,7 +14,7 @@ support it may be run with multiple threads or only 1. """ -from __future__ import print_function + import os.path import sys diff --git a/pandana/__init__.py b/pandana/__init__.py index c98279e4..a1b02fad 100644 --- a/pandana/__init__.py +++ b/pandana/__init__.py @@ -1,3 +1,3 @@ -from network import Network +from .network import Network version = __version__ = '0.2dev' diff --git a/pandana/loaders/osm.py b/pandana/loaders/osm.py index 4748b29e..97f29952 100644 --- a/pandana/loaders/osm.py +++ b/pandana/loaders/osm.py @@ -88,7 +88,7 @@ def process_node(e): } if 'tags' in e: - for t, v in e['tags'].items(): + for t, v in list(e['tags'].items()): if t not in uninteresting_tags: node[t] = v diff --git a/pandana/loaders/tests/test_pandash5.py b/pandana/loaders/tests/test_pandash5.py index aac3443e..140c00e8 100644 --- a/pandana/loaders/tests/test_pandash5.py +++ b/pandana/loaders/tests/test_pandash5.py @@ -34,7 +34,7 @@ def impedance_names(): def edge_weights(edges, impedance_names): return pd.DataFrame( {impedance_names[0]: [1] * len(edges), - impedance_names[1]: range(1, len(edges) + 1)}) + impedance_names[1]: list(range(1, len(edges) + 1))}) @pytest.fixture(scope='module') diff --git a/pandana/network.py b/pandana/network.py index 145879dd..2751ecb6 100644 --- a/pandana/network.py +++ b/pandana/network.py @@ -1,4 +1,4 @@ -from __future__ import division, print_function + import matplotlib # this might fix the travis build @@ -582,7 +582,7 @@ def nearest_pois(self, distance, category, num_pois=1, max_distance=None, 0) a[a == -1] = max_distance df = pd.DataFrame(a, index=self.node_ids) - df.columns = range(1, num_pois+1) + df.columns = list(range(1, num_pois+1)) if include_poi_ids: b = _pyaccess.find_all_nearest_pois(distance, diff --git a/pandana/tests/test_pandana.py b/pandana/tests/test_pandana.py index eff7e488..8a55edc9 100644 --- a/pandana/tests/test_pandana.py +++ b/pandana/tests/test_pandana.py @@ -255,4 +255,4 @@ def test_pois2(second_sample_osm): net2.set_pois("restaurants", x, y) - print net2.nearest_pois(2000, "restaurants", num_pois=10) + print(net2.nearest_pois(2000, "restaurants", num_pois=10)) diff --git a/src/pyaccesswrap.cpp b/src/pyaccesswrap.cpp index ef0eb865..7a3cbf2f 100644 --- a/src/pyaccesswrap.cpp +++ b/src/pyaccesswrap.cpp @@ -504,11 +504,69 @@ static PyMethodDef myMethods[] = { }; -PyMODINIT_FUNC init_pyaccess(void) +#if PY_MAJOR_VERSION >= 3 +struct module_state { + PyObject *error; +}; +#endif + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +static struct module_state _state; +#endif + +#if PY_MAJOR_VERSION >= 3 +static PyObject * +error_out(PyObject *m) { + struct module_state *st = GETSTATE(m); + PyErr_SetString(st->error, "something bad happened"); + return NULL; +} + +static int pyaccess_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int pyaccess_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_pyaccess", + NULL, + sizeof(struct module_state), + myMethods, + NULL, + pyaccess_traverse, + pyaccess_clear, + NULL +}; +#endif + + +PyMODINIT_FUNC +#if PY_MAJOR_VERSION >= 3 +PyInit__pyaccess(void) +#else +init_pyaccess(void) +#endif { +#if PY_MAJOR_VERSION >= 3 + PyObject *m = PyModule_Create(&moduledef); +#else PyObject *m=Py_InitModule("_pyaccess", myMethods); +#endif import_array(); PyObject *pyError = PyErr_NewException((char*)"pyaccess.error", NULL, NULL); - Py_INCREF(pyError); - PyModule_AddObject(m, "error", pyError); + Py_INCREF(pyError); + PyModule_AddObject(m, "error", pyError); + +#if PY_MAJOR_VERSION >= 3 + return m; +#endif } From 357fa7a1505499c37494d9563ab42e0d24cd89e8 Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Wed, 22 Mar 2017 10:31:04 -0300 Subject: [PATCH 19/42] Struct needed in Python 2.x too --- src/pyaccesswrap.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pyaccesswrap.cpp b/src/pyaccesswrap.cpp index 7a3cbf2f..8609c960 100644 --- a/src/pyaccesswrap.cpp +++ b/src/pyaccesswrap.cpp @@ -504,11 +504,9 @@ static PyMethodDef myMethods[] = { }; -#if PY_MAJOR_VERSION >= 3 struct module_state { PyObject *error; }; -#endif #if PY_MAJOR_VERSION >= 3 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) From c35d3fcabfeb45bd81295a3246c180069fbbde57 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 27 Mar 2017 12:10:03 -0700 Subject: [PATCH 20/42] added reindex functions to utils.py with test --- pandana/tests/test_utils.py | 34 ++++++++++++++++++++++++++++++++++ pandana/utils.py | 21 +++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 pandana/tests/test_utils.py create mode 100644 pandana/utils.py diff --git a/pandana/tests/test_utils.py b/pandana/tests/test_utils.py new file mode 100644 index 00000000..c847d76e --- /dev/null +++ b/pandana/tests/test_utils.py @@ -0,0 +1,34 @@ +import pandas as pd +import pytest + +from pandana.utils import reindex + + +@pytest.fixture +def dataframe(): + data = {'node_id': [44, 55, 33, 22, 11], + 'x': [-122, -123, -124, -125, -126], + 'y': [37, 38, 39, 40, 41]} + index = [1, 2, 3, 4, 5] + + df = pd.DataFrame(data, index) + return df + + +@pytest.fixture +def series(): + data = {'value': [10, 20, 30, 40, 50]} + index = [11,22,33,44,55] + df = pd.DataFrame(data, index) + df.index.name = 'id' + s = pd.Series(df.value, df.index) + return s + + +def test_reindex(dataframe, series): + + results = pd.DataFrame({'value': reindex(series, dataframe.node_id)}) + + assert len(results) == 5 + assert results['value'][1] == 40 + assert results['value'][5] == 10 \ No newline at end of file diff --git a/pandana/utils.py b/pandana/utils.py new file mode 100644 index 00000000..d72fadc7 --- /dev/null +++ b/pandana/utils.py @@ -0,0 +1,21 @@ +import pandas as pd + + +def reindex(series1, series2): + """ + Reindex the first series by the second series. + + Parameters + ---------- + series1 : pd.Series + Pandas series to reindex + series2 : pd.Series + Pandas series to set the index of series1 by + """ + + df = pd.merge(pd.DataFrame({"left": series2}), + pd.DataFrame({"right": series1}), + left_on="left", + right_index=True, + how="left") + return df.right From 1d138a24596d1b0f12f3d1a8bfa1df4fe3c9a139 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 27 Mar 2017 12:17:57 -0700 Subject: [PATCH 21/42] updated docstring and pep8 fix --- pandana/tests/test_utils.py | 4 ++-- pandana/utils.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pandana/tests/test_utils.py b/pandana/tests/test_utils.py index c847d76e..ff71a119 100644 --- a/pandana/tests/test_utils.py +++ b/pandana/tests/test_utils.py @@ -18,7 +18,7 @@ def dataframe(): @pytest.fixture def series(): data = {'value': [10, 20, 30, 40, 50]} - index = [11,22,33,44,55] + index = [11, 22, 33, 44, 55] df = pd.DataFrame(data, index) df.index.name = 'id' s = pd.Series(df.value, df.index) @@ -31,4 +31,4 @@ def test_reindex(dataframe, series): assert len(results) == 5 assert results['value'][1] == 40 - assert results['value'][5] == 10 \ No newline at end of file + assert results['value'][5] == 10 diff --git a/pandana/utils.py b/pandana/utils.py index d72fadc7..e6099ec4 100644 --- a/pandana/utils.py +++ b/pandana/utils.py @@ -7,10 +7,14 @@ def reindex(series1, series2): Parameters ---------- - series1 : pd.Series + series1 : pandas.Series Pandas series to reindex - series2 : pd.Series + series2 : pandas.Series Pandas series to set the index of series1 by + + Returns + ------- + df.right : pandas.DataFrame """ df = pd.merge(pd.DataFrame({"left": series2}), From b3c9d1cfc76025114b8a7214c29aa4d53d7ef7f4 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 27 Mar 2017 16:18:33 -0700 Subject: [PATCH 22/42] added comment and refactored test vars --- pandana/tests/test_utils.py | 15 ++++++++------- pandana/utils.py | 2 ++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pandana/tests/test_utils.py b/pandana/tests/test_utils.py index ff71a119..d6e4b3ec 100644 --- a/pandana/tests/test_utils.py +++ b/pandana/tests/test_utils.py @@ -5,7 +5,7 @@ @pytest.fixture -def dataframe(): +def node_df(): data = {'node_id': [44, 55, 33, 22, 11], 'x': [-122, -123, -124, -125, -126], 'y': [37, 38, 39, 40, 41]} @@ -16,7 +16,7 @@ def dataframe(): @pytest.fixture -def series(): +def result_series(): data = {'value': [10, 20, 30, 40, 50]} index = [11, 22, 33, 44, 55] df = pd.DataFrame(data, index) @@ -25,10 +25,11 @@ def series(): return s -def test_reindex(dataframe, series): +def test_reindex(node_df, result_series): - results = pd.DataFrame({'value': reindex(series, dataframe.node_id)}) + reindexed_results = pd.DataFrame({'value': reindex(result_series, + node_df.node_id)}) - assert len(results) == 5 - assert results['value'][1] == 40 - assert results['value'][5] == 10 + assert len(reindexed_results) == 5 + assert reindexed_results['value'][1] == 40 + assert reindexed_results['value'][5] == 10 diff --git a/pandana/utils.py b/pandana/utils.py index e6099ec4..10e0885d 100644 --- a/pandana/utils.py +++ b/pandana/utils.py @@ -17,6 +17,8 @@ def reindex(series1, series2): df.right : pandas.DataFrame """ + # this function is identical to the reindex function found in UrbanSim in + # urbansim/utils/misc.py df = pd.merge(pd.DataFrame({"left": series2}), pd.DataFrame({"right": series1}), left_on="left", From 248f10eb31cc8f0aeafc507d1461175128b1b04c Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Tue, 28 Mar 2017 15:15:11 -0300 Subject: [PATCH 23/42] Maintaining __future__ directives for better backwards compatibility --- pandana/network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandana/network.py b/pandana/network.py index 2751ecb6..43686c40 100644 --- a/pandana/network.py +++ b/pandana/network.py @@ -1,4 +1,4 @@ - +from __future__ import division, print_function import matplotlib # this might fix the travis build From aa0709ba1bae7465e56b45ad3119af768b3572d2 Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Tue, 28 Mar 2017 15:16:12 -0300 Subject: [PATCH 24/42] Maintaining __future__ directives for better backwards compatibility --- examples/simple_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple_example.py b/examples/simple_example.py index 0fedf87b..7ded02c7 100644 --- a/examples/simple_example.py +++ b/examples/simple_example.py @@ -14,7 +14,7 @@ support it may be run with multiple threads or only 1. """ - +from __future__ import print_function import os.path import sys From d811ac21457c1a54de1cdadb48f79e22101e8cdc Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Tue, 28 Mar 2017 16:10:55 -0300 Subject: [PATCH 25/42] Adding Python 3.5 to Travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 025138e0..053c67b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: python sudo: false python: - '2.7' +- '3.5' install: - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; From a85b4ce58dc3ac72341b2b7b61e4cd8e2e1755ac Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Fri, 31 Mar 2017 13:39:23 -0300 Subject: [PATCH 26/42] Installing osmnet from pip now that we have a package --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 053c67b8..9e193449 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,7 @@ install: - | conda create -q -c synthicity -n test-environment python=$TRAVIS_PYTHON_VERSION basemap matplotlib numpy pandas pip pytables requests - source activate test-environment -- pip install https://github.com/UDST/osmnet/archive/master.zip -- pip install pytest-cov coveralls pep8 +- pip install pytest-cov coveralls pep8 osmnet - python setup.py install script: - pep8 pandana From 42f75e265d93f8dd945b39e50fd88f2bd9bae075 Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Fri, 31 Mar 2017 13:40:10 -0300 Subject: [PATCH 27/42] Changing version required to the current version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3a900c05..47ca04ac 100644 --- a/setup.py +++ b/setup.py @@ -121,7 +121,7 @@ def run_tests(self): 'pandas>=0.13.1', 'requests>=2.0', 'tables>=3.1.0', - 'osmnet>=0.1a', + 'osmnet>=0.1.2', ], tests_require=['pytest'], cmdclass={'test': PyTest}, From ce36f4ad91d3a697ff511529cf4c25faf119a4a2 Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Fri, 31 Mar 2017 13:47:08 -0300 Subject: [PATCH 28/42] Migrating from PEP8 to pycodestyle Since pycodestyle is much more comprehensive, we are changing the tool used for enforcing coding style. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9e193449..7d7d4812 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,10 +18,10 @@ install: - | conda create -q -c synthicity -n test-environment python=$TRAVIS_PYTHON_VERSION basemap matplotlib numpy pandas pip pytables requests - source activate test-environment -- pip install pytest-cov coveralls pep8 osmnet +- pip install pytest-cov coveralls pycodestyle osmnet - python setup.py install script: -- pep8 pandana +- pycodestyle pandana - python setup.py test --pytest-args "--cov pandana --cov-report term-missing" after_success: - coveralls From c10dc9d9563236419839b74f0ad21d3d075fbddf Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Fri, 31 Mar 2017 15:57:29 -0300 Subject: [PATCH 29/42] Code quality improvements Suggested by pycodestyle. --- docs/conf.py | 101 ++++++++++++++++++++++----------------------- ez_setup.py | 24 ++++++++++- pandana/network.py | 6 +-- setup.py | 1 + 4 files changed, 76 insertions(+), 56 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 11f0b65e..2a4acb39 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' numpydoc_show_class_members = False numpydoc_class_members_toctree = False @@ -43,7 +43,7 @@ source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -63,13 +63,13 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -77,27 +77,27 @@ # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ---------------------------------------------- @@ -110,26 +110,26 @@ # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -139,48 +139,48 @@ # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'pandanadoc' @@ -189,43 +189,42 @@ # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # Additional stuff for the LaTeX preamble. + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'pandana.tex', 'pandana Documentation', - 'UrbanSim Inc.', 'manual'), + ('index', 'pandana.tex', 'pandana Documentation', 'UrbanSim Inc.', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- @@ -238,7 +237,7 @@ ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -247,19 +246,19 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'pandana', 'pandana Documentation', - 'UrbanSim Inc.', 'pandana', 'One line description of project.', - 'Miscellaneous'), + ('index', 'pandana', 'pandana Documentation', + 'UrbanSim Inc.', 'pandana', 'One line description of project.', + 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/ez_setup.py b/ez_setup.py index a34fa806..90b93f68 100644 --- a/ez_setup.py +++ b/ez_setup.py @@ -39,6 +39,7 @@ DEFAULT_VERSION = "5.7" DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" + def _python_cmd(*args): """ Return True if the command succeeded. @@ -130,7 +131,7 @@ def _do_download(version, download_base, to_dir, download_delay): def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, - to_dir=os.curdir, download_delay=15): + to_dir=os.curdir, download_delay=15): to_dir = os.path.abspath(to_dir) rep_modules = 'pkg_resources', 'setuptools' imported = set(sys.modules).intersection(rep_modules) @@ -160,6 +161,7 @@ def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, del pkg_resources, sys.modules['pkg_resources'] return _do_download(version, download_base, to_dir, download_delay) + def _clean_check(cmd, target): """ Run the command to download target. If the command fails, clean up before @@ -172,6 +174,7 @@ def _clean_check(cmd, target): os.unlink(target) raise + def download_file_powershell(url, target): """ Download the file at url to target using Powershell (which will validate @@ -191,6 +194,7 @@ def download_file_powershell(url, target): ] _clean_check(cmd, target) + def has_powershell(): if platform.system() != 'Windows': return False @@ -202,12 +206,15 @@ def has_powershell(): return False return True + download_file_powershell.viable = has_powershell + def download_file_curl(url, target): cmd = ['curl', url, '--silent', '--output', target] _clean_check(cmd, target) + def has_curl(): cmd = ['curl', '--version'] with open(os.path.devnull, 'wb') as devnull: @@ -217,12 +224,15 @@ def has_curl(): return False return True + download_file_curl.viable = has_curl + def download_file_wget(url, target): cmd = ['wget', url, '--quiet', '--output-document', target] _clean_check(cmd, target) + def has_wget(): cmd = ['wget', '--version'] with open(os.path.devnull, 'wb') as devnull: @@ -232,8 +242,10 @@ def has_wget(): return False return True + download_file_wget.viable = has_wget + def download_file_insecure(url, target): """ Use Python to download the file, even though it cannot authenticate the @@ -250,8 +262,10 @@ def download_file_insecure(url, target): with open(target, "wb") as dst: dst.write(data) + download_file_insecure.viable = lambda: True + def get_best_downloader(): downloaders = ( download_file_powershell, @@ -262,8 +276,10 @@ def get_best_downloader(): viable_downloaders = (dl for dl in downloaders if dl.viable()) return next(viable_downloaders, None) + def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, - to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader): + to_dir=os.curdir, delay=15, + downloader_factory=get_best_downloader): """ Download setuptools from a specified location and return its filename @@ -287,12 +303,14 @@ def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, downloader(url, saveto) return os.path.realpath(saveto) + def _build_install_args(options): """ Build the arguments to 'python setup.py install' on the setuptools package """ return ['--user'] if options.user_install else [] + def _parse_args(): """ Parse the command line for options @@ -318,6 +336,7 @@ def _parse_args(): # positional arguments are ignored return options + def main(): """Install or upgrade setuptools and EasyInstall""" options = _parse_args() @@ -328,5 +347,6 @@ def main(): ) return _install(archive, _build_install_args(options)) + if __name__ == '__main__': sys.exit(main()) diff --git a/pandana/network.py b/pandana/network.py index 43686c40..203a315e 100644 --- a/pandana/network.py +++ b/pandana/network.py @@ -228,13 +228,13 @@ def set(self, node_ids, variable=None, name="tmp"): df = pd.DataFrame({name: variable, "node_idx": self._node_indexes(node_ids)}) - l = len(df) + length = len(df) df = df.dropna(how="any") newl = len(df) - if l-newl > 0: + if length-newl > 0: print( "Removed %d rows because they contain missing values" % - (l-newl)) + (length-newl)) if name not in self.variable_names: self.variable_names.append(name) diff --git a/setup.py b/setup.py index 47ca04ac..ec125a21 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ def run_tests(self): errno = pytest.main(self.pytest_args or '') sys.exit(errno) + include_dirs = [ np.get_include(), '.', From c22a44b5889aa0e8ab1c869792fd82477cbbfa83 Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Fri, 31 Mar 2017 20:00:30 -0300 Subject: [PATCH 30/42] Bumping version to 0.2.1. --- pandana/__init__.py | 2 +- setup.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pandana/__init__.py b/pandana/__init__.py index a1b02fad..3e3a79aa 100644 --- a/pandana/__init__.py +++ b/pandana/__init__.py @@ -1,3 +1,3 @@ from .network import Network -version = __version__ = '0.2dev' +version = __version__ = '0.2.1' diff --git a/setup.py b/setup.py index ec125a21..ec9d7330 100644 --- a/setup.py +++ b/setup.py @@ -91,7 +91,7 @@ def run_tests(self): if mac_ver >= [10, 9]: extra_compile_args += ['-D NO_TR1_MEMORY'] -version = '0.2dev' +version = '0.2.1' # read long description from README with open('README.rst', 'r') as f: @@ -129,6 +129,7 @@ def run_tests(self): classifiers=[ 'Development Status :: 3 - Alpha', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.5', 'License :: OSI Approved :: GNU Affero General Public License v3' ], ) From d1da7f6057e323f6e96171a06abd4bd713a4aab6 Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Fri, 31 Mar 2017 20:02:29 -0300 Subject: [PATCH 31/42] Bumping version in the documentation for the upcoming release --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 2a4acb39..fd99122b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -57,9 +57,9 @@ # built documents. # # The short X.Y version. -version = '0.1' +version = '0.2.1' # The full version, including alpha/beta/rc tags. -release = '0.1' +release = '0.2.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From a2eff328d557b175457f9c291d36ea3c86507c82 Mon Sep 17 00:00:00 2001 From: "Federico J. Fernandez" Date: Fri, 31 Mar 2017 20:07:53 -0300 Subject: [PATCH 32/42] Changing version to 0.3.0 Given that the changes after 0.2 are many and important, we decided to name this version as 0.3.0 instead of 0.2.1. --- docs/conf.py | 4 ++-- pandana/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index fd99122b..d7b8bd16 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -57,9 +57,9 @@ # built documents. # # The short X.Y version. -version = '0.2.1' +version = '0.3.0' # The full version, including alpha/beta/rc tags. -release = '0.2.1' +release = '0.3.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/pandana/__init__.py b/pandana/__init__.py index 3e3a79aa..1d894360 100644 --- a/pandana/__init__.py +++ b/pandana/__init__.py @@ -1,3 +1,3 @@ from .network import Network -version = __version__ = '0.2.1' +version = __version__ = '0.3.0' diff --git a/setup.py b/setup.py index ec9d7330..3ac97e2e 100644 --- a/setup.py +++ b/setup.py @@ -91,7 +91,7 @@ def run_tests(self): if mac_ver >= [10, 9]: extra_compile_args += ['-D NO_TR1_MEMORY'] -version = '0.2.1' +version = '0.3.0' # read long description from README with open('README.rst', 'r') as f: From 1ae6338b1df2cb34a1fbe991a90fdf4d594ed693 Mon Sep 17 00:00:00 2001 From: Eddie Janowicz Date: Sun, 2 Apr 2017 22:14:08 -0700 Subject: [PATCH 33/42] update data file link in tutorial --- docs/tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 7e127017..0d7f2b3a 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -24,7 +24,7 @@ takes a small number of Pandas Series objects. The network is comprised of a set of nodes and edges. We store our nodes and edges as two Pandas DataFrames in an HDFStore object. We can access them as follows (the demo data file can be -`downloaded here `__):: +`downloaded here `__):: store = pd.HDFStore('data/osm_bayarea.h5', "r") From 071f9c05b383d85b635a73f617ce1c7f8aba2f61 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 3 Apr 2017 15:24:57 -0700 Subject: [PATCH 34/42] updated docstrings and documentation --- README.rst | 14 ++++++++-- docs/futurework.rst | 6 +---- docs/index.rst | 1 + docs/introduction.rst | 35 ++++++++++++++++++++++--- docs/loaders.rst | 7 +++++ docs/tutorial.rst | 27 +++++++++++-------- docs/utilities.rst | 4 +++ pandana/loaders/osm.py | 3 ++- pandana/network.py | 59 +++++++++++++++++++++++++++++++----------- 9 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 docs/utilities.rst diff --git a/README.rst b/README.rst index 50f75661..8302933d 100644 --- a/README.rst +++ b/README.rst @@ -40,8 +40,8 @@ queries are a more accurate representation of how people interact with their environment. We look forward to creative uses of a general library like this - please -let us know when you think you have a great use case with the hashtag -``#udst``. +let us know if you think you have a great use case by tweeting us at +``@urbansim`` or post on the UrbanSim `forum`_. Docs ---- @@ -70,3 +70,13 @@ A `complete description of the methodology `__ was presented at the Transportation Research Board Annual Conference in 2012. Please cite this paper when referring to the methodology implemented by this library. + +Related UDST libraries +---------------------- + +- `OSMnet`_ +- `UrbanAccess`_ + +.. _forum: http://discussion.urbansim.com/ +.. _OSMnet repo: https://github.com/udst/osmnet +.. _UrbanAccess: https://github.com/UDST/urbanaccess \ No newline at end of file diff --git a/docs/futurework.rst b/docs/futurework.rst index fafefecb..80f0a8a0 100644 --- a/docs/futurework.rst +++ b/docs/futurework.rst @@ -14,11 +14,7 @@ includes: * Batch (multi-threaded) routing between a large number of pairs of nodes in the network -* Additional OpenStreetMap, ESRI ShapeFile and Geodatabase, - and GTFS (General Transit Feed Service) importing - -* Unification of multiple networks, like combining the pedestrian - OpenStreetMap network with the GTFS transit schedules +* Additional OpenStreetMap, ESRI ShapeFile and Geodatabase importing * Returning a DataFrame of all source-destination nodes within a certain distance (and including the distance) diff --git a/docs/index.rst b/docs/index.rst index 589d58d1..daaede70 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,6 +24,7 @@ Contents tutorial network loaders + utilities futurework Indices and tables diff --git a/docs/introduction.rst b/docs/introduction.rst index 96f59c1a..c3ecf22f 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -13,7 +13,7 @@ Beyond simple access to destination queries, this library also implements more g we think network queries are a more accurate representation of how people interact with their environment. -We look forward to creative uses of a general library like this - please let us know when you think you have a great use case with the hashtag ``#udst``. +We look forward to creative uses of a general library like this - please let us know when you think you have a great use case by tweeting us at ``@urbansim`` or post on the UrbanSim `forum`_. The General Workflow ~~~~~~~~~~~~~~~~~~~~ @@ -95,6 +95,35 @@ variable using network queries. care, or urban predictive variables - e.g. average income in the local area, or simply for data exploration. A common use case will be to write to shapefiles and use in further GIS analysis, or to relate to parcels and - buildings and use in further analysis within UrbanSim and the Urban Data - Science Toolkit. There are many possibilities, and we hope designing a + buildings and use in further analysis within `UrbanSim`_ and the +`Urban Data Science Toolkit`_. There are many possibilities, and we hope designing a flexible and easy to use engine will serve many use cases. + + +Reporting bugs +~~~~~~~~~~~~~~~~~~~~~~~~ +Please report any bugs you encounter via `GitHub Issues `__. + +Contributing to Pandana +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If you have improvements or new features you would like to see in Pandana: + +1. Open a feature request via `GitHub Issues `__. +2. Contribute your code from a fork or branch by using a Pull Request and request a review so it can be considered as an addition to the codebase. + +License +~~~~~~~~ + +Pandana is licensed under the AGPL license. + +Related UDST libraries +---------------------- + +- `OSMnet`_ +- `UrbanAccess`_ + +.. _forum: http://discussion.urbansim.com/ +.. _UrbanSim: https://github.com/UDST/urbansim +.. _Urban Data Science Toolkit: https://github.com/UDST +.. _OSMnet repo: https://github.com/udst/osmnet +.. _UrbanAccess: https://github.com/UDST/urbanaccess diff --git a/docs/loaders.rst b/docs/loaders.rst index 66106afe..445fb056 100644 --- a/docs/loaders.rst +++ b/docs/loaders.rst @@ -21,6 +21,11 @@ specify ``type='drive'`` to get driveable routes. These networks have one impedance set, named ``'distance'``, which is the distance between nodes in meters. +.. note:: + `pdna_network_from_bbox` uses the UDST library OSMnet to download and + process OpenStreetMap (OSM) street network data. Please see + the `OSMnet`_ repo for any OSM loader questions, bugs, or features. + The OSM API also includes the :py:func:`~pandana.loaders.osm.node_query` function for getting specific nodes within a bounding box. This can be used to populate a network with points of interest:: @@ -87,3 +92,5 @@ Pandas HDF5 API .. autofunction:: pandana.loaders.pandash5.network_to_pandas_hdf5 .. autofunction:: pandana.loaders.pandash5.network_from_pandas_hdf5 + +.. _OSMnet repo: https://github.com/udst/osmnet diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 0d7f2b3a..24266b42 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -17,7 +17,7 @@ Create the Network First create the network. Although the API is incredibly simple, this is likely to be the most difficult part of using Pandana. In the future we will leverage the import functionality of tools like ``geopandas`` to -directly access OpenStreetMap and networks via shapefiles, +directly access networks via shapefiles, but for now the initialization :py:meth:`pandana.network.Network.__init__` takes a small number of Pandas Series objects. @@ -76,10 +76,10 @@ where 3000 meters is used as the horizon distance: :: net.precompute(3000) Note that a large amount of time is spent in the precomputations that take -place for these two lines of code. On my MacBook, these two lines of code +place for these two lines of code. On a MacBook, these two lines of code take 4 seconds and 8.5 seconds respectively. -**I also have a 4-core cpu, so if your precomputation is much slower, +**This was done on a 4-core cpu, so if your precomputation is much slower, check the IPython Notebook output (on the console) for a statement that says** ``Generating contraction hierarchies with 4 threads.`` **If your output says 1 instead of 4 you are running single threaded. If you are running on @@ -205,22 +205,27 @@ happen in the notebook) is also available. Note that these have a bounding box for reducing the display window. Although the underlying library is computing values for all nodes in the -region, it is extremely difficult to visualize this much data using -matplotlib. The GeoCanvas tool by UrbanSim Inc. is expressly designed to join -indicators at the node level to shapes of parcels and produces a much more -professional output map. For quick interactive checking of results, +region, it is difficult to visualize this much data using +matplotlib. For quick interactive checking of results, the bounding box can be used to reduce the number of points that are shown, and sample code and images are included below. :: bbox=[-122.539365,37.693047,-122.347698,37.816069] - net.plot(s, bbox=bbox, scheme="diverging", - color="BrBG", log_scale=True) + net.plot(s, bbox=bbox, + fig_kwargs={'figsize':[20,20]}, + bmap_kwargs={'suppress_ticks':False, + 'resolution':'h'}, + plot_kwargs={'cmap':'BrBG','s':8,'edgecolor':'none'}) + .. image:: img/500metersum.png :: - net.plot(u, bbox=bbox, scheme="diverging", - color="BrBG", log_scale=True) + net.plot(u, bbox=bbox, + fig_kwargs={'figsize':[20,20]}, + bmap_kwargs={'suppress_ticks':False, + 'resolution':'h'}, + plot_kwargs={'cmap':'BrBG','s':8,'edgecolor':'none'} .. image:: img/2000metersum.png diff --git a/docs/utilities.rst b/docs/utilities.rst new file mode 100644 index 00000000..8c719597 --- /dev/null +++ b/docs/utilities.rst @@ -0,0 +1,4 @@ +Utilities +========= + +.. autofunction:: pandana.utils.reindex diff --git a/pandana/loaders/osm.py b/pandana/loaders/osm.py index 97f29952..d9de9def 100644 --- a/pandana/loaders/osm.py +++ b/pandana/loaders/osm.py @@ -16,7 +16,7 @@ def pdna_network_from_bbox( timeout=180, memory=None, max_query_area_size=50 * 1000 * 50 * 1000): """ Make a Pandana network from a bounding lat/lon box - request to the Overpass API + request to the Overpass API. Distance will be in the default units meters. Parameters ---------- @@ -62,6 +62,7 @@ def process_node(e): """ Process a node element entry into a dict suitable for going into a Pandas DataFrame. + Parameters ---------- e : dict diff --git a/pandana/network.py b/pandana/network.py index 203a315e..8a705c69 100644 --- a/pandana/network.py +++ b/pandana/network.py @@ -62,12 +62,11 @@ def reserve_num_graphs(num): class Network: """ Create the transportation network in the city. Typical data would be - distance based from OpenStreetMap or possibly using transit data from - GTFS. + distance based from OpenStreetMap or travel time from GTFS transit data. Parameters ---------- - node_x : Pandas Series, flaot + node_x : Pandas Series, float Defines the x attribute for nodes in the network (e.g. longitude) node_y : Pandas Series, float Defines the y attribute for nodes in the network (e.g. latitude) @@ -83,10 +82,16 @@ class Network: Specifies one or more *impedances* on the network which define the distances between nodes. Multiple impedances can be used to capture travel times at different times of day, for instance - two_way : boolean, optional + twoway : boolean, optional Whether the edges in this network are two way edges or one way ( where the one direction is directed from the from node to the to - node) + node). If twoway = True, it is assumed that the from and to id in the + edge table occurs once and that travel can occur in both directions + on the single edge record. Pandana will internally flip and append + the from and to ids to the original edges to create a two direction + network. If twoway = False, it is assumed that travel can only occur + in the explicit direction indicated by the from and to id in the edge + table. """ @@ -209,7 +214,10 @@ def set(self, node_ids, variable=None, name="tmp"): not actually used). If variable is not set, then it is assumed that the variable is all "ones" at the location specified by node_ids. This could be, for instance, the location of all - coffee shops which don't really have a variable to aggregate. + coffee shops which don't really have a variable to aggregate. The + variable is connected to the closest node in the Pandana network + which assumes no impedance between the location of the variable + and the location of the closest network node. name : string, optional Name the variable. This is optional in the sense that if you don't specify it, the default name will be used. Since the same @@ -255,7 +263,9 @@ def precompute(self, distance): Parameters ---------- distance : float - The maximum distance to use + The maximum distance to use. This will usually be a distance unit + in meters however if you have customized the impedance this could + be in other units such as utility or time etc. Returns ------- @@ -286,7 +296,11 @@ def aggregate(self, distance, type="sum", decay="linear", imp_name=None, Parameters ---------- distance : float - The maximum distance to aggregate data within + The maximum distance to aggregate data within. 'distance' can + represent any impedance unit that you have set as your edge + weight. This will usually be a distance unit in meters however + if you have customized the impedance this could be in other + units such as utility or time etc. type : string The type of aggregation, can be one of "ave", "sum", "std", "count", and now "min", "25pct", "median", "75pct", and "max" will @@ -351,8 +365,11 @@ def get_node_ids(self, x_col, y_col, mapping_distance=-1): location of dataset. x_col and y_col should use the same index. mapping_distance : float, optional The maximum distance that will be considered a match between the - x, y data and the nearest node in the network. If not specified, - every x, y coordinate will be mapped to the nearest node + x, y data and the nearest node in the network. This will usually + be a distance unit in meters however if you have customized the + impedance this could be in other units such as utility or time + etc. If not specified, every x, y coordinate will be mapped to + the nearest node. Returns ------- @@ -391,7 +408,7 @@ def plot( cbar_kwargs=None): """ Plot an array of data on a map using matplotlib and Basemap, - automatically matching the data to node positions. + automatically matching the data to the Pandana network node positions. Keyword arguments are passed to the plotting routine. @@ -467,7 +484,10 @@ def init_pois(self, num_categories, max_dist, max_pois): num_categories : int Number of categories of POIs max_dist : float - Maximum distance that will be tested to nearest POIs + Maximum distance that will be tested to nearest POIs. This will + usually be a distance unit in meters however if you have + customized the impedance this could be in other + units such as utility or time etc. max_pois : Maximum number of POIs to return in the nearest query @@ -526,7 +546,10 @@ def nearest_pois(self, distance, category, num_pois=1, max_distance=None, Parameters ---------- distance : float - The maximum distance to look for pois + The maximum distance to look for pois. This will usually be a + distance unit in meters however if you have customized the + impedance this could be in other units such as utility or time + etc. category : string The name of the category of poi to look for num_pois : int @@ -534,7 +557,10 @@ def nearest_pois(self, distance, category, num_pois=1, max_distance=None, columns in the DataFrame that gets returned max_distance : float, optional The value to set the distance to if there is NO poi within the - specified distance - if not specified, gets set to distance + specified distance - if not specified, gets set to distance. This + will usually be a distance unit in meters however if you have + customized the impedance this could be in other units such as + utility or time etc. imp_name : string, optional The impedance name to use for the aggregation on this network. Must be one of the impedance names passed in the constructor of @@ -618,7 +644,10 @@ def low_connectivity_nodes(self, impedance, count, imp_name=None): Parameters ---------- impedance : float - Distance within which to search for other connected nodes. + Distance within which to search for other connected nodes. This + will usually be a distance unit in meters however if you have + customized the impedance this could be in other units such as + utility or time etc. count : int Threshold for connectivity. If a node is connected to fewer than this many nodes within `impedance` it will be identified From 6eabb3b29c65caf51049772abb2f4241335d0c37 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 3 Apr 2017 15:38:59 -0700 Subject: [PATCH 35/42] minor link update and added git issue template --- .github/ISSUE_TEMPLATE.md | 33 +++++++++++++++++++++++++++++++++ README.rst | 2 +- docs/introduction.rst | 2 +- docs/loaders.rst | 2 +- 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..0315c4f8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,33 @@ +If you encounter a bug in Pandana please: 1) first search the previously opened issues to see if the problem has already been reported; 2) If not, fill in the template below and tag with the appropriate `Type` label. You can delete any sections that do not apply. + +#### Description of the bug + + + + +#### Network data (optional) +If the issue is related to specific network data please provide a link to download the data or the function used to download the data. + + +#### Environment + +- Operating system: + +- Python version: + +- Pandana version: + +- Pandana required packages versions (optional): + + +#### Paste the code that reproduces the issue here: + +```python +# place code here +``` + + +#### Paste the error message (if applicable): +```python +# place error message here +``` \ No newline at end of file diff --git a/README.rst b/README.rst index 8302933d..b1b289ce 100644 --- a/README.rst +++ b/README.rst @@ -78,5 +78,5 @@ Related UDST libraries - `UrbanAccess`_ .. _forum: http://discussion.urbansim.com/ -.. _OSMnet repo: https://github.com/udst/osmnet +.. _OSMnet: https://github.com/udst/osmnet .. _UrbanAccess: https://github.com/UDST/urbanaccess \ No newline at end of file diff --git a/docs/introduction.rst b/docs/introduction.rst index c3ecf22f..35703263 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -125,5 +125,5 @@ Related UDST libraries .. _forum: http://discussion.urbansim.com/ .. _UrbanSim: https://github.com/UDST/urbansim .. _Urban Data Science Toolkit: https://github.com/UDST -.. _OSMnet repo: https://github.com/udst/osmnet +.. _OSMnet: https://github.com/udst/osmnet .. _UrbanAccess: https://github.com/UDST/urbanaccess diff --git a/docs/loaders.rst b/docs/loaders.rst index 445fb056..ff3a8ca2 100644 --- a/docs/loaders.rst +++ b/docs/loaders.rst @@ -93,4 +93,4 @@ Pandas HDF5 API .. autofunction:: pandana.loaders.pandash5.network_from_pandas_hdf5 -.. _OSMnet repo: https://github.com/udst/osmnet +.. _OSMnet: https://github.com/udst/osmnet From c95586ddc39796dfd7372f1bac4a25d0733cffd2 Mon Sep 17 00:00:00 2001 From: Paul Sohn Date: Mon, 3 Apr 2017 16:51:06 -0700 Subject: [PATCH 36/42] Update notebooks in example directory and tutorial docs --- .gitignore | 3 + docs/tutorial.rst | 19 +- examples/AnythingScore.ipynb | 162 ----- examples/Example.ipynb | 1160 +++++++++++++++++----------------- 4 files changed, 599 insertions(+), 745 deletions(-) delete mode 100644 examples/AnythingScore.ipynb diff --git a/.gitignore b/.gitignore index 8f21bdc5..32f57b41 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,6 @@ docs/_build/ # OSMnet logs logs + +# Example data +examples/data \ No newline at end of file diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 24266b42..56877764 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -210,22 +210,21 @@ matplotlib. For quick interactive checking of results, the bounding box can be used to reduce the number of points that are shown, and sample code and images are included below. :: - bbox=[-122.539365,37.693047,-122.347698,37.816069] + bbox = [-122.539365, 37.693047, -122.347698, 37.816069] net.plot(s, bbox=bbox, - fig_kwargs={'figsize':[20,20]}, - bmap_kwargs={'suppress_ticks':False, - 'resolution':'h'}, - plot_kwargs={'cmap':'BrBG','s':8,'edgecolor':'none'}) - + fig_kwargs={'figsize': [20, 20]}, + bmap_kwargs={'suppress_ticks': False, + 'resolution': 'h', 'epsg': '26943'}, + plot_kwargs={'cmap': 'BrBG', 's': 8, 'edgecolor': 'none'}) .. image:: img/500metersum.png :: net.plot(u, bbox=bbox, - fig_kwargs={'figsize':[20,20]}, - bmap_kwargs={'suppress_ticks':False, - 'resolution':'h'}, - plot_kwargs={'cmap':'BrBG','s':8,'edgecolor':'none'} + fig_kwargs={'figsize': [20, 20]}, + bmap_kwargs={'suppress_ticks': False, + 'resolution': 'h', 'epsg': '26943'}, + plot_kwargs={'cmap': 'BrBG', 's': 8, 'edgecolor': 'none'} .. image:: img/2000metersum.png diff --git a/examples/AnythingScore.ipynb b/examples/AnythingScore.ipynb deleted file mode 100644 index d4dd387a..00000000 --- a/examples/AnythingScore.ipynb +++ /dev/null @@ -1,162 +0,0 @@ -{ - "metadata": { - "name": "", - "signature": "sha256:52c5ec0c481a3b7ec7d72e43611bea87986cd8313542e0584cda9cc3b874edac" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ - { - "cells": [ - { - "cell_type": "heading", - "level": 3, - "metadata": {}, - "source": [ - "Output has been removed from this notebook to reduce file sizes in the repo" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before you get started, note that this notebook requires the [osm](https://github.com/geopandas/geopandas/tree/osm) branch of geopandas to be installed. This is all still fairly experimental functionality in geopandas, and thus should still be considered experimental here as well (`utils.py` is not fully unit tested). On the other hand, the actual POI queries in Pandana are unit tested and can be considered ready for release." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "import pandas as pd\n", - "import numpy as np\n", - "import pandana as pdna\n", - "from pandana import utils\n", - "import geopandas.io.osm as osm\n", - "%matplotlib inline" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Read in the network and initialize" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "store = pd.HDFStore('osm_bayarea.h5', \"r\")\n", - "net=pdna.Network(store.nodes[\"x\"], \n", - " store.nodes[\"y\"], \n", - " store.edges[\"from\"], \n", - " store.edges[\"to\"], \n", - " store.edges[[\"weight\"]])\n", - "\n", - "# make sure you have enough categories for your score\n", - "net.init_pois(num_categories=10, max_dist=2400, max_pois=10)\n", - "\n", - "bbox = [-122.8662,37.1373,-121.4798,38.2158] # san francisco" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Make a decay function" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from scipy.special import expit\n", - "def dist_to_contrib(dist):\n", - " dist = dist.astype('float') / 2400 # now varies from 0 to 1\n", - " dist = (dist * -10) + 5\n", - " return expit(dist)" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Create an anything score" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "s = utils.anything_score(\n", - " net,\n", - " {\n", - " \"shop=supermarket\": [3.0],\n", - " \"amenity=restaurant\": [.75, .45, .25, .25, .225, .225, .225, .225, .2, .2],\n", - " \"shop=convenience\": [.5, .45, .4, .35, .3],\n", - " \"amenity=cafe\": [1.25, .75],\n", - " \"amenity=bank\": [1.0],\n", - " \"leisure=park\": [1.0],\n", - " \"amenity=school\": [1.0],\n", - " \"amenity=library\": [1.0],\n", - " \"amenity=bar\": [1.0]\n", - " },\n", - " 2400,\n", - " dist_to_contrib, # decay function to apply to distance\n", - " bbox\n", - ")\n", - "print s.describe() " - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "See it" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from IPython.display import set_matplotlib_formats\n", - "set_matplotlib_formats('png')\n", - "out_bbox = utils.bbox_convert([-122.539365,37.693047,-122.347698,37.816069], \n", - " from_epsg=4326, to_epsg=3740)\n", - "net.plot(s, bbox=out_bbox, scheme=\"diverging\", color=\"BrBG\")" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] - } - ], - "metadata": {} - } - ] -} \ No newline at end of file diff --git a/examples/Example.ipynb b/examples/Example.ipynb index fe6e3436..4c2acad3 100644 --- a/examples/Example.ipynb +++ b/examples/Example.ipynb @@ -1,576 +1,590 @@ { + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Output has been removed from this notebook to reduce file sizes in the repo" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "import pandas as pd\n", + "import numpy as np\n", + "import pandana as pdna\n", + "from pandana.loaders import osm\n", + "%matplotlib inline\n", + "\n", + "import warnings\n", + "warnings.filterwarnings('ignore')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Download OpenStreetMap restaurants for a good part of the Bay Area" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "###### Note: used http://boundingbox.klokantech.com/ to get the bounding box" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Bounding box from link above\n", + "tmp = [-122.8662, 37.1373, -121.4798, 38.2158]\n", + "\n", + "# Reordered for Pandana functions\n", + "bbox = [tmp[1], tmp[0], tmp[3], tmp[2]]\n", + "\n", + "poi_df = osm.node_query(*bbox, tags='amenity=restaurant')\n", + "x, y = poi_df['lon'], poi_df['lat']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Get previously stored OpenStreetMap networks for Bay Area\n", + "\n", + "Download the data here: https://s3-us-west-1.amazonaws.com/synthpop-data2/pandana/osm_bayarea.h5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "store = pd.HDFStore('data/osm_bayarea.h5', \"r\")\n", + "nodes = store.nodes\n", + "edges = store.edges\n", + "print nodes.head(3)\n", + "print edges.head(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Initialize and preprocess the network" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net=pdna.Network(nodes.x, \n", + " nodes.y, \n", + " edges[\"from\"], \n", + " edges[\"to\"],\n", + " edges[[\"weight\"]])\n", + "net.precompute(3000)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Nearest *point-of-interest* queries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.init_pois(num_categories=1, max_dist=2000, max_pois=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.set_pois(\"restaurants\", x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "a = net.nearest_pois(2000, \"restaurants\", num_pois=10)\n", + "print a.head(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Here's a map of the distance to the nearest restaurant" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "fig_kwargs = {'figsize': [20, 20]}\n", + "bmap_kwargs = {'suppress_ticks': False, 'resolution': 'h', 'epsg': '26943'}\n", + "plot_kwargs = {'cmap': 'BrBG', 's': 8, 'edgecolor': 'none'}\n", + "\n", + "sf_tmp = [-122.524338, 37.707794, -122.34993, 37.834192]\n", + "sf_bbox = [sf_tmp[1], sf_tmp[0], sf_tmp[3], sf_tmp[2]]\n", + "\n", + "net.plot(a[1], bbox=sf_bbox, \n", + " fig_kwargs=fig_kwargs, bmap_kwargs=bmap_kwargs, plot_kwargs=plot_kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Here's a map of the distance to the 5th nearest restaurant" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.plot(a[5], bbox=sf_bbox, \n", + " fig_kwargs=fig_kwargs, bmap_kwargs=bmap_kwargs, plot_kwargs=plot_kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Here's a map of the distance to the 10th nearest restaurant" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.plot(a[10], bbox=sf_bbox, \n", + " fig_kwargs=fig_kwargs, bmap_kwargs=bmap_kwargs, plot_kwargs=plot_kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# A similar workflow is used to do general network aggregations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Relate the x-ys to nodes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "node_ids = net.get_node_ids(x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Assign the variable (in this case just location) to the network" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.set(node_ids)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## This is it - run the queries!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "%time s = net.aggregate(500, type=\"sum\", decay=\"linear\")\n", + "%time t = net.aggregate(1000, type=\"sum\", decay=\"linear\")\n", + "%time u = net.aggregate(2000, type=\"sum\", decay=\"linear\")\n", + "%time v = net.aggregate(3000, type=\"sum\", decay=\"linear\")\n", + "%time w = net.aggregate(3000, type=\"count\", decay=\"flat\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Here's a map of access to restaurants with a 500m radius" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.plot(s, bbox=sf_bbox, \n", + " fig_kwargs=fig_kwargs, bmap_kwargs=bmap_kwargs, plot_kwargs=plot_kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Or 1000 meters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.plot(t, bbox=sf_bbox, \n", + " fig_kwargs=fig_kwargs, bmap_kwargs=bmap_kwargs, plot_kwargs=plot_kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Or 2000 meters radius" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.plot(u, bbox=sf_bbox, \n", + " fig_kwargs=fig_kwargs, bmap_kwargs=bmap_kwargs, plot_kwargs=plot_kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Or 3000m radius" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.plot(v, bbox=sf_bbox, \n", + " fig_kwargs=fig_kwargs, bmap_kwargs=bmap_kwargs, plot_kwargs=plot_kwargs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Or the whole Bay Area region" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "net.plot(w, bbox=bbox, \n", + " fig_kwargs=fig_kwargs, bmap_kwargs=bmap_kwargs, plot_kwargs=plot_kwargs)" + ] + } + ], "metadata": { - "name": "", - "signature": "sha256:b761890efa92ab55edfbf2425340e80dee066f572c7a58c1b0c359d2d9def445" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ - { - "cells": [ - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Output has been removed from this notebook to reduce file sizes in the repo" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "import pandas as pd\n", - "import numpy as np\n", - "import pandana as pdna\n", - "import geopandas.io.osm as osm\n", - "%matplotlib inline" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Download OpenStreetMap restaurants for a good part of the Bay Area" - ] - }, - { - "cell_type": "heading", - "level": 6, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "source": [ - "Note: used http://boundingbox.klokantech.com/ to get the bounding box" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "gdf = osm.query_osm('node', \n", - " bbox=[-122.8662,37.1373,-121.4798,38.2158],\n", - " tags='amenity=restaurant')" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "gdf = gdf[gdf.type == 'Point'].to_crs(epsg=3740)\n", - "print gdf.geometry.head(3)\n", - "print len(gdf)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "x, y = zip(*[(p.x, p.y) for (i, p) \n", - " in gdf.geometry.iteritems()])\n", - "x = pd.Series(x)\n", - "y = pd.Series(y)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Get OpenStreetMap networks for Bay Area that I had previously - someday soon we'll have direct OSM import" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "store = pd.HDFStore('data/osm_bayarea.h5', \"r\")\n", - "nodes = store.nodes\n", - "edges = store.edges\n", - "print nodes.head(3)\n", - "print edges.head(3)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Initialize and preprocess the network" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net=pdna.Network(nodes.x, \n", - " nodes.y, \n", - " edges[\"from\"], \n", - " edges.to, \n", - " edges[[\"weight\"]])\n", - "net.precompute(3000)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Nearest *point-of-interest* queries" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.init_pois(num_categories=1, max_dist=2000, max_pois=10)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.set_pois(\"restaurants\", x, y)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "a = net.nearest_pois(2000, \"restaurants\", num_pois=10)\n", - "print a.head(1)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from shapely.geometry import Point\n", - "from fiona.crs import from_epsg\n", - "import geopandas as gpd\n", - "bbox=[-122.539365,37.693047,-122.347698,37.816069]\n", - "bbox = gpd.GeoSeries([Point(bbox[0], bbox[1]),\n", - " Point(bbox[2], bbox[3])], \n", - " crs=from_epsg(4326))\n", - "bbox = bbox.to_crs(epsg=3740)\n", - "bbox = [bbox[0].x, bbox[0].y, bbox[1].x, bbox[1].y]" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Here's a map of the distance to the nearest restaurant" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.plot(a[1], bbox=bbox, scheme=\"diverging\", \n", - " color=\"BrBG\")" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Here's a map of the distance to the 5th nearest restaurant" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.plot(a[5], bbox=bbox, scheme=\"diverging\", \n", - " color=\"BrBG\")" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Here's a map of the distance to the 10th nearest restaurant" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.plot(a[10], bbox=bbox, scheme=\"diverging\", \n", - " color=\"BrBG\")" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 1, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "A similar workflow is used to do general network aggregations" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Relate the x-ys to nodes" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "node_ids = net.get_node_ids(x, y)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "subslide" - } - }, - "source": [ - "Assign the variable (in this case just location) to the network" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.set(node_ids)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "subslide" - } - }, - "source": [ - "This is it - run the queries!" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%time s = net.aggregate(500, type=\"sum\", decay=\"linear\")\n", - "%time t = net.aggregate(1000, type=\"sum\", decay=\"linear\")\n", - "%time u = net.aggregate(2000, type=\"sum\", decay=\"linear\")\n", - "%time v = net.aggregate(3000, type=\"sum\", decay=\"linear\")\n", - "%time w = net.aggregate(3000, type=\"count\", decay=\"flat\")" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Here's a map of access to restaurants with a 500m radius" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.plot(s, bbox=bbox, scheme=\"diverging\", \n", - " color=\"BrBG\", log_scale=True)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Or 1000 meters" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.plot(t, bbox=bbox, scheme=\"diverging\", \n", - " color=\"BrBG\", log_scale=True)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Or 2000 meters radius" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.plot(u, bbox=bbox, scheme=\"diverging\", \n", - " color=\"BrBG\", log_scale=True)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Or 3000m radius" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.plot(v, bbox=bbox, scheme=\"diverging\", \n", - " color=\"BrBG\", log_scale=True)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Or the whole Bay Area region - someone please help me with this visualization!" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "net.plot(v, scheme=\"diverging\", \n", - " color=\"BrBG\", log_scale=True)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] - } - ], - "metadata": {} + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.13" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 0 +} From b4934871b078e733e90fb8c28902cc4228da24c7 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 3 Apr 2017 17:07:03 -0700 Subject: [PATCH 37/42] fixed bullet list in docs --- docs/introduction.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/introduction.rst b/docs/introduction.rst index 35703263..bfa5dbd4 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -95,9 +95,9 @@ variable using network queries. care, or urban predictive variables - e.g. average income in the local area, or simply for data exploration. A common use case will be to write to shapefiles and use in further GIS analysis, or to relate to parcels and - buildings and use in further analysis within `UrbanSim`_ and the -`Urban Data Science Toolkit`_. There are many possibilities, and we hope designing a - flexible and easy to use engine will serve many use cases. + buildings and use in further analysis within `UrbanSim`_ and the `Urban Data Science Toolkit`_. + There are many possibilities, and we hope designing a flexible and easy to + use engine will serve many use cases. Reporting bugs From 31b8d31c5cc9b5c10e6f8a8ad5e252c2be3ad016 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 3 Apr 2017 17:14:18 -0700 Subject: [PATCH 38/42] updated tutorial --- docs/tutorial.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 56877764..27a233c1 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -210,8 +210,10 @@ matplotlib. For quick interactive checking of results, the bounding box can be used to reduce the number of points that are shown, and sample code and images are included below. :: - bbox = [-122.539365, 37.693047, -122.347698, 37.816069] - net.plot(s, bbox=bbox, + sf_tmp = [-122.524338, 37.707794, -122.34993, 37.834192] + sf_bbox = [sf_tmp[1], sf_tmp[0], sf_tmp[3], sf_tmp[2]] + + net.plot(s, bbox=sf_bbox, fig_kwargs={'figsize': [20, 20]}, bmap_kwargs={'suppress_ticks': False, 'resolution': 'h', 'epsg': '26943'}, @@ -221,7 +223,7 @@ and sample code and images are included below. :: :: - net.plot(u, bbox=bbox, + net.plot(u, bbox=sf_bbox, fig_kwargs={'figsize': [20, 20]}, bmap_kwargs={'suppress_ticks': False, 'resolution': 'h', 'epsg': '26943'}, From ac809e28a2f03bbfdcd99fb8b86baa25250b2e3c Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 3 Apr 2017 17:28:00 -0700 Subject: [PATCH 39/42] removed slideshow from readme, fixed docstring typo, updated bbox in tutorial --- README.rst | 2 -- docs/tutorial.rst | 3 +-- pandana/network.py | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index b1b289ce..1b86f8fd 100644 --- a/README.rst +++ b/README.rst @@ -9,8 +9,6 @@ Pandana :alt: Coverage Status :target: https://coveralls.io/r/UDST/pandana -A nice slideshow showing example code is available -`here `__. In this case, a picture is worth a thousand words. The image below shows the distance to the *2nd* nearest restaurant (rendered by matplotlib) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 27a233c1..c43531fc 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -210,8 +210,7 @@ matplotlib. For quick interactive checking of results, the bounding box can be used to reduce the number of points that are shown, and sample code and images are included below. :: - sf_tmp = [-122.524338, 37.707794, -122.34993, 37.834192] - sf_bbox = [sf_tmp[1], sf_tmp[0], sf_tmp[3], sf_tmp[2]] + sf_bbox = [37.707794, -122.524338, 37.834192, -122.34993] net.plot(s, bbox=sf_bbox, fig_kwargs={'figsize': [20, 20]}, diff --git a/pandana/network.py b/pandana/network.py index 8a705c69..cfe55e32 100644 --- a/pandana/network.py +++ b/pandana/network.py @@ -201,7 +201,7 @@ def set(self, node_ids, variable=None, name="tmp"): Parameters ---------- - node_id : Pandas Series, int + node_ids : Pandas Series, int A series of node_ids which are usually computed using get_node_ids on this object. variable : Pandas Series, float, optional From a58ab6070ea48a462a54e5c9686819597edebcb0 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 3 Apr 2017 17:39:31 -0700 Subject: [PATCH 40/42] updated readme --- README.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 1b86f8fd..c559ce51 100644 --- a/README.rst +++ b/README.rst @@ -54,9 +54,8 @@ Pandana is also available. Acknowledgments --------------- -None of this would be possible without the help of Dennis Luxen (now at -MapBox) and his OSRM (https://github.com/DennisOSRM/Project-OSRM). Thank -you Dennis! +None of this would be possible without the help of Dennis Luxen and +his OSRM (https://github.com/DennisOSRM/Project-OSRM). Thank you Dennis! Nearest neighbor queries are performed with the fastest k-d tree around, i.e. ANN (http://www.cs.umd.edu/~mount/ANN/). From 284546abea0d4706e026148c017e3fcfd0dbc543 Mon Sep 17 00:00:00 2001 From: sablanchard Date: Mon, 3 Apr 2017 17:52:28 -0700 Subject: [PATCH 41/42] updated docstring --- pandana/network.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pandana/network.py b/pandana/network.py index cfe55e32..db7afabe 100644 --- a/pandana/network.py +++ b/pandana/network.py @@ -506,7 +506,10 @@ def init_pois(self, num_categories, max_dist, max_pois): def set_pois(self, category, x_col, y_col): """ - Set the location of all the pois of this category + Set the location of all the pois of this category. The pois are + connected to the closest node in the Pandana network which assumes + no impedance between the location of the variable and the location + of the closest network node. Parameters ---------- From 1785f80db5f0955b2019c35ffa95f40864fa9fa1 Mon Sep 17 00:00:00 2001 From: Paul Sohn Date: Tue, 4 Apr 2017 11:26:08 -0700 Subject: [PATCH 42/42] Hotfixes to documentation --- docs/conf.py | 2 +- docs/installation.rst | 2 ++ docs/introduction.rst | 2 +- docs/tutorial.rst | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index d7b8bd16..c4c2f2fb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ # General information about the project. project = 'pandana' -copyright = '2015, UrbanSim Inc.' +copyright = '2017, UrbanSim Inc.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/installation.rst b/docs/installation.rst index c1347eca..f078f77f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -15,6 +15,7 @@ Pandana depends on the following libraries, most of which are in Anaconda: * `numpy`_ >= 1.8.0 * `pandas`_ >= 0.13.1 * `tables`_ >= 3.1.0 +* `osmnet`_ >= 0.1.0 Install the latest release -------------------------- @@ -112,3 +113,4 @@ on your platform - for instance :code:`g++-mp-4.9` or :code:`g++-4.8`. .. _numpy: http://www.numpy.org/ .. _pandas: http://pandas.pydata.org/ .. _tables: http://www.pytables.org/ +.. _osmnet: http://github.com/udst/osmnet diff --git a/docs/introduction.rst b/docs/introduction.rst index bfa5dbd4..782a5da3 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -117,7 +117,7 @@ License Pandana is licensed under the AGPL license. Related UDST libraries ----------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ - `OSMnet`_ - `UrbanAccess`_ diff --git a/docs/tutorial.rst b/docs/tutorial.rst index c43531fc..5f3c502f 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -7,8 +7,9 @@ in the ``Pandana`` repo which gives the entire workflow, but the discussion here will take things line-by-line with a sufficient summary of the functionality. -Note that these code samples assume you have imported pandana as follows:: +Note that these code samples assume you have imported pandas and pandana as follows:: + import pandas as pd import pandana as pdna Create the Network