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) =>
{
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,
useRoadsWithLessJunctionsPriority);
return result.pathResult;
return result;
}
);
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,
0);
return result.pathResult;
return result;
}
);

View File

@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using OSMDatastructure.Graph;
namespace Pathfinding;
@ -6,11 +7,41 @@ public class PathResult
{
[JsonInclude]public TimeSpan calcTime;
[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]
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)
{
this.calcTime = calcTime;
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;
//TODO check parameters for all functions and determine global fields
public class Pathfinder
public static class Pathfinder
{
public RegionManager regionManager;
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,
public static PathResult AStar(string workingDir, Coordinates startCoordinates, Coordinates goalCoordinates,
SpeedType vehicle, double heuristicRoadLevelPriority, double heuristicSameRoadPriority,
double heuristicFewJunctionsPriority)
{
DateTime startCalc = DateTime.Now;
regionManager = new RegionManager(workingDir);
RegionManager regionManager = new RegionManager(workingDir);
OsmNode? startNode = regionManager.ClosestNodeToCoordinates(startCoordinates, vehicle);
OsmNode? goalNode = regionManager.ClosestNodeToCoordinates(goalCoordinates, vehicle);
if (startNode is null || goalNode is null)
{
pathResult = new(DateTime.Now - startCalc, new List<PathNode>());
return this;
}
return new PathResult(DateTime.Now - startCalc, new List<PathNode>());
PriorityQueue<OsmNode, double> openSetfScore = new();
openSetfScore.Enqueue(startNode, 0);
Dictionary<OsmNode, OsmNode> cameFromDict = new();
gScore = new() { { startNode, 0 } };
Dictionary<OsmNode, double> gScore = new();
gScore.Add(startNode, 0);
while (openSetfScore.Count > 0)
{
@ -48,8 +32,14 @@ public class Pathfinder
if (currentNode.Equals(goalNode))
{
Console.WriteLine("Path found.");
this.pathResult = GetPath(cameFromDict, goalNode, DateTime.Now - startCalc);
return this;
PathResult path = GetPath(cameFromDict, goalNode, regionManager, DateTime.Now - startCalc);
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)
@ -58,7 +48,7 @@ public class Pathfinder
if (neighbor is not null)
{
double tentativeGScore =
gScore[currentNode] + Weight(currentNode, neighbor, edge, vehicle);
gScore[currentNode] + Weight(currentNode, neighbor, edge, vehicle, regionManager);
gScore.TryAdd(neighbor, double.MaxValue);
if (tentativeGScore < gScore[neighbor])
{
@ -70,7 +60,7 @@ public class Pathfinder
gScore[neighbor] = tentativeGScore;
else
gScore.Add(neighbor, tentativeGScore);
double h = Heuristic(currentNode, neighbor, goalNode, edge, vehicle,
double h = Heuristic(currentNode, neighbor, goalNode, edge, vehicle, regionManager,
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);
@ -79,21 +69,20 @@ public class Pathfinder
}
}
pathResult = new(DateTime.Now - startCalc, new List<PathNode>());
return this;
return new PathResult(DateTime.Now - startCalc, new List<PathNode>());
}
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);
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;
while (cameFromDict.ContainsKey(cameFromDict[currentNode]))
{
@ -111,15 +100,15 @@ public class Pathfinder
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 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;
}
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;

View File

@ -11,10 +11,6 @@ public static class Renderer
{
private const int ImageMaxSize = 20000;
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
{
@ -30,16 +26,17 @@ public static class Renderer
}
[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");
ValueTuple<Image, Bounds> areaRender = DrawArea(pathfinder.regionManager);
Console.WriteLine("Rendering gScores (Weights)");
ValueTuple<Image, Bounds> areaGScoreRender = DrawGScores(pathfinder.gScore!, areaRender.Item1, areaRender.Item2);
Console.WriteLine("Rendering path");
ValueTuple<Image, Bounds> areaGScorePathRender = DrawPath(pathfinder.pathResult!, areaGScoreRender.Item1, areaGScoreRender.Item2);
FileStream fs = new FileStream(resultPath, FileMode.Open);
PathResult graph = JsonSerializer.Deserialize<PathResult>(fs)!;
List<Coordinates> coords = new List<Coordinates>();
foreach (PathNode node in graph.pathNodes)
coords.Add(node.coordinates);
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")]
@ -66,7 +63,6 @@ public static class Renderer
Graphics g = Graphics.FromImage(ret);
g.Clear(Color.White);
//TODO Use road priority for roadcolor
Color start = Color.FromArgb(255, 25, 25, 25);
Color center = Color.FromArgb(255, 0, 0, 0);
Color end = Color.FromArgb(255, 0, 255, 0);
@ -93,54 +89,13 @@ public static class Renderer
}
[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();
foreach(PathNode node in pathResult.pathNodes)
coordinates.Add(node.coordinates);
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);
float minLat = bounds?.minLat ?? nodes.Min(node => node.coordinates.latitude);
float minLon = bounds?.minLon ?? nodes.Min(node => node.coordinates.longitude);
float maxLat = bounds?.maxLat ?? nodes.Max(node => node.coordinates.latitude);
float maxLon = bounds?.maxLon ?? nodes.Max(node => node.coordinates.longitude);
double minWeight = gScoreDict.Min(kv => kv.Value);
double maxWeight = gScoreDict.Max(kv => kv.Value);
@ -152,28 +107,55 @@ public static class Renderer
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)
Image ret;
Graphics g;
if (renderOver is null)
{
ret = new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
g = Graphics.FromImage(ret);
g.Clear(Color.White);
foreach (KeyValuePair<OsmNode, double> kv in gScoreDict)
}
else
{
ret = renderOver;
g = Graphics.FromImage(ret);
}
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);
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 y = (maxLat - kv.Key.coordinates.latitude) * scaleFactor;
float x = (node.coordinates.longitude - minLon) * scaleFactor;
float y = (maxLat - node.coordinates.latitude) * scaleFactor;
x -= (PenThickness * 1.5f) / 2;
y -= (PenThickness * 1.5f) / 2;
g.FillEllipse(b, x, y, PenThickness * 1.5f, PenThickness * 1.5f);
}
Pen p = new Pen(Color.Red, PenThickness);
return new ValueTuple<Image, Bounds>(ret, new Bounds(minLat,minLon,maxLat,maxLon));
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;
}
/*
* https://stackoverflow.com/questions/55601338/get-a-color-value-within-a-gradient-based-on-a-value
*/

View File

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