Compare commits

..

No commits in common. "b87d8a03002243c9d1a8e332e108d39309255be6" and "619cad61eed86e84b1cf7ca5637758e0a90289d3" have entirely different histories.

5 changed files with 125 additions and 128 deletions

View File

@ -16,19 +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) => app.MapGet("/getRoute", (float latStart, float lonStart, float latEnd, float lonEnd, Tag.SpeedType vehicle, double stayOnSameRoadPriority, double useHigherLevelRoadsPriority, double useRoadsWithLessJunctionsPriority) =>
{ {
Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest").AStar(new Coordinates(latStart, lonStart),
PathResult result = Pathfinder.AStar("D:/stuttgart-regbez-latest", new Coordinates(latStart, lonStart),
new Coordinates(latEnd, lonEnd), vehicle, useHigherLevelRoadsPriority, stayOnSameRoadPriority, new Coordinates(latEnd, lonEnd), vehicle, useHigherLevelRoadsPriority, stayOnSameRoadPriority,
useRoadsWithLessJunctionsPriority); useRoadsWithLessJunctionsPriority);
return result.pathResult; return result;
} }
); );
app.MapGet("/getShortestRoute", (float latStart, float lonStart, float latEnd, float lonEnd) => app.MapGet("/getShortestRoute", (float latStart, float lonStart, float latEnd, float lonEnd) =>
{ {
Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest").AStar(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, new Coordinates(latEnd, lonEnd), Tag.SpeedType.any, 0, 0,
0); 0);
return result.pathResult; return result;
} }
); );

View File

@ -1,4 +1,5 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using OSMDatastructure.Graph;
namespace Pathfinding; namespace Pathfinding;
@ -6,11 +7,41 @@ public class PathResult
{ {
[JsonInclude]public TimeSpan calcTime; [JsonInclude]public TimeSpan calcTime;
[JsonInclude]public List<PathNode> pathNodes; [JsonInclude]public List<PathNode> pathNodes;
[JsonInclude]public Dictionary<ulong, double>? gScore;
[JsonInclude]public HashSet<OsmNode>? gScoreNodes;
[JsonIgnore] public RegionManager? regionManager { get; set; }
public string? name { get; set; }
[JsonConstructor] [JsonConstructor]
public PathResult(TimeSpan calcTime, List<PathNode> pathNodes, Dictionary<ulong, double>? gScore, HashSet<OsmNode>? gScoreNodes)
{
this.calcTime = calcTime;
this.pathNodes = pathNodes;
this.gScore = gScore;
this.gScoreNodes = gScoreNodes;
}
public PathResult(TimeSpan calcTime, List<PathNode> pathNodes) public PathResult(TimeSpan calcTime, List<PathNode> pathNodes)
{ {
this.calcTime = calcTime; this.calcTime = calcTime;
this.pathNodes = pathNodes; this.pathNodes = pathNodes;
} }
public PathResult(TimeSpan calcTime, List<PathNode> pathNodes, Dictionary<OsmNode, double> gScore)
{
this.calcTime = calcTime;
this.pathNodes = pathNodes;
AddGScores(gScore);
}
public void AddGScores(Dictionary<OsmNode, double> gScore)
{
this.gScore = new();
this.gScoreNodes = new();
foreach (KeyValuePair<OsmNode, double> kv in gScore)
{
this.gScore.Add(kv.Key.nodeId, kv.Value);
this.gScoreNodes.Add(kv.Key);
}
}
} }

View File

@ -6,41 +6,25 @@ using WayType = OSMDatastructure.Tag.WayType;
namespace Pathfinding; namespace Pathfinding;
//TODO check parameters for all functions and determine global fields public static class Pathfinder
public class Pathfinder
{ {
public RegionManager regionManager; public static PathResult AStar(string workingDir, Coordinates startCoordinates, Coordinates goalCoordinates,
public readonly string workingDir;
public PathResult? pathResult;
public Dictionary<OsmNode, double>? gScore;
public Pathfinder(string workingDirectory)
{
if (!Path.Exists(workingDirectory))
throw new DirectoryNotFoundException(workingDirectory);
regionManager = new(workingDirectory);
workingDir = workingDirectory;
}
public Pathfinder AStar(Coordinates startCoordinates, Coordinates goalCoordinates,
SpeedType vehicle, double heuristicRoadLevelPriority, double heuristicSameRoadPriority, SpeedType vehicle, double heuristicRoadLevelPriority, double heuristicSameRoadPriority,
double heuristicFewJunctionsPriority) double heuristicFewJunctionsPriority)
{ {
DateTime startCalc = DateTime.Now; DateTime startCalc = DateTime.Now;
regionManager = new RegionManager(workingDir); RegionManager regionManager = new RegionManager(workingDir);
OsmNode? startNode = regionManager.ClosestNodeToCoordinates(startCoordinates, vehicle); OsmNode? startNode = regionManager.ClosestNodeToCoordinates(startCoordinates, vehicle);
OsmNode? goalNode = regionManager.ClosestNodeToCoordinates(goalCoordinates, vehicle); OsmNode? goalNode = regionManager.ClosestNodeToCoordinates(goalCoordinates, vehicle);
if (startNode is null || goalNode is null) if (startNode is null || goalNode is null)
{ return new PathResult(DateTime.Now - startCalc, new List<PathNode>());
pathResult = new(DateTime.Now - startCalc, new List<PathNode>());
return this;
}
PriorityQueue<OsmNode, double> openSetfScore = new(); PriorityQueue<OsmNode, double> openSetfScore = new();
openSetfScore.Enqueue(startNode, 0); openSetfScore.Enqueue(startNode, 0);
Dictionary<OsmNode, OsmNode> cameFromDict = new(); Dictionary<OsmNode, OsmNode> cameFromDict = new();
gScore = new() { { startNode, 0 } }; Dictionary<OsmNode, double> gScore = new();
gScore.Add(startNode, 0);
while (openSetfScore.Count > 0) while (openSetfScore.Count > 0)
{ {
@ -48,8 +32,14 @@ public class Pathfinder
if (currentNode.Equals(goalNode)) if (currentNode.Equals(goalNode))
{ {
Console.WriteLine("Path found."); Console.WriteLine("Path found.");
this.pathResult = GetPath(cameFromDict, goalNode, DateTime.Now - startCalc); PathResult path = GetPath(cameFromDict, goalNode, regionManager, DateTime.Now - startCalc);
return this; string fileName = $"{new DirectoryInfo(workingDir).Name}-{DateTime.Now.ToFileTime()}.result";
string outputFilepath = Path.Join(Directory.GetParent(workingDir)!.FullName, fileName);
path.name = outputFilepath;
path.AddGScores(gScore);
SaveGraph(path, outputFilepath);
path.regionManager = regionManager;
return path;
} }
foreach (OsmEdge edge in currentNode.edges) foreach (OsmEdge edge in currentNode.edges)
@ -58,7 +48,7 @@ public class Pathfinder
if (neighbor is not null) if (neighbor is not null)
{ {
double tentativeGScore = double tentativeGScore =
gScore[currentNode] + Weight(currentNode, neighbor, edge, vehicle); gScore[currentNode] + Weight(currentNode, neighbor, edge, vehicle, regionManager);
gScore.TryAdd(neighbor, double.MaxValue); gScore.TryAdd(neighbor, double.MaxValue);
if (tentativeGScore < gScore[neighbor]) if (tentativeGScore < gScore[neighbor])
{ {
@ -70,7 +60,7 @@ public class Pathfinder
gScore[neighbor] = tentativeGScore; gScore[neighbor] = tentativeGScore;
else else
gScore.Add(neighbor, tentativeGScore); gScore.Add(neighbor, tentativeGScore);
double h = Heuristic(currentNode, neighbor, goalNode, edge, vehicle, double h = Heuristic(currentNode, neighbor, goalNode, edge, vehicle, regionManager,
heuristicRoadLevelPriority, heuristicFewJunctionsPriority, heuristicSameRoadPriority); 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}"); //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); openSetfScore.Enqueue(neighbor, tentativeGScore + h);
@ -79,21 +69,20 @@ public class Pathfinder
} }
} }
pathResult = new(DateTime.Now - startCalc, new List<PathNode>()); return new PathResult(DateTime.Now - startCalc, new List<PathNode>());
return this;
} }
public void SaveResult(string path) private static void SaveGraph(PathResult pathResult, string outputFilepath)
{ {
FileStream fs = new (path, FileMode.CreateNew); FileStream fs = new FileStream(outputFilepath, FileMode.CreateNew);
JsonSerializer.Serialize(fs, pathResult, JsonSerializerOptions.Default); JsonSerializer.Serialize(fs, pathResult, JsonSerializerOptions.Default);
fs.Dispose(); fs.Dispose();
Console.WriteLine($"Saved result to {path}"); Console.WriteLine($"Saved result to {outputFilepath}");
} }
private PathResult GetPath(Dictionary<OsmNode, OsmNode> cameFromDict, OsmNode goalNode, TimeSpan calcFinished) private static PathResult GetPath(Dictionary<OsmNode, OsmNode> cameFromDict, OsmNode goalNode, RegionManager regionManager, TimeSpan calcFinished)
{ {
List<PathNode> path = new(); List<PathNode> path = new List<PathNode>();
OsmNode currentNode = goalNode; OsmNode currentNode = goalNode;
while (cameFromDict.ContainsKey(cameFromDict[currentNode])) while (cameFromDict.ContainsKey(cameFromDict[currentNode]))
{ {
@ -111,15 +100,15 @@ public class Pathfinder
return new PathResult(calcFinished, path); return new PathResult(calcFinished, path);
} }
private double Weight(OsmNode fromNode, OsmNode neighborNode, OsmEdge edge, SpeedType vehicle) private static double Weight(OsmNode fromNode, OsmNode neighborNode, OsmEdge edge, SpeedType vehicle, RegionManager regionManager)
{ {
double distance = Utils.DistanceBetween(fromNode, neighborNode); double distance = Utils.DistanceBetween(fromNode, neighborNode);
double speed = regionManager.GetSpeedForEdge(fromNode, edge.wayId, vehicle); double speed = regionManager.GetSpeedForEdge(fromNode, edge.wayId, vehicle);
//double prio = GetPriorityVehicleRoad(edge, vehicle, regionManager.GetRegion(fromNode.coordinates)!); double prio = GetPriorityVehicleRoad(edge, vehicle, regionManager.GetRegion(fromNode.coordinates)!);
return distance / speed; return distance / speed;
} }
private double Heuristic(OsmNode fromNode, OsmNode neighborNode, OsmNode goalNode, OsmEdge edge, SpeedType vehicle, double roadPriorityFactor, double junctionFactor, double sameRoadFactor) private static double Heuristic(OsmNode fromNode, OsmNode neighborNode, OsmNode goalNode, OsmEdge edge, SpeedType vehicle, RegionManager regionManager, double roadPriorityFactor, double junctionFactor, double sameRoadFactor)
{ {
double roadPriority = GetPriorityVehicleRoad(edge, vehicle, regionManager.GetRegion(fromNode.coordinates)!) * roadPriorityFactor; double roadPriority = GetPriorityVehicleRoad(edge, vehicle, regionManager.GetRegion(fromNode.coordinates)!) * roadPriorityFactor;

View File

@ -11,10 +11,6 @@ public static class Renderer
{ {
private const int ImageMaxSize = 20000; private const int ImageMaxSize = 20000;
private const float PenThickness = 4; private const float PenThickness = 4;
private static readonly Color RouteColor = Color.Red;
private static readonly Color WeightStartColor = Color.FromArgb(0, 0, 255);
private static readonly Color WeightCenterColor = Color.FromArgb(255, 255, 0);
private static readonly Color WeightEndColor = Color.FromArgb(0, 255, 0);
public class Bounds public class Bounds
{ {
@ -30,16 +26,17 @@ public static class Renderer
} }
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")] [SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
public static Image DrawPathfinder(Pathfinder pathfinder) public static void DrawGraph(string resultPath, Image? area = null, Bounds? bounds = null)
{ {
Console.WriteLine("Rendering loaded Regions"); FileStream fs = new FileStream(resultPath, FileMode.Open);
ValueTuple<Image, Bounds> areaRender = DrawArea(pathfinder.regionManager); PathResult graph = JsonSerializer.Deserialize<PathResult>(fs)!;
Console.WriteLine("Rendering gScores (Weights)"); List<Coordinates> coords = new List<Coordinates>();
ValueTuple<Image, Bounds> areaGScoreRender = DrawGScores(pathfinder.gScore!, areaRender.Item1, areaRender.Item2); foreach (PathNode node in graph.pathNodes)
Console.WriteLine("Rendering path"); coords.Add(node.coordinates);
ValueTuple<Image, Bounds> areaGScorePathRender = DrawPath(pathfinder.pathResult!, areaGScoreRender.Item1, areaGScoreRender.Item2); string workingDir = new DirectoryInfo(resultPath).FullName;
return areaGScorePathRender.Item1; Image renderedImage = DrawLoadedNodes(graph.gScoreNodes!, graph.gScore!, coords, area, bounds);
renderedImage.Save($"{workingDir}-routing.png");
} }
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")] [SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
@ -66,7 +63,6 @@ public static class Renderer
Graphics g = Graphics.FromImage(ret); Graphics g = Graphics.FromImage(ret);
g.Clear(Color.White); g.Clear(Color.White);
//TODO Use road priority for roadcolor
Color start = Color.FromArgb(255, 25, 25, 25); Color start = Color.FromArgb(255, 25, 25, 25);
Color center = Color.FromArgb(255, 0, 0, 0); Color center = Color.FromArgb(255, 0, 0, 0);
Color end = Color.FromArgb(255, 0, 255, 0); Color end = Color.FromArgb(255, 0, 255, 0);
@ -93,54 +89,13 @@ public static class Renderer
} }
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")] [SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
public static ValueTuple<Image, Bounds> DrawPath(PathResult pathResult, Image? renderOver = null, Bounds? bounds = null) public static Image DrawLoadedNodes(HashSet<OsmNode> nodes, Dictionary<ulong, double> gScoreDict,
List<Coordinates> pathCoordinates, Image? renderOver = null, Bounds? bounds = null)
{ {
List<Coordinates> coordinates = new(); float minLat = bounds?.minLat ?? nodes.Min(node => node.coordinates.latitude);
foreach(PathNode node in pathResult.pathNodes) float minLon = bounds?.minLon ?? nodes.Min(node => node.coordinates.longitude);
coordinates.Add(node.coordinates); float maxLat = bounds?.maxLat ?? nodes.Max(node => node.coordinates.latitude);
float maxLon = bounds?.maxLon ?? nodes.Max(node => node.coordinates.longitude);
float minLat = bounds?.minLat ?? coordinates.Min(coords => coords.latitude);
float minLon = bounds?.minLon ?? coordinates.Min(coords => coords.longitude);
float maxLat = bounds?.maxLat ?? coordinates.Max(coords => coords.latitude);
float maxLon = bounds?.maxLon ?? coordinates.Max(coords => coords.longitude);
float latDiff = maxLat - minLat;
float lonDiff = maxLon - minLon;
float scaleFactor = latDiff > lonDiff ? ImageMaxSize / latDiff : ImageMaxSize / lonDiff;
int pixelsX = (int)(lonDiff * scaleFactor);
int pixelsY = (int)(latDiff * scaleFactor);
Image ret = renderOver ?? new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
Graphics g = Graphics.FromImage(ret);
if(renderOver is null)
g.Clear(Color.White);
Pen p = new Pen(RouteColor, PenThickness);
for (int i = 0; i < coordinates.Count - 1; i++)
{
Coordinates c1 = coordinates[i];
Coordinates c2 = coordinates[i + 1];
Point p1 = new(Convert.ToInt32((c1.longitude - minLon) * scaleFactor),
Convert.ToInt32((maxLat - c1.latitude) * scaleFactor));
Point p2 = new(Convert.ToInt32((c2.longitude - minLon) * scaleFactor),
Convert.ToInt32((maxLat - c2.latitude) * scaleFactor));
g.DrawLine(p, p1, p2);
}
return new ValueTuple<Image, Bounds>(ret, new Bounds(minLat,minLon,maxLat,maxLon));
}
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
public static ValueTuple<Image, Bounds> DrawGScores(Dictionary<OsmNode, double> gScoreDict, Image? renderOver = null,
Bounds? bounds = null)
{
float minLat = bounds?.minLat ?? gScoreDict.Min(kv => kv.Key.coordinates.latitude);
float minLon = bounds?.minLon ?? gScoreDict.Min(kv => kv.Key.coordinates.longitude);
float maxLat = bounds?.maxLat ?? gScoreDict.Max(kv => kv.Key.coordinates.latitude);
float maxLon = bounds?.maxLon ?? gScoreDict.Max(kv => kv.Key.coordinates.longitude);
double minWeight = gScoreDict.Min(kv => kv.Value); double minWeight = gScoreDict.Min(kv => kv.Value);
double maxWeight = gScoreDict.Max(kv => kv.Value); double maxWeight = gScoreDict.Max(kv => kv.Value);
@ -153,25 +108,52 @@ public static class Renderer
int pixelsX = (int)(lonDiff * scaleFactor); int pixelsX = (int)(lonDiff * scaleFactor);
int pixelsY = (int)(latDiff * scaleFactor); int pixelsY = (int)(latDiff * scaleFactor);
Image ret = renderOver ?? new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb); Image ret;
Graphics g = Graphics.FromImage(ret); Graphics g;
if (renderOver is null) if (renderOver is null)
{
ret = new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
g = Graphics.FromImage(ret);
g.Clear(Color.White); g.Clear(Color.White);
}
else
{
ret = renderOver;
g = Graphics.FromImage(ret);
}
foreach (KeyValuePair<OsmNode, double> kv in gScoreDict) Color start = Color.FromArgb(0, 0, 255);
Color center = Color.FromArgb(255, 255, 0);
Color end = Color.FromArgb(0, 255, 0);
foreach (KeyValuePair<ulong, double> kv in gScoreDict)
{ {
double percentage = (kv.Value - minWeight) / (maxWeight - minWeight); double percentage = (kv.Value - minWeight) / (maxWeight - minWeight);
Brush b = new SolidBrush(GradientPick(percentage, WeightStartColor, WeightCenterColor, WeightEndColor)); Brush b = new SolidBrush(GradientPick(percentage, start, center, end));
OsmNode node = nodes.First(node => node.nodeId.Equals(kv.Key));
float x = (kv.Key.coordinates.longitude - minLon) * scaleFactor; float x = (node.coordinates.longitude - minLon) * scaleFactor;
float y = (maxLat - kv.Key.coordinates.latitude) * scaleFactor; float y = (maxLat - node.coordinates.latitude) * scaleFactor;
x -= (PenThickness * 1.5f) / 2; x -= (PenThickness * 1.5f) / 2;
y -= (PenThickness * 1.5f) / 2; y -= (PenThickness * 1.5f) / 2;
g.FillEllipse(b, x, y, PenThickness * 1.5f, PenThickness * 1.5f); g.FillEllipse(b, x, y, PenThickness * 1.5f, PenThickness * 1.5f);
} }
return new ValueTuple<Image, Bounds>(ret, new Bounds(minLat,minLon,maxLat,maxLon)); Pen p = new Pen(Color.Red, PenThickness);
for (int i = 0; i < pathCoordinates.Count - 1; i++)
{
Coordinates c1 = pathCoordinates[i];
Coordinates c2 = pathCoordinates[i + 1];
Point p1 = new(Convert.ToInt32((c1.longitude - minLon) * scaleFactor),
Convert.ToInt32((maxLat - c1.latitude) * scaleFactor));
Point p2 = new(Convert.ToInt32((c2.longitude - minLon) * scaleFactor),
Convert.ToInt32((maxLat - c2.latitude) * scaleFactor));
g.DrawLine(p, p1, p2);
}
return ret;
} }
/* /*

View File

@ -1,5 +1,4 @@
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging;
using OSMDatastructure; using OSMDatastructure;
using OSMDatastructure.Graph; using OSMDatastructure.Graph;
using Pathfinding; using Pathfinding;
@ -12,30 +11,25 @@ public class Server
public static void Main(string[] args) public static void Main(string[] args)
{ {
ConsoleWriter newConsole = new(); ConsoleWriter newConsole = new ConsoleWriter();
Console.SetOut(newConsole); Console.SetOut(newConsole);
Console.SetError(newConsole); Console.SetError(newConsole);
string workingDir = "D:/stuttgart-regbez-latest";
//RegionConverter.ConvertXMLToRegions("D:/stuttgart-regbez-latest.osm", "D:/stuttgart-regbez-latest"); //RegionConverter.ConvertXMLToRegions("D:/stuttgart-regbez-latest.osm", "D:/stuttgart-regbez-latest");
//RegionConverter.ConvertXMLToRegions("D:/map.osm", "D:/map"); //RegionConverter.ConvertXMLToRegions("D:/map.osm", "D:/map");
//RegionConverter.ConvertXMLToRegions("D:/germany-latest.osm", "D:/germany-latest"); //RegionConverter.ConvertXMLToRegions("D:/germany-latest.osm", "D:/germany-latest");
Coordinates start = new (48.7933798f, 9.8275859f);
Coordinates finish = new (48.795918f, 9.021618f); Coordinates start = new Coordinates(48.7933798f, 9.8275859f);
Pathfinder result = new Pathfinder(workingDir).AStar(start, Coordinates finish = new Coordinates(48.795918f, 9.021618f);
PathResult result = Pathfinder.AStar("D:/stuttgart-regbez-latest", start,
finish, Tag.SpeedType.car, 0.01, 0.0001, finish, Tag.SpeedType.car, 0.01, 0.0001,
0); 0);
string parentFolder = new DirectoryInfo(workingDir).Parent!.FullName; Console.WriteLine("Drawing area");
string resultFileName = $"{new DirectoryInfo(workingDir).Name}-{DateTime.Now.ToFileTime()}.result"; ValueTuple<Image, Renderer.Bounds> area = Renderer.DrawArea(result.regionManager);
result.SaveResult(Path.Join(parentFolder, resultFileName));
string renderFileName = $"{new DirectoryInfo(workingDir).Name}-{DateTime.Now.ToFileTime()}.render.png"; Console.WriteLine("Drawing route");
Image render = Renderer.DrawPathfinder(result); Renderer.DrawGraph(result.name, area.Item1, area.Item2);
#pragma warning disable CA1416
render.Save(Path.Join(parentFolder, renderFileName), ImageFormat.Png);
#pragma warning restore CA1416
} }
} }