Skip to content

About A landuse visualization web map for the canton of Basel-Stadt, based on the Kiezcolors project from ODIS Berlin and the Grätzlfarben project from TU Wien

License

Notifications You must be signed in to change notification settings

StataBS/quartierfarben

Repository files navigation

Quartierfarben, aka Grätzlfarben, aka Kiezcolors

Quartierfarben is a map based tool that creates a postcard showing the landuse distribution in your neighborhood in Basel-Stadt. It is based on the Kiezcolors tool of the Open Data Informationsstelle Berlin and the Grätzlfarben of the TU Wien. By zooming in and out you can pick a location and position it inside the circle. Quartierfarben then maps the individual areas onto a tree map diagram. You can print the resulting motive as a postcard and share it!

Tech stack

This website is a Svelte app. The map is rendered using MapLibre GL JS with vector tiles, and the analysis is done using Turf.js. The app is built as a fully static app and does not require any active server technology.

Developing

Install dependencies by running:

npm install

Start a development server by running:

npm run dev

or start the server and open the app in a new browser tab

npm run dev -- --open

Building

To create a production version of your app:

npm run build

You can preview the production build with npm run preview.

To deploy your app, simply copy the build folder to your web server.

Data Processing Pipeline & Tile-Creation

This repository does not include the generated tiles. Tiles for the date 03.02.2026 can be downloaded from an older repository, where tiles were generated daily using GitHub Actions. To generate tiles from the latest data, follow the steps below.


The data processing was done in Python and marimo.

Check it out on molab: Open in molab

Sources (WFS)

All datasets are retrieved from the Canton Basel-Stadt WFS using robust requests with retries and backoff.

Theme WFS layer(s) / prefix Key fields used
Bodenbedeckung (land cover) ms:BS_Bodenbedeckungen* bs_art_txt, gml_id, geometry
Gebäudekategorien DM_Gebaeudeinformationen_DatenmarktGebaeudekategorie gebaeudekategorieid, geometry
Öffentlicher Raum OR_OeffentlicherRaum_Allmend, OR_OeffentlicherRaum_Noerg geometry
Kultur (POI) BI_KulturUnterhaltung bi_subkategorie, geometry
Schulstandorte (Basel) ms:SC* sc_schultyp, geometry
Schulstandorte (Riehen/Bettingen) ms:SO* so_schultyp, geometry

Retrieval

  • Capabilities via OWSLib; features via GetFeature (preferring GeoJSON, falling back to GML).
  • Jittered requests + retries to avoid rate-limits/outages.
  • Working CRS for geometry ops: LV95 (EPSG:2056).

Pipeline (high-level)

  1. Bodenbedeckung base layer

    • Load all ms:BS_Bodenbedeckungen*.
    • Add a sequential laufnr (1-based) to ensure row-unique IDs.
  2. Attach building categories (Gebäudekategorien → Gebäude)

    • Filter buildings: bs_art_txt == "Gebaeude.Gebaeude".
    • sjoin(intersects) to attach gebaeudekategorieid.
    • If a building hits multiple categories, resolve by largest % area overlap (intersection area ÷ building area).
    • Map gebaeudekategorieid to German labels and construct nutzung = "Gebäude - <Label>".
  3. Classify public space for “übrige befestigte”

    • Dissolve OR_OeffentlicherRaum_* into a single geometry.
    • For bs_art_txt == "befestigt.uebrige_befestigte.uebrige_befestigte", compute coverage % inside public space.
    • Threshold = 50 % (configurable).
    • Add oeffentlicher_raum_pct and label öffentlicher Raum vs kein öffentlicher Raum.
    • For these features (only), if no building category applies, set
      nutzung = "befestigt - uebrige befestigte - <öffentlicher Raum|kein öffentlicher Raum>".
    • Otherwise (general fallback), nutzung = cleaned bs_art_txt (dots → -, underscores → space).
  4. Kultur overrides (nearest point → building)

    • From BI_KulturUnterhaltung points: nearest-building join in LV95.
    • Max distance = 50 m.
    • Where matched building exists, override
      nutzung = "Gebäude - <bi_subkategorie>".
  5. Schulstandorte overrides (points inside buildings only)

    • Prepare points:
      • Basel (ms:SC*): convert non-point geometries to centroids.
      • Riehen/Bettingen (ms:SO*): use as-is.
      • Build unified schultyp (sc_schultyp or so_schultyp).
      • Concatenate to gdf_schulstandorte.
    • Nearest-building join with MAX_DISTANCE = 0 m (point must lie inside the building).
    • Where matched, final override:
      nutzung = "Gebäude - <schultyp>".
    • (School overrides take precedence over Kultur where both apply.)
  6. Export

    • Reproject to WGS84 (EPSG:4326). Necessary for tippecanoe
    • Write landuse.geojson.

Tiles (Vector MBTiles/PMTiles directory)

Tiles are generated with tippecanoe. This command can vary on the size of your city/region.

tippecanoe \
  --output-to-directory ./static/tiles \
  --layer landuse-data \
  --force --no-tile-compression \
  --minimum-zoom=12 --maximum-zoom=17 \
  --full-detail=17 --low-detail=12 \
  --no-tiny-polygon-reduction \
  --detect-shared-borders \
  --extend-zooms-if-still-dropping \
  --no-feature-limit --no-tile-size-limit \
  ./{input-file}.geojson
  • Rebuilt daily at 05:00 UTC via GitHub Actions.
  • Uses uv to execute the marimo script (PEP-723 header drives Python & deps), then runs tippecanoe, and commits static/tiles/.

Notes & Limitations

  • WFS services may rate-limit or briefly refuse connections. The loader retries with backoff and polite jitter between layer requests.
  • Ambiguous building category joins are resolved by largest percent area, not absolute area; adjust to taste.
  • Distance thresholds: Kultur 50 m, Schulen 0 m (inside)—both configurable.
  • All spatial analysis in EPSG:2056; export in EPSG:4326 for tippecanoe.

Data Licence

The landuse data Bodenbedeckung and all other data used in this project can be downloaded in the Dataportal or via WFS from the Geopotal of the Canton of Basel-Stadt and is licenced under CC BY 4.0 + OpenStreetMap.

Adapting to your city

The application is built to be easily implemented in other cities if suitable data is available.

What to adapt Where
App name, map bounds, URLs, etc. src/lib/settings.js — at least: projectTitle, og_siteName, mapBounds, initialMapCenter, country, url
Categories and colors src/lib/colors.jsoncategories, palettes, and landuseMapping (see Land-use data below)
Area modes src/lib/cityConfig.js (see below)
Texts and labels src/locales/ — add keys under inputs for new area modes
Map outline on postcard src/lib/borders.js — a GeoJSON FeatureCollection (e.g. export default function () { return { type: "FeatureCollection", features: [...] }; })
Tiles and static assets static/ (tiles), plus any images

Area modes (circle, Wahlkreis, Wohnviertel, …)

Analysis modes are in src/lib/cityConfig.js. Circle mode (radius around a point) is always available; you can add polygon modes (e.g. districts, neighbourhoods) so users pick an area from a dropdown.

  • Circle only: Set areaModes to [{ id: "circle", labelKey: "useCircle", default: true }], set locationLabelPolygonModeId = null, and remove or comment out the import wahlkreiseData / import wohnviertelData lines (and any polygon entries in areaModes) so the file doesn’t reference missing data.
  • Circle + polygon modes: Keep the default. Each polygon mode needs id, labelKey, selectLabelKey, data (GeoJSON), idProperty, and nameProperty. Add matching keys in src/locales/*.json under inputs and a GeoJSON module per mode. Optionally set locationLabelPolygonModeId to the mode whose polygons supply the location name in circle mode (e.g. neighbourhood under the coordinates).

Land-use data

If you use your own land-use tiles (see Data & Processing for how Basel builds them): vector tiles must expose a property whose name is set in settings.js as landuseFieldname (Basel: "nutzung"). Values in that property are mapped to diagram categories via colors.jsonlanduseMapping; category labels and palette come from categories and palettes in the same file. The app expects the tile layer name used in your map style (e.g. landuse-data in the tippecanoe example).

Kiosk mode

For use in public settings, Quartierfarben can be run in "kiosk mode", which offers a single print button instead of download buttons for the postcard images. Only printing the postcard front (the treemap visualizaton) is supported in kiosk mode -- it is assumed that postcards pre-printed with the back side are provided on site.

To activate kiosk mode in the app, append the ?kiosk url parameter before the # sign in the url, for example:

opendatabs.github.io/quartierfarben?kiosk#13/48.20996/16.3704

You can start most browsers in kiosk mode, which causes the app to be displayed in full screen, disables any user interface elements, and supports printing without showing a dialog. E.g. for Firefox, the command to launch the app in kiosk mode would be:

"C:\Program Files\Mozilla Firefox\firefox.exe" -kiosk -private-window https://opendatabs.github.io/quartierfarben?kiosk

Contributing

Before you create a pull request, write an issue so we can discuss your changes.

Contributors

ODIS Berlin / CityLAB Berlin has made the biggest contribution by developing and coding the initial Kiezcolors tool. On part of the research unit cartography at TU Wien, Ester Scheck and Florian Ledermann mostly worked on the code and the documentation while Sacha Schlumpf and Andrea Binn supported with feedback and brainstorming ideas. In the Statistisches Amt Kanton Basel-Stadt Orhan Saeedi and Felix Michel adapted it for their use case.

Contact

If you have any questions, please contact opendata@bs.ch

Content Licensing

Texts and content available as CC BY 4.0.

Related Projects

Kiezcolors Berlin Grätzlfarben Wien

About

About A landuse visualization web map for the canton of Basel-Stadt, based on the Kiezcolors project from ODIS Berlin and the Grätzlfarben project from TU Wien

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 6