How it works › Methodology
Every empty building this project maps sits inside several boundaries at once — a municipality, an infrastructure asset's reach, a watershed. Which boundary you draw changes who has standing in a decision about it. This page is the full reasoning behind offering three lenses, and one real worked example per lens.
It also spells out the other number the map shows you: the 0–3 vacancy score on every building — every tag that produces it, and everything it does not claim.
← Back to /how unlocking-housing.orgWhy three lenses
The map's default view already draws a boundary: the political one. A building is "in" Eisenerz, or Bruck an der Mur, because a municipal register says so. That boundary is useful and legible, but it is also an artefact of a specific history of administration — Gemeinde lines get redrawn (the 2015 Styrian municipal-structure reform folded several of this project's own pilot towns together), and they say nothing about who is actually affected by a given piece of infrastructure running through or near a building.
So a second lens: infrastructure. Instead of "which municipality is this building in", ask "which piece of infrastructure reaches this building, and how strongly". A rail line, a power line, a district-heating main — each has a real physical impact envelope, and buildings inside it have a stake in decisions about it whether or not they sit in the same Gemeinde as the operator's headquarters.
But infrastructure boundaries are themselves political artefacts — built by one country's decisions and not its neighbour's, financed and routed by treaties and state railway companies, not by physics alone. The Semmeringbahn exists because the Austrian Empire built it in 1854; the Koralmtunnel exists because Austria's and the EU's TEN-T corridor planning decided it should. So a third lens, deliberately the least political of the three: bioregional. Watersheds and the Alpine main divide are boundaries that predate every government that has ever drawn a line near them, and will outlast the next redrawing too. They don't replace the other two lenses — they offer a reading of the same territory that doesn't inherit either a state's or a rail company's boundary-drawing power.
Lens 1 of 3
This is the lens the app already uses everywhere by default: the region registry in
@atlas/kernel's packages/kernel/src/regions.ts. Every
pilot area on the map — the Mur-Mürz corridor, Reichenau an der Rax, Vienna's Innere
Stadt, Villach, Udine, Trieste — is defined first as a political boundary (a
Gemeinde, a Bezirk, a Stadtgemeinde, a comune), verified against Nominatim
administrative-relation data, and buildings are attributed to a municipality by an
addr:city/addr:place match rule.
One of the five towns in the pilot corridor's own regions.ts municipality
list (/bruck/i → 'Bruck an der Mur'), and the corridor's own rail-junction
town — the Mur and Mürz rivers, and the rail lines that follow them, meet here.
Under the political lens, a building here has standing in decisions the Bruck
municipal government makes; a building 200m away across the Kapfenberg town line
does not, even if it is exposed to the exact same rail traffic.
Lens 2 of 3
Specced in DEVLOG/modularization/spec-modular-map-platform.md §3.1 as
the domain model for the (not-yet-built) Infrastructure Governance variant: an
infrastructure object — a rail line, road, power line, district-heating main, and so
on — carries an impactEnvelope (how far and in what way its effect
reaches), and every building inside that envelope gets an affectedness record and a
standing weight:
floor > 0 so any affected household has a voice; a cap so no single household dominates; burden and benefit are counted separately and never silently netted; permanence and irreversibility raise weight — a decision you cannot undo needs broader consent.
This is a real, already-coded polyline in this monorepo — not a hypothetical. It
reuses the historic Südbahn/Semmeringbahn alignment out of Vienna, then continues
via the Erzbergbahn branch to Eisenerz, and its waypoints are the same five towns
the political-lens corridor region names by municipality
(Eisenerz/Vordernberg/Leoben/Bruck/Kapfenberg) — under this lens, though, a building
counts as "reached" by distance to the rail line itself (10km, using the same
point-to-segment geometry — ptToSegDistM — that
rail-distance.ts already uses at a much tighter 50m threshold for
rail-adjacency on the live map), not by which Gemeinde it happens to sit in.
Honesty note: what exists in code today is the geometry — distance from a building
to this named axis. The standing-weight formula above (floor/cap/permanence) is
the spec-modular-map-platform.md §3.1 specification for the not-yet-built
Infrastructure Governance variant, not something this axis currently computes. The
axis is also marked honorary: true in corridor-axes.ts —
it is excluded from the main off-corridor scoring boost gate and only annotates
today, a smaller role than the on-axis worked example above implies for a fully
built infrastructure-governance lens.
Lens 3 of 3
packages/bioregions is a standalone package in this monorepo (own
SvelteKit app, own map, not yet wired into the main Unlocking Housing map's lens
switcher) carrying real watershed and Alpine-divide data: HydroSHEDS/HydroBASINS
sub-basins, a hand-traced Alpine main-divide (Alpenhauptkamm) ridge line verified
against those basin boundaries, and EEA biogeographical-region polygons.
The corridor pilot's five towns — Eisenerz, Vordernberg, Leoben, Bruck an der Mur, Kapfenberg — drain south into the Mur river, which the package's own seed data labels "Mur river basin — drains Styria southward through Slovenia into the Drava/Danube". That is the corridor's real bioregion, and it cuts across the same political boundaries and the same Wien–Eisenerz rail axis described above without matching either.
The contrast that makes the lens worth having: Reichenau an der Rax — another
Unlocking Housing pilot region, ~50km north of the corridor, still Alpine terrain,
still Austria — sits at the Alpine main divide's own eastern terminus. The divide
line in alps-divide.ts is hand-traced to end at the Rax massif
(47.72°N/15.73°E), described there as "the northeast terminus of the main Alpine
chain" and "the divide between Styria, Oberösterreich, and Niederösterreich".
Reichenau sits on the north-draining side of that divide — its water
reaches the Danube directly via the Schwarza river, not via the Mur. Two pilot
regions, both squarely Austrian, both Alpine, but in different bioregions — a
distinction neither the political lens nor the rail-axis lens above draws at all.
Honesty note: the package's EEA biogeographical-region seed file
(eea-bioregions-europe-seed.geojson, an offline fallback for when the
live EEA data portal is unreachable) is a coarse hand-authored approximation, not
the official EEA polygon service — checked pointwise, it currently classifies both
the corridor and Reichenau as "Continental" rather than "Alpine", which does not
match the terrain. That is a seed-data simplification we are naming rather than
quietly relying on. The watershed and Alps-divide worked example above is used
instead because those layers are hand-traced and verified against real HydroSHEDS
basin boundaries — the more precisely grounded data this package has today.
The number on every building
The score measures how strong the open-data signal is that a building might be empty. It never measures that it is empty. Nothing in it observes vacancy directly — the registers that would (the GWR dwelling register, the ZMR residents register) are closed to non-authorities.
Every OpenStreetMap-derived building on the map carries a whole number from 0 to 3 and a
fixed plain-language label, computed by the app's own scoring pass
(loadVacancyBuildings in
packages/vacancy/src/lib/vacancy-api.ts) and clamped to that range. Three
paths lead to a score, checked in order — and the first one that matches wins outright.
| Score | Label the app shows | Example paths to this score |
|---|---|---|
| 3 / 3 | High vacancy signal | Any explicit disuse or lifecycle tag (the two blocks below) — or, where zoning data exists at all, a residential building inside a tagged residential area that also sits off the investor corridors |
| 2 / 3 | Moderate vacancy signal | A residential building inside a tagged residential area — or any building that picked up one point for its type or surroundings and a second for sitting off the investor corridors |
| 1 / 3 | Weak vacancy signal | A residential building where no zoning data was ever loaded — or a non-residential building that happens to sit inside a tagged residential area |
| 0 / 3 | No vacancy signals | Everything else: no disuse tag, not a residential building type, no zoning data. This is the great majority of footprints on the map. |
Two signals that score 3 on their own
These two are checked first and never combined with anything else — either one takes the building straight to 3, and no further points are added or removed. They are kept apart from each other in the code and in the map popup's wording, because their provenance genuinely differs: one is a record that the former use is gone, the other that the building is standing but idle.
demolished:*
was:*
removed:*
razed:*
Any OSM key carrying one of these four lifecycle prefixes. Where a was:*
tag is present, the former use is carried through to the popup as written — e.g.
was:amenity=restaurant reads back as "former use: amenity=restaurant".
historic:* is excluded on purpose. OSM's own wiki calls it
misleading for lifecycle tagging, and in practice it is overwhelmingly a heritage or
period-of-construction tag (historic:period,
historic:railway) — a listed, fully occupied townhouse carries it happily.
It is not a disuse signal and is never read as one here.
disused=yes · disused=building
abandoned=yes
ruins=yes · building=ruins
any disused: / abandoned: / ruins: prefixed key
The prefixed form matters as much as the bare one. Once a feature is re-tagged
abandoned:building=industrial, the plain building key is
frequently dropped from the object altogether — a check that only read bare tags would
not merely misscore such a building, it would never see it at all. Whichever tag
actually fired is the one named in the popup, never a generic stand-in.
Everything else: how the points add up
A building with no disuse and no lifecycle tag takes the third path, and only here are points genuinely additive: one building-type-and-zoning component, plus at most a single corridor point.
| Component | Condition | Points |
|---|---|---|
| Building type & zoning | A residential building type (apartments, residential, house, detached, semidetached_house) whose centroid falls inside an OSM-tagged residential area |
+2 |
| Building type & zoning | A residential building type outside any tagged residential area — or, far more often, in a region where no zoning data was ever loaded | +1 |
| Building type & zoning | A non-residential building type that happens to sit inside a tagged residential area | +1 |
| Building type & zoning | A non-residential building type with no tagged residential area around it | +0 |
| Investor corridor | Off all major investor corridors (Vienna–Graz, Westbahn, Trans-Alpine) — structurally below developer attention — and the building has already scored above 0 on the component above | +1 |
| Investor corridor | On a high-value corridor; or the building scored 0 so far; or the region does not use corridor scoring at all | +0 |
So this third path tops out at 3, and only as 2 + 1: a residential building inside tagged residential land that is also off-corridor. The corridor point is deliberately gated on the building already having scored something — an isolated shed in the middle of nowhere does not become a vacancy candidate merely by being remote.
What the score does not mean. The app itself puts it this way, and this page says nothing stronger: the vacancy score combines open-data signals, score 3 is the strongest combination available, and it does not confirm vacancy — field verification is always needed. Concretely:
building=ruins and disused=yes
land in the same branch. That distinction lives in a separate field — a move-in
readiness tier running from "disused but usable" down to "ruin, outside this
project's scope" — which is shown alongside the score and never folded into it.One more score, and a different one. The sortable Table view ranks buildings by a second, unrelated number: the renovation-opportunity score, a composite of five weighted components summing to 100 — space, a renovation-price proxy, quietness, nature access, and access — plus additive bonuses for nearby amenities and for move-in readiness that push its ceiling above 100 (125 at the app's default weights, and the app labels each score against the actual maximum rather than a fixed 100). It answers "how attractive is this as a project", not "is it empty", and the vacancy score above is deliberately excluded from every one of its components so the same signals are never counted twice. Its price-proxy component is exactly what its name says: it infers "likely cheaper to acquire" from disuse and building-typology tags — Kaserne, Kloster, Gasthaus, Fabrik and ten more — and uses no real price data whatsoever. It is not a valuation.
Provenance and honesty
Same provenance rule as the rest of this site: every boundary shown carries its source and its confidence, and a lens that is speculative or seed-data-only says so, rather than presenting itself as measured fact.
All three lenses answer the same underlying question differently: "who has a stake in this building, and by what right." None of the three is neutral. Political boundaries encode administrative history; infrastructure envelopes encode which asset and whose money built it; even bioregional boundaries encode a choice of dataset resolution and a divide line someone still had to trace. The bioregional lens is offered as less political, not apolitical — it is simply the frame whose boundary-drawing power belongs to neither a state nor a company.
Current status: political lens live and default; infrastructure lens's geometry (corridor axes, rail-adjacency) live, its standing-weight model spec-only; bioregional lens data real but package-standalone, not yet merged into the main map.