aboutsummaryrefslogtreecommitdiff
path: root/src/Enmarcha.Backend/Services/Processors
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2026-02-11 17:56:31 +0100
committerAriel Costas Guerrero <ariel@costas.dev>2026-02-11 17:56:37 +0100
commit06c873557d7842b015a4eb3e7ed02e27a6177824 (patch)
tree93e96741908ebf3a7e737da510b2bf59c251928a /src/Enmarcha.Backend/Services/Processors
parentf977f0e3ec27cf0b26e4c96bebdfec2073c59c5d (diff)
fix: Normalize headsign handling and improve destination trimming in arrival processing
Diffstat (limited to 'src/Enmarcha.Backend/Services/Processors')
-rw-r--r--src/Enmarcha.Backend/Services/Processors/FeedConfigProcessor.cs18
-rw-r--r--src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs65
2 files changed, 64 insertions, 19 deletions
diff --git a/src/Enmarcha.Backend/Services/Processors/FeedConfigProcessor.cs b/src/Enmarcha.Backend/Services/Processors/FeedConfigProcessor.cs
index 2d5f5d9..562b8f1 100644
--- a/src/Enmarcha.Backend/Services/Processors/FeedConfigProcessor.cs
+++ b/src/Enmarcha.Backend/Services/Processors/FeedConfigProcessor.cs
@@ -47,6 +47,8 @@ public class FeedConfigProcessor : IArrivalsProcessor
{
arrival.Headsign.Destination = arrival.Headsign.Destination.Replace("*", "");
+ var destinationTrimmed = arrival.Headsign.Destination.TrimStart();
+
if (arrival.Headsign.Destination == "FORA DE SERVIZO.G.B.")
{
arrival.Headsign.Destination = "García Barbón, 7 (fora de servizo)";
@@ -55,9 +57,21 @@ public class FeedConfigProcessor : IArrivalsProcessor
switch (arrival.Route.ShortName)
{
- case "A" when arrival.Headsign.Destination.StartsWith("\"1\""):
+ case "A" when destinationTrimmed.StartsWith("\"1\"", StringComparison.Ordinal) ||
+ (destinationTrimmed.Length >= 1 && destinationTrimmed[0] == '1' &&
+ (destinationTrimmed.Length == 1 || !char.IsDigit(destinationTrimmed[1]))):
arrival.Route.ShortName = "A1";
- arrival.Headsign.Destination = arrival.Headsign.Destination.Replace("\"1\"", "");
+ // NormalizeStopName() removes quotes for Vitrasa, so handle both "\"1\"" and leading "1".
+ if (destinationTrimmed.StartsWith("\"1\"", StringComparison.Ordinal))
+ {
+ destinationTrimmed = destinationTrimmed.Substring(3);
+ }
+ else
+ {
+ destinationTrimmed = destinationTrimmed.Substring(1);
+ }
+
+ arrival.Headsign.Destination = destinationTrimmed.TrimStart(' ', '-', '.', ':');
break;
case "6":
arrival.Headsign.Destination = arrival.Headsign.Destination.Replace("\"", "");
diff --git a/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs b/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs
index 0ce54c2..d1d1e7d 100644
--- a/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs
+++ b/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs
@@ -66,7 +66,13 @@ public class VitrasaRealTimeProcessor : AbstractRealTimeProcessor
.Where(a => a.Route.ShortName.Trim() == estimate.Line.Trim())
.Select(a =>
{
- var arrivalRouteNormalized = _feedService.NormalizeRouteNameForMatching(a.Headsign.Destination);
+ // Use tripHeadsign from GTFS if available, otherwise fall back to stop-level headsign
+ string scheduleHeadsign = a.Headsign.Destination;
+ if (a.RawOtpTrip is ArrivalsAtStopResponse.Arrival otpArr && !string.IsNullOrWhiteSpace(otpArr.Trip.TripHeadsign))
+ {
+ scheduleHeadsign = otpArr.Trip.TripHeadsign;
+ }
+ var arrivalRouteNormalized = _feedService.NormalizeRouteNameForMatching(scheduleHeadsign);
string? arrivalLongNameNormalized = null;
string? arrivalLastStopNormalized = null;
@@ -122,10 +128,38 @@ public class VitrasaRealTimeProcessor : AbstractRealTimeProcessor
var delayMinutes = estimate.Minutes - scheduledMinutes;
arrival.Delay = new DelayBadge { Minutes = delayMinutes };
- // Prefer real-time headsign if available and different
+ string scheduledHeadsign = arrival.Headsign.Destination;
+ if (arrival.RawOtpTrip is ArrivalsAtStopResponse.Arrival otpArr && !string.IsNullOrWhiteSpace(otpArr.Trip.TripHeadsign))
+ {
+ scheduledHeadsign = otpArr.Trip.TripHeadsign;
+ }
+
+ _logger.LogDebug("Matched RT estimate: Line {Line}, RT: {RTRoute} ({RTMin}m), Scheduled: {ScheduledRoute} ({ScheduledMin}m), Delay: {Delay}m",
+ estimate.Line, estimate.Route, estimate.Minutes, scheduledHeadsign, scheduledMinutes, delayMinutes);
+
+ // Prefer real-time headsign UNLESS it's just the last stop name (which is less informative)
if (!string.IsNullOrWhiteSpace(estimate.Route))
{
- arrival.Headsign.Destination = estimate.Route;
+ bool isJustLastStop = false;
+
+ if (arrival.RawOtpTrip is ArrivalsAtStopResponse.Arrival otpArrival)
+ {
+ var lastStop = otpArrival.Trip.Stoptimes.LastOrDefault();
+ if (lastStop != null)
+ {
+ var arrivalLastStopNormalized = _feedService.NormalizeRouteNameForMatching(lastStop.Stop.Name);
+ isJustLastStop = estimateRouteNormalized == arrivalLastStopNormalized;
+ }
+ }
+
+ _logger.LogDebug("Headsign: RT='{RT}' vs Scheduled='{Scheduled}', IsJustLastStop={Last}, WillUseRT={Use}",
+ estimate.Route, scheduledHeadsign, isJustLastStop, !isJustLastStop);
+
+ // Use real-time headsign unless it's just the final stop name
+ if (!isJustLastStop)
+ {
+ arrival.Headsign.Destination = estimate.Route;
+ }
}
// Calculate position
@@ -150,25 +184,22 @@ public class VitrasaRealTimeProcessor : AbstractRealTimeProcessor
currentPosition = result.BusPosition;
stopShapeIndex = result.StopIndex;
- if (currentPosition != null)
- {
- _logger.LogInformation("Calculated position from OTP geometry for trip {TripId}: {Lat}, {Lon}", arrival.TripId, currentPosition.Latitude, currentPosition.Longitude);
- }
-
// Populate Shape GeoJSON
if (!context.IsReduced && currentPosition != null)
{
- var features = new List<object>();
- features.Add(new
+ var features = new List<object>
{
- type = "Feature",
- geometry = new
+ new
{
- type = "LineString",
- coordinates = decodedPoints.Select(p => new[] { p.Longitude, p.Latitude }).ToList()
- },
- properties = new { type = "route" }
- });
+ type = "Feature",
+ geometry = new
+ {
+ type = "LineString",
+ coordinates = decodedPoints.Select(p => new[] { p.Longitude, p.Latitude }).ToList()
+ },
+ properties = new { type = "route" }
+ }
+ };
// Add stops if available
if (otpArrival.Trip.Stoptimes != null)