Beispiel #1
0
def test_synthetic_network_with_custom_stops():
    # Load in the GeoJSON as a JSON and convert to a dictionary
    geojson_path = fixture('synthetic_east_bay.geojson')
    with open(geojson_path, 'r') as gjf:
        reference_geojson = json.load(gjf)

    # Add in specific, custom stops under new properties key
    custom_stops = [[-122.29225158691406, 37.80876678753658],
                    [-122.28886127471924, 37.82341261847038],
                    [-122.2701072692871, 37.83005652796547]]
    reference_geojson['features'][0]['properties']['stops'] = custom_stops

    G1 = load_synthetic_network_as_graph(reference_geojson)

    # Sanity check the outputs against the custom stops input
    assert len(list(G1.nodes())) == (len(custom_stops) + 2)
    assert len(list(G1.edges())) == (len(custom_stops) + 1)

    # Go back to the GeoJSON and set optional bidirectional flag
    reference_geojson['features'][0]['properties']['bidirectional'] = True

    G2 = load_synthetic_network_as_graph(reference_geojson)

    # We re-use the same stop nodes for both directions
    nodes = list(G2.nodes())
    assert len(nodes) == (len(custom_stops) + 2)

    # Double the number of edges as before
    edges = list(G2.edges())
    assert len(edges) == (len(custom_stops) + 1) * 2

    # But now, by asking for a bidirectional graph, we can assert strong
    assert nx.is_strongly_connected(G2)
Beispiel #2
0
def test_conversion_to_graph_tool():
    # To be quick, load in the synthetic graph geojson
    geojson_path = fixture('synthetic_san_bruno.geojson')
    with open(geojson_path, 'r') as gjf:
        reference_geojson = json.load(gjf)

    # Then load it onto the graph, as well
    G = load_synthetic_network_as_graph(reference_geojson)

    # For now, just run the operation and ensure it completes as intended
    gtG = nx_to_gt(G)

    # Make sure that the result is indeed a graph-tool Graph class object
    gt = _import_graph_tool()
    assert isinstance(gtG, gt.Graph)

    # Also make sure that the attributes for all parameters have been preserved
    assert set(gtG.vp.keys()) == set(
        ('boarding_cost', 'modes', 'id', 'x', 'y'))
    assert set(gtG.gp.keys()) == set(('crs', 'name'))
    assert set(gtG.ep.keys()) == set(('length', 'mode'))

    # Make sure the edge count is the same as the NetworkX graph
    nx_edges_len = len([x for x in G.edges()])
    gt_edges_len = len([x for x in gtG.ep['length']])
    assert nx_edges_len == gt_edges_len

    # And same for the vertices count
    nx_nodes_len = len([x for x in G.nodes()])
    gt_nodes_len = len([x for x in gtG.vp['id']])
    assert nx_nodes_len == gt_nodes_len
Beispiel #3
0
def test_synthetic_network():
    # Load in the GeoJSON as a JSON and convert to a dictionary
    geojson_path = fixture('synthetic_east_bay.geojson')
    with open(geojson_path, 'r') as gjf:
        reference_geojson = json.load(gjf)

    G1 = load_synthetic_network_as_graph(reference_geojson)

    # This fixture gets broken into 15 chunks, so 15 + 1 = 16
    nodes = list(G1.nodes())
    assert len(nodes) == 16

    # And since it is one-directional, it gets the same edges as chunks
    edges = list(G1.edges())
    assert len(edges) == 15

    # Since this is a one-way graph, with no other context, the
    # graph will be weakly connected
    assert nx.is_strongly_connected(G1) is False

    # Go back to the GeoJSON and set optional bidirectional flag
    for i in range(len(reference_geojson['features'])):
        reference_geojson['features'][i]['properties']['bidirectional'] = True

    G2 = load_synthetic_network_as_graph(reference_geojson)

    # We re-use the same stop nodes for both directions
    nodes = list(G2.nodes())
    assert len(nodes) == 16

    # Double the number of edges as before
    edges = list(G2.edges())
    assert len(edges) == 15 * 2

    # But now, by asking for a bidirectional graph, we can assert strong
    assert nx.is_strongly_connected(G2)
Beispiel #4
0
def test_feed_to_graph_path():
    path_1 = fixture('caltrain-2017-07-24.zip')
    feed_1 = get_representative_feed(path_1)

    start = 7 * 60 * 60
    end = 10 * 60 * 60

    G = load_feed_as_graph(feed_1, start, end, 'foo')

    # We should assume all routes do not have segments that exceed some
    # given length (measured in seconds)
    max_reasonable_segment_length = 60 * 60
    _check_unreasonable_lengths(G, max_reasonable_segment_length)

    # Sanity check that the number of nodes and edges go up
    orig_node_len = len(G.nodes())
    orig_edge_len = len(G.edges())
    orig_node_list = list(G.nodes())

    path_2 = fixture('samtrans-2017-11-28.zip')
    feed_2 = get_representative_feed(path_2)
    G = load_feed_as_graph(feed_2, start, end, 'bar', G)

    assert isinstance(G, nx.MultiDiGraph)
    _check_unreasonable_lengths(G, max_reasonable_segment_length)

    # Part 2 of sanity check that the number of nodes and edges go up
    node_len_2 = len(G.nodes())
    edge_len_2 = len(G.edges())
    assert node_len_2 > orig_node_len
    assert edge_len_2 > orig_edge_len

    connector_edge_count = 0
    for from_node, to_node, edge in G.edges(data=True):
        # Make sure that a length measure has been calculated for each
        # edge in the resulting graph, also sanity check that all are
        # positive values
        assert 'length' in edge.keys()
        assert isinstance(edge['length'], float)
        assert edge['length'] >= 0

        # Also, we should also make sure that edges were also created that
        # connect the two feeds
        from_orig_a = from_node in orig_node_list
        from_orig_b = to_node in orig_node_list
        one_valid_fr = from_orig_a and (not from_orig_b)
        one_valid_to = (not from_orig_a) and from_orig_b
        if one_valid_fr or one_valid_to:
            connector_edge_count += 1

    # We know that there should be 9 new edges that are created to connect
    # the two GTFS feeds in the joint graph
    assert connector_edge_count == 9

    # Now reload in the synthetic graph geojson
    geojson_path = fixture('synthetic_san_bruno.geojson')
    with open(geojson_path, 'r') as gjf:
        reference_geojson = json.load(gjf)

    # Then load it onto the graph, as well
    G = load_synthetic_network_as_graph(reference_geojson, existing_graph=G)

    # And make sure it connected correctly
    node_len_3 = len(G.nodes())
    edge_len_3 = len(G.edges())
    assert node_len_3 - node_len_2 == 74
    assert edge_len_3 - edge_len_2 == 80
Beispiel #5
0
def test_synthetic_stop_assignment_adjustment():
    target_stop_dist_override = 50  # meters

    # First load original San Bruno line
    geojson_path_1 = fixture('synthetic_san_bruno.geojson')
    with open(geojson_path_1, 'r') as gjf:
        reference_geojson_sb1 = json.load(gjf)

    # We want there to be a lot of stops along
    # both of the routes
    (reference_geojson_sb1['features'][0]['properties']
     ['stop_distance_distribution']) = target_stop_dist_override

    G1 = load_synthetic_network_as_graph(reference_geojson_sb1)
    G1_copy = G1.copy()

    # Now load in the extension further down to Burlingame
    geojson_path_2 = fixture('synthetic_san_bruno_addition.geojson')
    with open(geojson_path_2, 'r') as gjf:
        reference_geojson_sb2 = json.load(gjf)

    # Also override this stop distance distribution value
    (reference_geojson_sb2['features'][0]['properties']
     ['stop_distance_distribution']) = target_stop_dist_override

    G2 = load_synthetic_network_as_graph(reference_geojson_sb2,
                                         existing_graph=G1_copy)
    assert len(G1.nodes()) == 293
    assert len(G2.nodes()) == 495

    # Now remove nodes that were in the original graph
    for i in G1.nodes():
        G2.remove_node(i)

    # The new graph should now be just (495 - 293) nodes
    assert len(G2.nodes()) == 495 - 293

    # We will use this as a reference distance limit for checking
    # nearest points to ensure there are some adjusted stops matches
    dist_limit = target_stop_dist_override * 0.5

    # Then reassess matches
    match_count = 0
    for i2, n2 in G2.nodes(data=True):
        found_match = False
        for i1, n1 in G1.nodes(data=True):

            # First check if exact match
            x_match = n1['x'] == n2['x']
            y_match = n1['y'] == n2['y']
            if x_match and y_match:
                found_match = True
                continue

            # Then check if near enough
            gc_dist = great_circle_vec(n1['x'], n1['y'], n2['x'], n2['y'])
            if gc_dist <= dist_limit:
                found_match = True

            # Stop looping once we find at least one match
            if found_match:
                break

        # If one constraint satisfied, add to tally
        if found_match:
            match_count += 1

    # This value will be high because the stop distance for the addition is
    # set at 50 meters, which means that we expect there to be a lot of
    # stops along the overlapping segment
    assert match_count == 8