Show the calculated speed of the route on the map

This commit is contained in:
Martin Asprusten 2025-06-30 14:30:34 +02:00
parent 8f0759fc63
commit f0b4479664
7 changed files with 74 additions and 33 deletions

View File

@ -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_<JSSearchResult>("SearchResult")

7
package-lock.json generated
View File

@ -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",

View File

@ -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"
}

View File

@ -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 {

View File

@ -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();

View File

@ -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() {

View File

@ -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);
}