Depave: Technical Methodology

Two cities, one analysis. How the Depave project measures pavement in New York City and Amsterdam, and how a single shared pipeline scores both cities on where depaving is most needed.

This document is the technical methodology for both Depave deployments. Part I covers data gathering and formatting, which differs by city because the available inputs differ. Part II covers the analysis pipeline, which is shared: per-city indicators feed one engine with one set of stages. It is written for a GIS analyst who wants to reproduce or extend the work, and for a reviewer who needs to judge its assumptions. Source code and configuration live in a single repository; paths are relative to the repository root.

Contents
  1. Scope: two cities, one analysis
  2. Part I — New York City: data gathering and formatting
  3. Part I — Amsterdam: data gathering and formatting
  4. Part II — The unified analysis pipeline
  5. Implementation status
  6. Known limitations
  7. Credits & citation

1. Scope: two cities, one analysis

Depave asks the same question in both cities: how much pavement is there, how much of it could realistically be removed, and where does removal do the most good for heat, flooding, and missing canopy. Both city products share one frame of three questions: why is depaving needed, “Where is depaving possible?”, and “How much can actually be depaved?” The two quoted questions are carried verbatim from the delivered Amsterdam viewer, where they appear as its sidebar headings; the NYC dashboard answers the same frame in its own layout. The project publishes two sites from one deployment:

The two cities start from very different inputs. New York infers pavement from a classified aerial-survey raster; Amsterdam reads it from a national topographic register. Part I therefore documents each city separately. From the indicator stage onward the method is shared: each city reduces its inputs to one raw value per analysis unit per need layer, and a single pipeline (Part II) normalizes, rates flood on an absolute ruler, composites, and flags priority units. That shared spine is implemented once, in src/depave/process/, and configured per city.

2. Part I — New York City: data gathering and formatting

The authoritative NYC methodology contract is docs/methodology.md in the repository: every published figure, its derivation, its verification method, and its uncertainties. That contract travels with the code and changes in the same commit as the pipeline it describes. This section summarizes it; where this summary and the contract disagree, the contract wins.

2.1 What the dashboard publishes

NumberValueRendered on the page?
Land193,372.8 acno (denominator only)
Pavement85,848.5 ac · 44.4% of landno
Buildings20.7% of landyes, composition bar
Depaveable13,211.5 ac · 6.83% of land · 15.06% of pavementyes, the headline
Extreme heat55.9% of land (69.2% of pavement)in dashboard_data.json; not on the map since 2026-08-26
Stormwater flooding2.5% of pavementin dashboard_data.json; not on the map since 2026-08-26

Heat and stormwater deliberately use different denominators. Heat is asked of land, because heat falls on everyone; a pavement-denominator heat share (69.2%) is published alongside it so the two can be read over the same base. Stormwater is asked of pavement, because the question is how much hard surface floods. Pavement (44.4%) and buildings (20.7%) are published separately and never summed: the typing masks buildings with footprints eroded by 3 ft, so a measured 2,575 ac of roof edge is typed as pavement, and the sum would double-count it.

2.2 Sources and staging

DatasetSourceVintageNotes
Typed pavement, 18 classderived (see 2.3)built 2026EPSG:2263 · 0.5 ft raster · uint8
LiDAR land coverNYC OTI2017the pavement observation itself
Building footprintsNYC DoITT2026eroded 3 ft for the roof mask
MapPLUTO 26v1NYC DCP26v1land use, ownership, cemetery lots (BldgClass='Z8')
NTA 2020 boundariesNYC DCP2020 linesreporting unit; simplified 40 ft for the web
Council districtsNYC DCP2023 linessha256-pinned on every run
Landsat 9 summer LSTUSGS via Earth EngineJun–Aug median 2022–2025, 62 scenes°F · ~30 m · staged by cache key, found by parameters rather than filename
DEP Stormwater Flood MapsNYC DEP2024extent polygons, Flooding_Category 1/2/3, per scenario GDB
Pavement fraction, 20 ftderived2026exact uint16 counts of 40×40 source pixels per cell

