def build_network(settings): """ Build a Pandana network from CSV files """ logger.info('building pandana network') network_settings_file = settings['network_settings_file'] if not network_settings_file: logger.error("Please specify 'network_settings_file' in settings") return network_settings = config.read_model_settings(network_settings_file) logger.debug('using settings %s' % network_settings) nodes = pd.read_csv(config.data_file_path(network_settings['nodes'])) links = pd.read_csv(config.data_file_path(network_settings['links'])) nodes.index = nodes[network_settings['nodes-id']] network = pdna.Network(nodes[network_settings['nodes-x']], nodes[network_settings['nodes-y']], links[network_settings['links-a']], links[network_settings['links-b']], links[[network_settings['links-impedance']]], twoway=network_settings['twoway']) network.save_hdf5(config.output_file_path('pandana_network.h5')) return network
def build_networkwalk(): nodeswalk = pd.read_csv(d + 'bayarea_walk_nodes.csv') \ .set_index('osmid') edgeswalk = pd.read_csv(d + 'bayarea_walk_edges.csv') netwalk = pdna.Network(nodeswalk.x, nodeswalk.y, edgeswalk.u, \ edgeswalk.v, edgeswalk[['length']], twoway=True) netwalk.precompute(2500) return netwalk
def build_networks(settings): name = settings['build_networks']['name'] st = pd.HDFStore(os.path.join(misc.data_dir(), name), "r") nodes, edges = st.nodes, st.edges net = pdna.Network(nodes["x"], nodes["y"], edges["from"], edges["to"], edges[["weight"]]) net.precompute(settings['build_networks']['max_distance']) return net
def build_networksmall(): nodessmall = pd.read_csv(d + 'bay_area_tertiary_strongly_nodes.csv') \ .set_index('osmid') edgessmall = pd.read_csv(d + 'bay_area_tertiary_strongly_edges.csv') netsmall = pdna.Network(nodessmall.x, nodessmall.y, edgessmall.u, \ edgessmall.v, edgessmall[['length']], twoway=False) netsmall.precompute(25000) return netsmall
def build_networkbeam(): nodesbeam = pd.read_csv(d + 'physsim-network-nodes.csv') \ .set_index('id') edgesbeam = pd.read_csv(d + 'physsim-network-links.csv') netbeam = pdna.Network( nodesbeam['x'], nodesbeam['y'], edgesbeam['from'], edgesbeam['to'], edgesbeam[['travelTime']], twoway=False) netbeam.precompute(7200) return netbeam
def build_networks(parcels, net_store): st = net_store nodes, edges = st.nodes, st.edges net = pdna.Network(nodes["x"], nodes["y"], edges["from"], edges["to"], edges[["weight"]]) net.precompute(3000) orca.add_injectable("net", net) p = parcels.to_frame(parcels.local_columns) p['node_id'] = net.get_node_ids(p['x'], p['y']) orca.add_table("parcels", p)
def build_networks(parcels): st = pd.HDFStore(os.path.join(misc.data_dir(), "osm_sandag.h5"), "r") nodes, edges = st.nodes, st.edges net = pdna.Network(nodes["x"], nodes["y"], edges["from"], edges["to"], edges[["weight"]]) net.precompute(3000) orca.add_injectable("net", net) p = parcels.to_frame(parcels.local_columns) p['node_id'] = net.get_node_ids(p['x'], p['y']) orca.add_table("parcels", p)
def build_networkbeam(nodesbeam, edgesbeam): nodesbeam, edgesbeam = nodesbeam.to_frame(), edgesbeam.to_frame() print('Number of nodes is %s.' % len(nodesbeam)) print('Number of edges is %s.' % len(edgesbeam)) netbeam = pdna.Network(nodesbeam['lon'], nodesbeam['lat'], edgesbeam['from'], edgesbeam['to'], edgesbeam[['traveltime']], twoway=False) netbeam.precompute(3600) return netbeam
def build_networkwalk(nodeswalk, edgeswalk): nodeswalk, edgeswalk = nodeswalk.to_frame(), edgeswalk.to_frame() print('Number of nodes is %s.' % len(nodeswalk)) print('Number of edges is %s.' % len(edgeswalk)) netwalk = pdna.Network(nodeswalk.x, nodeswalk.y, edgeswalk.u, edgeswalk.v, edgeswalk[['length']], twoway=True) netwalk.precompute(5000) return netwalk
def graph_to_pandananet(gdf_edges, gdf_nodes): twoway = list(~gdf_edges['oneway'].values) pdnet = pdna.Network(gdf_nodes['x'], gdf_nodes['y'], gdf_edges['u'], gdf_edges['v'], gdf_edges[["time_to_traverse"]], twoway=twoway) return pdnet
def get_osm_network(geodataframe, maxdist=5000, quiet=True, **kwargs): """Download a street network from OSM. Parameters ---------- geodataframe : geopandas.GeoDataFrame geopandas.GeoDataFrame of the study area. Coordinate system should be in WGS84 maxdist : int Total distance (in meters) of the network queries you may need. This is used to buffer the network to ensure theres enough to satisfy your largest query, otherwise there may be edge effects. quiet: bool If True, diagnostic messages from urbanaccess will be suppressed **kwargs : dict additional kwargs passed through to urbanaccess.ua_network_from_bbox Returns ------- pandana.Network A pandana Network instance for use in accessibility calculations or spatial segregation measures that include a distance decay """ try: import pandana as pdna from urbanaccess.osm.load import ua_network_from_bbox except ImportError: raise ImportError( "You need pandana and urbanaccess to work with segregation's network module\n" "You can install them with `pip install urbanaccess pandana` " "or `conda install -c udst pandana urbanaccess`") gdf = geodataframe.copy() gdf = gdf.to_crs(gdf.estimate_utm_crs()) gdf = gdf.buffer(maxdist) bounds = gdf.to_crs(epsg=4326).total_bounds if quiet: print('Downloading data from OSM. This may take awhile.') with _HiddenPrints(): net = ua_network_from_bbox(bounds[1], bounds[0], bounds[3], bounds[2], **kwargs) else: net = ua_network_from_bbox(bounds[1], bounds[0], bounds[3], bounds[2], **kwargs) print("Building network") network = pdna.Network(net[0]["x"], net[0]["y"], net[1]["from"], net[1]["to"], net[1][["distance"]]) return network
def get_network(geodataframe, maxdist=5000, quiet=True, **kwargs): """Download a street network from OSM. Parameters ---------- geodataframe : geopandas.GeoDataFrame geopandas.GeoDataFrame of the study area. maxdist : int Total distance (in meters) of the network queries you may need. This is used to buffer the network to ensure theres enough to satisfy your largest query, otherwise there may be edge effects. **kwargs : dict additional kwargs passed through to urbanaccess.ua_network_from_bbox Returns ------- pandana.Network A pandana Network instance for use in accessibility calculations or spatial segregation measures that include a distance decay Examples ------- Examples should be written in doctest format, and should illustrate how to use the function/class. >>> """ gdf = geodataframe.copy() assert gdf.crs == { 'init': 'epsg:4326' }, "geodataframe must be in epsg 4326" gdf = project_gdf(gdf) gdf = gdf.buffer(maxdist) bounds = gdf.to_crs(epsg=4326).total_bounds if quiet: print('Downloading data from OSM. This may take awhile.') with _HiddenPrints(): net = ua_network_from_bbox(bounds[1], bounds[0], bounds[3], bounds[2], **kwargs) else: net = ua_network_from_bbox(bounds[1], bounds[0], bounds[3], bounds[2], **kwargs) print("Building network") network = pdna.Network(net[0]["x"], net[0]["y"], net[1]["from"], net[1]["to"], net[1][["distance"]]) return network
def 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(500) def fin(): store.close() request.addfinalizer(fin) return net
def distance_to_points(centroids, points_of_interest, output_filename): data_dir = "data" output_dir = "output" # OS Open Road Nodes and Links dfNodes = pd.read_csv(os.path.join(data_dir, "OS Open Road Nodes.csv")) dfLinks = pd.read_csv(os.path.join(data_dir, "OS Open Road Links.csv")) dfNodes.drop_duplicates(inplace=True) dfNodes.set_index('identifier', inplace=True) # ---- Compute Distances ---- # Get largest connected component edges = dfLinks.loc[:,['startNode','endNode','length']].values G = nx.Graph() G.add_weighted_edges_from(edges) largest_connected_component = sorted(nx.connected_components(G), key = len, reverse=True)[0] # Clean up to save memory G = None edges = None # Create pandana network. It's much faster for nearest point-of-interest analysis # Filter nodes and edges to just include those in the largest connected componet dfLinksLCC = dfLinks.loc[(dfLinks['startNode'].isin(largest_connected_component)) & (dfLinks['endNode'].isin(largest_connected_component))] dfNodesLCC = dfNodes.loc[largest_connected_component] net=pdna.Network(dfNodesLCC["x"], dfNodesLCC["y"], dfLinksLCC["startNode"], dfLinksLCC["endNode"], dfLinksLCC[["length"]]) # Get the nearest three points of interest search_distance = 200000 net.set_pois("points", search_distance, 3, points_of_interest.geometry.x, points_of_interest.geometry.y) dfNear = net.nearest_pois(search_distance, "points", num_pois=3, include_poi_ids=True) # ---- LSOA Computations ---- # Get just the LSOA centroids and their nearest points lsoa_nodes = net.get_node_ids(centroids.X, centroids.Y) lsoaComps = dfNear.loc[lsoa_nodes] # Include LSOA codes centroids['lsoa_nodes'] = lsoa_nodes # Merge network distances and LSOA data lsoaComps.reset_index(inplace=True) lsoaComps = pd.merge(lsoaComps, centroids, left_on = 'identifier', right_on = 'lsoa_nodes', how = 'outer', indicator = True) # Wrangle lsoaComps['mean_distance_nearest_three_points'] = lsoaComps.loc[:,[1,2,3]].mean(axis = 1) # Write lsoaComps.to_csv(os.path.join(output_dir, output_filename), index=False)
def project_network(network, output_crs=None, input_crs=4326): """Reproject a pandana.Network object into another coordinate system Parameters ---------- network : pandana.Network an instantiated pandana Network object input_crs : int, optional the coordinate system used in the Network.node_df dataframe. Typically these data are collected in Lon/Lat, so the default 4326 output_crs : int, str, or pyproj.crs.CRS, required EPSG code or pyproj.crs.CRS object of the output coordinate system Returns ------- pandana.Network an initialized pandana.Network with 'x' and y' values represented by coordinates in the specified CRS """ try: import pandana as pdna except ImportError: raise ImportError( "You need pandana and urbanaccess to work with segregation's network module\n" "You can install them with `pip install urbanaccess pandana` " "or `conda install -c udst pandana urbanaccess`" ) assert output_crs, "You must provide an output CRS" # take original x,y coordinates and convert into geopandas.Series, then reproject nodes = _reproject_osm_nodes(network.nodes_df, input_crs, output_crs) # reinstantiate the network (needs to rebuild the tree) net = pdna.Network( node_x=nodes["x"], node_y=nodes["y"], edge_from=network.edges_df["from"], edge_to=network.edges_df["to"], edge_weights=network.edges_df[network.impedance_names], twoway=network._twoway, ) return net
def get_osm_network(zone_data, settings): """ Retrieve Pandana network from Open Street Maps """ logger.info('getting osm network') zones_df = zone_data.to_frame() miles = settings.get('distance_units') == 'miles' # distance to degrees: 111 km = 69 miles = 1 degree of long (y), 3mi = 0.043 conversion = 69 if miles else 111 * 1000 buffer = settings.get('max_dist') / conversion xmin = min(zones_df[settings['zones_lon']]) - buffer xmax = max(zones_df[settings['zones_lon']]) + buffer ymin = min(zones_df[settings['zones_lat']]) - buffer ymax = max(zones_df[settings['zones_lat']]) + buffer logger.debug('bounding box: %s, %s, %s, %s' % (str(ymin), str(xmin), str(ymax), str(xmax))) # default type=walk, which excludes freeways nodes, edges = osm.network_from_bbox(lat_min=ymin, lng_min=xmin, lat_max=ymax, lng_max=xmax, two_way=True, network_type='walk') if miles: logger.info('converting network distance units to miles...') edges[['distance']] = edges[['distance']] / 1609.34 network = pdna.Network(nodes['x'], nodes['y'], edges['from'], edges['to'], edges[['distance']]) print(edges.head()) print(edges[['distance']]) network.save_hdf5(config.output_file_path('pandana_network.h5')) return network
def create_pdna_net(gdf_nodes, gdf_edges, predistance=500): """ Create pandana network to prepare for calculating the accessibility to destinations The network is comprised of a set of nodes and edges. Parameters ---------- gdf_nodes: GeoDataFrame gdf_edges: GeoDataFrame predistance: int the distance of search (in meters), default is 500 meters Returns ------- pandana network """ # Defines the x attribute for nodes in the network gdf_nodes["x"] = gdf_nodes["geometry"].apply(lambda x: x.x) # Defines the y attribute for nodes in the network (e.g. latitude) gdf_nodes["y"] = gdf_nodes["geometry"].apply(lambda x: x.y) # Defines the node id that begins an edge gdf_edges["from"] = gdf_edges["u"].astype(np.int64) # Defines the node id that ends an edge gdf_edges["to"] = gdf_edges["v"].astype(np.int64) # Define the distance based on OpenStreetMap edges gdf_edges["length"] = gdf_edges["length"].astype(float) gdf_nodes["id"] = gdf_nodes["osmid"].astype(np.int64) gdf_nodes.set_index("id", inplace=True, drop=False) # Create the transportation network in the city # Typical data would be distance based from OSM or travel time from GTFS transit data net = pdna.Network(gdf_nodes["x"], gdf_nodes["y"], gdf_edges["from"], gdf_edges["to"], gdf_edges[["length"]]) # Precomputes the range queries (the reachable nodes within this maximum distance) # so that aggregations don’t perform the network queries unnecessarily net.precompute(predistance + 10) return net
def construct_network(self): # Get all edges query = f"SELECT * FROM {self.schema}.{self.edge_table_name}" edge_gdf = self.db.query_as_geo_df(query) # Get all nodes node_query = f""" SELECT sw_node_id AS node_id, ST_X(st_transform(geom, 4326)) as x, ST_Y(st_transform(geom, 4326)) as y, geom FROM {self.schema}.sw_nodes """ node_gdf = self.db.query_as_geo_df(node_query) # Force the ID columns in the edge gdf to integer for col in ["start_id", "end_id"]: edge_gdf[col] = edge_gdf[col].astype(int) # Set the index of the NODE gdf to the uid column node_gdf.set_index('node_id', inplace=True) # Build the pandana network network = pdna.Network(node_gdf["x"], node_gdf["y"], edge_gdf["start_id"], edge_gdf["end_id"], edge_gdf[["minutes"]], twoway=True) network.precompute(self.max_minutes) # Save the network and geodataframes within the object self.network = network self.edge_gdf = edge_gdf self.node_gdf = node_gdf
import geopandas as gpd import pandana as pdna roads = gpd.read_file("/home/cuip/edges/edges.shp") stops = gpd.read_file("/home/cuip/Archive/Stops.shp") net= pdna.Network(stops["Latitude"], stops["Longitude"], roads["from"], roads["to"], roads[["length"]]) net.precompute(420) net.set_pois(stops, 420, 10, stops.Latitude, stops.Longitude) net.nearest_pois(420, stops, num_pois=10)
def test_workflow(): # helper functions def _parse_wkt(s): """Parse wkt and ewkt strings into shapely shapes. For ewkt (the PostGIS extension to wkt), the SRID indicator is removed. """ if s.startswith('SRID'): s = s[s.index(';') + 1:] return shapely.wkt.loads(s) long_dash = ''.join(['-' for n in range(25)]) def update_status(custom_note='', clarify=False): for item in ['\n', long_dash, custom_note]: print(item) # let's find transit providers in Madison, WI search_results = urbanaccess.gtfsfeeds.search(search_text='madison') # ============= # Section break # ============= update_status(('We just queried for transit providers with UA. ' 'Next, we will specify a transit resource to download.')) # add a feed to the gtfs to include in the analysis feeds = urbanaccess.gtfsfeeds.feeds name = 'madison' # Note: query suggests: http://www.cityofmadison.com/metro/gtfs/mmt_gtfs.zip # but this address is currently is 404'ing (04-16-2017) # ...as a result, using link below which _does_ work in the meantime url = 'http://www.gtfs-data-exchange.com/agency/city-of-madison/latest.zip' new_feed = {name:url} feeds.add_feed(new_feed) # download the feed, will be placed in folders within data/gtfsfeed_text # according to the dict key name urbanaccess.gtfsfeeds.download() # ============= # Section break # ============= update_status(('Next, we need to load the feeds into a Pandas DataFrame.')) # now that we have saved the raw gtfs data, we need to load it in gtfsfeed_path = None # use default gtfs save location validation = True verbose = True bbox = (-89.441414,43.047314,-89.325371,43.129553) remove_stops_outsidebbox = True append_definitions = True # updates these attributes: stops, routes, trips, stop_times, calendar, # calendar_dates, stop_times_int, headways loaded_feeds = urbanaccess.gtfs.load.gtfsfeed_to_df( gtfsfeed_path, validation, verbose, bbox, remove_stops_outsidebbox, append_definitions) # ============= # Section break # ============= update_status(('Next, we need to interpolate stop times data from GTFS.')) # what remains an empty dataframe is stop_times_int, which we still # need to generate before we can get to calculating headways columns = ['route_id', 'direction_id', 'trip_id', 'service_id', 'unique_agency_id'] day = 'wednesday' # pick an arbitrary day of week tripschedualselector = urbanaccess.gtfs.network._trip_schedule_selector cal_selected_trips = tripschedualselector( loaded_feeds.trips[columns], loaded_feeds.calendar, loaded_feeds.calendar_dates, day) # approximate missing stop times via linear interpolation interpolatestoptimes = urbanaccess.gtfs.synthesize.interp_stop_times intermediate_interpolation = interpolatestoptimes( loaded_feeds.stop_times, cal_selected_trips, day) # now calculate the difference in top times in new column timedifference = urbanaccess.gtfs.synthesize.add_time_difference stop_times_int = timedifference(intermediate_interpolation) # now we can update loaded_feeds with this new dataframe loaded_feeds.stop_times_int = stop_times_int # ============= # Section break # ============= update_status(('Now we can calculate headways with the interpolated data.')) # now we need to calculate the headways, given the downloaded gtfs headway_timerange = ['07:00:00','10:00:00'] # approx a.m. peak # the below function updates loaded_feeds, so that headways is populated loaded_feeds = urbanaccess.gtfs.headways.headways(loaded_feeds, headway_timerange) # ============= # Section break # ============= update_status(('At this point we are able to save/reload the data locally.')) # save the results from these initial processing steps locally filename = 'temp_network_analyzed.h5' urbanaccess.gtfs.network.save_processed_gtfs_data(loaded_feeds, 'data', filename) # we can now reload from that save location if we want loaded_feeds = urbanaccess.gtfs.network.load_processed_gtfs_data('data', filename) # ============= # Section break # ============= update_status(('Next, we need to generate the transit and osm networks.')) # to proceed, we need to generate a network describing the transit data ua_network = urbanaccess.gtfs.network.create_transit_net( gtfsfeeds_dfs=loaded_feeds, day=day, timerange=headway_timerange, overwrite_existing_stop_times_int=False, use_existing_stop_times_int=True, save_processed_gtfs=False) update_status(('Transit feed downloaded, now we want OSM from bbox.')) # now we're ready to download OSM data, let's use same bbox from gtfs search bbox = (-89.871597,42.824365,-88.972778,43.307942) # big # bbox = (-89.395366,43.06663,-89.373479,43.082305) # small osm_nodes, osm_edges = urbanaccess.osm.load.ua_network_from_bbox(bbox=bbox) update_status(('Both networks downloaded, now can clean osm data.')) # clean up the osm edges, make them type string to allow concatenating osm_edges['from'] = osm_edges['from'].astype(str) osm_edges['to'] = osm_edges['to'].astype(str) osm_edges['id'] = osm_edges['from'].str.cat(osm_edges['to']) keep = ['id', 'access', 'bridge', 'distance', 'from', 'hgv', 'highway', 'lanes', 'maxspeed', 'name', 'oneway', 'ref', 'service', 'to', 'tunnel'] # ensure that we have all the cols we need for col in keep: # make all missing but required cols empty if col not in osm_edges.columns.values: print('Missing column: ' + str(col)) osm_edges[col] = '' osm_edges = osm_edges[keep] osm_edges.drop_duplicates(subset='id', keep='first', inplace=True) update_status(('Next, create an OSM network in Pandana ready format.')) # with osm data, we can create a network just as we did with the gtfs data ua_network = urbanaccess.osm.network.create_osm_net( osm_edges = osm_edges, osm_nodes = osm_nodes, travel_speed_mph = 3, # walk speed average network_type = 'walk') # ============= # Section break # ============= update_status(('Now we have all networks we need, ' 'so we can integrate them.')) # result urbanaccess_nw vars is an object with the following attributes: # osm_edges, osm_nodes, # transit_edges, transit_nodes # and then returns the above, plus: # net_connector_edges, net_edges, net_nodes urbanaccess_nw = urbanaccess.network.integrate_network( urbanaccess_network=ua_network, headways=True, urbanaccess_gtfsfeeds_df=loaded_feeds, headway_statistic='mean') # now to shift over to pandana's domain nod_x = urbanaccess_nw.net_nodes['x'].astype(float) nod_y = urbanaccess_nw.net_nodes['y'].astype(float) # use the integer representation of each from and to id # (pandana can't handle them as strings) fr_null = urbanaccess_nw.net_edges['from_int'].isnull() to_null = urbanaccess_nw.net_edges['to_int'].isnull() print('There are {} disconnected edges.'.format( urbanaccess_nw.net_edges[(fr_null | to_null)])) urbanaccess_nw.net_edges = urbanaccess_nw.net_edges[~(fr_null | to_null)] edg_fr = urbanaccess_nw.net_edges['from_int'].astype(int) edg_to = urbanaccess_nw.net_edges['to_int'].astype(int) edg_wt_df = urbanaccess_nw.net_edges[['weight']].astype(float) # insantiate a pandana network object # set twoway to false since UA networks are oneways p_net = pdna.Network(nod_x, nod_y, edg_fr, edg_to, edg_wt_df, twoway=False) # precompute step, requires a max 'horizon' distance horizon_dist = 60 p_net.precompute(horizon_dist) # read in an example dataset blocks_df = pd.read_csv('./urbanaccess/tests/fixtures/blocks.csv') geometry = blocks_df['geometry'].map(_parse_wkt) blocks_df = blocks_df.drop('geometry', axis=1) crs = {'init': 'epsg:4326'} blocks_gdf = gpd.GeoDataFrame(blocks_df, crs=crs, geometry=geometry) # we need to extract the point lat/lon values blocks_gdf['x'] = blocks_gdf.centroid.map(lambda p: p.x) blocks_gdf['y'] = blocks_gdf.centroid.map(lambda p: p.y) # set node_ids as an attribute on the geodataframe blocks_gdf['node_ids'] = p_net.get_node_ids(blocks_gdf['x'], blocks_gdf['y']) p_net.set(blocks_gdf['node_ids'], variable=blocks_gdf['emp'], name='emp') # let's just make sure it can run through at least one aggregation # without erroring in pandana and be happy with that for now # Note: the results will be very limited because the blocks do not overlap # well with the network dataset from the bbox s = p_net.aggregate(15, type='sum', decay='flat', imp_name='weight', name='emp')
bbox = (-117.157516, 32.715666, -117.095032, 32.767068) west, south, east, north = bbox new_store = pd.HDFStore('sandag_subset.h5', 'w', complib='zlib', complevel=1) new_net_store = pd.HDFStore('osm_sandag_subset.h5', 'w', complib='zlib', complevel=1) with pd.HDFStore('sandag.h5') as store: with pd.HDFStore('osm_sandag.h5') as net_store: print('Subset network and save to new store') nodes, edges = net_store.nodes, net_store.edges net = pdna.Network(nodes["x"], nodes["y"], edges["from"], edges["to"], edges[["weight"]]) net.precompute(3000) parcels = store.parcels parcels['node_id'] = net.get_node_ids(parcels['x'], parcels['y']) new_nodes = nodes.loc[(nodes.x > west) & (nodes.x < east) & (nodes.y > south) & (nodes.y < north)] new_edges = edges.loc[(edges['from'].isin(new_nodes.index)) & (edges['to'].isin(new_nodes.index))] new_net_store.put('edges', new_edges) new_net_store.put('nodes', new_nodes) new_net_store.close()
def multimodal_from_bbox(bbox, gtfs_dir=None, save_osm=None, save_gtfs=None, excluded_feeds=None, transit_net_kwargs=None, headways=False, additional_feeds=None): """Generate a combined walk/transit pandana Network from a bounding box of latitudes and longitudes Parameters ---------- bbox : tuple A bounding box formatted as (lng_max, lat_min, lng_min, lat_max). e.g. For a geodataframe stored in epsg 4326, this can be obtained with geodataframe.total_bounds gtfs_dir : str, optional path to directory for storing downloaded GTFS data. If None, the current directory will be used save_osm : str, optional Path to store the intermediate OSM Network as an h5 file save_gtfs : str, optional Path to store the intermediate GTFS Network as an h5 file excluded_feeds : list, optional list of feed names to exclude from the GTFS downloaded transit_net_kwargs : dict, optional additional keyword arguments to be passed to the urbanaccess GTFS network instantiator. defaults to {'day':"monday", 'timerange':["07:00:00", "10:00:00"]} headways : bool, optional Whether to include headway calculations for the combined network additional_feeds : dict, optional Dictionary of additional feed locations in case they are not hosted on transitland. Should be specified as {transitagencyname: url} Returns ------- pandana.Network a multimodal (walk/transit) Network object built from OSM and GTFS data that lie within the bounding box """ try: import osmnet import pandana as pdna import urbanaccess as ua except ImportError: raise ImportError( "You must have osmnet, pandana, and urbanaccess installed to use this function" ) assert bbox is not None, "You must provide a bounding box to collect network data" if not gtfs_dir: gtfs_dir = "./data/" if not transit_net_kwargs: transit_net_kwargs = dict(day="monday", timerange=["07:00:00", "10:00:00"], calendar_dates_lookup=None) # Get gtfs data feeds = feeds_from_bbox(bbox) if excluded_feeds: # remove problematic feeds if necessary for feed in list(feeds.keys()): if feed in excluded_feeds: feeds.pop(feed) if len(ua.gtfsfeeds.feeds.to_dict()["gtfs_feeds"]) > 0: ua.gtfsfeeds.feeds.remove_feed( remove_all=True ) # feeds object is global so reset it if there's anything leftover ua.gtfsfeeds.feeds.add_feed(feeds) if additional_feeds: ua.gtfsfeeds.feeds.add_feed(additional_feeds) ua.gtfsfeeds.download(data_folder=gtfs_dir) loaded_feeds = ua.gtfs.load.gtfsfeed_to_df(f"{gtfs_dir}/gtfsfeed_text/", bbox=bbox, remove_stops_outsidebbox=True) if save_gtfs: ua_to_h5(loaded_feeds, f"{gtfs_dir}/{save_gtfs}") # Get OSM data nodes, edges = osmnet.network_from_bbox(bbox=bbox) osm_network = pdna.Network(nodes["x"], nodes["y"], edges["from"], edges["to"], edges[["distance"]]) if save_osm: osm_network.save_hdf5(save_osm) # Create the transit network ua.create_transit_net(gtfsfeeds_dfs=loaded_feeds, **transit_net_kwargs) osm_network.nodes_df['id'] = osm_network.nodes_df.index ua.create_osm_net( osm_edges=osm_network.edges_df, osm_nodes=osm_network.nodes_df, travel_speed_mph=3, ) if headways: ua.gtfs.headways.headways( gtfsfeeds_df=loaded_feeds, headway_timerange=transit_net_kwargs["timerange"]) ua.network.integrate_network( urbanaccess_network=ua.ua_network, headways=True, urbanaccess_gtfsfeeds_df=loaded_feeds, headway_statistic="mean", ) else: ua.integrate_network(urbanaccess_network=ua.ua_network, headways=False) combined_net = pdna.Network( ua.ua_network.net_nodes["x"], ua.ua_network.net_nodes["y"], ua.ua_network.net_edges["from_int"], ua.ua_network.net_edges["to_int"], ua.ua_network.net_edges[["weight"]], ) return combined_net
def get_osm_network(geodataframe, maxdist=5000, quiet=True, output_crs=None, **kwargs): """Download a street network from OSM. Parameters ---------- geodataframe : geopandas.GeoDataFrame geopandas.GeoDataFrame of the study area. Coordinate system should be in WGS84 maxdist : int Maximum distance of the network queries you may need (this is used to buffer the network to ensure there's enough to satisfy your largest query, otherwise there may be edge effects. Distance is measured in the units of the geodataframe CRS. If the CRS is geographic, a UTM approximation is used, so the units are meters. quiet: bool If True, diagnostic messages from urbanaccess will be suppressed **kwargs : dict additional kwargs passed through to urbanaccess.ua_network_from_bbox Returns ------- pandana.Network A pandana Network instance for use in accessibility calculations or spatial segregation measures that include a distance decay """ assert geodataframe.crs, "The input geodataframe must have a valid CRS set" if not output_crs: output_crs = geodataframe.crs try: import pandana as pdna from urbanaccess.osm.load import ua_network_from_bbox except ImportError: raise ImportError( "You need pandana and urbanaccess to work with segregation's network module\n" "You can install them with `pip install urbanaccess pandana` " "or `conda install -c udst pandana urbanaccess`" ) gdf = geodataframe.copy() # Need coordinates in 4326 to request from OSM, but need projected for measuring distance if geodataframe.crs.is_geographic: # this is lazy because UTM can be inaccurate in some places on the earth, but generally works fine warn( "The geodataframe passed into the function is stored in a geographic CRS." "Estimating maximum distance threshold using a UTM transformation" ) gdf = gdf.to_crs(gdf.estimate_utm_crs()) gdf = gdf.buffer(maxdist) bounds = gdf.to_crs(epsg=4326).total_bounds else: bounds = gdf.total_bounds nodes, edges = ua_network_from_bbox( bounds[1], bounds[0], bounds[3], bounds[2], **kwargs ) nodes = _reproject_osm_nodes(nodes, 4326, output_crs) network = pdna.Network( nodes["x"], nodes["y"], edges["from"], edges["to"], edges[["distance"]] ) return network
G.add_weighted_edges_from(edges) largest_connected_component = sorted(nx.connected_components(G), key=len, reverse=True)[0] # Clean up to save memory G = None edges = None # Create pandana network. It's much faster for nearest point-of-interest analysis # Filter nodes and edges to just include those in the largest connected componet dfLinksLCC = dfLinks.loc[ (dfLinks['startNode'].isin(largest_connected_component)) & (dfLinks['endNode'].isin(largest_connected_component))] dfNodesLCC = dfNodes.loc[largest_connected_component] net = pdna.Network(dfNodesLCC["x"], dfNodesLCC["y"], dfLinksLCC["startNode"], dfLinksLCC["endNode"], dfLinksLCC[["length"]]) # Get the nearest three Food Banks search_distance = 200000 net.set_pois("Food Banks", search_distance, 3, foodBank.geometry.x, foodBank.geometry.y) dfNear = net.nearest_pois(search_distance, "Food Banks", num_pois=3, include_poi_ids=True) # ---- Ward Computations ---- # Select only the Wards centroids and their nearest foodbanks rather than every road node """ ward_road_nodes = net.get_node_ids(gdfWardsCent.geometry.x, gdfWardsCent.geometry.y) wardComps = dfNear.loc[ward_road_nodes]
def _create_pandana_net(nodes, edges): return pdna.Network(nodes.x, nodes.y, edges['from'], edges['to'], edges.xs('traveltime', axis=1, level=1))