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
= io.buffered_point_poly(lng, lat, buffer_dist)
poly_wgs, _ = io.buffered_point_poly(lng, lat, buffer_dist, projected=True)
poly_utm, _ # select extents for plotting
= poly_utm.centroid.buffer(plot_buffer).bounds plot_bbox
OSM
Preparation
Set the coordinates and buffer distances to your preferred extents.
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;
"""
= io.osm_graph_from_poly(
G_osm =query, simplify=True, iron_edges=False
poly_wgs, custom_request )
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")
= observe.street_continuity(G_osm, method="names")
G_cont, NamesContReport =5)
NamesContReport.report_by_count(n_items=5)
NamesContReport.report_by_length(n_items
print("Continuity by route numbers")
= observe.street_continuity(G_cont, method="routes")
G_cont, RoutesContReport =5)
RoutesContReport.report_by_count(n_items=5)
RoutesContReport.report_by_length(n_items
print("Continuity by highway types")
= observe.street_continuity(G_cont, method="highways")
G_cont, HwyContReport =5)
HwyContReport.report_by_count(n_items=5)
HwyContReport.report_by_length(n_items
print("Continuity by overlapping routes and names types")
= observe.hybrid_street_continuity(G_cont)
G_cont, HybridContReport =5)
HybridContReport.report_by_count(n_items=5) HybridContReport.report_by_length(n_items
INFO:cityseer.metrics.observe:Calculating metrics for names.
Continuity by street 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.
Continuity by route numbers
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
Continuity by highway types
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
Continuity by overlapping routes and names types
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
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
= "#111"
bg_colour = plt.subplots(
fig, axes 2, 1, dpi=150, figsize=(8, 12), facecolor=bg_colour, constrained_layout=True
)
fig.suptitle(f"OS Open Roads plotted by {descriptor} continuity",
="small",
fontsize="center",
ha
)# by count
plot.plot_nx_edges(0], # type: ignore
axes[=G_cont,
nx_multigraph=f"{method}_cont_by_count",
edge_metrics_key=plot_bbox,
bbox_extents=cmap,
cmap_key=0.5,
lw_min=2,
lw_max=f"{method}_cont_by_label",
edge_label_key=col_by_categ,
colour_by_categorical=shape_exp,
shape_exp=bg_colour,
face_colour=inverse,
invert_plot_order
)0].set_xlabel(f"{descriptor} by count", fontsize="x-small") # type: ignore
axes[# by length
plot.plot_nx_edges(1], # type: ignore
axes[=G_cont,
nx_multigraph=f"{method}_cont_by_length",
edge_metrics_key=plot_bbox,
bbox_extents=cmap,
cmap_key=0.5,
lw_min=2,
lw_max=f"{method}_cont_by_label",
edge_label_key=col_by_categ,
colour_by_categorical=shape_exp,
shape_exp=bg_colour,
face_colour=inverse,
invert_plot_order
)1].set_xlabel(f"{descriptor} by length (metres)", fontsize="x-small") # type: ignore
axes[ plt.show()
INFO:cityseer.tools.plot:Extracting edge geometries
Plotting results for method: names
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
Plotting results for method: routes
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
Plotting results for method: highways
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
Plotting results for method: hybrid
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]