Every staged dataset in the shared data library carries a source.md provenance sidecar. All metric geoprocessing runs in EPSG:2263 (US survey feet); the only reprojection to EPSG:4326 is the web export. The acre constant is 43,560 ft². The vintages are mixed: land cover is 2017 while footprints, parcels, and boundaries are 2026, so pavement extent is a 2017 observation carried against 2026 geography. This is the largest unquantified uncertainty in the NYC deliverable.

2.3 The measurement rule: raster pixel counts

Pavement is typed per 0.5 ft pixel from the land-cover raster, survey layers (roadbed, sidewalk, parking, plaza, median), the eroded footprints, and PLUTO land use, into 18 mutually exclusive classes. Counts accumulate from tile cores only, with a halo read but never counted, so there is no seam double-count. The published pavement figure was verified by an exact recount of all 97.7 billion pixels, and the 20 ft fraction grid sums to the identical pixel count by an independent path.

Acreage comes from the raster, never from polygon areas, because vectorising fattens fragmented classes by up to +12%. Three sanctioned exceptions carry polygon figures instead:

The vacant source carries a measured raster-to-vector correction factor (×0.9481), and the land denominator is deliberately a polygon area (the NTA union), so every “share of land” divides a raster numerator by a polygon denominator. The depaveable headline (13,211.5 ac) is the sum of six sources after two exclusions, reconciled source by source in the contract; conservation holds to 0.1 ac against the pavement total. On-street parking accounts for 7,651.7 ac of it, of which 4,018.9 ac is kerb carrying no sign at all — counted because NYC permits parking where nothing is posted, at confidence 0.3. Regulated kerb alone is 3,632.7 ac and the confidence-weighted figure is 4,109.2 ac; both are published as sensitivities rather than alternative builds, since the map can only draw a polygon or not draw it. The cemetery exclusion removes paved surface on cemetery lots (MapPLUTO BldgClass='Z8'). The athletic-hardscape exclusion (adopted 2026-08-26) removes active athletic hardscape such as courts, running tracks, and paved ball-field areas: 183.2 ac, of which 180.2 in parks, with the balance in turf, vacant, and sidewalk-strip sources. These surfaces are paved and depaveable in the physical sense, and are withheld from the headline because they are in active recreational use; the withheld-park figure in the panel carries them.

2.4 Assumptions and uncertainty digest

The full validation gate (independent recounts and reproductions of all six headline numbers, four of them from raw inputs with caches bypassed) is in docs/methodology.md §5, along with the reproducibility boundary: the archived LST GeoTIFF is a primary input that must ship with any replication package, because the Earth Engine composite cannot be re-derived scene for scene.

3. Part I — Amsterdam: data gathering and formatting

Amsterdam differs from New York in one decisive way: a national register records what every surface is, so pavement is read from recorded attributes instead of inferred from imagery. The Dutch BGT (Basisregistratie Grootschalige Topografie) is a legally maintained topographic register in which every surface is a polygon carrying its function (functie) and physical form (fysiek_voorkomen). The classification stage disappears, and with it classification error; pavement becomes many typed categories with per-category depaving recommendations; semi-permeable surfaces (half verhard, onverhard) are excluded by rule. The Amsterdam pipeline also adds a measured removability step, the depave carve, that the raster method has no equivalent for.

All spatial operations run in EPSG:28992 (Amersfoort / RD New, metres); web exports are reprojected to EPSG:4326. The reporting unit is the CBS buurt from the 2025 Wijk- en Buurtkaart. Study areas are configuration-driven: a pilot (Tuindorp Oostzaan, two buurten, 71.04 ha) and the full municipality (gemeente GM0363 including the Weesp exclave). The delivered citywide map covers the whole municipality.

Ownership scope. Amsterdam counts pavement only on land the City owns and has not leased out (§3.3). Depaving is a public-realm action, so the map deliberately shows the realm the City can act on. Amsterdam's pavement totals are therefore a subset of the true surfaced area and are not directly comparable to the NYC figures.

3.1 Sources

