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.

Once process_data.py has written galicia_scouting.geojson, the final ETL step converts it into a binary MBTiles file that TileServer GL can serve as a Mapbox Vector Tile (MVT) endpoint. This conversion is done by Tippecanoe, a CLI tool by Felt (formerly Mapbox) that tiles GeoJSON into a spatially indexed, multi-zoom-level binary format. The whole step takes under two minutes for a typical Galicia GeoJSON.

Command

From the project root:
bash etl/generate_tiles.sh
The script checks that galicia_scouting.geojson exists and exits with an error message if it does not, preventing accidental overwrite of a previously valid MBTiles file.

generate_tiles.sh (full source)

#!/bin/sh
set -eu

INPUT="etl/data/processed/galicia_scouting.geojson"
OUTPUT="etl/data/processed/galicia_scouting.mbtiles"
LAYER_NAME="secciones"

if [ ! -f "$INPUT" ]; then
  echo "ERROR: $INPUT no existe. Ejecuta process_data.py primero."
  exit 1
fi

rm -f "$OUTPUT"

tippecanoe \
  --output="$OUTPUT" \
  --layer="$LAYER_NAME" \
  --minimum-zoom=6 \
  --maximum-zoom=14 \
  --coalesce-densest-as-needed \
  --extend-zooms-if-still-dropping \
  --simplification=2 \
  --include=cusec \
  --include=renta_norm \
  --include=renta_abs \
  --include=densidad_norm \
  --include=jovenes_norm \
  --include=mayores_norm \
  --include=poblacion_abs \
  --include=actividad_norm \
  --include=actividad_abs \
  --include=uso_comercial_norm \
  --include=antiguedad_norm \
  "$INPUT"

echo "Teselas generadas: $OUTPUT"

Tippecanoe parameter reference

ParameterValueRationale
--outputgalicia_scouting.mbtilesDestination file path. Deleted first with rm -f to avoid corrupt state on re-runs.
--layer"secciones"Critical. The internal vector layer name embedded in the MBTiles. Must match source-layer: "secciones" in MapViewer.jsx and REACT_APP_LAYER_NAME in .env.
--minimum-zoom6Zoom 6 shows all of Galicia in a single viewport — the broadest useful view.
--maximum-zoom14Street-level detail. Beyond zoom 14 the census polygons contain no additional spatial information.
--coalesce-densest-as-needed(flag)Merges the densest adjacent polygons when a tile exceeds the size limit. Correct for polygon data — see warning below.
--extend-zooms-if-still-dropping(flag)Automatically increases the effective maximum zoom if features are still being dropped at zoom 14. Prevents data loss without requiring a hard-coded higher maximum.
--simplification2Mild Douglas-Peucker geometric simplification. Reduces tile size without visibly degrading census polygon borders at any zoom level in the range 6–14.
--include=cusec(repeated)Explicit allowlist of properties to embed in tiles. Excludes all intermediate ETL columns (area_km2, densidad, ratio_comercial, etc.), reducing tile size by up to 60%.
--include=renta_norm(×11)One flag per column in the output contract. Must stay in sync with COLS_OUTPUT in process_data.py.
The --layer name must be "secciones".
This string is hardcoded in MapViewer.jsx as the source-layer property of the MapLibre GL fill layer. If you rename the layer here, the map will render no polygons and no error will appear in the browser — the source will simply return zero features. Always verify after a rename with the curl check below.
--coalesce-densest-as-needed is the correct flag for polygon data. Its counterpart --drop-densest-as-needed silently removes the densest features from overcrowded tiles — appropriate for point datasets where individual POIs can be sacrificed. For census section polygons, dropping features would leave gaps in the choropleth map. --coalesce-densest-as-needed instead merges adjacent dense polygons, which preserves full coverage at every zoom level.

Expected output size

ScenarioApproximate MBTiles size
Sources 1 + 2 only (MVP)15–20 MB
All four sources25–40 MB
# Check the output size before uploading
du -sh etl/data/processed/galicia_scouting.mbtiles

Deployment after tile generation

After the MBTiles file is generated, upload it (and the metadata file) to the server with scp. TileServer GL detects the updated file without requiring a container restart:
# 1. Upload tiles and metadata
scp etl/data/processed/galicia_scouting.mbtiles  user@server:~/urbanviable/tiles_data/
scp etl/data/processed/last_update.json          user@server:~/urbanviable/tiles_data/

# 2. (Optional) Verify TileServer GL is serving the new file
curl https://your-domain.com/tiles/galicia-scouting.json | python3 -m json.tool
The curl command fetches the TileJSON descriptor that TileServer GL auto-generates for every MBTiles file it finds. A valid response looks like:
{
  "tilejson": "2.2.0",
  "name": "galicia_scouting",
  "format": "pbf",
  "minzoom": 6,
  "maxzoom": 14,
  "vector_layers": [
    {
      "id": "secciones",
      "fields": {
        "cusec": "String",
        "renta_norm": "Number",
        "renta_abs": "Number",
        "densidad_norm": "Number",
        "jovenes_norm": "Number",
        "mayores_norm": "Number",
        "poblacion_abs": "Number",
        "actividad_norm": "Number",
        "actividad_abs": "Number",
        "uso_comercial_norm": "Number",
        "antiguedad_norm": "Number"
      }
    }
  ]
}
Confirm that vector_layers[0].id equals "secciones" and that all 11 fields are listed. If any field is missing, re-check the corresponding --include flag in generate_tiles.sh and ensure the column exists in galicia_scouting.geojson.

Build docs developers (and LLMs) love