def get_box_data(x_data, selection, network_type='all', infrastructure='way["highway"]'): """ Get data for an intersection within a box. If the city data came from a file, return a subset of nodes and paths based on the network type and infrastructure, otherwise download data from OSM online. :param x_data: intersection dictionary :param selection: osmnx data structure obtained from the XML file. :param network_type: string :param infrastructure: string :return: osmnx data structure """ if x_data['from_file'] == 'yes': return copy.deepcopy( get_data_subset(selection, network_type=network_type, infrastructure=infrastructure)) else: try: return ox.osm_net_download(north=x_data['north'], south=x_data['south'], east=x_data['east'], west=x_data['west'], network_type=network_type, infrastructure=infrastructure) except Exception as e: logger.exception(e) time.sleep(5) return [{'elements': []}]
def get_street_structure(city_name): """ Get street structure of a city :param city_name: city name like 'Campbell, California, USA' :return: a tuple of list of paths and a nodes dictionary """ city_boundaries = ox.gdf_from_place(city_name) city_paths_nodes = ox.osm_net_download( city_boundaries['geometry'].unary_union, network_type="drive") nodes_dict = get_nodes_dict(city_paths_nodes) paths = [p for p in city_paths_nodes[0]['elements'] if p['type'] != 'node'] return paths, nodes_dict
def get_city_from_osm(city_name, network_type="drive"): """ Get city data from OSM as an osmnx data structure. :param city_name: city name like 'Campbell, California, USA' :param network_type: string: {'walk', 'bike', 'drive', 'drive_service', 'all', 'all_private', 'none'} :return: city data structure """ city_paths_nodes = None for which_result in range(1, 4): try: city_boundaries = ox.gdf_from_place(city_name, which_result=which_result) city_paths_nodes = ox.osm_net_download( city_boundaries['geometry'].unary_union, network_type=network_type) break except Exception as e: logger.exception(e) return None return city_paths_nodes
def graph_from_polygon(polygon, network_type='drive', simplify=True, retain_all=False, truncate_by_edge=False, name='unnamed', timeout=180, memory=None, max_query_area_size=50 * 1000 * 50 * 1000, clean_periphery=True, infrastructure='way["highway"]'): """ Create a networkx graph from OSM data within the spatial boundaries of the passed-in shapely polygon. Parameters ---------- polygon : shapely Polygon or MultiPolygon the shape to get network data within. coordinates should be in units of latitude-longitude degrees. network_type : string what type of street network to get simplify : bool if true, simplify the graph topology retain_all : bool if True, return the entire graph even if it is not connected truncate_by_edge : bool if True retain node if it's outside bbox but at least one of node's neighbors are within bbox name : string the name of the graph timeout : int the timeout interval for requests and to pass to API memory : int server memory allocation size for the query, in bytes. If none, server will use its default allocation size max_query_area_size : float max size for any part of the geometry, in square degrees: any polygon bigger will get divided up for multiple queries to API clean_periphery : bool if True (and simplify=True), buffer 0.5km to get a graph larger than requested, then simplify, then truncate it to requested spatial extent infrastructure : string download infrastructure of given type (default is streets (ie, 'way["highway"]') but other infrastructures may be selected like power grids (ie, 'way["power"~"line"]')) Returns ------- networkx multidigraph """ # verify that the geometry is valid and is a shapely Polygon/MultiPolygon # before proceeding if not polygon.is_valid: raise ValueError('Shape does not have a valid geometry') if not isinstance(polygon, (Polygon, MultiPolygon)): raise ValueError('Geometry must be a shapely Polygon or MultiPolygon') response_jsons = ox.osm_net_download( polygon=polygon, network_type=network_type, timeout=timeout, memory=memory, max_query_area_size=max_query_area_size, infrastructure=infrastructure) # create the graph from the downloaded data G = ox.create_graph(response_jsons, name=name, retain_all=True, network_type=network_type) return G
def graph_from_bbox(north, south, east, west, network_type='drive_main', retain_all=False, truncate_by_edge=False, timeout=180, memory=None, max_query_area_size=50 * 1000 * 50 * 1000, infrastructure='way["highway"]'): """ Create a networkx graph from OSM data within some bounding box. Parameters ---------- north : float northern latitude of bounding box south : float southern latitude of bounding box east : float eastern longitude of bounding box west : float western longitude of bounding box network_type : string what type of street network to get simplify : bool if true, simplify the graph topology retain_all : bool if True, return the entire graph even if it is not connected truncate_by_edge : bool if True retain node if it's outside bbox but at least one of node's neighbors are within bbox name : string the name of the graph timeout : int the timeout interval for requests and to pass to API memory : int server memory allocation size for the query, in bytes. If none, server will use its default allocation size max_query_area_size : float max size for any part of the geometry, in square degrees: any polygon bigger will get divided up for multiple queries to API clean_periphery : bool if True (and simplify=True), buffer 0.5km to get a graph larger than requested, then simplify, then truncate it to requested spatial extent infrastructure : string download infrastructure of given type (default is streets (ie, 'way["highway"]') but other infrastructures may be selected like power grids (ie, 'way["power"~"line"]')) Returns ------- networkx multidigraph """ # get the network data from OSM response_jsons = ox.osm_net_download( north=north, south=south, east=east, west=west, network_type=network_type, timeout=timeout, memory=memory, max_query_area_size=max_query_area_size, infrastructure=infrastructure) G = ox.create_graph(response_jsons, retain_all=retain_all, network_type=network_type) # with open('json_osm.txt', 'w') as outfile: # json.dump(response_jsons, outfile) return G
def analyze_city(boundary, crs, local_edges): """Analyze correspondence between Local and OSM bikeways throughout a city. Parameters ---------- boundary : :class:`shapely.geometry.Polygon` City boundary projected in WGS 84 crs : epsg coordinate system Local coordinate system in meters (e.g., UTM 10: {'init': 'epsg:26910'}) local_edges : :class:`geopandas.GeoDataFrame` Output from `structure_bikeways_shapefile` Returns ------- :obj:`tuple` * :class:`pandas.DataFrame` Output from `summarize_bikeway_correspondance` * :class:`geopandas.GeoDataFrame` OSM edges with OSM and local bikeway data attached """ # Define OSM tag filter # Importantly, no paths with 'highway':'service' or 'highway':'motorway' tags will be returned tag_filter = ( '["area"!~"yes"]["highway"!~"service|footway|motor|proposed|construction|abandoned|platform|raceway"]' '["bicycle"!~"no"]["access"!~"private"]') # Download the OSM data overpass_jsons = ox.osm_net_download(boundary, custom_filter=tag_filter) overpass_json = sp.merge_overpass_jsons(overpass_jsons) # Define bikeway columns and associated labels bikeway_types = [ 'off_street_path', 'bike_blvd', 'separated_bike_lane', 'bike_lane', 'shoulder', 'sharrow', 'bike_route' ] # Parse Overpass JSON into bikeway types overpass_parsed = sp.parse_osm_tags(overpass_json, bikeway_types, true_value=1, false_value=0, none_value=np.nan) # Specify attributes to include in graph path_tags = (bikeway_types + ['highway']) ox.config(useful_tags_path=path_tags) # Convert json to graph G = ox.create_graph([overpass_parsed]) # Simply graph by removing all nodes that are not intersections or dead ends G = ox.simplify_graph(G, strict=True) # Make graph undirected G = nx.to_undirected(G) # Convert graph to geodataframes osm_edges = ox.graph_to_gdfs(G, nodes=False) # Project to local coordinate system osm_edges = osm_edges.to_crs(crs) # Project city boundary to local coordinate system boundary, _ = ox.project_geometry(boundary, to_crs=crs) # Constrain edges to those intersecting the city boundary polygon osm_edges = sp.gdf_intersecting_polygon(osm_edges, boundary) # Summarize bikeway values stored in lists osm_edges[bikeway_types] = osm_edges[bikeway_types].applymap( lambda x: sp.nan_any(x, 1, np.nan)) # Idenfity largest available highway type def largest_highway(highways): # Specify highway order, # largest (least bikable) to smallest (most bikable) highway_order = [ 'trunk', 'primary', 'secondary', 'tertiary', 'unclassified', 'residential', 'living_street', 'cycleway' ] highways = sp.listify(highways) # Strip '_link' from tags highways = [x[:-5] if x[-5:] == '_link' else x for x in highways] # If list includes one of these tags, return the biggest one ranked_highways = [x for x in highways if x in highway_order] if len(ranked_highways) > 0: ranks = [highway_order.index(x) for x in ranked_highways] return highway_order[min(ranks)] # Otherwise, return 'other' else: return 'other' osm_edges['highway'] = osm_edges['highway'].apply(largest_highway) # Restrict edges to bikeable highway types bikable = [ 'primary', 'secondary', 'tertiary', 'unclassified', 'residential', 'living_street', 'cycleway' ] osm_edges = osm_edges[osm_edges['highway'].isin(bikable)].copy() # Project local edges to local coordinate system local_edges = local_edges.to_crs(crs) # Restrict to local edges intersecting the city boundary local_edges = sp.gdf_intersecting_polygon(local_edges, boundary) # Match local edges to OSM edges analysis_columns = bikeway_types + ['geometry'] # Match dataframes osm_matches = sp.match_lines_by_hausdorff( sp.select_columns( osm_edges, analysis_columns, suffix='_osm').rename(columns={'geometry_osm': 'geometry'}), sp.select_columns( local_edges, analysis_columns, suffix='_local').rename(columns={'geometry_local': 'geometry'}), constrain_target_features=True, distance_tolerance=20, azimuth_tolerance=20, match_fields=True) # Identify local and osm bikeway columns joint_bikeway_cols = [ column for column in osm_matches.columns if any(bikeway in column for bikeway in bikeway_types) ] # Reduce lists to a single single binary value osm_matches[joint_bikeway_cols] = osm_matches[joint_bikeway_cols].applymap( lambda x: sp.nan_any(x, 1, np.nan)) # Drop records without a bikeway in either dataset osm_matches = osm_matches.dropna(how='all', subset=joint_bikeway_cols) # Reclassify NaN values as 0 osm_matches = osm_matches.fillna(0) # Function fo calculate composite bikeways def composite_columns(matches, columns, suffix): # Select relevent columns relevent_columns = sp.select_columns(matches, [x + suffix for x in columns]) # Assess whether there are any values of 1 across each row return relevent_columns.apply(lambda x: sp.nan_any(x, 1, 0), axis=1) # Define exclusive and shared bikeway types exclusive_bikeways = ['bike_lane', 'separated_bike_lane'] shared_bikeways = ['bike_blvd', 'sharrow', 'bike_route'] # Calculate composite of exclusive bikeways osm_matches['exclusive_bikeway_osm'] = composite_columns( osm_matches, exclusive_bikeways, '_osm') osm_matches['exclusive_bikeway_local'] = composite_columns( osm_matches, exclusive_bikeways, '_local') # Calculate composite of shared bikeways osm_matches['shared_bikeway_osm'] = composite_columns( osm_matches, shared_bikeways, '_osm') osm_matches['shared_bikeway_local'] = composite_columns( osm_matches, shared_bikeways, '_local') # Calculate composite of all bikeways osm_matches['any_bikeway_osm'] = composite_columns(osm_matches, bikeway_types, '_osm') osm_matches['any_bikeway_local'] = composite_columns( osm_matches, bikeway_types, '_local') # Calculate the length of each edge osm_matches['length'] = osm_matches['geometry'].apply(lambda x: x.length) # Add labels to bikeway types bikeway_labels = [ 'Off Street Path', 'Bike Boulevard', 'Separated Bike Lane', 'Bike Lane', 'Shoulder', 'Sharrow', 'Bike Route' ] bikeway_labels = OrderedDict(zip(bikeway_types, bikeway_labels)) # Add labels for composite bikeway types bikeway_labels.update({'exclusive_bikeway': 'Exclusive'}) bikeway_labels.update({'shared_bikeway': 'Shared'}) bikeway_labels.update({'any_bikeway': 'Any'}) # Calculate summaries summaries = summarize_bikeway_correspondance(osm_matches, bikeway_labels) return summaries, osm_matches
def routes_from_polygon(polygon, route_type): """ Create a network graph corresponding to a selected route type from OSM data within the spatial boundaries of the passed-in shapely polygon. This function mostly reproduces that is already done in :py:func:`osmnx.graph_from_polygon`, but it preserves the route structure imposed by relations. The graph is always truncated to the polygon and simplified. :param shapely.Polygon polygon: the shpae to get network data within. Coordinates should be in units of latitude-longiute. :param str route_type: what type of route to get. :returns: a dictionary mapping route name to the corresponding multigraph. """ for tag in ("name", "public_transport"): # might be expanded later if tag not in osmnx.settings.useful_tags_node: osmnx.settings.useful_tags_node.append(tag) response_jsons = osmnx.osm_net_download( polygon=polygon, infrastructure='rel["route"]', custom_filter='["route"="{}"]'.format(route_type)) # Collect all the elements from the response. elements = [] for osm_data in response_jsons: elements.extend(osm_data["elements"]) # Sort them into paths, nodes and relations building a global # lookup dictionary. nodes = {} paths = {} relations = {} for element in elements: key = element["id"] if element["type"] == "node": nodes[key] = osmnx.get_node(element) if element["type"] == "way": paths[key] = osmnx.get_path(element) if element["type"] == "relation": relations[key] = element # Build a graph for every relation. routes = {} for _, relation in relations.items(): try: line, route = route_from_relation(relation, nodes, paths) route = osmnx.truncate_graph_polygon(route, polygon, retain_all=True) except Exception: # route might be empty or the line might be outside of # region continue if route.edges: routes[line] = route return routes