Skip to main content

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.

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.

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.
The division of responsibility is clean:
  • ETL pipeline (runs once, locally) — downloads raw data, normalises every indicator to [0, 1], and writes the result to a static .mbtiles file.
  • 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.
The column names in the vector tiles are the contract between the ETL and the frontend. Any column ending in _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 in frontend/src/constants/variables.js.
VariableDefault weightData sourceDescription
renta_norm0.20INE — Atlas de Renta de los HogaresNormalised mean net household income per census section
densidad_norm0.00IGE — Padrón MunicipalPopulation density (inhabitants / km²) — disabled by default, no data available
jovenes_norm0.20IGE — Padrón MunicipalProportion of population under 20 years old
mayores_norm0.20IGE — Padrón MunicipalProportion of population over 64 years old
actividad_norm0.20OpenStreetMap (osmium)Density of points of interest (shops, restaurants, offices) — log-scaled
uso_comercial_norm0.10Catastro — CONSTRU datasetRatio of commercial/industrial buildings to residential buildings
antiguedad_norm0.10Catastro — CONSTRU datasetMean building age, inverted so newer stock scores higher
All seven _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.
  1. 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 osmium to derive POI counts per section, and Catastro’s CONSTRU dataset provides building use and age.
  2. 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 CUSEC field, calculates population density, applies Min-Max normalisation, and exports exactly seven _norm columns plus their _abs counterparts to etl/data/processed/galicia_scouting.geojson.
  3. Tile generation — Tippecanoe converts the GeoJSON to a .mbtiles file covering zoom levels 6–14 in a single layer named secciones. The resulting file is 15–40 MB.
  4. Deployment — The .mbtiles file is copied to ./tiles_data/ and TileServer GL picks it up without a restart.
Data is updated annually (INE figures carry roughly one year of lag). The ETL writes a last_update.json manifest so the frontend can display the data vintage to users.

Data sources

DatasetProviderFormatUpdate cadence
Census section geometriesCNIG (Centro Nacional de Información Geográfica)Shapefile (ZIP, EPSG:25829)Annual
Household income atlasINE (Instituto Nacional de Estadística)ExcelAnnual (~1 year lag)
Municipal registerINE — Padrón MunicipalCSVAnnual
Points of interestOpenStreetMap via osmiumPBF extractOn demand
Building use and ageCatastro — CONSTRUCSVAnnual

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.

Build docs developers (and LLMs) love