Skip to content

Commit

Permalink
feat: spacematrix docs and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vasilstar97 committed Jul 26, 2024
1 parent 3993573 commit 827580c
Show file tree
Hide file tree
Showing 6 changed files with 326 additions and 14 deletions.
2 changes: 1 addition & 1 deletion blocksnet/method/centrality/population_centrality.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def calculate(self, connectivity_radius: float = 1000) -> gpd.GeoDataFrame:

# connect graph and blocks
nodes = gpd.GeoDataFrame(
geometry=[Point(position["pos"]) for _, position in connectivity_graph.nodes(data=True)], crs=32636
geometry=[Point(position["pos"]) for _, position in connectivity_graph.nodes(data=True)], crs=blocks.crs
)
nodes[POPULATION_CENTRALITY_COLUMN] = centrality
blocks_out = gpd.sjoin(blocks, nodes, how="left", predicate="intersects")
Expand Down
105 changes: 92 additions & 13 deletions blocksnet/method/spacematrix/spacematrix.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,73 @@
import pandas as pd
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from ..base_method import BaseMethod

SM_MORPHOTYPE_COLUMN = "spacematrix_morphotype"
SM_CLUSTER_COLUMN = "spacematrix_cluster"
KB_MORPHOTYPE_COLUMN = "strelka_morphotype"


class Spacematrix(BaseMethod):
"""
Provides methods for calculating and visualizing urban block morphotypes based on the Spacematrix model.
Attributes
----------
number_of_clusters : int
Number of clusters for KMeans clustering.
random_state : int
Random state for reproducibility of clustering results.
Methods
-------
plot(gdf, linewidth=0.1, figsize=(10, 10))
Plots Spacematrix morphotypes on a map.
calculate() -> gpd.GeoDataFrame
Calculates Spacematrix and Strelka morphotypes for urban blocks.
"""

number_of_clusters: int = 11
random_state: int = 10

@staticmethod
def plot(gdf, figsize=(20, 20)):
gdf.plot(column=f"spacematrix_morphotype", legend=True, figsize=figsize).set_axis_off()
def plot(gdf, linewidth: float = 0.1, figsize: tuple[int, int] = (10, 10)):
"""
Plots Spacematrix morphotypes for blocks on a map.
Parameters
----------
gdf : geopandas.GeoDataFrame
GeoDataFrame containing block geometries and Spacematrix morphotype data.
linewidth : float, optional
Line width for plotting the geometries, by default 0.1.
figsize : tuple of int, optional
Size of the figure to plot, by default (10, 10).
Returns
-------
None
"""
gdf.plot(column=SM_MORPHOTYPE_COLUMN, legend=True, linewidth=linewidth, figsize=figsize).set_axis_off()

@staticmethod
def _get_strelka_morphotypes(blocks):
def _get_strelka_morphotypes(blocks) -> gpd.GeoDataFrame:
"""
Calculates Strelka morphotypes for the given blocks.
Parameters
----------
blocks : geopandas.GeoDataFrame
GeoDataFrame containing block geometries and attributes.
Returns
-------
geopandas.GeoDataFrame
GeoDataFrame with Strelka morphotypes added.
"""
blocks = blocks.copy()
storeys = [blocks["l"].between(0, 3), blocks["l"].between(4, 8), (blocks["l"] >= 9)]
labels = ["Малоэтажная застройка", "Среднеэтажная застройка", "Многоэтажная застройка"]
Expand Down Expand Up @@ -57,7 +104,20 @@ def _get_strelka_morphotypes(blocks):
return blocks

@staticmethod
def _name_spacematrix_morphotypes(cluster):
def _name_spacematrix_morphotypes(cluster) -> str:
"""
Assigns names to Spacematrix morphotypes based on cluster data.
Parameters
----------
cluster : pandas.Series
Series containing cluster data for each block.
Returns
-------
str
The name of the Spacematrix morphotype.
"""
ranges = [[0, 3, 6, 10, 17], [0, 1, 2], [0, 0.22, 0.55]]
labels = [
["Малоэтажный", "Среднеэтажный", "Повышенной этажности", "Многоэтажный", "Высотный"],
Expand All @@ -71,7 +131,20 @@ def _name_spacematrix_morphotypes(cluster):
)
return "".join(cluster_name)

def _get_spacematrix_morphotypes(self, blocks):
def _get_spacematrix_morphotypes(self, blocks) -> gpd.GeoDataFrame:
"""
Calculates Spacematrix morphotypes for the given blocks using KMeans clustering.
Parameters
----------
blocks : geopandas.GeoDataFrame
GeoDataFrame containing block geometries and attributes.
Returns
-------
geopandas.GeoDataFrame
GeoDataFrame with Spacematrix morphotypes and clusters added.
"""
x = blocks[["fsi", "l", "mxi"]].copy()
scaler = StandardScaler()
x_scaler = pd.DataFrame(scaler.fit_transform(x))
Expand All @@ -85,13 +158,19 @@ def _get_spacematrix_morphotypes(self, blocks):
return blocks

def calculate(self) -> gpd.GeoDataFrame:
"""
Calculates Spacematrix and Strelka morphotypes for urban blocks.
Returns
-------
geopandas.GeoDataFrame
GeoDataFrame containing blocks with calculated Spacematrix and Strelka morphotypes.
"""
blocks = self.city_model.get_blocks_gdf()
developed_blocks = blocks.loc[blocks.footprint_area > 0] # or osr>=10
spacematrix_blocks = self._get_spacematrix_morphotypes(developed_blocks)
strelka_blocks = self._get_strelka_morphotypes(developed_blocks)
blocks["spacematrix_morphotype"] = spacematrix_blocks["morphotype"]
blocks["spacematrix_cluster"] = spacematrix_blocks["cluster"]
blocks["strelka_morphotype"] = strelka_blocks["morphotype"]
return blocks[
["geometry", "l", "fsi", "mxi", "strelka_morphotype", "spacematrix_morphotype", "spacematrix_cluster"]
]
blocks[SM_MORPHOTYPE_COLUMN] = spacematrix_blocks["morphotype"]
blocks[SM_CLUSTER_COLUMN] = spacematrix_blocks["cluster"]
blocks[KB_MORPHOTYPE_COLUMN] = strelka_blocks["morphotype"]
return blocks[["geometry", "l", "fsi", "mxi", KB_MORPHOTYPE_COLUMN, SM_CLUSTER_COLUMN, SM_MORPHOTYPE_COLUMN]]
1 change: 1 addition & 0 deletions docs/source/examples/methods/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ The examples of how to operate the `City` model to perform certain analytics.
connectivity
diversity
centrality
spacematrix
3 changes: 3 additions & 0 deletions docs/source/examples/methods/spacematrix.nblink
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"path": "../../../../examples/methods/spacematrix.ipynb"
}
208 changes: 208 additions & 0 deletions examples/methods/spacematrix.ipynb

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions tests/test_4_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
CENTRALITY_COLUMN,
POPULATION_CENTRALITY_COLUMN,
)
from blocksnet.method.spacematrix import Spacematrix, SM_CLUSTER_COLUMN, SM_MORPHOTYPE_COLUMN, KB_MORPHOTYPE_COLUMN

data_path = "./tests/data"

Expand Down Expand Up @@ -121,3 +122,23 @@ def test_population_centrality(population_centrality_result):
res = population_centrality_result
assert all(res[~res[POPULATION_CENTRALITY_COLUMN].isna()][POPULATION_CENTRALITY_COLUMN] >= 0)
assert all(res[~res[POPULATION_CENTRALITY_COLUMN].isna()][POPULATION_CENTRALITY_COLUMN] <= 10)


# spacematrix


@pytest.fixture
def spacematrix(city):
return Spacematrix(city_model=city)


@pytest.fixture
def spacematrix_result(spacematrix):
return spacematrix.calculate()


def test_spacematrix(spacematrix_result):
res = spacematrix_result
assert "fsi" in res.columns
assert "mxi" in res.columns
assert "l" in res.columns

0 comments on commit 827580c

Please sign in to comment.