From b96273b54a9b47c79e0afe40a918f751e82097ae Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 18:02:36 +0100 Subject: Link previous trip shapes for GPS positioning on circular routes (#111) Co-authored-by: Ariel Costas Guerrero Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: arielcostas <94913521+arielcostas@users.noreply.github.com> --- .../Controllers/VigoController.cs | 56 +++++++++++++++++----- 1 file changed, 45 insertions(+), 11 deletions(-) (limited to 'src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs') diff --git a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs index 1f81bf1..91ccdab 100644 --- a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs +++ b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs @@ -254,13 +254,13 @@ public class VigoController : ControllerBase ScheduledArrival? closestCirculation = null; // Matching strategy: - // 1) Filter trips that are not "too early" (TimeDiff <= 3). + // 1) Filter trips that are not "too early" (TimeDiff <= 7). // TimeDiff = Schedule - Realtime. - // If TimeDiff > 3, bus is > 3 mins early. Reject. + // If TimeDiff > 7, bus is > 7 mins early. Reject. // 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 = 5; + const int maxEarlyArrivalMinutes = 7; var bestMatch = possibleCirculations .Select(c => new @@ -280,7 +280,7 @@ public class VigoController : ControllerBase if (closestCirculation == null) { // No scheduled match: include realtime-only entry - _logger.LogWarning("No schedule match for realtime line {Line} towards {Route} in {Minutes} minutes", estimate.Line, estimate.Route, estimate.Minutes); + _logger.LogWarning("No schedule match for realtime line {Line} towards {Route} in {Minutes} minutes (tried matching {NormalizedRoute})", estimate.Line, estimate.Route, estimate.Minutes, NormalizeRouteName(estimate.Route)); consolidatedCirculations.Add(new ConsolidatedCirculation { Line = estimate.Line, @@ -307,15 +307,41 @@ public class VigoController : ControllerBase Position? currentPosition = null; int? stopShapeIndex = null; - // Calculate bus position only for realtime trips that have already departed - if (isRunning && !string.IsNullOrEmpty(closestCirculation.ShapeId)) + // Calculate bus position for realtime trips + if (!string.IsNullOrEmpty(closestCirculation.ShapeId)) { - var shape = await _shapeService.LoadShapeAsync(closestCirculation.ShapeId); - if (shape != null && stopLocation != null) + // Check if we are likely on the previous trip + // If the bus is further away than the distance from the start of the trip to the stop, + // it implies the bus is on the previous trip (or earlier). + double distOnPrevTrip = estimate.Meters - closestCirculation.ShapeDistTraveled; + bool usePreviousShape = !isRunning && + !string.IsNullOrEmpty(closestCirculation.PreviousTripShapeId) && + distOnPrevTrip > 0; + + if (usePreviousShape) { - var result = _shapeService.GetBusPosition(shape, stopLocation, estimate.Meters); - currentPosition = result.BusPosition; - stopShapeIndex = result.StopIndex; + var prevShape = await _shapeService.LoadShapeAsync(closestCirculation.PreviousTripShapeId); + if (prevShape != null && prevShape.Points.Count > 0) + { + // The bus is on the previous trip. + // We treat the end of the previous shape as the "stop" for the purpose of calculation. + // The distance to traverse backwards from the end of the previous shape is 'distOnPrevTrip'. + var lastPoint = prevShape.Points[prevShape.Points.Count - 1]; + var result = _shapeService.GetBusPosition(prevShape, lastPoint, (int)distOnPrevTrip); + currentPosition = result.BusPosition; + stopShapeIndex = result.StopIndex; + } + } + else + { + // Normal case: bus is on the current trip shape + var shape = await _shapeService.LoadShapeAsync(closestCirculation.ShapeId); + if (shape != null && stopLocation != null) + { + var result = _shapeService.GetBusPosition(shape, stopLocation, estimate.Meters); + currentPosition = result.BusPosition; + stopShapeIndex = result.StopIndex; + } } } @@ -423,6 +449,7 @@ public class VigoController : ControllerBase var normalized = route.Trim().ToLowerInvariant(); // Remove diacritics/accents first, then filter to alphanumeric normalized = RemoveDiacritics(normalized); + normalized = RenameCustom(normalized); return new string(normalized.Where(char.IsLetterOrDigit).ToArray()); } @@ -442,6 +469,13 @@ public class VigoController : ControllerBase return stringBuilder.ToString().Normalize(NormalizationForm.FormC); } + + private static string RenameCustom(string text) + { + // Custom replacements for known problematic route names + return text + .Replace("praza", "p"); + } } public static class StopScheduleExtensions -- cgit v1.3