diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2026-03-13 16:49:10 +0100 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2026-03-13 16:49:30 +0100 |
| commit | ee69c62adc5943a1dbd154df5142c0e726bdd317 (patch) | |
| tree | 5874249173aa249d4d497733ef9fc410e64ab664 | |
| parent | 90ad5395f6310da86fee9a29503e58ea74f3078b (diff) | |
feat(routes): add realtime estimates panel with pattern-aware styling
- New GET /api/stops/estimates endpoint (nano mode: tripId, patternId, estimate, delay only)
- useStopEstimates hook wiring estimates to routes-$id stop panel
- Pattern-aware styling: dim schedules and estimates from other patterns
- Past scheduled departures shown with strikethrough instead of hidden
- Persist selected pattern in URL hash (replace navigation, no history push)
- Fix planner arrivals using new estimates endpoint
26 files changed, 377 insertions, 1921 deletions
diff --git a/src/Enmarcha.Backend/Controllers/ArrivalsController.cs b/src/Enmarcha.Backend/Controllers/ArrivalsController.cs index 50d4012..7ca3ab9 100644 --- a/src/Enmarcha.Backend/Controllers/ArrivalsController.cs +++ b/src/Enmarcha.Backend/Controllers/ArrivalsController.cs @@ -54,58 +54,126 @@ public partial class ArrivalsController : ControllerBase activity?.SetTag("stop.id", id); activity?.SetTag("reduced", reduced); + var result = await FetchAndProcessArrivalsAsync(id, reduced, nano: false); + if (result is null) return StatusCode(500, "Error fetching stop data"); + var (stop, context) = result.Value; + + var feedId = id.Split(':')[0]; + var timeThreshold = GetThresholdForFeed(id); + var (fallbackColor, fallbackTextColor) = _feedService.GetFallbackColourForFeed(feedId); + + return Ok(new StopArrivalsResponse + { + StopCode = _feedService.NormalizeStopCode(feedId, stop.Code), + StopName = _feedService.NormalizeStopName(feedId, stop.Name), + StopLocation = new Position { Latitude = stop.Lat, Longitude = stop.Lon }, + Routes = [.. _feedService.ConsolidateRoutes(feedId, + stop.Routes + .OrderBy(r => SortingHelper.GetRouteSortKey(r.ShortName, r.GtfsId)) + .Select(r => new RouteInfo + { + GtfsId = r.GtfsId, + ShortName = _feedService.NormalizeRouteShortName(feedId, r.ShortName ?? ""), + Colour = r.Color ?? fallbackColor, + TextColour = r.TextColor is null or "000000" ? + ContrastHelper.GetBestTextColour(r.Color ?? fallbackColor) : + r.TextColor + }))], + Arrivals = [.. context.Arrivals.Where(a => a.Estimate.Minutes >= timeThreshold)], + Usage = context.Usage + }); + } + + [HttpGet("estimates")] + public async Task<IActionResult> GetEstimates( + [FromQuery] string stop, + [FromQuery] string route, + [FromQuery] string? via + ) + { + if (string.IsNullOrWhiteSpace(stop) || string.IsNullOrWhiteSpace(route)) + return BadRequest("'stop' and 'route' are required."); + + using var activity = Telemetry.Source.StartActivity("GetEstimates"); + activity?.SetTag("stop.id", stop); + activity?.SetTag("route.id", route); + activity?.SetTag("via.id", via); + + var result = await FetchAndProcessArrivalsAsync(stop, reduced: false, nano: true); + if (result is null) return StatusCode(500, "Error fetching stop data"); + var (_, context) = result.Value; + + // Annotate each arrival with its OTP pattern ID + foreach (var arrival in context.Arrivals) + { + if (arrival.RawOtpTrip is ArrivalsAtStopResponse.Arrival otpArrival) + arrival.PatternId = otpArrival.Trip.Pattern?.Id; + } + + // Filter by route GTFS ID + var timeThreshold = GetThresholdForFeed(stop); + var filtered = context.Arrivals + .Where(a => a.Route.GtfsId == route && a.Estimate.Minutes >= timeThreshold) + .ToList(); + + // Optionally filter by via stop: keep only trips whose remaining stoptimes include the via stop + if (!string.IsNullOrWhiteSpace(via)) + { + filtered = filtered.Where(a => + { + if (a.RawOtpTrip is not ArrivalsAtStopResponse.Arrival otpArrival) return false; + var stoptimes = otpArrival.Trip.Stoptimes; + var originIdx = stoptimes.FindIndex(s => s.Stop.GtfsId == stop); + var searchFrom = originIdx >= 0 ? originIdx + 1 : 0; + return stoptimes.Skip(searchFrom).Any(s => s.Stop.GtfsId == via); + }).ToList(); + } + + var estimates = filtered.Select(a => new ArrivalEstimate + { + TripId = a.TripId, + PatternId = a.PatternId, + Estimate = a.Estimate, + Delay = a.Delay + }).ToList(); + + return Ok(new StopEstimatesResponse { Arrivals = estimates }); + } + + private async Task<(ArrivalsAtStopResponse.StopItem Stop, ArrivalsContext Context)?> FetchAndProcessArrivalsAsync( + string id, bool reduced, bool nano) + { var tz = TimeZoneInfo.FindSystemTimeZoneById("Europe/Madrid"); var nowLocal = TimeZoneInfo.ConvertTime(DateTime.UtcNow, tz); - var todayLocal = nowLocal.Date; - var requestContent = ArrivalsAtStopContent.Query( - new ArrivalsAtStopContent.Args(id, reduced) - ); + var requestContent = ArrivalsAtStopContent.Query(new ArrivalsAtStopContent.Args(id, reduced || nano)); var request = new HttpRequestMessage(HttpMethod.Post, $"{_config.OpenTripPlannerBaseUrl}/gtfs/v1"); - request.Content = JsonContent.Create(new GraphClientRequest - { - Query = requestContent - }); + request.Content = JsonContent.Create(new GraphClientRequest { Query = requestContent }); var response = await _httpClient.SendAsync(request); var responseBody = await response.Content.ReadFromJsonAsync<GraphClientResponse<ArrivalsAtStopResponse>>(); if (responseBody is not { IsSuccess: true } || responseBody.Data?.Stop == null) { - activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, "Error fetching stop data from OTP"); LogErrorFetchingStopData(response.StatusCode, await response.Content.ReadAsStringAsync()); - return StatusCode(500, "Error fetching stop data"); + return null; } var stop = responseBody.Data.Stop; _logger.LogInformation("Fetched {Count} arrivals for stop {StopName} ({StopId})", stop.Arrivals.Count, stop.Name, id); - activity?.SetTag("arrivals.count", stop.Arrivals.Count); List<Arrival> arrivals = []; foreach (var item in stop.Arrivals) { - // Discard trip without pickup at stop - if (item.PickupTypeParsed.Equals(ArrivalsAtStopResponse.PickupType.None)) - { - continue; - } + if (item.PickupTypeParsed.Equals(ArrivalsAtStopResponse.PickupType.None)) continue; + if (item.Trip.ArrivalStoptime.Stop.GtfsId == id) continue; - // Discard on last stop - if (item.Trip.ArrivalStoptime.Stop.GtfsId == id) - { - continue; - } - - // Calculate departure time using the service day in the feed's timezone (Europe/Madrid) - // This ensures we treat ScheduledDepartureSeconds as relative to the local midnight of the service day var serviceDayLocal = TimeZoneInfo.ConvertTime(DateTimeOffset.FromUnixTimeSeconds(item.ServiceDay), tz); var departureTime = serviceDayLocal.Date.AddSeconds(item.ScheduledDepartureSeconds); - var minutesToArrive = (int)(departureTime - nowLocal).TotalMinutes; - //var isRunning = departureTime < nowLocal; - Arrival arrival = new() + arrivals.Add(new Arrival { TripId = item.Trip.GtfsId, Route = new RouteInfo @@ -115,19 +183,14 @@ public partial class ArrivalsController : ControllerBase Colour = item.Trip.Route.Color ?? "FFFFFF", TextColour = item.Trip.Route.TextColor ?? "000000" }, - Headsign = new HeadsignInfo - { - Destination = item.Trip.TripHeadsign ?? item.Headsign, - }, + Headsign = new HeadsignInfo { Destination = item.Trip.TripHeadsign ?? item.Headsign }, Estimate = new ArrivalDetails { Minutes = minutesToArrive, Precision = departureTime < nowLocal.AddMinutes(-1) ? ArrivalPrecision.Past : ArrivalPrecision.Scheduled }, RawOtpTrip = item - }; - - arrivals.Add(arrival); + }); } var context = new ArrivalsContext @@ -135,6 +198,7 @@ public partial class ArrivalsController : ControllerBase StopId = id, StopCode = stop.Code, IsReduced = reduced, + IsNano = nano, Arrivals = arrivals, NowLocal = nowLocal, StopLocation = new Position { Latitude = stop.Lat, Longitude = stop.Lon } @@ -142,38 +206,7 @@ public partial class ArrivalsController : ControllerBase await _pipeline.ExecuteAsync(context); - var feedId = id.Split(':')[0]; - - // Time after an arrival's time to still include it in the response. This is useful without real-time data, for delayed buses. - var timeThreshold = GetThresholdForFeed(id); - - var (fallbackColor, fallbackTextColor) = _feedService.GetFallbackColourForFeed(feedId); - - return Ok(new StopArrivalsResponse - { - StopCode = _feedService.NormalizeStopCode(feedId, stop.Code), - StopName = _feedService.NormalizeStopName(feedId, stop.Name), - StopLocation = new Position - { - Latitude = stop.Lat, - Longitude = stop.Lon - }, - Routes = [.. _feedService.ConsolidateRoutes(feedId, - stop.Routes - .OrderBy(r => SortingHelper.GetRouteSortKey(r.ShortName, r.GtfsId)) - .Select(r => new RouteInfo - { - GtfsId = r.GtfsId, - ShortName = _feedService.NormalizeRouteShortName(feedId, r.ShortName ?? ""), - Colour = r.Color ?? fallbackColor, - TextColour = r.TextColor is null or "000000" ? - ContrastHelper.GetBestTextColour(r.Color ?? fallbackColor) : - r.TextColor - }))], - Arrivals = [.. arrivals.Where(a => a.Estimate.Minutes >= timeThreshold)], - Usage = context.Usage - }); - + return (stop, context); } private static int GetThresholdForFeed(string id) diff --git a/src/Enmarcha.Backend/Controllers/TileController.cs b/src/Enmarcha.Backend/Controllers/TileController.cs index ef71b67..300f9db 100644 --- a/src/Enmarcha.Backend/Controllers/TileController.cs +++ b/src/Enmarcha.Backend/Controllers/TileController.cs @@ -134,7 +134,7 @@ public class TileController : ControllerBase features.Add(feature); }); - foreach (var feature in features.OrderBy(f => f.Attributes["transitKind"] as string switch + foreach (var feature in features.OrderBy(f => (f.Attributes["transitKind"] as string) switch { "bus" => 3, "train" => 2, diff --git a/src/Enmarcha.Backend/Extensions/StopScheduleExtensions.cs b/src/Enmarcha.Backend/Extensions/StopScheduleExtensions.cs deleted file mode 100644 index 1ef7990..0000000 --- a/src/Enmarcha.Backend/Extensions/StopScheduleExtensions.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Enmarcha.Backend.Types; - -namespace Enmarcha.Backend.Extensions; - -public static class StopScheduleExtensions -{ - public static DateTime? StartingDateTime(this StopArrivals.Types.ScheduledArrival stop, DateTime baseDate) - { - return ParseGtfsTime(stop.StartingTime, baseDate); - } - - public static DateTime? CallingDateTime(this StopArrivals.Types.ScheduledArrival stop, DateTime baseDate) - { - return ParseGtfsTime(stop.CallingTime, baseDate); - } - - /// <summary> - /// Parse GTFS time format (HH:MM:SS) which can have hours >= 24 for services past midnight - /// </summary> - private static DateTime? ParseGtfsTime(string timeStr, DateTime baseDate) - { - if (string.IsNullOrWhiteSpace(timeStr)) - { - return null; - } - - var parts = timeStr.Split(':'); - if (parts.Length != 3) - { - return null; - } - - if (!int.TryParse(parts[0], out var hours) || - !int.TryParse(parts[1], out var minutes) || - !int.TryParse(parts[2], out var seconds)) - { - return null; - } - - // Handle GTFS times that exceed 24 hours (e.g., 25:30:00 for 1:30 AM next day) - var days = hours / 24; - var normalizedHours = hours % 24; - - try - { - var dt = baseDate - .AddDays(days) - .AddHours(normalizedHours) - .AddMinutes(minutes) - .AddSeconds(seconds); - return dt.AddSeconds(60 - dt.Second); - } - catch - { - return null; - } - } -} diff --git a/src/Enmarcha.Backend/Services/ArrivalsPipeline.cs b/src/Enmarcha.Backend/Services/ArrivalsPipeline.cs index 6d8c2c0..9e44535 100644 --- a/src/Enmarcha.Backend/Services/ArrivalsPipeline.cs +++ b/src/Enmarcha.Backend/Services/ArrivalsPipeline.cs @@ -20,6 +20,12 @@ public class ArrivalsContext /// </summary> public bool IsReduced { get; set; } + /// <summary> + /// Nano mode: skip all enrichment except real-time estimates. + /// Processors that populate shapes, marquee, next stops, and usage should no-op when true. + /// </summary> + public bool IsNano { get; set; } + public Position? StopLocation { get; set; } public required List<Arrival> Arrivals { get; set; } diff --git a/src/Enmarcha.Backend/Services/Processors/CorunaRealTimeProcessor.cs b/src/Enmarcha.Backend/Services/Processors/CorunaRealTimeProcessor.cs index b933bf4..797221f 100644 --- a/src/Enmarcha.Backend/Services/Processors/CorunaRealTimeProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/CorunaRealTimeProcessor.cs @@ -95,7 +95,6 @@ public class CorunaRealTimeProcessor : AbstractRealTimeProcessor if (stopLocation != null) { Position? currentPosition = null; - int? stopShapeIndex = null; if (arrival.RawOtpTrip is ArrivalsAtStopResponse.Arrival otpArrival && otpArrival.Trip.Geometry?.Points != null) @@ -111,7 +110,6 @@ public class CorunaRealTimeProcessor : AbstractRealTimeProcessor var result = _shapeService.GetBusPosition(shape, stopLocation, meters); currentPosition = result.BusPosition; - stopShapeIndex = result.StopIndex; if (currentPosition != null) { @@ -168,7 +166,6 @@ public class CorunaRealTimeProcessor : AbstractRealTimeProcessor if (currentPosition != null) { arrival.CurrentPosition = currentPosition; - arrival.StopShapeIndex = stopShapeIndex; } } diff --git a/src/Enmarcha.Backend/Services/Processors/CtagShuttleRealTimeProcessor.cs b/src/Enmarcha.Backend/Services/Processors/CtagShuttleRealTimeProcessor.cs index 316591d..8f3e6db 100644 --- a/src/Enmarcha.Backend/Services/Processors/CtagShuttleRealTimeProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/CtagShuttleRealTimeProcessor.cs @@ -213,7 +213,6 @@ public class CtagShuttleRealTimeProcessor : AbstractRealTimeProcessor } activeArrival.CurrentPosition = shuttleWgs84; - activeArrival.StopShapeIndex = stopPointIndex; _logger.LogInformation( "Updated active trip {TripId}: {Minutes} min (was {Scheduled} min, delay: {Delay} min, distance: {Distance:F1}m)", diff --git a/src/Enmarcha.Backend/Services/Processors/FilterAndSortProcessor.cs b/src/Enmarcha.Backend/Services/Processors/FilterAndSortProcessor.cs index 18ba2ac..cbcda93 100644 --- a/src/Enmarcha.Backend/Services/Processors/FilterAndSortProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/FilterAndSortProcessor.cs @@ -29,8 +29,8 @@ public class FilterAndSortProcessor : IArrivalsProcessor return a.Estimate.Minutes >= -10; }).ToList(); - // 3. Limit results - var limit = context.IsReduced ? 4 : 10; + // 3. Limit results — nano requests a full set so route/via filtering in the controller doesn't starve + var limit = context.IsNano ? 100 : context.IsReduced ? 4 : 10; var limited = filtered.Take(limit).ToList(); // Update the context list in-place diff --git a/src/Enmarcha.Backend/Services/Processors/MarqueeProcessor.cs b/src/Enmarcha.Backend/Services/Processors/MarqueeProcessor.cs index 9e620c2..d5ff58e 100644 --- a/src/Enmarcha.Backend/Services/Processors/MarqueeProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/MarqueeProcessor.cs @@ -11,6 +11,8 @@ public class MarqueeProcessor : IArrivalsProcessor public Task ProcessAsync(ArrivalsContext context) { + if (context.IsNano) return Task.CompletedTask; + var feedId = context.StopId.Split(':')[0]; foreach (var arrival in context.Arrivals) diff --git a/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs b/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs index 5d02066..5bdb5bb 100644 --- a/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs @@ -13,6 +13,8 @@ public class NextStopsProcessor : IArrivalsProcessor public Task ProcessAsync(ArrivalsContext context) { + if (context.IsNano) return Task.CompletedTask; + var feedId = context.StopId.Split(':')[0]; foreach (var arrival in context.Arrivals) diff --git a/src/Enmarcha.Backend/Services/Processors/ShapeProcessor.cs b/src/Enmarcha.Backend/Services/Processors/ShapeProcessor.cs index f3af3a5..76f896f 100644 --- a/src/Enmarcha.Backend/Services/Processors/ShapeProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/ShapeProcessor.cs @@ -13,7 +13,7 @@ public class ShapeProcessor : IArrivalsProcessor public Task ProcessAsync(ArrivalsContext context) { - if (context.IsReduced) + if (context.IsReduced || context.IsNano) { return Task.CompletedTask; } diff --git a/src/Enmarcha.Backend/Services/Processors/VigoUsageProcessor.cs b/src/Enmarcha.Backend/Services/Processors/VigoUsageProcessor.cs index 487d55a..52218d9 100644 --- a/src/Enmarcha.Backend/Services/Processors/VigoUsageProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/VigoUsageProcessor.cs @@ -25,7 +25,7 @@ public class VigoUsageProcessor : IArrivalsProcessor public async Task ProcessAsync(ArrivalsContext context) { - if (!context.StopId.StartsWith("vitrasa:") || context.IsReduced) return; + if (!context.StopId.StartsWith("vitrasa:") || context.IsReduced || context.IsNano) return; var normalizedCode = _feedService.NormalizeStopCode("vitrasa", context.StopCode); diff --git a/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs b/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs index e374f24..43152fd 100644 --- a/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/VitrasaRealTimeProcessor.cs @@ -160,7 +160,6 @@ public class VitrasaRealTimeProcessor : AbstractRealTimeProcessor if (stopLocation != null) { Position? currentPosition = null; - int? stopShapeIndex = null; if (arrival.RawOtpTrip is ArrivalsAtStopResponse.Arrival otpArrival && otpArrival.Trip.Geometry?.Points != null) @@ -176,7 +175,6 @@ public class VitrasaRealTimeProcessor : AbstractRealTimeProcessor var result = _shapeService.GetBusPosition(shape, stopLocation, meters); currentPosition = result.BusPosition; - stopShapeIndex = result.StopIndex; // Populate Shape GeoJSON if (!context.IsReduced && currentPosition != null) @@ -228,7 +226,6 @@ public class VitrasaRealTimeProcessor : AbstractRealTimeProcessor if (currentPosition != null) { arrival.CurrentPosition = currentPosition; - arrival.StopShapeIndex = stopShapeIndex; } } diff --git a/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs b/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs index eab463d..9789d43 100644 --- a/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs +++ b/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs @@ -20,11 +20,12 @@ public class Arrival [JsonPropertyName("currentPosition")] public Position? CurrentPosition { get; set; } - [JsonPropertyName("stopShapeIndex")] public int? StopShapeIndex { get; set; } [JsonPropertyName("vehicleInformation")] public VehicleBadge? VehicleInformation { get; set; } + [JsonPropertyName("patternId")] public string? PatternId { get; set; } + [System.Text.Json.Serialization.JsonIgnore] public List<string> NextStops { get; set; } = []; diff --git a/src/Enmarcha.Backend/Types/Arrivals/StopEstimatesResponse.cs b/src/Enmarcha.Backend/Types/Arrivals/StopEstimatesResponse.cs new file mode 100644 index 0000000..6d83587 --- /dev/null +++ b/src/Enmarcha.Backend/Types/Arrivals/StopEstimatesResponse.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace Enmarcha.Backend.Types.Arrivals; + +public class StopEstimatesResponse +{ + [JsonPropertyName("arrivals")] public List<ArrivalEstimate> Arrivals { get; set; } = []; +} + +public class ArrivalEstimate +{ + [JsonPropertyName("tripId")] public required string TripId { get; set; } + + [JsonPropertyName("patternId")] public string? PatternId { get; set; } + + [JsonPropertyName("estimate")] public required ArrivalDetails Estimate { get; set; } + + [JsonPropertyName("delay")] public DelayBadge? Delay { get; set; } +} diff --git a/src/Enmarcha.Backend/Types/ConsolidatedCirculation.cs b/src/Enmarcha.Backend/Types/ConsolidatedCirculation.cs index 55d6e87..bb6a251 100644 --- a/src/Enmarcha.Backend/Types/ConsolidatedCirculation.cs +++ b/src/Enmarcha.Backend/Types/ConsolidatedCirculation.cs @@ -36,3 +36,15 @@ public class Position public int OrientationDegrees { get; set; } public int ShapeIndex { get; set; } } + +public class Epsg25829 +{ + public double X { get; set; } + public double Y { get; set; } +} + +public class Shape +{ + public string ShapeId { get; set; } + public List<Epsg25829> Points { get; set; } +} diff --git a/src/Enmarcha.Backend/Types/StopSchedule.cs b/src/Enmarcha.Backend/Types/StopSchedule.cs deleted file mode 100644 index 2a143ea..0000000 --- a/src/Enmarcha.Backend/Types/StopSchedule.cs +++ /dev/null @@ -1,1542 +0,0 @@ -// <auto-generated> -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: stop_schedule.proto -// </auto-generated> -#pragma warning disable 1591, 0612, 3021, 8981 -#region Designer generated code - -using pb = global::Google.Protobuf; -using pbc = global::Google.Protobuf.Collections; -using pbr = global::Google.Protobuf.Reflection; -using scg = global::System.Collections.Generic; -namespace Enmarcha.Backend.Types { - - /// <summary>Holder for reflection information generated from stop_schedule.proto</summary> - public static partial class StopScheduleReflection { - - #region Descriptor - /// <summary>File descriptor for stop_schedule.proto</summary> - public static pbr::FileDescriptor Descriptor { - get { return descriptor; } - } - private static pbr::FileDescriptor descriptor; - - static StopScheduleReflection() { - byte[] descriptorData = global::System.Convert.FromBase64String( - string.Concat( - "ChNzdG9wX3NjaGVkdWxlLnByb3RvEgVwcm90byIhCglFcHNnMjU4MjkSCQoB", - "eBgBIAEoARIJCgF5GAIgASgBIoMECgxTdG9wQXJyaXZhbHMSDwoHc3RvcF9p", - "ZBgBIAEoCRIiCghsb2NhdGlvbhgDIAEoCzIQLnByb3RvLkVwc2cyNTgyORI2", - "CghhcnJpdmFscxgFIAMoCzIkLnByb3RvLlN0b3BBcnJpdmFscy5TY2hlZHVs", - "ZWRBcnJpdmFsGoUDChBTY2hlZHVsZWRBcnJpdmFsEhIKCnNlcnZpY2VfaWQY", - "ASABKAkSDwoHdHJpcF9pZBgCIAEoCRIMCgRsaW5lGAMgASgJEg0KBXJvdXRl", - "GAQgASgJEhAKCHNoYXBlX2lkGAUgASgJEhsKE3NoYXBlX2Rpc3RfdHJhdmVs", - "ZWQYBiABKAESFQoNc3RvcF9zZXF1ZW5jZRgLIAEoDRIUCgxuZXh0X3N0cmVl", - "dHMYDCADKAkSFQoNc3RhcnRpbmdfY29kZRgVIAEoCRIVCg1zdGFydGluZ19u", - "YW1lGBYgASgJEhUKDXN0YXJ0aW5nX3RpbWUYFyABKAkSFAoMY2FsbGluZ190", - "aW1lGCEgASgJEhMKC2NhbGxpbmdfc3NtGCIgASgNEhUKDXRlcm1pbnVzX2Nv", - "ZGUYKSABKAkSFQoNdGVybWludXNfbmFtZRgqIAEoCRIVCg10ZXJtaW51c190", - "aW1lGCsgASgJEh4KFnByZXZpb3VzX3RyaXBfc2hhcGVfaWQYMyABKAkiOwoF", - "U2hhcGUSEAoIc2hhcGVfaWQYASABKAkSIAoGcG9pbnRzGAMgAygLMhAucHJv", - "dG8uRXBzZzI1ODI5QiSqAiFDb3N0YXNkZXYuQnVzdXJiYW5vLkJhY2tlbmQu", - "VHlwZXNiBnByb3RvMw==")); - descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, - new pbr::FileDescriptor[] { }, - new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Enmarcha.Backend.Types.Epsg25829), global::Enmarcha.Backend.Types.Epsg25829.Parser, new[]{ "X", "Y" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Enmarcha.Backend.Types.StopArrivals), global::Enmarcha.Backend.Types.StopArrivals.Parser, new[]{ "StopId", "Location", "Arrivals" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Enmarcha.Backend.Types.StopArrivals.Types.ScheduledArrival), global::Enmarcha.Backend.Types.StopArrivals.Types.ScheduledArrival.Parser, new[]{ "ServiceId", "TripId", "Line", "Route", "ShapeId", "ShapeDistTraveled", "StopSequence", "NextStreets", "StartingCode", "StartingName", "StartingTime", "CallingTime", "CallingSsm", "TerminusCode", "TerminusName", "TerminusTime", "PreviousTripShapeId" }, null, null, null, null)}), - new pbr::GeneratedClrTypeInfo(typeof(global::Enmarcha.Backend.Types.Shape), global::Enmarcha.Backend.Types.Shape.Parser, new[]{ "ShapeId", "Points" }, null, null, null, null) - })); - } - #endregion - - } - #region Messages - public sealed partial class Epsg25829 : pb::IMessage<Epsg25829> - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - , pb::IBufferMessage - #endif - { - private static readonly pb::MessageParser<Epsg25829> _parser = new pb::MessageParser<Epsg25829>(() => new Epsg25829()); - private pb::UnknownFieldSet _unknownFields; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pb::MessageParser<Epsg25829> Parser { get { return _parser; } } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pbr::MessageDescriptor Descriptor { - get { return global::Enmarcha.Backend.Types.StopScheduleReflection.Descriptor.MessageTypes[0]; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - pbr::MessageDescriptor pb::IMessage.Descriptor { - get { return Descriptor; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Epsg25829() { - OnConstruction(); - } - - partial void OnConstruction(); - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Epsg25829(Epsg25829 other) : this() { - x_ = other.x_; - y_ = other.y_; - _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Epsg25829 Clone() { - return new Epsg25829(this); - } - - /// <summary>Field number for the "x" field.</summary> - public const int XFieldNumber = 1; - private double x_; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public double X { - get { return x_; } - set { - x_ = value; - } - } - - /// <summary>Field number for the "y" field.</summary> - public const int YFieldNumber = 2; - private double y_; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public double Y { - get { return y_; } - set { - y_ = value; - } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override bool Equals(object other) { - return Equals(other as Epsg25829); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool Equals(Epsg25829 other) { - if (ReferenceEquals(other, null)) { - return false; - } - if (ReferenceEquals(other, this)) { - return true; - } - if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(X, other.X)) return false; - if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(Y, other.Y)) return false; - return Equals(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override int GetHashCode() { - int hash = 1; - if (X != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(X); - if (Y != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(Y); - if (_unknownFields != null) { - hash ^= _unknownFields.GetHashCode(); - } - return hash; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override string ToString() { - return pb::JsonFormatter.ToDiagnosticString(this); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void WriteTo(pb::CodedOutputStream output) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - output.WriteRawMessage(this); - #else - if (X != 0D) { - output.WriteRawTag(9); - output.WriteDouble(X); - } - if (Y != 0D) { - output.WriteRawTag(17); - output.WriteDouble(Y); - } - if (_unknownFields != null) { - _unknownFields.WriteTo(output); - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { - if (X != 0D) { - output.WriteRawTag(9); - output.WriteDouble(X); - } - if (Y != 0D) { - output.WriteRawTag(17); - output.WriteDouble(Y); - } - if (_unknownFields != null) { - _unknownFields.WriteTo(ref output); - } - } - #endif - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public int CalculateSize() { - int size = 0; - if (X != 0D) { - size += 1 + 8; - } - if (Y != 0D) { - size += 1 + 8; - } - if (_unknownFields != null) { - size += _unknownFields.CalculateSize(); - } - return size; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(Epsg25829 other) { - if (other == null) { - return; - } - if (other.X != 0D) { - X = other.X; - } - if (other.Y != 0D) { - Y = other.Y; - } - _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(pb::CodedInputStream input) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - input.ReadRawMessage(this); - #else - uint tag; - while ((tag = input.ReadTag()) != 0) { - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); - break; - case 9: { - X = input.ReadDouble(); - break; - } - case 17: { - Y = input.ReadDouble(); - break; - } - } - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { - uint tag; - while ((tag = input.ReadTag()) != 0) { - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); - break; - case 9: { - X = input.ReadDouble(); - break; - } - case 17: { - Y = input.ReadDouble(); - break; - } - } - } - } - #endif - - } - - public sealed partial class StopArrivals : pb::IMessage<StopArrivals> - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - , pb::IBufferMessage - #endif - { - private static readonly pb::MessageParser<StopArrivals> _parser = new pb::MessageParser<StopArrivals>(() => new StopArrivals()); - private pb::UnknownFieldSet _unknownFields; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pb::MessageParser<StopArrivals> Parser { get { return _parser; } } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pbr::MessageDescriptor Descriptor { - get { return global::Enmarcha.Backend.Types.StopScheduleReflection.Descriptor.MessageTypes[1]; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - pbr::MessageDescriptor pb::IMessage.Descriptor { - get { return Descriptor; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public StopArrivals() { - OnConstruction(); - } - - partial void OnConstruction(); - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public StopArrivals(StopArrivals other) : this() { - stopId_ = other.stopId_; - location_ = other.location_ != null ? other.location_.Clone() : null; - arrivals_ = other.arrivals_.Clone(); - _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public StopArrivals Clone() { - return new StopArrivals(this); - } - - /// <summary>Field number for the "stop_id" field.</summary> - public const int StopIdFieldNumber = 1; - private string stopId_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string StopId { - get { return stopId_; } - set { - stopId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "location" field.</summary> - public const int LocationFieldNumber = 3; - private global::Enmarcha.Backend.Types.Epsg25829 location_; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::Enmarcha.Backend.Types.Epsg25829 Location { - get { return location_; } - set { - location_ = value; - } - } - - /// <summary>Field number for the "arrivals" field.</summary> - public const int ArrivalsFieldNumber = 5; - private static readonly pb::FieldCodec<global::Enmarcha.Backend.Types.StopArrivals.Types.ScheduledArrival> _repeated_arrivals_codec - = pb::FieldCodec.ForMessage(42, global::Enmarcha.Backend.Types.StopArrivals.Types.ScheduledArrival.Parser); - private readonly pbc::RepeatedField<global::Enmarcha.Backend.Types.StopArrivals.Types.ScheduledArrival> arrivals_ = new pbc::RepeatedField<global::Enmarcha.Backend.Types.StopArrivals.Types.ScheduledArrival>(); - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public pbc::RepeatedField<global::Enmarcha.Backend.Types.StopArrivals.Types.ScheduledArrival> Arrivals { - get { return arrivals_; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override bool Equals(object other) { - return Equals(other as StopArrivals); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool Equals(StopArrivals other) { - if (ReferenceEquals(other, null)) { - return false; - } - if (ReferenceEquals(other, this)) { - return true; - } - if (StopId != other.StopId) return false; - if (!object.Equals(Location, other.Location)) return false; - if(!arrivals_.Equals(other.arrivals_)) return false; - return Equals(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override int GetHashCode() { - int hash = 1; - if (StopId.Length != 0) hash ^= StopId.GetHashCode(); - if (location_ != null) hash ^= Location.GetHashCode(); - hash ^= arrivals_.GetHashCode(); - if (_unknownFields != null) { - hash ^= _unknownFields.GetHashCode(); - } - return hash; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override string ToString() { - return pb::JsonFormatter.ToDiagnosticString(this); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void WriteTo(pb::CodedOutputStream output) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - output.WriteRawMessage(this); - #else - if (StopId.Length != 0) { - output.WriteRawTag(10); - output.WriteString(StopId); - } - if (location_ != null) { - output.WriteRawTag(26); - output.WriteMessage(Location); - } - arrivals_.WriteTo(output, _repeated_arrivals_codec); - if (_unknownFields != null) { - _unknownFields.WriteTo(output); - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { - if (StopId.Length != 0) { - output.WriteRawTag(10); - output.WriteString(StopId); - } - if (location_ != null) { - output.WriteRawTag(26); - output.WriteMessage(Location); - } - arrivals_.WriteTo(ref output, _repeated_arrivals_codec); - if (_unknownFields != null) { - _unknownFields.WriteTo(ref output); - } - } - #endif - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public int CalculateSize() { - int size = 0; - if (StopId.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(StopId); - } - if (location_ != null) { - size += 1 + pb::CodedOutputStream.ComputeMessageSize(Location); - } - size += arrivals_.CalculateSize(_repeated_arrivals_codec); - if (_unknownFields != null) { - size += _unknownFields.CalculateSize(); - } - return size; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(StopArrivals other) { - if (other == null) { - return; - } - if (other.StopId.Length != 0) { - StopId = other.StopId; - } - if (other.location_ != null) { - if (location_ == null) { - Location = new global::Enmarcha.Backend.Types.Epsg25829(); - } - Location.MergeFrom(other.Location); - } - arrivals_.Add(other.arrivals_); - _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(pb::CodedInputStream input) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - input.ReadRawMessage(this); - #else - uint tag; - while ((tag = input.ReadTag()) != 0) { - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); - break; - case 10: { - StopId = input.ReadString(); - break; - } - case 26: { - if (location_ == null) { - Location = new global::Enmarcha.Backend.Types.Epsg25829(); - } - input.ReadMessage(Location); - break; - } - case 42: { - arrivals_.AddEntriesFrom(input, _repeated_arrivals_codec); - break; - } - } - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { - uint tag; - while ((tag = input.ReadTag()) != 0) { - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); - break; - case 10: { - StopId = input.ReadString(); - break; - } - case 26: { - if (location_ == null) { - Location = new global::Enmarcha.Backend.Types.Epsg25829(); - } - input.ReadMessage(Location); - break; - } - case 42: { - arrivals_.AddEntriesFrom(ref input, _repeated_arrivals_codec); - break; - } - } - } - } - #endif - - #region Nested types - /// <summary>Container for nested types declared in the StopArrivals message type.</summary> - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static partial class Types { - public sealed partial class ScheduledArrival : pb::IMessage<ScheduledArrival> - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - , pb::IBufferMessage - #endif - { - private static readonly pb::MessageParser<ScheduledArrival> _parser = new pb::MessageParser<ScheduledArrival>(() => new ScheduledArrival()); - private pb::UnknownFieldSet _unknownFields; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pb::MessageParser<ScheduledArrival> Parser { get { return _parser; } } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pbr::MessageDescriptor Descriptor { - get { return global::Enmarcha.Backend.Types.StopArrivals.Descriptor.NestedTypes[0]; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - pbr::MessageDescriptor pb::IMessage.Descriptor { - get { return Descriptor; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public ScheduledArrival() { - OnConstruction(); - } - - partial void OnConstruction(); - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public ScheduledArrival(ScheduledArrival other) : this() { - serviceId_ = other.serviceId_; - tripId_ = other.tripId_; - line_ = other.line_; - route_ = other.route_; - shapeId_ = other.shapeId_; - shapeDistTraveled_ = other.shapeDistTraveled_; - stopSequence_ = other.stopSequence_; - nextStreets_ = other.nextStreets_.Clone(); - startingCode_ = other.startingCode_; - startingName_ = other.startingName_; - startingTime_ = other.startingTime_; - callingTime_ = other.callingTime_; - callingSsm_ = other.callingSsm_; - terminusCode_ = other.terminusCode_; - terminusName_ = other.terminusName_; - terminusTime_ = other.terminusTime_; - previousTripShapeId_ = other.previousTripShapeId_; - _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public ScheduledArrival Clone() { - return new ScheduledArrival(this); - } - - /// <summary>Field number for the "service_id" field.</summary> - public const int ServiceIdFieldNumber = 1; - private string serviceId_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string ServiceId { - get { return serviceId_; } - set { - serviceId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "trip_id" field.</summary> - public const int TripIdFieldNumber = 2; - private string tripId_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string TripId { - get { return tripId_; } - set { - tripId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "line" field.</summary> - public const int LineFieldNumber = 3; - private string line_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string Line { - get { return line_; } - set { - line_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "route" field.</summary> - public const int RouteFieldNumber = 4; - private string route_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string Route { - get { return route_; } - set { - route_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "shape_id" field.</summary> - public const int ShapeIdFieldNumber = 5; - private string shapeId_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string ShapeId { - get { return shapeId_; } - set { - shapeId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "shape_dist_traveled" field.</summary> - public const int ShapeDistTraveledFieldNumber = 6; - private double shapeDistTraveled_; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public double ShapeDistTraveled { - get { return shapeDistTraveled_; } - set { - shapeDistTraveled_ = value; - } - } - - /// <summary>Field number for the "stop_sequence" field.</summary> - public const int StopSequenceFieldNumber = 11; - private uint stopSequence_; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public uint StopSequence { - get { return stopSequence_; } - set { - stopSequence_ = value; - } - } - - /// <summary>Field number for the "next_streets" field.</summary> - public const int NextStreetsFieldNumber = 12; - private static readonly pb::FieldCodec<string> _repeated_nextStreets_codec - = pb::FieldCodec.ForString(98); - private readonly pbc::RepeatedField<string> nextStreets_ = new pbc::RepeatedField<string>(); - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public pbc::RepeatedField<string> NextStreets { - get { return nextStreets_; } - } - - /// <summary>Field number for the "starting_code" field.</summary> - public const int StartingCodeFieldNumber = 21; - private string startingCode_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string StartingCode { - get { return startingCode_; } - set { - startingCode_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "starting_name" field.</summary> - public const int StartingNameFieldNumber = 22; - private string startingName_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string StartingName { - get { return startingName_; } - set { - startingName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "starting_time" field.</summary> - public const int StartingTimeFieldNumber = 23; - private string startingTime_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string StartingTime { - get { return startingTime_; } - set { - startingTime_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "calling_time" field.</summary> - public const int CallingTimeFieldNumber = 33; - private string callingTime_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string CallingTime { - get { return callingTime_; } - set { - callingTime_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "calling_ssm" field.</summary> - public const int CallingSsmFieldNumber = 34; - private uint callingSsm_; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public uint CallingSsm { - get { return callingSsm_; } - set { - callingSsm_ = value; - } - } - - /// <summary>Field number for the "terminus_code" field.</summary> - public const int TerminusCodeFieldNumber = 41; - private string terminusCode_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string TerminusCode { - get { return terminusCode_; } - set { - terminusCode_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "terminus_name" field.</summary> - public const int TerminusNameFieldNumber = 42; - private string terminusName_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string TerminusName { - get { return terminusName_; } - set { - terminusName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "terminus_time" field.</summary> - public const int TerminusTimeFieldNumber = 43; - private string terminusTime_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string TerminusTime { - get { return terminusTime_; } - set { - terminusTime_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "previous_trip_shape_id" field.</summary> - public const int PreviousTripShapeIdFieldNumber = 51; - private string previousTripShapeId_ = ""; - /// <summary> - /// Shape ID of the previous trip when the bus comes from another trip that ends at the starting point - /// </summary> - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string PreviousTripShapeId { - get { return previousTripShapeId_; } - set { - previousTripShapeId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override bool Equals(object other) { - return Equals(other as ScheduledArrival); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool Equals(ScheduledArrival other) { - if (ReferenceEquals(other, null)) { - return false; - } - if (ReferenceEquals(other, this)) { - return true; - } - if (ServiceId != other.ServiceId) return false; - if (TripId != other.TripId) return false; - if (Line != other.Line) return false; - if (Route != other.Route) return false; - if (ShapeId != other.ShapeId) return false; - if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(ShapeDistTraveled, other.ShapeDistTraveled)) return false; - if (StopSequence != other.StopSequence) return false; - if(!nextStreets_.Equals(other.nextStreets_)) return false; - if (StartingCode != other.StartingCode) return false; - if (StartingName != other.StartingName) return false; - if (StartingTime != other.StartingTime) return false; - if (CallingTime != other.CallingTime) return false; - if (CallingSsm != other.CallingSsm) return false; - if (TerminusCode != other.TerminusCode) return false; - if (TerminusName != other.TerminusName) return false; - if (TerminusTime != other.TerminusTime) return false; - if (PreviousTripShapeId != other.PreviousTripShapeId) return false; - return Equals(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override int GetHashCode() { - int hash = 1; - if (ServiceId.Length != 0) hash ^= ServiceId.GetHashCode(); - if (TripId.Length != 0) hash ^= TripId.GetHashCode(); - if (Line.Length != 0) hash ^= Line.GetHashCode(); - if (Route.Length != 0) hash ^= Route.GetHashCode(); - if (ShapeId.Length != 0) hash ^= ShapeId.GetHashCode(); - if (ShapeDistTraveled != 0D) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(ShapeDistTraveled); - if (StopSequence != 0) hash ^= StopSequence.GetHashCode(); - hash ^= nextStreets_.GetHashCode(); - if (StartingCode.Length != 0) hash ^= StartingCode.GetHashCode(); - if (StartingName.Length != 0) hash ^= StartingName.GetHashCode(); - if (StartingTime.Length != 0) hash ^= StartingTime.GetHashCode(); - if (CallingTime.Length != 0) hash ^= CallingTime.GetHashCode(); - if (CallingSsm != 0) hash ^= CallingSsm.GetHashCode(); - if (TerminusCode.Length != 0) hash ^= TerminusCode.GetHashCode(); - if (TerminusName.Length != 0) hash ^= TerminusName.GetHashCode(); - if (TerminusTime.Length != 0) hash ^= TerminusTime.GetHashCode(); - if (PreviousTripShapeId.Length != 0) hash ^= PreviousTripShapeId.GetHashCode(); - if (_unknownFields != null) { - hash ^= _unknownFields.GetHashCode(); - } - return hash; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override string ToString() { - return pb::JsonFormatter.ToDiagnosticString(this); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void WriteTo(pb::CodedOutputStream output) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - output.WriteRawMessage(this); - #else - if (ServiceId.Length != 0) { - output.WriteRawTag(10); - output.WriteString(ServiceId); - } - if (TripId.Length != 0) { - output.WriteRawTag(18); - output.WriteString(TripId); - } - if (Line.Length != 0) { - output.WriteRawTag(26); - output.WriteString(Line); - } - if (Route.Length != 0) { - output.WriteRawTag(34); - output.WriteString(Route); - } - if (ShapeId.Length != 0) { - output.WriteRawTag(42); - output.WriteString(ShapeId); - } - if (ShapeDistTraveled != 0D) { - output.WriteRawTag(49); - output.WriteDouble(ShapeDistTraveled); - } - if (StopSequence != 0) { - output.WriteRawTag(88); - output.WriteUInt32(StopSequence); - } - nextStreets_.WriteTo(output, _repeated_nextStreets_codec); - if (StartingCode.Length != 0) { - output.WriteRawTag(170, 1); - output.WriteString(StartingCode); - } - if (StartingName.Length != 0) { - output.WriteRawTag(178, 1); - output.WriteString(StartingName); - } - if (StartingTime.Length != 0) { - output.WriteRawTag(186, 1); - output.WriteString(StartingTime); - } - if (CallingTime.Length != 0) { - output.WriteRawTag(138, 2); - output.WriteString(CallingTime); - } - if (CallingSsm != 0) { - output.WriteRawTag(144, 2); - output.WriteUInt32(CallingSsm); - } - if (TerminusCode.Length != 0) { - output.WriteRawTag(202, 2); - output.WriteString(TerminusCode); - } - if (TerminusName.Length != 0) { - output.WriteRawTag(210, 2); - output.WriteString(TerminusName); - } - if (TerminusTime.Length != 0) { - output.WriteRawTag(218, 2); - output.WriteString(TerminusTime); - } - if (PreviousTripShapeId.Length != 0) { - output.WriteRawTag(154, 3); - output.WriteString(PreviousTripShapeId); - } - if (_unknownFields != null) { - _unknownFields.WriteTo(output); - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { - if (ServiceId.Length != 0) { - output.WriteRawTag(10); - output.WriteString(ServiceId); - } - if (TripId.Length != 0) { - output.WriteRawTag(18); - output.WriteString(TripId); - } - if (Line.Length != 0) { - output.WriteRawTag(26); - output.WriteString(Line); - } - if (Route.Length != 0) { - output.WriteRawTag(34); - output.WriteString(Route); - } - if (ShapeId.Length != 0) { - output.WriteRawTag(42); - output.WriteString(ShapeId); - } - if (ShapeDistTraveled != 0D) { - output.WriteRawTag(49); - output.WriteDouble(ShapeDistTraveled); - } - if (StopSequence != 0) { - output.WriteRawTag(88); - output.WriteUInt32(StopSequence); - } - nextStreets_.WriteTo(ref output, _repeated_nextStreets_codec); - if (StartingCode.Length != 0) { - output.WriteRawTag(170, 1); - output.WriteString(StartingCode); - } - if (StartingName.Length != 0) { - output.WriteRawTag(178, 1); - output.WriteString(StartingName); - } - if (StartingTime.Length != 0) { - output.WriteRawTag(186, 1); - output.WriteString(StartingTime); - } - if (CallingTime.Length != 0) { - output.WriteRawTag(138, 2); - output.WriteString(CallingTime); - } - if (CallingSsm != 0) { - output.WriteRawTag(144, 2); - output.WriteUInt32(CallingSsm); - } - if (TerminusCode.Length != 0) { - output.WriteRawTag(202, 2); - output.WriteString(TerminusCode); - } - if (TerminusName.Length != 0) { - output.WriteRawTag(210, 2); - output.WriteString(TerminusName); - } - if (TerminusTime.Length != 0) { - output.WriteRawTag(218, 2); - output.WriteString(TerminusTime); - } - if (PreviousTripShapeId.Length != 0) { - output.WriteRawTag(154, 3); - output.WriteString(PreviousTripShapeId); - } - if (_unknownFields != null) { - _unknownFields.WriteTo(ref output); - } - } - #endif - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public int CalculateSize() { - int size = 0; - if (ServiceId.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(ServiceId); - } - if (TripId.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(TripId); - } - if (Line.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(Line); - } - if (Route.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(Route); - } - if (ShapeId.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(ShapeId); - } - if (ShapeDistTraveled != 0D) { - size += 1 + 8; - } - if (StopSequence != 0) { - size += 1 + pb::CodedOutputStream.ComputeUInt32Size(StopSequence); - } - size += nextStreets_.CalculateSize(_repeated_nextStreets_codec); - if (StartingCode.Length != 0) { - size += 2 + pb::CodedOutputStream.ComputeStringSize(StartingCode); - } - if (StartingName.Length != 0) { - size += 2 + pb::CodedOutputStream.ComputeStringSize(StartingName); - } - if (StartingTime.Length != 0) { - size += 2 + pb::CodedOutputStream.ComputeStringSize(StartingTime); - } - if (CallingTime.Length != 0) { - size += 2 + pb::CodedOutputStream.ComputeStringSize(CallingTime); - } - if (CallingSsm != 0) { - size += 2 + pb::CodedOutputStream.ComputeUInt32Size(CallingSsm); - } - if (TerminusCode.Length != 0) { - size += 2 + pb::CodedOutputStream.ComputeStringSize(TerminusCode); - } - if (TerminusName.Length != 0) { - size += 2 + pb::CodedOutputStream.ComputeStringSize(TerminusName); - } - if (TerminusTime.Length != 0) { - size += 2 + pb::CodedOutputStream.ComputeStringSize(TerminusTime); - } - if (PreviousTripShapeId.Length != 0) { - size += 2 + pb::CodedOutputStream.ComputeStringSize(PreviousTripShapeId); - } - if (_unknownFields != null) { - size += _unknownFields.CalculateSize(); - } - return size; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(ScheduledArrival other) { - if (other == null) { - return; - } - if (other.ServiceId.Length != 0) { - ServiceId = other.ServiceId; - } - if (other.TripId.Length != 0) { - TripId = other.TripId; - } - if (other.Line.Length != 0) { - Line = other.Line; - } - if (other.Route.Length != 0) { - Route = other.Route; - } - if (other.ShapeId.Length != 0) { - ShapeId = other.ShapeId; - } - if (other.ShapeDistTraveled != 0D) { - ShapeDistTraveled = other.ShapeDistTraveled; - } - if (other.StopSequence != 0) { - StopSequence = other.StopSequence; - } - nextStreets_.Add(other.nextStreets_); - if (other.StartingCode.Length != 0) { - StartingCode = other.StartingCode; - } - if (other.StartingName.Length != 0) { - StartingName = other.StartingName; - } - if (other.StartingTime.Length != 0) { - StartingTime = other.StartingTime; - } - if (other.CallingTime.Length != 0) { - CallingTime = other.CallingTime; - } - if (other.CallingSsm != 0) { - CallingSsm = other.CallingSsm; - } - if (other.TerminusCode.Length != 0) { - TerminusCode = other.TerminusCode; - } - if (other.TerminusName.Length != 0) { - TerminusName = other.TerminusName; - } - if (other.TerminusTime.Length != 0) { - TerminusTime = other.TerminusTime; - } - if (other.PreviousTripShapeId.Length != 0) { - PreviousTripShapeId = other.PreviousTripShapeId; - } - _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(pb::CodedInputStream input) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - input.ReadRawMessage(this); - #else - uint tag; - while ((tag = input.ReadTag()) != 0) { - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); - break; - case 10: { - ServiceId = input.ReadString(); - break; - } - case 18: { - TripId = input.ReadString(); - break; - } - case 26: { - Line = input.ReadString(); - break; - } - case 34: { - Route = input.ReadString(); - break; - } - case 42: { - ShapeId = input.ReadString(); - break; - } - case 49: { - ShapeDistTraveled = input.ReadDouble(); - break; - } - case 88: { - StopSequence = input.ReadUInt32(); - break; - } - case 98: { - nextStreets_.AddEntriesFrom(input, _repeated_nextStreets_codec); - break; - } - case 170: { - StartingCode = input.ReadString(); - break; - } - case 178: { - StartingName = input.ReadString(); - break; - } - case 186: { - StartingTime = input.ReadString(); - break; - } - case 266: { - CallingTime = input.ReadString(); - break; - } - case 272: { - CallingSsm = input.ReadUInt32(); - break; - } - case 330: { - TerminusCode = input.ReadString(); - break; - } - case 338: { - TerminusName = input.ReadString(); - break; - } - case 346: { - TerminusTime = input.ReadString(); - break; - } - case 410: { - PreviousTripShapeId = input.ReadString(); - break; - } - } - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { - uint tag; - while ((tag = input.ReadTag()) != 0) { - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); - break; - case 10: { - ServiceId = input.ReadString(); - break; - } - case 18: { - TripId = input.ReadString(); - break; - } - case 26: { - Line = input.ReadString(); - break; - } - case 34: { - Route = input.ReadString(); - break; - } - case 42: { - ShapeId = input.ReadString(); - break; - } - case 49: { - ShapeDistTraveled = input.ReadDouble(); - break; - } - case 88: { - StopSequence = input.ReadUInt32(); - break; - } - case 98: { - nextStreets_.AddEntriesFrom(ref input, _repeated_nextStreets_codec); - break; - } - case 170: { - StartingCode = input.ReadString(); - break; - } - case 178: { - StartingName = input.ReadString(); - break; - } - case 186: { - StartingTime = input.ReadString(); - break; - } - case 266: { - CallingTime = input.ReadString(); - break; - } - case 272: { - CallingSsm = input.ReadUInt32(); - break; - } - case 330: { - TerminusCode = input.ReadString(); - break; - } - case 338: { - TerminusName = input.ReadString(); - break; - } - case 346: { - TerminusTime = input.ReadString(); - break; - } - case 410: { - PreviousTripShapeId = input.ReadString(); - break; - } - } - } - } - #endif - - } - - } - #endregion - - } - - public sealed partial class Shape : pb::IMessage<Shape> - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - , pb::IBufferMessage - #endif - { - private static readonly pb::MessageParser<Shape> _parser = new pb::MessageParser<Shape>(() => new Shape()); - private pb::UnknownFieldSet _unknownFields; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pb::MessageParser<Shape> Parser { get { return _parser; } } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pbr::MessageDescriptor Descriptor { - get { return global::Enmarcha.Backend.Types.StopScheduleReflection.Descriptor.MessageTypes[2]; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - pbr::MessageDescriptor pb::IMessage.Descriptor { - get { return Descriptor; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Shape() { - OnConstruction(); - } - - partial void OnConstruction(); - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Shape(Shape other) : this() { - shapeId_ = other.shapeId_; - points_ = other.points_.Clone(); - _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Shape Clone() { - return new Shape(this); - } - - /// <summary>Field number for the "shape_id" field.</summary> - public const int ShapeIdFieldNumber = 1; - private string shapeId_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public string ShapeId { - get { return shapeId_; } - set { - shapeId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "points" field.</summary> - public const int PointsFieldNumber = 3; - private static readonly pb::FieldCodec<global::Enmarcha.Backend.Types.Epsg25829> _repeated_points_codec - = pb::FieldCodec.ForMessage(26, global::Enmarcha.Backend.Types.Epsg25829.Parser); - private readonly pbc::RepeatedField<global::Enmarcha.Backend.Types.Epsg25829> points_ = new pbc::RepeatedField<global::Enmarcha.Backend.Types.Epsg25829>(); - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public pbc::RepeatedField<global::Enmarcha.Backend.Types.Epsg25829> Points { - get { return points_; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override bool Equals(object other) { - return Equals(other as Shape); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool Equals(Shape other) { - if (ReferenceEquals(other, null)) { - return false; - } - if (ReferenceEquals(other, this)) { - return true; - } - if (ShapeId != other.ShapeId) return false; - if(!points_.Equals(other.points_)) return false; - return Equals(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override int GetHashCode() { - int hash = 1; - if (ShapeId.Length != 0) hash ^= ShapeId.GetHashCode(); - hash ^= points_.GetHashCode(); - if (_unknownFields != null) { - hash ^= _unknownFields.GetHashCode(); - } - return hash; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override string ToString() { - return pb::JsonFormatter.ToDiagnosticString(this); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void WriteTo(pb::CodedOutputStream output) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - output.WriteRawMessage(this); - #else - if (ShapeId.Length != 0) { - output.WriteRawTag(10); - output.WriteString(ShapeId); - } - points_.WriteTo(output, _repeated_points_codec); - if (_unknownFields != null) { - _unknownFields.WriteTo(output); - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { - if (ShapeId.Length != 0) { - output.WriteRawTag(10); - output.WriteString(ShapeId); - } - points_.WriteTo(ref output, _repeated_points_codec); - if (_unknownFields != null) { - _unknownFields.WriteTo(ref output); - } - } - #endif - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public int CalculateSize() { - int size = 0; - if (ShapeId.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(ShapeId); - } - size += points_.CalculateSize(_repeated_points_codec); - if (_unknownFields != null) { - size += _unknownFields.CalculateSize(); - } - return size; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(Shape other) { - if (other == null) { - return; - } - if (other.ShapeId.Length != 0) { - ShapeId = other.ShapeId; - } - points_.Add(other.points_); - _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(pb::CodedInputStream input) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - input.ReadRawMessage(this); - #else - uint tag; - while ((tag = input.ReadTag()) != 0) { - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); - break; - case 10: { - ShapeId = input.ReadString(); - break; - } - case 26: { - points_.AddEntriesFrom(input, _repeated_points_codec); - break; - } - } - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { - uint tag; - while ((tag = input.ReadTag()) != 0) { - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); - break; - case 10: { - ShapeId = input.ReadString(); - break; - } - case 26: { - points_.AddEntriesFrom(ref input, _repeated_points_codec); - break; - } - } - } - } - #endif - - } - - #endregion - -} - -#endregion Designer generated code diff --git a/src/Enmarcha.Backend/Types/VigoSchedules.cs b/src/Enmarcha.Backend/Types/VigoSchedules.cs deleted file mode 100644 index ee7930f..0000000 --- a/src/Enmarcha.Backend/Types/VigoSchedules.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Enmarcha.Backend.Types; - -public class ScheduledStop -{ - [JsonPropertyName("trip_id")] public required string TripId { get; set; } - [JsonPropertyName("service_id")] public required string ServiceId { get; set; } - [JsonPropertyName("line")] public required string Line { get; set; } - [JsonPropertyName("route")] public required string Route { get; set; } - [JsonPropertyName("stop_sequence")] public required int StopSequence { get; set; } - - [JsonPropertyName("shape_dist_traveled")] - public required double ShapeDistTraveled { get; set; } - - [JsonPropertyName("next_streets")] public required string[] NextStreets { get; set; } - - [JsonPropertyName("starting_code")] public required string StartingCode { get; set; } - [JsonPropertyName("starting_name")] public required string StartingName { get; set; } - [JsonPropertyName("starting_time")] public required string StartingTime { get; set; } - public DateTime? StartingDateTime(DateTime? baseDate = null) - { - return ParseGtfsTime(StartingTime, baseDate); - } - - [JsonPropertyName("calling_ssm")] public required int CallingSsm { get; set; } - [JsonPropertyName("calling_time")] public required string CallingTime { get; set; } - public DateTime? CallingDateTime(DateTime? baseDate = null) - { - return ParseGtfsTime(CallingTime, baseDate); - } - - [JsonPropertyName("terminus_code")] public required string TerminusCode { get; set; } - [JsonPropertyName("terminus_name")] public required string TerminusName { get; set; } - [JsonPropertyName("terminus_time")] public required string TerminusTime { get; set; } - - /// <summary> - /// Parse GTFS time format (HH:MM:SS) which can have hours >= 24 for services past midnight - /// </summary> - private static DateTime? ParseGtfsTime(string timeStr, DateTime? baseDate = null) - { - if (string.IsNullOrWhiteSpace(timeStr)) - { - return null; - } - - var parts = timeStr.Split(':'); - if (parts.Length != 3) - { - return null; - } - - if (!int.TryParse(parts[0], out var hours) || - !int.TryParse(parts[1], out var minutes) || - !int.TryParse(parts[2], out var seconds)) - { - return null; - } - - // Handle GTFS times that exceed 24 hours (e.g., 25:30:00 for 1:30 AM next day) - var days = hours / 24; - var normalizedHours = hours % 24; - - try - { - var dt = (baseDate ?? DateTime.Today) - .AddDays(days) - .AddHours(normalizedHours) - .AddMinutes(minutes) - .AddSeconds(seconds); - return dt.AddSeconds(60 - dt.Second); - } - catch - { - return null; - } - } -} diff --git a/src/Enmarcha.Sources.OpenTripPlannerGql/Queries/ArrivalsAtStop.cs b/src/Enmarcha.Sources.OpenTripPlannerGql/Queries/ArrivalsAtStop.cs index 64e148d..6e89f08 100644 --- a/src/Enmarcha.Sources.OpenTripPlannerGql/Queries/ArrivalsAtStop.cs +++ b/src/Enmarcha.Sources.OpenTripPlannerGql/Queries/ArrivalsAtStop.cs @@ -53,8 +53,12 @@ public class ArrivalsAtStopContent : IGraphRequest<ArrivalsAtStopContent.Args> }} }} {geometryField} + pattern {{ + id + }} stoptimes {{ stop {{ + gtfsId name lat lon @@ -125,9 +129,16 @@ public class ArrivalsAtStopResponse : AbstractGraphResponse [JsonPropertyName("tripGeometry")] public GeometryDetails? Geometry { get; set; } + [JsonPropertyName("pattern")] public PatternRef? Pattern { get; set; } + [JsonPropertyName("stoptimes")] public List<StoptimeDetails> Stoptimes { get; set; } = []; } + public class PatternRef + { + [JsonPropertyName("id")] public required string Id { get; set; } + } + public class GeometryDetails { [JsonPropertyName("points")] public string? Points { get; set; } @@ -141,6 +152,7 @@ public class ArrivalsAtStopResponse : AbstractGraphResponse public class StopDetails { + [JsonPropertyName("gtfsId")] public string? GtfsId { get; set; } [JsonPropertyName("name")] public required string Name { get; set; } [JsonPropertyName("lat")] public double Lat { get; set; } [JsonPropertyName("lon")] public double Lon { get; set; } diff --git a/src/frontend/app/api/arrivals.ts b/src/frontend/app/api/arrivals.ts index 8ae6e78..ad99630 100644 --- a/src/frontend/app/api/arrivals.ts +++ b/src/frontend/app/api/arrivals.ts @@ -1,6 +1,8 @@ import { StopArrivalsResponseSchema, + StopEstimatesResponseSchema, type StopArrivalsResponse, + type StopEstimatesResponse, } from "./schema"; export const fetchArrivals = async ( @@ -29,3 +31,31 @@ export const fetchArrivals = async ( throw e; } }; + +export const fetchEstimates = async ( + stopId: string, + routeId: string, + viaStopId?: string +): Promise<StopEstimatesResponse> => { + let url = `/api/stops/estimates?stop=${encodeURIComponent(stopId)}&route=${encodeURIComponent(routeId)}`; + if (viaStopId) { + url += `&via=${encodeURIComponent(viaStopId)}`; + } + + const resp = await fetch(url, { + headers: { Accept: "application/json" }, + }); + + if (!resp.ok) { + throw new Error(`HTTP ${resp.status}: ${resp.statusText}`); + } + + const data = await resp.json(); + try { + return StopEstimatesResponseSchema.parse(data); + } catch (e) { + console.error("Zod parsing failed for estimates:", e); + console.log("Received data:", data); + throw e; + } +}; diff --git a/src/frontend/app/api/schema.ts b/src/frontend/app/api/schema.ts index 0c55969..f68d413 100644 --- a/src/frontend/app/api/schema.ts +++ b/src/frontend/app/api/schema.ts @@ -64,10 +64,16 @@ export const ArrivalSchema = z.object({ shift: ShiftBadgeSchema.optional().nullable(), shape: z.any().optional().nullable(), currentPosition: PositionSchema.optional().nullable(), - stopShapeIndex: z.number().optional().nullable(), vehicleInformation: VehicleInformationSchema.optional().nullable(), }); +export const ArrivalEstimateSchema = z.object({ + tripId: z.string(), + patternId: z.string().optional().nullable(), + estimate: ArrivalDetailsSchema, + delay: DelayBadgeSchema.optional().nullable(), +}); + export const StopArrivalsResponseSchema = z.object({ stopCode: z.string(), stopName: z.string(), @@ -77,6 +83,10 @@ export const StopArrivalsResponseSchema = z.object({ usage: z.array(BusStopUsagePointSchema).optional().nullable(), }); +export const StopEstimatesResponseSchema = z.object({ + arrivals: z.array(ArrivalEstimateSchema), +}); + export type RouteInfo = z.infer<typeof RouteInfoSchema>; export type HeadsignInfo = z.infer<typeof HeadsignInfoSchema>; export type ArrivalPrecision = z.infer<typeof ArrivalPrecisionSchema>; @@ -85,8 +95,10 @@ export type DelayBadge = z.infer<typeof DelayBadgeSchema>; export type ShiftBadge = z.infer<typeof ShiftBadgeSchema>; export type Position = z.infer<typeof PositionSchema>; export type Arrival = z.infer<typeof ArrivalSchema>; +export type ArrivalEstimate = z.infer<typeof ArrivalEstimateSchema>; export type BusStopUsagePoint = z.infer<typeof BusStopUsagePointSchema>; export type StopArrivalsResponse = z.infer<typeof StopArrivalsResponseSchema>; +export type StopEstimatesResponse = z.infer<typeof StopEstimatesResponseSchema>; // Transit Routes export const RouteSchema = z.object({ diff --git a/src/frontend/app/components/stop/StopMapModal.tsx b/src/frontend/app/components/stop/StopMapModal.tsx index 30ac63f..8d3c6f8 100644 --- a/src/frontend/app/components/stop/StopMapModal.tsx +++ b/src/frontend/app/components/stop/StopMapModal.tsx @@ -15,14 +15,13 @@ import "./StopMapModal.css"; export interface Position { latitude: number; longitude: number; - orientationDegrees: number; - shapeIndex?: number; + orientationDegrees?: number | null; + shapeIndex?: number | null | undefined; } export interface ConsolidatedCirculationForMap { id: string; currentPosition?: Position; - stopShapeIndex?: number; colour: string; textColour: string; shape?: any; diff --git a/src/frontend/app/data/PlannerApi.ts b/src/frontend/app/data/PlannerApi.ts index 43c8ae1..6f39f50 100644 --- a/src/frontend/app/data/PlannerApi.ts +++ b/src/frontend/app/data/PlannerApi.ts @@ -31,6 +31,8 @@ export interface Itinerary { export interface Leg { mode?: string; feedId?: string; + routeId?: string; + tripId?: string; routeName?: string; routeShortName?: string; routeLongName?: string; diff --git a/src/frontend/app/hooks/useArrivals.ts b/src/frontend/app/hooks/useArrivals.ts index e86a0bf..530ebc4 100644 --- a/src/frontend/app/hooks/useArrivals.ts +++ b/src/frontend/app/hooks/useArrivals.ts @@ -1,5 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { fetchArrivals } from "../api/arrivals"; +import { fetchArrivals, fetchEstimates } from "../api/arrivals"; export const useStopArrivals = ( stopId: string, @@ -10,7 +10,22 @@ export const useStopArrivals = ( queryKey: ["arrivals", stopId, reduced], queryFn: () => fetchArrivals(stopId, reduced), enabled: !!stopId && enabled, - refetchInterval: 15000, // Refresh every 15 seconds - retry: false, // Disable retries to see errors immediately + refetchInterval: 15000, + retry: false, + }); +}; + +export const useStopEstimates = ( + stopId: string, + routeId: string, + viaStopId?: string, + enabled: boolean = true +) => { + return useQuery({ + queryKey: ["estimates", stopId, routeId, viaStopId], + queryFn: () => fetchEstimates(stopId, routeId, viaStopId), + enabled: !!stopId && !!routeId && enabled, + refetchInterval: 15000, + retry: false, }); }; diff --git a/src/frontend/app/routes/planner.tsx b/src/frontend/app/routes/planner.tsx index b7ecaf9..4038ef7 100644 --- a/src/frontend/app/routes/planner.tsx +++ b/src/frontend/app/routes/planner.tsx @@ -6,7 +6,8 @@ import { useTranslation } from "react-i18next"; import { Layer, Source, type MapRef } from "react-map-gl/maplibre"; import { useLocation } from "react-router"; -import { type ConsolidatedCirculation } from "~/api/schema"; +import { fetchEstimates } from "~/api/arrivals"; +import { type StopEstimatesResponse } from "~/api/schema"; import { PlannerOverlay } from "~/components/PlannerOverlay"; import RouteIcon from "~/components/RouteIcon"; import { AppMap } from "~/components/shared/AppMap"; @@ -208,7 +209,7 @@ const ItineraryDetail = ({ const mapRef = useRef<MapRef>(null); const { destination: userDestination } = usePlanner(); const [nextArrivals, setNextArrivals] = useState< - Record<string, ConsolidatedCirculation[]> + Record<string, StopEstimatesResponse> >({}); const routeGeoJson = { @@ -324,27 +325,27 @@ const ItineraryDetail = ({ // Fetch next arrivals for bus legs useEffect(() => { - const fetchArrivals = async () => { - const arrivalsByStop: Record<string, ConsolidatedCirculation[]> = {}; + const fetchArrivalsForLegs = async () => { + const arrivalsByLeg: Record<string, StopEstimatesResponse> = {}; for (const leg of itinerary.legs) { - if (leg.mode !== "WALK" && leg.from?.stopId) { - const stopKey = leg.from.name || leg.from.stopId; - if (!arrivalsByStop[stopKey]) { + if ( + leg.mode !== "WALK" && + leg.from?.stopId && + leg.to?.stopId && + leg.routeId + ) { + const legKey = `${leg.from.stopId}::${leg.to.stopId}`; + if (!arrivalsByLeg[legKey]) { try { - //TODO: Allow multiple stops one request - const resp = await fetch( - `/api/stops/arrivals?id=${encodeURIComponent(leg.from.stopId)}`, - { headers: { Accept: "application/json" } } + arrivalsByLeg[legKey] = await fetchEstimates( + leg.from.stopId, + leg.routeId, + leg.to.stopId ); - - if (resp.ok) { - arrivalsByStop[stopKey] = - (await resp.json()) satisfies ConsolidatedCirculation[]; - } } catch (err) { console.warn( - `Failed to fetch arrivals for ${leg.from.stopId}:`, + `Failed to fetch estimates for leg ${leg.from.stopId} -> ${leg.to.stopId}:`, err ); } @@ -352,10 +353,10 @@ const ItineraryDetail = ({ } } - setNextArrivals(arrivalsByStop); + setNextArrivals(arrivalsByLeg); }; - fetchArrivals(); + fetchArrivalsForLegs(); }, [itinerary]); return ( @@ -564,60 +565,45 @@ const ItineraryDetail = ({ </div> {leg.mode !== "WALK" && leg.from?.stopId && - nextArrivals[leg.from.name || leg.from.stopId] && ( + leg.to?.stopId && + nextArrivals[`${leg.from.stopId}::${leg.to.stopId}`] && ( <div className="mt-2 text-xs text-gray-600 dark:text-gray-400"> <div className="font-semibold mb-1"> {t("planner.next_arrivals", "Next arrivals")}: </div> - {(() => { - const currentLine = - leg.routeShortName || leg.routeName; - const previousLeg = - idx > 0 ? itinerary.legs[idx - 1] : null; - const previousLine = - previousLeg?.mode !== "WALK" - ? previousLeg?.routeShortName || - previousLeg?.routeName - : null; - - const linesToShow = [currentLine]; - if ( - previousLine && - previousLeg?.to?.stopId === leg.from?.stopId - ) { - linesToShow.push(previousLine); - } - - return nextArrivals[leg.from.stopId] - ?.filter((circ) => linesToShow.includes(circ.line)) - .slice(0, 3) - .map((circ, idx) => { - const minutes = - circ.realTime?.minutes ?? - circ.schedule?.minutes; - if (minutes === undefined) return null; - return ( - <div - key={idx} - className="flex items-center gap-2 py-0.5" - > - <span className="font-semibold"> - {circ.line} - </span> - <span className="text-gray-500 dark:text-gray-500"> - → + {nextArrivals[ + `${leg.from.stopId}::${leg.to.stopId}` + ].arrivals + .slice(0, 3) + .map((arrival, i) => ( + <div + key={`${arrival.tripId}-${i}`} + className="flex items-center gap-2 py-0.5" + > + <span className="font-semibold text-primary-600 dark:text-primary-400"> + {formatDuration(arrival.estimate.minutes, t)} + </span> + {arrival.estimate.precision !== "scheduled" && ( + <span className="text-green-600 dark:text-green-400"> + 🟢 + </span> + )} + {arrival.delay?.minutes !== undefined && + arrival.delay.minutes !== 0 && ( + <span + className={ + arrival.delay.minutes > 0 + ? "text-red-500" + : "text-green-500" + } + > + {arrival.delay.minutes > 0 + ? `+${arrival.delay.minutes}′` + : `${arrival.delay.minutes}′`} </span> - <span className="flex-1 truncate"> - {circ.route} - </span> - <span className="font-semibold text-primary-600 dark:text-primary-400"> - {formatDuration(minutes, t)} - {circ.realTime && " 🟢"} - </span> - </div> - ); - }); - })()} + )} + </div> + ))} </div> )} <div className="text-sm mt-1"> diff --git a/src/frontend/app/routes/routes-$id.tsx b/src/frontend/app/routes/routes-$id.tsx index 2174244..bccaf56 100644 --- a/src/frontend/app/routes/routes-$id.tsx +++ b/src/frontend/app/routes/routes-$id.tsx @@ -19,7 +19,7 @@ import { Source, type MapRef, } from "react-map-gl/maplibre"; -import { Link, useParams } from "react-router"; +import { Link, useLocation, useNavigate, useParams } from "react-router"; import { fetchRouteDetails } from "~/api/transit"; import { AppMap } from "~/components/shared/AppMap"; import { @@ -28,7 +28,7 @@ import { usePageTitle, usePageTitleNode, } from "~/contexts/PageTitleContext"; -import { useStopArrivals } from "~/hooks/useArrivals"; +import { useStopEstimates } from "~/hooks/useArrivals"; import { useFavorites } from "~/hooks/useFavorites"; import { formatHex } from "~/utils/colours"; import "../tailwind-full.css"; @@ -59,9 +59,9 @@ function FavoriteStar({ id }: { id?: string }) { export default function RouteDetailsPage() { const { id } = useParams(); const { t, i18n } = useTranslation(); - const [selectedPatternId, setSelectedPatternId] = useState<string | null>( - null - ); + const navigate = useNavigate(); + const location = useLocation(); + const selectedPatternId = location.hash ? location.hash.slice(1) : null; const [selectedStopId, setSelectedStopId] = useState<string | null>(null); const [layoutMode, setLayoutMode] = useState<"balanced" | "map" | "list">( "balanced" @@ -103,34 +103,13 @@ export default function RouteDetailsPage() { queryFn: () => fetchRouteDetails(id!, selectedDateKey), enabled: !!id, }); - const { data: selectedStopRealtime, isLoading: isRealtimeLoading } = - useStopArrivals( + const { data: selectedStopEstimates, isLoading: isRealtimeLoading } = + useStopEstimates( selectedStopId ?? "", - true, - Boolean(selectedStopId) && isTodaySelectedDate + id ?? "", + undefined, + Boolean(selectedStopId) && Boolean(id) && isTodaySelectedDate ); - const filteredRealtimeArrivals = useMemo(() => { - const arrivals = selectedStopRealtime?.arrivals ?? []; - if (arrivals.length === 0) { - return []; - } - - const routeId = id?.trim(); - const routeShortName = route?.shortName?.trim().toLowerCase(); - - return arrivals.filter((arrival) => { - const arrivalGtfsId = arrival.route.gtfsId?.trim(); - if (routeId && arrivalGtfsId) { - return arrivalGtfsId === routeId; - } - - if (routeShortName) { - return arrival.route.shortName.trim().toLowerCase() === routeShortName; - } - - return true; - }); - }, [selectedStopRealtime?.arrivals, id, route?.shortName]); usePageTitle( route?.shortName @@ -589,7 +568,10 @@ export default function RouteDetailsPage() { key={pattern.id} type="button" onClick={() => { - setSelectedPatternId(pattern.id); + navigate( + { hash: "#" + pattern.id }, + { replace: true } + ); setSelectedStopId(null); setIsPatternPickerOpen(false); }} @@ -748,33 +730,31 @@ export default function RouteDetailsPage() { {selectedStopId === stop.id && (departuresByStop.get(stop.id)?.length ?? 0) > 0 && ( <div className="mt-2 flex flex-wrap gap-1"> - {( - departuresByStop - .get(stop.id) - ?.filter((item) => - isTodaySelectedDate - ? item.departure >= - nowSeconds - ONE_HOUR_SECONDS - : true - ) ?? [] - ).map((item, i) => ( - <span - key={`${item.patternId}-${item.departure}-${i}`} - className={`text-[11px] px-2 py-0.5 rounded ${ - item.patternId === selectedPattern?.id - ? "bg-gray-100 dark:bg-gray-900" - : "bg-gray-50 dark:bg-gray-900 text-gray-400 font-light" - }`} - > - {Math.floor(item.departure / 3600) - .toString() - .padStart(2, "0")} - : - {Math.floor((item.departure % 3600) / 60) - .toString() - .padStart(2, "0")} - </span> - ))} + {(departuresByStop.get(stop.id) ?? []).map( + (item, i) => { + const isPast = + isTodaySelectedDate && + item.departure < nowSeconds; + return ( + <span + key={`${item.patternId}-${item.departure}-${i}`} + className={`text-[11px] px-2 py-0.5 rounded ${ + item.patternId === selectedPattern?.id + ? "bg-gray-100 dark:bg-gray-900" + : "bg-gray-50 dark:bg-gray-900 text-gray-400 font-light" + } ${isPast ? "line-through opacity-50" : ""}`} + > + {Math.floor(item.departure / 3600) + .toString() + .padStart(2, "0")} + : + {Math.floor((item.departure % 3600) / 60) + .toString() + .padStart(2, "0")} + </span> + ); + } + )} </div> )} @@ -787,7 +767,8 @@ export default function RouteDetailsPage() { <div className="text-[11px] text-muted"> {t("routes.loading_realtime", "Cargando...")} </div> - ) : filteredRealtimeArrivals.length === 0 ? ( + ) : (selectedStopEstimates?.arrivals.length ?? 0) === + 0 ? ( <div className="text-[11px] text-muted"> {t( "routes.realtime_no_route_estimates", @@ -796,37 +777,67 @@ export default function RouteDetailsPage() { </div> ) : ( <> - <div className="flex items-center justify-between gap-2 rounded-lg border border-green-500/30 bg-green-500/10 px-2.5 py-2"> - <span className="text-[11px] font-semibold uppercase tracking-wide text-green-700 dark:text-green-300"> - {t("routes.next_arrival", "Próximo")} - </span> - <span className="inline-flex min-w-16 items-center justify-center rounded-xl bg-green-600 px-3 py-1.5 text-base font-bold leading-none text-white"> - {filteredRealtimeArrivals[0].estimate.minutes}′ - {filteredRealtimeArrivals[0].delay?.minutes - ? formatDelayMinutes( - filteredRealtimeArrivals[0].delay.minutes - ) - : ""} - </span> - </div> + {(() => { + const firstArrival = + selectedStopEstimates!.arrivals[0]; + const isFirstSelectedPattern = + firstArrival.patternId === selectedPattern?.id; + return ( + <div + className={`flex items-center justify-between gap-2 rounded-lg border px-2.5 py-2 ${isFirstSelectedPattern ? "border-green-500/30 bg-green-500/10" : "border-emerald-500/20 bg-emerald-500/5 opacity-50"}`} + > + <span + className={`text-[11px] font-semibold uppercase tracking-wide ${isFirstSelectedPattern ? "text-green-700 dark:text-green-300" : "text-emerald-700 dark:text-emerald-400"}`} + > + {t("routes.next_arrival", "Próximo")} + </span> + <span + className={`inline-flex min-w-16 items-center justify-center rounded-xl px-3 py-1.5 text-base font-bold leading-none text-white ${isFirstSelectedPattern ? "bg-green-600" : "bg-emerald-600"}`} + > + {firstArrival.estimate.minutes}′ + {firstArrival.delay?.minutes + ? formatDelayMinutes( + firstArrival.delay.minutes + ) + : ""} + </span> + </div> + ); + })()} - {filteredRealtimeArrivals.length > 1 && ( + {selectedStopEstimates!.arrivals.length > 1 && ( <div className="mt-2 flex flex-wrap justify-end gap-1"> - {filteredRealtimeArrivals + {selectedStopEstimates!.arrivals .slice(1) - .map((arrival, i) => ( - <span - key={`${arrival.tripId}-${i}`} - className="text-[11px] px-2 py-0.5 bg-primary/10 text-primary rounded" - > - {arrival.estimate.minutes}′ - {arrival.delay?.minutes - ? formatDelayMinutes( - arrival.delay.minutes - ) - : ""} - </span> - ))} + .map((arrival, i) => { + const isSelectedPattern = + arrival.patternId === selectedPattern?.id; + return ( + <span + key={`${arrival.tripId}-${i}`} + className={`text-[11px] px-2 py-0.5 rounded ${ + isSelectedPattern + ? "bg-gray-100 dark:bg-gray-900" + : "bg-gray-50 dark:bg-gray-900 text-gray-400 font-light" + }`} + title={ + isSelectedPattern + ? undefined + : t( + "routes.other_pattern", + "Otro trayecto" + ) + } + > + {arrival.estimate.minutes}′ + {arrival.delay?.minutes + ? formatDelayMinutes( + arrival.delay.minutes + ) + : ""} + </span> + ); + })} </div> )} </> diff --git a/src/frontend/app/routes/stops-$id.tsx b/src/frontend/app/routes/stops-$id.tsx index a61e925..4b32040 100644 --- a/src/frontend/app/routes/stops-$id.tsx +++ b/src/frontend/app/routes/stops-$id.tsx @@ -274,7 +274,6 @@ export default function Estimates() { circulations={(data?.arrivals ?? []).map((a) => ({ id: getArrivalId(a), currentPosition: a.currentPosition ?? undefined, - stopShapeIndex: a.stopShapeIndex ?? undefined, colour: formatHex(a.route.colour), textColour: formatHex(a.route.textColour), shape: a.shape, |
