Compare commits

..

7 Commits

Author SHA1 Message Date
c238a9eed3 Implemented "smart" routing through priority:
Factors:
speed,
roadtype,
junctions / waychanges.
2023-04-09 20:41:33 +02:00
932465a564 changed some speeds 2023-04-09 20:40:53 +02:00
a0d2284e45 whitespace 2023-04-09 20:40:41 +02:00
6938c86ce2 moved to correct namespacefolder 2023-04-09 20:39:59 +02:00
6e836db79b changed speeds and return type to byte 2023-04-09 19:22:34 +02:00
9448187452 Added checks if item already in queue, only update direct-distance if it not already calculated. 2023-04-09 19:22:21 +02:00
2ca4207fd7 renamed currentNode 2023-04-09 18:37:45 +02:00
9 changed files with 163 additions and 57 deletions

View File

@ -1,3 +1,4 @@
using System.Text;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using OSMDatastructure; using OSMDatastructure;
using OSMDatastructure.Graph; using OSMDatastructure.Graph;
@ -14,8 +15,6 @@ builder.Services.AddSwaggerGen();
var app = builder.Build(); var app = builder.Build();
app.MapGet("/getRouteDistance", (float latStart, float lonStart, float latEnd, float lonEnd) => app.MapGet("/getRouteDistance", (float latStart, float lonStart, float latEnd, float lonEnd) =>
{ {
DateTime startCalc = DateTime.Now; DateTime startCalc = DateTime.Now;

View File

@ -98,19 +98,20 @@ public class Tag
public static readonly Dictionary<WayType, byte> defaultSpeedCar = new() { public static readonly Dictionary<WayType, byte> defaultSpeedCar = new() {
{ WayType.NONE, 0 }, { WayType.NONE, 0 },
{ WayType.motorway, 110 }, { WayType.motorway, 100 },
{ WayType.trunk, 100 }, { WayType.motorroad, 90 },
{ WayType.primary, 80 }, { WayType.trunk, 85 },
{ WayType.secondary, 80 }, { WayType.primary, 65 },
{ WayType.tertiary, 70 }, { WayType.secondary, 60 },
{ WayType.unclassified, 20 }, { WayType.tertiary, 50 },
{ WayType.unclassified, 15 },
{ WayType.residential, 10 }, { WayType.residential, 10 },
{ WayType.motorway_link, 50 }, { WayType.motorway_link, 60 },
{ WayType.trunk_link, 50 }, { WayType.trunk_link, 50 },
{ WayType.primary_link, 30 }, { WayType.primary_link, 50 },
{ WayType.secondary_link, 25 }, { WayType.secondary_link, 50 },
{ WayType.tertiary_link, 25 }, { WayType.tertiary_link, 30 },
{ WayType.living_street, 5 }, { WayType.living_street, 10 },
{ WayType.service, 1 }, { WayType.service, 1 },
{ WayType.pedestrian, 0 }, { WayType.pedestrian, 0 },
{ WayType.track, 15 }, { WayType.track, 15 },
@ -160,7 +161,7 @@ public class Tag
{ WayType.construction, 0 } { WayType.construction, 0 }
}; };
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
public enum WayType : byte { NONE, motorway, trunk, primary, secondary, tertiary, unclassified, residential, motorway_link, trunk_link, primary_link, secondary_link, tertiary_link, living_street, service, pedestrian, track, bus_guideway, escape, raceway, road, busway, footway, bridleway, steps, corridor, path, cycleway, construction } public enum WayType : byte { NONE, motorway, motorroad, trunk, primary, secondary, tertiary, unclassified, residential, motorway_link, trunk_link, primary_link, secondary_link, tertiary_link, living_street, service, pedestrian, track, bus_guideway, escape, raceway, road, busway, footway, bridleway, steps, corridor, path, cycleway, construction }
// ReSharper restore InconsistentNaming // ReSharper restore InconsistentNaming
public enum SpeedType { pedestrian, car, any } public enum SpeedType { pedestrian, car, any }

View File

@ -1,5 +1,6 @@
using OSMDatastructure; using OSMDatastructure;
using OSMDatastructure.Graph; using OSMDatastructure.Graph;
using WayType = OSMDatastructure.Tag.WayType;
namespace Pathfinding; namespace Pathfinding;
@ -25,10 +26,112 @@ public static partial class Pathfinder
if (node2 is null) if (node2 is null)
return double.MaxValue; return double.MaxValue;
double distance = Utils.DistanceBetween(node1, node2); double distance = Utils.DistanceBetween(node1, node2);
double speed = regionManager.GetSpeedForEdge(node1, edge.wayId, vehicle); byte speed = regionManager.GetSpeedForEdge(node1, edge.wayId, vehicle);
if (speed is 0) return speed is 0 ? double.MaxValue : distance / speed;
}
private static double GetPriority(OsmNode current, OsmNode? previous, OsmEdge edge, Tag.SpeedType vehicle, RegionManager regionManager)
{
if (vehicle == Tag.SpeedType.any)
return 1;
const double roadPriorityFactor = 1;
const double junctionFactor = 2;
const double wayChangeFactor = 2;
Region r = regionManager.GetRegion(current.coordinates)!;
double roadPriority = GetPriorityVehicleRoad(edge, vehicle, r) * 0.1 * roadPriorityFactor;
double roadSpeed = regionManager.GetSpeedForEdge(current, edge.wayId, vehicle);
if(vehicle == Tag.SpeedType.pedestrian)
return (current.directDistanceToGoal / roadSpeed) * roadPriority;
ulong previousWayId = UInt64.MaxValue;
if (previous?.edges is not null)
{
foreach (OsmEdge e in previous.edges)
if (e.neighborId.Equals(current.nodeId))
{
previousWayId = e.wayId;
break;
}
}
double junctionPriority = (current.edges.Count > 2 ? 1 : 0) * junctionFactor;
double wayChange = (previousWayId != edge.wayId ? 1 : 0) * wayChangeFactor;
if(vehicle == Tag.SpeedType.car)
return (current.directDistanceToGoal / roadSpeed) * roadPriority * ((junctionPriority + wayChange) / 2);
return double.MaxValue;
}
private static double GetPriorityVehicleRoad(OsmEdge edge, Tag.SpeedType vehicle, Region region)
{
if (vehicle == Tag.SpeedType.any)
return 1;
WayType? wayType = (WayType?)region.tagManager.GetTag(edge.wayId, Tag.TagType.highway);
if(wayType is null)
return double.MaxValue; return double.MaxValue;
return distance / speed; if (vehicle == Tag.SpeedType.car)
{
switch (wayType)
{
case WayType.motorway:
case WayType.motorway_link:
case WayType.motorroad:
case WayType.trunk:
case WayType.trunk_link:
case WayType.primary:
case WayType.primary_link:
return 1;
case WayType.secondary:
case WayType.secondary_link:
return 2;
case WayType.tertiary:
case WayType.tertiary_link:
return 3;
case WayType.unclassified:
case WayType.residential:
case WayType.road:
return 4;
case WayType.living_street:
case WayType.service:
case WayType.track:
return 5;
default:
return 100;
}
}
if (vehicle == Tag.SpeedType.pedestrian)
{
switch (wayType)
{
case WayType.pedestrian:
case WayType.corridor:
case WayType.footway:
case WayType.path:
case WayType.steps:
case WayType.residential:
case WayType.living_street:
return 1;
case WayType.service:
case WayType.cycleway:
case WayType.bridleway:
case WayType.road:
case WayType.track:
case WayType.unclassified:
return 2;
case WayType.tertiary:
case WayType.tertiary_link:
case WayType.escape:
return 5;
default:
return 100;
}
}
return 100;
} }
private static List<PathNode> GetRouteFromCalc(OsmNode goalNode, RegionManager regionManager) private static List<PathNode> GetRouteFromCalc(OsmNode goalNode, RegionManager regionManager)

View File

@ -20,25 +20,25 @@ public static partial class Pathfinder
while (toVisit.Count > 0) while (toVisit.Count > 0)
{ {
OsmNode closestNodeToGoal = toVisit.Dequeue(); OsmNode currentNode = toVisit.Dequeue();
foreach (OsmEdge edge in closestNodeToGoal.edges) foreach (OsmEdge edge in currentNode.edges)
{ {
OsmNode? neighbor = regionManager.GetNode(edge.neighborId, edge.neighborRegion); OsmNode? neighbor = regionManager.GetNode(edge.neighborId, edge.neighborRegion);
if (neighbor is not null) if (neighbor is not null)
{ {
double newPotentialLength = closestNodeToGoal.currentPathLength + Utils.DistanceBetween(closestNodeToGoal, neighbor); if (Math.Abs(neighbor.directDistanceToGoal - double.MaxValue) < 1)
neighbor.directDistanceToGoal = Utils.DistanceBetween(neighbor, goalNode);
double newPotentialLength = currentNode.currentPathLength + Utils.DistanceBetween(currentNode, neighbor);
if (newPotentialLength < neighbor.currentPathLength) if (newPotentialLength < neighbor.currentPathLength)
{ {
neighbor.previousPathNode = closestNodeToGoal; neighbor.previousPathNode = currentNode;
neighbor.currentPathLength = newPotentialLength; neighbor.currentPathLength = newPotentialLength;
neighbor.directDistanceToGoal = Utils.DistanceBetween(neighbor, goalNode);
if (neighbor.Equals(goalNode)) if(neighbor.Equals(goalNode))
{ return GetRouteFromCalc(goalNode, regionManager);
stop = true; //stop = true;
} if (!toVisit.UnorderedItems.Any(item => item.Element.Equals(neighbor)) && !stop)
else if(!stop)
{ {
toVisit.Enqueue(neighbor, neighbor.directDistanceToGoal); toVisit.Enqueue(neighbor, neighbor.directDistanceToGoal);
} }

View File

@ -21,31 +21,41 @@ public static partial class Pathfinder
while (toVisit.Count > 0) while (toVisit.Count > 0)
{ {
OsmNode closestNodeToGoal = toVisit.Dequeue(); OsmNode currentNode = toVisit.Dequeue();
foreach (OsmEdge edge in closestNodeToGoal.edges.Where( foreach (OsmEdge edge in currentNode.edges.Where(
edge => regionManager.TestValidConnectionForType(closestNodeToGoal, edge, vehicle))) edge => regionManager.TestValidConnectionForType(currentNode, edge, vehicle)))
{ {
OsmNode? neighbor = regionManager.GetNode(edge.neighborId, edge.neighborRegion); OsmNode? neighbor = regionManager.GetNode(edge.neighborId, edge.neighborRegion);
if (neighbor is not null) if (neighbor is not null)
{ {
double newPotentialWeight = closestNodeToGoal.currentPathWeight + if (Math.Abs(neighbor.directDistanceToGoal - double.MaxValue) < 1)
EdgeWeight(closestNodeToGoal, edge, vehicle, regionManager); neighbor.directDistanceToGoal = Utils.DistanceBetween(neighbor, goalNode);
double newPotentialWeight = currentNode.currentPathWeight +
EdgeWeight(currentNode, edge, vehicle, regionManager);
if (newPotentialWeight < neighbor.currentPathWeight) if (newPotentialWeight < neighbor.currentPathWeight)
{ {
neighbor.previousPathNode = closestNodeToGoal; neighbor.previousPathNode = currentNode;
neighbor.currentPathLength = closestNodeToGoal.currentPathLength + Utils.DistanceBetween(closestNodeToGoal, neighbor);
neighbor.currentPathWeight = newPotentialWeight; neighbor.currentPathWeight = newPotentialWeight;
neighbor.directDistanceToGoal = Utils.DistanceBetween(neighbor, goalNode);
if (neighbor.Equals(goalNode)) if (neighbor.Equals(goalNode))
{ {
stop = true; currentNode = neighbor;
} currentNode.currentPathLength = 0;
else if(!stop) while (currentNode != startAndEndNode.Item1 && currentNode.previousPathNode is not null)
{ {
toVisit.Enqueue(neighbor, neighbor.directDistanceToGoal); currentNode.previousPathNode.currentPathLength = currentNode.currentPathLength +
Utils.DistanceBetween(currentNode, currentNode.previousPathNode);
currentNode = currentNode.previousPathNode;
}
return GetRouteFromCalc(goalNode, regionManager);
} }
//stop = true;
if (!toVisit.UnorderedItems.Any(item => item.Element.Equals(neighbor)))
toVisit.Enqueue(neighbor, GetPriority(currentNode, currentNode.previousPathNode, edge, vehicle, regionManager));
} }
} }
} }

View File

@ -76,10 +76,8 @@ namespace Pathfinding
{ {
if (type == Tag.SpeedType.any) if (type == Tag.SpeedType.any)
return true; return true;
double speed = GetSpeedForEdge(node1, edge.wayId, type); byte speed = GetSpeedForEdge(node1, edge.wayId, type);
if (speed != 0) return (speed is not 0);
return true;
return false;
} }
public OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, Tag.SpeedType vehicle) public OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, Tag.SpeedType vehicle)
@ -97,7 +95,7 @@ namespace Pathfinding
hasConnectionUsingVehicle = false; hasConnectionUsingVehicle = false;
foreach (OsmEdge edge in node.edges) foreach (OsmEdge edge in node.edges)
{ {
double speed = GetSpeedForEdge(node, edge.wayId, vehicle); byte speed = GetSpeedForEdge(node, edge.wayId, vehicle);
if (speed != 0) if (speed != 0)
hasConnectionUsingVehicle = true; hasConnectionUsingVehicle = true;
} }
@ -114,25 +112,20 @@ namespace Pathfinding
return closest; return closest;
} }
public double GetSpeedForEdge(OsmNode node1, ulong wayId, Tag.SpeedType vehicle) public byte GetSpeedForEdge(OsmNode node1, ulong wayId, Tag.SpeedType vehicle)
{ {
TagManager tags = GetRegion(node1.coordinates)!.tagManager; TagManager tags = GetRegion(node1.coordinates)!.tagManager;
Tag.WayType wayType = (Tag.WayType)tags.GetTag(wayId, Tag.TagType.highway)!; Tag.WayType wayType = (Tag.WayType)tags.GetTag(wayId, Tag.TagType.highway)!;
byte speed = 0;
switch (vehicle) switch (vehicle)
{ {
case Tag.SpeedType.pedestrian: case Tag.SpeedType.pedestrian:
byte speed = Tag.defaultSpeedPedestrian[wayType]; speed = Tag.defaultSpeedPedestrian[wayType];
if (speed is not 0) return speed is not 0 ? speed : (byte)0;
return speed;
return 0;
case Tag.SpeedType.car: case Tag.SpeedType.car:
byte? maxSpeed = (byte?)tags.GetTag(wayId, Tag.TagType.maxspeed); byte? maxSpeed = (byte?)tags.GetTag(wayId, Tag.TagType.maxspeed);
if (maxSpeed is not null) speed = Tag.defaultSpeedCar[wayType];
return (double)maxSpeed; return maxSpeed < speed ? (byte)maxSpeed : speed;
maxSpeed = Tag.defaultSpeedCar[wayType];
if(maxSpeed is not 0)
return (byte)maxSpeed;
return 0;
case Tag.SpeedType.any: case Tag.SpeedType.any:
return 1; return 1;
default: default: