diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index e4ecd2be02a..48d60701a96 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -1542,6 +1542,7 @@ public enum GraphQLPlanAccessMode { BICYCLE, BICYCLE_PARKING, BICYCLE_RENTAL, + CAR, CAR_DROP_OFF, CAR_PARKING, CAR_RENTAL, @@ -1625,6 +1626,7 @@ public enum GraphQLPlanDirectMode { public enum GraphQLPlanEgressMode { BICYCLE, BICYCLE_RENTAL, + CAR, CAR_PICKUP, CAR_RENTAL, FLEX, @@ -1927,6 +1929,7 @@ public void setGraphQLWalk(GraphQLWalkPreferencesInput walk) { public enum GraphQLPlanTransferMode { BICYCLE, + CAR, WALK, } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/AccessModeMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/AccessModeMapper.java index ac4c90a1a56..e0e3ac0bbb2 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/AccessModeMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/AccessModeMapper.java @@ -13,6 +13,7 @@ public static StreetMode map(GraphQLTypes.GraphQLPlanAccessMode mode) { case BICYCLE -> StreetMode.BIKE; case BICYCLE_RENTAL -> StreetMode.BIKE_RENTAL; case BICYCLE_PARKING -> StreetMode.BIKE_TO_PARK; + case CAR -> StreetMode.CAR; case CAR_RENTAL -> StreetMode.CAR_RENTAL; case CAR_PARKING -> StreetMode.CAR_TO_PARK; case CAR_DROP_OFF -> StreetMode.CAR_PICKUP; diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/EgressModeMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/EgressModeMapper.java index f03b160ac97..ddcaa255f2a 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/EgressModeMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/EgressModeMapper.java @@ -12,6 +12,7 @@ public static StreetMode map(GraphQLTypes.GraphQLPlanEgressMode mode) { return switch (mode) { case BICYCLE -> StreetMode.BIKE; case BICYCLE_RENTAL -> StreetMode.BIKE_RENTAL; + case CAR -> StreetMode.CAR; case CAR_RENTAL -> StreetMode.CAR_RENTAL; case CAR_PICKUP -> StreetMode.CAR_PICKUP; case FLEX -> StreetMode.FLEXIBLE; diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ModePreferencesMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ModePreferencesMapper.java index 32d3456df57..663e93acca9 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ModePreferencesMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ModePreferencesMapper.java @@ -168,5 +168,10 @@ private static void validateStreetModes(JourneyRequest journey) { "If BICYCLE is used for access, egress or transfer, then it should be used for all." ); } + if (modes.contains(StreetMode.CAR) && modes.size() != 1) { + throw new IllegalArgumentException( + "If CAR is used for access, egress or transfer, then it should be used for all." + ); + } } } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/TransferModeMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/TransferModeMapper.java index ffa7363e3a7..18d2c0e3811 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/TransferModeMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/TransferModeMapper.java @@ -10,6 +10,7 @@ public class TransferModeMapper { public static StreetMode map(GraphQLTypes.GraphQLPlanTransferMode mode) { return switch (mode) { + case CAR -> StreetMode.CAR; case BICYCLE -> StreetMode.BIKE; case WALK -> StreetMode.WALK; }; diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java b/application/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java index 974b8dd10c3..bf9abd3a60d 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java @@ -10,7 +10,8 @@ class RequestModesMapper { - private static final Predicate IS_BIKE = m -> m == StreetMode.BIKE; + private static final Predicate IS_BIKE_OR_CAR = m -> + m == StreetMode.BIKE || m == StreetMode.CAR; private static final String accessModeKey = "accessMode"; private static final String egressModeKey = "egressMode"; private static final String directModeKey = "directMode"; @@ -27,7 +28,10 @@ static RequestModes mapRequestModes(Map modesInput) { ensureValueAndSet(accessMode, mBuilder::withAccessMode); ensureValueAndSet((StreetMode) modesInput.get(egressModeKey), mBuilder::withEgressMode); ensureValueAndSet((StreetMode) modesInput.get(directModeKey), mBuilder::withDirectMode); - Optional.ofNullable(accessMode).filter(IS_BIKE).ifPresent(mBuilder::withTransferMode); + // The only cases in which the transferMode isn't WALK are when the accessMode is either BIKE or CAR. + // In these cases, the transferMode is the same as the accessMode. This check is not strictly necessary + // if there is a need for more freedom for specifying the transferMode. + Optional.ofNullable(accessMode).filter(IS_BIKE_OR_CAR).ifPresent(mBuilder::withTransferMode); return mBuilder.build(); } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java index 32495617db9..04aeac76049 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -28,6 +28,7 @@ import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; +import org.opentripplanner.transit.model.network.CarAccess; import org.opentripplanner.transit.model.site.GroupStop; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; @@ -87,25 +88,12 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor LOG.info(progress.startMessage()); Set stopLocationsUsedForFlexTrips = Set.of(); - if (OTPFeature.FlexRouting.isOn()) { - stopLocationsUsedForFlexTrips = - timetableRepository - .getAllFlexTrips() - .stream() - .flatMap(t -> t.getStops().stream()) - .collect(Collectors.toSet()); - - stopLocationsUsedForFlexTrips.addAll( - stopLocationsUsedForFlexTrips - .stream() - .filter(GroupStop.class::isInstance) - .map(GroupStop.class::cast) - .flatMap(g -> g.getChildLocations().stream().filter(RegularStop.class::isInstance)) - .toList() - ); + stopLocationsUsedForFlexTrips = getStopLocationsUsedForFlexTrips(timetableRepository); } + Set stopLocationsUsedForCarsAllowedTrips = timetableRepository.getStopLocationsUsedForCarsAllowedTrips(); + for (TransitStopVertex tStop : vertices) { // Stops with pathways do not need to be connected to the street network, since there are explicit entrances defined for that if (tStop.hasPathways()) { @@ -120,7 +108,10 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor StopLinkType linkType = StopLinkType.WALK_ONLY; if ( - OTPFeature.FlexRouting.isOn() && stopLocationsUsedForFlexTrips.contains(tStop.getStop()) + ( + OTPFeature.FlexRouting.isOn() && stopLocationsUsedForFlexTrips.contains(tStop.getStop()) + ) || + stopLocationsUsedForCarsAllowedTrips.contains(tStop.getStop()) ) { linkType = StopLinkType.WALK_AND_CAR; } @@ -366,6 +357,26 @@ private VehicleParking removeVehicleParkingEntranceVertexFromGraph( } } + private Set getStopLocationsUsedForFlexTrips( + TimetableRepository timetableRepository + ) { + Set stopLocations = timetableRepository + .getAllFlexTrips() + .stream() + .flatMap(t -> t.getStops().stream()) + .collect(Collectors.toSet()); + + stopLocations.addAll( + stopLocations + .stream() + .filter(GroupStop.class::isInstance) + .map(GroupStop.class::cast) + .flatMap(g -> g.getChildLocations().stream().filter(RegularStop.class::isInstance)) + .toList() + ); + return stopLocations; + } + private enum StopLinkType { /** * Only ensure that the link leads to a walkable edge. diff --git a/application/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java b/application/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java new file mode 100644 index 00000000000..4034814462b --- /dev/null +++ b/application/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java @@ -0,0 +1,19 @@ +package org.opentripplanner.gtfs.mapping; + +import org.onebusaway.gtfs.model.Trip; +import org.opentripplanner.transit.model.network.CarAccess; + +/** + * Model car access for GTFS trips. + */ +class CarAccessMapper { + + public static CarAccess mapForTrip(Trip rhs) { + int carsAllowed = rhs.getCarsAllowed(); + return switch (carsAllowed) { + case 1 -> CarAccess.ALLOWED; + case 2 -> CarAccess.NOT_ALLOWED; + default -> CarAccess.UNKNOWN; + }; + } +} diff --git a/application/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/application/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index fdef2bb4e0c..4e805d6d1df 100644 --- a/application/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/application/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -71,6 +71,7 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { lhs.withShapeId(AgencyAndIdMapper.mapAgencyAndId(rhs.getShapeId())); lhs.withWheelchairBoarding(WheelchairAccessibilityMapper.map(rhs.getWheelchairAccessible())); lhs.withBikesAllowed(BikeAccessMapper.mapForTrip(rhs)); + lhs.withCarsAllowed(CarAccessMapper.mapForTrip(rhs)); var trip = lhs.build(); mapSafeTimePenalty(rhs).ifPresent(f -> flexSafeTimePenalties.put(trip, f)); diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java index 44d9f3cdb3d..514a5b56f9a 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java @@ -12,6 +12,7 @@ import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.BikeAccess; +import org.opentripplanner.transit.model.network.CarAccess; import org.opentripplanner.transit.model.network.RoutingTripPattern; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripTimes; @@ -20,6 +21,8 @@ public class RouteRequestTransitDataProviderFilter implements TransitDataProvide private final boolean requireBikesAllowed; + private final boolean requireCarsAllowed; + private final boolean wheelchairEnabled; private final WheelchairPreferences wheelchairPreferences; @@ -41,6 +44,7 @@ public class RouteRequestTransitDataProviderFilter implements TransitDataProvide public RouteRequestTransitDataProviderFilter(RouteRequest request) { this( request.journey().transfer().mode() == StreetMode.BIKE, + request.journey().transfer().mode() == StreetMode.CAR, request.wheelchair(), request.preferences().wheelchair(), request.preferences().transit().includePlannedCancellations(), @@ -53,6 +57,7 @@ public RouteRequestTransitDataProviderFilter(RouteRequest request) { // This constructor is used only for testing public RouteRequestTransitDataProviderFilter( boolean requireBikesAllowed, + boolean requireCarsAllowed, boolean wheelchairEnabled, WheelchairPreferences wheelchairPreferences, boolean includePlannedCancellations, @@ -61,6 +66,7 @@ public RouteRequestTransitDataProviderFilter( List filters ) { this.requireBikesAllowed = requireBikesAllowed; + this.requireCarsAllowed = requireCarsAllowed; this.wheelchairEnabled = wheelchairEnabled; this.wheelchairPreferences = wheelchairPreferences; this.includePlannedCancellations = includePlannedCancellations; @@ -97,10 +103,12 @@ public boolean tripPatternPredicate(TripPatternForDate tripPatternForDate) { public boolean tripTimesPredicate(TripTimes tripTimes, boolean withFilters) { final Trip trip = tripTimes.getTrip(); - if (requireBikesAllowed) { - if (bikeAccessForTrip(trip) != BikeAccess.ALLOWED) { - return false; - } + if (requireBikesAllowed && bikeAccessForTrip(trip) != BikeAccess.ALLOWED) { + return false; + } + + if (requireCarsAllowed && trip.getCarsAllowed() != CarAccess.ALLOWED) { + return false; } if (wheelchairEnabled) { diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/StreetMode.java b/application/src/main/java/org/opentripplanner/routing/api/request/StreetMode.java index 56e716d9d62..cbc2764f030 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/StreetMode.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/StreetMode.java @@ -36,10 +36,8 @@ public enum StreetMode implements DocumentedEnum { SCOOTER_RENTAL(Feature.ACCESS, Feature.EGRESS, Feature.WALKING, Feature.SCOOTER, Feature.RENTING), /** * Car only - *

- * Direct mode only. */ - CAR(Feature.ACCESS, Feature.DRIVING), + CAR(Feature.ACCESS, Feature.TRANSFER, Feature.EGRESS, Feature.DRIVING), /** * Start in the car, drive to a parking area, and walk the rest of the way. *

diff --git a/application/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java b/application/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java index 536a01a9b14..c0d850e9ce7 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java +++ b/application/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java @@ -1,6 +1,8 @@ package org.opentripplanner.transit.model.network; /** + * This represents the state of whether bikes are allowed on board trips (or routes). + *

* GTFS codes: * 0 = unknown / unspecified, 1 = bikes allowed, 2 = bikes NOT allowed */ diff --git a/application/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java b/application/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java new file mode 100644 index 00000000000..2fca43cfb32 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java @@ -0,0 +1,13 @@ +package org.opentripplanner.transit.model.network; + +/** + * This represents the state of whether cars are allowed on board trips. + *

+ * GTFS codes: + * 0 = unknown / unspecified, 1 = cars allowed, 2 = cars NOT allowed + */ +public enum CarAccess { + UNKNOWN, + NOT_ALLOWED, + ALLOWED, +} diff --git a/application/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java b/application/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java index 6e829e15866..7cca32c4a90 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java +++ b/application/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java @@ -16,6 +16,7 @@ import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.framework.LogInfo; import org.opentripplanner.transit.model.network.BikeAccess; +import org.opentripplanner.transit.model.network.CarAccess; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.organization.Operator; @@ -38,6 +39,7 @@ public final class Trip extends AbstractTransitEntity impleme private final TransitMode mode; private final Direction direction; private final BikeAccess bikesAllowed; + private final CarAccess carsAllowed; private final Accessibility wheelchairBoarding; private final SubMode netexSubmode; @@ -76,6 +78,7 @@ public final class Trip extends AbstractTransitEntity impleme : route.getNetexSubmode(); this.direction = requireNonNullElse(builder.getDirection(), Direction.UNKNOWN); this.bikesAllowed = requireNonNullElse(builder.getBikesAllowed(), route.getBikesAllowed()); + this.carsAllowed = requireNonNullElse(builder.getCarsAllowed(), CarAccess.UNKNOWN); this.wheelchairBoarding = requireNonNullElse(builder.getWheelchairBoarding(), Accessibility.NO_INFORMATION); this.netexAlteration = requireNonNullElse(builder.getNetexAlteration(), TripAlteration.PLANNED); @@ -156,6 +159,10 @@ public BikeAccess getBikesAllowed() { return bikesAllowed; } + public CarAccess getCarsAllowed() { + return carsAllowed; + } + public Accessibility getWheelchairBoarding() { return wheelchairBoarding; } @@ -217,6 +224,7 @@ public boolean sameAs(Trip other) { Objects.equals(this.shapeId, other.shapeId) && Objects.equals(this.direction, other.direction) && Objects.equals(this.bikesAllowed, other.bikesAllowed) && + Objects.equals(this.carsAllowed, other.carsAllowed) && Objects.equals(this.wheelchairBoarding, other.wheelchairBoarding) && Objects.equals(this.netexAlteration, other.netexAlteration) ); diff --git a/application/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java b/application/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java index 063dfe10da2..5ed0616831d 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java +++ b/application/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java @@ -6,6 +6,7 @@ import org.opentripplanner.transit.model.framework.AbstractEntityBuilder; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.BikeAccess; +import org.opentripplanner.transit.model.network.CarAccess; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.organization.Operator; @@ -21,6 +22,7 @@ public class TripBuilder extends AbstractEntityBuilder { private FeedScopedId shapeId; private Direction direction; private BikeAccess bikesAllowed; + private CarAccess carsAllowed; private Accessibility wheelchairBoarding; private String gtfsBlockId; private String netexInternalPlanningCode; @@ -44,6 +46,7 @@ public class TripBuilder extends AbstractEntityBuilder { this.shapeId = original.getShapeId(); this.direction = original.getDirection(); this.bikesAllowed = original.getBikesAllowed(); + this.carsAllowed = original.getCarsAllowed(); this.wheelchairBoarding = original.getWheelchairBoarding(); this.netexInternalPlanningCode = original.getNetexInternalPlanningCode(); } @@ -151,11 +154,20 @@ public BikeAccess getBikesAllowed() { return bikesAllowed; } + public CarAccess getCarsAllowed() { + return carsAllowed; + } + public TripBuilder withBikesAllowed(BikeAccess bikesAllowed) { this.bikesAllowed = bikesAllowed; return this; } + public TripBuilder withCarsAllowed(CarAccess carsAllowed) { + this.carsAllowed = carsAllowed; + return this; + } + public Accessibility getWheelchairBoarding() { return wheelchairBoarding; } diff --git a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java index 6dec0c09eb6..798da10a7c6 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -20,6 +20,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nullable; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.framework.lang.ObjectUtils; @@ -44,9 +45,12 @@ import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.CarAccess; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.organization.Operator; +import org.opentripplanner.transit.model.site.GroupStop; +import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.updater.GraphUpdaterManager; @@ -564,6 +568,36 @@ public FlexTrip getFlexTrip(FeedScopedId tripId) { return flexTripsById.get(tripId); } + /** + * The stops that are used by transit capable of transporting cars need to be + * connected to the road network (e.g. car ferries). This method returns the + * stops that are used by trips that allow cars. + * @return set of stop locations that are used for trips that allow cars + */ + public Set getStopLocationsUsedForCarsAllowedTrips() { + Set stopLocations = getAllTripPatterns() + .stream() + .filter(t -> + t + .getScheduledTimetable() + .getTripTimes() + .stream() + .anyMatch(tt -> tt.getTrip().getCarsAllowed() == CarAccess.ALLOWED) + ) + .flatMap(t -> t.getStops().stream()) + .collect(Collectors.toSet()); + + stopLocations.addAll( + stopLocations + .stream() + .filter(GroupStop.class::isInstance) + .map(GroupStop.class::cast) + .flatMap(g -> g.getChildLocations().stream().filter(RegularStop.class::isInstance)) + .toList() + ); + return stopLocations; + } + private void invalidateIndex() { this.index = null; } diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 00eb1eac565..de1230b9eca 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3081,6 +3081,12 @@ enum PlanAccessMode { """ BICYCLE_RENTAL """ + Driving to a stop and boarding a vehicle with the car. + Access can use driving only if the mode used for transfers + and egress is also `CAR`. + """ + CAR + """ Getting dropped off by a car to a location that is accessible with a car. Note, this can include walking after the drop-off. """ @@ -3190,6 +3196,11 @@ enum PlanEgressMode { """ BICYCLE_RENTAL """ + Driving from a stop to the destination. Egress can use driving only if the mode + used for access and transfers is also `CAR`. + """ + CAR + """ Getting picked up by a car from a location that is accessible with a car. Note, this can include walking before the pickup. """ @@ -3227,6 +3238,11 @@ enum PlanTransferMode { cycling if the mode used for access and egress is also `BICYCLE`. """ BICYCLE + """ + Driving between transit vehicles. Transfers can only use driving if the mode + used for access and egress is also `CAR`. + """ + CAR "Walking between transit vehicles (typically between stops)." WALK } diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java index 5ed267f1f4f..035d3605a21 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java @@ -2,20 +2,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Arrays; import org.junit.jupiter.api.Test; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.transit.model.network.BikeAccess; class BikesAllowedMapperTest { @Test void mapping() { - Arrays - .stream(BikeAccess.values()) - .filter(ba -> ba != BikeAccess.UNKNOWN) - .forEach(d -> { - var mapped = BikesAllowedMapper.map(d); - assertEquals(d.toString(), mapped.toString()); - }); + assertEquals(GraphQLBikesAllowed.NO_INFORMATION, BikesAllowedMapper.map(BikeAccess.UNKNOWN)); + assertEquals(GraphQLBikesAllowed.NOT_ALLOWED, BikesAllowedMapper.map(BikeAccess.NOT_ALLOWED)); + assertEquals(GraphQLBikesAllowed.ALLOWED, BikesAllowedMapper.map(BikeAccess.ALLOWED)); } } diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java index d04d1a33058..fd7996aefe3 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java @@ -1,6 +1,6 @@ package org.opentripplanner.graph_builder.module; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -9,12 +9,14 @@ import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; import static org.opentripplanner.transit.model._data.TimetableRepositoryForTest.id; +import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Test; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.model.StopTime; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.edge.Edge; @@ -23,7 +25,15 @@ import org.opentripplanner.street.model.vertex.TransitStopVertex; import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.network.CarAccess; +import org.opentripplanner.transit.model.network.Route; +import org.opentripplanner.transit.model.network.StopPattern; +import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripTimesFactory; import org.opentripplanner.transit.service.SiteRepository; import org.opentripplanner.transit.service.TimetableRepository; @@ -41,7 +51,7 @@ void linkingIsIdempotent() { module.buildGraph(); assertTrue(model.stopVertex().isConnectedToGraph()); - assertEquals(1, model.stopVertex().getOutgoing().size()); + assertThat(model.stopVertex().getOutgoing()).hasSize(1); } @Test @@ -53,7 +63,7 @@ void linkRegularStop() { assertTrue(model.stopVertex().isConnectedToGraph()); - assertEquals(1, model.stopVertex().getOutgoing().size()); + assertThat(model.stopVertex().getOutgoing()).hasSize(1); var outgoing = model.outgoingLinks().getFirst(); assertInstanceOf(StreetTransitStopLink.class, outgoing); @@ -77,7 +87,7 @@ void linkFlexStop() { assertTrue(model.stopVertex().isConnectedToGraph()); // stop is used by a flex trip, needs to be linked to both the walk and car edge - assertEquals(2, model.stopVertex().getOutgoing().size()); + assertThat(model.stopVertex().getOutgoing()).hasSize(2); var linkToWalk = model.outgoingLinks().getFirst(); SplitterVertex walkSplit = (SplitterVertex) linkToWalk.getToVertex(); @@ -92,6 +102,37 @@ void linkFlexStop() { }); } + @Test + void linkCarsAllowedStop() { + var model = new TestModel(); + var carsAllowedTrip = TimetableRepositoryForTest + .of() + .trip("carsAllowedTrip") + .withCarsAllowed(CarAccess.ALLOWED) + .build(); + model.withCarsAllowedTrip(carsAllowedTrip, model.stop()); + + var module = model.streetLinkerModule(); + + module.buildGraph(); + + assertTrue(model.stopVertex().isConnectedToGraph()); + + // Because the stop is used by a carsAllowed trip it needs to be linked to both the walk and car edge + assertThat(model.stopVertex().getOutgoing()).hasSize(2); + var linkToWalk = model.outgoingLinks().getFirst(); + SplitterVertex walkSplit = (SplitterVertex) linkToWalk.getToVertex(); + + assertTrue(walkSplit.isConnectedToWalkingEdge()); + assertFalse(walkSplit.isConnectedToDriveableEdge()); + + var linkToCar = model.outgoingLinks().getLast(); + SplitterVertex carSplit = (SplitterVertex) linkToCar.getToVertex(); + + assertFalse(carSplit.isConnectedToWalkingEdge()); + assertTrue(carSplit.isConnectedToDriveableEdge()); + } + private static class TestModel { private final TransitStopVertex stopVertex; @@ -155,5 +196,33 @@ public RegularStop stop() { public void withFlexTrip(UnscheduledTrip flexTrip) { timetableRepository.addFlexTrip(flexTrip.getId(), flexTrip); } + + public void withCarsAllowedTrip(Trip trip, StopLocation... stops) { + Route route = TimetableRepositoryForTest.route("carsAllowedRoute").build(); + var stopTimes = Arrays + .stream(stops) + .map(s -> { + var stopTime = new StopTime(); + stopTime.setStop(s); + stopTime.setArrivalTime(30); + stopTime.setDepartureTime(60); + stopTime.setTrip(trip); + return stopTime; + }) + .toList(); + StopPattern stopPattern = new StopPattern(stopTimes); + RealTimeTripTimes tripTimes = TripTimesFactory.tripTimes( + trip, + stopTimes, + timetableRepository.getDeduplicator() + ); + TripPattern tripPattern = TimetableRepositoryForTest + .tripPattern("carsAllowedTripPattern", route) + .withStopPattern(stopPattern) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)) + .build(); + + timetableRepository.addTripPattern(tripPattern.getId(), tripPattern); + } } } diff --git a/application/src/test/java/org/opentripplanner/gtfs/mapping/CarAccessMapperTest.java b/application/src/test/java/org/opentripplanner/gtfs/mapping/CarAccessMapperTest.java new file mode 100644 index 00000000000..a312d9967d6 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/gtfs/mapping/CarAccessMapperTest.java @@ -0,0 +1,26 @@ +package org.opentripplanner.gtfs.mapping; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.onebusaway.gtfs.model.Route; +import org.onebusaway.gtfs.model.Trip; +import org.opentripplanner.transit.model.network.CarAccess; + +public class CarAccessMapperTest { + + private static final int CARS_ALLOWED = 1; + private static final int CARS_NOT_ALLOWED = 2; + + @Test + public void testTripProvidedValues() { + Trip trip = new Trip(); + assertEquals(CarAccess.UNKNOWN, CarAccessMapper.mapForTrip(trip)); + + trip.setCarsAllowed(CARS_ALLOWED); + assertEquals(CarAccess.ALLOWED, CarAccessMapper.mapForTrip(trip)); + + trip.setCarsAllowed(CARS_NOT_ALLOWED); + assertEquals(CarAccess.NOT_ALLOWED, CarAccessMapper.mapForTrip(trip)); + } +} diff --git a/application/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java b/application/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java index 581cff59e79..4132b73826c 100644 --- a/application/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java +++ b/application/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java @@ -19,6 +19,7 @@ import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.network.BikeAccess; +import org.opentripplanner.transit.model.network.CarAccess; import org.opentripplanner.transit.model.timetable.Direction; public class TripMapperTest { @@ -26,6 +27,7 @@ public class TripMapperTest { private static final String FEED_ID = "FEED"; private static final AgencyAndId AGENCY_AND_ID = new AgencyAndId("A", "1"); private static final int BIKES_ALLOWED = 1; + private static final int CARS_ALLOWED = 1; private static final String BLOCK_ID = "Block Id"; private static final int DIRECTION_ID = 1; private static final String TRIP_HEADSIGN = "Trip Headsign"; @@ -52,6 +54,7 @@ private static TripMapper defaultTripMapper() { TRIP.setId(AGENCY_AND_ID); TRIP.setBikesAllowed(BIKES_ALLOWED); + TRIP.setCarsAllowed(CARS_ALLOWED); TRIP.setBlockId(BLOCK_ID); TRIP.setDirectionId(Integer.toString(DIRECTION_ID)); TRIP.setRoute(data.route); @@ -83,6 +86,7 @@ void testMap() throws Exception { assertEquals(TRIP_SHORT_NAME, result.getShortName()); assertEquals(Accessibility.POSSIBLE, result.getWheelchairBoarding()); assertEquals(BikeAccess.ALLOWED, result.getBikesAllowed()); + assertEquals(CarAccess.ALLOWED, result.getCarsAllowed()); } @Test @@ -104,6 +108,7 @@ void testMapWithNulls() throws Exception { assertEquals(Direction.UNKNOWN, result.getDirection()); assertEquals(Accessibility.NO_INFORMATION, result.getWheelchairBoarding()); assertEquals(BikeAccess.UNKNOWN, result.getBikesAllowed()); + assertEquals(CarAccess.UNKNOWN, result.getCarsAllowed()); } /** Mapping the same object twice, should return the same instance. */ diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java index 8b52f1b58fa..ea4b7174e79 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java @@ -1,5 +1,6 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; +import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -34,6 +35,7 @@ import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.BikeAccess; +import org.opentripplanner.transit.model.network.CarAccess; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.RouteBuilder; import org.opentripplanner.transit.model.network.RoutingTripPattern; @@ -107,6 +109,7 @@ void testWheelchairAccess(Accessibility wheelchair, WheelchairPreferences access .getRoutingTripPattern(); var filter = new RouteRequestTransitDataProviderFilter( + false, false, true, accessibility, @@ -157,6 +160,7 @@ void testRealtimeCancelledStops(boolean includeRealtimeCancellations) { .getRoutingTripPattern(); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -202,6 +206,7 @@ void notFilteringExpectedTripPatternForDateTest() { TripPatternForDate tripPatternForDate = createTestTripPatternForDate(); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -221,6 +226,7 @@ void bannedRouteFilteringTest() { TripPatternForDate tripPatternForDate = createTestTripPatternForDate(); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -246,6 +252,7 @@ void bannedTripFilteringTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -253,6 +260,7 @@ void bannedTripFilteringTest() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -281,6 +289,7 @@ void matchModeFilterAndBannedAgencyFilter() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -316,6 +325,7 @@ void matchCombinedModesAndBannedAgencyFilter() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -347,6 +357,7 @@ void matchSelectedAgencyExcludedSubMode() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -389,6 +400,7 @@ void transitModeFilteringTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, TransmodelTransportSubmode.LOCAL_BUS.getValue(), Accessibility.NOT_POSSIBLE, @@ -415,6 +427,7 @@ void notFilteringExpectedTripTimesTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -422,6 +435,7 @@ void notFilteringExpectedTripTimesTest() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -442,6 +456,36 @@ void bikesAllowedFilteringTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, + TransitMode.BUS, + null, + Accessibility.NOT_POSSIBLE, + null + ); + + var filter = new RouteRequestTransitDataProviderFilter( + true, + false, + true, + WheelchairPreferences.DEFAULT, + false, + false, + Set.of(), + List.of(AllowAllTransitFilter.of()) + ); + + boolean valid = filter.tripTimesPredicate(tripTimes, true); + + assertFalse(valid); + } + + @Test + void carsAllowedFilteringTest() { + TripTimes tripTimes = createTestTripTimes( + TRIP_ID, + ROUTE, + BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -449,6 +493,7 @@ void bikesAllowedFilteringTest() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, true, true, WheelchairPreferences.DEFAULT, @@ -469,6 +514,7 @@ void removeInaccessibleTrip() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -476,6 +522,7 @@ void removeInaccessibleTrip() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, true, WheelchairPreferences.DEFAULT, @@ -496,6 +543,7 @@ void keepAccessibleTrip() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.POSSIBLE, @@ -503,6 +551,7 @@ void keepAccessibleTrip() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, true, WheelchairPreferences.DEFAULT, @@ -523,6 +572,7 @@ void keepRealTimeAccessibleTrip() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -530,6 +580,7 @@ void keepRealTimeAccessibleTrip() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, true, WheelchairPreferences.DEFAULT, @@ -552,6 +603,7 @@ void includePlannedCancellationsTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -561,6 +613,7 @@ void includePlannedCancellationsTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -569,6 +622,7 @@ void includePlannedCancellationsTest() { // Given var filter1 = new RouteRequestTransitDataProviderFilter( + false, false, false, WheelchairPreferences.DEFAULT, @@ -590,6 +644,7 @@ void includePlannedCancellationsTest() { // Given var filter2 = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -616,6 +671,7 @@ void includeRealtimeCancellationsTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -626,6 +682,7 @@ void includeRealtimeCancellationsTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -635,6 +692,7 @@ void includeRealtimeCancellationsTest() { // Given var filter1 = new RouteRequestTransitDataProviderFilter( + false, false, false, WheelchairPreferences.DEFAULT, @@ -656,6 +714,7 @@ void includeRealtimeCancellationsTest() { // Given var filter2 = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -712,12 +771,64 @@ void testBikesAllowed() { ); } + @Test + void testCarsAllowed() { + TripTimes tripTimesCarsAllowed = createTestTripTimes( + TRIP_ID, + ROUTE, + BikeAccess.UNKNOWN, + CarAccess.ALLOWED, + TransitMode.FERRY, + null, + Accessibility.NO_INFORMATION, + TripAlteration.PLANNED + ); + + TripTimes tripTimesCarsNotAllowed = createTestTripTimes( + TRIP_ID, + ROUTE, + BikeAccess.UNKNOWN, + CarAccess.NOT_ALLOWED, + TransitMode.FERRY, + null, + Accessibility.NO_INFORMATION, + TripAlteration.PLANNED + ); + + TripTimes tripTimesCarsUnknown = createTestTripTimes( + TRIP_ID, + ROUTE, + BikeAccess.UNKNOWN, + CarAccess.UNKNOWN, + TransitMode.FERRY, + null, + Accessibility.NO_INFORMATION, + TripAlteration.PLANNED + ); + + RouteRequestTransitDataProviderFilter filter = new RouteRequestTransitDataProviderFilter( + false, + true, + false, + DEFAULT_ACCESSIBILITY, + false, + false, + Set.of(), + List.of(AllowAllTransitFilter.of()) + ); + + assertThat(filter.tripTimesPredicate(tripTimesCarsAllowed, false)).isTrue(); + assertThat(filter.tripTimesPredicate(tripTimesCarsNotAllowed, false)).isFalse(); + assertThat(filter.tripTimesPredicate(tripTimesCarsUnknown, false)).isFalse(); + } + @Test void multipleFilteringTest() { TripTimes matchingTripTimes = createTestTripTimes( TRIP_ID, ROUTE, BikeAccess.ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.POSSIBLE, @@ -727,6 +838,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.RAIL, null, Accessibility.POSSIBLE, @@ -736,6 +848,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.RAIL, null, Accessibility.POSSIBLE, @@ -745,6 +858,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.RAIL, null, Accessibility.NOT_POSSIBLE, @@ -754,6 +868,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -763,6 +878,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.POSSIBLE, @@ -771,6 +887,7 @@ void multipleFilteringTest() { var filter = new RouteRequestTransitDataProviderFilter( true, + false, true, DEFAULT_ACCESSIBILITY, false, @@ -793,6 +910,7 @@ private boolean validateModesOnTripTimes( TripTimes tripTimes ) { var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -870,6 +988,7 @@ private RealTimeTripTimes createTestTripTimes( FeedScopedId tripId, Route route, BikeAccess bikeAccess, + CarAccess carAccess, TransitMode mode, String submode, Accessibility wheelchairBoarding, @@ -881,6 +1000,7 @@ private RealTimeTripTimes createTestTripTimes( .withMode(mode) .withNetexSubmode(submode) .withBikesAllowed(bikeAccess) + .withCarsAllowed(carAccess) .withWheelchairBoarding(wheelchairBoarding) .withNetexAlteration(tripAlteration) .build(); @@ -899,6 +1019,7 @@ private TripTimes createTestTripTimesWithSubmode(String submode) { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, submode, Accessibility.NOT_POSSIBLE, diff --git a/application/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java b/application/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java index 5d50548564a..441ce74927a 100644 --- a/application/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java +++ b/application/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java @@ -12,6 +12,7 @@ import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.BikeAccess; +import org.opentripplanner.transit.model.network.CarAccess; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.organization.Operator; @@ -24,6 +25,7 @@ class TripTest { private static final Direction DIRECTION = Direction.INBOUND; public static final NonLocalizedString HEAD_SIGN = new NonLocalizedString("head sign"); private static final BikeAccess BIKE_ACCESS = BikeAccess.ALLOWED; + private static final CarAccess CAR_ACCESS = CarAccess.ALLOWED; private static final TransitMode TRANSIT_MODE = TransitMode.BUS; private static final String BLOCK_ID = "blockId"; private static final TripAlteration TRIP_ALTERATION = TripAlteration.CANCELLATION; @@ -43,6 +45,7 @@ class TripTest { .withDirection(DIRECTION) .withHeadsign(HEAD_SIGN) .withBikesAllowed(BIKE_ACCESS) + .withCarsAllowed(CAR_ACCESS) .withMode(TRANSIT_MODE) .withGtfsBlockId(BLOCK_ID) .withNetexAlteration(TRIP_ALTERATION) @@ -84,6 +87,7 @@ void copy() { assertEquals(DIRECTION, copy.getDirection()); assertEquals(HEAD_SIGN, copy.getHeadsign()); assertEquals(BIKE_ACCESS, copy.getBikesAllowed()); + assertEquals(CAR_ACCESS, copy.getCarsAllowed()); assertEquals(TRANSIT_MODE, copy.getMode()); assertEquals(BLOCK_ID, copy.getGtfsBlockId()); assertEquals(TRIP_ALTERATION, copy.getNetexAlteration()); @@ -110,6 +114,7 @@ void sameAs() { assertFalse(subject.sameAs(subject.copy().withDirection(Direction.OUTBOUND).build())); assertFalse(subject.sameAs(subject.copy().withHeadsign(new NonLocalizedString("X")).build())); assertFalse(subject.sameAs(subject.copy().withBikesAllowed(BikeAccess.NOT_ALLOWED).build())); + assertFalse(subject.sameAs(subject.copy().withCarsAllowed(CarAccess.NOT_ALLOWED).build())); assertFalse(subject.sameAs(subject.copy().withMode(TransitMode.RAIL).build())); assertFalse(subject.sameAs(subject.copy().withGtfsBlockId("X").build())); assertFalse(