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 _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.TryGetValue(id, out Region? value)) return value; Region? loadedRegion = RegionFromId(id); if(loadedRegion is not null) _regions.Add(loadedRegion!.regionHash, loadedRegion); return loadedRegion; } public Region[] GetAllRegions() { return _regions.Values.ToArray(); } private Region? RegionFromFile(string filePath) { if (!File.Exists(filePath)) return null; FileStream regionFile = new (filePath, FileMode.Open, FileAccess.Read, FileShare.Read, (int)new FileInfo(filePath).Length, FileOptions.SequentialScan); Region retRegion = JsonSerializer.Deserialize(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) { if (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 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; } } }