Urban Blocks Explained
On this page
Problem statement
The block β the patch of land enclosed by streets β is the unit that decides how a city works on foot. Small blocks mean frequent junctions, short detours and many routes; large blocks mean long walls, few crossings and trips that go around rather than through. Block size is one of the oldest and most robust measures in urban design, and one of the easiest to compute wrongly, because "a block" depends on which streets bound it and how cleanly the network is drawn.
Quick answer
A block is a face of the street network: a polygon bounded on all sides by street centrelines. Its key measures are:
- area β the grain of the city;
- perimeter β the frontage and walking distance around it;
- blocks (or intersections) per kmΒ² β the density of route choices.
import numpy as np, pandas as pd, geopandas as gpd, osmnx as ox, momepy
ox.settings.overpass_url = "https://maps.mail.ru/osm/tools/overpass/api" # overpass-api.de mirror
ox.settings.overpass_rate_limit = False
from shapely.geometry import Point
ox.settings.http_user_agent = "spatialworkflow-examples/1.0"
PLACES = {"Barcelona Eixample": (41.3928, 2.1633), "Chicago Loop": (41.8837, -87.6325),
"Den Haag centre": (52.0799, 4.3113), "Rome centro": (41.8986, 12.4768)}
rows = []
for name, (lat, lon) in PLACES.items():
G = ox.graph_from_point((lat, lon), dist=1000, network_type="drive")
streets = ox.graph_to_gdfs(ox.convert.to_undirected(G), nodes=False)
crs = streets.estimate_utm_crs()
streets = streets.to_crs(crs)
limit = gpd.GeoSeries([Point(lon, lat)], crs=4326).to_crs(crs).buffer(800)
blocks = momepy.enclosures(streets, limit=limit)
blocks = blocks[(blocks.area > 500) & (momepy.elongation(blocks) > 0.15)]
inner = blocks[blocks.within(limit.iloc[0].buffer(-1))] # drop edge fragments
rows.append({"place": name, "blocks": len(inner),
"median mΒ²": inner.area.median(), "median perimeter m": inner.length.median()})
print(pd.DataFrame(rows).set_index("place").round(0).to_string())
blocks median mΒ² median perimeter m
place
Barcelona Eixample 105 17316.0 518.0
Chicago Loop 93 16988.0 524.0
Den Haag centre 77 8054.0 429.0
Rome centro 67 4196.0 308.0
Barcelona's Eixample and Chicago's Loop come out almost identical, at about 17,000 mΒ². The Eixample figure is larger than its famous 113 m blocks because a block measured between centrelines includes half of each street: the grid repeats every 133 m, and 133Β² is 17,700 mΒ². Den Haag's centre has half that grain and Rome's a quarter of it. The median block area captures the difference in one number.
Bounding streets, polygonise within a limit, remove non-blocks
1. Choose the bounding streets
Drivable streets define traffic blocks; including footpaths and alleys defines pedestrian blocks, which are smaller. The choice should follow the question.
2. Polygonise within a limit
Blocks come from polygonising the network; a limit polygon keeps the edge of the study area from being lost. Blocks cut by the limit are fragments and should be excluded from statistics.
3. Remove non-blocks
Thin polygons between dual carriageways, traffic islands and roundabout centres are street space. Filter by area and elongation, or clean the network first.
4. Describe the distribution
Median block area and its spread, not the mean: a few park-sized blocks skew means badly.
5. Relate blocks to junctions
Blocks per kmΒ² and intersections per kmΒ² are two views of the same grain. Report one of them with block size.
Detours and traffic against pedestrian blocks
The data used on this page
OpenStreetMap's drive networks within 1 km of four centres β Barcelona's Eixample, Chicago's Loop, central Den Haag and central Rome β and Den Haag's full street-and-path network for Example 2, fetched through OSMnx and the maps.mail.ru Overpass mirror. The setup unpacks those answers as saved on 10 October 2026.
import io
import os
import requests
import zipfile
# OpenStreetMap as Overpass returned it on 10 October 2026, saved as osmnx's own cache so the counts
# below do not drift as OSM is edited; delete the cache folder to query live instead
if not os.path.exists("cache"):
reply = requests.get("https://data.spatialworkflow.io/osm-cache-urban-blocks-explained-2026-10-10.zip", timeout=300)
reply.raise_for_status()
with zipfile.ZipFile(io.BytesIO(reply.content)) as archive:
archive.extractall("cache")
Example 1 β the detour a block imposes
for r in rows:
side = np.sqrt(r["median mΒ²"])
print(f"{r['place']:<20} typical side {side:4.0f} m β a walk to the opposite corner "
f"of the block is {2 * side:4.0f} m along the streets, {side * np.sqrt(2):4.0f} m straight")
Barcelona Eixample typical side 132 m β a walk to the opposite corner of the block is 263 m along the streets, 186 m straight
Chicago Loop typical side 130 m β a walk to the opposite corner of the block is 261 m along the streets, 184 m straight
Den Haag centre typical side 90 m β a walk to the opposite corner of the block is 179 m along the streets, 127 m straight
Rome centro typical side 65 m β a walk to the opposite corner of the block is 130 m along the streets, 92 m straight
The ratio of route to straight line is the same for every square block; what changes with block size is the absolute detour β and the number of chances to turn.
Example 2 β traffic blocks against pedestrian blocks
lat, lon = PLACES["Den Haag centre"]
for net in ("drive", "all"):
G = ox.graph_from_point((lat, lon), dist=1000, network_type=net)
s = ox.graph_to_gdfs(ox.convert.to_undirected(G), nodes=False).to_crs(28992)
lim = gpd.GeoSeries([Point(lon, lat)], crs=4326).to_crs(28992).buffer(800)
bl = momepy.enclosures(s, limit=lim)
bl = bl[(bl.area > 500) & (momepy.elongation(bl) > 0.15) & bl.within(lim.iloc[0].buffer(-1))]
print(f"{net:<6} network: {len(bl):>4} blocks, median {bl.area.median():>7,.0f} mΒ²")
drive network: 77 blocks, median 8,058 mΒ²
all network: 244 blocks, median 3,632 mΒ²
Footpaths through courtyards and parks break traffic blocks into much smaller pedestrian ones. The same city has a coarse traffic grain and a fine walking grain.
Example 3 β the shape of the distribution
lat, lon = PLACES["Rome centro"]
G = ox.graph_from_point((lat, lon), dist=1000, network_type="drive")
s = ox.graph_to_gdfs(ox.convert.to_undirected(G), nodes=False)
crs = s.estimate_utm_crs()
bl = momepy.enclosures(s.to_crs(crs), limit=gpd.GeoSeries([Point(lon, lat)], crs=4326).to_crs(crs).buffer(800))
bl = bl[bl.area > 500]
q = bl.area.quantile([0.1, 0.5, 0.9])
print(f"Rome: 10th percentile {q[0.1]:,.0f} mΒ², median {q[0.5]:,.0f} mΒ², "
f"90th {q[0.9]:,.0f} mΒ²; mean {bl.area.mean():,.0f} mΒ²")
Rome: 10th percentile 820 mΒ², median 4,941 mΒ², 90th 27,205 mΒ²; mean 14,227 mΒ²
Block areas are right-skewed everywhere: a few very large blocks β parks, palaces, rail land β pull the mean far above the median.
Block size and walking
Why block size matters for walking
Every block edge is a stretch without a turn. Small blocks give pedestrians many route choices and keep detours short; large blocks force long walks around. Block size and intersection density are among the most consistent built-environment correlates of walking in the research literature.
Why the network definition dominates the answer
Blocks are only as good as the lines that bound them. Omitting footpaths merges pedestrian blocks; mapping dual carriageways separately creates slivers; unconnected lines leave blocks open. Two studies of the same city can report very different block sizes for these reasons alone.
Why the median, not the mean
A few non-residential blocks β a park, a hospital campus, a railway yard β can be twenty times the typical block. The median describes the typical grain; the mean mostly describes the outliers.
Edge blocks, superblocks and courtyards
- Edge blocks clipped by the study limit are partial; exclude them.
- Superblocks (Barcelona's traffic-calmed groups of nine blocks) are a design unit above the block; the street network still shows the original grid.
- Courtyard blocks contain private open space that is not visible in the street network.
- Water and rail should bound blocks too; pass them as additional barriers.
- Units: report area in mΒ² or hectares consistently.
Internal links
- How to extract urban blocks from a street network β the workflow
- Blocks from streets come out wrong β slivers and open blocks
- How to calculate street network metrics β intersection density
- Urban morphology explained β blocks among the elements of form
- Network vs straight-line distance explained β detours in general
- Morphological tessellation explained β the unit within the block
FAQ
What is an urban block?
The area enclosed by streets on all sides β a face of the street network.
How big is a typical city block?
It varies from under 5,000 mΒ² in old European centres to well over 10,000 mΒ² in many American grids.
How do I compute blocks from OSM?
Polygonise the undirected street network within a limit, for example with momepy.enclosures.
Why use the median block size?
Block areas are heavily right-skewed; the median describes the typical block.
Do footpaths count?
For pedestrian blocks, yes. Report which network defines the blocks.
How are blocks related to walkability?
Smaller blocks mean more junctions, shorter detours and more route choices.