
org.opentripplanner.street.search.state.StateData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
The newest version!
package org.opentripplanner.street.search.state;
import static org.opentripplanner.street.search.state.VehicleRentalState.BEFORE_RENTING;
import static org.opentripplanner.street.search.state.VehicleRentalState.HAVE_RENTED;
import static org.opentripplanner.street.search.state.VehicleRentalState.RENTING_FLOATING;
import static org.opentripplanner.street.search.state.VehicleRentalState.RENTING_FROM_STATION;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.street.model.RentalFormFactor;
import org.opentripplanner.street.search.TraverseMode;
import org.opentripplanner.street.search.request.StreetSearchRequest;
/**
* StateData contains the components of search state that are unlikely to be changed as often as
* time or weight. This avoids frequent duplication, which should have a positive impact on both
* time and space use during searches.
*/
public class StateData implements Cloneable {
protected boolean vehicleParked;
protected VehicleRentalState vehicleRentalState;
protected boolean mayKeepRentedVehicleAtDestination;
protected CarPickupState carPickupState;
/**
* The preferred mode, which may differ from backMode when for example walking with a bike. It may
* also change during traversal when switching between modes as in the case of Park & Ride or Kiss
* & Ride.
*/
protected TraverseMode currentMode;
/**
* The mode that was used to traverse the backEdge
*/
protected TraverseMode backMode;
protected boolean backWalkingBike;
public String vehicleRentalNetwork;
public RentalFormFactor rentalVehicleFormFactor;
/** This boolean is set to true upon transition from a normal street to a no-through-traffic street. */
protected boolean enteredNoThroughTrafficArea;
protected boolean insideNoRentalDropOffArea = false;
public Set noRentalDropOffZonesAtStartOfReverseSearch = Set.of();
/** Private constructor, use static methods to get a set of initial states. */
private StateData(StreetMode requestMode) {
currentMode =
switch (requestMode) {
// when renting or using a flex vehicle, you start on foot until you have found the vehicle
case NOT_SET, WALK, BIKE_RENTAL, SCOOTER_RENTAL, CAR_RENTAL, FLEXIBLE -> TraverseMode.WALK;
// when cycling all the way or to a stop, you start on your own bike
case BIKE, BIKE_TO_PARK -> TraverseMode.BICYCLE;
// when driving (not car rental) you start in your own car or your driver's car
case CAR, CAR_TO_PARK, CAR_PICKUP, CAR_HAILING -> TraverseMode.CAR;
};
}
/**
* Returns a set of initial StateDatas based on the options from the RouteRequest
*/
public static List getInitialStateDatas(StreetSearchRequest request) {
var rentalPreferences = request.preferences().rental(request.mode());
return getInitialStateDatas(
request.mode(),
request.arriveBy(),
rentalPreferences != null
? rentalPreferences.allowArrivingInRentedVehicleAtDestination()
: false
);
}
/**
* Returns an initial StateData based on the options from the {@link StreetSearchRequest}. This returns always
* only a single state, which is considered the "base case", should there be several possible for
* the given {@code request}.
*/
public static StateData getBaseCaseStateData(StreetSearchRequest request) {
var rentalPreferences = request.preferences().rental(request.mode());
var stateDatas = getInitialStateDatas(
request.mode(),
request.arriveBy(),
rentalPreferences != null
? rentalPreferences.allowArrivingInRentedVehicleAtDestination()
: false
);
var baseCaseDatas =
switch (request.mode()) {
case WALK, BIKE, BIKE_TO_PARK, CAR, CAR_TO_PARK, FLEXIBLE, NOT_SET -> stateDatas;
case CAR_PICKUP, CAR_HAILING -> stateDatas
.stream()
.filter(d -> d.carPickupState == CarPickupState.IN_CAR)
.toList();
case BIKE_RENTAL, SCOOTER_RENTAL, CAR_RENTAL -> {
if (request.arriveBy()) {
yield stateDatas
.stream()
.filter(d ->
d.vehicleRentalState == RENTING_FROM_STATION ||
d.vehicleRentalState == RENTING_FLOATING
)
.toList();
} else {
yield stateDatas;
}
}
};
if (baseCaseDatas.size() != 1) {
throw new IllegalStateException(
"Unable to create only a single state for %s".formatted(request)
);
}
return baseCaseDatas.get(0);
}
private static List getInitialStateDatas(
StreetMode requestMode,
boolean arriveBy,
boolean allowArrivingInRentedVehicleAtDestination
) {
List res = new ArrayList<>();
var proto = new StateData(requestMode);
// carPickup searches may start and end in two distinct states:
// - CAR / IN_CAR where pickup happens directly at the bus stop
// - WALK / WALK_FROM_DROP_OFF or WALK_TO_PICKUP for cases with an initial walk
// For forward/reverse searches to be symmetric both initial states need to be created.
if (requestMode.includesPickup()) {
var inCarPickupStateData = proto.clone();
inCarPickupStateData.carPickupState = CarPickupState.IN_CAR;
inCarPickupStateData.currentMode = TraverseMode.CAR;
res.add(inCarPickupStateData);
var walkingPickupStateData = proto.clone();
walkingPickupStateData.carPickupState =
arriveBy ? CarPickupState.WALK_FROM_DROP_OFF : CarPickupState.WALK_TO_PICKUP;
walkingPickupStateData.currentMode = TraverseMode.WALK;
res.add(walkingPickupStateData);
}
// Vehicle rental searches may end in four states (see State#isFinal()):
// When searching forward:
// - RENTING_FROM_STATION when allowKeepingRentedVehicleAtDestination is set
// - RENTING_FLOATING
// - HAVE_RENTED
// When searching backwards:
// - BEFORE_RENTING
else if (requestMode.includesRenting()) {
if (arriveBy) {
if (allowArrivingInRentedVehicleAtDestination) {
var keptVehicleStateData = proto.clone();
keptVehicleStateData.vehicleRentalState = RENTING_FROM_STATION;
keptVehicleStateData.currentMode = TraverseMode.BICYCLE;
keptVehicleStateData.mayKeepRentedVehicleAtDestination = true;
res.add(keptVehicleStateData);
}
var floatingRentalStateData = proto.clone();
floatingRentalStateData.vehicleRentalState = RENTING_FLOATING;
floatingRentalStateData.rentalVehicleFormFactor = toFormFactor(requestMode);
floatingRentalStateData.currentMode = TraverseMode.BICYCLE;
res.add(floatingRentalStateData);
var stationReturnedStateData = proto.clone();
stationReturnedStateData.vehicleRentalState = HAVE_RENTED;
stationReturnedStateData.currentMode = TraverseMode.WALK;
res.add(stationReturnedStateData);
} else {
var beforeRentalStateData = proto.clone();
beforeRentalStateData.vehicleRentalState = BEFORE_RENTING;
res.add(beforeRentalStateData);
}
}
// If the itinerary is to begin with a car that is parked for transit the initial state is
// - In arriveBy searches is with the car already "parked" and in WALK mode
// - In departAt searches, we are in CAR mode and "unparked".
else if (requestMode.includesParking()) {
var parkAndRideStateData = proto.clone();
parkAndRideStateData.vehicleParked = arriveBy;
parkAndRideStateData.currentMode =
parkAndRideStateData.vehicleParked
? TraverseMode.WALK
: requestMode.includesBiking() ? TraverseMode.BICYCLE : TraverseMode.CAR;
res.add(parkAndRideStateData);
} else {
res.add(proto.clone());
}
return res;
}
private static RentalFormFactor toFormFactor(StreetMode streetMode) {
return switch (streetMode) {
case BIKE_RENTAL -> RentalFormFactor.BICYCLE;
case SCOOTER_RENTAL -> RentalFormFactor.SCOOTER;
case CAR_RENTAL -> RentalFormFactor.CAR;
// there is no default here, so you get a compiler error when you add a new value to the enum
case NOT_SET,
WALK,
BIKE,
BIKE_TO_PARK,
CAR,
CAR_TO_PARK,
CAR_PICKUP,
CAR_HAILING,
FLEXIBLE -> throw new IllegalStateException(
"Cannot convert street mode %s to a form factor".formatted(streetMode)
);
};
}
protected StateData clone() {
try {
return (StateData) super.clone();
} catch (CloneNotSupportedException e1) {
throw new IllegalStateException("This is not happening");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy