def graph_from_shapefile(path, custom_standards=None, name='unnamed', simplify=True): """ Create Networkx Graph from the shapeFile provided. :param path: Path of the shapeFile. :param custom_standards: Dict object used to convert non standard properties attributes to standard example = { "LINK_ID": "osm_id", "DIRONSIGN":"oneway" } json format Key:Value where: Key is the Field Name from the shapeFile. Value is the standard to convert it to. See documentation for more. :param name: Name of the Graph to be created.(default = unnamed) :param simplify: Simplify the graph. (default = True) :return: Networkx MultiDiGraph """ geojson = shp2geojson(path, custom_standards) json_data = geojson2osm_json(geojson) g = ox.create_graph(json_data, name) if simplify is True: g = ox.simplify_graph(g) return g
def graph_from_shapefile(path, in_crs=None, custom_standards=None, name='unnamed', retain_all=False, simplify=True): """ Create Networkx Graph from the shapeFile provided. :param path: Path of the shapeFile. :param in_crs: CRS of the shapeFile. Should be a string in 'EPSG:{number}' format. (Default = 'EPSG:4326') :param custom_standards: Dict object used to convert non standard properties attributes to standard example = { "LINK_ID": "osm_id", "DIRONSIGN":"oneway" } json format Key:Value where: Key is the Field Name from the shapeFile. Value is the standard to convert it to. See documentation for more. :param name: Name of the Graph to be created.(default = unnamed) :param retain_all: if True, return the entire graph even if it is not connected :param simplify: Simplify the graph. (default = True) :return: Networkx MultiDiGraph """ geojson = shp2geojson(path, custom_standards) if in_crs is not None: geojson = coordinate_transform(geojson, in_crs=in_crs) json_data = geojson2osm_json(geojson) g = ox.create_graph(json_data, name, retain_all=retain_all) if simplify is True: g = ox.simplify_graph(g) return g
def getRowJson(self, indexes): if len(indexes) > 0: result = copy.deepcopy(self.json) result["elements"] = copy.deepcopy(self.nodes) for i in indexes: result["elements"] += self.alt[i.row()][2] return ox.create_graph([result], retain_all=True) else: return None
def updateAlt(self): self.alt = [] self.subgraphs = [] try: G = ox.create_graph([self.json], retain_all=True) updatedHeader = frozenset([]) for nodes in nx.weakly_connected_components(G): subgraph = nx.induced_subgraph(G, nodes) self.subgraphs.append(subgraph) altAppend = {} for key in self.allKeys: values = [] edges = subgraph.edges(data=True) for edge in edges: values.append(edge[2].get(key)) altAppend[key] = frozenset(values) if len(altAppend[key]) == 1 and values[0] is not None: updatedHeader |= frozenset([key]) self.alt.append(altAppend) self.headerItems = list(updatedHeader) except ox.errors.EmptyOverpassResponse: pass
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 graph_from_jsons(response_jsons, network_type='all_private', 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. This is a modified routine from osmnx Parameters ---------- response_jsons : list of responses from osmnx 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 """ if clean_periphery and simplify: g_buffered = ox.create_graph(response_jsons, name=name, retain_all=True, network_type=network_type) # simplify the graph topology g = ox.simplify_graph(g_buffered) # count how many street segments in buffered graph emanate from each # intersection in un-buffered graph, to retain true counts for each # intersection, even if some of its neighbors are outside the polygon g.graph['streets_per_node'] = ox.count_streets_per_node( g, nodes=g.nodes()) else: # create the graph from the downloaded data g = ox.create_graph(response_jsons, name=name, retain_all=True, network_type=network_type) # simplify the graph topology as the last step. don't truncate after # simplifying or you may have simplified out to an endpoint beyond the # truncation distance, in which case you will then strip out your entire # edge if simplify: g = ox.simplify_graph(g) 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