2026-02-05 22:51:41 +00:00
2026-02-05 12:03:22 +00:00
2026-02-05 12:03:22 +00:00
2026-02-05 12:03:22 +00:00
2026-02-05 12:03:22 +00:00
2026-02-05 22:51:41 +00:00
2026-02-05 12:03:22 +00:00

Project Hints - dees-catalog-geo

Overview

Geospatial web components library using MapLibre GL JS for map rendering and terra-draw for drawing capabilities.

Key Dependencies

  • maplibre-gl (v5.x): WebGL-based vector map library
  • terra-draw (v1.24.0): Modern drawing library with support for multiple map libraries
  • terra-draw-maplibre-gl-adapter (v1.x): Adapter connecting terra-draw with MapLibre

Component: dees-geo-map

Properties

Property Type Default Description
center [number, number] [0, 0] Map center as [lng, lat]
zoom number 2 Initial zoom level
mapStyle string 'osm' Map style ('osm' or custom URL)
activeTool TDrawTool 'static' Active drawing tool
geoJson GeoJSON.FeatureCollection {...} Initial features
showToolbar boolean true Show/hide drawing toolbar
projection 'mercator' | 'globe' 'mercator' Map projection type
showSearch boolean false Show address search input
showNavigation boolean false Show A-to-B navigation panel
navigationMode 'driving' | 'walking' | 'cycling' 'driving' Transport mode for routing
showTraffic boolean false Enable traffic layer visualization
trafficApiKey string '' HERE API key for traffic data
trafficProvider ITrafficProvider null Custom traffic data provider
enableGuidance boolean false Enable voice-guided navigation
voiceConfig Partial<IVoiceConfig> {} Voice synthesis configuration

Drawing Tools (TDrawTool)

  • point - Draw points
  • linestring - Draw lines
  • polygon - Draw polygons
  • rectangle - Draw rectangles
  • circle - Draw circles
  • freehand - Freehand drawing
  • select - Select and edit features
  • static - Pan/zoom only (no drawing)

Events

  • map-ready - Fired when map is initialized
  • map-move - Fired on pan/zoom with center and zoom
  • draw-change - Fired on any feature change
  • draw-finish - Fired when a shape is completed
  • address-selected - Fired when a search result is selected
  • route-calculated - Fired when a navigation route is calculated (includes route, startPoint, endPoint, mode)
  • traffic-updated - Fired when traffic data is refreshed
  • guidance-event - Fired during voice-guided navigation (includes type, position, stepIndex, instruction)

Public Methods

  • getFeatures() - Get all drawn features
  • getGeoJson() - Get features as FeatureCollection
  • loadGeoJson(geojson) - Load features from GeoJSON
  • clearAllFeatures() - Remove all features
  • setTool(tool) - Set active drawing tool
  • flyTo(center, zoom?) - Animate to location
  • fitToFeatures(padding?) - Fit view to all features
  • setProjection(projection) - Set map projection ('mercator' or 'globe')
  • getMap() - Get underlying MapLibre instance
  • getTerraDraw() - Get TerraDraw instance
  • calculateRoute() - Calculate route between start and end points
  • setNavigationStart(coords, address?) - Set navigation start point
  • setNavigationEnd(coords, address?) - Set navigation end point
  • clearNavigation() - Clear all navigation state
  • enableTraffic() - Enable traffic visualization
  • disableTraffic() - Disable traffic visualization
  • toggleTraffic() - Toggle traffic visualization
  • refreshTraffic() - Refresh traffic data
  • setTrafficProvider(provider) - Set custom traffic provider
  • supportsTrafficRouting() - Check if traffic-aware routing is available
  • getTrafficController() - Get the TrafficController instance
  • setPosition(coords, heading?, speed?) - Set current GPS position for navigation guidance
  • startGuidance() - Start voice-guided navigation for the current route
  • stopGuidance() - Stop voice-guided navigation
  • setVoiceEnabled(enabled) - Enable/disable voice guidance
  • isVoiceEnabled() - Check if voice guidance is enabled
  • getGuidanceState() - Get current navigation guidance state
  • isNavigating() - Check if actively navigating
  • createMockGPSSimulator(config?) - Create a mock GPS simulator for testing/demo
  • getMockGPSSimulator() - Get the mock GPS simulator instance
  • getGuidanceController() - Get the NavigationGuideController instance
  • setNavigationFollowPosition(enabled) - Enable/disable camera following GPS position during navigation
  • setNavigationFollowBearing(enabled) - Enable/disable camera rotating with heading during navigation
  • setNavigationPitch(pitch) - Set navigation camera pitch (0-85 degrees, default 60)
  • setNavigationZoom(zoom) - Set navigation zoom level (default 17)
  • getNavigationCameraConfig() - Get full navigation camera configuration
  • setNavigationCameraConfig(config) - Set navigation camera configuration

Context Menu

Right-click on the map to access a context menu with the following options:

  • Drag to Draw - Toggle between drag mode (click-drag for circles/rectangles) and two-click mode
  • Globe View - Toggle between globe (3D sphere) and Mercator (flat) projection
  • Show Traffic - Toggle traffic layer (requires configured traffic provider)
  • Clear All Features - Remove all drawn features from the map
  • Fit to Features - Zoom and pan to show all drawn features

Navigation Feature

The navigation panel (showNavigation={true}) provides A-to-B routing using OSRM (Open Source Routing Machine):

  • Transport modes: Driving, Walking, Cycling
  • Point selection: Type an address or click on the map
  • Route display: Blue line overlay with turn-by-turn directions
  • Click on step: Click any turn-by-turn step to fly/pan the map to that location
  • API: Uses free OSRM API (https://router.project-osrm.org) with fair-use rate limit
  • Traffic-aware routing: When a traffic provider is configured, shows duration with/without traffic

Traffic Feature

Live traffic visualization with pluggable provider architecture:

// Using API key property
<dees-geo-map trafficApiKey="YOUR_HERE_API_KEY" showTraffic></dees-geo-map>

// Or programmatically
import { HereTrafficProvider } from '@design.estate/dees-catalog-geo';
const map = document.querySelector('dees-geo-map');
const provider = new HereTrafficProvider();
provider.configure({ apiKey: 'YOUR_HERE_API_KEY' });
map.setTrafficProvider(provider);
map.enableTraffic();

Free Tier: 250,000 transactions/month (no credit card required) Sign up at: https://developer.here.com

Valhalla Traffic Provider (Self-Hosted)

import { ValhallaTrafficProvider } from '@design.estate/dees-catalog-geo';
const provider = new ValhallaTrafficProvider();
provider.configure({
  serverUrl: 'https://your-valhalla-server.com',
  trafficDataUrl: 'https://your-traffic-data-endpoint.com' // optional
});
map.setTrafficProvider(provider);

Traffic Color Legend

  • 🟢 Green - Free flow
  • 🟡 Yellow - Light congestion
  • 🟠 Orange - Moderate congestion
  • 🔴 Red - Heavy congestion
  • 🔴 Dark Red - Severe/stopped

Development

  • pnpm install - Install dependencies
  • pnpm watch - Start development server (port 3002)
  • pnpm build - Build for production

File Structure

ts_web/
├── index.ts                           # Main exports
├── 00_commitinfo_data.ts              # Auto-generated
└── elements/
    ├── index.ts                       # Elements barrel
    ├── 00colors.ts                    # Color definitions
    ├── 00componentstyles.ts           # Shared styles
    └── 00group-map/
        ├── index.ts
        └── dees-geo-map/
            ├── index.ts               # Exports main + modules
            ├── dees-geo-map.ts        # Main component
            ├── dees-geo-map.demo.ts   # Demo function
            ├── geo-map.icons.ts       # Icon SVG definitions
            ├── geo-map.search.ts      # SearchController class
            ├── geo-map.navigation.ts  # NavigationController class
            ├── geo-map.traffic.ts     # TrafficController class
            ├── geo-map.traffic.providers.ts # Traffic provider implementations
            ├── geo-map.voice.ts       # VoiceSynthesisManager class
            ├── geo-map.navigation-guide.ts # NavigationGuideController class
            └── geo-map.mock-gps.ts    # MockGPSSimulator class

Modular Architecture

The component was refactored for better maintainability:

geo-map.icons.ts

Contains all SVG icon definitions as a GEO_MAP_ICONS record and a renderIcon(name) helper function.

geo-map.search.ts

SearchController class encapsulating Nominatim geocoding search:

  • Reusable for standalone search or within navigation
  • Debounced API calls
  • Keyboard navigation support
  • Customizable via ISearchControllerConfig

geo-map.navigation.ts

NavigationController class for A-to-B routing:

  • OSRM routing API integration
  • Start/end point management with markers
  • Map click mode for point selection
  • Turn-by-turn directions rendering
  • Route overlay on map
  • Traffic-aware routing integration (shows congestion level and delay)

geo-map.traffic.ts

TrafficController class for live traffic visualization:

  • Traffic layer rendering with color-coded congestion
  • Auto-refresh logic with configurable interval
  • Supports pluggable traffic providers

geo-map.traffic.providers.ts

Traffic data provider implementations:

  • HereTrafficProvider - HERE Traffic API v7 (freemium)
  • ValhallaTrafficProvider - Self-hosted Valhalla server

geo-map.voice.ts

VoiceSynthesisManager class for voice-guided navigation:

  • Uses Web Speech API for text-to-speech
  • Queue-based speech with interrupt capability for urgent instructions
  • Configurable language, rate, pitch, volume
  • Navigation-specific methods: speakApproach(), speakManeuver(), speakArrival(), speakOffRoute()
  • Graceful fallback if speech synthesis not supported

geo-map.navigation-guide.ts

NavigationGuideController class for real-time GPS navigation guidance:

  • Position tracking with GPS updates via updatePosition() or setPosition()
  • Step progression tracking along route
  • Distance thresholds for voice announcements (500m, 200m, 50m, at maneuver)
  • Off-route detection (>50m from route line)
  • Arrival detection (within 30m of destination)
  • Renders GPS position marker on map with heading indicator
  • Emits guidance events: approach-maneuver, execute-maneuver, step-change, off-route, arrived
  • Camera following: Automatically moves camera to follow GPS position with smooth transitions
  • Camera configuration: Configurable pitch (60° default for 3D view), zoom (17 default), and bearing following

geo-map.mock-gps.ts

MockGPSSimulator class for testing/demo:

  • Interpolates positions along route geometry
  • Speed presets: Walking (5 km/h), Cycling (20 km/h), City (50 km/h), Highway (100 km/h)
  • Configurable update interval and GPS jitter
  • Calculates heading between consecutive points
  • Methods: start(), pause(), stop(), jumpToProgress()

Usage of Controllers

// SearchController is reusable
const search = new SearchController(
  { placeholder: 'Search...' },
  {
    onResultSelected: (result, coords, zoom) => { /* handle */ },
    onRequestUpdate: () => this.requestUpdate(),
  }
);

// NavigationController manages all navigation state
const nav = new NavigationController({
  onRouteCalculated: (event) => { /* dispatch */ },
  onRequestUpdate: () => this.requestUpdate(),
  getMap: () => this.map,
});

Notes

  • MapLibre CSS is loaded dynamically from CDN
  • Terra-draw requires the separate maplibre-gl-adapter package
  • The component uses Shadow DOM for style encapsulation

UI Layout

The component uses a CSS Grid layout with sidebars that push the map (not overlay):

┌──────────────────────────────────────────────────────────────────────┐
│ HEADER TOOLBAR                                                        │
│ [Nav Toggle] |         [Search Bar]        | [Draw Toggle] [Traffic] [+/-] │
├───────────────┬──────────────────────────────┬───────────────────────┤
│ LEFT SIDEBAR  │            MAP               │   RIGHT SIDEBAR       │
│ (Navigation)  │                              │   (Draw Tools)        │
│               │                              │                       │
│ - Mode select │                              │   [Point]  [Line]     │
│ - Start input │                              │   [Polygon][Rect]     │
│ - End input   │                              │   [Circle] [Free]     │
│ - Route steps │                              │   ─────────────────   │
│               │   [Traffic Legend]           │   [Select & Edit]     │
│               │   [Feature Count]            │   [Clear All]         │
└───────────────┴──────────────────────────────┴───────────────────────┘

CSS Grid Layout:

  • Grid columns: var(--left-panel-width) 1fr var(--right-panel-width)
  • Grid rows: auto 1fr (header + content)
  • Sidebars push the map area, not overlay it

Header Toolbar Sections:

  • Left: Navigation panel toggle button
  • Center: Search bar (expandable width)
  • Right: Draw panel toggle, Traffic toggle, Zoom in/out buttons

Left Sidebar (300px when open):

  • Contains NavigationController render output
  • Slides in/out with 0.25s ease transition
  • Default open when showNavigation={true}

Right Sidebar (180px when open):

  • Draw tools panel with 2-column grid layout
  • Tool buttons with icons AND labels
  • Select & Edit and Clear All actions
  • Slides in/out with 0.25s ease transition
  • Default open when showToolbar={true}

Map Overlays (remaining):

  • Traffic legend: Bottom-left overlay (when traffic enabled)
  • Feature count: Bottom-left overlay (when features exist)

Z-index hierarchy:

  • z-index: 20 - Dropdowns (search results, nav search results)
  • z-index: 10 - Header toolbar
  • z-index: 5 - Map overlays (traffic legend, feature count)

Dark/Light Theme Support

The component fully supports automatic dark/light theme switching:

UI Elements Theming:

  • All UI elements (toolbar, panels, search, navigation) use cssManager.bdTheme() for automatic color switching
  • Pattern: cssManager.bdTheme('lightValue', 'darkValue') - first arg is light theme, second is dark

Map Tiles Theming:

  • When mapStyle="osm" (default), the map automatically switches between:
    • Light theme: CartoDB Voyager GL (vector tiles) - clean, detailed style
    • Dark theme: CartoDB Dark Matter GL (vector tiles) - sleek dark style
  • Theme changes are detected via domtools.themeManager.themeObservable
  • No API key required for CartoDB basemaps

Semantic Colors (unchanged by theme):

  • Navigation markers: Green (#22c55e) for start, Red (#ef4444) for end
  • Route line: Blue (#3b82f6) with dark outline (#1e40af)
  • Traffic congestion: Green → Yellow → Orange → Red → Dark Red (universal traffic colors)

Shadow DOM & Terra-Draw Drawing Fix

Terra-draw's event listeners normally intercept map events through MapLibre's canvas element. In Shadow DOM contexts, these events are scoped locally and don't propagate correctly, causing terra-draw handlers to fail while MapLibre's drag handlers continue working.

Solution: Manual drag coordination in setTool():

  • When a drawing tool is active (polygon, rectangle, point, linestring, circle, freehand), MapLibre's dragPan and dragRotate are disabled
  • When static or select mode is active, dragging is re-enabled
  • The TerraDrawMapLibreGLAdapter does NOT accept a lib parameter - only map is required

Voice-Guided Navigation Feature

Real-time GPS tracking with voice-guided turn-by-turn instructions:

How to Use

  1. Calculate a route using the navigation panel (set start and end points)
  2. Call startGuidance() to begin voice-guided navigation
  3. Update position with setPosition([lng, lat], heading?, speed?) or use createMockGPSSimulator() for testing
  4. Listen to guidance-event for navigation updates
  5. Call stopGuidance() when done

Example Usage

const map = document.querySelector('dees-geo-map');

// 1. Set up route
map.setNavigationStart([8.68, 50.11], 'Frankfurt');
map.setNavigationEnd([8.75, 50.08], 'Sachsenhausen');
await map.calculateRoute();

// 2. Listen to guidance events
map.addEventListener('guidance-event', (e) => {
  console.log(e.detail.type, e.detail.instruction);
});

// 3. Start guidance with mock GPS simulation
const simulator = map.createMockGPSSimulator({ speed: 'city' });
map.startGuidance();
simulator.start();

// 4. Or use real GPS input
navigator.geolocation.watchPosition((pos) => {
  map.setPosition([pos.coords.longitude, pos.coords.latitude], pos.coords.heading, pos.coords.speed);
});

Voice Announcements

  • 500m: "In 500 meters, turn left onto Main Street"
  • 200m: "In 200 meters, turn left"
  • 50m: "Turn left ahead"
  • At maneuver: "Turn left now"
  • Arrival: "You have arrived at your destination"
  • Off-route: "You are off route. Recalculating."

Guidance Event Types

  • approach-maneuver - Approaching a turn/maneuver
  • execute-maneuver - At the maneuver point (urgent)
  • step-change - Advanced to next route step
  • off-route - Deviated more than 50m from route
  • arrived - Within 30m of destination
  • position-updated - Position was updated

Camera Following

During navigation, the map camera automatically:

  • Follows position: Centers on current GPS location with smooth continuous transitions
  • Follows bearing: Rotates map to match driving direction (heading)
  • 3D tilt: Uses 60° pitch by default for immersive driving view
  • Street-level zoom: Uses zoom level 17 for optimal route visibility
  • Smooth animation: Animation duration dynamically matches GPS update interval for fluid movement (no jerky pauses)

Camera behavior can be customized:

// Disable camera following (user can pan freely)
map.setNavigationFollowPosition(false);

// Disable bearing rotation (map stays north-up)
map.setNavigationFollowBearing(false);

// Use flat (2D) view instead of tilted
map.setNavigationPitch(0);

// Zoom out for wider view
map.setNavigationZoom(15);

// Or set multiple options at once
map.setNavigationCameraConfig({
  followPosition: true,
  followBearing: true,
  pitch: 60,
  zoom: 17,
});
Description
No description provided
Readme 6.9 MiB
Languages
TypeScript 99.7%
HTML 0.3%