From f0b4479664c24e30c8e494a38072d6041d6c97a8 Mon Sep 17 00:00:00 2001 From: Martin Asprusten Date: Mon, 30 Jun 2025 14:30:34 +0200 Subject: [PATCH] Show the calculated speed of the route on the map --- native/route_search.cpp | 4 ++- package-lock.json | 7 +++++ package.json | 1 + src/interfaces.ts | 8 ++++- src/main.ts | 3 +- src/modules/maphandler/maphandler.ts | 46 ++++++++++++++++------------ src/modules/worker/worker.ts | 38 +++++++++++++++++------ 7 files changed, 74 insertions(+), 33 deletions(-) diff --git a/native/route_search.cpp b/native/route_search.cpp index 63d7cb2..e0f9c0f 100644 --- a/native/route_search.cpp +++ b/native/route_search.cpp @@ -279,7 +279,7 @@ float calculate_speed(float starting_speed, float horizontal_distance, float hei if (final_speed < minimum_speed) { return -1; } else { - return std::min(final_speed, maximum_speed); + return std::fmin(final_speed, maximum_speed); } } @@ -759,6 +759,8 @@ EMSCRIPTEN_BINDINGS(my_module) { .property("nodeId", &JSNodeInfo::nodeId) .property("positionX", &JSNodeInfo::positionX) .property("positionY", &JSNodeInfo::positionY) + .property("positionZ", &JSNodeInfo::positionZ) + .property("currentSpeed", &JSNodeInfo::currentSpeed) .property("distanceFromStart", &JSNodeInfo::distanceFromStart); emscripten::class_("SearchResult") diff --git a/package-lock.json b/package-lock.json index d204312..0c4f465 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@geoman-io/leaflet-geoman-free": "^2.18.3", "@types/geojson": "^7946.0.16", "@types/leaflet": "^1.9.19", + "hue-map": "^1.0.0", "leaflet": "^1.9.4", "proj4": "^2.19.4" }, @@ -1096,6 +1097,12 @@ "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==", "license": "MIT" }, + "node_modules/hue-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hue-map/-/hue-map-1.0.0.tgz", + "integrity": "sha512-L7n6cfthm9ZTdYbqJF1yBflHvI4MaWRg+XMJ4Ifc35jWcOfQlCRBETZ4ntnHLPUyDOse7KeelcsbWxx14Aaa+w==", + "license": "MIT" + }, "node_modules/leaflet": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", diff --git a/package.json b/package.json index 7fb65d6..1be9afb 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@geoman-io/leaflet-geoman-free": "^2.18.3", "@types/geojson": "^7946.0.16", "@types/leaflet": "^1.9.19", + "hue-map": "^1.0.0", "leaflet": "^1.9.4", "proj4": "^2.19.4" } diff --git a/src/interfaces.ts b/src/interfaces.ts index 3136762..952b767 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -69,8 +69,14 @@ export interface Coordinate { longitude: number } +export interface PathSegment { + start: Coordinate, + end: Coordinate, + speed: number +} + interface ReturnFullPath { - coordinates: Coordinate[] + pathSegments: PathSegment[] } export interface Ring { diff --git a/src/main.ts b/src/main.ts index 9165a4f..cca530d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -82,7 +82,8 @@ routeWorker.onmessage = e => { } else if (message.foundPathsFromNode != null) { mapHandler?.drawEndPoints(message.foundPathsFromNode.endpoints); } else if (message.returnFullPath != null) { - mapHandler?.drawPath(message.returnFullPath.coordinates); + let settings = getSettings(); + mapHandler?.drawPath(message.returnFullPath.pathSegments, settings.minimumSpeed, settings.maximumSpeed); } else if (message.searchAreaResult != null) { searchStatusParagraph?.setHTMLUnsafe('Searching. ' + message.searchAreaResult.remainingNodes + ' possible starting points remain.'); let currentTime = Date.now(); diff --git a/src/modules/maphandler/maphandler.ts b/src/modules/maphandler/maphandler.ts index 40f2242..c05b3b4 100644 --- a/src/modules/maphandler/maphandler.ts +++ b/src/modules/maphandler/maphandler.ts @@ -1,13 +1,20 @@ import 'leaflet/dist/leaflet.css'; -import L, { FeatureGroup, Marker, Polygon, Polyline, type LatLngTuple } from 'leaflet'; +import L, { FeatureGroup, Marker, Polygon, type LatLngTuple } from 'leaflet'; import '@geoman-io/leaflet-geoman-free' import '@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css' import './maphandler.css' import { greenIcon, redIcon, violetIcon } from './icons.ts' -import type { Endpoint, Coordinate, Polygon as InterfacePolygon } from '../../interfaces.ts' +import type { Endpoint, Polygon as InterfacePolygon, PathSegment } from '../../interfaces.ts' import type { Position } from 'geojson'; +import { createPalette } from 'hue-map'; + +const viridisPalette = createPalette({ + map: 'inferno', + steps: 100 +}); + interface ClickedMapListener { (latitude: number, longitude: number): void; } @@ -44,7 +51,7 @@ class MapHandler { startMarker?: Marker; endMarkers: FeatureGroup; - path?: Polyline; + path: FeatureGroup; editingPolygons: boolean = false; @@ -150,6 +157,7 @@ class MapHandler { this.searchAreaFeatureGroup = new L.FeatureGroup().addTo(this.map); this.exclusionAreaFeatureGroup = new L.FeatureGroup().addTo(this.map); this.endMarkers = new L.FeatureGroup().addTo(this.map); + this.path = new L.FeatureGroup().addTo(this.map); this.map.addEventListener('click', e => { if (!this.editingPolygons) { @@ -252,10 +260,7 @@ class MapHandler { } public drawStartNode(nodeId: number, latitude: number, longitude: number): void { - if (this.path != null) { - this.map.removeLayer(this.path); - this.path = undefined; - } + this.path.clearLayers(); if (this.startMarker != null) { this.map.removeLayer(this.startMarker); @@ -268,10 +273,7 @@ class MapHandler { public drawEndPoints(endpoints: Endpoint[]): void { this.endMarkers.clearLayers(); - if (this.path != null) { - this.map.removeLayer(this.path); - this.path = undefined; - } + this.path.clearLayers(); var firstMarker = true; endpoints.forEach(endpoint => { var settings; @@ -303,17 +305,21 @@ class MapHandler { }); } - public drawPath(coordinates: Coordinate[]): void { - if (this.path != null) { - this.map.removeLayer(this.path); - } + public drawPath(pathSegments: PathSegment[], minimumSpeed: number, maximumSpeed: number): void { + this.path.clearLayers(); + pathSegments.forEach(pathSegment => { + let startLeafletCoordinate: LatLngTuple = [pathSegment.start.latitude, pathSegment.start.longitude]; + let endLeafletCoordinate: LatLngTuple = [pathSegment.end.latitude, pathSegment.end.longitude]; + let leafletCoordinates = [startLeafletCoordinate, endLeafletCoordinate]; - var leafletCoordinates = coordinates.map(coordinate => { - let latLngTuple: LatLngTuple = [coordinate.latitude, coordinate.longitude]; - return latLngTuple; + let intensity = Math.round((pathSegment.speed - minimumSpeed) * 99.0 / (maximumSpeed - minimumSpeed)); + intensity = Math.min(Math.max(intensity, 0), 99); + let color = viridisPalette.format('cssHex')[99-intensity]; + + let polyLine = L.polyline(leafletCoordinates, {color: color}).addTo(this.path); + let speedKmh = pathSegment.speed * 3.6; + polyLine.bindTooltip(Math.round(speedKmh) + '.' + (Math.round(speedKmh * 10.0) % 10) + ' km/h'); }); - - this.path = L.polyline(leafletCoordinates).addTo(this.map); } public enableEditing() { diff --git a/src/modules/worker/worker.ts b/src/modules/worker/worker.ts index 069142d..6f3ba60 100644 --- a/src/modules/worker/worker.ts +++ b/src/modules/worker/worker.ts @@ -1,7 +1,7 @@ import Module, { type AreaSearchEntries, type MainModule, type MultiPolygon } from '../../../native/route_search.js'; import proj4 from 'proj4'; -import type { Endpoint, Message, Polygon, SearchAreaResultEntry } from '../../interfaces'; +import type { Endpoint, Message, PathSegment, Polygon, SearchAreaResultEntry } from '../../interfaces'; var dataLoaded = false; var module: MainModule | undefined = undefined; @@ -133,22 +133,40 @@ onmessage = async (e) => { sendErrorMessage('Could not get path'); return; } - var coordinates = []; - for (var i = 0; i < path.size(); i++) { + let pathSegments: PathSegment[] = [] + for (var i = 1; i < path.size(); i++) { let currentPoint = path.get(i); if (!currentPoint) { continue; } - coordinates.push([currentPoint.positionX, currentPoint.positionY]); + let previousPoint = path.get(i-1); + if (!previousPoint) { + continue; + } + let previousUtm = [previousPoint.positionX, previousPoint.positionY]; + let currentUtm = [currentPoint.positionX, currentPoint.positionY]; + + let previousLngLat = proj4('EPSG:32633', 'EPSG:4326', previousUtm); + let currentLngLat = proj4('EPSG:32633', 'EPSG:4326', currentUtm); + + let speed = Math.max(previousPoint.currentSpeed, currentPoint.currentSpeed); + let segment: PathSegment = { + start: { + latitude: previousLngLat[1], + longitude: previousLngLat[0] + }, + end: { + latitude: currentLngLat[1], + longitude: currentLngLat[0] + }, + speed: speed + } + + pathSegments.push(segment); } path.delete(); - coordinates = coordinates.map(utmCoordinate => { - let lngLat = proj4('EPSG:32633', 'EPSG:4326', utmCoordinate); - return {latitude: lngLat[1], longitude: lngLat[0]}; - }); - - let returnMessage: Message = {returnFullPath: {coordinates: coordinates}}; + let returnMessage: Message = {returnFullPath: {pathSegments: pathSegments}}; postMessage(returnMessage); }