LayerSourceNotes
PavementBGT via PDOK: wegdeel, onbegroeidterreindeel, overbruggingsdeel, ondersteunendwegdeelEPSG:28992; temporal-filtered to live versions
WaterBGT waterdeeltrims pavement over water
BuildingsBGT pand via PDOK198,455 footprints, 2,601.8 ha staged; 2,566.1 ha (13.97% of dryland) published — see below
Land ownershipBRK cadastre via Amsterdam Beheerkaart (eigendomsrecht WFS)the public-land clip
Boundary / reporting unitsPDOK Bestuurlijke Gebieden; CBS Wijk- en Buurtkaart 2025buurtcode is the join key
HeatKlimaateffectatlas — felt temperature (PET) per buurtprivate link; see §3.7
FloodKlimaateffectatlas — pluvial depth classes, 1-in-100 & 1-in-1000 yrprivate link; see §3.7
Tree canopyRIVM Bomenkaart 2022 (Atlas Natuurlijk Kapitaal)10 m raster, % cover; private link
Pedestrian demandDUO school enrolment; NS + GVB ridership; OSM POIs and parks; curated destinationssee §3.4

3.2 Pavement extraction — the BGT clean map

Each surface category is produced by an editable rule in config/amsterdam_classify_pavement.yaml, executed by src/depave/process/amsterdam/classify_pavement.py, in a fixed order:

SELECT     BGT features whose recorded function matches (e.g. fietspad → bike paths)
REMOVE /   drop or keep features touching a mask (trams, squares)
  KEEP
TRIM       cut away parts lying over water (bgt_waterdeel)
DISSOLVE   merge into one clean layer for the category
SUBTRACT   squares become the residual open surface, minus vegetation and other layers
CLIP       keep only what sits on public land (§3.3)

A global exclusion drops the unpaved fysiek_voorkomen values from every road-part category. Each category carries a constant depave_status (depave_potential or no_depave_potential), which replaces the US core / non-core split. The delivered citywide map builds eleven categories totalling 3,157.2 ha on public land: roads 1,134.3 · sidewalks 907.0 · parking 290.9 · other impervious 228.6 · bike paths 220.1 · plazas 134.5 · sidewalks in parks 83.8 · medians 70.4 · bridges 56.5 · railway/tram 31.2. Roads, bike paths, and bridges are non-candidates. National rail (~200.6 ha) and state highways (~429.6 ha) sit on non-City land and are shown as reference infrastructure only, bringing the true surfaced total to roughly 3,787 ha.

Tracked vs delivered. The version-controlled engine emits nine categories for a single study area; the eleven-category citywide map is produced by experimental streaming scripts that import the same acquisition and rules and extend them. See §5 for the boundary.

3.3 Public-land clip (BRK ownership)

After each category is built, the map is clipped to land the City owns and has not leased out. A cadastral parcel is kept only when the right is base ownership (Eigendom (recht van)), the City of Amsterdam holds it, and the ownership is unencumbered (indicatie_belast_recht = False). The unencumbered filter is the one that matters: the City holds base ownership of roughly 97% of its land but leases most of it out via erfpacht, and those leased rights are private in use. Parcels are dissolved before area is taken, because the register carries duplicate and temporal rows whose sum would double-count. This is why the port district appears nearly empty: about 83% of its pavement sits on leased terminals or national highways. If the ownership mask comes back empty, the build refuses to ship an unclipped map.

3.4 Pedestrian-demand model

Before pavement can be called removable, the tool has to know how much walking space a street needs. The demand model (src/depave/process/amsterdam/ped_demand/) is a faithful port of New York City DOT's Pedestrian Demand Map: a deterministic rule table in which every public footway takes the highest tier any single nearby generator assigns, on a five-tier ladder from Baseline (1) to Global Corridor (5). Six generators feed it, each with its own source, catchment buffer, and threshold: restaurants (OSM, 25 m), schools (DUO enrolment, 25 m), rail and metro stations (NS + GVB ridership, 75 m), hospitals (OSM, 25 m), parks (OSM, 15 m; regional parks rank higher), and thirteen curated destinations (120 m; five Global corridors such as Dam and Museumplein, eight Regional draws). The thresholds are ported unchanged from the NYC table, which yields a strongly skewed Amsterdam distribution: Baseline 81.0% · Community 13.8% · Neighborhood 2.7% · Regional 2.1% · Global 0.3%.

