OSMServer/Pathfinding/RegionManager.cs
2023-05-17 19:08:10 +02:00

200 lines
7.0 KiB
C#

using System.Text.Json;
using OSMDatastructure;
using OSMDatastructure.Graph;
using SpeedType = OSMDatastructure.Tag.SpeedType;
using WayType = OSMDatastructure.Tag.WayType;
namespace Pathfinding
{
public class RegionManager
{
private string workingDirectory { get; }
private readonly Dictionary<ulong, Region> _regions = new();
public RegionManager(string workingDirectory)
{
this.workingDirectory = workingDirectory;
}
public Region? GetRegion(Coordinates coordinates)
{
return GetRegion(Coordinates.GetRegionHashCode(coordinates));
}
public Region? GetRegion(ulong id)
{
if (!_regions.ContainsKey(id))
{
Region? loadedRegion = RegionFromId(id);
if (loadedRegion is not null)
_regions.TryAdd(loadedRegion.regionHash, loadedRegion);
return _regions[id]; //return from _regions instead of loadedRegion for multithreading/pointers
}
return _regions[id];
}
public Region[] GetAllRegions()
{
return _regions.Values.ToArray();
}
private static Region? RegionFromFile(string filePath)
{
if (!File.Exists(filePath))
{
//throw new FileNotFoundException(filePath);
return null;
}
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)!;
regionFile.Dispose();
return retRegion;
}
private Region? RegionFromId(ulong regionId)
{
string filePath = Path.Join(workingDirectory, $"{regionId}.region");
return RegionFromFile(filePath);
}
public OsmNode? GetNode(ulong nodeId, ulong regionId)
{
Region? r = GetRegion(regionId);
return r?.GetNode(nodeId);
}
public bool TestValidConnectionForType(OsmNode node1, OsmNode node2, SpeedType type)
{
foreach (OsmEdge edge in node1.edges.Where(edge => edge.neighborId.Equals(node2.nodeId)))
{
return TestValidConnectionForType(node1, edge, type);
}
return false;
}
public bool TestValidConnectionForType(OsmNode node1, OsmEdge edge, SpeedType type)
{
if (type == SpeedType.any)
return true;
byte speed = GetSpeedForEdge(node1, edge.wayId, type);
return (speed is not 0);
}
public OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, SpeedType vehicle)
{
OsmNode? closest = null;
double distance = double.MaxValue;
Region? region = GetRegion(coordinates);
if (region is null)
return null;
foreach (OsmNode node in region.nodes)
{
bool hasConnectionUsingVehicle = true;
if (vehicle is not SpeedType.any)
{
hasConnectionUsingVehicle = false;
foreach (OsmEdge edge in node.edges)
{
if (TestValidConnectionForType(node, edge, vehicle))
hasConnectionUsingVehicle = true;
}
}
double nodeDistance = Utils.DistanceBetween(node, coordinates);
if (hasConnectionUsingVehicle && nodeDistance < distance)
{
closest = node;
distance = nodeDistance;
}
}
return closest;
}
public byte GetSpeedForEdge(OsmNode node1, ulong wayId, SpeedType vehicle)
{
TagManager tags = GetRegion(node1.coordinates)!.tagManager;
WayType wayType = (WayType)tags.GetTag(wayId, Tag.TagType.highway)!;
byte speed = 0;
switch (vehicle)
{
case SpeedType.pedestrian:
speed = Tag.defaultSpeedPedestrian[wayType];
return speed;
case SpeedType.car:
byte? maxSpeed = (byte?)tags.GetTag(wayId, Tag.TagType.maxspeed);
speed = Tag.defaultSpeedCar[wayType];
return maxSpeed < speed ? (byte)maxSpeed : speed;
case SpeedType.any:
return 1;
default:
return 0;
}
}
public double GetPriorityForVehicle(SpeedType speedType, OsmEdge edge, OsmNode node)
{
if (speedType == SpeedType.any)
return 1;
Region region = GetRegion(node.coordinates)!;
WayType? wayType = (WayType?)region.tagManager.GetTag(edge.wayId, Tag.TagType.highway);
if(wayType is null)
return 0;
if (speedType == SpeedType.car)
{
switch (wayType)
{
case WayType.motorway:
case WayType.motorway_link:
case WayType.motorroad:
return 20;
case WayType.trunk:
case WayType.trunk_link:
case WayType.primary:
case WayType.primary_link:
return 10;
case WayType.secondary:
case WayType.secondary_link:
case WayType.tertiary:
case WayType.tertiary_link:
return 6;
case WayType.unclassified:
case WayType.residential:
case WayType.road:
case WayType.living_street:
return 2;
}
}
if (speedType == SpeedType.pedestrian)
{
switch (wayType)
{
case WayType.pedestrian:
case WayType.corridor:
case WayType.footway:
case WayType.path:
case WayType.steps:
case WayType.residential:
case WayType.living_street:
return 10;
case WayType.service:
case WayType.cycleway:
case WayType.bridleway:
case WayType.road:
case WayType.track:
case WayType.unclassified:
return 5;
case WayType.tertiary:
case WayType.tertiary_link:
case WayType.escape:
return 2;
}
}
return 0;
}
}
}