Note

This page was generated from examples/streetscape.ipynb.
Interactive online version: Binder badge

Example of using the Streetscape class#

The class is designed for morphological streetscape analysis, focusing on generating nd analyzing streetscape measures based on sight points and sightlines. It attempts to capture the space from the pedestrian point of view.

[1]:
import geopandas as gpd
import momepy
import numpy as np
import osmnx as ox
import rioxarray
/Users/martin/dev/pysal/.pixi/envs/default/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

Read all the data. Only streets and buildings are required.

[2]:
streets = gpd.read_file(
    momepy.datasets.get_path("bubenec"), layer="streets"
).to_crs(5514)
buildings = gpd.read_file(
    momepy.datasets.get_path("bubenec"), layer="buildings"
).to_crs(5514)
plots = gpd.read_file(
    momepy.datasets.get_path("bubenec"), layer="plots"
).to_crs(5514)
dtm = rioxarray.open_rasterio(momepy.datasets.get_path("bubenec"), layer="dtm")

Mimic data on building category and height.

[3]:
buildings["category"] = np.random.randint(0, 6, len(buildings))
buildings["height"] = np.random.randint(12, 30, len(buildings))

Initiate the class. This will dricectly compute builk of the sightline indicators based on streets and buildings.

[4]:
sc = momepy.Streetscape(
    streets, buildings, category_col="category", height_col="height"
)
/Users/martin/dev/pysal/.pixi/envs/default/lib/python3.12/site-packages/shapely/set_operations.py:131: RuntimeWarning: invalid value encountered in intersection
  return lib.intersection(a, b, **kwargs)

If you have plots and DTM, you can use two additional methods to compute additional variables.

[ ]:
sc.compute_plots(plots)
sc.compute_slope(dtm)

The resulting data can be extracted either on a street level:

