Compare commits

..

No commits in common. "ec6725a5c513a9b52c881c8077a1a07a59a7eb3a" and "95c0088b73c306af36808bb4f802823af527ec9d" have entirely different histories.

7 changed files with 162 additions and 264 deletions

View File

@ -13,18 +13,20 @@ builder.Services.AddSwaggerGen();
var app = builder.Build(); var app = builder.Build();
app.MapGet("/getRoute", (float latStart, float lonStart, float latEnd, float lonEnd, Tag.SpeedType vehicle, double useHigherLevelRoadsPriority, double maxTurnAngle) => app.MapGet("/getRoute", (float latStart, float lonStart, float latEnd, float lonEnd, Tag.SpeedType vehicle, double stayOnSameRoadPriority, double useHigherLevelRoadsPriority, double useRoadsWithLessJunctionsPriority, double angleFactor) =>
{ {
Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest", useHigherLevelRoadsPriority, maxTurnAngle).AStar(new Coordinates(latStart, lonStart), Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest").AStar(new Coordinates(latStart, lonStart),
new Coordinates(latEnd, lonEnd), vehicle); new Coordinates(latEnd, lonEnd), vehicle, useHigherLevelRoadsPriority, stayOnSameRoadPriority,
useRoadsWithLessJunctionsPriority, angleFactor);
return result.pathResult; return result.pathResult;
} }
); );
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", 0, 30).AStar(new Coordinates(latStart, lonStart), Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest").AStar(new Coordinates(latStart, lonStart),
new Coordinates(latEnd, lonEnd), Tag.SpeedType.any); new Coordinates(latEnd, lonEnd), Tag.SpeedType.any, 0, 0,
0, 0);
return result.pathResult; return result.pathResult;
} }
); );

View File

@ -1,14 +1,13 @@
using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Pathfinding; namespace Pathfinding;
public class PathResult public class PathResult
{ {
[JsonInclude]public double distance;
[JsonInclude]public double weight;
[JsonInclude]public TimeSpan calcTime; [JsonInclude]public TimeSpan calcTime;
[JsonInclude]public List<PathNode> pathNodes; [JsonInclude]public List<PathNode> pathNodes;
[JsonInclude]public double distance;
[JsonInclude]public double weight;
[JsonConstructor] [JsonConstructor]
public PathResult(TimeSpan calcTime, List<PathNode> pathNodes, double distance, double weight) public PathResult(TimeSpan calcTime, List<PathNode> pathNodes, double distance, double weight)
@ -18,10 +17,4 @@ public class PathResult
this.distance = distance; this.distance = distance;
this.weight = weight; this.weight = weight;
} }
public static PathResult PathresultFromFile(string filePath)
{
return JsonSerializer.Deserialize<PathResult>(new FileStream(filePath, FileMode.Open, FileAccess.Read,
FileShare.Read))!;
}
} }

View File

