Search in sources :

Example 1 with CallStatusEnumeration

use of uk.org.siri.siri20.CallStatusEnumeration in project OpenTripPlanner by opentripplanner.

the class TimetableHelper method createModifiedStopTimes.

/**
 * Apply the SIRI ET to the appropriate TripTimes from this Timetable.
 * Calculate new stoppattern based on single stop cancellations
 *
 * @param oldTimes
 * @param journey    SIRI-ET EstimatedVehicleJourney
 * @param trip
 * @param routingService
 * @return new copy of updated TripTimes after TripUpdate has been applied on TripTimes of trip
 * with the id specified in the trip descriptor of the TripUpdate; null if something
 * went wrong
 */
public static List<StopTime> createModifiedStopTimes(Timetable timetable, TripTimes oldTimes, EstimatedVehicleJourney journey, Trip trip, RoutingService routingService) {
    if (journey == null) {
        return null;
    }
    EstimatedVehicleJourney.EstimatedCalls journeyCalls = journey.getEstimatedCalls();
    if (journeyCalls == null) {
        return null;
    }
    List<EstimatedCall> estimatedCalls = journeyCalls.getEstimatedCalls();
    List<Stop> stops = createModifiedStops(timetable, journey, routingService);
    List<StopTime> modifiedStops = new ArrayList<>();
    ZonedDateTime departureDate = null;
    int numberOfRecordedCalls = (journey.getRecordedCalls() != null && journey.getRecordedCalls().getRecordedCalls() != null) ? journey.getRecordedCalls().getRecordedCalls().size() : 0;
    Set<Object> alreadyVisited = new HashSet<>();
    // modify updated stop-times
    for (int i = 0; i < stops.size(); i++) {
        Stop stop = stops.get(i);
        final StopTime stopTime = new StopTime();
        stopTime.setStop(stop);
        stopTime.setTrip(trip);
        stopTime.setStopSequence(i);
        stopTime.setDropOffType(timetable.pattern.stopPattern.dropoffs[i]);
        stopTime.setPickupType(timetable.pattern.stopPattern.pickups[i]);
        stopTime.setArrivalTime(oldTimes.getScheduledArrivalTime(i));
        stopTime.setDepartureTime(oldTimes.getScheduledDepartureTime(i));
        stopTime.setStopHeadsign(oldTimes.getHeadsign(i));
        // TODO: Do we need to set the StopTime.id?
        // stopTime.setId(oldTimes.getStopTimeIdByIndex(i));
        boolean foundMatch = false;
        if (i >= numberOfRecordedCalls) {
            for (EstimatedCall estimatedCall : estimatedCalls) {
                if (alreadyVisited.contains(estimatedCall)) {
                    continue;
                }
                if (departureDate == null) {
                    departureDate = (estimatedCall.getAimedDepartureTime() != null ? estimatedCall.getAimedDepartureTime() : estimatedCall.getAimedArrivalTime());
                }
                // Current stop is being updated
                boolean stopsMatchById = stop.getId().getId().equals(estimatedCall.getStopPointRef().getValue());
                if (!stopsMatchById && stop.isPartOfStation()) {
                    Stop alternativeStop = routingService.getStopForId(new FeedScopedId(stop.getId().getFeedId(), estimatedCall.getStopPointRef().getValue()));
                    if (alternativeStop != null && stop.isPartOfSameStationAs(alternativeStop)) {
                        stopsMatchById = true;
                        stopTime.setStop(alternativeStop);
                    }
                }
                if (stopsMatchById) {
                    foundMatch = true;
                    CallStatusEnumeration arrivalStatus = estimatedCall.getArrivalStatus();
                    if (arrivalStatus == CallStatusEnumeration.CANCELLED) {
                        stopTime.setDropOffType(PICKDROP_NONE);
                    } else if (estimatedCall.getArrivalBoardingActivity() == ArrivalBoardingActivityEnumeration.ALIGHTING) {
                        stopTime.setDropOffType(PICKDROP_SCHEDULED);
                    } else if (estimatedCall.getArrivalBoardingActivity() == ArrivalBoardingActivityEnumeration.NO_ALIGHTING) {
                        stopTime.setDropOffType(PICKDROP_NONE);
                    } else if (estimatedCall.getArrivalBoardingActivity() == null && i == 0) {
                        // First stop - default no pickup
                        stopTime.setDropOffType(PICKDROP_NONE);
                    }
                    CallStatusEnumeration departureStatus = estimatedCall.getDepartureStatus();
                    if (departureStatus == CallStatusEnumeration.CANCELLED) {
                        stopTime.setPickupType(PICKDROP_NONE);
                    } else if (estimatedCall.getDepartureBoardingActivity() == DepartureBoardingActivityEnumeration.BOARDING) {
                        stopTime.setPickupType(PICKDROP_SCHEDULED);
                    } else if (estimatedCall.getDepartureBoardingActivity() == DepartureBoardingActivityEnumeration.NO_BOARDING) {
                        stopTime.setPickupType(PICKDROP_NONE);
                    } else if (estimatedCall.getDepartureBoardingActivity() == null && i == (stops.size() - 1)) {
                        // Last stop - default no dropoff
                        stopTime.setPickupType(PICKDROP_NONE);
                    }
                    if (estimatedCall.getDestinationDisplaies() != null && !estimatedCall.getDestinationDisplaies().isEmpty()) {
                        NaturalLanguageStringStructure destinationDisplay = estimatedCall.getDestinationDisplaies().get(0);
                        stopTime.setStopHeadsign(destinationDisplay.getValue());
                    }
                    modifiedStops.add(stopTime);
                    alreadyVisited.add(estimatedCall);
                    break;
                }
            }
        }
        if (!foundMatch) {
            modifiedStops.add(stopTime);
        }
    }
    return modifiedStops;
}
Also used : NaturalLanguageStringStructure(uk.org.siri.siri20.NaturalLanguageStringStructure) EstimatedVehicleJourney(uk.org.siri.siri20.EstimatedVehicleJourney) Stop(org.opentripplanner.model.Stop) ArrayList(java.util.ArrayList) ZonedDateTime(java.time.ZonedDateTime) FeedScopedId(org.opentripplanner.model.FeedScopedId) EstimatedCall(uk.org.siri.siri20.EstimatedCall) CallStatusEnumeration(uk.org.siri.siri20.CallStatusEnumeration) StopTime(org.opentripplanner.model.StopTime) HashSet(java.util.HashSet)

Example 2 with CallStatusEnumeration

use of uk.org.siri.siri20.CallStatusEnumeration in project OpenTripPlanner by opentripplanner.

the class TimetableHelper method createUpdatedTripTimes.

/**
 * Apply the TripUpdate to the appropriate TripTimes from this Timetable. The existing TripTimes
 * must not be modified directly because they may be shared with the underlying
 * scheduledTimetable, or other updated Timetables. The {@link TimetableSnapshot} performs the
 * protective copying of this Timetable. It is not done in this update method to avoid
 * repeatedly cloning the same Timetable when several updates are applied to it at once. We
 * assume here that all trips in a timetable are from the same feed, which should always be the
 * case.
 *
 * @param journey  SIRI-ET EstimatedVehicleJourney
 * @param timeZone time zone of trip update
 * @param tripId
 * @return new copy of updated TripTimes after TripUpdate has been applied on TripTimes of trip
 * with the id specified in the trip descriptor of the TripUpdate; null if something
 * went wrong
 */
public static TripTimes createUpdatedTripTimes(final Graph graph, Timetable timetable, EstimatedVehicleJourney journey, TimeZone timeZone, FeedScopedId tripId) {
    if (journey == null) {
        return null;
    }
    int tripIndex = timetable.getTripIndex(tripId);
    if (tripIndex == -1) {
        LOG.debug("tripId {} not found in pattern.", tripId);
        return null;
    }
    final TripTimes existingTripTimes = timetable.getTripTimes(tripIndex);
    TripTimes oldTimes = new TripTimes(existingTripTimes);
    if (journey.isCancellation() != null && journey.isCancellation()) {
        oldTimes.cancel();
        return oldTimes;
    }
    EstimatedVehicleJourney.EstimatedCalls journeyEstimatedCalls = journey.getEstimatedCalls();
    EstimatedVehicleJourney.RecordedCalls journeyRecordedCalls = journey.getRecordedCalls();
    if (journeyEstimatedCalls == null) {
        return null;
    }
    List<EstimatedCall> estimatedCalls = journeyEstimatedCalls.getEstimatedCalls();
    List<RecordedCall> recordedCalls;
    if (journeyRecordedCalls != null) {
        recordedCalls = journeyRecordedCalls.getRecordedCalls();
    } else {
        recordedCalls = new ArrayList<>();
    }
    boolean stopPatternChanged = false;
    Stop[] modifiedStops = timetable.pattern.stopPattern.stops;
    Trip trip = getTrip(tripId, timetable);
    List<StopTime> modifiedStopTimes = createModifiedStopTimes(timetable, oldTimes, journey, trip, new RoutingService(graph));
    if (modifiedStopTimes == null) {
        return null;
    }
    TripTimes newTimes = new TripTimes(trip, modifiedStopTimes, graph.deduplicator);
    // Populate missing data from existing TripTimes
    newTimes.serviceCode = oldTimes.serviceCode;
    int callCounter = 0;
    ZonedDateTime departureDate = null;
    Set<Object> alreadyVisited = new HashSet<>();
    boolean isJourneyPredictionInaccurate = (journey.isPredictionInaccurate() != null && journey.isPredictionInaccurate());
    int departureFromPreviousStop = 0;
    int lastArrivalDelay = 0;
    int lastDepartureDelay = 0;
    for (Stop stop : modifiedStops) {
        boolean foundMatch = false;
        for (RecordedCall recordedCall : recordedCalls) {
            if (alreadyVisited.contains(recordedCall)) {
                continue;
            }
            // Current stop is being updated
            foundMatch = stop.getId().getId().equals(recordedCall.getStopPointRef().getValue());
            if (!foundMatch && stop.isPartOfStation()) {
                Stop alternativeStop = graph.index.getStopForId(new FeedScopedId(stop.getId().getFeedId(), recordedCall.getStopPointRef().getValue()));
                if (alternativeStop != null && stop.isPartOfSameStationAs(alternativeStop)) {
                    foundMatch = true;
                    stopPatternChanged = true;
                }
            }
            if (foundMatch) {
                if (departureDate == null) {
                    departureDate = recordedCall.getAimedDepartureTime();
                    if (departureDate == null) {
                        departureDate = recordedCall.getAimedArrivalTime();
                    }
                    if (oldTimes.getDepartureTime(0) > 86400) {
                        // The "departure-date" for this trip is set to "yesterday" (or before) even though it actually departs "today"
                        // calculate number of offset-days
                        int dayOffsetCount = oldTimes.getDepartureTime(0) / 86400;
                        departureDate = departureDate.minusDays(dayOffsetCount);
                    }
                }
                // Flag as recorded
                newTimes.setRecorded(callCounter, true);
                if (recordedCall.isCancellation() != null) {
                    newTimes.setCancelledStop(callCounter, recordedCall.isCancellation());
                }
                newTimes.setDropoffType(callCounter, timetable.pattern.stopPattern.dropoffs[callCounter]);
                newTimes.setPickupType(callCounter, timetable.pattern.stopPattern.pickups[callCounter]);
                int arrivalTime = newTimes.getArrivalTime(callCounter);
                int realtimeArrivalTime = arrivalTime;
                if (recordedCall.getActualArrivalTime() != null) {
                    realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getActualArrivalTime());
                } else if (recordedCall.getExpectedArrivalTime() != null) {
                    realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getExpectedArrivalTime());
                } else if (recordedCall.getAimedArrivalTime() != null) {
                    realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getAimedArrivalTime());
                }
                int arrivalDelay = realtimeArrivalTime - arrivalTime;
                newTimes.updateArrivalDelay(callCounter, arrivalDelay);
                lastArrivalDelay = arrivalDelay;
                int departureTime = newTimes.getDepartureTime(callCounter);
                int realtimeDepartureTime = departureTime;
                if (recordedCall.getActualDepartureTime() != null) {
                    realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getActualDepartureTime());
                } else if (recordedCall.getExpectedDepartureTime() != null) {
                    realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getExpectedDepartureTime());
                } else if (recordedCall.getAimedDepartureTime() != null) {
                    realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getAimedDepartureTime());
                }
                if (realtimeDepartureTime < realtimeArrivalTime) {
                    realtimeDepartureTime = realtimeArrivalTime;
                }
                int departureDelay = realtimeDepartureTime - departureTime;
                newTimes.updateDepartureDelay(callCounter, departureDelay);
                lastDepartureDelay = departureDelay;
                departureFromPreviousStop = newTimes.getDepartureTime(callCounter);
                alreadyVisited.add(recordedCall);
                break;
            }
        }
        if (!foundMatch) {
            for (EstimatedCall estimatedCall : estimatedCalls) {
                if (alreadyVisited.contains(estimatedCall)) {
                    continue;
                }
                // Current stop is being updated
                foundMatch = stop.getId().getId().equals(estimatedCall.getStopPointRef().getValue());
                if (!foundMatch && stop.isPartOfStation()) {
                    Stop alternativeStop = graph.index.getStopForId(new FeedScopedId(stop.getId().getFeedId(), estimatedCall.getStopPointRef().getValue()));
                    if (alternativeStop != null && stop.isPartOfSameStationAs(alternativeStop)) {
                        foundMatch = true;
                        stopPatternChanged = true;
                    }
                }
                if (foundMatch) {
                    if (departureDate == null) {
                        departureDate = estimatedCall.getAimedDepartureTime();
                        if (departureDate == null) {
                            departureDate = estimatedCall.getAimedArrivalTime();
                        }
                    }
                    if (estimatedCall.isCancellation() != null) {
                        newTimes.setCancelledStop(callCounter, estimatedCall.isCancellation());
                    }
                    boolean isCallPredictionInaccurate = estimatedCall.isPredictionInaccurate() != null && estimatedCall.isPredictionInaccurate();
                    // Set flag for inaccurate prediction if either call OR journey has inaccurate-flag set.
                    newTimes.setPredictionInaccurate(callCounter, (isJourneyPredictionInaccurate | isCallPredictionInaccurate));
                    // Update dropoff-/pickuptype only if status is cancelled
                    CallStatusEnumeration arrivalStatus = estimatedCall.getArrivalStatus();
                    if (arrivalStatus == CallStatusEnumeration.CANCELLED) {
                        newTimes.setDropoffType(callCounter, PICKDROP_NONE);
                    }
                    CallStatusEnumeration departureStatus = estimatedCall.getDepartureStatus();
                    if (departureStatus == CallStatusEnumeration.CANCELLED) {
                        newTimes.setPickupType(callCounter, PICKDROP_NONE);
                    }
                    int arrivalTime = newTimes.getArrivalTime(callCounter);
                    int realtimeArrivalTime = -1;
                    if (estimatedCall.getExpectedArrivalTime() != null) {
                        realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, estimatedCall.getExpectedArrivalTime());
                    } else if (estimatedCall.getAimedArrivalTime() != null) {
                        realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, estimatedCall.getAimedArrivalTime());
                    }
                    int departureTime = newTimes.getDepartureTime(callCounter);
                    int realtimeDepartureTime = departureTime;
                    if (estimatedCall.getExpectedDepartureTime() != null) {
                        realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, estimatedCall.getExpectedDepartureTime());
                    } else if (estimatedCall.getAimedDepartureTime() != null) {
                        realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, estimatedCall.getAimedDepartureTime());
                    }
                    if (realtimeArrivalTime == -1) {
                        realtimeArrivalTime = realtimeDepartureTime;
                    }
                    if (realtimeDepartureTime < realtimeArrivalTime) {
                        realtimeDepartureTime = realtimeArrivalTime;
                    }
                    int arrivalDelay = realtimeArrivalTime - arrivalTime;
                    newTimes.updateArrivalDelay(callCounter, arrivalDelay);
                    lastArrivalDelay = arrivalDelay;
                    int departureDelay = realtimeDepartureTime - departureTime;
                    newTimes.updateDepartureDelay(callCounter, departureDelay);
                    lastDepartureDelay = departureDelay;
                    departureFromPreviousStop = newTimes.getDepartureTime(callCounter);
                    alreadyVisited.add(estimatedCall);
                    break;
                }
            }
        }
        if (!foundMatch) {
            if (timetable.pattern.stopPattern.pickups[callCounter] == PICKDROP_NONE && timetable.pattern.stopPattern.dropoffs[callCounter] == PICKDROP_NONE) {
                // When newTimes contains stops without pickup/dropoff - set both arrival/departure to previous stop's departure
                // This necessary to accommodate the case when delay is reduced/eliminated between to stops with pickup/dropoff, and
                // multiple non-pickup/dropoff stops are in between.
                newTimes.updateArrivalTime(callCounter, departureFromPreviousStop);
                newTimes.updateDepartureTime(callCounter, departureFromPreviousStop);
            } else {
                int arrivalDelay = lastArrivalDelay;
                int departureDelay = lastDepartureDelay;
                if (lastArrivalDelay == 0 && lastDepartureDelay == 0) {
                    // No match has been found yet (i.e. still in RecordedCalls) - keep existing delays
                    arrivalDelay = existingTripTimes.getArrivalDelay(callCounter);
                    departureDelay = existingTripTimes.getDepartureDelay(callCounter);
                }
                newTimes.updateArrivalDelay(callCounter, arrivalDelay);
                newTimes.updateDepartureDelay(callCounter, departureDelay);
            }
            departureFromPreviousStop = newTimes.getDepartureTime(callCounter);
        }
        callCounter++;
    }
    if (stopPatternChanged) {
        // This update modified stopPattern
        newTimes.setRealTimeState(RealTimeState.MODIFIED);
    } else {
        // This is the first update, and StopPattern has not been changed
        newTimes.setRealTimeState(RealTimeState.UPDATED);
    }
    if (journey.isCancellation() != null && journey.isCancellation()) {
        LOG.debug("Trip is cancelled");
        newTimes.cancel();
    }
    if (!newTimes.timesIncreasing()) {
        LOG.info("TripTimes are non-increasing after applying SIRI delay propagation - LineRef {}, TripId {}.", journey.getLineRef().getValue(), tripId);
        return null;
    }
    if (newTimes.getNumStops() != timetable.pattern.stopPattern.stops.length) {
        return null;
    }
    LOG.debug("A valid TripUpdate object was applied using the Timetable class update method.");
    return newTimes;
}
Also used : Trip(org.opentripplanner.model.Trip) EstimatedVehicleJourney(uk.org.siri.siri20.EstimatedVehicleJourney) Stop(org.opentripplanner.model.Stop) RoutingService(org.opentripplanner.routing.RoutingService) RecordedCall(uk.org.siri.siri20.RecordedCall) ZonedDateTime(java.time.ZonedDateTime) TripTimes(org.opentripplanner.routing.trippattern.TripTimes) FeedScopedId(org.opentripplanner.model.FeedScopedId) EstimatedCall(uk.org.siri.siri20.EstimatedCall) CallStatusEnumeration(uk.org.siri.siri20.CallStatusEnumeration) StopTime(org.opentripplanner.model.StopTime) HashSet(java.util.HashSet)

Aggregations

ZonedDateTime (java.time.ZonedDateTime)2 HashSet (java.util.HashSet)2 FeedScopedId (org.opentripplanner.model.FeedScopedId)2 Stop (org.opentripplanner.model.Stop)2 StopTime (org.opentripplanner.model.StopTime)2 CallStatusEnumeration (uk.org.siri.siri20.CallStatusEnumeration)2 EstimatedCall (uk.org.siri.siri20.EstimatedCall)2 EstimatedVehicleJourney (uk.org.siri.siri20.EstimatedVehicleJourney)2 ArrayList (java.util.ArrayList)1 Trip (org.opentripplanner.model.Trip)1 RoutingService (org.opentripplanner.routing.RoutingService)1 TripTimes (org.opentripplanner.routing.trippattern.TripTimes)1 NaturalLanguageStringStructure (uk.org.siri.siri20.NaturalLanguageStringStructure)1 RecordedCall (uk.org.siri.siri20.RecordedCall)1