From 055a751c9ddc7be11421f2f5e6b7f3c2a94aba61 Mon Sep 17 00:00:00 2001 From: glax Date: Thu, 13 Apr 2023 19:18:25 +0200 Subject: [PATCH] Save Pathfinding result to file. Load result for render. --- API/Program.cs | 24 ++++-------------- Pathfinding/PathNode.cs | 7 ++++++ Pathfinding/PathResult.cs | 46 ++++++++++++++++++++++++++++++++++ Pathfinding/Pathfinder.cs | 43 ++++++++++++++++++------------- Pathfinding/Pathfinding.csproj | 5 +++- RenderPath/RenderPath.csproj | 2 ++ RenderPath/Renderer.cs | 36 ++++++++++++++++++-------- Server/Server.cs | 10 +++++--- 8 files changed, 123 insertions(+), 50 deletions(-) create mode 100644 Pathfinding/PathResult.cs diff --git a/API/Program.cs b/API/Program.cs index e255200..ef175b3 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -16,23 +16,20 @@ var app = builder.Build(); app.MapGet("/getRoute", (float latStart, float lonStart, float latEnd, float lonEnd, Tag.SpeedType vehicle, double stayOnSameRoadPriority, double useHigherLevelRoadsPriority, double useRoadsWithLessJunctionsPriority) => { - DateTime startCalc = DateTime.Now; - List result = Pathfinder.AStar("D:/stuttgart-regbez-latest", new Coordinates(latStart, lonStart), + + PathResult result = Pathfinder.AStar("D:/stuttgart-regbez-latest", new Coordinates(latStart, lonStart), new Coordinates(latEnd, lonEnd), vehicle, useHigherLevelRoadsPriority, stayOnSameRoadPriority, useRoadsWithLessJunctionsPriority); - PathResult pathResult = new PathResult(DateTime.Now - startCalc, result); - return pathResult; + return result; } ); app.MapGet("/getShortestRoute", (float latStart, float lonStart, float latEnd, float lonEnd) => { - DateTime startCalc = DateTime.Now; - List result = Pathfinder.AStar("D:/stuttgart-regbez-latest", new Coordinates(latStart, lonStart), + PathResult result = Pathfinder.AStar("D:/stuttgart-regbez-latest", new Coordinates(latStart, lonStart), new Coordinates(latEnd, lonEnd), Tag.SpeedType.any, 0, 0, 0); - PathResult pathResult = new PathResult(DateTime.Now - startCalc, result); - return pathResult; + return result; } ); @@ -57,14 +54,3 @@ app.MapControllers(); app.Run(); -internal class PathResult -{ - [JsonInclude]public TimeSpan calcTime; - [JsonInclude]public List pathNodes; - - public PathResult(TimeSpan calcTime, List pathNodes) - { - this.calcTime = calcTime; - this.pathNodes = pathNodes; - } -} \ No newline at end of file diff --git a/Pathfinding/PathNode.cs b/Pathfinding/PathNode.cs index 6a50d99..06ec069 100644 --- a/Pathfinding/PathNode.cs +++ b/Pathfinding/PathNode.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.Tracing; using System.Text.Json.Serialization; using OSMDatastructure; using OSMDatastructure.Graph; @@ -7,6 +8,12 @@ namespace Pathfinding; public class PathNode : OsmNode { [JsonInclude]public Dictionary tags = new(); + + [JsonConstructor] + public PathNode(ulong nodeId, Coordinates coordinates, Dictionary tags) : base(nodeId, coordinates) + { + this.tags = tags; + } public PathNode(ulong nodeId, float lat, float lon) : base(nodeId, lat, lon) { diff --git a/Pathfinding/PathResult.cs b/Pathfinding/PathResult.cs new file mode 100644 index 0000000..5a0f788 --- /dev/null +++ b/Pathfinding/PathResult.cs @@ -0,0 +1,46 @@ +using System.Text.Json.Serialization; +using OSMDatastructure.Graph; + +namespace Pathfinding; + +public class PathResult +{ + [JsonInclude]public TimeSpan calcTime; + [JsonInclude]public List pathNodes; + [JsonInclude]public Dictionary? gScore; + [JsonInclude]public HashSet? nodes; + public string? name { get; set; } + + [JsonConstructor] + public PathResult(TimeSpan calcTime, List pathNodes, Dictionary? gScore, HashSet? nodes) + { + this.calcTime = calcTime; + this.pathNodes = pathNodes; + this.gScore = gScore; + this.nodes = nodes; + } + + public PathResult(TimeSpan calcTime, List pathNodes) + { + this.calcTime = calcTime; + this.pathNodes = pathNodes; + } + + public PathResult(TimeSpan calcTime, List pathNodes, Dictionary gScore) + { + this.calcTime = calcTime; + this.pathNodes = pathNodes; + AddGScores(gScore); + } + + public void AddGScores(Dictionary gScore) + { + this.gScore = new(); + this.nodes = new(); + foreach (KeyValuePair kv in gScore) + { + this.gScore.Add(kv.Key.nodeId, kv.Value); + this.nodes.Add(kv.Key); + } + } +} \ No newline at end of file diff --git a/Pathfinding/Pathfinder.cs b/Pathfinding/Pathfinder.cs index 5a1c638..a8724e4 100644 --- a/Pathfinding/Pathfinder.cs +++ b/Pathfinding/Pathfinder.cs @@ -1,6 +1,6 @@ -using OSMDatastructure; +using System.Text.Json; +using OSMDatastructure; using OSMDatastructure.Graph; -using RenderPath; using static OSMDatastructure.Tag; using WayType = OSMDatastructure.Tag.WayType; @@ -8,16 +8,17 @@ namespace Pathfinding; public static class Pathfinder { - - public static List AStar(string workingDir, Coordinates startCoordinates, Coordinates goalCoordinates, + + public static PathResult AStar(string workingDir, Coordinates startCoordinates, Coordinates goalCoordinates, SpeedType vehicle, double heuristicRoadLevelPriority, double heuristicSameRoadPriority, double heuristicFewJunctionsPriority) { + DateTime startCalc = DateTime.Now; RegionManager regionManager = new RegionManager(workingDir); OsmNode? startNode = regionManager.ClosestNodeToCoordinates(startCoordinates, vehicle); OsmNode? goalNode = regionManager.ClosestNodeToCoordinates(goalCoordinates, vehicle); if (startNode is null || goalNode is null) - return new List(); + return new PathResult(DateTime.Now - startCalc, new List()); PriorityQueue openSetfScore = new(); openSetfScore.Enqueue(startNode, 0); @@ -31,13 +32,12 @@ public static class Pathfinder if (currentNode.Equals(goalNode)) { Console.WriteLine("Path found."); - List path = GetPath(cameFromDict, goalNode, regionManager); - Console.WriteLine("Rendering."); - List coords = new(); - foreach(PathNode pn in path) - coords.Add(pn.coordinates); - Renderer.DrawLoadedNodes(gScore, coords, workingDir); - Console.WriteLine("Done."); + PathResult path = GetPath(cameFromDict, goalNode, regionManager, DateTime.Now - startCalc); + path.AddGScores(gScore); + string fileName = $"{new DirectoryInfo(workingDir).Name}-{DateTime.Now.ToFileTime()}.result"; + string outputFilepath = Path.Join(Directory.GetParent(workingDir)!.FullName, fileName); + path.name = outputFilepath; + SaveGraph(path, outputFilepath); return path; } @@ -46,7 +46,8 @@ public static class Pathfinder OsmNode? neighbor = regionManager.GetNode(edge.neighborId, edge.neighborRegion); if (neighbor is not null) { - double tentativeGScore = gScore[currentNode] + Weight(currentNode, neighbor, edge, vehicle, regionManager); + double tentativeGScore = + gScore[currentNode] + Weight(currentNode, neighbor, edge, vehicle, regionManager); gScore.TryAdd(neighbor, double.MaxValue); if (tentativeGScore < gScore[neighbor]) { @@ -62,16 +63,24 @@ public static class Pathfinder heuristicRoadLevelPriority, heuristicFewJunctionsPriority, heuristicSameRoadPriority); //Console.WriteLine($"Queue: {openSetfScore.Count:00000} Current Distance: {Utils.DistanceBetween(currentNode, goalNode):000000.00} Visited: {cameFromDict.Count:00000} Current heuristic: {h:00000.00}"); openSetfScore.Enqueue(neighbor, tentativeGScore + h); - + } } } } - return new List(); + return new PathResult(DateTime.Now - startCalc, new List()); } - private static List GetPath(Dictionary cameFromDict, OsmNode goalNode, RegionManager regionManager) + private static void SaveGraph(PathResult pathResult, string outputFilepath) + { + FileStream fs = new FileStream(outputFilepath, FileMode.CreateNew); + JsonSerializer.Serialize(fs, pathResult, JsonSerializerOptions.Default); + fs.Dispose(); + Console.WriteLine($"Saved result to {outputFilepath}"); + } + + private static PathResult GetPath(Dictionary cameFromDict, OsmNode goalNode, RegionManager regionManager, TimeSpan calcFinished) { List path = new List(); OsmNode currentNode = goalNode; @@ -88,7 +97,7 @@ public static class Pathfinder path.Reverse(); - return path; + return new PathResult(calcFinished, path); } private static double Weight(OsmNode fromNode, OsmNode neighborNode, OsmEdge edge, SpeedType vehicle, RegionManager regionManager) diff --git a/Pathfinding/Pathfinding.csproj b/Pathfinding/Pathfinding.csproj index 6582717..a855539 100644 --- a/Pathfinding/Pathfinding.csproj +++ b/Pathfinding/Pathfinding.csproj @@ -8,7 +8,10 @@ - + + + + diff --git a/RenderPath/RenderPath.csproj b/RenderPath/RenderPath.csproj index 3aebfb5..ec3ff2a 100644 --- a/RenderPath/RenderPath.csproj +++ b/RenderPath/RenderPath.csproj @@ -8,10 +8,12 @@ + + diff --git a/RenderPath/Renderer.cs b/RenderPath/Renderer.cs index 17b269b..e2f30e5 100644 --- a/RenderPath/Renderer.cs +++ b/RenderPath/Renderer.cs @@ -1,20 +1,34 @@ using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Imaging; +using System.Text.Json; using OSMDatastructure.Graph; +using Pathfinding; namespace RenderPath; public class Renderer { - [SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")] - public static void DrawLoadedNodes(Dictionary gScoreDict, List pathCoordinates, string workingDir) + public static void DrawGraph(string resultPath) { - float minLat = gScoreDict.Min(kv => kv.Key.coordinates.latitude); - float minLon = gScoreDict.Min(kv => kv.Key.coordinates.longitude); - float maxLat = gScoreDict.Max(kv => kv.Key.coordinates.latitude); - float maxLon = gScoreDict.Max(kv => kv.Key.coordinates.longitude); + FileStream fs = new FileStream(resultPath, FileMode.Open); + PathResult graph = JsonSerializer.Deserialize(fs)!; + List coords = new List(); + foreach (PathNode node in graph.pathNodes) + coords.Add(node.coordinates); + string workingDir = new DirectoryInfo(resultPath).FullName; + + DrawLoadedNodes(graph.nodes!, graph.gScore!, coords, workingDir); + } + + [SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")] + public static void DrawLoadedNodes(HashSet nodes, Dictionary gScoreDict, List pathCoordinates, string workingDir) + { + float minLat = nodes.Min(node => node.coordinates.latitude); + float minLon = nodes.Min(node => node.coordinates.longitude); + float maxLat = nodes.Max(node => node.coordinates.latitude); + float maxLon = nodes.Max(node => node.coordinates.longitude); double minWeight = gScoreDict.Min(kv => kv.Value); double maxWeight = gScoreDict.Max(kv => kv.Value); @@ -36,12 +50,14 @@ public class Renderer Color center = Color.FromArgb(255, 255, 0); Color end = Color.FromArgb(0, 255, 0); - foreach (KeyValuePair kv in gScoreDict) + foreach (KeyValuePair kv in gScoreDict) { double percentage = (kv.Value - minWeight) / (maxWeight - minWeight); Brush b = new SolidBrush(GradientPick(percentage, start, center, end)); - float x = (kv.Key.coordinates.longitude - minLon) * scaleFactor; - float y = (maxLat - kv.Key.coordinates.latitude) * scaleFactor; + OsmNode node = nodes.First(node => node.nodeId.Equals(kv.Key)); + + float x = (node.coordinates.longitude - minLon) * scaleFactor; + float y = (maxLat - node.coordinates.latitude) * scaleFactor; g.FillEllipse(b, x, y, 2, 2); } @@ -56,7 +72,7 @@ public class Renderer g.DrawLine(p, p1, p2); } - ret.Save(Path.Join(Directory.GetParent(workingDir)!.FullName, "routing.png"), ImageFormat.Bmp); + ret.Save($"{workingDir}-routing.png", ImageFormat.Bmp); } /* diff --git a/Server/Server.cs b/Server/Server.cs index d378dde..c542bbe 100644 --- a/Server/Server.cs +++ b/Server/Server.cs @@ -1,6 +1,7 @@ using OSMDatastructure; using OSMDatastructure.Graph; using Pathfinding; +using RenderPath; namespace Server; @@ -17,10 +18,13 @@ public class Server //RegionConverter.ConvertXMLToRegions("D:/map.osm", "D:/map"); //RegionConverter.ConvertXMLToRegions("D:/germany-latest.osm", "D:/germany-latest"); + Coordinates start = new Coordinates( 48.7933798f, 9.8275859f); - Coordinates finish = new Coordinates( 48.8407632f, 10.0676979f); - List result = Pathfinder.AStar("D:/stuttgart-regbez-latest", start, - finish, Tag.SpeedType.car, 20, 2, + Coordinates finish = new Coordinates( 48.795918f, 9.021618f); + PathResult result = Pathfinder.AStar("D:/stuttgart-regbez-latest", start, + finish, Tag.SpeedType.car, 0.1, 0.2, 0); + + Renderer.DrawGraph(result.name); } } \ No newline at end of file