Exemple #1
0
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
Exemple #3
0
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
Exemple #7
0
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