194 lines
7.8 KiB
C#
194 lines
7.8 KiB
C#
using System.Drawing;
|
|
using OSMDatastructure;
|
|
using OSMDatastructure.Graph;
|
|
using Pathfinding;
|
|
|
|
namespace RenderPath;
|
|
|
|
public abstract class Renderer
|
|
{
|
|
private const int ImageMaxSize = 20000;
|
|
private const int PenThickness = 2;
|
|
private static readonly Color RouteColor = Color.Red;
|
|
private static readonly Color WeightStartColor = Color.FromArgb(127, 0, 100, 255);
|
|
private static readonly Color WeightEndColor = Color.FromArgb(255, 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 Bounds? bounds;
|
|
|
|
public enum RenderType { png, svg}
|
|
|
|
public abstract void DrawLine(float x1, float y1, float x2, float y2, int width, Color color);
|
|
public abstract void DrawDot(float x, float y, int r, Color color);
|
|
public abstract void Save(string path);
|
|
|
|
public static Renderer DrawArea(RegionManager rm, RenderType renderType)
|
|
{
|
|
HashSet<OsmNode> nodes = new();
|
|
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);
|
|
|
|
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);
|
|
|
|
Renderer renderer;
|
|
switch (renderType)
|
|
{
|
|
case RenderType.svg:
|
|
renderer = new SVGRenderer(pixelsX, pixelsY);
|
|
break;
|
|
default:
|
|
renderer = new PNGRenderer(pixelsX, pixelsY);
|
|
break;
|
|
}
|
|
|
|
foreach (OsmNode node in nodes)
|
|
{
|
|
foreach (OsmEdge edge in node.edges)
|
|
{
|
|
double priority = rm.GetPriorityForVehicle(Tag.SpeedType.car, edge, node) / 20;
|
|
Coordinates c1 = node.coordinates;
|
|
OsmNode nNode = rm.GetNode(edge.neighborId, edge.neighborRegion)!;
|
|
Coordinates c2 = nNode.coordinates;
|
|
|
|
float x1 = (c1.longitude - minLon) * scaleFactor;
|
|
float y1 = (maxLat - c1.latitude) * scaleFactor;
|
|
float x2 = (c2.longitude - minLon) * scaleFactor;
|
|
float y2 = (maxLat - c2.latitude) * scaleFactor;
|
|
|
|
renderer.DrawLine(x1, y1, x2, y2, PenThickness, ColorInterp(RoadPrioStart, RoadPrioEnd, priority));
|
|
}
|
|
}
|
|
|
|
renderer.bounds = new Bounds(minLat,minLon,maxLat,maxLon);
|
|
return renderer;
|
|
}
|
|
|
|
public static Renderer DrawPath(PathResult pathResult, RenderType renderType, Renderer? drawOver)
|
|
{
|
|
List<Coordinates> coordinates = new();
|
|
foreach(PathNode node in pathResult.pathNodes)
|
|
coordinates.Add(node.coordinates);
|
|
|
|
float minLat = drawOver?.bounds!.minLat ?? coordinates.Min(coords => coords.latitude);
|
|
float minLon = drawOver?.bounds!.minLon ?? coordinates.Min(coords => coords.longitude);
|
|
float maxLat = drawOver?.bounds!.maxLat ?? coordinates.Max(coords => coords.latitude);
|
|
float maxLon = drawOver?.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);
|
|
|
|
Renderer renderer;
|
|
if(drawOver is null)
|
|
switch (renderType)
|
|
{
|
|
case RenderType.svg:
|
|
renderer = new SVGRenderer(pixelsX, pixelsY);
|
|
break;
|
|
default:
|
|
renderer = new PNGRenderer(pixelsX, pixelsY);
|
|
break;
|
|
}
|
|
else
|
|
renderer = drawOver;
|
|
|
|
for (int i = 0; i < coordinates.Count - 1; i++)
|
|
{
|
|
Coordinates c1 = coordinates[i];
|
|
Coordinates c2 = coordinates[i + 1];
|
|
float x1 = (c1.longitude - minLon) * scaleFactor;
|
|
float y1 = (maxLat - c1.latitude) * scaleFactor;
|
|
float x2 = (c2.longitude - minLon) * scaleFactor;
|
|
float y2 = (maxLat - c2.latitude) * scaleFactor;
|
|
|
|
renderer.DrawLine(x1, y1, x2, y2, PenThickness, RouteColor);
|
|
}
|
|
|
|
renderer.bounds = new Bounds(minLat, minLon, maxLat, maxLon);
|
|
return renderer;
|
|
}
|
|
|
|
public static Renderer DrawGScores(Dictionary<OsmNode, double> gScore, RenderType renderType, Renderer? drawOver)
|
|
{
|
|
|
|
float minLat = drawOver?.bounds!.minLat ?? gScore.Min(kv => kv.Key.coordinates.latitude);
|
|
float minLon = drawOver?.bounds!.minLon ?? gScore.Min(kv => kv.Key.coordinates.longitude);
|
|
float maxLat = drawOver?.bounds!.maxLat ?? gScore.Max(kv => kv.Key.coordinates.latitude);
|
|
float maxLon = drawOver?.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 lonDiff = maxLon - minLon;
|
|
|
|
float scaleFactor = latDiff > lonDiff ? ImageMaxSize / latDiff : ImageMaxSize / lonDiff;
|
|
|
|
int pixelsX = (int)(lonDiff * scaleFactor);
|
|
int pixelsY = (int)(latDiff * scaleFactor);
|
|
|
|
Renderer renderer;
|
|
if(drawOver is null)
|
|
switch (renderType)
|
|
{
|
|
case RenderType.svg:
|
|
renderer = new SVGRenderer(pixelsX, pixelsY);
|
|
break;
|
|
default:
|
|
renderer = new PNGRenderer(pixelsX, pixelsY);
|
|
break;
|
|
}
|
|
else
|
|
renderer = drawOver;
|
|
|
|
foreach (KeyValuePair<OsmNode, double> kv in gScore)
|
|
{
|
|
double percentage = (kv.Value - minWeight) / (maxWeight - minWeight);
|
|
float x = (kv.Key.coordinates.longitude - minLon) * scaleFactor;
|
|
float y = (maxLat - kv.Key.coordinates.latitude) * scaleFactor;
|
|
|
|
renderer.DrawDot(x, y, PenThickness, ColorInterp(WeightStartColor, WeightEndColor, percentage));
|
|
}
|
|
|
|
renderer.bounds = new Bounds(minLat,minLon,maxLat,maxLon);
|
|
return renderer;
|
|
}
|
|
|
|
public static Renderer DrawPathfinder(Pathfinder pathfinder, RenderType renderType)
|
|
{
|
|
Console.WriteLine("Rendering loaded Regions");
|
|
Renderer areaRender = DrawArea(pathfinder.regionManager, renderType);
|
|
Console.WriteLine("Rendering gScores (Weights)");
|
|
Renderer areaGScoreRender = DrawGScores(pathfinder.gScore!, renderType, areaRender);
|
|
Console.WriteLine("Rendering path");
|
|
Renderer areaGScorePathRender = DrawPath(pathfinder.pathResult!, renderType, areaGScoreRender);
|
|
|
|
return areaGScorePathRender;
|
|
}
|
|
|
|
/*
|
|
* 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));
|
|
} |