[6]:
street_df = sc.street_level()
street_df.head()
[6]:
N n_l n_r left_os right_os os left_os_std right_os_std os_std left_os_mad ... left_plot_WD_ratio right_plot_WD_ratio plot_WP_ratio left_plot_WP_ratio right_plot_WP_ratio slope_degree slope_percent n_slopes slope_valid geometry
street_index
0 57 28 38 33.607135 38.163914 71.771049 14.794107 12.059253 13.436283 13.873261 ... 1.156827 1.765917 0.170662 0.156054 0.184501 2.809318 0.049073 57 True LINESTRING (-743681.992 -1040957.169, -743809....
1 22 0 19 50.000000 17.497487 67.497487 0.000000 14.015687 9.794670 0.000000 ... 1.440014 1.037320 0.255912 0.284263 0.230261 0.166605 0.002908 22 True LINESTRING (-743916.081 -1041162.952, -743899....
2 43 36 21 13.907200 35.792031 49.699231 16.207022 15.188099 15.613165 11.913767 ... 0.822465 1.635065 0.200568 0.221176 0.181546 1.122029 0.019587 43 True LINESTRING (-743689.806 -1041115.822, -743698....
3 24 0 0 50.000000 48.865531 98.865531 0.000000 4.101247 2.869002 0.000000 ... 1.444666 1.307638 0.156703 0.060853 0.248559 2.735747 0.047794 24 True LINESTRING (-743618.342 -1040934.607, -743621....
4 15 0 0 50.000000 50.000000 100.000000 0.000000 0.000000 0.000000 0.000000 ... 1.023257 0.842462 0.068937 0.050164 0.058499 1.505840 0.026294 15 True LINESTRING (-743701.515 -1040870.813, -743693....

5 rows × 102 columns

It is a GeoDataFrame, so you can directly plot it.

[7]:
ax = street_df.plot("os", figsize=(12, 12), legend=True)
buildings.plot(ax=ax).set_axis_off()
../_images/examples_streetscape_13_0.png

Or for all individual sightline points.

[8]:
point_df = sc.point_level()
point_df.head(10)
[8]:
geometry left_os_count left_os left_sb_count left_sb left_h left_hw left_bc right_os_count right_os ... right_plot_seq_sb_depth os_count os sb_count sb h hw bc plot_seq_sb plot_seq_sb_depth
street_index
0 POINT (-743682.367 -1040957.5) 1 50.000000 0 NaN NaN NaN 0.000000 1 50.000000 ... 19.240118 2 50.000000 0 NaN NaN NaN 0.000000 14.568200 19.240118
0 POINT (-743684.627 -1040959.485) 1 50.000000 0 NaN NaN NaN 0.000000 1 50.000000 ... 18.011884 2 50.000000 0 NaN NaN NaN 0.000000 10.090066 18.011884
0 POINT (-743686.886 -1040961.471) 1 50.000000 0 NaN NaN NaN 0.000000 1 27.593446 ... 15.658901 2 38.796723 1 27.593446 18.0 0.652329 2.491801 10.100149 15.658901
0 POINT (-743689.145 -1040963.457) 1 50.000000 0 NaN NaN NaN 0.000000 1 26.229691 ... 14.184017 2 38.114846 1 26.229691 18.0 0.686245 5.604209 9.996735 27.216748
0 POINT (-743691.405 -1040965.443) 1 50.000000 0 NaN NaN NaN 0.000000 1 25.412462 ... 23.317758 2 37.706231 1 25.412462 18.0 0.708314 6.825341 9.996961 32.570991
0 POINT (-743693.664 -1040967.428) 1 23.978541 0 NaN NaN NaN 6.557431 1 25.198986 ... 21.500873 2 24.588764 1 25.198986 18.0 0.714314 11.811294 9.997186 29.171224
0 POINT (-743695.923 -1040969.414) 1 14.720375 0 NaN NaN NaN 15.763951 1 26.764536 ... 20.928554 2 20.742455 1 26.764536 18.0 0.672532 16.722367 9.997412 25.070210
0 POINT (-743698.182 -1040971.4) 1 14.705134 0 NaN NaN NaN 16.541363 1 28.184169 ... 18.024346 2 21.444652 1 28.184169 18.0 0.638656 15.669026 9.997637 21.235336
0 POINT (-743700.442 -1040973.386) 1 14.689893 0 NaN NaN NaN 19.812513 1 29.961410 ... 19.832787 2 22.325652 1 29.961410 18.0 0.600773 13.593521 9.997863 24.073478
0 POINT (-743702.701 -1040975.371) 1 14.674652 0 NaN NaN NaN 19.807911 1 31.357056 ... 20.430319 2 23.015854 1 31.357056 18.0 0.574033 13.355549 9.998088 25.161555

10 rows × 30 columns

Again, it is a GeoDataFrame, this time with point geometry.

[9]:
ax = point_df.plot("os", figsize=(12, 12), legend=True, markersize=5)
buildings.plot(ax=ax).set_axis_off()
../_images/examples_streetscape_17_0.png

Using OpenStreetMap data#

You can also use any other data. If you fetch buildings and streets from OSM, you can measure a subset of characters depending on those.

[10]:
gdf = ox.features_from_place("Kahla, Germany", tags={"building": True})
buildings = ox.projection.project_gdf(gdf).reset_index()

streets_graph = ox.graph_from_place("Kahla, Germany", network_type="drive")
streets_graph = ox.projection.project_graph(streets_graph)
edges = ox.graph_to_gdfs(
    streets_graph,
    nodes=False,
    edges=True,
    node_geometry=False,
    fill_edge_geometry=True,
).reset_index(drop=True)
/Users/martin/dev/pysal/.pixi/envs/default/lib/python3.12/site-packages/osmnx/graph.py:392: DeprecationWarning: The 'unary_union' attribute is deprecated, use the 'union_all()' method instead.
  polygon = gdf_place["geometry"].unary_union

Initiate the class. This time without additional arguments as OSM does not contain category or reliable height.

[11]:
sc = momepy.Streetscape(edges, buildings)
/Users/martin/dev/pysal/momepy/momepy/streetscape.py:286: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
  prof_st = (seg_mid.x + float(vec_anti[0]), seg_mid.y + float(vec_anti[1]))
/Users/martin/dev/pysal/momepy/momepy/streetscape.py:288: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
  seg_mid.x + float(vec_clock[0]),
/Users/martin/dev/pysal/momepy/momepy/streetscape.py:289: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
  seg_mid.y + float(vec_clock[1]),
/Users/martin/dev/pysal/.pixi/envs/default/lib/python3.12/site-packages/shapely/set_operations.py:131: RuntimeWarning: invalid value encountered in intersection
  return lib.intersection(a, b, **kwargs)

The measurememnt happens on class initialisation. Now you can simply extract it either on a street level:

[12]:
street_df = sc.street_level()

ax = street_df.plot("os", figsize=(12, 12), legend=True)
buildings.plot(ax=ax, color="lightgray").set_axis_off()
../_images/examples_streetscape_23_0.png

Or on a point level, as before.

[13]:
point_df = sc.point_level()

ax = point_df.plot("os", figsize=(12, 12), legend=True, markersize=0.1)
buildings.plot(ax=ax, color="lightgray").set_axis_off()
../_images/examples_streetscape_25_0.png