diff options
| author | copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> | 2025-11-22 16:17:29 +0000 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-11-22 18:31:59 +0100 |
| commit | 71dd498ab6ef05df944ce42e0d2228d8ff4dc418 (patch) | |
| tree | 57e2a42ddca99d5c8c050cd04d0ae7fb75b8f1f1 /src | |
| parent | bb5745d849d4899d1672b6a689cd071953b01866 (diff) | |
Add previous_trip_shape_id field to protobuf and implement linking logic
Co-authored-by: arielcostas <94913521+arielcostas@users.noreply.github.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs | 7 | ||||
| -rw-r--r-- | src/Costasdev.Busurbano.Backend/Types/ConsolidatedCirculation.cs | 2 | ||||
| -rw-r--r-- | src/gtfs_vigo_stops/stop_report.py | 59 |
3 files changed, 38 insertions, 30 deletions
diff --git a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs index 91ccdab..570b56d 100644 --- a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs +++ b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs @@ -306,6 +306,7 @@ public class VigoController : ControllerBase var isRunning = closestCirculation.StartingDateTime()!.Value <= now; Position? currentPosition = null; int? stopShapeIndex = null; + bool usePreviousShape = false; // Calculate bus position for realtime trips if (!string.IsNullOrEmpty(closestCirculation.ShapeId)) @@ -314,7 +315,7 @@ public class VigoController : ControllerBase // If the bus is further away than the distance from the start of the trip to the stop, // it implies the bus is on the previous trip (or earlier). double distOnPrevTrip = estimate.Meters - closestCirculation.ShapeDistTraveled; - bool usePreviousShape = !isRunning && + usePreviousShape = !isRunning && !string.IsNullOrEmpty(closestCirculation.PreviousTripShapeId) && distOnPrevTrip > 0; @@ -364,7 +365,9 @@ public class VigoController : ControllerBase Distance = estimate.Meters }, CurrentPosition = currentPosition, - StopShapeIndex = stopShapeIndex + StopShapeIndex = stopShapeIndex, + IsPreviousTrip = usePreviousShape, + PreviousTripShapeId = usePreviousShape ? closestCirculation.PreviousTripShapeId : null }); usedTripIds.Add(closestCirculation.TripId); diff --git a/src/Costasdev.Busurbano.Backend/Types/ConsolidatedCirculation.cs b/src/Costasdev.Busurbano.Backend/Types/ConsolidatedCirculation.cs index ff6dbde..5b6373d 100644 --- a/src/Costasdev.Busurbano.Backend/Types/ConsolidatedCirculation.cs +++ b/src/Costasdev.Busurbano.Backend/Types/ConsolidatedCirculation.cs @@ -9,6 +9,8 @@ public class ConsolidatedCirculation public RealTimeData? RealTime { get; set; } public Position? CurrentPosition { get; set; } public int? StopShapeIndex { get; set; } + public bool IsPreviousTrip { get; set; } + public string? PreviousTripShapeId { get; set; } public string[] NextStreets { get; set; } = []; } diff --git a/src/gtfs_vigo_stops/stop_report.py b/src/gtfs_vigo_stops/stop_report.py index cee11ea..014e40f 100644 --- a/src/gtfs_vigo_stops/stop_report.py +++ b/src/gtfs_vigo_stops/stop_report.py @@ -149,11 +149,11 @@ def parse_trip_id_components(trip_id: str) -> Optional[Tuple[str, str, int]]: - XXX = line number (e.g., 003) - YYY = shift/internal ID (e.g., 001) - Z = trip number (e.g., 12) - + Supported formats: 1. ..._XXXYYY_Z (e.g. "C1 01SNA00_001001_18") 2. ..._XXXYYY-Z (e.g. "VIGO_20241122_003001-12") - + Returns tuple of (line, shift_id, trip_number) or None if parsing fails. """ try: @@ -166,7 +166,7 @@ def parse_trip_id_components(trip_id: str) -> Optional[Tuple[str, str, int]]: if len(parts) >= 2: shift_part = parts[-2] trip_num_str = parts[-1] - + if len(shift_part) == 6 and shift_part.isdigit() and trip_num_str.isdigit(): line = shift_part[:3] shift_id = shift_part[3:6] @@ -176,17 +176,17 @@ def parse_trip_id_components(trip_id: str) -> Optional[Tuple[str, str, int]]: # Try format 2: ..._XXXYYY-Z # The trip ID is the last part in format XXXYYY-Z trip_part = parts[-1] - + if "-" in trip_part: shift_part, trip_num_str = trip_part.split("-", 1) - + # shift_part should be 6 digits: XXXYYY if len(shift_part) == 6 and shift_part.isdigit(): line = shift_part[:3] # First 3 digits shift_id = shift_part[3:6] # Next 3 digits trip_number = int(trip_num_str) return (line, shift_id, trip_number) - + return None except (ValueError, IndexError): return None @@ -198,72 +198,72 @@ def build_trip_previous_shape_map( ) -> Dict[str, Optional[str]]: """ Build a mapping from trip_id to previous_trip_shape_id. - + Links trips based on trip ID structure (XXXYYY-Z) where trips with the same XXX (line) and YYY (shift) and sequential Z (trip numbers) are connected if the terminus of trip N matches the start of trip N+1. - + Args: trips: Dictionary of service_id -> list of trips stops_for_all_trips: Dictionary of trip_id -> list of stop times - + Returns: Dictionary mapping trip_id to previous_trip_shape_id (or None) """ trip_previous_shape: Dict[str, Optional[str]] = {} - + # Collect all trips across all services all_trips_list: List[TripLine] = [] for trip_list in trips.values(): all_trips_list.extend(trip_list) - + # Group trips by shift ID (line + shift combination) trips_by_shift: Dict[str, List[Tuple[TripLine, int, str, str]]] = {} - + for trip in all_trips_list: parsed = parse_trip_id_components(trip.trip_id) if not parsed: continue - + line, shift_id, trip_number = parsed shift_key = f"{line}{shift_id}" - + trip_stops = stops_for_all_trips.get(trip.trip_id) if not trip_stops or len(trip_stops) < 2: continue - + first_stop = trip_stops[0] last_stop = trip_stops[-1] - + if shift_key not in trips_by_shift: trips_by_shift[shift_key] = [] - + trips_by_shift[shift_key].append(( trip, trip_number, first_stop.stop_id, last_stop.stop_id )) - + # For each shift, sort trips by trip number and link consecutive trips for shift_key, shift_trips in trips_by_shift.items(): # Sort by trip number shift_trips.sort(key=lambda x: x[1]) - + # Link consecutive trips if their stops match for i in range(1, len(shift_trips)): current_trip, current_num, current_start_stop, _ = shift_trips[i] prev_trip, prev_num, _, prev_end_stop = shift_trips[i - 1] - + # Check if trips are consecutive (trip numbers differ by 1), # if previous trip's terminus matches current trip's start, # and if both trips have valid shape IDs - if (current_num == prev_num + 1 and - prev_end_stop == current_start_stop and + if (current_num == prev_num + 1 and + prev_end_stop == current_start_stop and prev_trip.shape_id and - current_trip.shape_id): + current_trip.shape_id): trip_previous_shape[current_trip.trip_id] = prev_trip.shape_id - + return trip_previous_shape @@ -317,8 +317,10 @@ def get_stop_arrivals(feed_dir: str, date: str) -> Dict[str, List[Dict[str, Any] logger.info(f"Precomputed stops for {len(stops_for_all_trips)} trips.") # Build mapping from trip_id to previous trip's shape_id - trip_previous_shape_map = build_trip_previous_shape_map(trips, stops_for_all_trips) - logger.info(f"Built previous trip shape mapping for {len(trip_previous_shape_map)} trips.") + trip_previous_shape_map = build_trip_previous_shape_map( + trips, stops_for_all_trips) + logger.info( + f"Built previous trip shape mapping for {len(trip_previous_shape_map)} trips.") # Load routes information routes = load_routes(feed_dir) @@ -463,9 +465,10 @@ def get_stop_arrivals(feed_dir: str, date: str) -> Dict[str, List[Dict[str, Any] next_streets = [] trip_id_fmt = "_".join(trip_id.split("_")[1:3]) - + # Get previous trip shape_id if available - previous_trip_shape_id = trip_previous_shape_map.get(trip_id, "") + previous_trip_shape_id = trip_previous_shape_map.get( + trip_id, "") stop_arrivals[stop_code].append( { |
