org.opentripplanner.routing.api.request.RoutingRequest Maven / Gradle / Ivy
package org.opentripplanner.routing.api.request;
import static org.opentripplanner.util.time.DurationUtils.durationInSeconds;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import javax.annotation.Nonnull;
import org.geotools.geojson.geom.GeometryJSON;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.GeometryFactory;
import org.opentripplanner.api.common.LocationStringParser;
import org.opentripplanner.api.common.Message;
import org.opentripplanner.api.common.ParameterException;
import org.opentripplanner.common.geometry.SphericalDistanceLibrary;
import org.opentripplanner.ext.dataoverlay.api.DataOverlayParameters;
import org.opentripplanner.model.FeedScopedId;
import org.opentripplanner.model.GenericLocation;
import org.opentripplanner.model.Route;
import org.opentripplanner.model.TransitMode;
import org.opentripplanner.model.modes.AllowedTransitMode;
import org.opentripplanner.model.plan.SortOrder;
import org.opentripplanner.model.plan.pagecursor.PageCursor;
import org.opentripplanner.model.plan.pagecursor.PageType;
import org.opentripplanner.routing.algorithm.filterchain.ItineraryListFilter;
import org.opentripplanner.routing.algorithm.transferoptimization.api.TransferOptimizationParameters;
import org.opentripplanner.routing.core.BicycleOptimizeType;
import org.opentripplanner.routing.core.RouteMatcher;
import org.opentripplanner.routing.core.RoutingContext;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.core.TraverseModeSet;
import org.opentripplanner.routing.core.intersection_model.IntersectionTraversalCostModel;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.impl.DurationComparator;
import org.opentripplanner.routing.impl.PathComparator;
import org.opentripplanner.routing.spt.DominanceFunction;
import org.opentripplanner.routing.spt.GraphPath;
import org.opentripplanner.routing.spt.ShortestPathTree;
import org.opentripplanner.routing.vehicle_rental.RentalVehicleType.FormFactor;
import org.opentripplanner.routing.vehicle_rental.VehicleRentalStation;
import org.opentripplanner.util.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A trip planning request. Some parameters may not be honored by the trip planner for some or all
* itineraries. For example, maxWalkDistance may be relaxed if the alternative is to not provide a
* route.
*
* All defaults should be specified here in the RoutingRequest, NOT as annotations on query
* parameters in web services that create RoutingRequests. This establishes a priority chain for
* default values:
* RoutingRequest field initializers, then JSON router config, then query parameters.
*
* @Deprecated tag is added to all parameters that are not currently functional in either the Raptor router or other
* non-transit routing (walk, bike, car etc.)
*
* TODO OTP2 Many fields are deprecated in this class, the reason is documented in the
* RoutingResource class, not here. Eventually the field will be removed from this
* class, but we want to keep it in the RoutingResource as long as we support the
* REST API.
*/
public class RoutingRequest implements AutoCloseable, Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(RoutingRequest.class);
private static final long NOW_THRESHOLD_SEC = durationInSeconds("15h");
/* FIELDS UNIQUELY IDENTIFYING AN SPT REQUEST */
/** The complete list of incoming query parameters. */
public final HashMap parameters = new HashMap();
/** The start location */
public GenericLocation from;
/** The end location */
public GenericLocation to;
/**
* If true, the tree will be allowed to grow in all directions, rather than being directed
* toward a single target. This parameter only apply to access/egress AStar searches,
* not transit searches in Raptor.
*
* @deprecated TODO OTP2 - This looks like an A Star implementation detail. Should be moved to
* - an A Star specific request class
*/
@Deprecated
public boolean oneToMany = false;
/**
* An ordered list of intermediate locations to be visited.
*
* @deprecated TODO OTP2 - Regression. Not currently working in OTP2. Must be re-implemented
* - using raptor.
*/
@Deprecated
public List intermediatePlaces;
/**
* This is the maximum duration in seconds for a direct street search. This is a performance
* limit and should therefore be set high. Results close to the limit are not guaranteed to be
* optimal. Use filters to limit what is presented to the client.
*
* @see ItineraryListFilter
*/
public double maxDirectStreetDurationSeconds = Duration.ofHours(4).toSeconds();
/**
* This is the maximum duration in seconds for access/egress street searches. This is a
* performance limit and should therefore be set high. Results close to the limit are not
* guaranteed to be optimal. Use filters to limit what is presented to the client.
*
* @see ItineraryListFilter
*/
public double maxAccessEgressDurationSeconds = Duration.ofMinutes(45).toSeconds();
/**
* Override the settings in maxAccessEgressDurationSeconds for specific street modes. This is
* done because some street modes searches are much more resource intensive than others.
*/
public Map maxAccessEgressDurationSecondsForMode = new HashMap<>();
/**
* The access/egress/direct/transit modes allowed for this main request. The parameter
* "streetSubRequestModes" below is used for a single A Star sub request.
*
* // TODO OTP2 Street routing requests should eventually be split into its own request class.
*/
public RequestModes modes = new RequestModes(
StreetMode.WALK,
StreetMode.WALK,
StreetMode.WALK,
StreetMode.WALK,
AllowedTransitMode.getAllTransitModes()
);
/**
* The set of TraverseModes allowed when doing creating sub requests and doing street routing.
* // TODO OTP2 Street routing requests should eventually be split into its own request class.
*/
public TraverseModeSet streetSubRequestModes = new TraverseModeSet(TraverseMode.WALK); // defaults in constructor overwrite this
/**
* The set of characteristics that the user wants to optimize for -- defaults to SAFE.
*/
public BicycleOptimizeType bicycleOptimizeType = BicycleOptimizeType.SAFE;
/**
* The epoch date/time in seconds that the trip should depart (or arrive, for requests where
* arriveBy is true)
*/
private Instant dateTime = Instant.now();
/**
* This is the time/duration in seconds from the earliest-departure-time(EDT) to
* latest-departure-time(LDT). In case of a reverse search it will be the time from earliest
* to latest arrival time (LAT - EAT).
*
* All optimal travels that depart within the search window is guarantied to be found.
*
* This is sometimes referred to as the Range Raptor Search Window - but could be used in a none
* Transit search as well; Hence this is named search-window and not raptor-search-window. Do
* not confuse this with the travel-window, which is the time between EDT to LAT.
*
* Use {@code null} to unset, and {@link Duration#ZERO} to do one Raptor iteration. The value
* is dynamically assigned a suitable value, if not set. In a small to medium size operation
* you may use a fixed value, like 60 minutes. If you have a mixture of high frequency cities
* routes and infrequent long distant journeys, the best option is normally to use the dynamic
* auto assignment.
*
* There is no need to set this when going to the next/previous page any more.
*/
public Duration searchWindow;
/**
* The expected maximum time a journey can last across all possible journeys for the current deployment.
* Normally you would just do an estimate and add enough slack, so you are sure that there is no journeys that
* falls outside this window. The parameter is used find all possible dates for the journey and then search only
* the services which run on those dates. The duration must include access, egress, wait-time and transit time
* for the whole journey. It should also take low frequency days/periods like holidays into account. In other words,
* pick the two points within your area that has the worst connection and then try to travel on the worst possible
* day, and find the maximum journey duration. Using a value that is too high has the effect of including more
* patterns in the search, hence, making it a bit slower. Recommended values would be from 12 hours(small
* town/city), 1 day (region) to 2 days (country like Norway).
*/
public Duration maxJourneyDuration = Duration.ofHours(24);
/**
* Use the cursor to go to the next or previous "page" of trips.
* You should pass in the original request as is.
*
* The next page of itineraries will depart after the current results
* and the previous page of itineraries will depart before the current results.
*
* The paging does not support timeTableView=false and arriveBy=true, this will result in
* none pareto-optimal results.
*/
public PageCursor pageCursor;
/**
* Search for the best trip options within a time window. If {@code true} two itineraries are
* considered optimal if one is better on arrival time(earliest wins) and the other is better
* on departure time(latest wins).
*
* In combination with {@code arriveBy} this parameter cover the following 3 use cases:
*
* -
* The traveler want to find the best alternative within a time window. Set
* {@code timetableView=true} and {@code arriveBy=false}. This is the default, and if the
* intention of the traveler is unknown, this gives the best result. This use-case includes
* all itineraries in the two next use-cases. This option also work well with paging.
*
* Setting the {@code arriveBy=false}, covers the same use-case, but the input time is
* interpreted as latest-arrival-time, and not earliest-departure-time.
*
* -
* The traveler want to find the best alternative with departure after a specific time.
* For example: I am at the station now and want to get home as quickly as possible.
* Set {@code timetableView=true} and {@code arriveBy=false}. Do not support paging.
*
* -
* Traveler want to find the best alternative with arrival before specific time. For
* example going to a meeting. Set {@code timetableView=true} and {@code arriveBy=false}.
* Do not support paging.
*
*
* Default: true
*/
public boolean timetableView = true;
/**
* Whether the trip should depart at dateTime (false, the default), or arrive at dateTime.
*/
public boolean arriveBy = false;
/**
* Whether the trip must be wheelchair accessible.
*/
public boolean wheelchairAccessible = false;
/**
* The maximum number of itineraries to return. In OTP1 this parameter terminates the search,
* but in OTP2 it crops the list of itineraries AFTER the search is complete. This parameter is
* a post search filter function. A side effect from reducing the result is that OTP2 cannot
* guarantee to find all pareto-optimal itineraries when paging. Also, a large search-window
* and a small {@code numItineraries} waste computer CPU calculation time.
*
* The default value is 50. This is a reasonably high threshold to prevent large amount of data
* to be returned. Consider tuning the search-window instead of setting this to a small value.
*/
public int numItineraries = 50;
/** The maximum slope of streets for wheelchair trips. */
public double maxWheelchairSlope = 0.0833333333333; // ADA max wheelchair ramp slope is a good default.
/** Whether the planner should return intermediate stops lists for transit legs. */
public boolean showIntermediateStops = false;
/** max walk/bike speed along streets, in meters per second */
public double walkSpeed;
public double bikeSpeed;
public double bikeWalkingSpeed;
public double carSpeed;
public Locale locale = new Locale("en", "US");
/**
* An extra penalty added on transfers (i.e. all boardings except the first one).
* Not to be confused with bikeBoardCost and walkBoardCost, which are the cost of boarding a
* vehicle with and without a bicycle. The boardCosts are used to model the 'usual' perceived
* cost of using a transit vehicle, and the transferCost is used when a user requests even
* less transfers. In the latter case, we don't actually optimize for fewest transfers, as this
* can lead to absurd results. Consider a trip in New York from Grand Army
* Plaza (the one in Brooklyn) to Kalustyan's at noon. The true lowest transfers route is to
* wait until midnight, when the 4 train runs local the whole way. The actual fastest route is
* the 2/3 to the 4/5 at Nevins to the 6 at Union Square, which takes half an hour.
* Even someone optimizing for fewest transfers doesn't want to wait until midnight. Maybe they
* would be willing to walk to 7th Ave and take the Q to Union Square, then transfer to the 6.
* If this takes less than optimize_transfer_penalty seconds, then that's what we'll return.
*/
public int transferCost = 0;
/**
* Penalty for using a non-preferred transfer
*
* @deprecated TODO OTP2 Regression. Not currently working in OTP2. We might not implement the
* old functionality the same way, but we will try to map this parameter
* so it does work similar as before.
*/
@Deprecated
public int nonpreferredTransferCost = 180;
/** Configure the transfer optimization */
public final TransferOptimizationParameters transferOptimization = new TransferOptimizationRequest();
/**
* Transit reluctance per mode. Use this to add a advantage(<1.0) to specific modes, or to add
* a penalty to other modes (> 1.0). The type used here it the internal model
* {@link TransitMode} make sure to create a mapping for this before using it on the API.
*
* If set, the alight-slack-for-mode override the default value {@code 1.0}.
*
* This is a scalar multiplied with the time in second on board the transit vehicle. Default
* value is not-set(empty map).
*/
private Map transitReluctanceForMode = new HashMap<>();
/** A multiplier for how bad walking is, compared to being in transit for equal lengths of time.
* Defaults to 2. Empirically, values between 10 and 20 seem to correspond well to the concept
* of not wanting to walk too much without asking for totally ridiculous itineraries, but this
* observation should in no way be taken as scientific or definitive. Your mileage may vary.
*/
public double walkReluctance = 2.0;
public double bikeWalkingReluctance = 5.0;
public double bikeReluctance = 2.0;
public double carReluctance = 2.0;
/**
* How much more time does it take to walk a flight of stairs compared to walking a similar
* horizontal length
*
* Default value is based on:
* Fujiyama, T., & Tyler, N. (2010).
* Predicting the walking speed of pedestrians on stairs.
* Transportation Planning and Technology, 33(2), 177–202.
*/
public double stairsTimeFactor = 3.0;
/** Used instead of walk reluctance for stairs */
public double stairsReluctance = 2.0;
/** Multiplicative factor on expected turning time. */
public double turnReluctance = 1.0;
/**
* How long does it take to get an elevator, on average (actually, it probably should be a bit *more* than average, to prevent optimistic trips)?
* Setting it to "seems like forever," while accurate, will probably prevent OTP from working correctly.
*/
// TODO: how long does it /really/ take to get an elevator?
public int elevatorBoardTime = 90;
/** What is the cost of boarding an elevator? */
public int elevatorBoardCost = 90;
/** How long does it take to advance one floor on an elevator? */
public int elevatorHopTime = 20;
/** What is the cost of travelling one floor on an elevator? */
public int elevatorHopCost = 20;
// it is assumed that getting off an elevator is completely free
/** Time to get on and off your own bike */
public int bikeSwitchTime;
/** Cost of getting on and off your own bike */
public int bikeSwitchCost;
/** Time to rent a vehicle */
public int vehicleRentalPickupTime = 60;
/**
* Cost of renting a vehicle. The cost is a bit more than actual time to model the associated cost and trouble.
*/
public int vehicleRentalPickupCost = 120;
/** Time to drop-off a rented vehicle */
public int vehicleRentalDropoffTime = 30;
/** Cost of dropping-off a rented vehicle */
public int vehicleRentalDropoffCost = 30;
/** The vehicle rental networks which may be used. If empty all networks may be used. */
public Set allowedVehicleRentalNetworks = Set.of();
/** The vehicle rental networks which may not be used. If empty, no networks are banned. */
public Set bannedVehicleRentalNetworks = Set.of();
/** Time to park a bike */
public int bikeParkTime = 60;
/** Cost of parking a bike. */
public int bikeParkCost = 120;
/** Time to park a car */
public int carParkTime = 60;
/** Cost of parking a car. */
public int carParkCost = 120;
/** Tags which are required to use a vehicle parking. If empty, no tags are required. */
public Set requiredVehicleParkingTags = Set.of();
/** Tags with which a vehicle parking will not be used. If empty, no tags are banned. */
public Set bannedVehicleParkingTags = Set.of();
/**
* Time to park a car in a park and ride, w/o taking into account driving and walking cost
* (time to park, switch off, pick your stuff, lock the car, etc...)
*/
public int carDropoffTime = 120;
/** Time of getting in/out of a carPickup (taxi) */
public int carPickupTime = 60;
/** Cost of getting in/out of a carPickup (taxi) */
public int carPickupCost = 120;
/**
* How much worse is waiting for a transit vehicle than being on a transit vehicle, as a multiplier. The default value treats wait and on-vehicle
* time as the same.
*
* It may be tempting to set this higher than walkReluctance (as studies often find this kind of preferences among
* riders) but the planner will take this literally and walk down a transit line to avoid waiting at a stop.
* This used to be set less than 1 (0.95) which would make waiting offboard preferable to waiting onboard in an
* interlined trip. That is also undesirable.
*
* If we only tried the shortest possible transfer at each stop to neighboring stop patterns, this problem could disappear.
*/
public double waitReluctance = 1.0;
/** How much less bad is waiting at the beginning of the trip (replaces waitReluctance on the first boarding)
*
* @deprecated TODO OTP2 Probably a regression, but I'm not sure it worked correctly in OTP 1.X
* either. It could be a part of itinerary-filtering after a Raptor search.
* */
@Deprecated
public double waitAtBeginningFactor = 0.4;
/**
* This prevents unnecessary transfers by adding a cost for boarding a vehicle. This is in
* addition to the cost of the transfer(walking) and waiting-time. It is also in addition to
* the {@link #transferCost}.
*/
public int walkBoardCost = 60 * 10;
/**
* Separate cost for boarding a vehicle with a bicycle, which is more difficult than on foot.
* This is in addition to the cost of the transfer(biking) and waiting-time. It is also in
* addition to the {@link #transferCost}.
*/
public int bikeBoardCost = 60 * 10;
/**
* Do not use certain named agencies
*/
private Set bannedAgencies = Set.of();
/**
* Only use certain named agencies
*/
private Set whiteListedAgencies = Set.of();
/**
* Set of preferred agencies by user.
*/
@Deprecated
private Set preferredAgencies = Set.of();
/**
* Set of unpreferred agencies for given user.
*/
@Deprecated
private Set unpreferredAgencies = Set.of();
/**
* Do not use certain named routes.
* The paramter format is: feedId_routeId,feedId_routeId,feedId_routeId
* This parameter format is completely nonstandard and should be revised for the 2.0 API, see issue #1671.
*/
private RouteMatcher bannedRoutes = RouteMatcher.emptyMatcher();
/** Only use certain named routes
*/
private RouteMatcher whiteListedRoutes = RouteMatcher.emptyMatcher();
/** Set of preferred routes by user.
*
* @deprecated TODO OTP2 Needs to be implemented
*/
@Deprecated
private RouteMatcher preferredRoutes = RouteMatcher.emptyMatcher();
/**
* Penalty added for using every route that is not preferred if user set any route as preferred.
* We return number of seconds that we are willing to wait for preferred route.
*
* @deprecated TODO OTP2 Needs to be implemented
*/
@Deprecated
public int otherThanPreferredRoutesPenalty = 300;
/**
* Set of unpreferred routes for given user.
*
* @deprecated TODO OTP2: Needs to be implemented
*/
@Deprecated
private RouteMatcher unpreferredRoutes = RouteMatcher.emptyMatcher();
/**
* Penalty added for using every unpreferred route. We return number of seconds that we are
* willing to wait for preferred route.
*
* @deprecated TODO OTP2: Needs to be implemented
*/
@Deprecated
public int useUnpreferredRoutesPenalty = 300;
/**
* Do not use certain trips
*
* @deprecated TODO OTP2: Needs to be implemented
*/
@Deprecated
public HashMap bannedTrips = new HashMap();
/**
* A global minimum transfer time (in seconds) that specifies the minimum amount of time that
* must pass between exiting one transit vehicle and boarding another. This time is in addition
* to time it might take to walk between transit stops, the {@link #alightSlack}, and the
* {@link #boardSlack}. This time should also be overridden by specific transfer timing
* information in transfers.txt
*
* This only apply to transfers between two trips, it does not apply when boarding the first
* transit.
*
* Unit is seconds. Default value is 2 minutes.
*/
public int transferSlack = 120;
/**
* The number of seconds to add before boarding a transit leg. It is recommended to use the
* `boardTimes` in the `router-config.json` to set this for each mode.
*
* Unit is seconds. Default value is 0.
*/
public int boardSlack = 0;
/**
* Has information how much time boarding a vehicle takes. Can be significant eg in airplanes
* or ferries.
*
* If set, the board-slack-for-mode override the more general {@link #boardSlack}. This
* enables configuring the board-slack for airplane boarding to be 30 minutes and a slack
* for bus of 2 minutes.
*
* Unit is seconds. Default value is not-set(empty map).
*/
public Map boardSlackForMode = new EnumMap<>(TransitMode.class);
/**
* The number of seconds to add after alighting a transit leg. It is recommended to use the
* `alightTimes` in the `router-config.json` to set this for each mode.
*
* Unit is seconds. Default value is 0.
*/
public int alightSlack = 0;
/**
* Has information how much time alighting a vehicle takes. Can be significant eg in airplanes
* or ferries.
*
* If set, the alight-slack-for-mode override the more general {@link #alightSlack}. This
* enables configuring the alight-slack for train alighting to be 4 minutes and a bus alight
* slack to be 0 minutes.
*
* Unit is seconds. Default value is not-set(empty map).
*/
public Map alightSlackForMode = new EnumMap<>(TransitMode.class);
/**
* Ideally maxTransfers should be set in the router config, not here. Instead the client should
* be able to pass in a parameter for the max number of additional/extra transfers relative to
* the best trip (with the fewest possible transfers) within constraint of the other search
* parameters(TODO OTP2 Expose {@link org.opentripplanner.transit.raptor.api.request.SearchParams#numberOfAdditionalTransfers()}
* in APIs). This might be to complicated to explain to the customer, so we might stick to the
* old limit, but that have side-effects that you might not find any trips on a day where a
* critical part of the trip is not available, because of some real-time disruption.
*
* @see https://github.com/opentripplanner/OpenTripPlanner/issues/2886
*/
public Integer maxTransfers = 12;
/**
* For the bike triangle, how important time is.
* triangleTimeFactor+triangleSlopeFactor+triangleSafetyFactor == 1
*/
public double bikeTriangleTimeFactor;
/** For the bike triangle, how important slope is */
public double bikeTriangleSlopeFactor;
/** For the bike triangle, how important safety is */
public double bikeTriangleSafetyFactor;
/**
* Whether or not vehicle rental availability information will be used to plan vehicle rental trips
*/
public boolean useVehicleRentalAvailabilityInformation = false;
/**
* Whether arriving at the destination with a rented (station) bicycle is allowed without
* dropping it off.
*
* @see RoutingRequest#keepingRentedVehicleAtDestinationCost
* @see VehicleRentalStation#isKeepingVehicleRentalAtDestinationAllowed
*/
public boolean allowKeepingRentedVehicleAtDestination = false;
/**
* The cost of arriving at the destination with the rented bicycle, to discourage doing so.
*
* @see RoutingRequest#allowKeepingRentedVehicleAtDestination
*/
public double keepingRentedVehicleAtDestinationCost = 0;
/**
* The deceleration speed of an automobile, in meters per second per second.
*/
// 2.9 m/s/s: 65 mph - 0 mph in 10 seconds
public double carDecelerationSpeed = 2.9;
/**
* The acceleration speed of an automobile, in meters per second per second.
*/
// 2.9 m/s/s: 0 mph to 65 mph in 10 seconds
public double carAccelerationSpeed = 2.9;
/**
* When true, realtime updates are ignored during this search.
*/
public boolean ignoreRealtimeUpdates = false;
/**
* When true, trips cancelled in scheduled data are included in this search.
*/
public boolean includePlannedCancellations = false;
/**
* If true, the remaining weight heuristic is disabled. Currently only implemented for the long
* distance path service.
*
* This is used by the Street search only.
*
* TODO OTP2 Can we merge this with the 'oneToMany' option?
*/
public boolean disableRemainingWeightHeuristic = false;
/**
* The routing context used to actually carry out this search. It is important to build States from TraverseOptions
* rather than RoutingContexts,and just keep a reference to the context in the TraverseOptions, rather than using
* RoutingContexts for everything because in some testing and graph building situations we need to build a bunch of
* initial states with different times and vertices from a single TraverseOptions, without setting all the transit
* context or building temporary vertices (with all the exception-throwing checks that entails).
*
* While they are conceptually separate, TraverseOptions does maintain a reference to its accompanying
* RoutingContext (and vice versa) so that both do not need to be passed/injected separately into tight inner loops
* within routing algorithms. These references should be set to null when the request scope is torn down -- the
* routing context becomes irrelevant at that point, since temporary graph elements have been removed and the graph
* may have been reloaded.
*/
public RoutingContext rctx;
/**
* A transit stop that this trip must start from
*
* @deprecated TODO OTP2 Is this in use, what is is used for. It seems to overlap with
* the fromPlace parameter. Is is used for onBoard routing only?
*/
@Deprecated
public FeedScopedId startingTransitStopId;
/**
* A trip where this trip must start from (depart-onboard routing)
*
* @deprecated TODO OTP2 Regression. Not currently working in OTP2. We might not implement the
* old functionality the same way, but we will try to map this parameter
* so it does work similar as before.
*/
@Deprecated
public FeedScopedId startingTransitTripId;
/*
Additional flags affecting mode transitions.
This is a temporary solution, as it only covers parking and rental at the beginning of the trip.
*/
public boolean vehicleRental = false;
public boolean parkAndRide = false;
public boolean carPickup = false;
public Set allowedRentalFormFactors = new HashSet<>();
/**
* If true vehicle parking availability information will be used to plan park and ride trips where it exists.
*/
public boolean useVehicleParkingAvailabilityInformation = false;
/** The function that compares paths converging on the same vertex to decide which ones continue to be explored. */
public DominanceFunction dominanceFunction = new DominanceFunction.Pareto();
/**
* Accept only paths that use transit (no street-only paths).
*
* @Deprecated TODO OTP2 Regression. Not currently working in OTP2. This is only used in the
* deprecated Transmodel GraphQL API.
*
*/
@Deprecated
public boolean onlyTransitTrips = false;
/** Option to disable the default filtering of GTFS-RT alerts by time. */
@Deprecated
public boolean disableAlertFiltering = false;
/** Whether to apply the ellipsoid→geoid offset to all elevations in the response */
public boolean geoidElevation = false;
/** Which path comparator to use
*
* @deprecated TODO OTP2 Regression. Not currently working in OTP2 at the moment.
*/
@Deprecated
public String pathComparator = null;
@Nonnull
public ItineraryFilterParameters itineraryFilters = ItineraryFilterParameters.createDefault();
/**
* The filled request parameters for penalties and thresholds values
*/
public DataOverlayParameters dataOverlay = null;
/**
* Raptor can print all events when arriving at stops to system error. For developers only.
*/
public DebugRaptor raptorDebuging = null;
/* CONSTRUCTORS */
/** Constructor for options; modes defaults to walk and transit */
public RoutingRequest() {
// http://en.wikipedia.org/wiki/Walking
walkSpeed = 1.33; // 1.33 m/s ~ 3mph, avg. human speed
bikeSpeed = 5; // 5 m/s, ~11 mph, a random bicycling speed
bikeWalkingSpeed = 1.33; // 1.33 m/s ~ 3mph, avg. human speed
// http://en.wikipedia.org/wiki/Speed_limit
carSpeed = 40; // 40 m/s, 144 km/h, above the maximum (finite) driving speed limit worldwide
// Default to walk for access/egress/direct modes and all transit modes
// So that they are never null.
from = new GenericLocation(null, null);
to = new GenericLocation(null, null);
}
public RoutingRequest(TraverseModeSet streetSubRequestModes) {
this();
this.setStreetSubRequestModes(streetSubRequestModes);
}
public RoutingRequest(TraverseMode mode) {
this();
this.setStreetSubRequestModes(new TraverseModeSet(mode));
}
public RoutingRequest(TraverseMode mode, BicycleOptimizeType bicycleOptimizeType) {
this(new TraverseModeSet(mode), bicycleOptimizeType);
}
public RoutingRequest(TraverseModeSet modeSet, BicycleOptimizeType bicycleOptimizeType) {
this();
this.bicycleOptimizeType = bicycleOptimizeType;
this.setStreetSubRequestModes(modeSet);
}
public RoutingRequest(RequestModes modes) {
this();
this.modes = modes;
}
/* ACCESSOR/SETTER METHODS */
public boolean transitAllowed() {
return streetSubRequestModes.isTransit();
}
public void setArriveBy(boolean arriveBy) {
this.arriveBy = arriveBy;
}
public void setMode(TraverseMode mode) {
setStreetSubRequestModes(new TraverseModeSet(mode));
}
public void setStreetSubRequestModes(TraverseModeSet streetSubRequestModes) {
this.streetSubRequestModes = streetSubRequestModes;
}
public void setBicycleOptimizeType(BicycleOptimizeType bicycleOptimizeType) {
this.bicycleOptimizeType = bicycleOptimizeType;
}
public void setWheelchairAccessible(boolean wheelchairAccessible) {
this.wheelchairAccessible = wheelchairAccessible;
}
public void setTransitReluctanceForMode(Map reluctanceForMode) {
transitReluctanceForMode.clear();
transitReluctanceForMode.putAll(reluctanceForMode);
}
public Map transitReluctanceForMode() {
return Collections.unmodifiableMap(transitReluctanceForMode);
}
public void setWalkBoardCost(int walkBoardCost) {
if (walkBoardCost < 0) {
this.walkBoardCost = 0;
}
else {
this.walkBoardCost = walkBoardCost;
}
}
public void setBikeBoardCost(int bikeBoardCost) {
if (bikeBoardCost < 0) {
this.bikeBoardCost = 0;
}
else {
this.bikeBoardCost = bikeBoardCost;
}
}
public void setPreferredAgencies(Collection ids) {
if(ids != null) {
preferredAgencies = Set.copyOf(ids);
}
}
public void setPreferredAgenciesFromString(String s) {
if (!s.isEmpty()) {
preferredAgencies = FeedScopedId.parseListOfIds(s);
}
}
public void setUnpreferredAgencies(Collection ids) {
if (ids != null) {
unpreferredAgencies = Set.copyOf(ids);
}
}
public void setUnpreferredAgenciesFromString(String s) {
if (!s.isEmpty()) {
unpreferredAgencies = FeedScopedId.parseListOfIds(s);
}
}
public void setBannedAgencies(Collection ids) {
if (ids != null) {
bannedAgencies = Set.copyOf(ids);
}
}
public void setBannedAgenciesFromSting(String s) {
if (!s.isEmpty()) {
bannedAgencies = FeedScopedId.parseListOfIds(s);
}
}
public void setWhiteListedAgencies(Collection ids) {
if (ids != null) {
whiteListedAgencies = Set.copyOf(ids);
}
}
public void setWhiteListedAgenciesFromSting(String s) {
if (!s.isEmpty()) {
whiteListedAgencies = FeedScopedId.parseListOfIds(s);
}
}
public void setOtherThanPreferredRoutesPenalty(int penalty) {
if(penalty < 0) penalty = 0;
this.otherThanPreferredRoutesPenalty = penalty;
}
public void setPreferredRoutes(List routeIds) {
preferredRoutes = RouteMatcher.idMatcher(routeIds);
}
public void setPreferredRoutesFromSting(String s) {
if (!s.isEmpty()) {
preferredRoutes = RouteMatcher.parse(s);
}
else {
preferredRoutes = RouteMatcher.emptyMatcher();
}
}
public void setUnpreferredRoutes(List routeIds) {
unpreferredRoutes = RouteMatcher.idMatcher(routeIds);
}
public void setUnpreferredRoutesFromSting(String s) {
if (!s.isEmpty()) {
unpreferredRoutes = RouteMatcher.parse(s);
}
else {
unpreferredRoutes = RouteMatcher.emptyMatcher();
}
}
public void setBannedRoutes(List routeIds) {
bannedRoutes = RouteMatcher.idMatcher(routeIds);
}
public void setBannedRoutesFromSting(String s) {
if (!s.isEmpty()) {
bannedRoutes = RouteMatcher.parse(s);
}
else {
bannedRoutes = RouteMatcher.emptyMatcher();
}
}
public void setWhiteListedRoutesFromSting(String s) {
if (!s.isEmpty()) {
whiteListedRoutes = RouteMatcher.parse(s);
}
else {
whiteListedRoutes = RouteMatcher.emptyMatcher();
}
}
public void setWhiteListedRoutes(List routeIds) {
whiteListedRoutes = RouteMatcher.idMatcher(routeIds);
}
public void setFromString(String from) {
this.from = LocationStringParser.fromOldStyleString(from);
}
public void setToString(String to) {
this.to = LocationStringParser.fromOldStyleString(to);
}
/**
* Add a TraverseMode to the set of allowed modes.
*/
public void addMode(TraverseMode mode) {
streetSubRequestModes.setMode(mode, true);
}
/**
* The search time for the current request. If the client have moved to the next page
* then this is the adjusted search time - the dateTime passed in is ignored and replaced with
* by a time from the pageToken.
*/
public Instant getDateTime() {
return dateTime;
}
public void setDateTime(Instant dateTime) {
this.dateTime = dateTime;
}
public void setDateTime(String date, String time, TimeZone tz) {
Date dateObject = DateUtils.toDate(date, time, tz);
setDateTime(dateObject == null ? Instant.now() : dateObject.toInstant());
}
/**
* Is the trip originally planned withing the previous/next 15h?
*/
public boolean isTripPlannedForNow() {
return Duration.between(dateTime, Instant.now()).abs().toSeconds() < NOW_THRESHOLD_SEC;
}
/**
* Currently only one itinerary is returned for a direct street search
*/
public int getNumItinerariesForDirectStreetSearch() {
return 1;
}
public void setPageCursor(String pageCursor) {
this.pageCursor = PageCursor.decode(pageCursor);
}
public SortOrder getItinerariesSortOrder() {
if(pageCursor != null) {
return pageCursor.originalSortOrder;
}
return arriveBy
? SortOrder.STREET_AND_DEPARTURE_TIME
: SortOrder.STREET_AND_ARRIVAL_TIME;
}
/**
* Adjust the 'dateTime' if the page cursor is set to "goto next/previous page".
* The date-time is used for many things, for example finding the days to search,
* but the transit search is using the cursor[if exist], not the date-time.
*/
public void applyPageCursor() {
if(pageCursor != null) {
// We switch to "depart-after" search when paging next(lat==null). It does not make
// sense anymore to keep the latest-arrival-time when going to the "next page".
if(pageCursor.latestArrivalTime == null) {
arriveBy = false;
}
setDateTime(arriveBy ? pageCursor.latestArrivalTime : pageCursor.earliestDepartureTime);
modes.directMode = StreetMode.NOT_SET;
LOG.debug("Request dateTime={} set from pageCursor.", dateTime);
}
}
/**
* When paging we must crop the list of itineraries in the right end according to the
* sorting of the original search and according to the page cursor type (next or previous).
*
* We need to flip the cropping and crop the head/start of the itineraries when:
*
* - Paging to the previous page for a {@code depart-after/sort-on-arrival-time} search.
*
- Paging to the next page for a {@code arrive-by/sort-on-departure-time} search.
*
*/
public boolean maxNumberOfItinerariesCropHead() {
if(pageCursor == null) { return false; }
var previousPage = pageCursor.type == PageType.PREVIOUS_PAGE;
return pageCursor.originalSortOrder.isSortedByArrivalTimeAcceding() == previousPage;
}
/**
* Related to {@link #maxNumberOfItinerariesCropHead()}, but is {@code true} if we should
* crop the search-window head(in the beginning) or tail(in the end).
*
* For the first search we look if the sort is ascending(crop tail) or descending(crop head),
* and for paged results we look at the paging type: next(tail) and previous(head).
*/
public boolean doCropSearchWindowAtTail() {
if(pageCursor == null) {
return getItinerariesSortOrder().isSortedByArrivalTimeAcceding();
}
return pageCursor.type == PageType.NEXT_PAGE;
}
public void setNumItineraries(int numItineraries) {
this.numItineraries = numItineraries;
}
public String toString() {
return toString(" ");
}
public String toString(String sep) {
return from + sep + to + sep + dateTime + sep
+ arriveBy + sep + bicycleOptimizeType + sep + streetSubRequestModes.getAsStr() + sep
+ getNumItinerariesForDirectStreetSearch();
}
public void removeMode(TraverseMode mode) {
streetSubRequestModes.setMode(mode, false);
}
/**
* Sets intermediatePlaces by parsing GenericLocations from a list of string.
*/
public void setIntermediatePlacesFromStrings(List intermediates) {
this.intermediatePlaces = new ArrayList(intermediates.size());
for (String place : intermediates) {
intermediatePlaces.add(LocationStringParser.fromOldStyleString(place));
}
}
/** Clears any intermediate places from this request. */
public void clearIntermediatePlaces() {
if (this.intermediatePlaces != null) {
this.intermediatePlaces.clear();
}
}
/**
* Returns true if there are any intermediate places set.
*/
public boolean hasIntermediatePlaces() {
return this.intermediatePlaces != null && this.intermediatePlaces.size() > 0;
}
/**
* Adds a GenericLocation to the end of the intermediatePlaces list. Will initialize intermediatePlaces if it is null.
*/
public void addIntermediatePlace(GenericLocation location) {
if (this.intermediatePlaces == null) {
this.intermediatePlaces = new ArrayList();
}
this.intermediatePlaces.add(location);
}
/* INSTANCE METHODS */
public RoutingRequest getStreetSearchRequest(StreetMode streetMode) {
RoutingRequest streetRequest = this.clone();
streetRequest.streetSubRequestModes = new TraverseModeSet();
if (streetMode != null) {
switch (streetMode) {
case WALK:
case FLEXIBLE:
streetRequest.setStreetSubRequestModes(new TraverseModeSet(TraverseMode.WALK));
break;
case BIKE:
streetRequest.setStreetSubRequestModes(new TraverseModeSet(TraverseMode.BICYCLE));
break;
case BIKE_TO_PARK:
streetRequest.setStreetSubRequestModes(new TraverseModeSet(TraverseMode.BICYCLE, TraverseMode.WALK));
streetRequest.parkAndRide = true;
break;
case BIKE_RENTAL:
streetRequest.setStreetSubRequestModes(new TraverseModeSet(TraverseMode.BICYCLE, TraverseMode.WALK));
streetRequest.vehicleRental = true;
streetRequest.allowedRentalFormFactors.add(FormFactor.BICYCLE);
break;
case SCOOTER_RENTAL:
streetRequest.setStreetSubRequestModes(new TraverseModeSet(TraverseMode.BICYCLE, TraverseMode.WALK));
streetRequest.vehicleRental = true;
streetRequest.allowedRentalFormFactors.add(FormFactor.SCOOTER);
break;
case CAR:
streetRequest.setStreetSubRequestModes(new TraverseModeSet(TraverseMode.CAR));
break;
case CAR_TO_PARK:
streetRequest.setStreetSubRequestModes(new TraverseModeSet(TraverseMode.CAR, TraverseMode.WALK));
streetRequest.parkAndRide = true;
break;
case CAR_PICKUP:
streetRequest.setStreetSubRequestModes(new TraverseModeSet(TraverseMode.CAR, TraverseMode.WALK));
streetRequest.carPickup = true;
break;
case CAR_RENTAL:
streetRequest.setStreetSubRequestModes(new TraverseModeSet(TraverseMode.CAR, TraverseMode.WALK));
streetRequest.vehicleRental = true;
streetRequest.allowedRentalFormFactors.add(FormFactor.CAR);
}
}
streetRequest.resetRoutingContext();
return streetRequest;
}
// TODO OTP2 This is needed in order to find the correct from/to vertices for the mode
private void resetRoutingContext() {
if (rctx != null) {
Graph graph = rctx.graph;
rctx = null;
setRoutingContext(graph);
}
}
@SuppressWarnings("unchecked")
@Override
public RoutingRequest clone() {
try {
RoutingRequest clone = (RoutingRequest) super.clone();
clone.streetSubRequestModes = streetSubRequestModes.clone();
clone.allowedVehicleRentalNetworks = Set.copyOf(allowedVehicleRentalNetworks);
clone.bannedVehicleRentalNetworks = Set.copyOf(bannedVehicleRentalNetworks);
clone.requiredVehicleParkingTags = Set.copyOf(requiredVehicleParkingTags);
clone.bannedVehicleParkingTags = Set.copyOf(bannedVehicleParkingTags);
clone.preferredAgencies = Set.copyOf(preferredAgencies);
clone.unpreferredAgencies = Set.copyOf(unpreferredAgencies);
clone.whiteListedAgencies = Set.copyOf(whiteListedAgencies);
clone.bannedAgencies = Set.copyOf(bannedAgencies);
clone.bannedRoutes = bannedRoutes.clone();
clone.whiteListedRoutes = whiteListedRoutes.clone();
clone.preferredRoutes = preferredRoutes.clone();
clone.unpreferredRoutes = unpreferredRoutes.clone();
clone.bannedTrips = (HashMap) bannedTrips.clone();
clone.allowedRentalFormFactors = new HashSet<>(allowedRentalFormFactors);
return clone;
} catch (CloneNotSupportedException e) {
/* this will never happen since our super is the cloneable object */
throw new RuntimeException(e);
}
}
public RoutingRequest reversedClone() {
RoutingRequest ret = this.clone();
ret.setArriveBy(!ret.arriveBy);
ret.useVehicleRentalAvailabilityInformation = false;
return ret;
}
public void setRoutingContext(Graph graph) {
if (rctx == null) {
// graphService.getGraph(routerId)
this.rctx = new RoutingContext(this, graph);
// check after back reference is established, to allow temp edge cleanup on exceptions
this.rctx.checkIfVerticesFound();
} else {
if (rctx.graph == graph) {
LOG.debug("keeping existing routing context");
return;
} else {
LOG.error("attempted to reset routing context using a different graph");
return;
}
}
}
/**
* For use in tests. Force RoutingContext to specific vertices rather than making temp edges.
* TODO rename - this is not a "setter", it creates a new routingContext, which has side effects on Graph
* (Constructors with side effects on their parameters are a bad design).
*/
public void setRoutingContext(Graph graph, Edge fromBackEdge, Vertex from, Vertex to) {
if (rctx != null) {
this.rctx.destroy();
}
this.rctx = new RoutingContext(this, graph, from, to);
this.rctx.originBackEdge = fromBackEdge;
}
public void setRoutingContext(Graph graph, Vertex from, Vertex to) {
setRoutingContext(graph, null, from, to);
}
public void setRoutingContext(Graph graph, Set from, Set to) {
setRoutingContext(graph, null, from, to);
}
public void setRoutingContext(Graph graph, Edge fromBackEdge, Set from, Set to) {
// normally you would want to tear down the routing context...
// but this method is mostly used in tests, and teardown interferes with testHalfEdges
// FIXME here, or in test, and/or in other places like TSP that use this method
if (rctx != null)
this.rctx.destroy();
this.rctx = new RoutingContext(this, graph, from, to);
this.rctx.originBackEdge = fromBackEdge;
}
/** For use in tests. Force RoutingContext to specific vertices rather than making temp edges. */
public void setRoutingContext(Graph graph, String from, String to) {
this.setRoutingContext(graph, graph.getVertex(from), graph.getVertex(to));
}
/** Used in internals API. Make a RoutingContext with no origin or destination vertices specified. */
public void setDummyRoutingContext(Graph graph) {
this.setRoutingContext(graph, "", "");
}
public RoutingContext getRoutingContext() {
return this.rctx;
}
/** Tear down any routing context (remove temporary edges from edge lists) */
public void cleanup() {
if (this.rctx != null) {
try {
rctx.destroy();
}
catch (Exception e) {
LOG.error("Could not destroy the routing context", e);
}
}
}
@Override
public void close() {
cleanup();
}
/**
* The road speed for a specific traverse mode.
*/
public double getReluctance(TraverseMode mode, boolean walkingBike) {
switch (mode) {
case WALK:
return walkingBike ? bikeWalkingReluctance : walkReluctance;
case BICYCLE:
return bikeReluctance;
case CAR:
return carReluctance;
default:
throw new IllegalArgumentException("getReluctance(): Invalid mode " + mode);
}
}
/**
* The road speed for a specific traverse mode.
*/
public double getSpeed(TraverseMode mode, boolean walkingBike) {
switch (mode) {
case WALK:
return walkingBike ? bikeWalkingSpeed : walkSpeed;
case BICYCLE:
return bikeSpeed;
case CAR:
return carSpeed;
default:
throw new IllegalArgumentException("getSpeed(): Invalid mode " + mode);
}
}
/** @return The highest speed for all possible road-modes. */
public double getStreetSpeedUpperBound() {
// Assume carSpeed > bikeSpeed > walkSpeed
if (streetSubRequestModes.getCar()) {
return carSpeed;
}
if (streetSubRequestModes.getBicycle()) {
return bikeSpeed;
}
return walkSpeed;
}
public void setBikeReluctance(double bikeReluctance) {
if (bikeReluctance > 0) {
this.bikeReluctance = bikeReluctance;
}
}
public void setBikeWalkingReluctance(double bikeWalkingReluctance) {
if (bikeWalkingReluctance > 0) {
this.bikeWalkingReluctance = bikeWalkingReluctance;
}
}
public void setCarReluctance(double carReluctance) {
if (carReluctance > 0) {
this.carReluctance = carReluctance;
}
}
public void setWalkReluctance(double walkReluctance) {
if (walkReluctance > 0) {
this.walkReluctance = walkReluctance;
}
}
public void setNonTransitReluctance(double nonTransitReluctance) {
if (nonTransitReluctance > 0) {
this.bikeReluctance = nonTransitReluctance;
this.walkReluctance = nonTransitReluctance;
this.carReluctance = nonTransitReluctance;
this.bikeWalkingReluctance = nonTransitReluctance * 2.7;
}
}
public void setWaitReluctance(double waitReluctance) {
if (waitReluctance > 0) {
this.waitReluctance = waitReluctance;
}
}
public void setWaitAtBeginningFactor(double waitAtBeginningFactor) {
if (waitAtBeginningFactor > 0) {
this.waitAtBeginningFactor = waitAtBeginningFactor;
}
}
public Set getBannedRoutes(Collection routes) {
if (bannedRoutes.isEmpty() && bannedAgencies.isEmpty() &&
whiteListedRoutes.isEmpty() && whiteListedAgencies.isEmpty()
) {
return Set.of();
}
Set bannedRoutes = new HashSet<>();
for (Route route : routes) {
if (routeIsBanned(route)) {
bannedRoutes.add(route.getId());
}
}
return bannedRoutes;
}
public double getMaxAccessEgressDurationSecondsForMode(StreetMode mode) {
return maxAccessEgressDurationSecondsForMode.getOrDefault(
mode,
maxAccessEgressDurationSeconds
);
}
/**
* Checks if the route is banned. Also, if whitelisting is used, the route (or its agency) has
* to be whitelisted in order to not count as banned.
*
* @return True if the route is banned
*/
private boolean routeIsBanned(Route route) {
/* check if agency is banned for this plan */
if (!bannedAgencies.isEmpty()) {
if (bannedAgencies.contains(route.getAgency().getId())) {
return true;
}
}
/* check if route banned for this plan */
if (!bannedRoutes.isEmpty()) {
if (bannedRoutes.matches(route)) {
return true;
}
}
boolean whiteListed = false;
boolean whiteListInUse = false;
/* check if agency is whitelisted for this plan */
if (!whiteListedAgencies.isEmpty()) {
whiteListInUse = true;
if (whiteListedAgencies.contains(route.getAgency().getId())) {
whiteListed = true;
}
}
/* check if route is whitelisted for this plan */
if (!whiteListedRoutes.isEmpty()) {
whiteListInUse = true;
if (whiteListedRoutes.matches(route)) {
whiteListed = true;
}
}
if (whiteListInUse && !whiteListed) {
return true;
}
return false;
}
/** Check if route is preferred according to this request. */
public long preferencesPenaltyForRoute(Route route) {
long preferences_penalty = 0;
FeedScopedId agencyID = route.getAgency().getId();
if (!preferredRoutes.equals(RouteMatcher.emptyMatcher()) || !preferredAgencies.isEmpty()) {
boolean isPreferedRoute = preferredRoutes.matches(route);
boolean isPreferedAgency = preferredAgencies.contains(agencyID);
if (!isPreferedRoute && !isPreferedAgency) {
preferences_penalty += otherThanPreferredRoutesPenalty;
}
}
boolean isUnpreferedRoute = unpreferredRoutes.matches(route);
boolean isUnpreferedAgency = unpreferredAgencies.contains(agencyID);
if (isUnpreferedRoute || isUnpreferedAgency) {
preferences_penalty += useUnpreferredRoutesPenalty;
}
return preferences_penalty;
}
/**
* Sets the bicycle triangle routing parameters -- the relative importance of safety, flatness, and speed.
* These three fields of the RoutingRequest should have values between 0 and 1, and should add up to 1.
* This setter function accepts any three numbers and will normalize them to add up to 1.
*/
public void setTriangleNormalized(double safe, double slope, double time) {
if(safe == 0 && slope == 0 && time == 0) {
var oneThird = 1f /3;
safe = oneThird;
slope = oneThird;
time = oneThird;
}
safe = setMinValue(safe);
slope = setMinValue(slope);
time = setMinValue(time);
double total = safe + slope + time;
if(total != 1) {
LOG.warn("Bicycle triangle factors don't add up to 1. Values will be scaled proportionally to each other.");
}
safe /= total;
slope /= total;
time /= total;
this.bikeTriangleSafetyFactor = safe;
this.bikeTriangleSlopeFactor = slope;
this.bikeTriangleTimeFactor = time;
}
private double setMinValue(double value) {
return Math.max(0, value);
}
/** Create a new ShortestPathTree instance using the DominanceFunction specified in this RoutingRequest. */
public ShortestPathTree getNewShortestPathTree() {
return this.dominanceFunction.getNewShortestPathTree(this);
}
public Comparator getPathComparator(boolean compareStartTimes) {
if ("duration".equals(pathComparator)) {
return new DurationComparator();
}
return new PathComparator(compareStartTimes);
}
/**
* How close to do you have to be to the start or end to be considered "close".
*
* @see RoutingRequest#isCloseToStartOrEnd(Vertex)
* @see DominanceFunction#betterOrEqualAndComparable(State, State)
*/
private static final int MAX_CLOSENESS_METERS = 500;
private Envelope fromEnvelope;
private Envelope toEnvelope;
/**
* Returns if the vertex is considered "close" to the start or end point of the request.
* This is useful if you want to allow loops in car routes under certain conditions.
*
* Note: If you are doing Raptor access/egress searches this method does not take the possible
* intermediate points (stations) into account. This means that stations might be skipped
* because a car route to it cannot be found and a suboptimal route to another station is
* returned instead.
*
* If you encounter a case of this, you can adjust this code to take this into account.
*
* @see RoutingRequest#MAX_CLOSENESS_METERS
* @see DominanceFunction#betterOrEqualAndComparable(State, State)
*/
public boolean isCloseToStartOrEnd(Vertex vertex) {
if(from == null || to == null || from.getCoordinate() == null || to.getCoordinate() == null) {
return false;
}
if (fromEnvelope == null) {
fromEnvelope = getEnvelope(from.getCoordinate(), MAX_CLOSENESS_METERS);
}
if (toEnvelope == null) {
toEnvelope = getEnvelope(to.getCoordinate(), MAX_CLOSENESS_METERS);
}
return fromEnvelope.intersects(vertex.getCoordinate()) || toEnvelope.intersects(
vertex.getCoordinate());
}
private static Envelope getEnvelope(Coordinate c, int meters) {
double lat = SphericalDistanceLibrary.metersToDegrees(meters);
double lon = SphericalDistanceLibrary.metersToLonDegrees(meters, c.y);
Envelope env = new Envelope(c);
env.expandBy(lon, lat);
if (LOG.isDebugEnabled()) {
var geom = new GeometryFactory().toGeometry(env);
var geoJson = new GeometryJSON();
try {
var stream = new ByteArrayOutputStream();
geoJson.write(geom, stream);
LOG.debug(
"Computing {}m envelope around coordinate {}. GeoJSON: {}", meters, c,
stream.toString()
);
}
catch (IOException e) {
LOG.error("Could not build debug GeoJSON", e);
}
}
return env;
}
/**
* This method is needed because we sometimes traverse edges with no graph. It returns a
* default intersection traversal model if no graph is present.
*/
public IntersectionTraversalCostModel getIntersectionTraversalCostModel() {
if (this.rctx != null && this.rctx.graph != null) {
return this.rctx.graph.getIntersectionTraversalModel();
} else {
// This is only to maintain compatibility with existing tests
return Graph.DEFAULT_INTERSECTION_TRAVERSAL_COST_MODEL;
}
}
}