2023-04-09 16:22:22 +02:00
|
|
|
using System.Text.Json;
|
2023-02-03 23:35:22 +01:00
|
|
|
using OSMDatastructure;
|
2023-03-14 17:00:59 +01:00
|
|
|
using OSMDatastructure.Graph;
|
2023-04-21 13:41:01 +02:00
|
|
|
using SpeedType = OSMDatastructure.Tag.SpeedType;
|
|
|
|
using WayType = OSMDatastructure.Tag.WayType;
|
2023-02-03 23:35:22 +01:00
|
|
|
|
2023-04-09 16:24:43 +02:00
|
|
|
namespace Pathfinding
|
2023-02-03 23:35:22 +01:00
|
|
|
{
|
|
|
|
public class RegionManager
|
|
|
|
{
|
|
|
|
private string workingDirectory { get; }
|
2023-03-31 21:56:27 +02:00
|
|
|
private readonly Dictionary<ulong, Region> _regions = new();
|
2023-02-03 23:35:22 +01:00
|
|
|
|
|
|
|
public RegionManager(string workingDirectory)
|
|
|
|
{
|
|
|
|
this.workingDirectory = workingDirectory;
|
|
|
|
}
|
2023-04-01 14:43:05 +02:00
|
|
|
|
|
|
|
public Region? GetRegion(Coordinates coordinates)
|
|
|
|
{
|
|
|
|
return GetRegion(Coordinates.GetRegionHashCode(coordinates));
|
|
|
|
}
|
|
|
|
|
|
|
|
public Region? GetRegion(ulong id)
|
|
|
|
{
|
2023-04-21 15:13:42 +02:00
|
|
|
|
|
|
|
if(_regions.TryGetValue(id, out Region? retRegion))
|
|
|
|
return retRegion;
|
2023-04-20 23:02:38 +02:00
|
|
|
|
|
|
|
Region? loadedRegion = RegionFromId(id);
|
2023-04-21 15:13:42 +02:00
|
|
|
|
2023-04-21 18:34:49 +02:00
|
|
|
if(_regions.TryGetValue(id, out Region? retRegion2)) //Prevent other thread from changing collection
|
|
|
|
return retRegion2;
|
2023-04-20 23:02:38 +02:00
|
|
|
if(loadedRegion is not null)
|
2023-04-21 15:13:42 +02:00
|
|
|
_regions.TryAdd(loadedRegion.regionHash, loadedRegion);
|
|
|
|
|
|
|
|
return _regions[id];
|
2023-02-03 23:35:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public Region[] GetAllRegions()
|
|
|
|
{
|
2023-04-20 23:02:38 +02:00
|
|
|
return _regions.Values.ToArray();
|
2023-02-03 23:35:22 +01:00
|
|
|
}
|
2023-04-01 14:43:05 +02:00
|
|
|
|
2023-04-21 15:13:42 +02:00
|
|
|
private static Region? RegionFromFile(string filePath)
|
2023-04-09 16:22:22 +02:00
|
|
|
{
|
2023-04-20 23:02:38 +02:00
|
|
|
if (!File.Exists(filePath))
|
|
|
|
return null;
|
2023-04-21 11:01:05 +02:00
|
|
|
|
2023-04-21 14:44:18 +02:00
|
|
|
FileStream regionFile = new (filePath, FileMode.Open, FileAccess.Read, FileShare.Read, (int)new FileInfo(filePath).Length, FileOptions.SequentialScan);
|
2023-04-21 11:40:15 +02:00
|
|
|
Region retRegion = JsonSerializer.Deserialize<Region>(regionFile, Region.serializerOptions)!;
|
|
|
|
regionFile.Dispose();
|
|
|
|
return retRegion;
|
2023-04-09 16:22:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private Region? RegionFromId(ulong regionId)
|
|
|
|
{
|
|
|
|
string filePath = Path.Join(workingDirectory, $"{regionId}.region");
|
|
|
|
return RegionFromFile(filePath);
|
|
|
|
}
|
2023-04-09 16:32:02 +02:00
|
|
|
|
|
|
|
public OsmNode? GetNode(ulong nodeId, ulong regionId)
|
|
|
|
{
|
|
|
|
Region? r = GetRegion(regionId);
|
|
|
|
return r?.GetNode(nodeId);
|
|
|
|
}
|
2023-04-09 16:41:42 +02:00
|
|
|
|
2023-04-21 14:44:18 +02:00
|
|
|
public bool TestValidConnectionForType(OsmNode node1, OsmNode node2, SpeedType type)
|
2023-04-09 16:41:42 +02:00
|
|
|
{
|
|
|
|
foreach (OsmEdge edge in node1.edges)
|
|
|
|
{
|
|
|
|
if (edge.neighborId.Equals(node2.nodeId))
|
|
|
|
return TestValidConnectionForType(node1, edge, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-04-21 14:44:18 +02:00
|
|
|
public bool TestValidConnectionForType(OsmNode node1, OsmEdge edge, SpeedType type)
|
2023-04-09 16:41:42 +02:00
|
|
|
{
|
2023-04-21 14:44:18 +02:00
|
|
|
if (type == SpeedType.any)
|
2023-04-09 18:27:53 +02:00
|
|
|
return true;
|
2023-04-09 19:22:34 +02:00
|
|
|
byte speed = GetSpeedForEdge(node1, edge.wayId, type);
|
|
|
|
return (speed is not 0);
|
2023-04-09 16:41:42 +02:00
|
|
|
}
|
|
|
|
|
2023-04-21 14:44:18 +02:00
|
|
|
public OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, SpeedType vehicle)
|
2023-04-09 16:41:42 +02:00
|
|
|
{
|
|
|
|
OsmNode? closest = null;
|
|
|
|
double distance = double.MaxValue;
|
|
|
|
Region? region = GetRegion(coordinates);
|
|
|
|
if (region is null)
|
|
|
|
return null;
|
|
|
|
foreach (OsmNode node in region.nodes)
|
|
|
|
{
|
2023-04-09 18:27:53 +02:00
|
|
|
bool hasConnectionUsingVehicle = true;
|
2023-04-21 14:44:18 +02:00
|
|
|
if (vehicle is not SpeedType.any)
|
2023-04-09 16:41:42 +02:00
|
|
|
{
|
2023-04-09 18:27:53 +02:00
|
|
|
hasConnectionUsingVehicle = false;
|
|
|
|
foreach (OsmEdge edge in node.edges)
|
|
|
|
{
|
2023-04-09 21:02:32 +02:00
|
|
|
if (TestValidConnectionForType(node, edge, vehicle))
|
2023-04-09 18:27:53 +02:00
|
|
|
hasConnectionUsingVehicle = true;
|
|
|
|
}
|
2023-04-09 16:41:42 +02:00
|
|
|
}
|
2023-04-09 18:27:53 +02:00
|
|
|
|
2023-04-09 16:41:42 +02:00
|
|
|
double nodeDistance = Utils.DistanceBetween(node, coordinates);
|
2023-04-09 21:02:32 +02:00
|
|
|
if (hasConnectionUsingVehicle && nodeDistance < distance)
|
2023-04-09 16:41:42 +02:00
|
|
|
{
|
|
|
|
closest = node;
|
|
|
|
distance = nodeDistance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return closest;
|
|
|
|
}
|
|
|
|
|
2023-04-21 14:44:18 +02:00
|
|
|
public byte GetSpeedForEdge(OsmNode node1, ulong wayId, SpeedType vehicle)
|
2023-04-09 16:41:42 +02:00
|
|
|
{
|
|
|
|
TagManager tags = GetRegion(node1.coordinates)!.tagManager;
|
2023-04-21 14:44:18 +02:00
|
|
|
WayType wayType = (WayType)tags.GetTag(wayId, Tag.TagType.highway)!;
|
2023-04-09 19:22:34 +02:00
|
|
|
byte speed = 0;
|
2023-04-09 16:41:42 +02:00
|
|
|
switch (vehicle)
|
|
|
|
{
|
2023-04-21 14:44:18 +02:00
|
|
|
case SpeedType.pedestrian:
|
2023-04-09 19:22:34 +02:00
|
|
|
speed = Tag.defaultSpeedPedestrian[wayType];
|
2023-04-09 21:02:32 +02:00
|
|
|
return speed;
|
2023-04-21 14:44:18 +02:00
|
|
|
case SpeedType.car:
|
2023-04-09 16:41:42 +02:00
|
|
|
byte? maxSpeed = (byte?)tags.GetTag(wayId, Tag.TagType.maxspeed);
|
2023-04-09 19:22:34 +02:00
|
|
|
speed = Tag.defaultSpeedCar[wayType];
|
|
|
|
return maxSpeed < speed ? (byte)maxSpeed : speed;
|
2023-04-21 14:44:18 +02:00
|
|
|
case SpeedType.any:
|
2023-04-09 18:27:53 +02:00
|
|
|
return 1;
|
2023-04-09 16:41:42 +02:00
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2023-04-21 13:41:01 +02:00
|
|
|
|
2023-04-21 13:43:50 +02:00
|
|
|
public double GetPriorityForVehicle(SpeedType speedType, OsmEdge edge, OsmNode node)
|
2023-04-21 13:41:01 +02:00
|
|
|
{
|
|
|
|
if (speedType == SpeedType.any)
|
|
|
|
return 1;
|
2023-04-21 13:43:50 +02:00
|
|
|
Region region = GetRegion(node.coordinates)!;
|
2023-04-21 13:41:01 +02:00
|
|
|
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 17;
|
|
|
|
case WayType.trunk:
|
|
|
|
case WayType.trunk_link:
|
|
|
|
case WayType.primary:
|
|
|
|
case WayType.primary_link:
|
|
|
|
return 10;
|
|
|
|
case WayType.secondary:
|
|
|
|
case WayType.secondary_link:
|
|
|
|
return 7;
|
|
|
|
case WayType.tertiary:
|
|
|
|
case WayType.tertiary_link:
|
|
|
|
return 5;
|
|
|
|
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;
|
|
|
|
}
|
2023-02-03 23:35:22 +01:00
|
|
|
}
|
|
|
|
}
|