banyaro/backend/static/js/map-gl-style.js
rene cc1fdb00b1 GL-Style: kräftigere Schrift (Open Sans Semibold, self-hosted), sattere Farben, Bahntrassen
- Labels + Cluster-Zahlen auf Open Sans Semibold (Glyphs gehostet) — Schrift war zu dünn
- Farben gesättigt: Grün/Park/Wasser kräftiger, Füll-Deckkraft 0.55→0.8 (wirkten blass)
- Bahn-Layer (class rail/transit): Basis-Linie + Schwellen-Effekt (fehlten ganz)
2026-06-05 11:47:52 +02:00

120 lines
7.5 KiB
JavaScript

// MapLibre-GL-Style für die zentrale Karte — gerendert aus unseren DACH-PMTiles
// (OpenMapTiles-Schema). GPU + Worker → performant auf dem Handy (Ziel der Migration).
// GEOMETRIE-ONLY (keine Symbol/Text-Layer) → KEINE Glyphs/Fonts nötig für den ersten
// Perf-Test. Labels (mit Glyph-Hosting) kommen in M3, wenn die Performance steht.
(function () {
'use strict';
var TILES_FILE = 'dach.pmtiles';
function tilesUrl() { return window.location.origin + '/tiles/' + TILES_FILE; }
var THEMES = {
light: {
bg: '#f2efe8', land: '#cbe3a8', park: '#aedd88', water: '#7fbbe8',
road: '#ffffff', roadCasing: '#cdbfa9', building: '#e6d8bf',
buildingLine: '#cdbb9c', boundary: '#a06ec0', path: '#b08160', rail: '#9a9aa2',
label: '#2a2823', roadLabel: '#574f43', waterLabel: '#2f6aa0', poiLabel: '#4a4236', labelHalo: 'rgba(255,255,255,0.95)',
},
dark: {
bg: '#1a1d21', land: '#252e1d', park: '#2c3c1f', water: '#163242',
road: '#444a52', roadCasing: '#23282d', building: '#2a2f35',
buildingLine: '#373d44', boundary: '#8a63a0', path: '#6b5d52', rail: '#5e5e68',
label: '#e2e5e9', roadLabel: '#a6acb3', waterLabel: '#7db0dd', poiLabel: '#c3b9a8', labelHalo: 'rgba(0,0,0,0.85)',
},
};
var FONT = ['Open Sans Regular'];
var FONT_BOLD = ['Open Sans Semibold'];
// Liefert ein MapLibre-Style-JSON (Version 8) ohne glyphs/sprite.
function build(opts) {
opts = opts || {};
var t = THEMES[opts.dark ? 'dark' : 'light'];
return {
version: 8,
glyphs: window.location.origin + '/fonts/{fontstack}/{range}.pbf',
sources: {
by: { type: 'vector', url: 'pmtiles://' + tilesUrl() },
},
layers: [
{ id: 'bg', type: 'background', paint: { 'background-color': t.bg } },
{ id: 'landcover', type: 'fill', source: 'by', 'source-layer': 'landcover',
paint: { 'fill-color': t.land, 'fill-opacity': 0.8 } },
{ id: 'park', type: 'fill', source: 'by', 'source-layer': 'park',
paint: { 'fill-color': t.park, 'fill-opacity': 0.75 } },
{ id: 'water', type: 'fill', source: 'by', 'source-layer': 'water',
paint: { 'fill-color': t.water } },
{ id: 'waterway', type: 'line', source: 'by', 'source-layer': 'waterway',
paint: { 'line-color': t.water, 'line-width': 1 } },
// Pfade/Wege/Tracks: dünn + gestrichelt (NICHT wie Straßen).
{ id: 'paths', type: 'line', source: 'by', 'source-layer': 'transportation', minzoom: 13,
filter: ['in', ['get', 'class'], ['literal', ['path', 'track']]],
paint: { 'line-color': t.path, 'line-dasharray': [1.8, 1.8],
'line-width': ['interpolate', ['linear'], ['zoom'], 13, 0.6, 16, 1.2, 19, 2] } },
// Straßen-Casing (nur echte Straßen, Breite nach Klasse).
{ id: 'road-casing', type: 'line', source: 'by', 'source-layer': 'transportation', minzoom: 11,
filter: ['!', ['in', ['get', 'class'], ['literal', ['path', 'track', 'ferry', 'rail', 'transit', 'aerialway']]]],
paint: { 'line-color': t.roadCasing,
'line-width': ['interpolate', ['linear'], ['zoom'],
11, ['match', ['get', 'class'], ['motorway', 'trunk'], 3, ['primary', 'secondary'], 2.2, 1.6],
16, ['match', ['get', 'class'], ['motorway', 'trunk'], 8, ['primary', 'secondary'], 6, 4.5]] } },
// Straßen-Füllung.
{ id: 'roads', type: 'line', source: 'by', 'source-layer': 'transportation',
filter: ['!', ['in', ['get', 'class'], ['literal', ['path', 'track', 'ferry', 'rail', 'transit', 'aerialway']]]],
paint: { 'line-color': t.road,
'line-width': ['interpolate', ['linear'], ['zoom'],
6, ['match', ['get', 'class'], ['motorway', 'trunk'], 1.4, 0.4],
12, ['match', ['get', 'class'], ['motorway', 'trunk', 'primary'], 2.4, 1.1],
16, ['match', ['get', 'class'], ['motorway', 'trunk'], 6, ['primary', 'secondary'], 4.5, 3]] } },
// Bahntrassen: Basis-Linie + Schwellen (dicke gestrichelte Überlagerung).
{ id: 'railway', type: 'line', source: 'by', 'source-layer': 'transportation', minzoom: 11,
filter: ['in', ['get', 'class'], ['literal', ['rail', 'transit']]],
paint: { 'line-color': t.rail, 'line-width': ['interpolate', ['linear'], ['zoom'], 11, 0.8, 16, 2] } },
{ id: 'railway-ties', type: 'line', source: 'by', 'source-layer': 'transportation', minzoom: 13,
filter: ['in', ['get', 'class'], ['literal', ['rail', 'transit']]],
paint: { 'line-color': t.rail, 'line-dasharray': [0.35, 3],
'line-width': ['interpolate', ['linear'], ['zoom'], 13, 3, 16, 6] } },
{ id: 'buildings', type: 'fill', source: 'by', 'source-layer': 'building',
minzoom: 13,
paint: { 'fill-color': t.building, 'fill-outline-color': t.buildingLine } },
{ id: 'boundary', type: 'line', source: 'by', 'source-layer': 'boundary',
paint: { 'line-color': t.boundary, 'line-dasharray': [2, 2], 'line-width': 1 } },
// ---- Labels (brauchen glyphs). Reihenfolge = Kollisions-Priorität (zuerst = wichtiger). ----
// Ortsnamen (Städte/Dörfer) zuerst — höchste Priorität, auch bei kleinem Zoom.
{ id: 'place-labels', type: 'symbol', source: 'by', 'source-layer': 'place',
filter: ['in', ['get', 'class'], ['literal', ['city', 'town', 'village', 'suburb', 'hamlet', 'neighbourhood']]],
layout: {
'text-field': ['coalesce', ['get', 'name:de'], ['get', 'name']],
'text-font': FONT_BOLD, 'text-max-width': 8, 'text-anchor': 'center',
'text-size': ['interpolate', ['linear'], ['zoom'], 4, 10, 8, 12, 12, 14, 16, 17],
},
paint: { 'text-color': t.label, 'text-halo-color': t.labelHalo, 'text-halo-width': 1.6 } },
{ id: 'water-labels', type: 'symbol', source: 'by', 'source-layer': 'water_name',
layout: { 'text-field': ['coalesce', ['get', 'name:de'], ['get', 'name']], 'text-font': FONT_BOLD, 'text-size': 12, 'text-max-width': 6 },
paint: { 'text-color': t.waterLabel, 'text-halo-color': t.labelHalo, 'text-halo-width': 1.2 } },
{ id: 'street-labels', type: 'symbol', source: 'by', 'source-layer': 'transportation_name', minzoom: 14,
layout: { 'text-field': ['coalesce', ['get', 'name:de'], ['get', 'name']], 'text-font': FONT_BOLD, 'symbol-placement': 'line', 'text-size': 11 },
paint: { 'text-color': t.roadLabel, 'text-halo-color': t.labelHalo, 'text-halo-width': 1.2 } },
// POI-Namen (Kinderspielplatz, Schule, …) ab Z15 — Kollisionserkennung verhindert Überladung.
{ id: 'poi-labels', type: 'symbol', source: 'by', 'source-layer': 'poi', minzoom: 15,
layout: {
'text-field': ['coalesce', ['get', 'name:de'], ['get', 'name']],
'text-font': FONT_BOLD, 'text-size': 11, 'text-max-width': 8,
'text-anchor': 'top', 'text-offset': [0, 0.4], 'symbol-sort-key': ['get', 'rank'],
},
paint: { 'text-color': t.poiLabel, 'text-halo-color': t.labelHalo, 'text-halo-width': 1.2 } },
// Hausnummern ab Z17 (niedrigste Priorität).
{ id: 'housenumbers', type: 'symbol', source: 'by', 'source-layer': 'housenumber', minzoom: 17,
layout: { 'text-field': ['get', 'housenumber'], 'text-font': FONT_BOLD, 'text-size': 9.5 },
paint: { 'text-color': t.roadLabel, 'text-halo-color': t.labelHalo, 'text-halo-width': 1 } },
],
};
}
window.MapGLStyle = { build: build, tilesUrl: tilesUrl, tilesFile: TILES_FILE };
})();