diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-11-21 21:22:33 +0100 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-11-21 21:22:33 +0100 |
| commit | 04a8eb43eead686c0e32255965f6e573c5ffcbfa (patch) | |
| tree | 95defdcdbb7e1fccbfaa2534ac959b99b8b4b54a /src/Costasdev.Busurbano.Backend | |
| parent | a08d0262115dfebdd11141df8a9b4204d0456dfa (diff) | |
feat: Enhance shape retrieval with bus and stop point indexing; update related components
Diffstat (limited to 'src/Costasdev.Busurbano.Backend')
3 files changed, 91 insertions, 16 deletions
diff --git a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs index 151baa3..1f81bf1 100644 --- a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs +++ b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs @@ -49,18 +49,60 @@ public class VigoController : ControllerBase [HttpGet("GetShape")] public async Task<IActionResult> GetShape( [FromQuery] string shapeId, - [FromQuery] int startPointIndex = 0 + [FromQuery] int? startPointIndex = null, + [FromQuery] double? busLat = null, + [FromQuery] double? busLon = null, + [FromQuery] int? busShapeIndex = null, + [FromQuery] double? stopLat = null, + [FromQuery] double? stopLon = null, + [FromQuery] int? stopShapeIndex = null ) { - // Include a significant number of previous points to ensure continuity and context - // Backtrack 15 points to cover any potential gaps or dense point sequences - var adjustedStartIndex = Math.Max(0, startPointIndex - 15); - var path = await _shapeService.GetShapePathAsync(shapeId, adjustedStartIndex); + var path = await _shapeService.GetShapePathAsync(shapeId, 0); if (path == null) { return NotFound(); } + // Determine bus point + object? busPoint = null; + if (busShapeIndex.HasValue && busShapeIndex.Value >= 0 && busShapeIndex.Value < path.Count) + { + var p = path[busShapeIndex.Value]; + busPoint = new { lat = p.Latitude, lon = p.Longitude, index = busShapeIndex.Value }; + } + else if (busLat.HasValue && busLon.HasValue) + { + var idx = await _shapeService.FindClosestPointIndexAsync(shapeId, busLat.Value, busLon.Value); + if (idx.HasValue && idx.Value >= 0 && idx.Value < path.Count) + { + var p = path[idx.Value]; + busPoint = new { lat = p.Latitude, lon = p.Longitude, index = idx.Value }; + } + } + else if (startPointIndex.HasValue && startPointIndex.Value >= 0 && startPointIndex.Value < path.Count) + { + var p = path[startPointIndex.Value]; + busPoint = new { lat = p.Latitude, lon = p.Longitude, index = startPointIndex.Value }; + } + + // Determine stop point + object? stopPoint = null; + if (stopShapeIndex.HasValue && stopShapeIndex.Value >= 0 && stopShapeIndex.Value < path.Count) + { + var p = path[stopShapeIndex.Value]; + stopPoint = new { lat = p.Latitude, lon = p.Longitude, index = stopShapeIndex.Value }; + } + else if (stopLat.HasValue && stopLon.HasValue) + { + var idx = await _shapeService.FindClosestPointIndexAsync(shapeId, stopLat.Value, stopLon.Value); + if (idx.HasValue && idx.Value >= 0 && idx.Value < path.Count) + { + var p = path[idx.Value]; + stopPoint = new { lat = p.Latitude, lon = p.Longitude, index = idx.Value }; + } + } + // Convert to GeoJSON LineString var coordinates = path.Select(p => new[] { p.Longitude, p.Latitude }).ToList(); @@ -72,7 +114,11 @@ public class VigoController : ControllerBase type = "LineString", coordinates = coordinates }, - properties = new { } + properties = new + { + busPoint, + stopPoint + } }; return Ok(geoJson); @@ -214,7 +260,7 @@ public class VigoController : ControllerBase // 2) From the valid trips, pick the one with smallest Abs(TimeDiff). // This handles "as late as it gets" (large negative TimeDiff) by preferring smaller delays if available, // but accepting large delays if that's the only option (and better than an invalid early trip). - const int maxEarlyArrivalMinutes = 3; + const int maxEarlyArrivalMinutes = 5; var bestMatch = possibleCirculations .Select(c => new @@ -259,6 +305,7 @@ public class VigoController : ControllerBase var isRunning = closestCirculation.StartingDateTime()!.Value <= now; Position? currentPosition = null; + int? stopShapeIndex = null; // Calculate bus position only for realtime trips that have already departed if (isRunning && !string.IsNullOrEmpty(closestCirculation.ShapeId)) @@ -266,7 +313,9 @@ public class VigoController : ControllerBase var shape = await _shapeService.LoadShapeAsync(closestCirculation.ShapeId); if (shape != null && stopLocation != null) { - currentPosition = _shapeService.GetBusPosition(shape, stopLocation, estimate.Meters); + var result = _shapeService.GetBusPosition(shape, stopLocation, estimate.Meters); + currentPosition = result.BusPosition; + stopShapeIndex = result.StopIndex; } } @@ -288,7 +337,8 @@ public class VigoController : ControllerBase Minutes = estimate.Minutes, Distance = estimate.Meters }, - CurrentPosition = currentPosition + CurrentPosition = currentPosition, + StopShapeIndex = stopShapeIndex }); usedTripIds.Add(closestCirculation.TripId); @@ -310,6 +360,12 @@ public class VigoController : ControllerBase continue; // already represented via a matched realtime } + var minutes = (int)(sched.CallingDateTime()!.Value - now).TotalMinutes; + if (minutes == 0) + { + continue; + } + consolidatedCirculations.Add(new ConsolidatedCirculation { Line = sched.Line, @@ -317,7 +373,7 @@ public class VigoController : ControllerBase Schedule = new ScheduleData { Running = sched.StartingDateTime()!.Value <= now, - Minutes = (int)(sched.CallingDateTime()!.Value - now).TotalMinutes, + Minutes = minutes, TripId = sched.TripId, ServiceId = sched.ServiceId, ShapeId = sched.ShapeId, diff --git a/src/Costasdev.Busurbano.Backend/Services/ShapeTraversalService.cs b/src/Costasdev.Busurbano.Backend/Services/ShapeTraversalService.cs index 37b76ee..7263ad0 100644 --- a/src/Costasdev.Busurbano.Backend/Services/ShapeTraversalService.cs +++ b/src/Costasdev.Busurbano.Backend/Services/ShapeTraversalService.cs @@ -68,27 +68,45 @@ public class ShapeTraversalService var result = new List<Position>(); // 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++) { - result.Add(TransformToLatLng(shape.Points[i])); + var pos = TransformToLatLng(shape.Points[i]); + pos.ShapeIndex = i; + result.Add(pos); } return result; } + public async Task<int?> 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); + } + /// <summary> /// Calculates the bus position by reverse-traversing the shape from the stop location /// </summary> /// <param name="shape">The shape points (in EPSG:25829 meters)</param> /// <param name="stopLocation">The stop location (in EPSG:25829 meters)</param> /// <param name="distanceMeters">Distance in meters from the stop to traverse backwards</param> - /// <returns>The lat/lng position of the bus, or null if not calculable</returns> - public Position? GetBusPosition(Shape shape, Epsg25829 stopLocation, int distanceMeters) + /// <returns>The lat/lng position of the bus and the stop index on the shape</returns> + public (Position? BusPosition, int StopIndex) GetBusPosition(Shape shape, Epsg25829 stopLocation, int distanceMeters) { if (shape.Points.Count == 0 || distanceMeters <= 0) { - return null; + return (null, -1); } // Find the closest point on the shape to the stop @@ -99,7 +117,7 @@ public class ShapeTraversalService if (busPoint == null) { - return null; + return (null, closestPointIndex); } var forwardPoint = shape.Points[forwardIndex]; @@ -114,7 +132,7 @@ public class ShapeTraversalService var pos = TransformToLatLng(busPoint); pos.OrientationDegrees = (int)Math.Round(bearing); pos.ShapeIndex = forwardIndex; - return pos; + return (pos, closestPointIndex); } /// <summary> diff --git a/src/Costasdev.Busurbano.Backend/Types/ConsolidatedCirculation.cs b/src/Costasdev.Busurbano.Backend/Types/ConsolidatedCirculation.cs index 2ac3206..ff6dbde 100644 --- a/src/Costasdev.Busurbano.Backend/Types/ConsolidatedCirculation.cs +++ b/src/Costasdev.Busurbano.Backend/Types/ConsolidatedCirculation.cs @@ -8,6 +8,7 @@ public class ConsolidatedCirculation public ScheduleData? Schedule { get; set; } public RealTimeData? RealTime { get; set; } public Position? CurrentPosition { get; set; } + public int? StopShapeIndex { get; set; } public string[] NextStreets { get; set; } = []; } |
