aboutsummaryrefslogtreecommitdiff
path: root/src/Enmarcha.Backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/Enmarcha.Backend')
-rw-r--r--src/Enmarcha.Backend/Controllers/ArrivalsController.cs2
-rw-r--r--src/Enmarcha.Backend/Services/OtpService.cs18
-rw-r--r--src/Enmarcha.Backend/Services/Processors/FeedConfigProcessor.cs18
-rw-r--r--src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs65
-rw-r--r--src/Enmarcha.Backend/Types/Geoapify/GeoapifyModels.cs4
5 files changed, 83 insertions, 24 deletions
diff --git a/src/Enmarcha.Backend/Controllers/ArrivalsController.cs b/src/Enmarcha.Backend/Controllers/ArrivalsController.cs
index 5c64efa..13fb430 100644
--- a/src/Enmarcha.Backend/Controllers/ArrivalsController.cs
+++ b/src/Enmarcha.Backend/Controllers/ArrivalsController.cs
@@ -117,7 +117,7 @@ public partial class ArrivalsController : ControllerBase
},
Headsign = new HeadsignInfo
{
- Destination = item.Headsign
+ Destination = item.Trip.TripHeadsign ?? item.Headsign,
},
Estimate = new ArrivalDetails
{
diff --git a/src/Enmarcha.Backend/Services/OtpService.cs b/src/Enmarcha.Backend/Services/OtpService.cs
index 07c4d81..a01079f 100644
--- a/src/Enmarcha.Backend/Services/OtpService.cs
+++ b/src/Enmarcha.Backend/Services/OtpService.cs
@@ -303,6 +303,8 @@ public class OtpService
var shortName = _feedService.NormalizeRouteShortName(feedId, leg.Route?.ShortName ?? string.Empty);
var headsign = leg.Headsign;
+ var headsignTrimmed = headsign?.Trim();
+
if (feedId == "vitrasa")
{
headsign = headsign?.Replace("*", "");
@@ -313,9 +315,21 @@ public class OtpService
switch (shortName)
{
- case "A" when headsign != null && headsign.StartsWith("\"1\""):
+ case "A" when headsignTrimmed != null &&
+ (headsignTrimmed.StartsWith("\"1\"", StringComparison.Ordinal) ||
+ (headsignTrimmed.Length >= 1 && headsignTrimmed[0] == '1' &&
+ (headsignTrimmed.Length == 1 || !char.IsDigit(headsignTrimmed[1])))):
shortName = "A1";
- headsign = headsign.Replace("\"1\"", "");
+ if (headsignTrimmed.StartsWith("\"1\"", StringComparison.Ordinal))
+ {
+ headsign = headsignTrimmed[3..];
+ }
+ else
+ {
+ headsign = headsignTrimmed[1..];
+ }
+
+ headsign = headsign.TrimStart(' ', '-', '.', ':');
break;
case "6":
headsign = headsign?.Replace("\"", "");
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)
diff --git a/src/Enmarcha.Backend/Types/Geoapify/GeoapifyModels.cs b/src/Enmarcha.Backend/Types/Geoapify/GeoapifyModels.cs
index ac692ca..a15e54e 100644
--- a/src/Enmarcha.Backend/Types/Geoapify/GeoapifyModels.cs
+++ b/src/Enmarcha.Backend/Types/Geoapify/GeoapifyModels.cs
@@ -1,7 +1,7 @@
-using System.Text.Json.Serialization;
-
namespace Enmarcha.Backend.Types.Geoapify;
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
+
public class GeoapifyResult
{
public Result[] results { get; set; }