Files
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

370 lines
9.3 KiB
TypeScript

/**
* 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,
},
},
],
}
}