UrbanViable is a client-side commercial scouting platform for Galicia (Spain). It lets retailers, real-estate analysts, and urban planners evaluate every one of the ~3,800 Galician census sections at a glance by combining socioeconomic data — income, demographics, commercial activity, and building stock — into a single weighted score rendered as a live heatmap. Adjusting the indicator sliders in the sidebar instantly repaints the map; no network round-trip is ever needed because every score is calculated directly on the user’s GPU.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/danizd/urbanviable/llms.txt
Use this file to discover all available pages before exploring further.
Zero-backend scoring model
UrbanViable has no server-side scoring API. All score computation is
performed by MapLibre GL JS paint expressions that run on the client GPU.
When a user moves a slider, React rebuilds a MapLibre expression array with
updated weight literals and calls
map.setPaintProperty(...). The GPU then
recalculates score = Σ(var_norm × peso) / Σ(pesos_activos) for all ~3,800
polygons in under 16 ms — keeping the heatmap at 60 fps with zero requests
to the server.- ETL pipeline (runs once, locally) — downloads raw data, normalises every indicator to
[0, 1], and writes the result to a static.mbtilesfile. - TileServer GL (Docker container) — streams pre-computed vector tiles (
.pbf) to the browser on demand. - MapLibre GL JS (browser / GPU) — receives raw normalised values inside the tile features and applies user-defined weights at paint time. No score is ever stored; it is always derived at the moment of rendering.
_norm is a Min-Max normalised [0, 1] float ready for GPU consumption; columns ending in _abs carry the raw absolute value shown in tooltips.
Weighted indicators
UrbanViable exposes seven indicators, each mapped to a sidebar slider. Default weights are defined infrontend/src/constants/variables.js.
| Variable | Default weight | Data source | Description |
|---|---|---|---|
renta_norm | 0.20 | INE — Atlas de Renta de los Hogares | Normalised mean net household income per census section |
densidad_norm | 0.00 | IGE — Padrón Municipal | Population density (inhabitants / km²) — disabled by default, no data available |
jovenes_norm | 0.20 | IGE — Padrón Municipal | Proportion of population under 20 years old |
mayores_norm | 0.20 | IGE — Padrón Municipal | Proportion of population over 64 years old |
actividad_norm | 0.20 | OpenStreetMap (osmium) | Density of points of interest (shops, restaurants, offices) — log-scaled |
uso_comercial_norm | 0.10 | Catastro — CONSTRU dataset | Ratio of commercial/industrial buildings to residential buildings |
antiguedad_norm | 0.10 | Catastro — CONSTRU dataset | Mean building age, inverted so newer stock scores higher |
_norm columns are written by the ETL using Min-Max normalisation across all Galician sections, so the values are directly comparable and always sum to a score in [0, 1].
ETL and tile pipeline overview
The data pipeline runs outside Docker on a developer workstation and produces a single artifact —galicia_scouting.mbtiles — that is then deployed to the server.
- Extract — Python downloads the CNIG census-section shapefile (EPSG:25829), the INE household income atlas (Excel), and the INE municipal register (CSV). OSM data is processed with
osmiumto derive POI counts per section, and Catastro’sCONSTRUdataset provides building use and age. - Transform — GeoPandas filters the four Galician provinces (codes 15, 27, 32, 36), reprojects geometries to WGS84 (EPSG:4326), joins all tables on the 10-digit
CUSECfield, calculates population density, applies Min-Max normalisation, and exports exactly seven_normcolumns plus their_abscounterparts toetl/data/processed/galicia_scouting.geojson. - Tile generation — Tippecanoe converts the GeoJSON to a
.mbtilesfile covering zoom levels 6–14 in a single layer namedsecciones. The resulting file is 15–40 MB. - Deployment — The
.mbtilesfile is copied to./tiles_data/and TileServer GL picks it up without a restart.
last_update.json manifest so the frontend can display the data vintage to users.
Data sources
| Dataset | Provider | Format | Update cadence |
|---|---|---|---|
| Census section geometries | CNIG (Centro Nacional de Información Geográfica) | Shapefile (ZIP, EPSG:25829) | Annual |
| Household income atlas | INE (Instituto Nacional de Estadística) | Excel | Annual (~1 year lag) |
| Municipal register | INE — Padrón Municipal | CSV | Annual |
| Points of interest | OpenStreetMap via osmium | PBF extract | On demand |
| Building use and age | Catastro — CONSTRU | CSV | Annual |
Explore further
Quickstart
Install dependencies, run the ETL, generate tiles, and launch UrbanViable
locally in four steps.
Architecture
Two-container Docker setup, ETL pipeline detail, and the full user
interaction flow from slider move to GPU colour change.
ETL Overview
Detailed walkthrough of every transformation step, column contracts, and
the Tippecanoe tile-generation configuration.
Frontend Overview
MapLibre GL JS integration, the
useMapStyle hook, the paint expression
builder, and the sidebar slider component.