Validation is central only. The model is checked against CMSA pedestrian-counter footfall for validation, never fitted to it. All 27 available counters sit in the city centre and central rank agreement is modest (Kendall τ ≈ 0.17), so peripheral tiers are modelled, unvalidated, and should be read that way.

3.5 The depave carve (sidewalks)

Rather than declaring a whole sidewalk removable or untouchable, the carve (src/depave/process/amsterdam/sidewalk_carve.py) keeps a walking corridor sized to the street's demand tier, anchored along the building frontage, and measures everything beyond it as removable excess. Retained corridor widths: Global 3.6 m, Regional 3.0 m, all lower tiers and unmatched pieces 1.8 m. Per sidewalk piece: in-park sidewalks are treated as 100% depaveable; street sidewalks take their tier by a join on lokaal_id; plaza-like blobs (max inscribed width ≥ 5 m and low elongation) are excluded from the linear carve; the frontage carve keeps the part within the tier width of the building frontage (the polygon boundary minus road and water edges) and books the rest as excess, with inscribed width measured from a distance transform at 0.30 m resolution. A refinement releases paved collars trapped around planting where the retained strip fully encloses vegetation inside a depave zone.

Citywide, the published carve layer measures 302.1 ha of depaveable sidewalk excess and retains 534.9 ha of walking corridor across 45,949 pieces; only 1.7% of retained area sits below its tier target. The measured excess decomposes into 290.8 ha carved to the demand standard plus about 11.2 ha of vegetation islands merged into the layer afterwards. The carve manifest's totals predate that merge, so the veg-island share is derived by difference rather than attributed per feature. A piece with no building frontage to measure against is skipped rather than partitioned, so the removable figure is a conservative undercount.

3.6 Deparking opportunity

On-street parking is published as a separate opportunity, because reclaiming parking is a different kind of decision from widening a footway. The layer is all on-street parking (BGT parkeervlak) plus any paved median or traffic island touching it: 314.9 ha citywide (290.9 ha parking + 24.0 ha adjacent islands, across 41,035 parking features and 5,281 medians). Keeping it distinct keeps the policy question visible.

The citywide dashboard counts on-street parking (290.9 ha) as a depaveable source in its own right, alongside the sidewalk carve excess, the open plaza surface, the railway and tram bed, turf, park sidewalks and medians. That set gives the dashboard's headline of 1,152.4 ha, 6.28% of dryland. Until 10 September 2026 it read 1,162.3 ha and 6.33%: the denominator was dryland (area less BGT waterdeel) while the pavement masks kept the area they had OVER water, so the segments described more ground than the total they were shares of. Both masks are now cut to non-water at the point of publication, removing 42.6 ha of paved and 9.9 ha of depaveable. That removal is decked structure rather than edge slivers: 41.7% of the bridges layer sits over water, and the depaveable side is almost entirely decked plaza (6.9 ha) and railway deck (3.1 ha). The reconciliation oracle still reproduces the original unclipped method, so the gates compare the unclipped masks against it and check the clip separately. Parking was excluded from that set between 27 August and 2 September 2026 and restored by project decision on 2 September 2026. The 314.9 ha deparking layer stays a separate row of the classic viewer; its adjacent medians are already a full member of the dashboard set, so it is not added on top. The tracked pipeline (depaveable.py) still gives parking depaveable_method: deparking and skips it, so its depaveable output excludes parking and the two figures differ by design.

3.7 Measurement basis and private-link inputs

Buildings. BGT pand is rasterised onto the same 1 m EPSG:28992 grid as the pavement masks, rather than assigned by centroid-in-polygon as New York does (§2.3): Amsterdam reports down to CBS buurt, and a large building straddling a buurt line would otherwise put its whole footprint on one side. The staged layer is 198,455 footprints and 2,601.8 ha; the figure the composition bar publishes is 2,566.1 ha, 13.97% of dryland. The 35.6 ha difference is a deliberate narrowing, so that the bar's segments cannot double-count and still sum to 100: building cells already claimed by either pavement mask are removed, 19.2 ha of them. That total is a union, not a sum: 16.9 ha of building cells fall on the paved mask and 7.0 ha on the depaveable mask, and 4.7 ha lie on both, because parking, medians and park sidewalks are members of each. Cells over BGT waterdeel go too (2.2 ha), because dryland is the bar's denominator and a rooftop over water would be charged against land that denominator has already taken out. The same reasoning is applied to both pavement masks since 10 September 2026; before that date it was applied to buildings alone. The remainder falls outside the municipal clip. Buildings sit INSIDE the bar's paved segment, matching the New York contract, and the page derives street pavement by subtracting them back out.

