Compare commits
29 Commits
95c0088b73
...
ec6725a5c5
Author | SHA1 | Date | |
---|---|---|---|
ec6725a5c5 | |||
886ccaa8dc | |||
af1d9baf4f | |||
e2332847cd | |||
dca4d56866 | |||
5f6cccd17d | |||
97a8c2ea6f | |||
b89a3715a1 | |||
6bc1d3c7ce | |||
5b8a1d1e10 | |||
7856f1c66c | |||
97a057a3d4 | |||
bc39785f6f | |||
18822e2152 | |||
68cb0ee3fd | |||
465d40a475 | |||
7fd9047ac4 | |||
601200a8d6 | |||
a758c8c63e | |||
ed46a419e3 | |||
a1d9ccad46 | |||
914731c8a3 | |||
d8ce6e4ce5 | |||
976108569b | |||
6373874495 | |||
c43c6dc985 | |||
33232a7eb7 | |||
cf5b1e9945 | |||
aa8b1e4451 |
@ -13,20 +13,18 @@ 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 stayOnSameRoadPriority, double useHigherLevelRoadsPriority, double useRoadsWithLessJunctionsPriority, double angleFactor) =>
|
app.MapGet("/getRoute", (float latStart, float lonStart, float latEnd, float lonEnd, Tag.SpeedType vehicle, double useHigherLevelRoadsPriority, double maxTurnAngle) =>
|
||||||
{
|
{
|
||||||
Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest").AStar(new Coordinates(latStart, lonStart),
|
Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest", useHigherLevelRoadsPriority, maxTurnAngle).AStar(new Coordinates(latStart, lonStart),
|
||||||
new Coordinates(latEnd, lonEnd), vehicle, useHigherLevelRoadsPriority, stayOnSameRoadPriority,
|
new Coordinates(latEnd, lonEnd), vehicle);
|
||||||
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").AStar(new Coordinates(latStart, lonStart),
|
Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest", 0, 30).AStar(new Coordinates(latStart, lonStart),
|
||||||
new Coordinates(latEnd, lonEnd), Tag.SpeedType.any, 0, 0,
|
new Coordinates(latEnd, lonEnd), Tag.SpeedType.any);
|
||||||
0, 0);
|
|
||||||
return result.pathResult;
|
return result.pathResult;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
|
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 TimeSpan calcTime;
|
|
||||||
[JsonInclude]public List<PathNode> pathNodes;
|
|
||||||
[JsonInclude]public double distance;
|
[JsonInclude]public double distance;
|
||||||
[JsonInclude]public double weight;
|
[JsonInclude]public double weight;
|
||||||
|
[JsonInclude]public TimeSpan calcTime;
|
||||||
|
[JsonInclude]public List<PathNode> pathNodes;
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public PathResult(TimeSpan calcTime, List<PathNode> pathNodes, double distance, double weight)
|
public PathResult(TimeSpan calcTime, List<PathNode> pathNodes, double distance, double weight)
|
||||||
@ -17,4 +18,10 @@ 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))!;
|
||||||
|
}
|
||||||
}
|
}
|
@ -9,26 +9,31 @@ 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)
|
public Pathfinder(string workingDirectory, double roadPriorityFactor, double turnAngle)
|
||||||
{
|
{
|
||||||
if (!Path.Exists(workingDirectory))
|
if (!Path.Exists(workingDirectory))
|
||||||
throw new DirectoryNotFoundException(workingDirectory);
|
throw new DirectoryNotFoundException(workingDirectory);
|
||||||
regionManager = new(workingDirectory);
|
regionManager = new(workingDirectory);
|
||||||
workingDir = workingDirectory;
|
this.roadPriorityFactor = roadPriorityFactor;
|
||||||
|
this.turnAngle = turnAngle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pathfinder AStar(Coordinates startCoordinates, Coordinates goalCoordinates,
|
public Pathfinder(RegionManager regionManager, double roadPriorityFactor, double turnAngle)
|
||||||
SpeedType vehicle, double heuristicRoadLevelPriority, double heuristicSameRoadPriority,
|
{
|
||||||
double heuristicFewJunctionsPriority, double angleWeightFactor)
|
this.regionManager = regionManager;
|
||||||
|
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);
|
||||||
@ -38,49 +43,71 @@ public class Pathfinder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PriorityQueue<OsmNode, double> openSetfScore = new();
|
RPriorityQueue<OsmNode, double> openSetfScore = new();
|
||||||
openSetfScore.Enqueue(startNode, 0);
|
openSetfScore.Enqueue(startNode, 0);
|
||||||
gScore = new() { { startNode, 0 } };
|
gScore = new() { { startNode, 0 } };
|
||||||
_cameFromDict = new();
|
_cameFromDict = new();
|
||||||
|
|
||||||
while (openSetfScore.Count > 0)
|
bool found = false;
|
||||||
|
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))
|
||||||
{
|
{
|
||||||
TimeSpan calcTime = DateTime.Now - startCalc;
|
if (!found)
|
||||||
pathResult = GetPath(goalNode, calcTime);
|
{
|
||||||
Console.WriteLine($"Path found. {calcTime} PathLength {pathResult.pathNodes.Count} VisitedNodes {gScore.Count} Distance {pathResult.distance} Duration {pathResult.weight}");
|
firstFound = DateTime.Now - startCalc;
|
||||||
return this;
|
found = true;
|
||||||
|
}
|
||||||
|
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 =
|
double tentativeGScore = gScore[currentNode] + Weight(currentNode, neighbor, edge);
|
||||||
gScore[currentNode] + Weight(currentNode, neighbor, edge, angleWeightFactor);
|
|
||||||
gScore.TryAdd(neighbor, double.MaxValue);
|
gScore.TryAdd(neighbor, double.MaxValue);
|
||||||
if (tentativeGScore < gScore[neighbor])
|
if ((!found || (found && tentativeGScore < maxGscore)) && 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pathResult = new(DateTime.Now - startCalc, new List<PathNode>(),0 ,0);
|
TimeSpan calcTime = DateTime.Now - startCalc;
|
||||||
|
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, double angleWeightFactor)
|
private double Weight(OsmNode currentNode, OsmNode neighborNode, OsmEdge edge)
|
||||||
{
|
{
|
||||||
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);
|
||||||
@ -89,52 +116,43 @@ public class Pathfinder
|
|||||||
if (_cameFromDict!.ContainsKey(currentNode))
|
if (_cameFromDict!.ContainsKey(currentNode))
|
||||||
{
|
{
|
||||||
OsmNode previousNode = _cameFromDict[currentNode];
|
OsmNode previousNode = _cameFromDict[currentNode];
|
||||||
Vector v1 = new(previousNode, currentNode);
|
Vector v1 = new(currentNode, previousNode);
|
||||||
Vector v2 = new(currentNode, neighborNode);
|
Vector v2 = new(currentNode, neighborNode);
|
||||||
double nodeAngle = v1.Angle(v2);
|
double nodeAngle = v1.Angle(v2);
|
||||||
angle = ((180 - nodeAngle) / 180) * angleWeightFactor;
|
if (nodeAngle < turnAngle)
|
||||||
|
angle = 0;
|
||||||
|
else
|
||||||
|
angle = nodeAngle / 180;
|
||||||
}
|
}
|
||||||
double prio = regionManager.GetPriorityForVehicle(_speedType,edge, currentNode);
|
double prio = regionManager.GetPriorityForVehicle(_speedType,edge, currentNode) * roadPriorityFactor;
|
||||||
return distance / (1 + speed + angle + prio);
|
|
||||||
|
return distance / (speed * angle + prio + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double Heuristic(OsmNode currentNode, OsmNode neighborNode, OsmNode goalNode, OsmEdge edge, double roadPriorityFactor, double junctionFactor, double sameRoadFactor, double nodeAngleFactor)
|
private double Heuristic(OsmNode currentNode, OsmNode neighborNode, OsmNode goalNode, OsmEdge edge)
|
||||||
{
|
{
|
||||||
double roadPriority = regionManager.GetPriorityForVehicle(_speedType, edge, currentNode) * roadPriorityFactor;
|
if (neighborNode.Equals(goalNode)) return 0;
|
||||||
if (roadPriority == 0)
|
double priority = regionManager.GetPriorityForVehicle(_speedType, edge, currentNode);
|
||||||
|
if (priority == 0)
|
||||||
return double.MaxValue;
|
return double.MaxValue;
|
||||||
|
|
||||||
double speed = regionManager.GetSpeedForEdge(currentNode, edge.wayId, _speedType) * 0.0003;
|
double speed = regionManager.GetSpeedForEdge(currentNode, edge.wayId, _speedType);
|
||||||
|
|
||||||
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(previousNode, currentNode);
|
Vector v1 = new(currentNode, previousNode);
|
||||||
Vector v2 = new(currentNode, neighborNode);
|
Vector v2 = new(currentNode, neighborNode);
|
||||||
double nodeAngle = v1.Angle(v2);
|
double nodeAngle = v1.Angle(v2);
|
||||||
angle = ((180 - nodeAngle) / 180) * nodeAngleFactor;
|
if (nodeAngle < turnAngle)
|
||||||
|
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)
|
||||||
@ -199,6 +217,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
36
Pathfinding/RPriorityQueue.cs
Normal file
36
Pathfinding/RPriorityQueue.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -23,13 +23,18 @@ namespace Pathfinding
|
|||||||
|
|
||||||
public Region? GetRegion(ulong id)
|
public Region? GetRegion(ulong id)
|
||||||
{
|
{
|
||||||
if(_regions.TryGetValue(id, out Region? value))
|
|
||||||
return value;
|
if(_regions.TryGetValue(id, out Region? retRegion))
|
||||||
|
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.Add(loadedRegion!.regionHash, value: loadedRegion);
|
_regions.TryAdd(loadedRegion.regionHash, loadedRegion);
|
||||||
return loadedRegion;
|
|
||||||
|
return _regions[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Region[] GetAllRegions()
|
public Region[] GetAllRegions()
|
||||||
@ -37,12 +42,12 @@ namespace Pathfinding
|
|||||||
return _regions.Values.ToArray();
|
return _regions.Values.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Region? RegionFromFile(string filePath)
|
private static 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);
|
FileStream regionFile = new (filePath, FileMode.Open, FileAccess.Read, FileShare.Read, (int)new FileInfo(filePath).Length, FileOptions.SequentialScan);
|
||||||
Region retRegion = JsonSerializer.Deserialize<Region>(regionFile, Region.serializerOptions)!;
|
Region retRegion = JsonSerializer.Deserialize<Region>(regionFile, Region.serializerOptions)!;
|
||||||
regionFile.Dispose();
|
regionFile.Dispose();
|
||||||
return retRegion;
|
return retRegion;
|
||||||
@ -60,7 +65,7 @@ namespace Pathfinding
|
|||||||
return r?.GetNode(nodeId);
|
return r?.GetNode(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TestValidConnectionForType(OsmNode node1, OsmNode node2, Tag.SpeedType type)
|
public bool TestValidConnectionForType(OsmNode node1, OsmNode node2, SpeedType type)
|
||||||
{
|
{
|
||||||
foreach (OsmEdge edge in node1.edges)
|
foreach (OsmEdge edge in node1.edges)
|
||||||
{
|
{
|
||||||
@ -71,15 +76,15 @@ namespace Pathfinding
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TestValidConnectionForType(OsmNode node1, OsmEdge edge, Tag.SpeedType type)
|
public bool TestValidConnectionForType(OsmNode node1, OsmEdge edge, SpeedType type)
|
||||||
{
|
{
|
||||||
if (type == Tag.SpeedType.any)
|
if (type == 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, Tag.SpeedType vehicle)
|
public OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, SpeedType vehicle)
|
||||||
{
|
{
|
||||||
OsmNode? closest = null;
|
OsmNode? closest = null;
|
||||||
double distance = double.MaxValue;
|
double distance = double.MaxValue;
|
||||||
@ -89,7 +94,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 Tag.SpeedType.any)
|
if (vehicle is not SpeedType.any)
|
||||||
{
|
{
|
||||||
hasConnectionUsingVehicle = false;
|
hasConnectionUsingVehicle = false;
|
||||||
foreach (OsmEdge edge in node.edges)
|
foreach (OsmEdge edge in node.edges)
|
||||||
@ -110,21 +115,21 @@ namespace Pathfinding
|
|||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte GetSpeedForEdge(OsmNode node1, ulong wayId, Tag.SpeedType vehicle)
|
public byte GetSpeedForEdge(OsmNode node1, ulong wayId, 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)!;
|
WayType wayType = (WayType)tags.GetTag(wayId, Tag.TagType.highway)!;
|
||||||
byte speed = 0;
|
byte speed = 0;
|
||||||
switch (vehicle)
|
switch (vehicle)
|
||||||
{
|
{
|
||||||
case Tag.SpeedType.pedestrian:
|
case SpeedType.pedestrian:
|
||||||
speed = Tag.defaultSpeedPedestrian[wayType];
|
speed = Tag.defaultSpeedPedestrian[wayType];
|
||||||
return speed;
|
return speed;
|
||||||
case Tag.SpeedType.car:
|
case 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 Tag.SpeedType.any:
|
case SpeedType.any:
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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;
|
||||||
|
|
||||||
@ -9,11 +10,12 @@ namespace RenderPath;
|
|||||||
public static class Renderer
|
public static class Renderer
|
||||||
{
|
{
|
||||||
private const int ImageMaxSize = 20000;
|
private const int ImageMaxSize = 20000;
|
||||||
private const float PenThickness = 4;
|
private const float PenThickness = 2;
|
||||||
private static readonly Color RouteColor = Color.Red;
|
private static readonly Color RouteColor = Color.Red;
|
||||||
private static readonly Color WeightStartColor = Color.FromArgb(0, 0, 255);
|
private static readonly Color WeightStartColor = Color.FromArgb(127, 0, 100, 255);
|
||||||
private static readonly Color WeightCenterColor = Color.FromArgb(255, 255, 0);
|
private static readonly Color WeightEndColor = Color.FromArgb(255,0, 255, 0);
|
||||||
private static readonly Color WeightEndColor = Color.FromArgb(0, 255, 0);
|
private static readonly Color RoadPrioStart = Color.FromArgb(200, 100, 100, 100);
|
||||||
|
private static readonly Color RoadPrioEnd = Color.FromArgb(255, 255, 180, 0);
|
||||||
|
|
||||||
public class Bounds
|
public class Bounds
|
||||||
{
|
{
|
||||||
@ -61,24 +63,20 @@ 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.Format32bppRgb);
|
Image ret = new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppPArgb);
|
||||||
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(GradientPick(0, start, center, end), PenThickness);
|
Pen p = new(ColorInterp(RoadPrioStart, RoadPrioEnd, priority), 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;
|
||||||
@ -111,7 +109,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.Format32bppRgb);
|
Image ret = renderOver ?? new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppPArgb);
|
||||||
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);
|
||||||
@ -133,16 +131,17 @@ 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> gScoreDict, Image? renderOver = null,
|
public static ValueTuple<Image, Bounds> DrawGScores(Dictionary<OsmNode, double> gScore, 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);
|
|
||||||
|
|
||||||
double minWeight = gScoreDict.Min(kv => kv.Value);
|
float minLat = bounds?.minLat ?? gScore.Min(kv => kv.Key.coordinates.latitude);
|
||||||
double maxWeight = gScoreDict.Max(kv => kv.Value);
|
float minLon = bounds?.minLon ?? gScore.Min(kv => kv.Key.coordinates.longitude);
|
||||||
|
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;
|
||||||
@ -152,22 +151,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 = renderOver ?? new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
|
Image ret = renderOver ?? new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppPArgb);
|
||||||
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);
|
||||||
|
|
||||||
foreach (KeyValuePair<OsmNode, double> kv in gScoreDict)
|
float pointSize = PenThickness * 1.5f;
|
||||||
|
|
||||||
|
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(GradientPick(percentage, WeightStartColor, WeightCenterColor, WeightEndColor));
|
Brush b = new SolidBrush(ColorInterp(WeightStartColor, WeightEndColor, percentage));
|
||||||
|
|
||||||
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 -= (PenThickness * 1.5f) / 2;
|
x -= pointSize / 2;
|
||||||
y -= (PenThickness * 1.5f) / 2;
|
y -= pointSize / 2;
|
||||||
g.FillEllipse(b, x, y, PenThickness * 1.5f, PenThickness * 1.5f);
|
g.FillEllipse(b, x, y, pointSize, pointSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ValueTuple<Image, Bounds>(ret, new Bounds(minLat,minLon,maxLat,maxLon));
|
return new ValueTuple<Image, Bounds>(ret, new Bounds(minLat,minLon,maxLat,maxLon));
|
||||||
@ -182,12 +183,4 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
139
Server/Server.cs
139
Server/Server.cs
@ -26,31 +26,27 @@ 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);
|
TestVariables(workingDir, start, finish, 16);
|
||||||
|
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,
|
||||||
Console.WriteLine("Preparing BaseRender");
|
0, 1, 30).AStar(start,
|
||||||
RegionManager allRegions = new(workingDir);
|
finish, Tag.SpeedType.car);
|
||||||
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}");
|
||||||
|
|
||||||
@ -64,54 +60,101 @@ 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 TestVariables(string workingDir, Coordinates start, Coordinates finish)
|
private static void GetShortestRoute(string directory)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
Queue<Thread> CalcThreads = new();
|
RegionManager rm = new (workingDir);
|
||||||
|
|
||||||
for (double sameRoadPriority = 0.002; sameRoadPriority < 0.4; sameRoadPriority += 0.02)
|
Queue<Thread> calcThreads = new();
|
||||||
|
|
||||||
|
for (double roadLevelPriority = 0.02; roadLevelPriority > 0; roadLevelPriority -= 0.001)
|
||||||
{
|
{
|
||||||
for (double roadLevelPriority = 0.4; roadLevelPriority > 0; roadLevelPriority -= 0.02)
|
for (double maxAngle = 5; maxAngle < 45; maxAngle += 5)
|
||||||
{
|
{
|
||||||
for (double angleWeightFactor = 0.01; angleWeightFactor < 0.25; angleWeightFactor += 0.01)
|
double priority = roadLevelPriority;
|
||||||
|
double angle = maxAngle;
|
||||||
|
calcThreads.Enqueue(new Thread(() =>
|
||||||
{
|
{
|
||||||
double priority = roadLevelPriority;
|
Pathfinder testresult = new Pathfinder(rm, priority, angle).AStar(start,
|
||||||
double roadPriority = sameRoadPriority;
|
finish, Tag.SpeedType.car);
|
||||||
double factor = angleWeightFactor;
|
string fileName = $"angle{angle:0.000}_level{priority:0.000}.result";
|
||||||
CalcThreads.Enqueue(new Thread(() =>
|
testresult.SaveResult(Path.Join(parentFolder, fileName));
|
||||||
{
|
}));
|
||||||
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 completed = 0;
|
int completedTasks = 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 < 16 && CalcThreads.Count > 0)
|
while (runningThreads.Count < threads && calcThreads.Count > 0)
|
||||||
{
|
{
|
||||||
Thread t = CalcThreads.Dequeue();
|
Thread t = calcThreads.Dequeue();
|
||||||
runningThreads.Add(t);
|
runningThreads.Add(t);
|
||||||
t.Start();
|
t.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
int newCompleted = runningThreads.RemoveWhere(thread => !thread.IsAlive);
|
int newCompletedTasks = runningThreads.RemoveWhere(thread => !thread.IsAlive);
|
||||||
completed += newCompleted;
|
completedTasks += newCompletedTasks;
|
||||||
if(newCompleted > 0)
|
if (newCompletedTasks > 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}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user