OSMServer/RenderPath/Renderer.cs

176 lines
7.0 KiB
C#
Raw Normal View History

2023-04-13 00:24:33 +02:00
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.Json;
2023-04-13 00:24:33 +02:00
using OSMDatastructure.Graph;
using Pathfinding;
2023-04-13 00:24:33 +02:00
namespace RenderPath;
public static class Renderer
2023-04-13 00:24:33 +02:00
{
private const int ImageMaxSize = 20000;
private const float PenThickness = 4;
public class Bounds
{
public readonly float minLat, maxLat, minLon, maxLon;
public Bounds(float minLat, float minLon, float maxLat, float maxLon)
{
this.minLon = minLon;
this.maxLat = maxLat;
this.maxLon = maxLon;
this.minLat = minLat;
}
}
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
public static void DrawGraph(string resultPath, Image? area = null, Bounds? bounds = null)
{
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;
Image renderedImage = DrawLoadedNodes(graph.gScoreNodes!, graph.gScore!, coords, area, bounds);
renderedImage.Save($"{workingDir}-routing.png");
}
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
public static ValueTuple<Image, Bounds> DrawArea(RegionManager rm)
2023-04-13 00:24:33 +02:00
{
HashSet<OsmNode> nodes = new HashSet<OsmNode>();
foreach (OSMDatastructure.Region r in rm.GetAllRegions())
nodes = nodes.Concat(r.nodes).ToHashSet();
float minLat = nodes.Min(node => node.coordinates.latitude);
float minLon = nodes.Min(node => node.coordinates.longitude);
float maxLat = nodes.Max(node => node.coordinates.latitude);
float maxLon = nodes.Max(node => node.coordinates.longitude);
2023-04-13 00:24:33 +02:00
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 = new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
Graphics g = Graphics.FromImage(ret);
g.Clear(Color.White);
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 (OsmEdge edge in node.edges)
{
Coordinates c1 = node.coordinates;
OsmNode nNode = rm.GetNode(edge.neighborId, edge.neighborRegion)!;
Coordinates c2 = nNode.coordinates;
Pen p = new Pen(GradientPick(0, start, center, end), PenThickness);
float x1 = (c1.longitude - minLon) * scaleFactor;
float y1 = (maxLat - c1.latitude) * scaleFactor;
float x2 = (c2.longitude - minLon) * scaleFactor;
float y2 = (maxLat - c2.latitude) * scaleFactor;
g.DrawLine(p, x1, y1, x2, y2);
}
}
return new ValueTuple<Image, Bounds>(ret, new Bounds(minLat,minLon,maxLat,maxLon));
}
[SuppressMessage("Interoperability", "CA1416:Plattformkompatibilität überprüfen")]
public static Image DrawLoadedNodes(HashSet<OsmNode> nodes, Dictionary<ulong, double> gScoreDict,
List<Coordinates> pathCoordinates, Image? renderOver = null, Bounds? bounds = null)
{
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);
2023-04-13 00:24:33 +02:00
float latDiff = maxLat - minLat;
float lonDiff = maxLon - minLon;
float scaleFactor = latDiff > lonDiff ? ImageMaxSize / latDiff : ImageMaxSize / lonDiff;
2023-04-13 00:24:33 +02:00
int pixelsX = (int)(lonDiff * scaleFactor);
int pixelsY = (int)(latDiff * scaleFactor);
Image ret;
Graphics g;
if (renderOver is null)
{
ret = new Bitmap(pixelsX, pixelsY, PixelFormat.Format32bppRgb);
g = Graphics.FromImage(ret);
g.Clear(Color.White);
}
else
{
ret = renderOver;
g = Graphics.FromImage(ret);
}
2023-04-13 00:24:33 +02:00
Color start = Color.FromArgb(0, 0, 255);
Color center = Color.FromArgb(255, 255, 0);
2023-04-13 01:12:29 +02:00
Color end = Color.FromArgb(0, 255, 0);
foreach (KeyValuePair<ulong, double> kv in gScoreDict)
2023-04-13 00:24:33 +02:00
{
2023-04-13 01:12:29 +02:00
double percentage = (kv.Value - minWeight) / (maxWeight - minWeight);
Brush b = new SolidBrush(GradientPick(percentage, start, center, end));
OsmNode node = nodes.First(node => node.nodeId.Equals(kv.Key));
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);
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));
2023-04-13 00:24:33 +02:00
g.DrawLine(p, p1, p2);
}
return ret;
}
/*
* https://stackoverflow.com/questions/55601338/get-a-color-value-within-a-gradient-based-on-a-value
*/
private static int LinearInterp(int start, int end, double percentage) => start + (int)Math.Round(percentage * (end - start));
private static Color ColorInterp(Color start, Color end, double percentage) =>
Color.FromArgb(LinearInterp(start.A, end.A, percentage),
LinearInterp(start.R, end.R, percentage),
LinearInterp(start.G, end.G, 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);
2023-04-13 00:24:33 +02:00
}
}