Where NYC measures by raster pixel counts (§2.3), Amsterdam measures by vector area: hectares are taken from dissolved BGT polygons in EPSG:28992 after the trim and clip steps. There is no rasterisation in the measurement path, so the vector-fattening concern that drives the NYC rule does not arise; the register's polygons are the measurement.

Serving format (new unified pipeline). In the unified pipeline, the Amsterdam dashboard's raster layers are rasterized at 1 m in EPSG:28992 from the BGT-derived vector layers (the same vectors previously served directly), then tiled to PMTiles in the same way as NYC's layers. This rasterisation is a serving format only; every measured hectare is still taken from the dissolved vectors as above.

Three needs-stage inputs are not publicly downloadable. They are requested from their providers and delivered by private link, so no permanent source URL exists; provenance is recorded with the staged data. The staged paths are the contract the pipeline reads:

InputDeliveryStaged path
Felt temperature (PET) per buurtKlimaateffectatlas, private linkdata/amsterdam/raw/heat_pet_buurt.gpkg
Pluvial depth classes, 1-in-100 yrKlimaateffectatlas, private linkdata/amsterdam/raw/flood/rain_1_100.tif
Pluvial depth classes, 1-in-1000 yrKlimaateffectatlas, private linkdata/amsterdam/raw/flood/rain_1_1000.tif
Tree-canopy cover raster (RIVM Bomenkaart 2022)Atlas Natuurlijk Kapitaal, private linkdata/amsterdam/raw/canopy_cover.tif

The tier-fallback configuration (config/locations/amsterdam.yaml) pins these as forced staging tiers with registered validators: a missing file produces a curated stage-this-file error instead of a silent fallback to a lower-quality public source.

4. Part II — The unified analysis pipeline

From here the method is one pipeline, shared between cities. Each city's Part I outputs reduce to one raw value per analysis unit per need layer (heat, canopy, pavement, flood); the shared stages then normalize, composite, and flag priority. The analysis unit is the census tract in NYC and the CBS buurt in Amsterdam (src/depave/process/study_units.py loads the buurt universe: every non-water buurt with a real population value whose representative point falls inside the municipal boundary, geometries kept unclipped so zonal statistics see each buurt whole).

4.1 Per-city indicators as preprocessing

LayerNew York CityAmsterdam
HeatShare of the unit's land at or above 104 °F in the staged Landsat 9 summer LST composite (heat_layer.compute_heat_scores_from_lst); metric tag lst_share_pctPer-buurt zonal mean PET (felt temperature, °C) from the national product, attribute-joined by buurtcode (compute_heat_scores_from_unit_join); metric tag pet_mean_c. The unrounded _mean field is used, never the integer PET_gem
Canopyclass_share mode: deficit = 1 − (canopy pixels / non-water pixels) from the classed land-cover rastercontinuous_mean mode: deficit = 1 − mean cover / 100 from the RIVM percent-cover raster (nodata −9999 dropped; zero valid pixels → NaN, never a default)
PavementPaved fraction of tract area from the intersection of the classified pavement polygon union with each tract (the citywide dashboard's acreages, by contrast, come from raster pixel counts — see §2.3)Paved fraction of buurt area from the BGT vector clean map. With full-city BGT coverage, zero pavement means genuinely pervious and is scored low, never flagged missing
FloodFlood engine, category_polygons backend (§4.2)Flood engine, class_raster backend (§4.2)

Every raster read is masked to the unit polygon, so pixels outside the outline never enter a statistic. Each indicator writes a *_raw column; the heat indicator additionally stamps a heat_metric tag naming the scale its raw value sits on, which the absolute framing (§4.6) depends on.

4.2 The flood engine

Flooding is pluvial (rain-on-street) only in both cities. Depaving mitigates rainfall flooding; it does little about tidal inundation or dike-breach depth, which are governed by sea level, elevation, and flood defences. Amsterdam's LIWO breach maps and NYC's Flooding_Category 3 (Future High Tides) are excluded for the same reason, which also keeps both cities on one portable basis.

The engine (src/depave/process/flood_engine.py) is config-driven: a city supplies a YAML listing pluvial scenarios with event weights summing to 1.0, a depth class → centimetre midpoint map, and an absolute cm band ruler. The method, identical across backends:

  1. Per scenario, express expected flood depth in cm over the unit's ground.
  2. Combine scenarios by event weight: cm = Σ(weight⋅cm). Weights summing to 1.0 keeps the result a probability-weighted expected depth on the same scale as any single scenario, which is what keeps the band ruler calibrated.
  3. Per unit, one number carrying severity × extent: deep and widespread raises it, while deep-but-tiny and shallow-but-everywhere both stay low.
  4. Band 1–5 on an absolute cm ruler, then flood_score = (flood_rating − 1) / 4.

The ruler is deliberately absolute rather than a quantile of the observed distribution, so a rating means the same thing across runs with different unit sets. Units with no coverage in any scenario get NaN, never band 1: a unit with no flood data is excluded by the assessed guard (§4.4), never labelled “lowest risk”.

Two backends adapt the same arithmetic to each city's input shape. Amsterdam's rasters hold depth classes per cell (nodata = dry); the engine maps class → cm, weights per pixel, and takes the in-polygon mean with dry counted as 0, after checking that all scenario rasters share one grid. NYC's flood maps are extent polygons with a category column; per unit, cm = Σ weight ⋅ Σ class_cm[cat] ⋅ (area of unit within that category's extent / unit area), which is the raster method at infinite resolution.

The two city configs, as wired (config/flood_engine_amsterdam.yaml, config/flood_engine_nyc.yaml):

# Amsterdam — class_raster backend
input_kind: class_raster
scenarios:
  - {name: rain_1_100,  weight: 0.7, nodata: 255,
     file: data/amsterdam/raw/flood/rain_1_100.tif}
  - {name: rain_1_1000, weight: 0.3, nodata: 127,
     file: data/amsterdam/raw/flood/rain_1_1000.tif}
class_cm: {1: 7.5, 2: 12.5, 3: 17.5, 4: 25.0, 5: 40.0}   # TUNABLE
bands:                                                    # TUNABLE; absolute cm ruler
  - [1.37, 1]
  - [2.74, 2]
  - [4.79, 3]
  - [6.85, 4]        # above 6.85 -> band 5
# New York City — category_polygons backend
input_kind: category_polygons
category_field: Flooding_Category
include_categories: [1, 2]        # 1 nuisance (4 in – 1 ft), 2 deep (1 ft+);
                                  # 3 = Future High Tides, tidal, always excluded
class_cm: {1: 20.3, 2: 45.7}      # TUNABLE midpoints: nuisance 8 in = 20.3 cm;
                                  # deep open-ended, 18 in = 45.7 cm representative
scenarios:
  - {name: moderate_current, weight: 0.7,
     layer_match: {rain: Moderate, sea: Current_Sea_Levels}}
  - {name: extreme_2080,     weight: 0.3,
     layer_match: {rain: Extreme, sea: 2080_Sea_Level_Rise}}
bands:                            # TUNABLE; absolute cm ruler
  - [1.0, 1]
  - [2.0, 2]
  - [4.1, 3]
  - [6.1, 4]         # above 6.1 -> band 5

Scenario selection notes. Amsterdam weights the frequent event most, because it is the one a neighbourhood actually experiences; its bands were recalibrated by the observed median shift (×1.37) when the probability weighting went in, restoring discrimination (12 buurten in band 5, 2.3%). NYC includes two of its published set: the 1.77 in/hr “limited” GDB is excluded because it ships as an Esri CDF-compressed feature table that stock GDAL cannot read at all, and moderate-2050 is excluded because it is a sea-level variant of the same rain depth as moderate-current, so including both would double-weight moderate rain. The NYC bands anchor on the source's own depth semantics: with the nuisance midpoint at 20.3 cm, an expected depth of 1.0 / 2.0 / 4.1 / 6.1 cm corresponds to roughly 5 / 10 / 20 / 30 percent of a tract's ground under nuisance flooding.

Calibration status. The NYC parameters (scenario pair, weights, class-to-cm midpoints, band ruler) were approved by analyst sign-off on 25 August 2026. The Amsterdam values are the published reference calibration of the delivered needs assessment (verified 14 August 2026).

4.3 Normalization and the weighted composite

Heat, canopy, and pavement raw values become relative 0–1 scores by min-max normalization across the units in the run:

score = (value − min) / (max − min)

Higher score means higher need. When the layer has no spread, units with a present raw value fall back to 0.5; a NaN raw value stays NaN in the score, so missing units are never laundered past the assessed guard. Flood arrives already scored on its absolute ruler (§4.2), so it is the one layer whose score keeps its meaning across runs.

The composite (src/depave/process/stacked_needs.py) is a weighted mean of the four scores. It is titled “Combined needs score” across all deliverables. Some earlier materials labelled it “Stacked needs score”, and the module and column names below still carry that older word. Weights come from the location YAML (needs_weights), normalized to sum to 1.0; the default is equal weight, 0.25 each, and both cities currently run the default. Both location configs declare needs_layers: [heat, flood, canopy, pavement], which switches the composite to strict mode: a declared layer whose score column is missing raises, instead of silently re-weighting over whichever columns exist.

Citywide New York run. Citywide pavement burden is read from the surveyed pavement product that also backs the published acreage figures; it counts mapped road, sidewalk, and parking surface even where street-tree canopy overhangs it, while the earlier study-area reports counted only pavement visible in the 2017 LiDAR land cover. In heavily tree-lined districts such as East Harlem the citywide paved shares therefore run about 2 to 15 percentage points higher per tract, and because stacked scores are normalized across all 2,325 tracts, rankings within a small, internally homogeneous district can differ by several places from the earlier district-normalized reports. This paragraph describes the tract run, which is still what the neighborhood viewers read; the citywide dashboard scores its own boundary levels separately (see below).

The citywide dashboard scores each boundary level separately. The dashboard’s needs rows are not the tract run aggregated upward. scripts/build_citywide_needs_levels.py puts each of the three levels — borough, neighborhood, council district — through the scoring chain above in its own right, reading the same rasters and scenario polygons through the same code (src/depave/process/needs_scoring.py, shared with the pipeline’s needs stage). Two things made the earlier roll-up untenable: the 2023 council districts do not nest inside census tracts, so each district’s figure was assembled from whichever tracts a representative point happened to fall in, and every level’s scores were normalized against the spread of 2,325 tracts, which left a borough with no meaningful position among the other four.

The consequence a reader has to hold onto: a heat, canopy or pavement score places an area among the others at its own level. Those three are min-max normalized within the level, and is_priority is that level’s top quartile of the composite. A neighborhood’s heat score of 0.80 puts it high among 262 neighborhoods; a borough’s 0.80 puts it high among five. The same ground therefore reads differently depending on which tab is open, and the borough-level priority overlay marks one or two boroughs because a quartile of five is one or two. The page states the level in the legend and in the section heading for this reason.

Flooding is deliberately not on that footing. It arrives already banded on the absolute cm ruler of §4.2, so a flood score means the same expected depth at every level and in either city — the one layer that stays comparable across tabs. It also takes only n_bands + 1 distinct values, five as New York is calibrated, so across five boroughs the flood map will often carry two or three flat colours. That is the ruler reporting what it measures, not the map failing. The stacked composite is the weighted mean of all four and therefore mixes the two framings, exactly as the tract run always has. Each level’s output also carries the level-independent *_abs columns (§4.3 absolute framing) for comparisons across levels; the dashboard does not publish them.

Amsterdam is scored natively at buurt level only. The Klimaateffectatlas PET delivery, the RIVM canopy raster and the two pluvial depth rasters are private-link staged files and are not yet in place, so the dashboard’s wijk and stadsdeel rows carry dryland-area-weighted roll-ups of the buurt scores, flagged as estimates, and the priority overlay is offered at buurt level alone.

4.4 The assessed guard

A unit with NaN in any available score column is flagged assessed = False: its stacked_score is NaN, it is excluded from the priority quantile, and renderers hatch it as no-data. Missing data is never defaulted to a midpoint and never silently re-weighted onto the layers a unit does have; either treatment would launder an unmeasured unit into the ranking. Citywide Amsterdam this assesses 514 of 517 buurten, with the 3 unassessed kept and hatched rather than dropped.

4.5 Priority and the equity overlay

Priority is the top quartile: is_priority = stacked_score ≥ 75th percentile, with the percentile computed over assessed rows only, and rank ordering units by need (1 = highest).

The equity overlay is a post-hoc lens on the priority set, deliberately outside the score. In NYC, priority tracts are overlaid against disadvantaged-community designations (NYS DAC / EJNYC, and CEJST federally); the designation never enters the composite, so the score stays a physical heat / canopy / pavement / flood lens and the overlay reports how the priority set intersects designated communities. Amsterdam has no equity overlay yet: the equity data source is deliberately unconfigured, the stage skips when nothing resolves, and the existing overlay code carries US-only semantics. The candidate route on record is the CBS 2025 buurt socio-economic indicators already attached to the buurt layer. Until one is chosen, the Amsterdam priority set should never be read as an environmental-justice designation.

4.6 The absolute-framing parallel view

Because three of the four scores are relative to the run, a single-neighborhood run hides where that neighborhood sits citywide. The absolute framing (src/depave/process/absolute_framing.py) keeps a second, study-area-independent view built from the raw values on fixed scales: heat_abs rescaled per the unit's heat_metric tag (LST share on 0–100; HVI on 1–5), flood_abs / canopy_abs / pavement_abs already on fixed 0–1 scales, a stacked_abs weighted mean, and is_priority_abs at the same 75th-percentile rule. A recognized-but-wrong scale would be silent and severe (the HVI transform on a percentage clips nearly every unit to maximum heat), so an unrecognized heat_metric tag raises rather than guessing. The PET metric is not yet registered in the absolute framing's scale table, so the absolute view is skipped for locations using it until that follow-up lands.

5. Implementation status

What runs where today, stated plainly so a reviewer knows the boundary between built, migrated, and pending.

New York City

Amsterdam

The published Amsterdam results, verified against code and output on 14 August 2026: 514 of 517 buurten assessed, 129 flagged priority (mean needs score 0.527). The depave funnel citywide:

StageAreaWhat it means
All public pavement3,157 haeverything the City owns and has not leased
Categories with depaving potential1,746 haexcludes roads, bike paths, bridges
Realistically depaveable526 haafter pedestrian demand is respected
Deparking opportunity315 haon-street parking + adjacent islands (a separate question)
Remains as pavement552 hathe walking corridors that must stay

The middle step is the one the traditional method cannot perform: the difference between 1,746 hectares of in-principle candidates and 526 hectares removable without narrowing a footway below what its pedestrian traffic needs. These figures are provisional where the plaza definition is unresolved (§6), and Amsterdam counts only City-owned, unleased land, so its figures are not comparable to the other cities' all-ownership totals.

6. Known limitations

7. Credits & citation

Depave is produced by ONE Architecture & Urbanism. The Amsterdam pedestrian-demand model is adapted from New York City DOT's Pedestrian Demand Map; the pluvial flood engine was developed on Amsterdam and ported to New York City.

Data providers — New York City: NYC OTI (2017 LiDAR land cover), NYC DoITT (building footprints), NYC DCP (MapPLUTO, NTA and council boundaries), NYC DEP (Stormwater Flood Maps), USGS (Landsat 9, via Google Earth Engine). Amsterdam: Kadaster / PDOK (BGT, BRK, boundaries), CBS (Wijk- en Buurtkaart), Gemeente Amsterdam (Beheerkaart, open geodata, BAG public space), Klimaateffectatlas (felt temperature and rainfall depth), RIVM / Atlas Natuurlijk Kapitaal (Bomenkaart 2022), DUO (school enrolment), NS and GVB (transit ridership), and OpenStreetMap contributors.

Suggested citation: Depave: A Screening Analysis of Removable Pavement and Environmental Need in New York City and Amsterdam. ONE Architecture & Urbanism, 2026.