import { html, css, cssManager } from '@design.estate/dees-element'; import '@design.estate/dees-wcctools/demotools'; import type { DeesGeoMap } from './dees-geo-map.js'; import { getSpeedPresets, type TSimulationSpeed } from './geo-map.mock-gps.js'; export const demoFunc = () => html`
{ const map = elementArg.querySelector('dees-geo-map') as DeesGeoMap; const eventLog = elementArg.querySelector('#event-log') as HTMLElement; const featureJson = elementArg.querySelector('#feature-json') as HTMLElement; // Simulation state let simulationState = { isRunning: false, isPaused: false, progress: 0, hasRoute: false, }; const updateSimulationUI = () => { const startBtn = elementArg.querySelector('#sim-start') as HTMLButtonElement; const pauseBtn = elementArg.querySelector('#sim-pause') as HTMLButtonElement; const stopBtn = elementArg.querySelector('#sim-stop') as HTMLButtonElement; const progressFill = elementArg.querySelector('#sim-progress-fill') as HTMLElement; const progressValue = elementArg.querySelector('#sim-progress-value') as HTMLElement; const statusEl = elementArg.querySelector('#sim-status') as HTMLElement; if (!startBtn) return; // Update button states startBtn.disabled = !simulationState.hasRoute || (simulationState.isRunning && !simulationState.isPaused); pauseBtn.disabled = !simulationState.isRunning || simulationState.isPaused; stopBtn.disabled = !simulationState.isRunning && !simulationState.isPaused; // Update progress if (progressFill) { progressFill.style.width = `${simulationState.progress}%`; } if (progressValue) { progressValue.textContent = `${simulationState.progress.toFixed(1)}%`; } // Update status if (statusEl) { statusEl.className = 'sim-status'; if (!simulationState.hasRoute) { statusEl.className += ' no-route'; statusEl.textContent = 'Calculate a route first to enable simulation'; } else if (simulationState.isRunning && !simulationState.isPaused) { statusEl.className += ' running'; statusEl.textContent = 'Simulation running...'; } else if (simulationState.isPaused) { statusEl.className += ' paused'; statusEl.textContent = 'Simulation paused'; } else if (simulationState.progress >= 100) { statusEl.className += ' completed'; statusEl.textContent = 'Simulation completed!'; } else { statusEl.className += ' idle'; statusEl.textContent = 'Ready to simulate'; } } }; const addLogEntry = (type: string, message: string) => { const entry = document.createElement('div'); entry.className = 'event-entry'; entry.innerHTML = `${type}: ${message}`; eventLog.insertBefore(entry, eventLog.firstChild); }; const updateFeatureDisplay = () => { if (map && featureJson) { featureJson.textContent = JSON.stringify(map.getGeoJson(), null, 2); } }; if (map) { map.addEventListener('map-ready', () => { addLogEntry('ready', 'Map initialized successfully'); updateSimulationUI(); }); map.addEventListener('draw-change', (e: CustomEvent) => { addLogEntry('change', `${e.detail.type} - ${e.detail.ids.length} feature(s) affected`); updateFeatureDisplay(); }); map.addEventListener('draw-finish', (e: CustomEvent) => { addLogEntry('finish', `${e.detail.context.mode} completed (id: ${e.detail.id})`); }); map.addEventListener('map-move', (e: CustomEvent) => { console.log('Map moved:', e.detail); }); map.addEventListener('address-selected', (e: CustomEvent) => { addLogEntry('address', `Selected: ${e.detail.address.substring(0, 50)}...`); console.log('Address selected:', e.detail); }); map.addEventListener('route-calculated', (e: CustomEvent) => { const { route, mode } = e.detail; const distKm = (route.distance / 1000).toFixed(1); const durationMin = Math.round(route.duration / 60); addLogEntry('route', `${mode}: ${distKm} km, ${durationMin} min`); console.log('Route calculated:', e.detail); // Update simulation state simulationState.hasRoute = true; simulationState.progress = 0; updateSimulationUI(); // Create/update the simulator with the new route const simulator = map.createMockGPSSimulator(); simulator.setRoute(route); }); map.addEventListener('guidance-event', (e: CustomEvent) => { const event = e.detail; addLogEntry('guidance', `${event.type}: ${event.instruction || 'Step ' + event.stepIndex}`); console.log('Guidance event:', event); // Update progress const simulator = map.getMockGPSSimulator(); if (simulator) { simulationState.progress = simulator.getProgress(); updateSimulationUI(); } }); } // Set up simulation controls const setupSimulationControls = () => { const startBtn = elementArg.querySelector('#sim-start') as HTMLButtonElement; const pauseBtn = elementArg.querySelector('#sim-pause') as HTMLButtonElement; const stopBtn = elementArg.querySelector('#sim-stop') as HTMLButtonElement; const speedSelect = elementArg.querySelector('#sim-speed') as HTMLSelectElement; const voiceCheckbox = elementArg.querySelector('#sim-voice') as HTMLInputElement; if (startBtn && map) { startBtn.addEventListener('click', () => { let simulator = map.getMockGPSSimulator(); if (!simulator) { simulator = map.createMockGPSSimulator(); const route = map.getNavigationState()?.route; if (route) { simulator.setRoute(route); } } // Start guidance map.startGuidance(); simulator.start(); simulationState.isRunning = true; simulationState.isPaused = false; updateSimulationUI(); addLogEntry('simulation', 'Started'); }); } if (pauseBtn && map) { pauseBtn.addEventListener('click', () => { const simulator = map.getMockGPSSimulator(); if (simulator) { simulator.pause(); simulationState.isPaused = true; updateSimulationUI(); addLogEntry('simulation', 'Paused'); } }); } if (stopBtn && map) { stopBtn.addEventListener('click', () => { map.stopGuidance(); simulationState.isRunning = false; simulationState.isPaused = false; simulationState.progress = 0; updateSimulationUI(); addLogEntry('simulation', 'Stopped'); }); } if (speedSelect && map) { speedSelect.addEventListener('change', () => { const speed = speedSelect.value as TSimulationSpeed; const simulator = map.getMockGPSSimulator(); if (simulator) { simulator.setSpeed(speed); addLogEntry('simulation', `Speed changed to ${speedSelect.options[speedSelect.selectedIndex].text}`); } }); } if (voiceCheckbox && map) { // Set initial state voiceCheckbox.checked = true; voiceCheckbox.addEventListener('change', () => { map.setVoiceEnabled(voiceCheckbox.checked); addLogEntry('simulation', `Voice ${voiceCheckbox.checked ? 'enabled' : 'disabled'}`); }); } }; // Set up navigation buttons const locations: Record = { paris: [2.3522, 48.8566], london: [-0.1276, 51.5074], newyork: [-74.006, 40.7128], tokyo: [139.6917, 35.6895], sydney: [151.2093, -33.8688], rio: [-43.1729, -22.9068], }; Object.entries(locations).forEach(([name, coords]) => { const btn = elementArg.querySelector(`#nav-${name}`) as HTMLButtonElement; if (btn && map) { btn.addEventListener('click', () => map.flyTo(coords, 13)); } }); // Set up control buttons const clearBtn = elementArg.querySelector('#btn-clear') as HTMLButtonElement; const fitBtn = elementArg.querySelector('#btn-fit') as HTMLButtonElement; const downloadBtn = elementArg.querySelector('#btn-download') as HTMLButtonElement; const loadBtn = elementArg.querySelector('#btn-load') as HTMLButtonElement; if (clearBtn && map) { clearBtn.addEventListener('click', () => { map.clearAllFeatures(); updateFeatureDisplay(); }); } if (fitBtn && map) { fitBtn.addEventListener('click', () => map.fitToFeatures()); } if (downloadBtn && map) { downloadBtn.addEventListener('click', () => { const geojson = map.getGeoJson(); const blob = new Blob([JSON.stringify(geojson, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'features.geojson'; a.click(); URL.revokeObjectURL(url); }); } if (loadBtn && map) { loadBtn.addEventListener('click', () => { map.loadGeoJson({ type: 'FeatureCollection', features: [ { type: 'Feature', properties: { mode: 'polygon' }, geometry: { type: 'Polygon', coordinates: [[ [8.675, 50.115], [8.690, 50.115], [8.690, 50.105], [8.675, 50.105], [8.675, 50.115], ]], }, }, ], }); updateFeatureDisplay(); }); } // Initialize simulation controls after a tick setTimeout(() => { setupSimulationControls(); updateSimulationUI(); }, 100); }}>

Interactive Map with Drawing Tools

Click on the drawing tools in the toolbar to create shapes on the map. Use the Select tool to edit, move, or delete shapes. All features are rendered using terra-draw with MapLibre GL JS.

Traffic: To enable live traffic, set the trafficApiKey property with your HERE API key (free tier: 250k requests/month at developer.here.com).

GPS Simulation & Voice Navigation

Calculate a route using the navigation panel, then use these controls to simulate GPS movement along the route with voice-guided turn-by-turn instructions.

Speed:
Route Progress 0%
Calculate a route first to enable simulation

Quick Navigation

Controls

Event Log

init: Waiting for map...

Current Features (GeoJSON)

{ "type": "FeatureCollection", "features": [] }
`;