Renderer now renders 3 different layers over each other: Area (Regions), gScore-Weights (as blobs) and PathResult path.

Returns the Image instead of saving to disk.
This commit is contained in:
glax 2023-04-20 19:40:50 +02:00
parent 946fa0206b
commit 23429c8a00

View File

@ -11,6 +11,10 @@ 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
{ {
@ -26,17 +30,16 @@ public static class Renderer
} }
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")] [SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
public static void DrawGraph(string resultPath, Image? area = null, Bounds? bounds = null) public static Image DrawPathfinder(Pathfinder pathfinder)
{ {
FileStream fs = new FileStream(resultPath, FileMode.Open); Console.WriteLine("Rendering loaded Regions");
PathResult graph = JsonSerializer.Deserialize<PathResult>(fs)!; ValueTuple<Image, Bounds> areaRender = DrawArea(pathfinder.regionManager);
List<Coordinates> coords = new List<Coordinates>(); Console.WriteLine("Rendering gScores (Weights)");
foreach (PathNode node in graph.pathNodes) ValueTuple<Image, Bounds> areaGScoreRender = DrawGScores(pathfinder.gScore!, areaRender.Item1, areaRender.Item2);
coords.Add(node.coordinates); Console.WriteLine("Rendering path");
string workingDir = new DirectoryInfo(resultPath).FullName; ValueTuple<Image, Bounds> areaGScorePathRender = DrawPath(pathfinder.pathResult!, areaGScoreRender.Item1, areaGScoreRender.Item2);
Image renderedImage = DrawLoadedNodes(graph.gScoreNodes!, graph.gScore!, coords, area, bounds); return areaGScorePathRender.Item1;
renderedImage.Save($"{workingDir}-routing.png");
} }
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")] [SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
@ -63,6 +66,7 @@ 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);
@ -89,13 +93,54 @@ public static class Renderer
} }
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")] [SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
public static Image DrawLoadedNodes(HashSet<OsmNode> nodes, Dictionary<ulong, double> gScoreDict, public static ValueTuple<Image, Bounds> DrawPath(PathResult pathResult, Image? renderOver = null, Bounds? bounds = null)
List<Coordinates> pathCoordinates, Image? renderOver = null, Bounds? bounds = null)
{ {
float minLat = bounds?.minLat ?? nodes.Min(node => node.coordinates.latitude); List<Coordinates> coordinates = new();
float minLon = bounds?.minLon ?? nodes.Min(node => node.coordinates.longitude); foreach(PathNode node in pathResult.pathNodes)
float maxLat = bounds?.maxLat ?? nodes.Max(node => node.coordinates.latitude); coordinates.Add(node.coordinates);
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);
@ -107,55 +152,28 @@ 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; Image ret = renderOver ?? new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
Graphics g; Graphics g = Graphics.FromImage(ret);
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 foreach (KeyValuePair<OsmNode, double> kv in gScoreDict)
{
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); double percentage = (kv.Value - minWeight) / (maxWeight - minWeight);
Brush b = new SolidBrush(GradientPick(percentage, start, center, end)); Brush b = new SolidBrush(GradientPick(percentage, WeightStartColor, WeightCenterColor, WeightEndColor));
OsmNode node = nodes.First(node => node.nodeId.Equals(kv.Key));
float x = (node.coordinates.longitude - minLon) * scaleFactor; float x = (kv.Key.coordinates.longitude - minLon) * scaleFactor;
float y = (maxLat - node.coordinates.latitude) * scaleFactor; float y = (maxLat - kv.Key.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);
} }
Pen p = new Pen(Color.Red, PenThickness);
for (int i = 0; i < pathCoordinates.Count - 1; i++) return new ValueTuple<Image, Bounds>(ret, new Bounds(minLat,minLon,maxLat,maxLon));
{
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 * https://stackoverflow.com/questions/55601338/get-a-color-value-within-a-gradient-based-on-a-value
*/ */