OSM

Last update

March 23, 2024

Preparation

Set the coordinates and buffer distances to your preferred extents.

from cityseer.tools import io

# lng, lat, buffer_dist, plot_buffer = -1.7063649924889566, 52.19277374082795, 1500, 1250  # stratford-upon-avon
lng, lat, buffer_dist, plot_buffer = (
    -0.13039709427587876,
    51.516434828344366,
    6000,
    5000,
)  # london
# lng, lat, buffer_dist, plot_buffer = 18.425702641104582, -33.9204746754594, 3000, 2500  # cape town
poly_wgs, _ = io.buffered_point_poly(lng, lat, buffer_dist)
poly_utm, _ = io.buffered_point_poly(lng, lat, buffer_dist, projected=True)
# select extents for plotting
plot_bbox = poly_utm.centroid.buffer(plot_buffer).bounds

Loading data from OSM

For this example, we’ll use a custom OSM query that excludes footways because the continuity analysis needs OSM ways that have street name or route number information.

For this form of analysis, only basic cleaning is recommended:

  • not consolidating nodes because this risks accidentally dropping highway / ref / name attributes
  • not like closeness or betweenness methods, where topological distortions are arguably a bigger issue
query = """
[out:json];
(
    way["highway"]
    ["area"!="yes"]
    ["highway"!~"footway|pedestrian|steps|bus_guideway|escape|raceway|proposed|planned|abandoned|platform|construction"]
    ["service"!~"parking_aisle"]
    ["amenity"!~"charging_station|parking|fuel|motorcycle_parking|parking_entrance|parking_space"]
    ["access"!~"private|customers"]
    ["indoor"!="yes"]
    (poly:"{geom_osm}");
);
out body;
>;
out qt;
"""
G_osm = io.osm_graph_from_poly(
    poly_wgs, custom_request=query, simplify=True, iron_edges=False
)
INFO:cityseer.tools.io:Converting networkX graph from EPSG code 4326 to EPSG code 32630.
INFO:cityseer.tools.io:Processing node x, y coordinates.
100%|██████████| 141799/141799 [00:00<00:00, 307109.58it/s]
INFO:cityseer.tools.io:Processing edge geom coordinates, if present.
100%|██████████| 152077/152077 [00:00<00:00, 672708.16it/s]
INFO:cityseer.tools.graphs:Generating interpolated edge geometries.
100%|██████████| 152077/152077 [00:03<00:00, 47701.10it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 141799/141799 [00:23<00:00, 6088.50it/s]
INFO:cityseer.tools.graphs:Removing dangling nodes.
100%|██████████| 36411/36411 [00:00<00:00, 226888.05it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 34012/34012 [00:00<00:00, 56288.52it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 32172/32172 [00:00<00:00, 87738.65it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 32172/32172 [00:08<00:00, 3622.80it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 26632/26632 [00:00<00:00, 184209.95it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 36520/36520 [00:03<00:00, 10934.54it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 34380/34380 [00:00<00:00, 529406.13it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 26335/26335 [00:09<00:00, 2859.41it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 35324/35324 [00:00<00:00, 149166.62it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 27279/27279 [00:00<00:00, 97660.35it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 27279/27279 [00:04<00:00, 6087.78it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 24920/24920 [00:00<00:00, 58761.52it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 32513/32513 [00:01<00:00, 20982.18it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 23585/23585 [00:00<00:00, 141766.68it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 30677/30677 [00:00<00:00, 700146.18it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 23133/23133 [00:05<00:00, 4191.83it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 30750/30750 [00:00<00:00, 43371.19it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 23206/23206 [00:00<00:00, 119011.86it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 23206/23206 [00:01<00:00, 12658.30it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 23083/23083 [00:00<00:00, 621311.58it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 30654/30654 [00:00<00:00, 131804.30it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 23066/23066 [00:00<00:00, 503339.17it/s]

Observe continuity metrics

We can now run the contunity metrics

import matplotlib.pyplot as plt

from cityseer.tools import io, plot
from cityseer.metrics import observe

# methods can be "names", "routes", "highways"

print("Continuity by street names")
G_cont, NamesContReport = observe.street_continuity(G_osm, method="names")
NamesContReport.report_by_count(n_items=5)
NamesContReport.report_by_length(n_items=5)

print("Continuity by route numbers")
G_cont, RoutesContReport = observe.street_continuity(G_cont, method="routes")
RoutesContReport.report_by_count(n_items=5)
RoutesContReport.report_by_length(n_items=5)

print("Continuity by highway types")
G_cont, HwyContReport = observe.street_continuity(G_cont, method="highways")
HwyContReport.report_by_count(n_items=5)
HwyContReport.report_by_length(n_items=5)

print("Continuity by overlapping routes and names types")
G_cont, HybridContReport = observe.hybrid_street_continuity(G_cont)
HybridContReport.report_by_count(n_items=5)
HybridContReport.report_by_length(n_items=5)
INFO:cityseer.metrics.observe:Calculating metrics for names.
100%|██████████| 30519/30519 [00:00<00:00, 44680.14it/s]
INFO:cityseer.metrics.observe:Reporting top 5 continuity observations by street counts.
INFO:cityseer.metrics.observe:Count: 52 - harrow road
INFO:cityseer.metrics.observe:Count: 50 - holloway road
INFO:cityseer.metrics.observe:Count: 48 - king's road
INFO:cityseer.metrics.observe:Count: 46 - wandsworth road
INFO:cityseer.metrics.observe:Count: 44 - battersea park road
INFO:cityseer.metrics.observe:Reporting top 5 continuity observations by street lengths.
INFO:cityseer.metrics.observe:Length: 6.0km - westway
INFO:cityseer.metrics.observe:Length: 5.02km - regent's canal towpath
INFO:cityseer.metrics.observe:Length: 4.43km - outer circle
INFO:cityseer.metrics.observe:Length: 3.95km - harrow road
INFO:cityseer.metrics.observe:Length: 3.35km - finchley road
INFO:cityseer.metrics.observe:Calculating metrics for routes.
100%|██████████| 30519/30519 [00:00<00:00, 172552.22it/s]
INFO:cityseer.metrics.observe:Reporting top 5 continuity observations by street counts.
INFO:cityseer.metrics.observe:Count: 174 - a4
INFO:cityseer.metrics.observe:Count: 151 - a3220
INFO:cityseer.metrics.observe:Count: 144 - a501
INFO:cityseer.metrics.observe:Count: 107 - a400
INFO:cityseer.metrics.observe:Count: 99 - a202
INFO:cityseer.metrics.observe:Reporting top 5 continuity observations by street lengths.
INFO:cityseer.metrics.observe:Length: 12.6km - a4
INFO:cityseer.metrics.observe:Length: 10.95km - a3220
INFO:cityseer.metrics.observe:Length: 9.0km - a501
INFO:cityseer.metrics.observe:Length: 7.94km - a400
INFO:cityseer.metrics.observe:Length: 7.71km - a202
INFO:cityseer.metrics.observe:Calculating metrics for highways.
100%|██████████| 30519/30519 [00:00<00:00, 87953.52it/s]
INFO:cityseer.metrics.observe:Reporting top 5 continuity observations by street counts.
INFO:cityseer.metrics.observe:Count: 13294 - residential
INFO:cityseer.metrics.observe:Count: 6573 - service
INFO:cityseer.metrics.observe:Count: 3200 - unclassified
INFO:cityseer.metrics.observe:Count: 2650 - primary
INFO:cityseer.metrics.observe:Count: 1963 - cycleway
INFO:cityseer.metrics.observe:Reporting top 5 continuity observations by street lengths.
INFO:cityseer.metrics.observe:Length: 1045.4km - residential
INFO:cityseer.metrics.observe:Length: 428.53km - service
INFO:cityseer.metrics.observe:Length: 243.57km - unclassified
INFO:cityseer.metrics.observe:Length: 181.72km - primary
INFO:cityseer.metrics.observe:Length: 167.71km - cycleway
INFO:cityseer.metrics.observe:Calculating metrics for routes.
100%|██████████| 30519/30519 [00:00<00:00, 140316.03it/s]
INFO:cityseer.metrics.observe:Reporting top 5 continuity observations by street counts.
INFO:cityseer.metrics.observe:Count: 304 - a501
INFO:cityseer.metrics.observe:Count: 281 - a202
INFO:cityseer.metrics.observe:Count: 280 - a3220
INFO:cityseer.metrics.observe:Count: 255 - a4
INFO:cityseer.metrics.observe:Count: 170 - a400
INFO:cityseer.metrics.observe:Reporting top 5 continuity observations by street lengths.
INFO:cityseer.metrics.observe:Length: 22.66km - a501
INFO:cityseer.metrics.observe:Length: 21.08km - a3220
INFO:cityseer.metrics.observe:Length: 19.9km - a202
INFO:cityseer.metrics.observe:Length: 19.06km - a4
INFO:cityseer.metrics.observe:Length: 13.35km - a40
Continuity by street names
Continuity by route numbers
Continuity by highway types
Continuity by overlapping routes and names types
for method, shape_exp, descriptor, cmap, inverse, col_by_categ in zip(
    ["names", "routes", "highways", "hybrid"],  #
    [1, 0.75, 0.5, 1],  #
    ["Street names", "Routes", "Road types", "Hybrid routes & names"],  #
    ["plasma", "viridis", "tab10", "tab10"],  #
    [False, False, True, False],  #
    [False, False, True, True],
):
    print(f"Plotting results for method: {method}")
    # plot
    bg_colour = "#111"
    fig, axes = plt.subplots(
        2, 1, dpi=150, figsize=(8, 12), facecolor=bg_colour, constrained_layout=True
    )
    fig.suptitle(
        f"OS Open Roads plotted by {descriptor} continuity",
        fontsize="small",
        ha="center",
    )
    # by count
    plot.plot_nx_edges(
        axes[0],  # type: ignore
        nx_multigraph=G_cont,
        edge_metrics_key=f"{method}_cont_by_count",
        bbox_extents=plot_bbox,
        cmap_key=cmap,
        lw_min=0.5,
        lw_max=2,
        edge_label_key=f"{method}_cont_by_label",
        colour_by_categorical=col_by_categ,
        shape_exp=shape_exp,
        face_colour=bg_colour,
        invert_plot_order=inverse,
    )
    axes[0].set_xlabel(f"{descriptor} by count", fontsize="x-small")  # type: ignore
    # by length
    plot.plot_nx_edges(
        axes[1],  # type: ignore
        nx_multigraph=G_cont,
        edge_metrics_key=f"{method}_cont_by_length",
        bbox_extents=plot_bbox,
        cmap_key=cmap,
        lw_min=0.5,
        lw_max=2,
        edge_label_key=f"{method}_cont_by_label",
        colour_by_categorical=col_by_categ,
        shape_exp=shape_exp,
        face_colour=bg_colour,
        invert_plot_order=inverse,
    )
    axes[1].set_xlabel(f"{descriptor} by length (metres)", fontsize="x-small")  # type: ignore
    plt.show()
INFO:cityseer.tools.plot:Extracting edge geometries
30519it [00:00, 436173.32it/s]
INFO:cityseer.tools.plot:Generating plot
100%|██████████| 30519/30519 [00:03<00:00, 9848.79it/s] 
INFO:cityseer.tools.plot:Extracting edge geometries
30519it [00:00, 515888.89it/s]
INFO:cityseer.tools.plot:Generating plot
100%|██████████| 30519/30519 [00:02<00:00, 11225.09it/s]
INFO:cityseer.tools.plot:Extracting edge geometries
30519it [00:00, 639530.98it/s]
INFO:cityseer.tools.plot:Generating plot
100%|██████████| 30519/30519 [00:02<00:00, 10577.33it/s]
INFO:cityseer.tools.plot:Extracting edge geometries
30519it [00:00, 623780.34it/s]
INFO:cityseer.tools.plot:Generating plot
100%|██████████| 30519/30519 [00:02<00:00, 10582.54it/s]
INFO:cityseer.tools.plot:Extracting edge geometries
30519it [00:00, 583939.51it/s]
INFO:cityseer.tools.plot:Generating plot
100%|██████████| 11/11 [00:02<00:00,  5.28it/s]
INFO:cityseer.tools.plot:Extracting edge geometries
30519it [00:00, 538861.30it/s]
INFO:cityseer.tools.plot:Generating plot
100%|██████████| 11/11 [00:01<00:00,  5.67it/s]
INFO:cityseer.tools.plot:Extracting edge geometries
30519it [00:00, 632900.20it/s]
INFO:cityseer.tools.plot:Generating plot
100%|██████████| 11/11 [00:01<00:00,  6.20it/s]
INFO:cityseer.tools.plot:Extracting edge geometries
30519it [00:00, 686204.52it/s]
INFO:cityseer.tools.plot:Generating plot
100%|██████████| 11/11 [00:01<00:00,  7.26it/s]
Plotting results for method: names
Plotting results for method: routes
Plotting results for method: highways
Plotting results for method: hybrid