From a304c24b32c0327436bbd8c2853e60668e161b42 Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Mon, 29 Dec 2025 00:41:52 +0100 Subject: Rename a lot of stuff, add Santiago real time --- .../Services/ShapeTraversalService.cs | 284 --------------------- 1 file changed, 284 deletions(-) delete mode 100644 src/Costasdev.Busurbano.Backend/Services/ShapeTraversalService.cs (limited to 'src/Costasdev.Busurbano.Backend/Services/ShapeTraversalService.cs') diff --git a/src/Costasdev.Busurbano.Backend/Services/ShapeTraversalService.cs b/src/Costasdev.Busurbano.Backend/Services/ShapeTraversalService.cs deleted file mode 100644 index c3c66f4..0000000 --- a/src/Costasdev.Busurbano.Backend/Services/ShapeTraversalService.cs +++ /dev/null @@ -1,284 +0,0 @@ -using Costasdev.Busurbano.Backend.Configuration; -using Costasdev.Busurbano.Backend.Types; -using Microsoft.Extensions.Options; -using ProjNet.CoordinateSystems; -using ProjNet.CoordinateSystems.Transformations; -using SysFile = System.IO.File; - -namespace Costasdev.Busurbano.Backend.Services; - -/// -/// Service for loading shapes and calculating remaining path from a given stop point -/// -public class ShapeTraversalService -{ - private readonly AppConfiguration _configuration; - private readonly ILogger _logger; - private readonly ICoordinateTransformation _transformation; - - public ShapeTraversalService(IOptions options, ILogger logger) - { - _configuration = options.Value; - _logger = logger; - - // Set up coordinate transformation from EPSG:25829 (meters) to EPSG:4326 (lat/lng) - var ctFactory = new CoordinateTransformationFactory(); - var csFactory = new CoordinateSystemFactory(); - - // EPSG:25829 - ETRS89 / UTM zone 29N - var source = csFactory.CreateFromWkt( - "PROJCS[\"ETRS89 / UTM zone 29N\",GEOGCS[\"ETRS89\",DATUM[\"European_Terrestrial_Reference_System_1989\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6258\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4258\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-9],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"25829\"]]"); - - // EPSG:4326 - WGS84 - var target = GeographicCoordinateSystem.WGS84; - - _transformation = ctFactory.CreateFromCoordinateSystems(source, target); - } - - /// - /// Loads a shape from disk - /// - public async Task LoadShapeAsync(string shapeId) - { - var file = Path.Combine(_configuration.VitrasaScheduleBasePath, "shapes", shapeId + ".pb"); - if (!SysFile.Exists(file)) - { - _logger.LogWarning("Shape file not found: {ShapeId}", shapeId); - return null; - } - - try - { - var contents = await SysFile.ReadAllBytesAsync(file); - var shape = Shape.Parser.ParseFrom(contents); - return shape; - } - catch (Exception ex) - { - _logger.LogError(ex, "Error loading shape {ShapeId}", shapeId); - return null; - } - } - - public async Task?> GetShapePathAsync(string shapeId, int startIndex) - { - var shape = await LoadShapeAsync(shapeId); - if (shape == null) return null; - - var result = new List(); - // Ensure startIndex is within bounds - if (startIndex < 0) startIndex = 0; - // If startIndex is beyond the end, return empty list - if (startIndex >= shape.Points.Count) return result; - - for (int i = startIndex; i < shape.Points.Count; i++) - { - var pos = TransformToLatLng(shape.Points[i]); - pos.ShapeIndex = i; - result.Add(pos); - } - return result; - } - - public async Task FindClosestPointIndexAsync(string shapeId, double lat, double lon) - { - var shape = await LoadShapeAsync(shapeId); - if (shape == null) return null; - - // Transform input WGS84 to EPSG:25829 - // Input is [Longitude, Latitude] - var inverseTransform = _transformation.MathTransform.Inverse(); - var transformed = inverseTransform.Transform(new[] { lon, lat }); - - var location = new Epsg25829 { X = transformed[0], Y = transformed[1] }; - - return FindClosestPointIndex(shape.Points, location); - } - - public Shape CreateShapeFromWgs84(List points) - { - var shape = new Shape(); - var inverseTransform = _transformation.MathTransform.Inverse(); - - foreach (var point in points) - { - var transformed = inverseTransform.Transform(new[] { point.Longitude, point.Latitude }); - shape.Points.Add(new Epsg25829 { X = transformed[0], Y = transformed[1] }); - } - return shape; - } - - public Epsg25829 TransformToEpsg25829(double lat, double lon) - { - var inverseTransform = _transformation.MathTransform.Inverse(); - var transformed = inverseTransform.Transform(new[] { lon, lat }); - return new Epsg25829 { X = transformed[0], Y = transformed[1] }; - } - - /// - /// Calculates the bus position by reverse-traversing the shape from the stop location - /// - /// The shape points (in EPSG:25829 meters) - /// The stop location (in EPSG:25829 meters) - /// Distance in meters from the stop to traverse backwards - /// The lat/lng position of the bus and the stop index on the shape - public (Position? BusPosition, int StopIndex) GetBusPosition(Shape shape, Epsg25829 stopLocation, int distanceMeters) - { - if (shape.Points.Count == 0 || distanceMeters <= 0) - { - return (null, -1); - } - - // Find the closest point on the shape to the stop - int closestPointIndex = FindClosestPointIndex(shape.Points, stopLocation); - - // Calculate the total distance from the start of the shape to the stop - double totalDistanceToStop = CalculateTotalDistance(shape.Points.ToArray(), closestPointIndex); - - // If the reported distance exceeds the total distance to the stop, the bus is likely - // on a previous trip whose shape we don't have. Don't provide position information. - if (distanceMeters > totalDistanceToStop) - { - _logger.LogDebug("Distance {Distance}m exceeds total shape distance to stop {Total}m - bus likely on previous trip", distanceMeters, totalDistanceToStop); - return (null, closestPointIndex); - } - - // Traverse backwards from the closest point to find the position at the given distance - var (busPoint, forwardIndex) = TraverseBackwards(shape.Points.ToArray(), closestPointIndex, distanceMeters); - - if (busPoint == null) - { - return (null, closestPointIndex); - } - - var forwardPoint = shape.Points[forwardIndex]; - - // Compute orientation in EPSG:25829 (meters): 0°=North, 90°=East (azimuth) - var dx = forwardPoint.X - busPoint.X; // Easting difference - var dy = forwardPoint.Y - busPoint.Y; // Northing difference - var bearing = Math.Atan2(dx, dy) * 180.0 / Math.PI; // swap for 0° north - if (bearing < 0) bearing += 360.0; - - // Transform from EPSG:25829 (meters) to EPSG:4326 (lat/lng) - var pos = TransformToLatLng(busPoint); - pos.OrientationDegrees = (int)Math.Round(bearing); - pos.ShapeIndex = forwardIndex; - return (pos, closestPointIndex); - } - - /// - /// Traverses backwards along the shape from a starting point by the specified distance - /// - private (Epsg25829 point, int forwardIndex) TraverseBackwards(Epsg25829[] shapePoints, int startIndex, double distanceMeters) - { - if (startIndex <= 0) - { - // Already at the beginning, return the first point - var forwardIdx = Math.Min(1, shapePoints.Length - 1); - return (shapePoints[0], forwardIdx); - } - - double remainingDistance = distanceMeters; - int currentIndex = startIndex; - - while (currentIndex > 0 && remainingDistance > 0) - { - var segmentDistance = CalculateDistance(shapePoints[currentIndex], shapePoints[currentIndex - 1]); - - if (segmentDistance >= remainingDistance) - { - // The bus position is somewhere along this segment - // Interpolate between the two points - var ratio = remainingDistance / segmentDistance; - var interpolated = InterpolatePoint(shapePoints[currentIndex], shapePoints[currentIndex - 1], ratio); - // Forward direction is towards the stop (increasing index direction) - return (interpolated, currentIndex); - } - - remainingDistance -= segmentDistance; - currentIndex--; - } - - // We've reached the beginning of the shape - var fwd = Math.Min(1, shapePoints.Length - 1); - return (shapePoints[0], fwd); - } - - /// - /// Interpolates a point between two points at a given ratio - /// - private Epsg25829 InterpolatePoint(Epsg25829 from, Epsg25829 to, double ratio) - { - return new Epsg25829 - { - X = from.X + (to.X - from.X) * ratio, - Y = from.Y + (to.Y - from.Y) * ratio - }; - } - - /// - /// Finds the index of the closest point in the shape to the given location - /// - private int FindClosestPointIndex(IEnumerable shapePoints, Epsg25829 location) - { - var pointsArray = shapePoints.ToArray(); - var minDistance = double.MaxValue; - var closestIndex = 0; - - for (int i = 0; i < pointsArray.Length; i++) - { - var distance = CalculateDistance(pointsArray[i], location); - if (distance < minDistance) - { - minDistance = distance; - closestIndex = i; - } - } - - return closestIndex; - } - - /// - /// Calculates Euclidean distance between two points in meters - /// - private double CalculateDistance(Epsg25829 p1, Epsg25829 p2) - { - var dx = p1.X - p2.X; - var dy = p1.Y - p2.Y; - return Math.Sqrt(dx * dx + dy * dy); - } - - /// - /// Calculates the total distance along the shape from the start to a given index - /// - private double CalculateTotalDistance(Epsg25829[] shapePoints, int endIndex) - { - if (endIndex <= 0 || shapePoints.Length == 0) - { - return 0; - } - - double totalDistance = 0; - for (int i = 1; i <= endIndex && i < shapePoints.Length; i++) - { - totalDistance += CalculateDistance(shapePoints[i - 1], shapePoints[i]); - } - - return totalDistance; - } - - /// - /// Transforms a point from EPSG:25829 (meters) to EPSG:4326 (lat/lng) - /// - private Position TransformToLatLng(Epsg25829 point) - { - var transformed = _transformation.MathTransform.Transform(new[] { point.X, point.Y }); - return new Position - { - // Round to 6 decimals (~0.1m precision) - Longitude = Math.Round(transformed[0], 6), - Latitude = Math.Round(transformed[1], 6) - }; - } - -} -- cgit v1.3