Source code for blocksnet.method.population_centrality

import geopandas as gpd
import networkx as nx
import numpy as np
from scipy.spatial import cKDTree
from shapely import Point
from sklearn.preprocessing import MinMaxScaler
from .base_method import BaseMethod


POPULATION_CENTRALITY_COLUMN = "population_centrality"

PLOT_KWARGS = {"column": POPULATION_CENTRALITY_COLUMN, "legend": True, "vmin": 0}


[docs]class PopulationCentrality(BaseMethod): """ Provides methods to calculate and visualize the population centrality of urban blocks. Population centrality is determined based on connectivity and population density within a specified radius. Methods ------- plot(gdf, linewidth=0.1, figsize=(10, 10)) Plots population centrality data on a map. calculate(connectivity_radius=1000) -> gpd.GeoDataFrame Calculates population centrality for urban blocks based on connectivity and population density. """
[docs] @staticmethod def plot(gdf: gpd.GeoDataFrame, linewidth=0.1, figsize: tuple[int, int] = (10, 10)): """ Plots population centrality data for blocks on a map. Parameters ---------- gdf : geopandas.GeoDataFrame GeoDataFrame containing block geometries and population centrality 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 """ ax = gdf.plot(color="#ddd", linewidth=linewidth, figsize=figsize) gdf.plot(ax=ax, linewidth=linewidth, **PLOT_KWARGS) ax.set_axis_off()
[docs] def calculate(self, connectivity_radius: float = 1000) -> gpd.GeoDataFrame: """ Calculate population centrality for urban blocks based on connectivity and population density. Parameters ---------- connectivity_radius : float, optional The radius within which to consider connectivity between blocks, by default 1000 meters. Returns ------- geopandas.GeoDataFrame GeoDataFrame of urban blocks with an added column for population centrality. This method calculates the connectivity of urban blocks within the specified radius, computes the degree centrality of each block, normalizes the values, and combines them with normalized population density to determine the final population centrality. """ # get blocks and find neighbors in radius blocks = self.city_model.get_blocks_gdf() points = np.array([(point.x, point.y) for point in blocks.geometry.centroid]) tree = cKDTree(points) pairs = tree.query_pairs(r=connectivity_radius) # graph creation and centrality calc connectivity_graph = nx.Graph() for idx, point in enumerate(points): connectivity_graph.add_node(idx, pos=(point[0], point[1])) for i, j in pairs: connectivity_graph.add_edge(i, j) centrality = nx.degree_centrality(connectivity_graph) # connect graph and blocks nodes = gpd.GeoDataFrame( 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") # normalize values data = blocks_out[POPULATION_CENTRALITY_COLUMN].values.astype(float).reshape(-1, 1) minmax_scaler = MinMaxScaler(feature_range=(1, 2)) normalized_data = minmax_scaler.fit_transform(data) blocks_out[POPULATION_CENTRALITY_COLUMN] = normalized_data data = blocks_out["population"].values.astype(float).reshape(-1, 1) normalized_data = minmax_scaler.fit_transform(data) blocks_out["population"] = normalized_data minmax_scaler = MinMaxScaler(feature_range=(0, 10)) blocks_out[POPULATION_CENTRALITY_COLUMN] = np.round( minmax_scaler.fit_transform( (blocks_out[POPULATION_CENTRALITY_COLUMN] * blocks_out["population"]) .values.astype(float) .reshape(-1, 1) ), 2, ) return blocks_out[["geometry", POPULATION_CENTRALITY_COLUMN]]