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

org.opentripplanner.api.common.RoutingResource Maven / Gradle / Ivy

package org.opentripplanner.api.common;

import static org.opentripplanner.api.common.LocationStringParser.fromOldStyleString;
import static org.opentripplanner.api.common.RequestToPreferencesMapper.setIfNotNull;

import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import org.opentripplanner.api.parameter.QualifiedModeSet;
import org.opentripplanner.ext.dataoverlay.api.DataOverlayParameters;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.core.BicycleOptimizeType;
import org.opentripplanner.standalone.api.OtpServerRequestContext;
import org.opentripplanner.util.OTPFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class defines all the JAX-RS query parameters for a path search as fields, allowing them to
 * be inherited by other REST resource classes (the trip planner and the Analyst WMS or tile
 * resource). They will be properly included in API docs generated by Enunciate. This implies that
 * the concrete REST resource subclasses will be request-scoped rather than singleton-scoped.
 * 

* All defaults should be specified in the RouteRequest, NOT as annotations on the query * parameters. JSON router configuration can then overwrite those built-in defaults, and only the * fields of the resulting prototype routing request for which query parameters are found are * overwritten here. This establishes a priority chain: RouteRequest field initializers, then JSON * router config, then query parameters. *

* See the configuration for documentation of each field. */ @SuppressWarnings({ "FieldMayBeFinal", "unused" }) public abstract class RoutingResource { private static final Logger LOG = LoggerFactory.getLogger(RoutingResource.class); /** * The start location -- either latitude, longitude pair in degrees or a Vertex label. For * example, 40.714476,-74.005966 or * mtanyctsubway_A27_S. */ @QueryParam("fromPlace") protected String fromPlace; /** The end location (see fromPlace for format). */ @QueryParam("toPlace") protected String toPlace; /** * An ordered list of intermediate locations to be visited (see the fromPlace for format). * Parameter can be specified multiple times. * * @deprecated TODO OTP2 - Regression. Not currently working in OTP2. Must be re-implemented * - using raptor. */ @Deprecated @QueryParam("intermediatePlaces") protected List intermediatePlaces; /** The date that the trip should depart (or arrive, for requests where arriveBy is true). */ @QueryParam("date") protected String date; /** The time that the trip should depart (or arrive, for requests where arriveBy is true). */ @QueryParam("time") protected String time; /** * The length of the search-window in seconds. This parameter is optional. *

* The search-window is defined as the duration between the earliest-departure-time(EDT) and the * latest-departure-time(LDT). OTP will search for all itineraries in this departure window. If * {@code arriveBy=true} the {@code dateTime} parameter is the latest-arrival-time, so OTP will * dynamically calculate the EDT. Using a short search-window is faster than using a longer one, * but the search duration is not linear. Using a \"too\" short search-window will waste resources * server side, while using a search-window that is too long will be slow. *

* OTP will dynamically calculate a reasonable value for the search-window, if not provided. The * calculation comes with a significant overhead (10-20% extra). Whether you should use the * dynamic calculated value or pass in a value depends on your use-case. For a travel planner in a * small geographical area, with a dense network of public transportation, a fixed value between * 40 minutes and 2 hours makes sense. To find the appropriate search-window, adjust it so that * the number of itineraries on average is around the wanted {@code numItineraries}. Make sure you * set the {@code numItineraries} to a high number while testing. For a country wide area like * Norway, using the dynamic search-window is the best. *

* When paginating, the search-window is calculated using the {@code numItineraries} in the * original search together with statistics from the search for the last page. This behaviour is * configured server side, and can not be overridden from the client. *

* The search-window used is returned to the response metadata as {@code searchWindowUsed} for * debugging purposes. */ @QueryParam("searchWindow") protected Integer searchWindow; /** * Use the cursor to go to the next "page" of itineraries. Copy the cursor from the last response * and keep the original request as is. This will enable you to search for itineraries in the next * or previous time-window. *

* This is an optional parameter. */ @QueryParam("pageCursor") public String 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: *

    *
  • * Traveler want to find thee 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 it gives the best result, because it includes * the two next use-cases. 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. This works great with paging, request next/previous time-window. *
  • *
  • * 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=false} 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. Do not support paging. * Set {@code timetableView=false} and {@code arriveBy=true}. *
  • *
