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

org.opentripplanner.routing.alertpatch.TransitAlert Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.routing.alertpatch;

import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.transit.model.framework.AbstractTransitEntity;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.framework.TransitBuilder;

/**
 * Internal representation of a GTFS-RT Service Alert or SIRI Situation Exchange (SX) message.
 * These are text descriptions of problems affecting specific stops, routes, or other components
 * of the transit system which will be displayed to users as text.
 * Although they have flags describing the effect of the problem described in the text, these
 * messages do not currently modify routing behavior on their own. They must be accompanied by
 * messages of other types to actually impact routing. However, there is ongoing discussion about
 * allowing Alerts to affect routing, especially for cases such as stop closure messages.
 */
public class TransitAlert extends AbstractTransitEntity {

  private final I18NString headerText;
  private final I18NString descriptionText;
  private final I18NString detailText;
  private final I18NString adviceText;
  // TODO OTP2 we wanted to merge the GTFS single alertUrl and the SIRI multiple URLs.
  //      However, GTFS URLs are one-per-language in a single object, and SIRI URLs are N objects with no translation.
  private final I18NString url;
  private final List siriUrls;
  //null means unknown
  private final String type;
  private final AlertSeverity severity;
  private final AlertCause cause;
  private final AlertEffect effect;
  //null means unknown
  private final Integer priority;
  private final ZonedDateTime creationTime;
  private final Integer version;
  private final ZonedDateTime updatedTime;
  private final String siriCodespace;
  private final Set entities;
  private final List timePeriods;

  TransitAlert(TransitAlertBuilder builder) {
    super(builder.getId());
    this.headerText = builder.headerText();
    this.descriptionText = builder.descriptionText();
    this.detailText = builder.detailText();
    this.adviceText = builder.adviceText();
    this.url = builder.url();
    this.siriUrls = List.copyOf(builder.siriUrls());
    this.type = builder.type();
    this.severity = builder.severity();
    this.cause = builder.cause();
    this.effect = builder.effect();
    this.priority = builder.priority();
    this.creationTime = builder.creationTime();
    this.version = builder.version();
    this.updatedTime = builder.updatedTime();
    this.siriCodespace = builder.siriCodespace();
    this.entities = Set.copyOf(builder.entities());
    this.timePeriods = List.copyOf(builder.timePeriods());
  }

  public static TransitAlertBuilder of(FeedScopedId id) {
    return new TransitAlertBuilder(id);
  }

  public Optional headerText() {
    return Optional.ofNullable(headerText);
  }

  public Optional descriptionText() {
    return Optional.ofNullable(descriptionText);
  }

  public I18NString detailText() {
    return detailText;
  }

  public I18NString adviceText() {
    return adviceText;
  }

  public Optional url() {
    return Optional.ofNullable(url);
  }

  public List siriUrls() {
    return siriUrls;
  }

  public String type() {
    return type;
  }

  /**
   * The severity of the alert.
   */
  public AlertSeverity severity() {
    return severity;
  }

  /**
   * The cause of the disruption.
   */
  public AlertCause cause() {
    return cause;
  }

  /**
   * The effect of the disruption.
   */
  public AlertEffect effect() {
    return effect;
  }

  public Integer priority() {
    return priority;
  }

  public ZonedDateTime creationTime() {
    return creationTime;
  }

  /**
   * Note: Only supported for TransitAlerts created from SIRI-SX messages
   *
   * @return Version as provided, or null
   */
  @Nullable
  public Integer version() {
    return version;
  }

  public ZonedDateTime updatedTime() {
    return updatedTime;
  }

  public String siriCodespace() {
    return siriCodespace;
  }

  public Set entities() {
    return entities;
  }

  public Collection timePeriods() {
    return timePeriods;
  }

  public boolean displayDuring(long startTimeSeconds, long endTimeSeconds) {
    for (TimePeriod timePeriod : timePeriods) {
      if (endTimeSeconds >= timePeriod.startTime) {
        if (timePeriod.endTime == 0 || startTimeSeconds < timePeriod.endTime) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Finds the first validity startTime from all timePeriods for this alert.
   *
   * @return First startDate for this Alert, null if 0 (not set)
   */
  @Nullable
  public Instant getEffectiveStartDate() {
    return timePeriods
      .stream()
      .map(timePeriod -> timePeriod.startTime)
      .min(Comparator.naturalOrder())
      .filter(startTime -> startTime > 0) //If 0, null should be returned
      .map(Instant::ofEpochSecond)
      .orElse(null);
  }

  /**
   * Finds the last validity endTime from all timePeriods for this alert. Returns null
   * if the validity is open-ended
   *
   * @return Last endDate for this Alert, null if open-ended
   */
  @Nullable
  public Instant getEffectiveEndDate() {
    return timePeriods
      .stream()
      .map(timePeriod -> timePeriod.endTime)
      .max(Comparator.naturalOrder())
      .filter(endTime -> endTime < TimePeriod.OPEN_ENDED) //If open-ended, null should be returned
      .map(Instant::ofEpochSecond)
      .orElse(null);
  }

  /**
   * Checks if the alert has a NO_SERVICE alert active at the requested time.
   * @param instant
   * @return
   */
  public boolean noServiceAt(Instant instant) {
    return (
      effect.equals(AlertEffect.NO_SERVICE) &&
      (getEffectiveStartDate() != null && getEffectiveStartDate().isBefore(instant)) &&
      (getEffectiveEndDate() == null || getEffectiveEndDate().isAfter(instant))
    );
  }

  @Override
  public boolean sameAs(TransitAlert other) {
    return (
      getId().equals(other.getId()) &&
      Objects.equals(headerText, other.headerText) &&
      Objects.equals(descriptionText, other.descriptionText) &&
      Objects.equals(detailText, other.detailText) &&
      Objects.equals(adviceText, other.adviceText) &&
      Objects.equals(url, other.url) &&
      Objects.equals(siriUrls, other.siriUrls) &&
      Objects.equals(type, other.type) &&
      Objects.equals(severity, other.severity) &&
      Objects.equals(cause, other.cause) &&
      Objects.equals(effect, other.effect) &&
      Objects.equals(priority, other.priority) &&
      Objects.equals(creationTime, other.creationTime) &&
      Objects.equals(version, other.version) &&
      Objects.equals(updatedTime, other.updatedTime) &&
      Objects.equals(siriCodespace, other.siriCodespace) &&
      Objects.equals(entities, other.entities) &&
      Objects.equals(timePeriods, other.timePeriods)
    );
  }

  @Override
  public TransitBuilder copy() {
    return new TransitAlertBuilder(this);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy