Allow reverse search, where you find the farthest start point from a given end point

This commit is contained in:
Martin Asprusten 2025-07-05 17:06:52 +02:00
parent 2ca8a14d8f
commit dc2e8170a4
6 changed files with 171 additions and 91 deletions

View File

@ -32,6 +32,9 @@
<br /> <br />
<button id="search-button">Start search</button> <button id="search-button">Start search</button>
<button id="export-route-button">Export KML route</button> <button id="export-route-button">Export KML route</button>
<br />
<label for="reverse-search-input">Reverse search:</label><input type="checkbox" id="reverse-search-input" />
<br />
<p id="search-status-paragraph"></p> <p id="search-status-paragraph"></p>
<table id="search-result-table" style="display: none;"> <table id="search-result-table" style="display: none;">
<thead> <thead>
@ -80,6 +83,10 @@
end points are shown as purple circles. Click an endpoint to see the calculated route that leads from the start point end points are shown as purple circles. Click an endpoint to see the calculated route that leads from the start point
to the end point. to the end point.
</p> </p>
<p>
It is also possible to reverse the search, to find the farthest start point from a given end point. When this is done,
farthest possible start point is shown in green, and other candidate start points are shown in orange.
</p>
<p> <p>
It is possible to mark areas on the map you would like to avoid. Click the "Draw areas that should not be It is possible to mark areas on the map you would like to avoid. Click the "Draw areas that should not be
entered" button at the top left of the map. It is marked with this icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="20px"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc. --> <path d="M367.2 412.5L99.5 144.8C77.1 176.1 64 214.5 64 256c0 106 86 192 192 192c41.5 0 79.9-13.1 111.2-35.5zm45.3-45.3C434.9 335.9 448 297.5 448 256c0-106-86-192-192-192c-41.5 0-79.9 13.1-111.2 35.5L412.5 367.2zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256z"/></svg>. entered" button at the top left of the map. It is marked with this icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="20px"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc. --> <path d="M367.2 412.5L99.5 144.8C77.1 176.1 64 214.5 64 256c0 106 86 192 192 192c41.5 0 79.9-13.1 111.2-35.5zm45.3-45.3C434.9 335.9 448 297.5 448 256c0-106-86-192-192-192c-41.5 0-79.9 13.1-111.2 35.5L412.5 367.2zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256z"/></svg>.

View File

@ -51,6 +51,7 @@ struct SearchResult {
uint32_t startingNode; uint32_t startingNode;
std::map<uint32_t, uint32_t> previous; std::map<uint32_t, uint32_t> previous;
std::map<uint32_t, SearchNodeInfo> reachableNodes; std::map<uint32_t, SearchNodeInfo> reachableNodes;
bool reverse;
}; };
struct JSNodeInfo { struct JSNodeInfo {
@ -65,6 +66,7 @@ struct JSNodeInfo {
struct JSSearchResult { struct JSSearchResult {
std::vector<JSNodeInfo> endPoints; std::vector<JSNodeInfo> endPoints;
bool reverse;
}; };
struct ListNode { struct ListNode {
@ -240,57 +242,51 @@ JSNodeInfo findClosestNode(float positionX, float positionY) {
return result; return result;
} }
float calculateSpeed(float startingSpeed, float horizontalDistance, float heightDifference, float minimumSpeed, float maximumSpeed, float dragCoefficient) { float calculateSpeed(float startingSpeed, float horizontalDistance, float heightDifference, float dragCoefficient) {
float slopeTan = heightDifference / horizontalDistance; float slopeTan = heightDifference / horizontalDistance;
float finalSpeed = -1;
// If the slope is flat, that is one calculation // If the slope is flat, that is one calculation
if (fabs(slopeTan) < 0.0001) { if (fabs(slopeTan) < 0.0001) {
float timeToFinish = (exp(horizontalDistance * dragCoefficient) - 1) / (startingSpeed * dragCoefficient); return startingSpeed * exp(-dragCoefficient * horizontalDistance);
finalSpeed = startingSpeed / (startingSpeed * dragCoefficient * timeToFinish + 1); }
} else {
// Otherwise, we need to find some parameters // If the slope is not flat, we should calculate some trig identities, and how long the slope is
float slope = atan(slopeTan); float slope = atan(slopeTan);
float slopeSin = sin(slope); float slopeSin = sin(slope);
float fullDistance = horizontalDistance * slopeTan / slopeSin; float fullDistance = horizontalDistance * slopeTan / slopeSin;
float acceleration = -GRAVITY_ACCELERATION * slopeSin;
float terminalVelocity = sqrt(fabs(acceleration) / dragCoefficient);
// Uphill // We need to calculate the terminal velocity given the slope we're in
float terminalVelocity = sqrt(fabs(GRAVITY_ACCELERATION * slopeSin) / dragCoefficient);
// First, calculate the final speed if we're going uphill
if (slope > 0) { if (slope > 0) {
float timeToPeak = atan(startingSpeed / terminalVelocity) / (dragCoefficient * terminalVelocity); float timeToPeak = atan(startingSpeed / terminalVelocity) / (dragCoefficient * terminalVelocity);
// If the discriminant is greater than 1, the slope is so steep that we cannot reach the end with our starting speed float discriminant = exp(fullDistance * dragCoefficient) * cos(dragCoefficient * terminalVelocity * timeToPeak);
float discriminant = cos(dragCoefficient * terminalVelocity * timeToPeak) * exp(fullDistance * dragCoefficient);
if (discriminant > 1.f) { if (discriminant > 1.f) {
// If this value is greater than 1, it means that the slope is too steep and we can never reach the top with our
// starting speed
return -1; return -1;
} }
return terminalVelocity * tan(acos(discriminant));
float timeToReachEnd = timeToPeak - acos(discriminant) / (dragCoefficient * terminalVelocity);
finalSpeed = terminalVelocity * tan(dragCoefficient * terminalVelocity * (timeToPeak - timeToReachEnd));
} else {
// Downhill
// If the starting speed is very close to the terminal velocity, we'll just stay at terminal velocity
if (fabs(startingSpeed - terminalVelocity) < 0.001) {
finalSpeed = terminalVelocity;
} else if (startingSpeed < terminalVelocity) {
float k1 = terminalVelocity * log((terminalVelocity + startingSpeed) / (terminalVelocity - startingSpeed)) * 0.5;
float k2 = -log(cosh(k1 / terminalVelocity)) / dragCoefficient;
float timeSpent = acosh(exp(dragCoefficient * (fullDistance - k2))) / (dragCoefficient * terminalVelocity) - k1 / (dragCoefficient * pow(terminalVelocity, 2));
finalSpeed = terminalVelocity * tanh(dragCoefficient * terminalVelocity * timeSpent + k1 / terminalVelocity);
} else if (startingSpeed > terminalVelocity) {
float k1 = log((startingSpeed - terminalVelocity) / (startingSpeed + terminalVelocity)) * terminalVelocity / 2;
float k2 = -log(-sinh(k1 / terminalVelocity)) / dragCoefficient;
float timeSpent = k1 / (dragCoefficient * pow(terminalVelocity, 2)) - asinh(-exp(dragCoefficient * (fullDistance - k2))) / (dragCoefficient * terminalVelocity);
finalSpeed = -terminalVelocity / tanh(k1 / terminalVelocity - dragCoefficient * terminalVelocity * timeSpent);
}
}
} }
if (finalSpeed < minimumSpeed) { // Downhill must be split in three: starting slower than terminal velocity, starting at terminal velocity, and starting
return -1; // above terminal velocity
} else { if (fabs(startingSpeed - terminalVelocity) < 0.0001) {
return std::fmin(finalSpeed, maximumSpeed); return terminalVelocity;
} }
// If we're going faster than terminal velocity
if (startingSpeed > terminalVelocity) {
float k1 = log((startingSpeed - terminalVelocity) / (startingSpeed + terminalVelocity));
float tanhInput = asinh(exp(dragCoefficient * fullDistance)*sinh(k1 * 0.5));
return -terminalVelocity / tanh(tanhInput);
}
// We only get here if we're going slower than terminal velocity
float k1 = log((terminalVelocity + startingSpeed) / (terminalVelocity - startingSpeed));
float tanhInput = acosh(exp(dragCoefficient * fullDistance) * cosh(k1 * 0.5));
return terminalVelocity * tanh(tanhInput);
} }
float calculateRequiredSpeed(float endSpeed, float horizontalDistance, float heightDifference, float dragCoefficient) { float calculateRequiredSpeed(float endSpeed, float horizontalDistance, float heightDifference, float dragCoefficient) {
@ -352,8 +348,9 @@ void getNeighbourConnections(RoadNode node, Connection* targetArray, int &number
} }
} }
SearchResult findAllPathsFromPoint(int startingNode, float minimumSpeed, float maximumSpeed, int maximumSpeedLimit, float dragCoefficient, bool allowMotorways, bool allowTunnels, bool allowAgainstOneway, bool limitCornerSpeed) { SearchResult findAllPathsFromPoint(int startingNode, float minimumSpeed, float maximumSpeed, int maximumSpeedLimit, float dragCoefficient, bool allowMotorways, bool allowTunnels, bool allowAgainstOneway, bool limitCornerSpeed, bool reverse) {
SearchResult result; SearchResult result;
result.reverse = reverse;
result.startingNode = startingNode; result.startingNode = startingNode;
RoadNode firstNode = set.roadNodes[startingNode]; RoadNode firstNode = set.roadNodes[startingNode];
@ -408,11 +405,21 @@ SearchResult findAllPathsFromPoint(int startingNode, float minimumSpeed, float m
RoadNode neighbourNode = set.roadNodes[neighbour.connectedPointNumber]; RoadNode neighbourNode = set.roadNodes[neighbour.connectedPointNumber];
float heightDifference = neighbourNode.positionZ - bestNode.positionZ; float heightDifference = neighbourNode.positionZ - bestNode.positionZ;
float resultingSpeed = calculateSpeed(currentSpeed, neighbour.distance, heightDifference, minimumSpeed, maximumSpeed, dragCoefficient); float resultingSpeed = -1;
if (!reverse) {
if (resultingSpeed < 0) { resultingSpeed = calculateSpeed(currentSpeed, neighbour.distance, heightDifference, dragCoefficient);
if (resultingSpeed < minimumSpeed) {
continue; continue;
} }
resultingSpeed = fmin(resultingSpeed, maximumSpeed);
} else {
resultingSpeed = calculateRequiredSpeed(currentSpeed, neighbour.distance, -heightDifference, dragCoefficient);
if (resultingSpeed > maximumSpeed) {
continue;
}
resultingSpeed = fmax(resultingSpeed, minimumSpeed);
}
// If we limit the speed on corners, do that here // If we limit the speed on corners, do that here
if (limitCornerSpeed) { if (limitCornerSpeed) {
@ -421,19 +428,35 @@ SearchResult findAllPathsFromPoint(int startingNode, float minimumSpeed, float m
courseDifference = 360 - courseDifference; courseDifference = 360 - courseDifference;
} }
float maximumCornerSpeed;
if (courseDifference > 95) { if (courseDifference > 95) {
resultingSpeed = minimumSpeed; maximumCornerSpeed = minimumSpeed;
} else if (courseDifference > 45.0) { } else if (courseDifference > 45.0) {
float maximumCornerSpeed = (95 - courseDifference) / 50.0 * (maximumSpeed - minimumSpeed) + minimumSpeed; maximumCornerSpeed = (95 - courseDifference) / 50.0 * (maximumSpeed - minimumSpeed) + minimumSpeed;
resultingSpeed = fmin(resultingSpeed, maximumCornerSpeed); } else {
maximumCornerSpeed = maximumSpeed;
}
if (reverse) {
if (resultingSpeed > maximumCornerSpeed) {
continue;
}
} else {
resultingSpeed = fmin(maximumCornerSpeed, resultingSpeed);
} }
} }
// Check if this node is already in the reachable nodes map // Check if this node is already in the reachable nodes map
auto resultIterator = result.reachableNodes.find(neighbour.connectedPointNumber); auto resultIterator = result.reachableNodes.find(neighbour.connectedPointNumber);
if (reverse) {
if (resultIterator != result.reachableNodes.end() && resultingSpeed >= resultIterator->second.currentSpeed) {
continue;
}
} else {
if (resultIterator != result.reachableNodes.end() && resultingSpeed <= resultIterator->second.currentSpeed) { if (resultIterator != result.reachableNodes.end() && resultingSpeed <= resultIterator->second.currentSpeed) {
continue; continue;
} }
}
SearchNodeInfo reachableNodeInfo; SearchNodeInfo reachableNodeInfo;
reachableNodeInfo.currentSpeed = resultingSpeed; reachableNodeInfo.currentSpeed = resultingSpeed;
@ -447,6 +470,23 @@ SearchResult findAllPathsFromPoint(int startingNode, float minimumSpeed, float m
neighbourListNode->currentSpeed = reachableNodeInfo.currentSpeed; neighbourListNode->currentSpeed = reachableNodeInfo.currentSpeed;
neighbourListNode->currentCourse = neighbour.course; neighbourListNode->currentCourse = neighbour.course;
if (reverse) {
if (nextNode == NULL || resultingSpeed > nextNode->currentSpeed) {
neighbourListNode->next = nextNode;
nextNode = neighbourListNode;
} else {
ListNode* previousSearchNode = nextNode;
ListNode* currentSearchNode = nextNode->next;
while (currentSearchNode != NULL && currentSearchNode->currentSpeed < resultingSpeed) {
previousSearchNode = currentSearchNode;
currentSearchNode = currentSearchNode->next;
}
previousSearchNode->next = neighbourListNode;
neighbourListNode->next = currentSearchNode;
}
} else {
if (nextNode == NULL || resultingSpeed < nextNode->currentSpeed) { if (nextNode == NULL || resultingSpeed < nextNode->currentSpeed) {
neighbourListNode->next = nextNode; neighbourListNode->next = nextNode;
nextNode = neighbourListNode; nextNode = neighbourListNode;
@ -464,12 +504,13 @@ SearchResult findAllPathsFromPoint(int startingNode, float minimumSpeed, float m
} }
} }
} }
}
return result; return result;
} }
JSSearchResult findAllPathsFromPointJS(int startingNode, float minimumSpeed, float maximumSpeed, int maximumSpeedLimit, float dragCoefficient, bool allowMotorways, bool allowTunnels, bool allowAgainstOneway, bool limitCornerSpeed) { JSSearchResult findAllPathsFromPointJS(int startingNode, float minimumSpeed, float maximumSpeed, int maximumSpeedLimit, float dragCoefficient, bool allowMotorways, bool allowTunnels, bool allowAgainstOneway, bool limitCornerSpeed, bool reverse) {
lastSearchResult = findAllPathsFromPoint(startingNode, minimumSpeed, maximumSpeed, maximumSpeedLimit, dragCoefficient, allowMotorways, allowTunnels, allowAgainstOneway, limitCornerSpeed); lastSearchResult = findAllPathsFromPoint(startingNode, minimumSpeed, maximumSpeed, maximumSpeedLimit, dragCoefficient, allowMotorways, allowTunnels, allowAgainstOneway, limitCornerSpeed, reverse);
float startX = set.roadNodes[startingNode].positionX; float startX = set.roadNodes[startingNode].positionX;
float startY = set.roadNodes[startingNode].positionY; float startY = set.roadNodes[startingNode].positionY;
@ -520,6 +561,7 @@ JSSearchResult findAllPathsFromPointJS(int startingNode, float minimumSpeed, flo
JSSearchResult searchResult; JSSearchResult searchResult;
searchResult.endPoints = filteredEndpoints; searchResult.endPoints = filteredEndpoints;
searchResult.reverse = lastSearchResult.reverse;
return searchResult; return searchResult;
} }
@ -560,6 +602,10 @@ std::vector<JSNodeInfo> getPathJS(uint32_t startingNode, uint32_t endNode, float
path.push_back(nodeInfo); path.push_back(nodeInfo);
} }
if (lastSearchResult.reverse) {
std::reverse(path.begin(), path.end());
}
float currentRequiredSpeed = -1.0; float currentRequiredSpeed = -1.0;
for (auto it = path.rbegin(); it != path.rend(); it++) { for (auto it = path.rbegin(); it != path.rend(); it++) {
if (currentRequiredSpeed <= -1.0) { if (currentRequiredSpeed <= -1.0) {
@ -771,7 +817,8 @@ AreaSearchResult continueAreaSearch() {
currentAreaSearch.allowMotorways, currentAreaSearch.allowMotorways,
currentAreaSearch.allowTunnels, currentAreaSearch.allowTunnels,
currentAreaSearch.allowAgainstOneway, currentAreaSearch.allowAgainstOneway,
currentAreaSearch.limitCornerSpeed currentAreaSearch.limitCornerSpeed,
false
); );
// Remove all nodes we have reached from here as possible future start nodes // Remove all nodes we have reached from here as possible future start nodes
@ -919,7 +966,8 @@ EMSCRIPTEN_BINDINGS(my_module) {
emscripten::class_<JSSearchResult>("SearchResult") emscripten::class_<JSSearchResult>("SearchResult")
.constructor<>() .constructor<>()
.property("endPoints", &JSSearchResult::endPoints); .property("endPoints", &JSSearchResult::endPoints)
.property("reverse", &JSSearchResult::reverse);
emscripten::class_<PolygonCoordinate>("PolygonCoordinate") emscripten::class_<PolygonCoordinate>("PolygonCoordinate")
.constructor<>() .constructor<>()

View File

@ -45,7 +45,8 @@ interface FindPathsFromNode {
allowMotorways: boolean, allowMotorways: boolean,
allowTunnels: boolean, allowTunnels: boolean,
allowAgainstOneway: boolean, allowAgainstOneway: boolean,
limitCornerSpeed: boolean limitCornerSpeed: boolean,
reverse: boolean
} }
export interface Endpoint { export interface Endpoint {
@ -57,7 +58,8 @@ export interface Endpoint {
interface FoundPathsFromNode { interface FoundPathsFromNode {
nodeId: number, nodeId: number,
endpoints: Endpoint[] endpoints: Endpoint[],
reverse: boolean
} }
interface GetFullPath { interface GetFullPath {

View File

@ -17,7 +17,8 @@ interface Settings {
allowTunnels: boolean, allowTunnels: boolean,
allowAgainstOneway: boolean, allowAgainstOneway: boolean,
limitCornerSpeed: boolean, limitCornerSpeed: boolean,
cutoffDistance: number cutoffDistance: number,
reverse: boolean
} }
// Default settings values // Default settings values
@ -47,6 +48,7 @@ let loadingContainer = document.getElementById('loadingcontainer');
let mapContainer = document.getElementById('mapcontainer'); let mapContainer = document.getElementById('mapcontainer');
let searchButton = document.getElementById('search-button'); let searchButton = document.getElementById('search-button');
let exportButton = document.getElementById('export-route-button'); let exportButton = document.getElementById('export-route-button');
let reverseCheckbox = document.getElementById('reverse-search-input');
let searchStatusParagraph = document.getElementById('search-status-paragraph'); let searchStatusParagraph = document.getElementById('search-status-paragraph');
let searchResultsTable = document.getElementById('search-result-table'); let searchResultsTable = document.getElementById('search-result-table');
let searchResultTableBody = document.getElementById('search-result-table-body'); let searchResultTableBody = document.getElementById('search-result-table-body');
@ -72,8 +74,8 @@ routeWorker.onmessage = e => {
} }
else if (message.foundClosestNode != null) { else if (message.foundClosestNode != null) {
mapHandler?.drawStartNode(message.foundClosestNode.foundNodeId, message.foundClosestNode.foundLatitude, message.foundClosestNode.foundLongitude);
let settings = getSettings(); let settings = getSettings();
mapHandler?.drawStartNode(message.foundClosestNode.foundNodeId, message.foundClosestNode.foundLatitude, message.foundClosestNode.foundLongitude, settings.reverse);
let findPathsMessage: Message = {findPathsFromNode: { let findPathsMessage: Message = {findPathsFromNode: {
nodeId: message.foundClosestNode.foundNodeId, nodeId: message.foundClosestNode.foundNodeId,
minimumSpeed: settings.minimumSpeed, minimumSpeed: settings.minimumSpeed,
@ -83,11 +85,12 @@ routeWorker.onmessage = e => {
allowMotorways: settings.allowMotorways, allowMotorways: settings.allowMotorways,
allowTunnels: settings.allowTunnels, allowTunnels: settings.allowTunnels,
allowAgainstOneway: settings.allowAgainstOneway, allowAgainstOneway: settings.allowAgainstOneway,
limitCornerSpeed: settings.limitCornerSpeed limitCornerSpeed: settings.limitCornerSpeed,
reverse: settings.reverse
}}; }};
routeWorker.postMessage(findPathsMessage); routeWorker.postMessage(findPathsMessage);
} else if (message.foundPathsFromNode != null) { } else if (message.foundPathsFromNode != null) {
mapHandler?.drawEndPoints(message.foundPathsFromNode.endpoints); mapHandler?.drawEndPoints(message.foundPathsFromNode.endpoints, message.foundPathsFromNode.reverse);
} else if (message.returnFullPath != null) { } else if (message.returnFullPath != null) {
let settings = getSettings(); let settings = getSettings();
mapHandler?.drawPath(message.returnFullPath.pathSegments, settings.minimumSpeed, settings.maximumSpeed); mapHandler?.drawPath(message.returnFullPath.pathSegments, settings.minimumSpeed, settings.maximumSpeed);
@ -130,8 +133,7 @@ routeWorker.onmessage = e => {
button.setHTMLUnsafe('Show in map'); button.setHTMLUnsafe('Show in map');
button.addEventListener('click', _ => { button.addEventListener('click', _ => {
mapHandler?.drawStartNode(result.nodeId, result.latitude, result.longitude); mapHandler?.drawStartNode(result.nodeId, result.latitude, result.longitude, false);
let settings = getSettings();
let requestMessage: Message = { let requestMessage: Message = {
findPathsFromNode: { findPathsFromNode: {
nodeId: result.nodeId, nodeId: result.nodeId,
@ -142,7 +144,8 @@ routeWorker.onmessage = e => {
allowMotorways: settings.allowMotorways, allowMotorways: settings.allowMotorways,
allowTunnels: settings.allowTunnels, allowTunnels: settings.allowTunnels,
allowAgainstOneway: settings.allowAgainstOneway, allowAgainstOneway: settings.allowAgainstOneway,
limitCornerSpeed: settings.limitCornerSpeed limitCornerSpeed: settings.limitCornerSpeed,
reverse: false
} }
}; };
routeWorker.postMessage(requestMessage); routeWorker.postMessage(requestMessage);
@ -195,7 +198,8 @@ function setUpMapHandler() {
allowMotorways: settings.allowMotorways, allowMotorways: settings.allowMotorways,
allowTunnels: settings.allowTunnels, allowTunnels: settings.allowTunnels,
allowAgainstOneway: settings.allowAgainstOneway, allowAgainstOneway: settings.allowAgainstOneway,
limitCornerSpeed: settings.limitCornerSpeed limitCornerSpeed: settings.limitCornerSpeed,
reverse: settings.reverse
}}; }};
routeWorker.postMessage(newRoutesMessage); routeWorker.postMessage(newRoutesMessage);
} }
@ -325,7 +329,8 @@ function getSettings(): Settings {
allowTunnels: getBooleanValue(allowTunnelsInput, DEFAULT_ALLOW_TUNNELS), allowTunnels: getBooleanValue(allowTunnelsInput, DEFAULT_ALLOW_TUNNELS),
allowAgainstOneway: getBooleanValue(allowAgainstOnewayInput, DEFAULT_ALLOW_AGAINST_ONE_WAY), allowAgainstOneway: getBooleanValue(allowAgainstOnewayInput, DEFAULT_ALLOW_AGAINST_ONE_WAY),
limitCornerSpeed: getBooleanValue(limitCornerSpeedInput, DEFAULT_LIMIT_CORNER_SPEED), limitCornerSpeed: getBooleanValue(limitCornerSpeedInput, DEFAULT_LIMIT_CORNER_SPEED),
cutoffDistance: getNumberValue(cutoffDistanceInput, DEFAULT_CUTOFF_DISTANCE) cutoffDistance: getNumberValue(cutoffDistanceInput, DEFAULT_CUTOFF_DISTANCE),
reverse: getBooleanValue(reverseCheckbox, false)
}; };
} }

View File

@ -250,28 +250,44 @@ class MapHandler {
this.searchAreaPolygonListeners.push(searchAreaListener); this.searchAreaPolygonListeners.push(searchAreaListener);
} }
public drawStartNode(nodeId: number, latitude: number, longitude: number): void { public drawStartNode(nodeId: number, latitude: number, longitude: number, reverse: boolean): void {
this.path.clearLayers(); this.path.clearLayers();
if (this.startMarker != null) { if (this.startMarker != null) {
this.map.removeLayer(this.startMarker); this.map.removeLayer(this.startMarker);
} }
this.currentStartPoint = nodeId; var settings;
this.endMarkers.clearLayers(); if (reverse) {
this.startMarker = L.marker([latitude, longitude], {icon: greenIcon}).addTo(this.map); settings = {icon: redIcon};
} else {
settings = {icon: greenIcon};
} }
public drawEndPoints(endpoints: Endpoint[]): void { this.currentStartPoint = nodeId;
this.endMarkers.clearLayers();
this.startMarker = L.marker([latitude, longitude], settings).addTo(this.map);
}
public drawEndPoints(endpoints: Endpoint[], reverse: boolean): void {
this.endMarkers.clearLayers(); this.endMarkers.clearLayers();
this.path.clearLayers(); this.path.clearLayers();
var firstMarker = true; var firstMarker = true;
var color = reverse ? 'orange' : 'violet';
var settings: L.MarkerOptions;
if (reverse) {
settings = {icon: greenIcon};
} else {
settings = {icon: redIcon};
}
endpoints.forEach(endpoint => { endpoints.forEach(endpoint => {
var marker; var marker;
if (firstMarker) { if (firstMarker) {
marker = L.marker([endpoint.latitude, endpoint.longitude], {icon: redIcon}).addTo(this.endMarkers); marker = L.marker([endpoint.latitude, endpoint.longitude], settings).addTo(this.endMarkers);
} else { } else {
marker = L.circleMarker([endpoint.latitude, endpoint.longitude], {radius: 2, fillOpacity: 1.0, color: 'purple', bubblingMouseEvents: false}).addTo(this.endMarkers); marker = L.circleMarker([endpoint.latitude, endpoint.longitude], {radius: 2, fillOpacity: 1.0, color: color, bubblingMouseEvents: false}).addTo(this.endMarkers);
} }
marker.bindTooltip(Math.round(endpoint.distanceFromStart) + 'm'); marker.bindTooltip(Math.round(endpoint.distanceFromStart) + 'm');

View File

@ -103,7 +103,8 @@ onmessage = async (e) => {
message.findPathsFromNode.allowMotorways, message.findPathsFromNode.allowMotorways,
message.findPathsFromNode.allowTunnels, message.findPathsFromNode.allowTunnels,
message.findPathsFromNode.allowAgainstOneway, message.findPathsFromNode.allowAgainstOneway,
message.findPathsFromNode.limitCornerSpeed message.findPathsFromNode.limitCornerSpeed,
message.findPathsFromNode.reverse
); );
let endpoints: Endpoint[] = []; let endpoints: Endpoint[] = [];
@ -122,9 +123,10 @@ onmessage = async (e) => {
distanceFromStart: nodeData.distanceFromStart distanceFromStart: nodeData.distanceFromStart
}); });
} }
let reverse: boolean = results.reverse;
results.delete(); results.delete();
let returnMessage: Message = {foundPathsFromNode: {nodeId: message.findPathsFromNode.nodeId, endpoints: endpoints}}; let returnMessage: Message = {foundPathsFromNode: {nodeId: message.findPathsFromNode.nodeId, endpoints: endpoints, reverse: reverse}};
postMessage(returnMessage); postMessage(returnMessage);
} }