/** * MapLibre Style Configurations for GeoEdu Service * Styles for displaying OSM data with terrain */ import { MapStyle, MapLayer } from '@/app/geo-lernwelt/types' // Germany bounds export const GERMANY_BOUNDS: [[number, number], [number, number]] = [ [5.87, 47.27], [15.04, 55.06], ] // Default center (Germany) export const GERMANY_CENTER: [number, number] = [10.45, 51.16] // Mainau island (demo location) export const MAINAU_CENTER: [number, number] = [9.1925, 47.7085] export const MAINAU_BOUNDS: [[number, number], [number, number]] = [ [9.185, 47.705], [9.200, 47.712], ] /** * Create a MapLibre style for the self-hosted tile server */ export function createMapStyle(geoServiceUrl: string): MapStyle { return { version: 8, name: 'GeoEdu Germany', metadata: { description: 'Self-hosted OSM tiles for DSGVO-compliant education', attribution: '© OpenStreetMap contributors', }, sources: { osm: { type: 'vector', tiles: [`${geoServiceUrl}/api/v1/tiles/{z}/{x}/{y}.pbf`], minzoom: 0, maxzoom: 14, attribution: '© OpenStreetMap contributors (ODbL)', }, terrain: { type: 'raster-dem', tiles: [`${geoServiceUrl}/api/v1/terrain/{z}/{x}/{y}.png`], tileSize: 256, attribution: '© Copernicus DEM GLO-30', }, hillshade: { type: 'raster', tiles: [`${geoServiceUrl}/api/v1/terrain/hillshade/{z}/{x}/{y}.png`], tileSize: 256, }, }, layers: [ // Background { id: 'background', type: 'background', paint: { 'background-color': '#f8f4f0' }, }, // Hillshade { id: 'hillshade', type: 'raster', source: 'hillshade', paint: { 'raster-opacity': 0.3 }, }, // Water areas { id: 'water', type: 'fill', source: 'osm', 'source-layer': 'water', paint: { 'fill-color': '#a0c8f0' }, }, // Parks { id: 'landuse-park', type: 'fill', source: 'osm', 'source-layer': 'landuse', filter: ['==', 'class', 'park'], paint: { 'fill-color': '#c8e6c8', 'fill-opacity': 0.5 }, }, // Forest { id: 'landuse-forest', type: 'fill', source: 'osm', 'source-layer': 'landuse', filter: ['==', 'class', 'wood'], paint: { 'fill-color': '#94d294', 'fill-opacity': 0.5 }, }, // Buildings { id: 'building', type: 'fill', source: 'osm', 'source-layer': 'building', minzoom: 13, paint: { 'fill-color': '#d9d0c9', 'fill-opacity': 0.8, }, }, // Building outlines { id: 'building-outline', type: 'line', source: 'osm', 'source-layer': 'building', minzoom: 13, paint: { 'line-color': '#b8a89a', 'line-width': 1, }, }, // Minor roads { id: 'road-minor', type: 'line', source: 'osm', 'source-layer': 'transportation', filter: ['all', ['==', '$type', 'LineString'], ['in', 'class', 'minor', 'service']], paint: { 'line-color': '#ffffff', 'line-width': ['interpolate', ['linear'], ['zoom'], 10, 0.5, 14, 2], }, }, // Secondary roads { id: 'road-secondary', type: 'line', source: 'osm', 'source-layer': 'transportation', filter: ['all', ['==', '$type', 'LineString'], ['in', 'class', 'secondary', 'tertiary']], paint: { 'line-color': '#ffc107', 'line-width': ['interpolate', ['linear'], ['zoom'], 8, 1, 14, 4], }, }, // Primary roads { id: 'road-primary', type: 'line', source: 'osm', 'source-layer': 'transportation', filter: ['all', ['==', '$type', 'LineString'], ['==', 'class', 'primary']], paint: { 'line-color': '#ff9800', 'line-width': ['interpolate', ['linear'], ['zoom'], 6, 1, 14, 6], }, }, // Highways { id: 'road-highway', type: 'line', source: 'osm', 'source-layer': 'transportation', filter: ['all', ['==', '$type', 'LineString'], ['==', 'class', 'motorway']], paint: { 'line-color': '#ff6f00', 'line-width': ['interpolate', ['linear'], ['zoom'], 4, 1, 14, 8], }, }, // Railways { id: 'railway', type: 'line', source: 'osm', 'source-layer': 'transportation', filter: ['==', 'class', 'rail'], paint: { 'line-color': '#555555', 'line-width': 2, 'line-dasharray': [3, 3], }, }, // Water lines (rivers, streams) { id: 'waterway', type: 'line', source: 'osm', 'source-layer': 'waterway', paint: { 'line-color': '#a0c8f0', 'line-width': ['interpolate', ['linear'], ['zoom'], 10, 1, 14, 3], }, }, // Place labels { id: 'place-label-city', type: 'symbol', source: 'osm', 'source-layer': 'place', filter: ['==', 'class', 'city'], layout: { 'text-field': '{name}', 'text-font': ['Open Sans Bold'], 'text-size': 16, }, paint: { 'text-color': '#333333', 'text-halo-color': '#ffffff', 'text-halo-width': 2, }, }, { id: 'place-label-town', type: 'symbol', source: 'osm', 'source-layer': 'place', filter: ['==', 'class', 'town'], minzoom: 8, layout: { 'text-field': '{name}', 'text-font': ['Open Sans Semibold'], 'text-size': 14, }, paint: { 'text-color': '#444444', 'text-halo-color': '#ffffff', 'text-halo-width': 1.5, }, }, { id: 'place-label-village', type: 'symbol', source: 'osm', 'source-layer': 'place', filter: ['==', 'class', 'village'], minzoom: 10, layout: { 'text-field': '{name}', 'text-font': ['Open Sans Regular'], 'text-size': 12, }, paint: { 'text-color': '#555555', 'text-halo-color': '#ffffff', 'text-halo-width': 1, }, }, ], terrain: { source: 'terrain', exaggeration: 1.5, }, } } /** * Fallback style using OSM raster tiles (when self-hosted tiles not available) */ export function createFallbackStyle(): MapStyle { return { version: 8, name: 'OSM Fallback', metadata: { description: 'Fallback style using public OSM raster tiles', attribution: '© OpenStreetMap contributors', }, sources: { osm: { type: 'raster', tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], tileSize: 256, attribution: '© OpenStreetMap contributors', }, }, layers: [ { id: 'osm-tiles', type: 'raster', source: 'osm', minzoom: 0, maxzoom: 19, }, ], } } /** * Dark mode variant of the map style */ export function createDarkStyle(geoServiceUrl: string): MapStyle { const baseStyle = createMapStyle(geoServiceUrl) return { ...baseStyle, name: 'GeoEdu Germany (Dark)', layers: baseStyle.layers.map((layer: MapLayer) => { // Adjust colors for dark mode if (layer.id === 'background') { return { ...layer, paint: { 'background-color': '#1a1a2e' } } } if (layer.id === 'water') { return { ...layer, paint: { 'fill-color': '#1e3a5f' } } } if (layer.type === 'symbol') { return { ...layer, paint: { ...layer.paint, 'text-color': '#e0e0e0', 'text-halo-color': '#1a1a2e', }, } } return layer }), } } /** * Terrain-focused style (for topography theme) */ export function createTerrainStyle(geoServiceUrl: string): MapStyle { const baseStyle = createMapStyle(geoServiceUrl) // Add contour lines source and layer return { ...baseStyle, name: 'GeoEdu Terrain', sources: { ...baseStyle.sources, contours: { type: 'vector', tiles: [`${geoServiceUrl}/api/v1/terrain/contours/{z}/{x}/{y}.pbf`], maxzoom: 14, }, }, layers: [ ...baseStyle.layers, { id: 'contour-lines', type: 'line', source: 'contours', 'source-layer': 'contour', minzoom: 10, paint: { 'line-color': '#8b4513', 'line-width': ['match', ['get', 'index'], 5, 1.5, 0.5], 'line-opacity': 0.5, }, }, { id: 'contour-labels', type: 'symbol', source: 'contours', 'source-layer': 'contour', minzoom: 12, filter: ['==', ['%', ['get', 'ele'], 50], 0], layout: { 'text-field': '{ele}m', 'text-font': ['Open Sans Regular'], 'text-size': 10, 'symbol-placement': 'line', }, paint: { 'text-color': '#8b4513', 'text-halo-color': '#ffffff', 'text-halo-width': 1, }, }, ], } }