@ -9,31 +9,26 @@ public class Pathfinder
{ {
public RegionManager regionManager; public RegionManager regionManager;
public readonly string workingDir;
public PathResult? pathResult; public PathResult? pathResult;
public Dictionary<OsmNode, double>? gScore; public Dictionary<OsmNode, double>? gScore;
private Dictionary<OsmNode, OsmNode>? _cameFromDict; private Dictionary<OsmNode, OsmNode>? _cameFromDict;
private SpeedType _speedType; private SpeedType _speedType;
private double roadPriorityFactor, turnAngle;
public Pathfinder(string workingDirectory, double roadPriorityFactor, double turnAngle) public Pathfinder(string workingDirectory)
{ {
if (!Path.Exists(workingDirectory)) if (!Path.Exists(workingDirectory))
throw new DirectoryNotFoundException(workingDirectory); throw new DirectoryNotFoundException(workingDirectory);
regionManager = new(workingDirectory); regionManager = new(workingDirectory);
this.roadPriorityFactor = roadPriorityFactor; workingDir = workingDirectory;
this.turnAngle = turnAngle;
} }
public Pathfinder(RegionManager regionManager, double roadPriorityFactor, double turnAngle) public Pathfinder AStar(Coordinates startCoordinates, Coordinates goalCoordinates,
{ SpeedType vehicle, double heuristicRoadLevelPriority, double heuristicSameRoadPriority,
this.regionManager = regionManager; double heuristicFewJunctionsPriority, double angleWeightFactor)
this.roadPriorityFactor = roadPriorityFactor;
this.turnAngle = turnAngle;
}
public Pathfinder AStar(Coordinates startCoordinates, Coordinates goalCoordinates, SpeedType vehicle)
{ {
DateTime startCalc = DateTime.Now; DateTime startCalc = DateTime.Now;
regionManager = new RegionManager(workingDir);
_speedType = vehicle; _speedType = vehicle;
OsmNode? startNode = regionManager.ClosestNodeToCoordinates(startCoordinates, _speedType); OsmNode? startNode = regionManager.ClosestNodeToCoordinates(startCoordinates, _speedType);
OsmNode? goalNode = regionManager.ClosestNodeToCoordinates(goalCoordinates, _speedType); OsmNode? goalNode = regionManager.ClosestNodeToCoordinates(goalCoordinates, _speedType);
@ -43,71 +38,49 @@ public class Pathfinder
return this; return this;
} }
RPriorityQueue<OsmNode, double> openSetfScore = new(); PriorityQueue<OsmNode, double> openSetfScore = new();
openSetfScore.Enqueue(startNode, 0); openSetfScore.Enqueue(startNode, 0);
gScore = new() { { startNode, 0 } }; gScore = new() { { startNode, 0 } };
_cameFromDict = new(); _cameFromDict = new();
bool found = false; while (openSetfScore.Count > 0)
bool stop = false;
TimeSpan firstFound = TimeSpan.MaxValue;
double maxGscore = double.MaxValue;
while (openSetfScore.Count > 0 && !stop)
{ {
OsmNode currentNode = openSetfScore.Dequeue()!; OsmNode currentNode = openSetfScore.Dequeue();
if (currentNode.Equals(goalNode)) if (currentNode.Equals(goalNode))
{ {
if (!found) TimeSpan calcTime = DateTime.Now - startCalc;
{ pathResult = GetPath(goalNode, calcTime);
firstFound = DateTime.Now - startCalc; Console.WriteLine($"Path found. {calcTime} PathLength {pathResult.pathNodes.Count} VisitedNodes {gScore.Count} Distance {pathResult.distance} Duration {pathResult.weight}");
found = true; return this;
}
maxGscore = gScore[goalNode];
/*
int removed = openSetfScore.Remove(gScore.Where(item => item.Value > maxGscore)
.ToDictionary(item => item.Key, item => item.Value).Keys);
Console.WriteLine($"Removed {removed}");*/
} }
if (found && DateTime.Now - startCalc > firstFound.Multiply(2))
stop = true;
foreach (OsmEdge edge in currentNode.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 tentativeGScore = gScore[currentNode] + Weight(currentNode, neighbor, edge); double tentativeGScore =
gScore[currentNode] + Weight(currentNode, neighbor, edge, angleWeightFactor);
gScore.TryAdd(neighbor, double.MaxValue); gScore.TryAdd(neighbor, double.MaxValue);
if ((!found || (found && tentativeGScore < maxGscore)) && tentativeGScore < gScore[neighbor]) if (tentativeGScore < gScore[neighbor])
{ {
if(!_cameFromDict.TryAdd(neighbor, currentNode)) if(!_cameFromDict.TryAdd(neighbor, currentNode))
_cameFromDict[neighbor] = currentNode; _cameFromDict[neighbor] = currentNode;
gScore[neighbor] = tentativeGScore; gScore[neighbor] = tentativeGScore;
double h = Heuristic(currentNode, neighbor, goalNode, edge); double h = Heuristic(currentNode, neighbor, goalNode, edge,
heuristicRoadLevelPriority, heuristicFewJunctionsPriority, heuristicSameRoadPriority, angleWeightFactor);
//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);
} }
} }
} }
} }
TimeSpan calcTime = DateTime.Now - startCalc; pathResult = new(DateTime.Now - startCalc, new List<PathNode>(),0 ,0);
if (!found)
{
pathResult = new(DateTime.Now - startCalc, new List<PathNode>(),0 ,0);
Console.Write("No path found.");
return this;
}
else
{
pathResult = GetPath(goalNode, calcTime);
}
Console.WriteLine($"Path found. {calcTime} PathLength {pathResult.pathNodes.Count} VisitedNodes {gScore.Count} Distance {pathResult.distance} Duration {pathResult.weight}");
return this; return this;
} }
private double Weight(OsmNode currentNode, OsmNode neighborNode, OsmEdge edge) private double Weight(OsmNode currentNode, OsmNode neighborNode, OsmEdge edge, double angleWeightFactor)
{ {
double distance = Utils.DistanceBetween(currentNode, neighborNode); double distance = Utils.DistanceBetween(currentNode, neighborNode);
double speed = regionManager.GetSpeedForEdge(currentNode, edge.wayId, _speedType); double speed = regionManager.GetSpeedForEdge(currentNode, edge.wayId, _speedType);
@ -116,43 +89,52 @@ public class Pathfinder
if (_cameFromDict!.ContainsKey(currentNode)) if (_cameFromDict!.ContainsKey(currentNode))
{ {
OsmNode previousNode = _cameFromDict[currentNode]; OsmNode previousNode = _cameFromDict[currentNode];
Vector v1 = new(currentNode, previousNode); Vector v1 = new(previousNode, currentNode);
Vector v2 = new(currentNode, neighborNode); Vector v2 = new(currentNode, neighborNode);
double nodeAngle = v1.Angle(v2); double nodeAngle = v1.Angle(v2);
if (nodeAngle < turnAngle) angle = ((180 - nodeAngle) / 180) * angleWeightFactor;
angle = 0;
else
angle = nodeAngle / 180;
} }
double prio = regionManager.GetPriorityForVehicle(_speedType,edge, currentNode) * roadPriorityFactor; double prio = regionManager.GetPriorityForVehicle(_speedType,edge, currentNode);
return distance / (1 + speed + angle + prio);
return distance / (speed * angle + prio + 1);
} }
private double Heuristic(OsmNode currentNode, OsmNode neighborNode, OsmNode goalNode, OsmEdge edge) private double Heuristic(OsmNode currentNode, OsmNode neighborNode, OsmNode goalNode, OsmEdge edge, double roadPriorityFactor, double junctionFactor, double sameRoadFactor, double nodeAngleFactor)
{ {
if (neighborNode.Equals(goalNode)) return 0; double roadPriority = regionManager.GetPriorityForVehicle(_speedType, edge, currentNode) * roadPriorityFactor;
double priority = regionManager.GetPriorityForVehicle(_speedType, edge, currentNode); if (roadPriority == 0)
if (priority == 0)
return double.MaxValue; return double.MaxValue;
double speed = regionManager.GetSpeedForEdge(currentNode, edge.wayId, _speedType); double speed = regionManager.GetSpeedForEdge(currentNode, edge.wayId, _speedType) * 0.0003;
TagManager curTags = regionManager.GetRegion(currentNode.coordinates)!.tagManager;
TagManager nextTags = regionManager.GetRegion(neighborNode.coordinates)!.tagManager;
bool sameName = false;
string? curName = (string?)curTags.GetTag(edge.wayId, TagType.name);
bool sameRef = false;
string? curRef = (string?)curTags.GetTag(edge.wayId, TagType.tagref);
if(curName is not null)
foreach (OsmEdge pEdge in neighborNode.edges)
{
if ((string?)nextTags.GetTag(pEdge.wayId, TagType.name) == curName)
sameName = true;
if ((string?)nextTags.GetTag(pEdge.wayId, TagType.tagref) == curRef)
sameRef = true;
}
double sameRoadName = (sameRef || sameName ? 1 : 0) * sameRoadFactor;
double junctionCount = (neighborNode.edges.Count > 2 ? 0 : 1) * junctionFactor;
double angle = 0; double angle = 0;
if (_cameFromDict!.ContainsKey(currentNode)) if (_cameFromDict!.ContainsKey(currentNode))
{ {
OsmNode previousNode = _cameFromDict[currentNode]; OsmNode previousNode = _cameFromDict[currentNode];
Vector v1 = new(currentNode, previousNode); Vector v1 = new(previousNode, currentNode);
Vector v2 = new(currentNode, neighborNode); Vector v2 = new(currentNode, neighborNode);
double nodeAngle = v1.Angle(v2); double nodeAngle = v1.Angle(v2);
if (nodeAngle < turnAngle) angle = ((180 - nodeAngle) / 180) * nodeAngleFactor;
angle = 0;
else
angle = nodeAngle / 180;
} }
return Utils.DistanceBetween(neighborNode, goalNode) / (roadPriority + sameRoadName + junctionCount + angle);
double roadPriority = priority * roadPriorityFactor;
return Utils.DistanceBetween(neighborNode, goalNode) / (speed * angle + roadPriority + 1);
} }
public void SaveResult(string path) public void SaveResult(string path)
@ -217,8 +199,6 @@ public class Pathfinder
double v1L = Math.Sqrt(v1.x * v1.x + v1.y * v1.y); double v1L = Math.Sqrt(v1.x * v1.x + v1.y * v1.y);
double v2L = Math.Sqrt(v2.x * v2.x + v2.y * v2.y); double v2L = Math.Sqrt(v2.x * v2.x + v2.y * v2.y);
double ang = Math.Acos(dotProd / (v1L * v2L)); double ang = Math.Acos(dotProd / (v1L * v2L));
if (ang.Equals(double.NaN))
return 0;
double angle = Utils.RadiansToDegrees(ang); double angle = Utils.RadiansToDegrees(ang);
return angle; return angle;
} }

