Calculate and show the required speed at all points of the route
This commit is contained in:
parent
f0b4479664
commit
00db764fd0
@ -54,6 +54,7 @@ struct JSNodeInfo {
|
|||||||
float positionZ;
|
float positionZ;
|
||||||
float distanceFromStart;
|
float distanceFromStart;
|
||||||
float currentSpeed;
|
float currentSpeed;
|
||||||
|
float requiredSpeed;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct JSSearchResult {
|
struct JSSearchResult {
|
||||||
@ -459,7 +460,36 @@ JSSearchResult findAllPathsFromPointJS(int startingNode, float minimumSpeed, flo
|
|||||||
return searchResult;
|
return searchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<JSNodeInfo> getPathJS(uint32_t startingNode, uint32_t endNode) {
|
float calculateRequiredSpeed(float endSpeed, float horizontalDistance, float heightDifference, float dragCoefficient) {
|
||||||
|
// The bike will never reach faster speeds than terminal velocity in free fall
|
||||||
|
float maximumSpeed = sqrt(GRAVITY_ACCELERATION / dragCoefficient);
|
||||||
|
|
||||||
|
// First, check if we'll reach the required speed no matter what
|
||||||
|
if (calculate_speed(0.00001, horizontalDistance, heightDifference, 0.0, maximumSpeed, dragCoefficient) >= endSpeed) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divide and conquer
|
||||||
|
float numerator = 0;
|
||||||
|
float denominator = 0.5;
|
||||||
|
float foundEndSpeed = -100.0;
|
||||||
|
do {
|
||||||
|
numerator *= 2;
|
||||||
|
denominator *= 2;
|
||||||
|
if (foundEndSpeed > endSpeed) {
|
||||||
|
numerator -= 1;
|
||||||
|
} else {
|
||||||
|
numerator += 1;
|
||||||
|
}
|
||||||
|
foundEndSpeed = calculate_speed(maximumSpeed * numerator / denominator, horizontalDistance, heightDifference, 0.01, 100.0, dragCoefficient);
|
||||||
|
} while (fabs(foundEndSpeed - endSpeed) > 0.013);
|
||||||
|
|
||||||
|
float requiredSpeed = maximumSpeed * numerator / denominator;
|
||||||
|
|
||||||
|
return requiredSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<JSNodeInfo> getPathJS(uint32_t startingNode, uint32_t endNode, float dragCoefficient) {
|
||||||
std::vector<JSNodeInfo> path;
|
std::vector<JSNodeInfo> path;
|
||||||
|
|
||||||
if (lastSearchResult.startingNode != startingNode) {
|
if (lastSearchResult.startingNode != startingNode) {
|
||||||
@ -496,6 +526,34 @@ std::vector<JSNodeInfo> getPathJS(uint32_t startingNode, uint32_t endNode) {
|
|||||||
path.push_back(nodeInfo);
|
path.push_back(nodeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float currentRequiredSpeed = -1.0;
|
||||||
|
for (auto it = path.rbegin(); it != path.rend(); it++) {
|
||||||
|
if (currentRequiredSpeed <= -1.0) {
|
||||||
|
it->requiredSpeed = 1.0;
|
||||||
|
currentRequiredSpeed = 1.0;
|
||||||
|
} else {
|
||||||
|
uint32_t currentNodeId = it->nodeId;
|
||||||
|
RoadNode currentNode = set.roadNodes[currentNodeId];
|
||||||
|
uint32_t nextNodeId = (it - 1)->nodeId;
|
||||||
|
RoadNode nextNode = set.roadNodes[nextNodeId];
|
||||||
|
|
||||||
|
float heightDifference = nextNode.position_z - currentNode.position_z;
|
||||||
|
|
||||||
|
Connection neighbours[10];
|
||||||
|
int numberOfNeighbours = 0;
|
||||||
|
getNeighbourConnections(currentNode, neighbours, numberOfNeighbours);
|
||||||
|
for (int i = 0; i < numberOfNeighbours; i++) {
|
||||||
|
Connection neighbour = neighbours[i];
|
||||||
|
if (neighbour.connected_point_number == nextNodeId) {
|
||||||
|
float horizontalDistance = neighbour.distance;
|
||||||
|
currentRequiredSpeed = calculateRequiredSpeed(currentRequiredSpeed, horizontalDistance, heightDifference, dragCoefficient);
|
||||||
|
it->requiredSpeed = currentRequiredSpeed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -761,6 +819,7 @@ EMSCRIPTEN_BINDINGS(my_module) {
|
|||||||
.property("positionY", &JSNodeInfo::positionY)
|
.property("positionY", &JSNodeInfo::positionY)
|
||||||
.property("positionZ", &JSNodeInfo::positionZ)
|
.property("positionZ", &JSNodeInfo::positionZ)
|
||||||
.property("currentSpeed", &JSNodeInfo::currentSpeed)
|
.property("currentSpeed", &JSNodeInfo::currentSpeed)
|
||||||
|
.property("requiredSpeed", &JSNodeInfo::requiredSpeed)
|
||||||
.property("distanceFromStart", &JSNodeInfo::distanceFromStart);
|
.property("distanceFromStart", &JSNodeInfo::distanceFromStart);
|
||||||
|
|
||||||
emscripten::class_<JSSearchResult>("SearchResult")
|
emscripten::class_<JSSearchResult>("SearchResult")
|
||||||
|
|||||||
@ -61,7 +61,8 @@ interface FoundPathsFromNode {
|
|||||||
|
|
||||||
interface GetFullPath {
|
interface GetFullPath {
|
||||||
startNodeId: number,
|
startNodeId: number,
|
||||||
endNodeId: number
|
endNodeId: number,
|
||||||
|
dragCoefficient: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Coordinate {
|
export interface Coordinate {
|
||||||
@ -72,7 +73,8 @@ export interface Coordinate {
|
|||||||
export interface PathSegment {
|
export interface PathSegment {
|
||||||
start: Coordinate,
|
start: Coordinate,
|
||||||
end: Coordinate,
|
end: Coordinate,
|
||||||
speed: number
|
calculatedSpeed: number,
|
||||||
|
requiredSpeed: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReturnFullPath {
|
interface ReturnFullPath {
|
||||||
|
|||||||
@ -160,7 +160,8 @@ function setUpMapHandler() {
|
|||||||
routeWorker.postMessage(message);
|
routeWorker.postMessage(message);
|
||||||
});
|
});
|
||||||
mapHandler.addClickedEndpointListener((startNodeId, endNodeId) => {
|
mapHandler.addClickedEndpointListener((startNodeId, endNodeId) => {
|
||||||
let message: Message = {getFullPath: {startNodeId: startNodeId, endNodeId: endNodeId}};
|
let settings = getSettings();
|
||||||
|
let message: Message = {getFullPath: {startNodeId: startNodeId, endNodeId: endNodeId, dragCoefficient: settings.dragCoefficient}};
|
||||||
routeWorker.postMessage(message);
|
routeWorker.postMessage(message);
|
||||||
});
|
});
|
||||||
mapHandler.addExclusionAreaListener(polygons => {
|
mapHandler.addExclusionAreaListener(polygons => {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import type { Position } from 'geojson';
|
|||||||
import { createPalette } from 'hue-map';
|
import { createPalette } from 'hue-map';
|
||||||
|
|
||||||
const viridisPalette = createPalette({
|
const viridisPalette = createPalette({
|
||||||
map: 'inferno',
|
map: 'viridis',
|
||||||
steps: 100
|
steps: 100
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -312,13 +312,15 @@ class MapHandler {
|
|||||||
let endLeafletCoordinate: LatLngTuple = [pathSegment.end.latitude, pathSegment.end.longitude];
|
let endLeafletCoordinate: LatLngTuple = [pathSegment.end.latitude, pathSegment.end.longitude];
|
||||||
let leafletCoordinates = [startLeafletCoordinate, endLeafletCoordinate];
|
let leafletCoordinates = [startLeafletCoordinate, endLeafletCoordinate];
|
||||||
|
|
||||||
let intensity = Math.round((pathSegment.speed - minimumSpeed) * 99.0 / (maximumSpeed - minimumSpeed));
|
let intensity = Math.round((pathSegment.requiredSpeed - minimumSpeed) * 99.0 / (maximumSpeed - minimumSpeed));
|
||||||
intensity = Math.min(Math.max(intensity, 0), 99);
|
intensity = Math.min(Math.max(intensity, 0), 99);
|
||||||
let color = viridisPalette.format('cssHex')[99-intensity];
|
let color = viridisPalette.format('cssHex')[99-intensity];
|
||||||
|
|
||||||
let polyLine = L.polyline(leafletCoordinates, {color: color}).addTo(this.path);
|
let polyLine = L.polyline(leafletCoordinates, {color: color}).addTo(this.path);
|
||||||
let speedKmh = pathSegment.speed * 3.6;
|
let requiredSpeed = Math.max(minimumSpeed, pathSegment.requiredSpeed);
|
||||||
polyLine.bindTooltip(Math.round(speedKmh) + '.' + (Math.round(speedKmh * 10.0) % 10) + ' km/h');
|
let requiredSpeedKmh = requiredSpeed * 3.6;
|
||||||
|
let requiredSpeedKmhRounded = Math.round(requiredSpeedKmh * 10.0);
|
||||||
|
polyLine.bindTooltip('Required speed: ' + Math.floor(requiredSpeedKmhRounded / 10.0) + '.' + (requiredSpeedKmhRounded % 10) + ' km/h');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -128,7 +128,7 @@ onmessage = async (e) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (message.getFullPath != null) {
|
if (message.getFullPath != null) {
|
||||||
let path = module.getPath(message.getFullPath.startNodeId, message.getFullPath.endNodeId);
|
let path = module.getPath(message.getFullPath.startNodeId, message.getFullPath.endNodeId, message.getFullPath.dragCoefficient);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
sendErrorMessage('Could not get path');
|
sendErrorMessage('Could not get path');
|
||||||
return;
|
return;
|
||||||
@ -150,6 +150,7 @@ onmessage = async (e) => {
|
|||||||
let currentLngLat = proj4('EPSG:32633', 'EPSG:4326', currentUtm);
|
let currentLngLat = proj4('EPSG:32633', 'EPSG:4326', currentUtm);
|
||||||
|
|
||||||
let speed = Math.max(previousPoint.currentSpeed, currentPoint.currentSpeed);
|
let speed = Math.max(previousPoint.currentSpeed, currentPoint.currentSpeed);
|
||||||
|
let requiredSpeed = Math.max(previousPoint.requiredSpeed, currentPoint.requiredSpeed);
|
||||||
let segment: PathSegment = {
|
let segment: PathSegment = {
|
||||||
start: {
|
start: {
|
||||||
latitude: previousLngLat[1],
|
latitude: previousLngLat[1],
|
||||||
@ -159,7 +160,8 @@ onmessage = async (e) => {
|
|||||||
latitude: currentLngLat[1],
|
latitude: currentLngLat[1],
|
||||||
longitude: currentLngLat[0]
|
longitude: currentLngLat[0]
|
||||||
},
|
},
|
||||||
speed: speed
|
calculatedSpeed: speed,
|
||||||
|
requiredSpeed: requiredSpeed
|
||||||
}
|
}
|
||||||
|
|
||||||
pathSegments.push(segment);
|
pathSegments.push(segment);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user