Note
Shape#
While the majority of momepy
functions require the interaction of more GeoDataFrames
or using spatial graph, there are some which are calculated on single GeoDataFrame
assessing the dimensions or shapes of features. This notebook illustrates how to measure simple shape characters.
[1]:
import momepy
import numpy as np
We will again use osmnx
to get the data for our example and after preprocessing of building layer will generate tessellation.
[2]:
import osmnx as ox
gdf = ox.features_from_place("Kahla, Germany", tags={"building": True})
buildings = ox.projection.project_gdf(gdf).reset_index()
limit = momepy.buffered_limit(buildings)
tessellation = momepy.morphological_tessellation(buildings, clip=limit)
/Users/martin/miniforge3/envs/momepy/lib/python3.11/site-packages/osmnx/features.py:294: FutureWarning: The 'unary_union' attribute is deprecated, use the 'union_all()' method instead.
polygon = gdf_place["geometry"].unary_union
[3]:
ax = tessellation.plot(figsize=(8, 8))
buildings.plot(ax=ax, color="white", alpha=0.5)
ax.set_axis_off()
Building shapes#
Few examples of measuring building shapes. Circular compactness measures the ratio of object area to the area of its smallest circumsribed circle:
[4]:
buildings["circular_com"] = momepy.circular_compactness(buildings)
[5]:
ax = buildings.plot(column="circular_com", legend=True, figsize=(8, 8))
ax.set_axis_off()
While elongation is seen as elongation of its minimum bounding rectangle:
[6]:
buildings["elongation"] = momepy.elongation(buildings)
[7]:
ax = buildings.plot(
column="elongation", legend=True, cmap="Blues_r", figsize=(8, 8)
)
ax.set_axis_off()
And squareness measures mean deviation of all corners from 90 degrees:
[8]:
buildings["squareness"] = momepy.squareness(buildings)
[9]:
ax = buildings.plot(
column="squareness",
legend=True,
scheme="quantiles",
cmap="Purples_r",
figsize=(8, 8),
)
ax.set_axis_off()
For the form factor, we need to know the volume of each building. While we do not have building height data for Kahla, we will generate them randomly and pass a Series
containing volume values to form_factor
.
For the majority of parameters you shall pass np.array
, pd.Series
or any other list-like object.
[10]:
height = np.random.randint(4, 20, size=len(buildings))
buildings["formfactor"] = momepy.form_factor(buildings, height=height)
[11]:
ax = buildings.plot(
column="formfactor",
legend=True,
scheme="quantiles",
k=10,
cmap="PuRd_r",
figsize=(8, 8),
)
ax.set_axis_off()
Cell shapes#
In theory, you can measure most of the 2D characters on all elements, including tessellation or blocks:
[12]:
tessellation["cwa"] = momepy.compactness_weighted_axis(tessellation)
[13]:
ax = tessellation.plot(
column="cwa",
legend=True,
scheme="quantiles",
k=10,
cmap="Greens_r",
figsize=(8, 8),
)
ax.set_axis_off()
Street network shapes#
There are some characters which requires street network as an input. We can again use osmnx
to retrieve it from OSM.
[14]:
streets_graph = ox.graph_from_place("Kahla, Germany", network_type="drive")
streets_graph = ox.projection.project_graph(streets_graph)
/Users/martin/miniforge3/envs/momepy/lib/python3.11/site-packages/osmnx/graph.py:392: FutureWarning: The 'unary_union' attribute is deprecated, use the 'union_all()' method instead.
polygon = gdf_place["geometry"].unary_union
osmnx
returns networkx Graph. While momepy works with graph in some cases, for this one we need GeoDataFrame
. To get it, we can use ox.graph_to_gdfs
.
Note: momepy.nx_to_gdf
might work as well, but OSM network needs to be complete in that case. osmnx
takes care of it.
[15]:
edges = ox.graph_to_gdfs(
streets_graph,
nodes=False,
edges=True,
node_geometry=False,
fill_edge_geometry=True,
)
[16]:
ax = edges.plot(color="pink", figsize=(8, 8))
buildings.plot(ax=ax, color="lightgrey")
ax.set_axis_off()
Now we can calculate linearity of each segment:
[17]:
edges["linearity"] = momepy.linearity(edges)
[18]:
ax = edges.plot(
column="linearity",
legend=True,
cmap="coolwarm_r",
scheme="quantiles",
k=4,
figsize=(8, 8),
)
buildings.plot(ax=ax, color="lightgrey")
ax.set_axis_off()