View File

@ -1,36 +0,0 @@
namespace Pathfinding;
public class RPriorityQueue<TKey, TPriority> where TKey : notnull
{
public Dictionary<TKey, TPriority> queue;
public int Count { get; private set; }
public RPriorityQueue()
{
queue = new();
}
public void Enqueue(TKey key, TPriority priority)
{
if (!queue.TryAdd(key, priority))
queue[key] = priority;
Count = queue.Count;
}
public TKey? Dequeue()
{
TKey? retKey = queue.MinBy(item => item.Value).Key;
queue.Remove(retKey);
Count = queue.Count;
return retKey;
}
public int Remove(IEnumerable<TKey> elements)
{
int before = Count;
queue = queue.Where(queueitem => !elements.Contains(queueitem.Key))
.ToDictionary(item => item.Key, item => item.Value);
Count = queue.Count;
return before - Count;
}
}

View File

@ -23,18 +23,13 @@ namespace Pathfinding
public Region? GetRegion(ulong id) public Region? GetRegion(ulong id)
{ {
if(_regions.TryGetValue(id, out Region? value))
if(_regions.TryGetValue(id, out Region? retRegion)) return value;
return retRegion;
Region? loadedRegion = RegionFromId(id); Region? loadedRegion = RegionFromId(id);
if(_regions.TryGetValue(id, out Region? retRegion2)) //Prevent other thread from changing collection
return retRegion2;
if(loadedRegion is not null) if(loadedRegion is not null)
_regions.TryAdd(loadedRegion.regionHash, loadedRegion); _regions.Add(loadedRegion!.regionHash, value: loadedRegion);
return loadedRegion;
return _regions[id];
} }
public Region[] GetAllRegions() public Region[] GetAllRegions()
@ -42,12 +37,12 @@ namespace Pathfinding
return _regions.Values.ToArray(); return _regions.Values.ToArray();
} }
private static Region? RegionFromFile(string filePath) private Region? RegionFromFile(string filePath)
{ {
if (!File.Exists(filePath)) if (!File.Exists(filePath))
return null; return null;
FileStream regionFile = new (filePath, FileMode.Open, FileAccess.Read, FileShare.Read, (int)new FileInfo(filePath).Length, FileOptions.SequentialScan); FileStream regionFile = new (filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
Region retRegion = JsonSerializer.Deserialize<Region>(regionFile, Region.serializerOptions)!; Region retRegion = JsonSerializer.Deserialize<Region>(regionFile, Region.serializerOptions)!;
regionFile.Dispose(); regionFile.Dispose();
return retRegion; return retRegion;
@ -65,7 +60,7 @@ namespace Pathfinding
return r?.GetNode(nodeId); return r?.GetNode(nodeId);
} }
public bool TestValidConnectionForType(OsmNode node1, OsmNode node2, SpeedType type) public bool TestValidConnectionForType(OsmNode node1, OsmNode node2, Tag.SpeedType type)
{ {
foreach (OsmEdge edge in node1.edges) foreach (OsmEdge edge in node1.edges)
{ {
@ -76,15 +71,15 @@ namespace Pathfinding
return false; return false;
} }
public bool TestValidConnectionForType(OsmNode node1, OsmEdge edge, SpeedType type) public bool TestValidConnectionForType(OsmNode node1, OsmEdge edge, Tag.SpeedType type)
{ {
if (type == SpeedType.any) if (type == Tag.SpeedType.any)
return true; return true;
byte speed = GetSpeedForEdge(node1, edge.wayId, type); byte speed = GetSpeedForEdge(node1, edge.wayId, type);
return (speed is not 0); return (speed is not 0);
} }
public OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, SpeedType vehicle) public OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, Tag.SpeedType vehicle)
{ {
OsmNode? closest = null; OsmNode? closest = null;
double distance = double.MaxValue; double distance = double.MaxValue;
@ -94,7 +89,7 @@ namespace Pathfinding
foreach (OsmNode node in region.nodes) foreach (OsmNode node in region.nodes)
{ {
bool hasConnectionUsingVehicle = true; bool hasConnectionUsingVehicle = true;
if (vehicle is not SpeedType.any) if (vehicle is not Tag.SpeedType.any)
{ {
hasConnectionUsingVehicle = false; hasConnectionUsingVehicle = false;
foreach (OsmEdge edge in node.edges) foreach (OsmEdge edge in node.edges)
@ -115,21 +110,21 @@ namespace Pathfinding
return closest; return closest;
} }
public byte GetSpeedForEdge(OsmNode node1, ulong wayId, SpeedType vehicle) public byte GetSpeedForEdge(OsmNode node1, ulong wayId, Tag.SpeedType vehicle)
{ {
TagManager tags = GetRegion(node1.coordinates)!.tagManager; TagManager tags = GetRegion(node1.coordinates)!.tagManager;
WayType wayType = (WayType)tags.GetTag(wayId, Tag.TagType.highway)!; Tag.WayType wayType = (Tag.WayType)tags.GetTag(wayId, Tag.TagType.highway)!;
byte speed = 0; byte speed = 0;
switch (vehicle) switch (vehicle)
{ {
case SpeedType.pedestrian: case Tag.SpeedType.pedestrian:
speed = Tag.defaultSpeedPedestrian[wayType]; speed = Tag.defaultSpeedPedestrian[wayType];
return speed; return speed;
case SpeedType.car: case Tag.SpeedType.car:
byte? maxSpeed = (byte?)tags.GetTag(wayId, Tag.TagType.maxspeed); byte? maxSpeed = (byte?)tags.GetTag(wayId, Tag.TagType.maxspeed);
speed = Tag.defaultSpeedCar[wayType]; speed = Tag.defaultSpeedCar[wayType];
return maxSpeed < speed ? (byte)maxSpeed : speed; return maxSpeed < speed ? (byte)maxSpeed : speed;
case SpeedType.any: case Tag.SpeedType.any:
return 1; return 1;
default: default:
return 0; return 0;

View File

@ -1,7 +1,6 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using OSMDatastructure;
using OSMDatastructure.Graph; using OSMDatastructure.Graph;
using Pathfinding; using Pathfinding;
@ -10,12 +9,11 @@ namespace RenderPath;
public static class Renderer public static class Renderer
{ {
private const int ImageMaxSize = 20000; private const int ImageMaxSize = 20000;
private const float PenThickness = 2; private const float PenThickness = 4;
private static readonly Color RouteColor = Color.Red; private static readonly Color RouteColor = Color.Red;
private static readonly Color WeightStartColor = Color.FromArgb(127, 0, 100, 255); private static readonly Color WeightStartColor = Color.FromArgb(0, 0, 255);
private static readonly Color WeightEndColor = Color.FromArgb(255,0, 255, 0); private static readonly Color WeightCenterColor = Color.FromArgb(255, 255, 0);
private static readonly Color RoadPrioStart = Color.FromArgb(200, 100, 100, 100); private static readonly Color WeightEndColor = Color.FromArgb(0, 255, 0);
private static readonly Color RoadPrioEnd = Color.FromArgb(255, 255, 180, 0);
public class Bounds public class Bounds
{ {
@ -63,20 +61,24 @@ 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 = new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppPArgb); Image ret = new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
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 center = Color.FromArgb(255, 0, 0, 0);
Color end = Color.FromArgb(255, 0, 255, 0);
foreach (OsmNode node in nodes) foreach (OsmNode node in nodes)
{ {
foreach (OsmEdge edge in node.edges) foreach (OsmEdge edge in node.edges)
{ {
double priority = rm.GetPriorityForVehicle(Tag.SpeedType.car, edge, node) / 17;
Coordinates c1 = node.coordinates; Coordinates c1 = node.coordinates;
OsmNode nNode = rm.GetNode(edge.neighborId, edge.neighborRegion)!; OsmNode nNode = rm.GetNode(edge.neighborId, edge.neighborRegion)!;
Coordinates c2 = nNode.coordinates; Coordinates c2 = nNode.coordinates;
Pen p = new(ColorInterp(RoadPrioStart, RoadPrioEnd, priority), PenThickness); Pen p = new(GradientPick(0, start, center, end), PenThickness);
float x1 = (c1.longitude - minLon) * scaleFactor; float x1 = (c1.longitude - minLon) * scaleFactor;
float y1 = (maxLat - c1.latitude) * scaleFactor; float y1 = (maxLat - c1.latitude) * scaleFactor;
float x2 = (c2.longitude - minLon) * scaleFactor; float x2 = (c2.longitude - minLon) * scaleFactor;
@ -109,7 +111,7 @@ 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.Format32bppPArgb); Image ret = renderOver ?? new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
Graphics g = Graphics.FromImage(ret); Graphics g = Graphics.FromImage(ret);
if(renderOver is null) if(renderOver is null)
g.Clear(Color.White); g.Clear(Color.White);
@ -131,17 +133,16 @@ public static class Renderer
} }
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")] [SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
public static ValueTuple<Image, Bounds> DrawGScores(Dictionary<OsmNode, double> gScore, Image? renderOver = null, public static ValueTuple<Image, Bounds> DrawGScores(Dictionary<OsmNode, double> gScoreDict, Image? renderOver = null,
Bounds? bounds = 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 ?? gScore.Min(kv => kv.Key.coordinates.latitude); double minWeight = gScoreDict.Min(kv => kv.Value);
float minLon = bounds?.minLon ?? gScore.Min(kv => kv.Key.coordinates.longitude); double maxWeight = gScoreDict.Max(kv => kv.Value);
float maxLat = bounds?.maxLat ?? gScore.Max(kv => kv.Key.coordinates.latitude);
float maxLon = bounds?.maxLon ?? gScore.Max(kv => kv.Key.coordinates.longitude);
double minWeight = gScore.Min(kv => kv.Value);
double maxWeight = gScore.Max(kv => kv.Value);
float latDiff = maxLat - minLat; float latDiff = maxLat - minLat;
float lonDiff = maxLon - minLon; float lonDiff = maxLon - minLon;
@ -151,24 +152,22 @@ 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.Format32bppPArgb); Image ret = renderOver ?? new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
Graphics g = Graphics.FromImage(ret); Graphics g = Graphics.FromImage(ret);
if(renderOver is null) if(renderOver is null)
g.Clear(Color.White); g.Clear(Color.White);
float pointSize = PenThickness * 1.5f; foreach (KeyValuePair<OsmNode, double> kv in gScoreDict)
foreach (KeyValuePair<OsmNode, double> kv in gScore)
{ {
double percentage = (kv.Value - minWeight) / (maxWeight - minWeight); double percentage = (kv.Value - minWeight) / (maxWeight - minWeight);
Brush b = new SolidBrush(ColorInterp(WeightStartColor, WeightEndColor, percentage)); Brush b = new SolidBrush(GradientPick(percentage, WeightStartColor, WeightCenterColor, WeightEndColor));
float x = (kv.Key.coordinates.longitude - minLon) * scaleFactor; float x = (kv.Key.coordinates.longitude - minLon) * scaleFactor;
float y = (maxLat - kv.Key.coordinates.latitude) * scaleFactor; float y = (maxLat - kv.Key.coordinates.latitude) * scaleFactor;
x -= pointSize / 2; x -= (PenThickness * 1.5f) / 2;
y -= pointSize / 2; y -= (PenThickness * 1.5f) / 2;
g.FillEllipse(b, x, y, pointSize, pointSize); g.FillEllipse(b, x, y, PenThickness * 1.5f, PenThickness * 1.5f);
} }
return new ValueTuple<Image, Bounds>(ret, new Bounds(minLat,minLon,maxLat,maxLon)); return new ValueTuple<Image, Bounds>(ret, new Bounds(minLat,minLon,maxLat,maxLon));
@ -183,4 +182,12 @@ public static class Renderer
LinearInterp(start.R, end.R, percentage), LinearInterp(start.R, end.R, percentage),
LinearInterp(start.G, end.G, percentage), LinearInterp(start.G, end.G, percentage),
LinearInterp(start.B, end.B, percentage)); LinearInterp(start.B, end.B, percentage));
private static Color GradientPick(double percentage, Color Start, Color Center, Color End) {
if (percentage < 0.5)
return ColorInterp(Start, Center, percentage / 0.5);
else if (percentage == 0.5)
return Center;
else
return ColorInterp(Center, End, (percentage - 0.5)/0.5);
}
} }

View File

@ -26,27 +26,31 @@ public class Server
Coordinates start = new (48.7933798f, 9.8275859f); Coordinates start = new (48.7933798f, 9.8275859f);
Coordinates finish = new (48.795918f, 9.021618f); Coordinates finish = new (48.795918f, 9.021618f);
TestVariables(workingDir, start, finish, 16); TestVariables(workingDir, start, finish);
GetShortestRoute("D:");
/*
ValueTuple<Image, Renderer.Bounds> area = RenderAreaBaseImage(workingDir, start, finish);
area.Item1.Save(@"D:\Base.png", ImageFormat.Png);
ValueTuple<Image, Renderer.Bounds> areaDistance = Renderer.DrawPath(
PathResult.PathresultFromFile(@"D:\angle0,140_level0,020_same0,160.result"), Image.FromFile(@"D:\Base.png"), area.Item2);
areaDistance.Item1.Save(@"D:\Distance.png", ImageFormat.Png);
ValueTuple<Image, Renderer.Bounds> areaWeight = Renderer.DrawPath(
PathResult.PathresultFromFile(@"D:\angle0,160_level0,020_same0,020.result"), Image.FromFile(@"D:\Base.png"), area.Item2);
areaWeight.Item1.Save(@"D:\Weight.png", ImageFormat.Png);
*/
/*
string parentFolder = new DirectoryInfo(workingDir).Parent!.FullName; string parentFolder = new DirectoryInfo(workingDir).Parent!.FullName;
Pathfinder result = new Pathfinder(workingDir, 0.002, 0, /*
0, 1, 30).AStar(start, Console.WriteLine("Preparing BaseRender");
finish, Tag.SpeedType.car); RegionManager allRegions = new(workingDir);
for (float lat = 48.78f - Region.RegionSize / 2; lat < 48.87f + Region.RegionSize / 2; lat += Region.RegionSize / 2)
{
for (float lon = 9f - Region.RegionSize / 2; lon < 9.9f + Region.RegionSize / 2; lon += Region.RegionSize / 2)
{
allRegions.GetRegion(new Coordinates(lat, lon));
}
}
Console.WriteLine("Regions Loaded. Rendering.");
ValueTuple<Image, Renderer.Bounds> baseRender = Renderer.DrawArea(allRegions);
*/
/*
Pathfinder result = new Pathfinder(workingDir).AStar(start,
finish, Tag.SpeedType.car, 0.034, 0.012,
0, 0.18);
Console.WriteLine($"Calc-time {result.pathResult!.calcTime} Path-length: {result.pathResult.pathNodes.Count} Visited-nodes: {result.gScore!.Count}"); Console.WriteLine($"Calc-time {result.pathResult!.calcTime} Path-length: {result.pathResult.pathNodes.Count} Visited-nodes: {result.gScore!.Count}");
@ -60,101 +64,54 @@ public class Server
#pragma warning disable CA1416 #pragma warning disable CA1416
render.Save(Path.Join(parentFolder, renderFileName), ImageFormat.Png); render.Save(Path.Join(parentFolder, renderFileName), ImageFormat.Png);
#pragma warning restore CA1416*/ #pragma warning restore CA1416*/
Console.Beep(400, 100);
} }
private static void GetShortestRoute(string directory) private static void TestVariables(string workingDir, Coordinates start, Coordinates finish)
{
DateTime start = DateTime.Now;
HashSet<string> allFiles = Directory.GetFiles(directory).Where(file => file.EndsWith(".result")).ToHashSet();
PathResult first = PathResult.PathresultFromFile(allFiles.First());
KeyValuePair<PathResult, string> shortest = new(first, allFiles.First());
KeyValuePair<PathResult, string> fastest = new(first, allFiles.First());
KeyValuePair<PathResult, string> calcTime = new(first, allFiles.First());
int loaded = 0;
foreach (string filePath in allFiles)
{
PathResult result = PathResult.PathresultFromFile(filePath);
Console.WriteLine($"{loaded++}/{allFiles.Count()} {filePath} " +
$"Time elapsed: {DateTime.Now - start} " +
$"Remaining {((DateTime.Now - start)/loaded)*(allFiles.Count-loaded)}");
if (shortest.Key.distance > result.distance)
shortest = new KeyValuePair<PathResult, string>(result, filePath);
if (fastest.Key.weight > result.weight)
fastest = new KeyValuePair<PathResult, string>(result, filePath);
if (calcTime.Key.calcTime > result.calcTime)
calcTime = new KeyValuePair<PathResult, string>(result, filePath);
}
Console.WriteLine($"Shortest: {shortest.Key.distance} {shortest.Value}\nFastest: {shortest.Key.weight} {fastest.Value}\nCalcTime: {calcTime.Key.calcTime} {calcTime.Value}");
}
private static ValueTuple<Image, Renderer.Bounds> RenderAreaBaseImage(string workingDir, Coordinates c1, Coordinates c2)
{
float minLat = c1.latitude < c2.latitude ? c1.latitude : c2.latitude;
float minLon = c1.longitude < c2.longitude ? c1.longitude : c2.longitude;
float maxLat = c1.latitude > c2.latitude ? c1.latitude : c2.latitude;
float maxLon = c1.longitude > c2.longitude ? c1.longitude : c2.longitude;
RegionManager allRegions = new(workingDir);
for (float lat = minLat - Region.RegionSize; lat < maxLat + Region.RegionSize; lat += Region.RegionSize / 2)
{
for (float lon = minLon - Region.RegionSize; lon < maxLon + Region.RegionSize; lon += Region.RegionSize / 2)
{
allRegions.GetRegion(new Coordinates(lat, lon));
}
}
Console.WriteLine("Regions Loaded. Rendering.");
ValueTuple<Image, Renderer.Bounds> baseRender = Renderer.DrawArea(allRegions);
return baseRender;
}
private static void TestVariables(string workingDir, Coordinates start, Coordinates finish, int threads)
{ {
string parentFolder = new DirectoryInfo(workingDir).Parent!.FullName; string parentFolder = new DirectoryInfo(workingDir).Parent!.FullName;
RegionManager rm = new (workingDir); Queue<Thread> CalcThreads = new();
Queue<Thread> calcThreads = new(); for (double sameRoadPriority = 0.002; sameRoadPriority < 0.4; sameRoadPriority += 0.02)
for (double roadLevelPriority = 0.02; roadLevelPriority > 0; roadLevelPriority -= 0.001)
{ {
for (double maxAngle = 5; maxAngle < 45; maxAngle += 5) for (double roadLevelPriority = 0.4; roadLevelPriority > 0; roadLevelPriority -= 0.02)
{ {
double priority = roadLevelPriority; for (double angleWeightFactor = 0.01; angleWeightFactor < 0.25; angleWeightFactor += 0.01)
double angle = maxAngle;
calcThreads.Enqueue(new Thread(() =>
{ {
Pathfinder testresult = new Pathfinder(rm, priority, angle).AStar(start, double priority = roadLevelPriority;
finish, Tag.SpeedType.car); double roadPriority = sameRoadPriority;
string fileName = $"angle{angle:0.000}_level{priority:0.000}.result"; double factor = angleWeightFactor;
testresult.SaveResult(Path.Join(parentFolder, fileName)); CalcThreads.Enqueue(new Thread(() =>
})); {
Pathfinder testresult = new Pathfinder(workingDir).AStar(start,
finish, Tag.SpeedType.car, priority, roadPriority,
0, factor);
string fileName =
$"angle{factor:0.000}_level{priority:0.000}_same{roadPriority:0.000}.result";
testresult.SaveResult(Path.Join(parentFolder, fileName));
}));
}
} }
} }
int totalTasks = calcThreads.Count; int totalTasks = CalcThreads.Count;
int completedTasks = 0; int completed = 0;
DateTime startTime = DateTime.Now; DateTime startTime = DateTime.Now;
HashSet<Thread> runningThreads = new(); HashSet<Thread> runningThreads = new();
while (calcThreads.Count > 0) while (CalcThreads.Count > 0)
{ {
while (runningThreads.Count < threads && calcThreads.Count > 0) while (runningThreads.Count < 16 && CalcThreads.Count > 0)
{ {
Thread t = calcThreads.Dequeue(); Thread t = CalcThreads.Dequeue();
runningThreads.Add(t); runningThreads.Add(t);
t.Start(); t.Start();
} }
int newCompletedTasks = runningThreads.RemoveWhere(thread => !thread.IsAlive); int newCompleted = runningThreads.RemoveWhere(thread => !thread.IsAlive);
completedTasks += newCompletedTasks; completed += newCompleted;
if (newCompletedTasks > 0) if(newCompleted > 0)
{ Console.WriteLine($"To calculate: {CalcThreads.Count}/{totalTasks} Average Time: {(DateTime.Now - startTime)/(completed)} Elapsed: {DateTime.Now - startTime} Remaining: {(DateTime.Now - startTime)/(completed)*CalcThreads.Count}");
TimeSpan elapsedTime = DateTime.Now - startTime;
Console.WriteLine($"To calculate: {calcThreads.Count}/{totalTasks} Time Average: {(elapsedTime)/(completedTasks)} Elapsed: {elapsedTime} Remaining: {(elapsedTime)/(completedTasks)*calcThreads.Count}");
}
} }
} }
} }