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

org.opentripplanner.routing.algorithm.RoutingWorker Maven / Gradle / Ivy

package org.opentripplanner.routing.algorithm;

import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import org.opentripplanner.model.plan.Itinerary;
import org.opentripplanner.model.plan.PagingSearchWindowAdjuster;
import org.opentripplanner.routing.algorithm.filterchain.ItineraryListFilterChain;
import org.opentripplanner.routing.algorithm.mapping.RoutingRequestToFilterChainMapper;
import org.opentripplanner.routing.algorithm.mapping.RoutingResponseMapper;
import org.opentripplanner.routing.algorithm.raptoradapter.router.AdditionalSearchDays;
import org.opentripplanner.routing.algorithm.raptoradapter.router.FilterTransitWhenDirectModeIsEmpty;
import org.opentripplanner.routing.algorithm.raptoradapter.router.TransitRouter;
import org.opentripplanner.routing.algorithm.raptoradapter.router.street.DirectFlexRouter;
import org.opentripplanner.routing.algorithm.raptoradapter.router.street.DirectStreetRouter;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.DateMapper;
import org.opentripplanner.routing.api.request.RoutingRequest;
import org.opentripplanner.routing.api.response.RoutingError;
import org.opentripplanner.routing.api.response.RoutingResponse;
import org.opentripplanner.routing.error.RoutingValidationException;
import org.opentripplanner.routing.framework.DebugTimingAggregator;
import org.opentripplanner.standalone.config.RouterConfig;
import org.opentripplanner.standalone.server.Router;
import org.opentripplanner.transit.raptor.api.request.RaptorTuningParameters;
import org.opentripplanner.transit.raptor.api.request.SearchParams;
import org.opentripplanner.util.OTPFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Does a complete transit search, including access and egress legs.
 * 

