290 lines
13 KiB
Markdown
290 lines
13 KiB
Markdown
# 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 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
|
|
|
|
### 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
|
|
|
|
### 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)
|
|
```typescript
|
|
// 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)
|
|
```typescript
|
|
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
|
|
```
|
|
|
|
## 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
|
|
```typescript
|
|
// 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
|