2023-02-03 23:35:22 +01:00
|
|
|
|
using OSMDatastructure;
|
2023-03-14 17:00:59 +01:00
|
|
|
|
using OSMDatastructure.Graph;
|
2023-02-03 23:35:22 +01:00
|
|
|
|
using OSMImporter;
|
|
|
|
|
|
|
|
|
|
namespace Pathfinding;
|
|
|
|
|
|
2023-04-06 02:23:12 +02:00
|
|
|
|
public static class Pathfinder
|
2023-02-03 23:35:22 +01:00
|
|
|
|
{
|
2023-04-06 14:46:08 +02:00
|
|
|
|
public static ValueTuple<TimeSpan, List<PathNode>> CustomAStar(string workingDir, Coordinates start, Coordinates goal, Tag.SpeedType vehicle)
|
2023-02-03 23:35:22 +01:00
|
|
|
|
{
|
2023-04-06 14:46:08 +02:00
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
|
|
|
TimeSpan calcTime;
|
2023-04-06 14:31:38 +02:00
|
|
|
|
RegionManager regionManager = new (workingDir);
|
|
|
|
|
OsmNode? startNode = ClosestNodeToCoordinates(start, vehicle, ref regionManager);
|
|
|
|
|
OsmNode? goalNode = ClosestNodeToCoordinates(goal, vehicle, ref regionManager);
|
2023-02-06 17:32:55 +01:00
|
|
|
|
if (startNode == null || goalNode == null)
|
2023-04-06 14:46:08 +02:00
|
|
|
|
{
|
|
|
|
|
calcTime = DateTime.Now - startTime;
|
|
|
|
|
return new ValueTuple<TimeSpan, List<PathNode>>(calcTime, new List<PathNode>());
|
|
|
|
|
}
|
2023-04-01 18:10:21 +02:00
|
|
|
|
|
|
|
|
|
PriorityQueue<OsmNode, double> toVisit = new();
|
|
|
|
|
toVisit.Enqueue(startNode, 0);
|
|
|
|
|
startNode.currentPathWeight = 0;
|
|
|
|
|
startNode.currentPathLength = 0;
|
|
|
|
|
startNode.directDistanceToGoal = Utils.DistanceBetween(startNode, goalNode);
|
2023-02-05 20:03:47 +01:00
|
|
|
|
bool stop = false;
|
|
|
|
|
|
2023-04-06 02:23:12 +02:00
|
|
|
|
while (toVisit.Count > 0)
|
2023-02-05 20:03:47 +01:00
|
|
|
|
{
|
2023-04-06 14:27:25 +02:00
|
|
|
|
OsmNode closestNodeToGoal = toVisit.Dequeue();
|
2023-04-06 14:46:08 +02:00
|
|
|
|
//Console.WriteLine($"{toVisit.Count:000} {closestNodeToGoal.directDistanceToGoal:#.00} current:{closestNodeToGoal}");
|
2023-04-01 14:43:05 +02:00
|
|
|
|
|
|
|
|
|
foreach (OsmEdge edge in closestNodeToGoal.edges)
|
2023-02-05 20:03:47 +01:00
|
|
|
|
{
|
2023-04-01 14:43:05 +02:00
|
|
|
|
OsmNode? neighbor = regionManager.GetNode(edge.neighborId, edge.neighborRegion);
|
|
|
|
|
if (neighbor is not null)
|
2023-02-05 20:03:47 +01:00
|
|
|
|
{
|
2023-02-08 19:05:13 +01:00
|
|
|
|
double newPotentialWeight =
|
2023-04-01 14:43:05 +02:00
|
|
|
|
closestNodeToGoal.currentPathWeight + EdgeWeight(closestNodeToGoal, neighbor, edge.wayId, vehicle, ref regionManager);
|
2023-04-06 02:32:04 +02:00
|
|
|
|
if (newPotentialWeight < neighbor.currentPathWeight)
|
2023-02-08 19:05:13 +01:00
|
|
|
|
{
|
|
|
|
|
neighbor.previousPathNode = closestNodeToGoal;
|
|
|
|
|
neighbor.currentPathWeight = newPotentialWeight;
|
2023-02-08 19:09:46 +01:00
|
|
|
|
neighbor.currentPathLength = closestNodeToGoal.currentPathLength + Utils.DistanceBetween(closestNodeToGoal, neighbor);
|
2023-04-06 14:27:25 +02:00
|
|
|
|
neighbor.directDistanceToGoal = Utils.DistanceBetween(neighbor, goalNode);
|
2023-04-06 02:23:12 +02:00
|
|
|
|
|
|
|
|
|
if (neighbor.Equals(goalNode) || closestNodeToGoal.directDistanceToGoal < 10)
|
|
|
|
|
{
|
2023-02-08 19:05:13 +01:00
|
|
|
|
stop = true;
|
2023-04-06 02:23:12 +02:00
|
|
|
|
goalNode = neighbor;
|
|
|
|
|
}
|
|
|
|
|
else if(!stop)
|
2023-04-01 18:10:21 +02:00
|
|
|
|
{
|
|
|
|
|
toVisit.Enqueue(neighbor, neighbor.directDistanceToGoal);
|
|
|
|
|
}
|
2023-02-08 19:05:13 +01:00
|
|
|
|
}
|
2023-02-05 20:03:47 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 14:27:25 +02:00
|
|
|
|
List<PathNode> path = new();
|
2023-04-06 02:23:12 +02:00
|
|
|
|
OsmNode? currentNode = goalNode;
|
2023-04-06 14:27:25 +02:00
|
|
|
|
while (currentNode is not null)
|
2023-02-05 20:03:47 +01:00
|
|
|
|
{
|
2023-04-06 14:27:25 +02:00
|
|
|
|
path.Add(PathNode.FromOsmNode(currentNode)!);
|
2023-02-06 17:32:55 +01:00
|
|
|
|
currentNode = currentNode.previousPathNode;
|
2023-02-05 20:03:47 +01:00
|
|
|
|
}
|
2023-02-08 19:05:13 +01:00
|
|
|
|
path.Reverse();
|
2023-04-06 14:46:08 +02:00
|
|
|
|
|
|
|
|
|
calcTime = DateTime.Now - startTime;
|
|
|
|
|
return new ValueTuple<TimeSpan, List<PathNode>>(calcTime, path);
|
2023-02-03 23:35:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 14:31:38 +02:00
|
|
|
|
public static OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, Tag.SpeedType vehicle, ref RegionManager regionManager)
|
2023-02-03 23:35:22 +01:00
|
|
|
|
{
|
2023-02-06 17:32:55 +01:00
|
|
|
|
OsmNode? closest = null;
|
|
|
|
|
double distance = double.MaxValue;
|
2023-04-06 14:31:38 +02:00
|
|
|
|
Region? region = regionManager.GetRegion(coordinates);
|
|
|
|
|
if (region is null)
|
|
|
|
|
return null;
|
2023-02-06 17:32:55 +01:00
|
|
|
|
foreach (OsmNode node in region.nodes)
|
2023-02-03 23:35:22 +01:00
|
|
|
|
{
|
2023-04-06 02:32:04 +02:00
|
|
|
|
bool hasConnectionUsingVehicle = false;
|
2023-04-01 18:10:21 +02:00
|
|
|
|
foreach (OsmEdge edge in node.edges)
|
|
|
|
|
{
|
2023-04-06 02:23:12 +02:00
|
|
|
|
double speed = GetSpeed(node, edge.wayId, vehicle, ref regionManager);
|
2023-04-01 18:10:21 +02:00
|
|
|
|
if (speed != 0)
|
2023-04-06 02:32:04 +02:00
|
|
|
|
hasConnectionUsingVehicle = true;
|
2023-04-01 18:10:21 +02:00
|
|
|
|
}
|
2023-02-06 17:32:55 +01:00
|
|
|
|
double nodeDistance = Utils.DistanceBetween(node, coordinates);
|
2023-04-06 02:32:04 +02:00
|
|
|
|
if (nodeDistance < distance && hasConnectionUsingVehicle)
|
2023-02-03 23:35:22 +01:00
|
|
|
|
{
|
2023-02-06 17:32:55 +01:00
|
|
|
|
closest = node;
|
|
|
|
|
distance = nodeDistance;
|
2023-02-03 23:35:22 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-06 17:32:55 +01:00
|
|
|
|
|
|
|
|
|
return closest;
|
2023-04-01 14:43:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 02:23:12 +02:00
|
|
|
|
private static double GetSpeed(OsmNode node1, ulong wayId, Tag.SpeedType vehicle, ref RegionManager regionManager)
|
2023-04-01 14:43:05 +02:00
|
|
|
|
{
|
|
|
|
|
TagManager tags = regionManager.GetRegion(node1.coordinates)!.tagManager;
|
|
|
|
|
Tag.WayType wayType = (Tag.WayType)tags.GetTag(wayId, Tag.TagType.highway)!;
|
|
|
|
|
switch (vehicle)
|
|
|
|
|
{
|
|
|
|
|
case Tag.SpeedType.pedestrian:
|
|
|
|
|
byte speed = Tag.defaultSpeedPedestrian[wayType];
|
2023-04-06 02:23:12 +02:00
|
|
|
|
if (speed is not 0)
|
|
|
|
|
return speed;
|
|
|
|
|
return 0;
|
2023-04-01 14:43:05 +02:00
|
|
|
|
case Tag.SpeedType.car:
|
|
|
|
|
case Tag.SpeedType.road:
|
2023-04-06 02:32:04 +02:00
|
|
|
|
byte? maxSpeed = (byte?)tags.GetTag(wayId, Tag.TagType.maxspeed);
|
|
|
|
|
if (maxSpeed is not null)
|
|
|
|
|
return (double)maxSpeed;
|
|
|
|
|
maxSpeed = Tag.defaultSpeedCar[wayType];
|
|
|
|
|
if(maxSpeed is not 0)
|
|
|
|
|
return (byte)maxSpeed;
|
2023-04-06 02:23:12 +02:00
|
|
|
|
return 0;
|
2023-04-01 14:43:05 +02:00
|
|
|
|
default:
|
2023-04-06 02:23:12 +02:00
|
|
|
|
return 0;
|
2023-04-01 14:43:05 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-06 02:23:12 +02:00
|
|
|
|
|
|
|
|
|
private static double EdgeWeight(OsmNode node1, OsmNode node2, ulong wayId, Tag.SpeedType vehicle, ref RegionManager regionManager)
|
|
|
|
|
{
|
|
|
|
|
double distance = Utils.DistanceBetween(node1, node2);
|
|
|
|
|
double speed = GetSpeed(node1, wayId, vehicle, ref regionManager);
|
|
|
|
|
if (speed is not 0)
|
|
|
|
|
return distance / speed;
|
2023-04-06 02:32:04 +02:00
|
|
|
|
return double.PositiveInfinity;
|
2023-04-06 02:23:12 +02:00
|
|
|
|
}
|
2023-02-03 23:35:22 +01:00
|
|
|
|
}
|