* This class has a request scope, hence the "Worker" name. */ public class RoutingWorker { private static final Logger LOG = LoggerFactory.getLogger(RoutingWorker.class); /** An object that accumulates profiling and debugging info for inclusion in the response. */ public final DebugTimingAggregator debugTimingAggregator = new DebugTimingAggregator(); public final PagingSearchWindowAdjuster pagingSearchWindowAdjuster; private final RoutingRequest request; private final Router router; /** * The transit service time-zero normalized for the current search. All transit times are * relative to a "time-zero". This enables us to use an integer(small memory footprint). The * times are number for seconds past the {@code transitSearchTimeZero}. In the internal model * all times are stored relative to the {@link org.opentripplanner.model.calendar.ServiceDate}, * but to be able to compare trip times for different service days we normalize all times by * calculating an offset. Now all times for the selected trip patterns become relative to the * {@code transitSearchTimeZero}. */ private final ZonedDateTime transitSearchTimeZero; private SearchParams raptorSearchParamsUsed = null; private Itinerary firstRemovedItinerary = null; private final AdditionalSearchDays additionalSearchDays; public RoutingWorker(Router router, RoutingRequest request, ZoneId zoneId) { request.applyPageCursor(); this.request = request; this.router = router; this.transitSearchTimeZero = DateMapper.asStartOfService(request.getDateTime(), zoneId); this.pagingSearchWindowAdjuster = createPagingSearchWindowAdjuster(router.routerConfig); this.additionalSearchDays = createAdditionalSearchDays( router.routerConfig.raptorTuningParameters(), zoneId, request ); } public RoutingResponse route() { // If no direct mode is set, then we set one. // See {@link FilterTransitWhenDirectModeIsEmpty} var emptyDirectModeHandler = new FilterTransitWhenDirectModeIsEmpty(request.modes); request.modes.directMode = emptyDirectModeHandler.resolveDirectMode(); this.debugTimingAggregator.finishedPrecalculating(); var itineraries = Collections.synchronizedList(new ArrayList()); var routingErrors = Collections.synchronizedSet(new HashSet()); if (OTPFeature.ParallelRouting.isOn()) { try { CompletableFuture.allOf( CompletableFuture.runAsync(() -> routeDirectStreet(itineraries, routingErrors)), CompletableFuture.runAsync(() -> routeDirectFlex(itineraries, routingErrors)), CompletableFuture.runAsync(() -> routeTransit(itineraries, routingErrors)) ).join(); } catch (CompletionException e) { RoutingValidationException.unwrapAndRethrowCompletionException(e); } } else { // Direct street routing routeDirectStreet(itineraries, routingErrors); // Direct flex routing routeDirectFlex(itineraries, routingErrors); // Transit routing routeTransit(itineraries, routingErrors); } debugTimingAggregator.finishedRouting(); // Filter itineraries ItineraryListFilterChain filterChain = RoutingRequestToFilterChainMapper.createFilterChain( request.getItinerariesSortOrder(), request.itineraryFilters, request.numItineraries, filterOnLatestDepartureTime(), emptyDirectModeHandler.removeWalkAllTheWayResults(), request.maxNumberOfItinerariesCropHead(), it -> firstRemovedItinerary = it ); List filteredItineraries = filterChain.filter(itineraries); routingErrors.addAll(filterChain.getRoutingErrors()); if(LOG.isDebugEnabled()) { LOG.debug( "Return TripPlan with {} filtered itineraries out of {} total.", filteredItineraries.stream().filter(it -> !it.isFlaggedForDeletion()).count(), itineraries.size()); } this.debugTimingAggregator.finishedFiltering(); // Restore original directMode. request.modes.directMode = emptyDirectModeHandler.originalDirectMode(); // Adjust the search-window for the next search if the current search-window // is off (too few or too many results found). var searchWindowNextSearch = calculateSearchWindowNextSearch(filteredItineraries); return RoutingResponseMapper.map( request, transitSearchTimeZero, raptorSearchParamsUsed, searchWindowNextSearch, firstRemovedItinerary, filteredItineraries, routingErrors, debugTimingAggregator ); } /** * Filter itineraries away that depart after the latest-departure-time for depart after * search. These itineraries are a result of time-shifting the access leg and is needed for * the raptor to prune the results. These itineraries are often not ideal, but if they * pareto optimal for the "next" window, they will appear when a "next" search is performed. */ private Instant filterOnLatestDepartureTime() { if(!request.arriveBy && raptorSearchParamsUsed != null && raptorSearchParamsUsed.isSearchWindowSet() && raptorSearchParamsUsed.isEarliestDepartureTimeSet() ) { int ldt = raptorSearchParamsUsed.earliestDepartureTime() + raptorSearchParamsUsed.searchWindowInSeconds(); return transitSearchTimeZero.plusSeconds(ldt).toInstant(); } return null; } private void routeDirectStreet( List itineraries, Collection routingErrors ) { debugTimingAggregator.startedDirectStreetRouter(); try { itineraries.addAll(DirectStreetRouter.route(router, request)); } catch (RoutingValidationException e) { routingErrors.addAll(e.getRoutingErrors()); } finally { debugTimingAggregator.finishedDirectStreetRouter(); } } private void routeDirectFlex( List itineraries, Collection routingErrors ) { if (!OTPFeature.FlexRouting.isOn()) { return; } debugTimingAggregator.startedDirectFlexRouter(); try { itineraries.addAll(DirectFlexRouter.route(router, request, additionalSearchDays)); } catch (RoutingValidationException e) { routingErrors.addAll(e.getRoutingErrors()); } finally { debugTimingAggregator.finishedDirectFlexRouter(); } } private void routeTransit( List itineraries, Collection routingErrors ) { debugTimingAggregator.startedTransitRouting(); try { var transitResults = TransitRouter.route( request, router, transitSearchTimeZero, additionalSearchDays, debugTimingAggregator ); raptorSearchParamsUsed = transitResults.getSearchParams(); itineraries.addAll(transitResults.getItineraries()); } catch (RoutingValidationException e) { routingErrors.addAll(e.getRoutingErrors()); } finally { debugTimingAggregator.finishedTransitRouter(); } } private static AdditionalSearchDays createAdditionalSearchDays( RaptorTuningParameters raptorTuningParameters, ZoneId zoneId, RoutingRequest request ) { var searchDateTime = ZonedDateTime.ofInstant(request.getDateTime(), zoneId); var maxWindow = Duration.ofMinutes( raptorTuningParameters .dynamicSearchWindowCoefficients() .maxWinTimeMinutes() ); return new AdditionalSearchDays( request.arriveBy, searchDateTime, request.searchWindow, maxWindow, request.maxJourneyDuration ); } private Duration calculateSearchWindowNextSearch(List itineraries) { // No transit search performed if(raptorSearchParamsUsed == null) { return null; } var sw = Duration.ofSeconds(raptorSearchParamsUsed.searchWindowInSeconds()); // SearchWindow cropped -> decrease search-window if(firstRemovedItinerary != null) { Instant swStartTime = searchStartTime().plusSeconds(raptorSearchParamsUsed.earliestDepartureTime()); boolean cropSWHead = request.doCropSearchWindowAtTail(); Instant rmItineraryStartTime = firstRemovedItinerary.startTime().toInstant(); return pagingSearchWindowAdjuster.decreaseSearchWindow( sw, swStartTime, rmItineraryStartTime, cropSWHead ); } // (num-of-itineraries found <= numItineraries) -> increase or keep search-window else { int nRequested = request.numItineraries; int nFound = (int) itineraries.stream() .filter(it -> !it.isFlaggedForDeletion() && it.hasTransit()) .count(); return pagingSearchWindowAdjuster.increaseOrKeepSearchWindow(sw, nRequested, nFound); } } private Instant searchStartTime() { return transitSearchTimeZero.toInstant(); } private PagingSearchWindowAdjuster createPagingSearchWindowAdjuster(RouterConfig routerConfig) { var c = routerConfig.raptorTuningParameters().dynamicSearchWindowCoefficients(); return new PagingSearchWindowAdjuster( c.minWinTimeMinutes(), c.maxWinTimeMinutes(), routerConfig.transitTuningParameters().pagingSearchWindowAdjustments() ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy