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

org.opentripplanner.ext.flex.FlexRouter Maven / Gradle / Ivy

package org.opentripplanner.ext.flex;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.opentripplanner.common.model.T2;
import org.opentripplanner.ext.flex.flexpathcalculator.DirectFlexPathCalculator;
import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator;
import org.opentripplanner.ext.flex.flexpathcalculator.StreetFlexPathCalculator;
import org.opentripplanner.ext.flex.template.FlexAccessTemplate;
import org.opentripplanner.ext.flex.template.FlexEgressTemplate;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.model.plan.Itinerary;
import org.opentripplanner.routing.algorithm.mapping.GraphPathToItineraryMapper;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graphfinder.NearbyStop;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.service.TransitService;
import org.opentripplanner.util.time.ServiceDateUtils;

public class FlexRouter {

  /* Transit data */

  private final Graph graph;
  private final TransitService transitService;
  private final FlexParameters config;
  private final Collection streetAccesses;
  private final Collection streetEgresses;
  private final FlexIndex flexIndex;
  private final FlexPathCalculator accessFlexPathCalculator;
  private final FlexPathCalculator egressFlexPathCalculator;
  private final GraphPathToItineraryMapper graphPathToItineraryMapper;

  /* Request data */
  private final ZonedDateTime startOfTime;
  private final int departureTime;
  private final boolean arriveBy;

  private final FlexServiceDate[] dates;

  /* State */
  private List flexAccessTemplates = null;
  private List flexEgressTemplates = null;

  public FlexRouter(
    Graph graph,
    TransitService transitService,
    FlexParameters config,
    Instant searchInstant,
    boolean arriveBy,
    int additionalPastSearchDays,
    int additionalFutureSearchDays,
    Collection streetAccesses,
    Collection egressTransfers
  ) {
    this.graph = graph;
    this.transitService = transitService;
    this.config = config;
    this.streetAccesses = streetAccesses;
    this.streetEgresses = egressTransfers;
    this.flexIndex = transitService.getFlexIndex();
    this.graphPathToItineraryMapper =
      new GraphPathToItineraryMapper(
        transitService.getTimeZone(),
        graph.streetNotesService,
        graph.ellipsoidToGeoidDifference
      );

    if (graph.hasStreets) {
      this.accessFlexPathCalculator = new StreetFlexPathCalculator(false);
      this.egressFlexPathCalculator = new StreetFlexPathCalculator(true);
    } else {
      // this is only really useful in tests. in real world scenarios you're unlikely to get useful
      // results if you don't have streets
      this.accessFlexPathCalculator = new DirectFlexPathCalculator();
      this.egressFlexPathCalculator = new DirectFlexPathCalculator();
    }

    ZoneId tz = transitService.getTimeZone();
    LocalDate searchDate = LocalDate.ofInstant(searchInstant, tz);
    this.startOfTime = ServiceDateUtils.asStartOfService(searchDate, tz);
    this.departureTime = ServiceDateUtils.secondsSinceStartOfTime(startOfTime, searchInstant);
    this.arriveBy = arriveBy;

    int totalDays = additionalPastSearchDays + 1 + additionalFutureSearchDays;

    this.dates = new FlexServiceDate[totalDays];

    for (int d = -additionalPastSearchDays; d <= additionalFutureSearchDays; ++d) {
      LocalDate date = searchDate.plusDays(d);
      int index = d + additionalPastSearchDays;
      dates[index] =
        new FlexServiceDate(
          date,
          ServiceDateUtils.secondsSinceStartOfTime(startOfTime, date),
          transitService.getServiceCodesRunningForDate(date)
        );
    }
  }

  public Collection createFlexOnlyItineraries() {
    calculateFlexAccessTemplates();
    calculateFlexEgressTemplates();

    Multimap streetEgressByStop = HashMultimap.create();
    streetEgresses.forEach(it -> streetEgressByStop.put(it.stop, it));

    Collection itineraries = new ArrayList<>();

    for (FlexAccessTemplate template : this.flexAccessTemplates) {
      StopLocation transferStop = template.getTransferStop();
      if (
        this.flexEgressTemplates.stream()
          .anyMatch(t -> t.getAccessEgressStop().equals(transferStop))
      ) {
        for (NearbyStop egress : streetEgressByStop.get(transferStop)) {
          Itinerary itinerary = template.createDirectGraphPath(
            egress,
            arriveBy,
            departureTime,
            startOfTime,
            graphPathToItineraryMapper
          );
          if (itinerary != null) {
            itineraries.add(itinerary);
          }
        }
      }
    }

    return itineraries;
  }

  public Collection createFlexAccesses() {
    calculateFlexAccessTemplates();

    return this.flexAccessTemplates.stream()
      .flatMap(template -> template.createFlexAccessEgressStream(graph, transitService))
      .collect(Collectors.toList());
  }

  public Collection createFlexEgresses() {
    calculateFlexEgressTemplates();

    return this.flexEgressTemplates.stream()
      .flatMap(template -> template.createFlexAccessEgressStream(graph, transitService))
      .collect(Collectors.toList());
  }

  private void calculateFlexAccessTemplates() {
    if (this.flexAccessTemplates != null) {
      return;
    }

    // Fetch the closest flexTrips reachable from the access stops
    this.flexAccessTemplates =
      getClosestFlexTrips(streetAccesses, true)
        // For each date the router has data for
        .flatMap(t2 ->
          Arrays
            .stream(dates)
            // Discard if service is not running on date
            .filter(date -> date.isFlexTripRunning(t2.second, this.transitService))
            // Create templates from trip, boarding at the nearbyStop
            .flatMap(date ->
              t2.second.getFlexAccessTemplates(t2.first, date, accessFlexPathCalculator, config)
            )
        )
        .collect(Collectors.toList());
  }

  private void calculateFlexEgressTemplates() {
    if (this.flexEgressTemplates != null) {
      return;
    }

    // Fetch the closest flexTrips reachable from the egress stops
    this.flexEgressTemplates =
      getClosestFlexTrips(streetEgresses, false)
        // For each date the router has data for
        .flatMap(t2 ->
          Arrays
            .stream(dates)
            // Discard if service is not running on date
            .filter(date -> date.isFlexTripRunning(t2.second, this.transitService))
            // Create templates from trip, alighting at the nearbyStop
            .flatMap(date ->
              t2.second.getFlexEgressTemplates(t2.first, date, egressFlexPathCalculator, config)
            )
        )
        .collect(Collectors.toList());
  }

  private Stream>> getClosestFlexTrips(
    Collection nearbyStops,
    boolean pickup
  ) {
    // Find all trips reachable from the nearbyStops
    Stream>> flexTripsReachableFromNearbyStops = nearbyStops
      .stream()
      .flatMap(accessEgress ->
        flexIndex
          .getFlexTripsByStop(accessEgress.stop)
          .stream()
          .filter(flexTrip ->
            pickup
              ? flexTrip.isBoardingPossible(accessEgress)
              : flexTrip.isAlightingPossible(accessEgress)
          )
          .map(flexTrip -> new T2<>(accessEgress, flexTrip))
      );

    // Group all (NearbyStop, FlexTrip) tuples by flexTrip
    Collection>>> groupedReachableFlexTrips = flexTripsReachableFromNearbyStops
      .collect(Collectors.groupingBy(t2 -> t2.second))
      .values();

    // Get the tuple with least walking time from each group
    return groupedReachableFlexTrips
      .stream()
      .map(t2s ->
        t2s.stream().min(Comparator.comparingLong(t2 -> t2.first.state.getElapsedTimeSeconds()))
      )
      .flatMap(Optional::stream);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy