Examples

This page includes examples of usage of the Wayfarer library.

Named Tuples

Wayfarer includes an Edge class that can be used to access values using properties.

import networkx
from wayfarer import Edge, to_edge
from wayfarer import functions

net = networkx.MultiGraph()
edge = Edge(start_node=0, end_node=1, key=1, attributes={})

# use the helper function to add the Edge named tuple to the network
functions.add_edge(net, *edge)

# use some networkx functions to get edges back
node_ids = [1]
edges = net.edges(nbunch=node_ids, data=True, keys=True)

# now use the to_edge function to convert back to a named tuple
edges = [to_edge(e) for e in edges]
edge = edges[0]

# now we can access the properties as below
print(edge.key)
print(edge.start_node)
print(edge.end_node)
print(edge.attributes)

Node to Node Routing

from wayfarer import loader, routing
from shapely.geometry import mapping
from shapely.ops import transform
import json
import pyproj

net = loader.load_network_from_file("./data/riga.pickle")
path = routing.solve_shortest_path(net, start_node=1390592196, end_node=1812435081)

features = []

transformer = pyproj.Transformer.from_crs(
    "EPSG:3857", "EPSG:4326", always_xy=True
).transform

for edge in path:
    # convert to EPSG:4326
    geom = transform(transformer, edge.attributes["geometry"])
    feature = {
        "type": "Feature",
        # convert the Shapely geometry to a GeoJSON geometry
        "geometry": mapping(geom),
        "properties": {"id": edge.key},
    }
    features.append(feature)

geojson_obj = {"type": "FeatureCollection", "features": features}

# save to a geojson file
with open("./data/riga.geojson", "w") as f:
    json.dump(geojson_obj, f, indent=4)

print("Done!")

Node to Node Routing

from wayfarer import loader, routing
from shapely.geometry import mapping
from shapely.ops import transform
import json
import pyproj

net = loader.load_network_from_file("./data/riga.pickle")
path = routing.solve_shortest_path(net, start_node=1390592196, end_node=1812435081)

features = []

transformer = pyproj.Transformer.from_crs(
    "EPSG:3857", "EPSG:4326", always_xy=True
).transform

for edge in path:
    # convert to EPSG:4326
    geom = transform(transformer, edge.attributes["geometry"])
    feature = {
        "type": "Feature",
        # convert the Shapely geometry to a GeoJSON geometry
        "geometry": mapping(geom),
        "properties": {"id": edge.key},
    }
    features.append(feature)

geojson_obj = {"type": "FeatureCollection", "features": features}

# save to a geojson file
with open("./data/riga.geojson", "w") as f:
    json.dump(geojson_obj, f, indent=4)

print("Done!")

Loading a network using GeoPandas

"""
python ./docs/examples/create_network_geopandas.py
"""

import geopandas as gpd
from wayfarer import loader
import os


def load_network(input_lines, output_network, overwrite=False):

    lines = gpd.read_file(input_lines, layer="edges")
    # add a unique field
    lines["oid"] = range(1, len(lines) + 1)
    lines.drop(columns="key", inplace=True)
    print(lines.head())

    if not os.path.exists(output_network) or overwrite is True:
        net = loader.load_network_from_records(
            lines.to_dict("records"),
            key_field="oid",
            length_field="length",
            from_field="from",
            to_field="to",
        )
        loader.save_network_to_file(net, output_network)
    return loader.load_network_from_file(output_network)


if __name__ == "__main__":
    # works with any GDAL supported dataset
    input_lines = "./data/dublin.gpkg"
    output_network = "./data/net.pickle"
    net = load_network(input_lines, output_network, overwrite=False)
    print("Done!")

Loading a network from OSM

import osmnx as ox
from wayfarer import loader
from osmnx import convert, projection


def download_osm():
    """
    Download roads from OSM centered around Riga, Latvia
    This only needs to be run once.
    """

    location_point = (56.94105, 24.09682)
    # get roads within a distance of 500m from this point
    bbox = ox.utils_geo.bbox_from_point(location_point, dist=500)

    G = ox.graph_from_bbox(bbox=bbox)

    # save as a graph
    ox.save_graphml(G, filepath="./data/riga.graphml")

    # also save the data as a GeoPackage for review (not required for routing)
    ox.save_graph_geopackage(G, filepath="./data/riga.gpkg")


def create_wayfarer_graph():

    # load the OSMnx graph from the previously saved file
    G = ox.load_graphml("./data/riga.graphml")

    # get data frames from the graph
    _, gdf_edges = convert.graph_to_gdfs(convert.to_undirected(G))

    # project to Web Mercator from EPSG:4326 so we can work with planar distance calculations
    gdf_edges = projection.project_gdf(gdf_edges, to_crs="EPSG:3857")

    # get a dictionary of all edges in the network
    d = gdf_edges.to_dict("records")

    # loop through the edges and add a unique key
    recs = []

    for fid, props in enumerate(d):
        # we can't use osmid as the key as sometimes it is not always unique and sometimes a list
        # instead use the enumerator which will match the FID in the GeoPackage
        props.update({"key": fid})
        # we reprojected the geometry to Web Mercator, so we also need to update the length field
        props["length"] = props["geometry"].length
        recs.append(props)

    # create the wayfarer network
    net = loader.load_network_from_records(
        recs,
        key_field="key",
        length_field="length",
        from_field="from",
        to_field="to",
    )

    # save the network
    loader.save_network_to_file(net, "./data/riga.pickle")


if __name__ == "__main__":
    download_osm()
    create_wayfarer_graph()
    print("Done!")

Snapping Points to a Network

from shapely.geometry import shape, Point
from wayfarer import loader, to_edge, linearref


def get_closest_edge(net, pt):

    closest_line = None
    min_dist = float("inf")

    for edge in net.edges(data=True, keys=True):
        edge = to_edge(edge)
        geom = shape(edge.attributes["geometry"])
        dist = pt.distance(geom)
        if dist < min_dist:
            min_dist = dist
            closest_line = geom
            fid = edge.key

    # find snapped point on the line
    snap_point, _ = linearref.get_nearest_vertex(pt, closest_line)

    # get the measure along the line
    distance_along = linearref.get_measure_on_line(closest_line, snap_point)
    return (fid, distance_along)


net = loader.load_network_from_file("./data/riga.pickle")

start_pt = Point(2682555, 7748329)
end_pt = Point(2682585, 7747272)

print(get_closest_edge(net, start_pt))
# (281, 55.395036218377086)

print(get_closest_edge(net, end_pt))
# (1103, 7.734503415793048)