Analysis of the shape of urban layouts

As (Hamaina et al., 2011) shows, the structural analysis of street networks is very useful to characterize the urban factory. This is the reason why we have chosen to couple t4gpd to NetworkX. This coupling allows to benefit, in the context of t4gpd, from betweenness_centrality and closeness_centrality calculations.

Betweenness centrality

from t4gpd.demos.GeoDataFrameDemos import GeoDataFrameDemos
from t4gpd.graph.STBetweennessCentrality import STBetweennessCentrality

roads = GeoDataFrameDemos.districtRoyaleInNantesRoads()

betweenness = STBetweennessCentrality(roads).run()

Closeness centrality

from t4gpd.demos.GeoDataFrameDemos import GeoDataFrameDemos
from t4gpd.graph.STClosenessCentrality import STClosenessCentrality

roads = GeoDataFrameDemos.districtRoyaleInNantesRoads()

closeness= STClosenessCentrality(roads).run()

Shortest distance on a graph

from geopandas import GeoDataFrame
from shapely.geometry import Point
from t4gpd.demos.GeoDataFrameDemos import GeoDataFrameDemos
from t4gpd.graph.STShortestPath import STShortestPath

roads = GeoDataFrameDemos.districtRoyaleInNantesRoads()

fromPoints = GeoDataFrame([ {'geometry': Point((355126, 6689365))} ], crs=roads.crs)
toPoints = GeoDataFrame([ {'geometry': Point((3553125, 6689362))} ], crs=roads.crs)

result = STShortestPath(roads, fromPoints, toPoints).run()

Street intersection pattern determination (version 0.5.0+)

The structure of the city can be analyzed at various levels of scale. As developed in the article (Leduc, 2022), we propose here "to work on the scale of the street, the one in which the pedestrian is immersed, and more precisely on the scale of its articulations, the street intersections". More specifically, it is a question of "matching between the shape of the open space as captured by the pedestrian in immersion (this visual pattern is more commonly called the isovist) and a corpus of geospatial patterns". Beyond these two extracts from the abstract of (Leduc, 2022), let's see how to implement a compression technique based on Haar wavelets to analyze the Hippodamian plan of the city of Neuf-Brisach1, France.

Let's start by loading the data
from t4gpd.demos.GeoDataFrameDemos8 import GeoDataFrameDemos8
from t4gpd.graph.STToRoadsSectionsNodes import STToRoadsSectionsNodes

roads = GeoDataFrameDemos8.neufBrisachRoads()
buildings = GeoDataFrameDemos8.neufBrisachBuildingsAndWalls()
crossroads = STToRoadsSectionsNodes(roads).run()

This last instruction allows you to extract all the intersection points of the previously loaded road network. Let's represent the loaded data.

import matplotlib.pyplot as plt

_, basemap = plt.subplots(figsize=(0.75 * 8.26, 0.75 * 8.26))
buildings.plot(ax=basemap, color='grey', edgecolor='white', linewidth=0.5)
roads.plot(ax=basemap, color='black', linewidth=0.5)
crossroads.plot(ax=basemap, color='red', marker='+')
plt.axis('off')
plt.savefig('img/neufBrisach-1.png', bbox_inches='tight')

Neuf Brisach

Let's generate the set of geospatial intersection patterns
from t4gpd.morph.STCrossroadsGeneration import STCrossroadsGeneration

nbranchs, branchLength, branchWidth = 8, 100.0, 11.25

patterns = STCrossroadsGeneration(nbranchs, branchLength, branchWidth,
    mirror=False, withBranchs=True, withSectors=True,
    crs='EPSG:2154', magnitude=2.5).run()

Note that the 78 patterns thus produced repeat and extend the 35 geospatial patterns proposed in (Leduc & Hartwell, 2020). This new repertoire contains 43 quarter-round patterns better able to represent roundabout or square.

Let us now represent these 78 patterns.

_, basemap = plt.subplots(figsize=(8.26, 8.26))
patterns.plot(ax=basemap, color='lightgrey')
patterns.apply(lambda x: basemap.annotate(
    text=x.gid, xy=x.geometry.centroid.coords[0],
    color='red', size=10, ha='center'), axis=1)
plt.axis('off')
plt.savefig('img/intersection-patterns.png', bbox_inches='tight')

78 intersection patterns

Let's proceed to a sampling in 64 angular abscissas - by a systematic radial sweep starting due east - as evoked by (Leduc & Hartwell, 2020). In accordance with (Leduc, 2022), we will call this discretization the pattern signature:

from t4gpd.morph.geoProcesses.AngularAbscissa import AngularAbscissa
from t4gpd.morph.geoProcesses.STGeoProcess import STGeoProcess

nRays = 64
pattRays = STGeoProcess(AngularAbscissa(patterns, 'vpoint_x',
    'vpoint_y', nRays), patterns).run()
Let's generate the set of visual patterns (the field of isovists associated with the previously identified intersection points)
from t4gpd.isovist.STIsovistField2D import STIsovistField2D

rayLength = branchLength
isovRays, _ = STIsovistField2D(buildings, crossroads, nRays, rayLength).run()

Let's now map this field of isovists:

_, basemap = plt.subplots(figsize=(0.75 * 8.26, 0.75 * 8.26))
buildings.plot(ax=basemap, color='grey', edgecolor='white', linewidth=0.5)
isovRays.plot(ax=basemap, color='black', linewidth=0.5)
plt.axis('off')
plt.savefig('img/neufBrisach-2.png', bbox_inches='tight')

Neuf Brisach: field of isovists

Let's now proceed to the match making
from shapely.wkt import loads
from t4gpd.morph.geoProcesses.CrossroadRecognition import CrossroadRecognition

op = CrossroadRecognition('FWT', pattRays, 'gid', nRays, rayLength)
results = STGeoProcess(op, isovRays).run()
results.geometry = results.viewpoint.apply(lambda p: loads(p))

Let's finally map the result:

from t4gpd.pyplot.MultipleMarkerStyles import MultipleMarkerStyles

_, basemap = plt.subplots(figsize=(0.75 * 8.26, 0.75 * 8.26))
buildings.plot(ax=basemap, color='grey', edgecolor='white', linewidth=0.5)
roads.plot(ax=basemap, color='black', linewidth=0.5)
MultipleMarkerStyles(results, None,
    left_on='recId_fwt', right_on='gid',
    rotation='rotation', basemap=basemap,
    marker_size=800, alpha=0.71, nbranchs=nbranchs).run()
results.apply(lambda x: basemap.annotate(
    text=x.recId_fwt, xy=x.geometry.centroid.coords[0],
    color='red', size=12, ha='center'), axis=1)
plt.axis('off')
plt.axis([1037408.1, 1037659.7, 6778111.7, 6778268.5])
plt.savefig('img/neufBrisach-3.png', bbox_inches='tight')

Neuf Brisach: street intersection patterns


  1. See the dedicated Wikipedia page.