* Default: true */ @QueryParam("timetableView") public Boolean timetableView; @Deprecated @QueryParam("arriveBy") protected Boolean arriveBy; /** * Whether the trip must be wheelchair accessible. * * @deprecated TODO OTP2 Regression. Not currently working in OTP2. This is not implemented * in Raptor jet. */ @Deprecated @QueryParam("wheelchair") protected Boolean wheelchair; /** * The maximum time (in seconds) of pre-transit travel when using drive-to-transit (park and ride * or kiss and ride). Defaults to unlimited. *

* See https://github.com/opentripplanner/OpenTripPlanner/issues/2886 * * @deprecated TODO OTP2 - Regression. Not currently working in OTP2. */ @Deprecated @QueryParam("maxPreTransitTime") protected Integer maxPreTransitTime; /** * A multiplier for how bad walking with a bike is, compared to being in transit for equal lengths * of time. Defaults to 3. */ @QueryParam("bikeWalkingReluctance") protected Double bikeWalkingReluctance; @QueryParam("walkReluctance") protected Double walkReluctance; @QueryParam("bikeReluctance") protected Double bikeReluctance; @QueryParam("carReluctance") protected Double carReluctance; /** * 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. */ @QueryParam("waitReluctance") protected Double waitReluctance; @QueryParam("walkSpeed") protected Double walkSpeed; @QueryParam("bikeSpeed") protected Double bikeSpeed; @QueryParam("bikeWalkingSpeed") protected Double bikeWalkingSpeed; @QueryParam("bikeSwitchTime") protected Integer bikeSwitchTime; @QueryParam("bikeSwitchCost") protected Integer bikeSwitchCost; @QueryParam("triangleSafetyFactor") protected Double triangleSafetyFactor; @QueryParam("triangleSlopeFactor") protected Double triangleSlopeFactor; @QueryParam("triangleTimeFactor") protected Double triangleTimeFactor; /** * The set of characteristics that the user wants to optimize for. @See OptimizeType. * * @deprecated TODO OTP2 this should be completely removed and done only with individual cost * parameters * Also: apparently OptimizeType only affects BICYCLE mode traversal of * street segments. If this is the case it should be very well * documented and carried over into the Enum name. */ @Deprecated @QueryParam("optimize") protected BicycleOptimizeType bikeOptimizeType; /** * The set of modes that a user is willing to use, with qualifiers stating whether vehicles should * be parked, rented, etc. *

* The possible values of the comma-separated list are: * *

    *
  • WALK
  • *
  • TRANSIT
  • *
  • BICYCLE
  • *
  • BICYCLE_RENT
  • *
  • BICYCLE_PARK
  • *
  • CAR
  • *
  • CAR_PARK
  • *
  • TRAM
  • *
  • SUBWAY
  • *
  • RAIL
  • *
  • BUS
  • *
  • CABLE_CAR
  • *
  • FERRY
  • *
  • GONDOLA
  • *
  • FUNICULAR
  • *
  • AIRPLANE
  • *
*

* For a more complete discussion of this parameter see * Routing modes. */ @QueryParam("mode") protected QualifiedModeSet modes; /** * The minimum time, in seconds, between successive trips on different vehicles. This is designed * to allow for imperfect schedule adherence. This is a minimum; transfers over longer distances * might use a longer time. * * @deprecated TODO OTP2: Needs to be implemented */ @Deprecated @QueryParam("minTransferTime") protected Integer minTransferTime; /** The maximum number of possible itineraries to return. */ @QueryParam("numItineraries") protected Integer numItineraries; /** * The comma-separated list of preferred agencies. * * @deprecated TODO OTP2: Needs to be implemented */ @Deprecated @QueryParam("preferredAgencies") protected String preferredAgencies; /** * The comma-separated list of unpreferred agencies. * * @deprecated TODO OTP2: Needs to be implemented */ @Deprecated @QueryParam("unpreferredAgencies") protected String unpreferredAgencies; /** * The comma-separated list of banned agencies. */ @QueryParam("bannedAgencies") protected String bannedAgencies; /** * Functions the same as banned agencies, except only the listed agencies are allowed. */ @QueryParam("whiteListedAgencies") protected String whiteListedAgencies; /** * Whether intermediate stops -- those that the itinerary passes in a vehicle, but does not board * or alight at -- should be returned in the response. For example, on a Q train trip from * Prospect Park to DeKalb Avenue, whether 7th Avenue and Atlantic Avenue should be included. */ @QueryParam("showIntermediateStops") @DefaultValue("false") protected Boolean showIntermediateStops; @QueryParam("walkBoardCost") protected Integer walkBoardCost; @QueryParam("bikeBoardCost") protected Integer bikeBoardCost; @QueryParam("walkSafetyFactor") protected Double walkSafetyFactor; @QueryParam("allowKeepingRentedBicycleAtDestination") protected Boolean allowKeepingRentedBicycleAtDestination; @QueryParam("keepingRentedBicycleAtDestinationCost") protected Double keepingRentedBicycleAtDestinationCost; @QueryParam("allowedVehicleRentalNetworks") protected Set allowedVehicleRentalNetworks; @QueryParam("bannedVehicleRentalNetworks") protected Set bannedVehicleRentalNetworks; @QueryParam("bikeParkTime") protected Integer bikeParkTime; @QueryParam("bikeParkCost") protected Integer bikeParkCost; @QueryParam("carParkTime") protected Integer carParkTime = 60; @QueryParam("carParkCost") protected Integer carParkCost = 120; @QueryParam("requiredVehicleParkingTags") protected Set requiredVehicleParkingTags = Set.of(); @QueryParam("bannedVehicleParkingTags") protected Set bannedVehicleParkingTags = Set.of(); /** * The comma-separated list of banned routes. The format is agency_[routename][_routeid], so * TriMet_100 (100 is route short name) or Trimet__42 (two underscores, 42 is the route internal * ID). */ @Deprecated @QueryParam("bannedRoutes") protected String bannedRoutes; /** * Functions the same as bannnedRoutes, except only the listed routes are allowed. */ @QueryParam("whiteListedRoutes") @Deprecated protected String whiteListedRoutes; /** * The list of preferred routes. The format is agency_[routename][_routeid], so TriMet_100 (100 is * route short name) or Trimet__42 (two underscores, 42 is the route internal ID). * * @deprecated TODO OTP2 Needs to be implemented */ @Deprecated @QueryParam("preferredRoutes") protected String preferredRoutes; /** * The list of unpreferred routes. The format is agency_[routename][_routeid], so TriMet_100 (100 * is route short name) or Trimet__42 (two underscores, 42 is the route internal ID). * * @deprecated TODO OTP2 Needs to be implemented */ @Deprecated @QueryParam("unpreferredRoutes") protected String unpreferredRoutes; /** * Penalty added for using every route that is not preferred if user set any route as preferred, * i.e. number of seconds that we are willing to wait for preferred route. * * @deprecated TODO OTP2 Needs to be implemented */ @Deprecated @QueryParam("otherThanPreferredRoutesPenalty") protected Integer otherThanPreferredRoutesPenalty; /** * The comma-separated list of banned trips. The format is feedId:tripId */ @QueryParam("bannedTrips") protected String bannedTrips; /** * A comma-separated list of banned stops. A stop is banned by ignoring its pre-board and * pre-alight edges. This means the stop will be reachable via the street network. Also, it is * still possible to travel through the stop. Just boarding and alighting is prohibited. The * format is agencyId_stopId, so: TriMet_2107 * * @deprecated TODO OTP2 This no longer works in OTP2, see issue #2843. */ @Deprecated @QueryParam("bannedStops") protected String bannedStops; /** * A comma-separated list of banned stops. A stop is banned by ignoring its pre-board and * pre-alight edges. This means the stop will be reachable via the street network. It is not * possible to travel through the stop. For example, this parameter can be used when a train * station is destroyed, such that no trains can drive through the station anymore. The format is * agencyId_stopId, so: TriMet_2107 * * @deprecated TODO OTP2 This no longer works in OTP2, see issue #2843. */ @Deprecated @QueryParam("bannedStopsHard") protected String bannedStopsHard; @QueryParam("transferPenalty") protected Integer transferPenalty; /** * An additional penalty added to boardings after the first when the transfer is not preferred. * Preferred transfers also include timed transfers. The value is in OTP's internal weight units, * which are roughly equivalent to seconds. Set this to a high value to discourage transfers that * are not preferred. Of course, transfers that save significant time or walking will still be * taken. When no preferred or timed transfer is defined, this value is ignored. *

* TODO OTP2 This JavaDoc needs clarification. What is a "preferred" Transfer, the GTFS * specification do not have "preferred Transfers". The GTFS spec transfer * type 0 is _Recommended transfer point_ - is this what is meant? * * @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 @QueryParam("nonpreferredTransferPenalty") protected Integer nonpreferredTransferPenalty; /** * The maximum number of transfers (that is, one plus the maximum number of boardings) that a trip * will be allowed. *

* Consider using the {@link #transferPenalty} instead of this parameter. *

* See https://github.com/opentripplanner/OpenTripPlanner/issues/2886 * * @deprecated TODO OTP2 Regression. A 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. * 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. */ @Deprecated @QueryParam("maxTransfers") protected Integer maxTransfers; /** * If true, goal direction is turned off and a full path tree is built (specify only once) * * @Deprecated - This is not supported in OTP2 any more. */ @Deprecated @QueryParam("batch") protected Boolean batch; /** * A transit stop required to be the first stop in the search (AgencyId_StopId) * * @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 @QueryParam("startTransitStopId") protected String startTransitStopId; /** * A transit trip acting as a starting "state" for depart-onboard routing (AgencyId_TripId) * * @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 @QueryParam("startTransitTripId") protected String startTransitTripId; /** * When subtracting initial wait time, do not subtract more than this value, to prevent overly * optimistic trips. Reasoning is that it is reasonable to delay a trip start 15 minutes to make a * better trip, but that it is not reasonable to delay a trip start 15 hours; if that is to be * done, the time needs to be included in the trip time. This number depends on the transit * system; for transit systems where trips are planned around the vehicles, this number can be * much higher. For instance, it's perfectly reasonable to delay one's trip 12 hours if one is * taking a cross-country Amtrak train from Emeryville to Chicago. Has no effect in stock OTP, * only in Analyst. *

* A value of 0 means that initial wait time will not be subtracted out (will be clamped to 0). A * value of -1 (the default) means that clamping is disabled, so any amount of initial wait time * will be subtracted out. * * @deprecated This parameter is not in use any more. */ @Deprecated @QueryParam("clampInitialWait") protected Long clampInitialWait; /** * THIS PARAMETER IS NO LONGER IN USE. *

* If true, this trip will be reverse-optimized on the fly. Otherwise, reverse-optimization will * occur once a trip has been chosen (in Analyst, it will not be done at all). * * @deprecated This parameter is not in use any more after the transit search switched from AStar * to Raptor. */ @Deprecated @QueryParam("reverseOptimizeOnTheFly") protected Boolean reverseOptimizeOnTheFly; /** * The number of seconds to add before boarding a transit leg. It is recommended to use the * {@code boardTimes} in the {@code router-config.json} to set this for each mode. *

* Unit is seconds. Default value is 0. */ @QueryParam("boardSlack") protected Integer boardSlack; /** * The number of seconds to add after alighting a transit leg. It is recommended to use the * {@code alightTimes} in the {@code router-config.json} to set this for each mode. *

* Unit is seconds. Default value is 0. */ @QueryParam("alightSlack") protected Integer alightSlack; @QueryParam("locale") private String locale; /** * If true, realtime updates are ignored during this search. * * @deprecated TODO OTP2 Regression. Not currently working in OTP2. */ @Deprecated @QueryParam("ignoreRealtimeUpdates") protected Boolean ignoreRealtimeUpdates; /** * If true, the remaining weight heuristic is disabled. Currently only implemented for the long * distance path service. */ @QueryParam("disableRemainingWeightHeuristic") protected Boolean disableRemainingWeightHeuristic; /** * See https://github.com/opentripplanner/OpenTripPlanner/issues/2886 * * @deprecated TODO OTP2 This is not useful as a search parameter, but could be used as a * post search filter to reduce number of itineraries down to an * acceptable number, but there are probably better ways to do that. */ @Deprecated @QueryParam("maxHours") private Double maxHours; /** * See https://github.com/opentripplanner/OpenTripPlanner/issues/2886 * * @deprecated see {@link #maxHours} */ @QueryParam("useRequestedDateTimeInMaxHours") @Deprecated private Boolean useRequestedDateTimeInMaxHours; /** * @deprecated This is not supported in OTP2. */ @QueryParam("disableAlertFiltering") @Deprecated private Boolean disableAlertFiltering; @QueryParam("debugItineraryFilter") protected Boolean debugItineraryFilter; @QueryParam("geoidElevation") protected Boolean geoidElevation; @QueryParam("useVehicleParkingAvailabilityInformation") protected Boolean useVehicleParkingAvailabilityInformation; @QueryParam("debugRaptorStops") private String debugRaptorStops; @QueryParam("debugRaptorPath") private String debugRaptorPath; /** * somewhat ugly bug fix: the graphService is only needed here for fetching per-graph time zones. * this should ideally be done when setting the routing context, but at present departure/ arrival * time is stored in the request as an epoch time with the TZ already resolved, and other code * depends on this behavior. (AMB) Alternatively, we could eliminate the separate RouteRequest * objects and just resolve vertices and timezones here right away, but just ignore them in * semantic equality checks. */ @Context protected OtpServerRequestContext serverContext; /** * Range/sanity check the query parameter fields and build a Request object from them. * * @param queryParameters incoming request parameters */ protected RouteRequest buildRequest(MultivaluedMap queryParameters) { RouteRequest request = serverContext.defaultRouteRequest(); // The routing request should already contain defaults, which are set when it is initialized or // in the JSON router configuration and cloned. We check whether each parameter was supplied // before overwriting the default. setIfNotNull(fromPlace, it -> request.setFrom(fromOldStyleString(it))); setIfNotNull(toPlace, it -> request.setTo(fromOldStyleString(it))); { //FIXME: move into setter method on routing request ZoneId tz = serverContext.transitService().getTimeZone(); if (date == null && time != null) { // Time was provided but not date LOG.debug("parsing ISO datetime {}", time); try { // If the time query param doesn't specify a timezone, use the graph's default. See issue #1373. DatatypeFactory df = javax.xml.datatype.DatatypeFactory.newInstance(); XMLGregorianCalendar xmlGregCal = df.newXMLGregorianCalendar(time); ZonedDateTime dateTime = xmlGregCal.toGregorianCalendar().toZonedDateTime(); if (xmlGregCal.getTimezone() == DatatypeConstants.FIELD_UNDEFINED) { dateTime = dateTime.withZoneSameLocal(tz); } request.setDateTime(dateTime.toInstant()); } catch (DatatypeConfigurationException e) { request.setDateTime(date, time, tz); } } else { request.setDateTime(date, time, tz); } } setIfNotNull(searchWindow, it -> request.setSearchWindow(Duration.ofSeconds(it))); setIfNotNull(pageCursor, request::setPageCursorFromEncoded); setIfNotNull(timetableView, request::setTimetableView); setIfNotNull(wheelchair, request::setWheelchair); setIfNotNull(numItineraries, request::setNumItineraries); { var journey = request.journey(); /* Temporary code to get bike/car parking and renting working. */ if (modes != null && !modes.qModes.isEmpty()) { journey.setModes(modes.getRequestModes()); } { var rental = journey.rental(); setIfNotNull( allowKeepingRentedBicycleAtDestination, rental::setAllowArrivingInRentedVehicleAtDestination ); setIfNotNull(allowedVehicleRentalNetworks, rental::setAllowedNetworks); setIfNotNull(bannedVehicleRentalNetworks, rental::setBannedNetworks); } { var parking = journey.parking(); setIfNotNull(bannedVehicleParkingTags, parking::setBannedTags); setIfNotNull(requiredVehicleParkingTags, parking::setRequiredTags); } setIfNotNull(arriveBy, request::setArriveBy); { var transit = journey.transit(); // Filter Agencies setIfNotNull(preferredAgencies, transit::setPreferredAgenciesFromString); setIfNotNull(unpreferredAgencies, transit::setUnpreferredAgenciesFromString); setIfNotNull(bannedAgencies, transit::setBannedAgenciesFromSting); setIfNotNull(whiteListedAgencies, transit::setWhiteListedAgenciesFromSting); // Filter Routes setIfNotNull(preferredRoutes, transit::setPreferredRoutesFromString); setIfNotNull(unpreferredRoutes, transit::setUnpreferredRoutesFromString); setIfNotNull(bannedRoutes, transit::setBannedRoutesFromString); setIfNotNull(whiteListedRoutes, transit::setWhiteListedRoutesFromString); // Filter Trips setIfNotNull(bannedTrips, transit::setBannedTripsFromString); } { var debugRaptor = journey.transit().raptorDebugging(); setIfNotNull(debugRaptorStops, debugRaptor::withStops); setIfNotNull(debugRaptorPath, debugRaptor::withPath); } } if (locale != null) { request.setLocale(Locale.forLanguageTag(locale.replaceAll("-", "_"))); } request.withPreferences(preferences -> { // Map all preferences, note dependency on 'isTripPlannedForNow'. new RequestToPreferencesMapper(this, preferences, request.isTripPlannedForNow()).map(); if (OTPFeature.DataOverlay.isOn()) { var dataOverlayParameters = DataOverlayParameters.parseQueryParams(queryParameters); if (!dataOverlayParameters.isEmpty()) { preferences.withSystem(it -> it.withDataOverlay(dataOverlayParameters)); } } }); return request; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy