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 |
Drawing Tools (TDrawTool)
point- Draw pointslinestring- Draw linespolygon- Draw polygonsrectangle- Draw rectanglescircle- Draw circlesfreehand- Freehand drawingselect- Select and edit featuresstatic- Pan/zoom only (no drawing)
Events
map-ready- Fired when map is initializedmap-move- Fired on pan/zoom with center and zoomdraw-change- Fired on any feature changedraw-finish- Fired when a shape is completedaddress-selected- Fired when a search result is selectedroute-calculated- Fired when a navigation route is calculated (includes route, startPoint, endPoint, mode)traffic-updated- Fired when traffic data is refreshed
Public Methods
getFeatures()- Get all drawn featuresgetGeoJson()- Get features as FeatureCollectionloadGeoJson(geojson)- Load features from GeoJSONclearAllFeatures()- Remove all featuressetTool(tool)- Set active drawing toolflyTo(center, zoom?)- Animate to locationfitToFeatures(padding?)- Fit view to all featuressetProjection(projection)- Set map projection ('mercator' or 'globe')getMap()- Get underlying MapLibre instancegetTerraDraw()- Get TerraDraw instancecalculateRoute()- Calculate route between start and end pointssetNavigationStart(coords, address?)- Set navigation start pointsetNavigationEnd(coords, address?)- Set navigation end pointclearNavigation()- Clear all navigation stateenableTraffic()- Enable traffic visualizationdisableTraffic()- Disable traffic visualizationtoggleTraffic()- Toggle traffic visualizationrefreshTraffic()- Refresh traffic datasetTrafficProvider(provider)- Set custom traffic providersupportsTrafficRouting()- Check if traffic-aware routing is availablegetTrafficController()- Get the TrafficController instance
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:
HERE Traffic Provider (Recommended)
// 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 dependenciespnpm 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
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
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 toolbarz-index: 5- Map overlays (traffic legend, feature count)
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'sdragPananddragRotateare disabled - When
staticorselectmode is active, dragging is re-enabled - The
TerraDrawMapLibreGLAdapterdoes NOT accept alibparameter - onlymapis required