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

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

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.Set;
import java.util.function.Function;
import org.opentripplanner.model.plan.Leg;
import org.opentripplanner.model.plan.StopArrival;
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;
  }

  /**
   * Find and add alerts to the leg passed in.
   *
   * @param isFirstLeg Whether the leg is a first leg of the itinerary. This affects the matched
   *                   stop condition.
   */
  public void addTransitAlertsToLeg(Leg leg, boolean isFirstLeg) {
    // Alert alerts are only relevant for transit legs
    if (!leg.isTransitLeg()) {
      return;
    }

    Set departingStopConditions = isFirstLeg
      ? StopCondition.DEPARTURE
      : StopCondition.FIRST_DEPARTURE;

    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();

    if (fromStop instanceof RegularStop stop) {
      Collection alerts = getAlertsForStopAndRoute(stop, routeId);
      alerts.addAll(getAlertsForStopAndTrip(stop, tripId, serviceDate));
      alerts.addAll(getAlertsForRelatedStops(stop, transitAlertService::getStopAlerts));
      addTransitAlertsToLeg(leg, departingStopConditions, alerts, legStartTime, legEndTime);
    }
    if (toStop instanceof RegularStop stop) {
      Collection alerts = getAlertsForStopAndRoute(stop, routeId);
      alerts.addAll(getAlertsForStopAndTrip(stop, tripId, serviceDate));
      alerts.addAll(getAlertsForRelatedStops(stop, transitAlertService::getStopAlerts));
      addTransitAlertsToLeg(leg, StopCondition.ARRIVING, alerts, legStartTime, legEndTime);
    }

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

          ZonedDateTime stopArrival = visit.arrival;
          ZonedDateTime stopDeparture = visit.departure;

          addTransitAlertsToLeg(leg, StopCondition.PASSING, alerts, stopArrival, stopDeparture);
        }
      }
    }

    Collection alerts;

    // trips - alerts tagged on ServiceDate
    alerts = transitAlertService.getTripAlerts(leg.getTrip().getId(), serviceDate);
    addTransitAlertsToLeg(leg, alerts, legStartTime, legEndTime);

    // trips - alerts tagged on any date
    alerts = transitAlertService.getTripAlerts(leg.getTrip().getId(), null);
    addTransitAlertsToLeg(leg, alerts, legStartTime, legEndTime);

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

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

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

  /**
   * Add alerts for the leg, if they are valid for the duration of the leg, and if the stop
   * condition(s) match
   */
  private static void addTransitAlertsToLeg(
    Leg leg,
    Collection stopConditions,
    Collection alerts,
    ZonedDateTime fromTime,
    ZonedDateTime toTime
  ) {
    if (alerts != null) {
      for (TransitAlert alert : alerts) {
        if (alert.displayDuring(fromTime.toEpochSecond(), toTime.toEpochSecond())) {
          if (
            !alert.getStopConditions().isEmpty() && // Skip if stopConditions are not set for alert
            stopConditions != null &&
            !stopConditions.isEmpty()
          ) { // ...or specific stopConditions are not requested
            for (StopCondition stopCondition : stopConditions) {
              if (alert.getStopConditions().contains(stopCondition)) {
                leg.addAlert(alert);
                break; //Only add alert once
              }
            }
          } else {
            leg.addAlert(alert);
          }
        }
      }
    }
  }

  /**
   * Add alerts for the leg, if they are valid for the duration of the leg, without considering the
   * stop condition(s) of the alert
   */
  private static void addTransitAlertsToLeg(
    Leg leg,
    Collection alerts,
    ZonedDateTime fromTime,
    ZonedDateTime toTime
  ) {
    addTransitAlertsToLeg(leg, null, alerts, fromTime, toTime);
  }

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

  private Collection getAlertsForStopAndTrip(
    RegularStop stop,
    FeedScopedId tripId,
    LocalDate serviceDate
  ) {
    // Finding alerts for ServiceDate
    final Collection alerts = getAlertsForRelatedStops(
      stop,
      id -> transitAlertService.getStopAndTripAlerts(id, tripId, serviceDate)
    );

    // Finding alerts for any date
    alerts.addAll(
      getAlertsForRelatedStops(
        stop,
        id -> transitAlertService.getStopAndTripAlerts(id, tripId, null)
      )
    );

    return alerts;
  }

  /**
   * 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