Allow reverse search, where you find the farthest start point from a given end point
This commit is contained in:
parent
2ca8a14d8f
commit
dc2e8170a4
@ -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>.
|
||||||
|
|||||||
@ -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
|
|
||||||
float slope = atan(slopeTan);
|
|
||||||
float slopeSin = sin(slope);
|
|
||||||
float fullDistance = horizontalDistance * slopeTan / slopeSin;
|
|
||||||
float acceleration = -GRAVITY_ACCELERATION * slopeSin;
|
|
||||||
float terminalVelocity = sqrt(fabs(acceleration) / dragCoefficient);
|
|
||||||
|
|
||||||
// Uphill
|
// If the slope is not flat, we should calculate some trig identities, and how long the slope is
|
||||||
if (slope > 0) {
|
float slope = atan(slopeTan);
|
||||||
float timeToPeak = atan(startingSpeed / terminalVelocity) / (dragCoefficient * terminalVelocity);
|
float slopeSin = sin(slope);
|
||||||
// If the discriminant is greater than 1, the slope is so steep that we cannot reach the end with our starting speed
|
float fullDistance = horizontalDistance * slopeTan / slopeSin;
|
||||||
float discriminant = cos(dragCoefficient * terminalVelocity * timeToPeak) * exp(fullDistance * dragCoefficient);
|
|
||||||
if (discriminant > 1.f) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
float timeToReachEnd = timeToPeak - acos(discriminant) / (dragCoefficient * terminalVelocity);
|
// We need to calculate the terminal velocity given the slope we're in
|
||||||
finalSpeed = terminalVelocity * tan(dragCoefficient * terminalVelocity * (timeToPeak - timeToReachEnd));
|
float terminalVelocity = sqrt(fabs(GRAVITY_ACCELERATION * slopeSin) / dragCoefficient);
|
||||||
} else {
|
|
||||||
// Downhill
|
// First, calculate the final speed if we're going uphill
|
||||||
// If the starting speed is very close to the terminal velocity, we'll just stay at terminal velocity
|
if (slope > 0) {
|
||||||
if (fabs(startingSpeed - terminalVelocity) < 0.001) {
|
float timeToPeak = atan(startingSpeed / terminalVelocity) / (dragCoefficient * terminalVelocity);
|
||||||
finalSpeed = terminalVelocity;
|
float discriminant = exp(fullDistance * dragCoefficient) * cos(dragCoefficient * terminalVelocity * timeToPeak);
|
||||||
} else if (startingSpeed < terminalVelocity) {
|
if (discriminant > 1.f) {
|
||||||
float k1 = terminalVelocity * log((terminalVelocity + startingSpeed) / (terminalVelocity - startingSpeed)) * 0.5;
|
// If this value is greater than 1, it means that the slope is too steep and we can never reach the top with our
|
||||||
float k2 = -log(cosh(k1 / terminalVelocity)) / dragCoefficient;
|
// starting speed
|
||||||
float timeSpent = acosh(exp(dragCoefficient * (fullDistance - k2))) / (dragCoefficient * terminalVelocity) - k1 / (dragCoefficient * pow(terminalVelocity, 2));
|
return -1;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return terminalVelocity * tan(acos(discriminant));
|
||||||
}
|
}
|
||||||
|
|
||||||
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,12 +405,22 @@ 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);
|
||||||
continue;
|
if (resultingSpeed < minimumSpeed) {
|
||||||
|
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) {
|
||||||
float courseDifference = fabs(currentCourse - neighbour.course);
|
float courseDifference = fabs(currentCourse - neighbour.course);
|
||||||
@ -421,18 +428,34 @@ 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 (resultIterator != result.reachableNodes.end() && resultingSpeed <= resultIterator->second.currentSpeed) {
|
if (reverse) {
|
||||||
continue;
|
if (resultIterator != result.reachableNodes.end() && resultingSpeed >= resultIterator->second.currentSpeed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (resultIterator != result.reachableNodes.end() && resultingSpeed <= resultIterator->second.currentSpeed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchNodeInfo reachableNodeInfo;
|
SearchNodeInfo reachableNodeInfo;
|
||||||
@ -447,20 +470,38 @@ 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 (nextNode == NULL || resultingSpeed < nextNode->currentSpeed) {
|
if (reverse) {
|
||||||
neighbourListNode->next = nextNode;
|
if (nextNode == NULL || resultingSpeed > nextNode->currentSpeed) {
|
||||||
nextNode = neighbourListNode;
|
neighbourListNode->next = nextNode;
|
||||||
} else {
|
nextNode = neighbourListNode;
|
||||||
ListNode* previousSearchNode = nextNode;
|
} else {
|
||||||
ListNode* currentSearchNode = nextNode->next;
|
ListNode* previousSearchNode = nextNode;
|
||||||
|
ListNode* currentSearchNode = nextNode->next;
|
||||||
|
|
||||||
while(currentSearchNode != NULL && currentSearchNode->currentSpeed > resultingSpeed) {
|
while (currentSearchNode != NULL && currentSearchNode->currentSpeed < resultingSpeed) {
|
||||||
previousSearchNode = currentSearchNode;
|
previousSearchNode = currentSearchNode;
|
||||||
currentSearchNode = currentSearchNode->next;
|
currentSearchNode = currentSearchNode->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
previousSearchNode->next = neighbourListNode;
|
||||||
|
neighbourListNode->next = currentSearchNode;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (nextNode == NULL || resultingSpeed < nextNode->currentSpeed) {
|
||||||
|
neighbourListNode->next = nextNode;
|
||||||
|
nextNode = neighbourListNode;
|
||||||
|
} else {
|
||||||
|
ListNode* previousSearchNode = nextNode;
|
||||||
|
ListNode* currentSearchNode = nextNode->next;
|
||||||
|
|
||||||
previousSearchNode->next = neighbourListNode;
|
while(currentSearchNode != NULL && currentSearchNode->currentSpeed > resultingSpeed) {
|
||||||
neighbourListNode->next = currentSearchNode;
|
previousSearchNode = currentSearchNode;
|
||||||
|
currentSearchNode = currentSearchNode->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
previousSearchNode->next = neighbourListNode;
|
||||||
|
neighbourListNode->next = currentSearchNode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -468,8 +509,8 @@ 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<>()
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
23
src/main.ts
23
src/main.ts
@ -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)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var settings;
|
||||||
|
if (reverse) {
|
||||||
|
settings = {icon: redIcon};
|
||||||
|
} else {
|
||||||
|
settings = {icon: greenIcon};
|
||||||
|
}
|
||||||
|
|
||||||
this.currentStartPoint = nodeId;
|
this.currentStartPoint = nodeId;
|
||||||
this.endMarkers.clearLayers();
|
this.endMarkers.clearLayers();
|
||||||
this.startMarker = L.marker([latitude, longitude], {icon: greenIcon}).addTo(this.map);
|
this.startMarker = L.marker([latitude, longitude], settings).addTo(this.map);
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawEndPoints(endpoints: Endpoint[]): void {
|
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');
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user