All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.routing.algorithm.mapping.AlertToLegMapper Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.routing.algorithm.mapping;

import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import org.opentripplanner.ext.flex.FlexibleTransitLeg;
import org.opentripplanner.model.plan.AlertsAware;
import org.opentripplanner.model.plan.ScheduledTransitLeg;
import org.opentripplanner.model.plan.StopArrival;
import org.opentripplanner.model.plan.TransitLeg;
import org.opentripplanner.routing.alertpatch.StopCondition;
import org.opentripplanner.routing.alertpatch.TransitAlert;
import org.opentripplanner.routing.services.TransitAlertService;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.site.MultiModalStation;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.site.Station;
import org.opentripplanner.transit.model.site.StopLocation;

/**
 * This class is responsible for finding and adding transit alerts to individual transit legs.
 */
public class AlertToLegMapper {

  private final TransitAlertService transitAlertService;

  private final Function getMultiModalStation;

  public AlertToLegMapper(
    TransitAlertService transitAlertService,
    Function getMultiModalStation
  ) {
    this.transitAlertService = transitAlertService;
    this.getMultiModalStation = getMultiModalStation;
  }

  /**
   * Takes the (immutable) leg and returns a copy of it with the alerts attached.
   *
   * @param isFirstLeg Whether the leg is a first leg of the itinerary. This affects the matched
   *                   stop condition.
   */
  public TransitLeg decorateWithAlerts(TransitLeg leg, boolean isFirstLeg) {
    ZonedDateTime legStartTime = leg.getStartTime();
    ZonedDateTime legEndTime = leg.getEndTime();
    StopLocation fromStop = leg.getFrom() == null ? null : leg.getFrom().stop;
    StopLocation toStop = leg.getTo() == null ? null : leg.getTo().stop;

    FeedScopedId routeId = leg.getRoute().getId();
    FeedScopedId tripId = leg.getTrip().getId();
    LocalDate serviceDate = leg.getServiceDate();

    var totalAlerts = new HashSet();

    if (fromStop instanceof RegularStop stop) {
      Set stopConditions = isFirstLeg
        ? StopCondition.FIRST_DEPARTURE
        : StopCondition.DEPARTURE;

      Collection alerts = getAlertsForStopAndRoute(stop, routeId, stopConditions);
      alerts.addAll(getAlertsForStopAndTrip(stop, tripId, serviceDate, stopConditions));
      alerts.addAll(
        getAlertsForRelatedStops(stop, id -> transitAlertService.getStopAlerts(id, stopConditions))
      );
      totalAlerts.addAll(filterAlertsByTime(alerts, legStartTime, legEndTime));
    }
    if (toStop instanceof RegularStop stop) {
      Set stopConditions = StopCondition.ARRIVING;
      Collection alerts = getAlertsForStopAndRoute(stop, routeId, stopConditions);
      alerts.addAll(getAlertsForStopAndTrip(stop, tripId, serviceDate, stopConditions));
      alerts.addAll(
        getAlertsForRelatedStops(stop, id -> transitAlertService.getStopAlerts(id, stopConditions))
      );
      totalAlerts.addAll(filterAlertsByTime(alerts, legStartTime, legEndTime));
    }

    if (leg.getIntermediateStops() != null) {
      Set stopConditions = StopCondition.PASSING;
      for (StopArrival visit : leg.getIntermediateStops()) {
        if (visit.place.stop instanceof RegularStop stop) {
          Collection alerts = getAlertsForStopAndRoute(stop, routeId, stopConditions);
          alerts.addAll(getAlertsForStopAndTrip(stop, tripId, serviceDate, stopConditions));
          alerts.addAll(
            getAlertsForRelatedStops(stop, id ->
              transitAlertService.getStopAlerts(id, stopConditions)
            )
          );

          ZonedDateTime stopArrival = visit.arrival.scheduledTime();
          ZonedDateTime stopDeparture = visit.departure.scheduledTime();

          totalAlerts.addAll(filterAlertsByTime(alerts, stopArrival, stopDeparture));
        }
      }
    }

    Collection alerts;

    // trips
    alerts = transitAlertService.getTripAlerts(leg.getTrip().getId(), serviceDate);
    totalAlerts.addAll(filterAlertsByTime(alerts, legStartTime, legEndTime));

    // route
    alerts = transitAlertService.getRouteAlerts(leg.getRoute().getId());
    totalAlerts.addAll(filterAlertsByTime(alerts, legStartTime, legEndTime));

    // agency
    alerts = transitAlertService.getAgencyAlerts(leg.getAgency().getId());
    totalAlerts.addAll(filterAlertsByTime(alerts, legStartTime, legEndTime));

    // Filter alerts when there are multiple timePeriods for each alert
    totalAlerts.removeIf(alert ->
      !alert.displayDuring(leg.getStartTime().toEpochSecond(), leg.getEndTime().toEpochSecond())
    );

    return leg.decorateWithAlerts(Set.copyOf(totalAlerts));
  }

  /**
   * Filter alerts if they are valid for the duration of the leg.
   */
  private static List filterAlertsByTime(
    Collection alerts,
    ZonedDateTime fromTime,
    ZonedDateTime toTime
  ) {
    return alerts
      .stream()
      .filter(alert -> alert.displayDuring(fromTime.toEpochSecond(), toTime.toEpochSecond()))
      .toList();
  }

  private Collection getAlertsForStopAndRoute(
    RegularStop stop,
    FeedScopedId routeId,
    Set stopConditions
  ) {
    return getAlertsForRelatedStops(stop, id ->
      transitAlertService.getStopAndRouteAlerts(id, routeId, stopConditions)
    );
  }

  private Collection getAlertsForStopAndTrip(
    RegularStop stop,
    FeedScopedId tripId,
    LocalDate serviceDate,
    Set stopConditions
  ) {
    return getAlertsForRelatedStops(stop, id ->
      transitAlertService.getStopAndTripAlerts(id, tripId, serviceDate, stopConditions)
    );
  }

  /**
   * Find alerts, which are for the stop, its parent(s) and siblings, using a provided function for
   * finding alerts for those stops. This can be used to only find eg. alerts that are valid for
   * only a specific route at that stop.
   */
  private Collection getAlertsForRelatedStops(
    RegularStop stop,
    Function> getAlertsForStop
  ) {
    if (stop == null) {
      return new ArrayList<>();
    }

    Collection alertsForStop = getAlertsForStop.apply(stop.getId());
    if (alertsForStop == null) {
      alertsForStop = new HashSet<>();
    }

    if (stop.isPartOfStation()) {
      // Also check parent
      final Station parentStation = stop.getParentStation();
      Collection parentStopAlerts = getAlertsForStop.apply(parentStation.getId());
      if (parentStopAlerts != null) {
        alertsForStop.addAll(parentStopAlerts);
      }

      // ...and siblings - platform may have been changed
      for (var siblingStop : parentStation.getChildStops()) {
        if (!stop.getId().equals(siblingStop.getId())) {
          Collection siblingAlerts = getAlertsForStop.apply(parentStation.getId());
          if (siblingAlerts != null) {
            alertsForStop.addAll(siblingAlerts);
          }
        }
      }

      // Also check multimodal parent
      MultiModalStation multiModalStation = getMultiModalStation.apply(parentStation);
      if (multiModalStation != null) {
        Collection multimodalStopAlerts = getAlertsForStop.apply(
          multiModalStation.getId()
        );
        if (multimodalStopAlerts != null) {
          alertsForStop.addAll(multimodalStopAlerts);
        }
      }
    }
    return alertsForStop;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy