From b70c9d6249df47a7dc5a161422ed230e785be06e Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 3 Sep 2024 16:25:09 +0300 Subject: [PATCH 01/12] Change GraphQL schema for carsAllowed and generate files. --- .../gtfs/generated/GraphQLDataFetchers.java | 33 ++++++++++--------- .../apis/gtfs/generated/GraphQLTypes.java | 9 +++++ .../apis/gtfs/generated/graphql-codegen.yml | 1 + .../opentripplanner/apis/gtfs/schema.graphqls | 26 +++++++++++++++ 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 67944543580..a33a5d31f10 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -12,16 +12,30 @@ import org.locationtech.jts.geom.Geometry; import org.opentripplanner.api.resource.DebugOutput; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAbsoluteDirection; +import org.opentripplanner.transit.model.organization.Agency; +import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertCauseType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertEffectType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; +import org.opentripplanner.service.vehiclerental.model.RentalVehicleEntityCounts; +import org.opentripplanner.service.vehiclerental.model.RentalVehicleTypeCount; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; +import org.opentripplanner.transit.model.timetable.booking.BookingInfo; +import org.opentripplanner.transit.model.timetable.booking.BookingTime; +import org.opentripplanner.routing.graphfinder.PatternAtStop; +import org.opentripplanner.model.plan.Emissions; +import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLOccupancyStatus; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; -import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.model.PlanPageInfo; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; import org.opentripplanner.apis.gtfs.model.StopPosition; @@ -36,37 +50,24 @@ import org.opentripplanner.model.fare.FareProduct; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.fare.RiderCategory; -import org.opentripplanner.model.plan.Emissions; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.LegTime; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.WalkStep; -import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.api.response.RoutingError; import org.opentripplanner.routing.graphfinder.NearbyStop; -import org.opentripplanner.routing.graphfinder.PatternAtStop; +import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.routing.graphfinder.PlaceAtDistance; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; import org.opentripplanner.routing.vehicle_parking.VehicleParkingState; import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle; import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle.StopRelationship; -import org.opentripplanner.service.vehiclerental.model.RentalVehicleEntityCounts; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; -import org.opentripplanner.service.vehiclerental.model.RentalVehicleTypeCount; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.transit.model.basic.Money; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.model.organization.Agency; -import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.booking.BookingInfo; -import org.opentripplanner.transit.model.timetable.booking.BookingTime; public class GraphQLDataFetchers { @@ -1148,6 +1149,8 @@ public interface GraphQLTrip { public DataFetcher blockId(); + public DataFetcher carsAllowed(); + public DataFetcher departureStoptime(); public DataFetcher directionId(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index 67051444cdf..06f515466ac 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -566,6 +566,12 @@ public void setGraphQLBannedNetworks(List bannedNetworks) { } } + public enum GraphQLCarsAllowed { + ALLOWED, + NOT_ALLOWED, + NO_INFORMATION, + } + public static class GraphQLCyclingOptimizationInput { private GraphQLTriangleCyclingFactorsInput triangle; @@ -1492,6 +1498,7 @@ public enum GraphQLPlanAccessMode { BICYCLE, BICYCLE_PARKING, BICYCLE_RENTAL, + CAR, CAR_DROP_OFF, CAR_PARKING, CAR_RENTAL, @@ -1575,6 +1582,7 @@ public enum GraphQLPlanDirectMode { public enum GraphQLPlanEgressMode { BICYCLE, BICYCLE_RENTAL, + CAR, CAR_PICKUP, CAR_RENTAL, FLEX, @@ -1877,6 +1885,7 @@ public void setGraphQLWalk(GraphQLWalkPreferencesInput walk) { public enum GraphQLPlanTransferMode { BICYCLE, + CAR, WALK, } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 29490a28b78..fb72e63986c 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -53,6 +53,7 @@ config: RentalVehicle: org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle#VehicleRentalVehicle VehicleRentalUris: org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris#VehicleRentalStationUris BikesAllowed: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed#GraphQLBikesAllowed + CarsAllowed: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed#GraphQLCarsAllowed BookingInfo: org.opentripplanner.transit.model.timetable.booking.BookingInfo#BookingInfo BookingTime: org.opentripplanner.transit.model.timetable.booking.BookingTime#BookingTime CarPark: org.opentripplanner.routing.vehicle_parking.VehicleParking#VehicleParking diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 927af19f8b1..dee8d0b7a60 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2267,6 +2267,8 @@ type Trip implements Node { ): Stoptime "Whether bikes are allowed on board the vehicle running this trip" bikesAllowed: BikesAllowed + "Whether cars are allowed on board the vehicle running this trip" + carsAllowed: CarsAllowed blockId: String "Departure time from the first stop" departureStoptime( @@ -2774,6 +2776,15 @@ enum BikesAllowed { NO_INFORMATION } +enum CarsAllowed { + "The vehicle being used on this particular trip can accommodate at least one car." + ALLOWED + "No cars are allowed on this trip." + NOT_ALLOWED + "There is no car information for the trip." + NO_INFORMATION +} + """ Predefined optimization alternatives for bicycling routing. For more customization, one can use the triangle factors. @@ -3062,6 +3073,11 @@ enum PlanAccessMode { """ BICYCLE_RENTAL """ + Driving a car from the origin to the destination. + TODO add more thorough description + """ + 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. """ @@ -3171,6 +3187,11 @@ enum PlanEgressMode { """ BICYCLE_RENTAL """ + Driving a car from the origin to the destination. + TODO add more thorough description + """ + CAR + """ Getting picked up by a car from a location that is accessible with a car. Note, this can include walking before the pickup. """ @@ -3208,6 +3229,11 @@ enum PlanTransferMode { cycling if the mode used for access and egress is also `BICYCLE`. """ BICYCLE + """ + Driving a car from the origin to the destination. + TODO add more thorough description + """ + CAR "Walking between transit vehicles (typically between stops)." WALK } From 3efe676f264f8f9301e8e20506ed5f3cc834213a Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 3 Sep 2024 16:30:42 +0300 Subject: [PATCH 02/12] Add functionality for cars on trips. --- .../api/parameter/QualifiedModeSet.java | 7 +++-- .../apis/gtfs/datafetchers/TripImpl.java | 7 +++++ .../apis/gtfs/mapping/CarsAllowedMapper.java | 17 ++++++++++ .../routerequest/AccessModeMapper.java | 1 + .../routerequest/EgressModeMapper.java | 1 + .../routerequest/ModePreferencesMapper.java | 5 +++ .../routerequest/TransferModeMapper.java | 1 + .../mapping/RequestModesMapper.java | 5 +-- .../module/StreetLinkerModule.java | 31 ++++++++++++++++++- .../gtfs/mapping/CarAccessMapper.java | 22 +++++++++++++ .../gtfs/mapping/TripMapper.java | 1 + ...RouteRequestTransitDataProviderFilter.java | 16 ++++++++++ .../routing/api/request/StreetMode.java | 4 +-- .../transit/model/network/CarAccess.java | 11 +++++++ .../transit/model/timetable/Trip.java | 9 ++++++ .../transit/model/timetable/TripBuilder.java | 12 +++++++ 16 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java create mode 100644 src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java create mode 100644 src/main/java/org/opentripplanner/transit/model/network/CarAccess.java diff --git a/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java b/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java index c6f1a3d74ec..6e62c4ea78a 100644 --- a/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java +++ b/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java @@ -126,9 +126,10 @@ public RequestModes getRequestModes() { mBuilder.withEgressMode(StreetMode.CAR_HAILING); mBuilder.withDirectMode(StreetMode.WALK); } else { - mBuilder.withAccessMode(StreetMode.WALK); - mBuilder.withTransferMode(StreetMode.WALK); - mBuilder.withEgressMode(StreetMode.WALK); + // Cars can use transit, for example, with car ferries. + mBuilder.withAccessMode(StreetMode.CAR); + mBuilder.withTransferMode(StreetMode.CAR); + mBuilder.withEgressMode(StreetMode.CAR); mBuilder.withDirectMode(StreetMode.CAR); } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java index 21bff637976..0ec7d63b101 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java @@ -20,7 +20,9 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; import org.opentripplanner.apis.gtfs.mapping.BikesAllowedMapper; +import org.opentripplanner.apis.gtfs.mapping.CarsAllowedMapper; import org.opentripplanner.apis.gtfs.model.TripOccupancy; import org.opentripplanner.apis.support.SemanticHash; import org.opentripplanner.framework.time.ServiceDateUtils; @@ -172,6 +174,11 @@ public DataFetcher bikesAllowed() { return environment -> BikesAllowedMapper.map(getSource(environment).getBikesAllowed()); } + @Override + public DataFetcher carsAllowed() { + return environment -> CarsAllowedMapper.map(getSource(environment).getCarsAllowed()); + } + @Override public DataFetcher blockId() { return environment -> getSource(environment).getGtfsBlockId(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java new file mode 100644 index 00000000000..0c479a8664c --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java @@ -0,0 +1,17 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import javax.annotation.Nonnull; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; +import org.opentripplanner.transit.model.network.CarAccess; + +public class CarsAllowedMapper { + + @Nonnull + public static GraphQLCarsAllowed map(@Nonnull CarAccess carsAllowed) { + return switch (carsAllowed) { + case UNKNOWN -> GraphQLCarsAllowed.NO_INFORMATION; + case ALLOWED -> GraphQLCarsAllowed.ALLOWED; + case NOT_ALLOWED -> GraphQLCarsAllowed.NOT_ALLOWED; + }; + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/AccessModeMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/AccessModeMapper.java index ac4c90a1a56..e0e3ac0bbb2 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/AccessModeMapper.java +++ b/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/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/EgressModeMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/EgressModeMapper.java index f03b160ac97..ddcaa255f2a 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/EgressModeMapper.java +++ b/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/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ModePreferencesMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ModePreferencesMapper.java index 32d3456df57..663e93acca9 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ModePreferencesMapper.java +++ b/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/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/TransferModeMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/TransferModeMapper.java index ffa7363e3a7..18d2c0e3811 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/TransferModeMapper.java +++ b/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/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java index 974b8dd10c3..849d39e35e7 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java +++ b/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,7 @@ 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); + Optional.ofNullable(accessMode).filter(IS_BIKE_OR_CAR).ifPresent(mBuilder::withTransferMode); return mBuilder.build(); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java index 48e6e484a0c..fbb65a5a850 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -25,6 +25,7 @@ import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; 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; @@ -102,6 +103,31 @@ public void linkTransitStops(Graph graph, TransitModel transitModel) { ); } + // The stops that are used by transit capable of transporting cars need to be connected to the road network (e.g. car ferries). + Set stopLocationsUsedForCarsAllowedTrips = Set.of(); + stopLocationsUsedForCarsAllowedTrips = + transitModel + .getAllTripPatterns() + .stream() + .filter(t -> + t + .getScheduledTimetable() + .getTripTimes() + .stream() + .anyMatch(tt -> tt.getTrip().getCarsAllowed() == CarAccess.ALLOWED) + ) + .flatMap(t -> t.getStops().stream()) + .collect(Collectors.toSet()); + + stopLocationsUsedForCarsAllowedTrips.addAll( + stopLocationsUsedForCarsAllowedTrips + .stream() + .filter(GroupStop.class::isInstance) + .map(GroupStop.class::cast) + .flatMap(g -> g.getChildLocations().stream().filter(RegularStop.class::isInstance)) + .toList() + ); + 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()) { @@ -116,7 +142,10 @@ public void linkTransitStops(Graph graph, TransitModel transitModel) { 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; } diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java new file mode 100644 index 00000000000..d328d1aa06e --- /dev/null +++ b/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java @@ -0,0 +1,22 @@ +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(); + switch (carsAllowed) { + case 1: + return CarAccess.ALLOWED; + case 2: + return CarAccess.NOT_ALLOWED; + default: + return CarAccess.UNKNOWN; + } + } +} diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index 3a62ba2b269..9f2663dd9b3 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/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/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java index 44d9f3cdb3d..2fa4a9dff15 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java +++ b/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; @@ -83,6 +89,10 @@ public static BikeAccess bikeAccessForTrip(Trip trip) { return trip.getRoute().getBikesAllowed(); } + public static CarAccess carAccessForTrip(Trip trip) { + return trip.getCarsAllowed(); + } + @Override public boolean tripPatternPredicate(TripPatternForDate tripPatternForDate) { for (TransitFilter filter : filters) { @@ -103,6 +113,12 @@ public boolean tripTimesPredicate(TripTimes tripTimes, boolean withFilters) { } } + if (requireCarsAllowed) { + if (carAccessForTrip(trip) != CarAccess.ALLOWED) { + return false; + } + } + if (wheelchairEnabled) { if ( wheelchairPreferences.trip().onlyConsiderAccessible() && diff --git a/src/main/java/org/opentripplanner/routing/api/request/StreetMode.java b/src/main/java/org/opentripplanner/routing/api/request/StreetMode.java index 56e716d9d62..cbc2764f030 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/StreetMode.java +++ b/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/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java b/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java new file mode 100644 index 00000000000..0a7a4f91845 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java @@ -0,0 +1,11 @@ +package org.opentripplanner.transit.model.network; + +/** + * GTFS codes: + * 0 = unknown / unspecified, 1 = cars allowed, 2 = cars NOT allowed + */ +public enum CarAccess { + UNKNOWN, + NOT_ALLOWED, + ALLOWED, +} diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java b/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java index 5a1e9150e78..ac16ec0173a 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java @@ -17,6 +17,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; @@ -33,6 +34,7 @@ public final class Trip extends AbstractTransitEntity impleme private final Direction direction; private final BikeAccess bikesAllowed; + private final CarAccess carsAllowed; private final Accessibility wheelchairBoarding; private final String gtfsBlockId; @@ -52,6 +54,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); @@ -137,6 +140,11 @@ public BikeAccess getBikesAllowed() { return bikesAllowed; } + @Nonnull + public CarAccess getCarsAllowed() { + return carsAllowed; + } + @Nonnull public Accessibility getWheelchairBoarding() { return wheelchairBoarding; @@ -200,6 +208,7 @@ public boolean sameAs(@Nonnull 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/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java index 063dfe10da2..5ed0616831d 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java +++ b/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; } From a555f3dee169349f18c32fe315b7572ad98925be Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 3 Sep 2024 16:31:14 +0300 Subject: [PATCH 03/12] Add tests for cars on trips. --- .../gtfs/mapping/CarsAllowedMapperTest.java | 21 +++++ .../module/StreetLinkerModuleTest.java | 69 ++++++++++++++ .../gtfs/mapping/CarAccessMapperTest.java | 26 ++++++ .../gtfs/mapping/TripMapperTest.java | 5 ++ ...eRequestTransitDataProviderFilterTest.java | 90 +++++++++++++++++++ .../transit/model/timetable/TripTest.java | 5 ++ 6 files changed, 216 insertions(+) create mode 100644 src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java create mode 100644 src/test/java/org/opentripplanner/gtfs/mapping/CarAccessMapperTest.java diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java new file mode 100644 index 00000000000..a2a1b40a5e8 --- /dev/null +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java @@ -0,0 +1,21 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import org.junit.jupiter.api.Test; +import org.opentripplanner.transit.model.network.CarAccess; + +class CarsAllowedMapperTest { + + @Test + void mapping() { + Arrays + .stream(CarAccess.values()) + .filter(ba -> ba != CarAccess.UNKNOWN) + .forEach(d -> { + var mapped = CarsAllowedMapper.map(d); + assertEquals(d.toString(), mapped.toString()); + }); + } +} diff --git a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java index 06b10575ef9..e5f77f57659 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java @@ -9,12 +9,14 @@ import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; import static org.opentripplanner.transit.model._data.TransitModelForTest.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.TransitModelForTest; 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.StopModel; import org.opentripplanner.transit.service.TransitModel; @@ -92,6 +102,37 @@ void linkFlexStop() { }); } + @Test + void linkCarsAllowedStop() { + var model = new TestModel(); + var carsAllowedTrip = TransitModelForTest + .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 + assertEquals(2, model.stopVertex().getOutgoing().size()); + 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) { transitModel.addFlexTrip(flexTrip.getId(), flexTrip); } + + public void withCarsAllowedTrip(Trip trip, StopLocation... stops) { + Route route = TransitModelForTest.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); + TripPattern tripPattern = TransitModelForTest + .tripPattern("carsAllowedTripPattern", route) + .withStopPattern(stopPattern) + .build(); + RealTimeTripTimes tripTimes = TripTimesFactory.tripTimes( + trip, + stopTimes, + transitModel.getDeduplicator() + ); + + tripPattern.add(tripTimes); + transitModel.addTripPattern(tripPattern.getId(), tripPattern); + } } } diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/CarAccessMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/CarAccessMapperTest.java new file mode 100644 index 00000000000..a312d9967d6 --- /dev/null +++ b/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/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java index 964c3d8155e..98f5519926c 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java @@ -14,6 +14,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 { @@ -21,6 +22,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"; @@ -47,6 +49,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); @@ -78,6 +81,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 @@ -99,6 +103,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/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java index be6266ccdbe..c5dea627d5a 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java @@ -34,6 +34,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 +108,7 @@ void testWheelchairAccess(Accessibility wheelchair, WheelchairPreferences access .getRoutingTripPattern(); var filter = new RouteRequestTransitDataProviderFilter( + false, false, true, accessibility, @@ -157,6 +159,7 @@ void testRealtimeCancelledStops(boolean includeRealtimeCancellations) { .getRoutingTripPattern(); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -202,6 +205,7 @@ void notFilteringExpectedTripPatternForDateTest() { TripPatternForDate tripPatternForDate = createTestTripPatternForDate(); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -221,6 +225,7 @@ void bannedRouteFilteringTest() { TripPatternForDate tripPatternForDate = createTestTripPatternForDate(); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -246,6 +251,7 @@ void bannedTripFilteringTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -253,6 +259,7 @@ void bannedTripFilteringTest() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -281,6 +288,7 @@ void matchModeFilterAndBannedAgencyFilter() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -316,6 +324,7 @@ void matchCombinedModesAndBannedAgencyFilter() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -347,6 +356,7 @@ void matchSelectedAgencyExcludedSubMode() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -386,6 +396,7 @@ void transitModeFilteringTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, TransmodelTransportSubmode.LOCAL_BUS.getValue(), Accessibility.NOT_POSSIBLE, @@ -412,6 +423,7 @@ void notFilteringExpectedTripTimesTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -419,6 +431,7 @@ void notFilteringExpectedTripTimesTest() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -439,6 +452,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, @@ -446,6 +489,7 @@ void bikesAllowedFilteringTest() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, true, true, WheelchairPreferences.DEFAULT, @@ -466,6 +510,7 @@ void removeInaccessibleTrip() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -473,6 +518,7 @@ void removeInaccessibleTrip() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, true, WheelchairPreferences.DEFAULT, @@ -493,6 +539,7 @@ void keepAccessibleTrip() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.POSSIBLE, @@ -500,6 +547,7 @@ void keepAccessibleTrip() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, true, WheelchairPreferences.DEFAULT, @@ -520,6 +568,7 @@ void keepRealTimeAccessibleTrip() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -527,6 +576,7 @@ void keepRealTimeAccessibleTrip() { ); var filter = new RouteRequestTransitDataProviderFilter( + false, false, true, WheelchairPreferences.DEFAULT, @@ -549,6 +599,7 @@ void includePlannedCancellationsTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -558,6 +609,7 @@ void includePlannedCancellationsTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -566,6 +618,7 @@ void includePlannedCancellationsTest() { // Given var filter1 = new RouteRequestTransitDataProviderFilter( + false, false, false, WheelchairPreferences.DEFAULT, @@ -587,6 +640,7 @@ void includePlannedCancellationsTest() { // Given var filter2 = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -613,6 +667,7 @@ void includeRealtimeCancellationsTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -623,6 +678,7 @@ void includeRealtimeCancellationsTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -632,6 +688,7 @@ void includeRealtimeCancellationsTest() { // Given var filter1 = new RouteRequestTransitDataProviderFilter( + false, false, false, WheelchairPreferences.DEFAULT, @@ -653,6 +710,7 @@ void includeRealtimeCancellationsTest() { // Given var filter2 = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -709,12 +767,34 @@ void testBikesAllowed() { ); } + @Test + void testCarsAllowed() { + RouteBuilder routeBuilder = TransitModelForTest.route("1"); + TripBuilder trip = Trip.of(TransitModelForTest.id("T1")).withRoute(routeBuilder.build()); + + assertEquals( + CarAccess.UNKNOWN, + RouteRequestTransitDataProviderFilter.carAccessForTrip(trip.build()) + ); + trip.withCarsAllowed(CarAccess.ALLOWED); + assertEquals( + CarAccess.ALLOWED, + RouteRequestTransitDataProviderFilter.carAccessForTrip(trip.build()) + ); + trip.withCarsAllowed(CarAccess.NOT_ALLOWED); + assertEquals( + CarAccess.NOT_ALLOWED, + RouteRequestTransitDataProviderFilter.carAccessForTrip(trip.build()) + ); + } + @Test void multipleFilteringTest() { TripTimes matchingTripTimes = createTestTripTimes( TRIP_ID, ROUTE, BikeAccess.ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.POSSIBLE, @@ -724,6 +804,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.RAIL, null, Accessibility.POSSIBLE, @@ -733,6 +814,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.RAIL, null, Accessibility.POSSIBLE, @@ -742,6 +824,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.RAIL, null, Accessibility.NOT_POSSIBLE, @@ -751,6 +834,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.NOT_POSSIBLE, @@ -760,6 +844,7 @@ void multipleFilteringTest() { TRIP_ID, ROUTE, BikeAccess.ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, null, Accessibility.POSSIBLE, @@ -768,6 +853,7 @@ void multipleFilteringTest() { var filter = new RouteRequestTransitDataProviderFilter( true, + false, true, DEFAULT_ACCESSIBILITY, false, @@ -790,6 +876,7 @@ private boolean validateModesOnTripTimes( TripTimes tripTimes ) { var filter = new RouteRequestTransitDataProviderFilter( + false, false, false, DEFAULT_ACCESSIBILITY, @@ -867,6 +954,7 @@ private RealTimeTripTimes createTestTripTimes( FeedScopedId tripId, Route route, BikeAccess bikeAccess, + CarAccess carAccess, TransitMode mode, String submode, Accessibility wheelchairBoarding, @@ -878,6 +966,7 @@ private RealTimeTripTimes createTestTripTimes( .withMode(mode) .withNetexSubmode(submode) .withBikesAllowed(bikeAccess) + .withCarsAllowed(carAccess) .withWheelchairBoarding(wheelchairBoarding) .withNetexAlteration(tripAlteration) .build(); @@ -896,6 +985,7 @@ private TripTimes createTestTripTimesWithSubmode(String submode) { TRIP_ID, ROUTE, BikeAccess.NOT_ALLOWED, + CarAccess.NOT_ALLOWED, TransitMode.BUS, submode, Accessibility.NOT_POSSIBLE, diff --git a/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java b/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java index 3a3f35643fa..eeaa979257d 100644 --- a/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java +++ b/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( From 5afc63c34cc4b4a577468f7791085f0d8b879bcb Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 5 Sep 2024 12:36:03 +0300 Subject: [PATCH 04/12] Remove changes to QualifiedModeSet. --- .../opentripplanner/api/parameter/QualifiedModeSet.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java b/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java index 6e62c4ea78a..c6f1a3d74ec 100644 --- a/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java +++ b/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java @@ -126,10 +126,9 @@ public RequestModes getRequestModes() { mBuilder.withEgressMode(StreetMode.CAR_HAILING); mBuilder.withDirectMode(StreetMode.WALK); } else { - // Cars can use transit, for example, with car ferries. - mBuilder.withAccessMode(StreetMode.CAR); - mBuilder.withTransferMode(StreetMode.CAR); - mBuilder.withEgressMode(StreetMode.CAR); + mBuilder.withAccessMode(StreetMode.WALK); + mBuilder.withTransferMode(StreetMode.WALK); + mBuilder.withEgressMode(StreetMode.WALK); mBuilder.withDirectMode(StreetMode.CAR); } } From c644034c08cda3eaf5fb5b8c59dd293524ae3cfe Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 10 Sep 2024 09:20:08 +0300 Subject: [PATCH 05/12] Fix format. --- .../gtfs/generated/GraphQLDataFetchers.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index a33a5d31f10..0a0d231299c 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -12,30 +12,17 @@ import org.locationtech.jts.geom.Geometry; import org.opentripplanner.api.resource.DebugOutput; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAbsoluteDirection; -import org.opentripplanner.transit.model.organization.Agency; -import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertCauseType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertEffectType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; -import org.opentripplanner.service.vehiclerental.model.RentalVehicleEntityCounts; -import org.opentripplanner.service.vehiclerental.model.RentalVehicleTypeCount; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; -import org.opentripplanner.transit.model.timetable.booking.BookingInfo; -import org.opentripplanner.transit.model.timetable.booking.BookingTime; -import org.opentripplanner.routing.graphfinder.PatternAtStop; -import org.opentripplanner.model.plan.Emissions; -import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLOccupancyStatus; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; +import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.model.PlanPageInfo; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; import org.opentripplanner.apis.gtfs.model.StopPosition; @@ -50,24 +37,37 @@ import org.opentripplanner.model.fare.FareProduct; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.fare.RiderCategory; +import org.opentripplanner.model.plan.Emissions; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.LegTime; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.WalkStep; +import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.api.response.RoutingError; import org.opentripplanner.routing.graphfinder.NearbyStop; -import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.routing.graphfinder.PatternAtStop; import org.opentripplanner.routing.graphfinder.PlaceAtDistance; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; import org.opentripplanner.routing.vehicle_parking.VehicleParkingState; import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle; import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle.StopRelationship; +import org.opentripplanner.service.vehiclerental.model.RentalVehicleEntityCounts; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; +import org.opentripplanner.service.vehiclerental.model.RentalVehicleTypeCount; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.transit.model.basic.Money; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.organization.Agency; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.booking.BookingInfo; +import org.opentripplanner.transit.model.timetable.booking.BookingTime; public class GraphQLDataFetchers { From 54752ba734c0f9589f8d7fb450145b3298834809 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 10 Sep 2024 14:02:59 +0300 Subject: [PATCH 06/12] Fix format. --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index dee8d0b7a60..0162c2090f2 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2267,9 +2267,9 @@ type Trip implements Node { ): Stoptime "Whether bikes are allowed on board the vehicle running this trip" bikesAllowed: BikesAllowed + blockId: String "Whether cars are allowed on board the vehicle running this trip" carsAllowed: CarsAllowed - blockId: String "Departure time from the first stop" departureStoptime( """ From fe067208d65816fca706f5c0491b95f687ef64a9 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 17 Sep 2024 14:08:20 +0300 Subject: [PATCH 07/12] Add changes based on review comments. --- .../apis/gtfs/datafetchers/TripImpl.java | 7 --- .../gtfs/generated/GraphQLDataFetchers.java | 3 - .../apis/gtfs/mapping/BikesAllowedMapper.java | 4 +- .../apis/gtfs/mapping/CarsAllowedMapper.java | 4 +- .../mapping/RequestModesMapper.java | 3 + .../module/StreetLinkerModule.java | 42 +------------- .../gtfs/mapping/CarAccessMapper.java | 13 ++--- ...RouteRequestTransitDataProviderFilter.java | 6 +- .../transit/model/network/BikeAccess.java | 3 + .../transit/model/network/CarAccess.java | 3 + .../transit/service/TransitModel.java | 51 +++++++++++++++++ .../opentripplanner/apis/gtfs/schema.graphqls | 15 +++-- .../gtfs/mapping/BikesAllowedMapperTest.java | 15 ++--- .../gtfs/mapping/CarsAllowedMapperTest.java | 15 ++--- .../module/StreetLinkerModuleTest.java | 10 ++-- ...eRequestTransitDataProviderFilterTest.java | 57 ++++++++++++++----- 16 files changed, 138 insertions(+), 113 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java index 0ec7d63b101..21bff637976 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripImpl.java @@ -20,9 +20,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; import org.opentripplanner.apis.gtfs.mapping.BikesAllowedMapper; -import org.opentripplanner.apis.gtfs.mapping.CarsAllowedMapper; import org.opentripplanner.apis.gtfs.model.TripOccupancy; import org.opentripplanner.apis.support.SemanticHash; import org.opentripplanner.framework.time.ServiceDateUtils; @@ -174,11 +172,6 @@ public DataFetcher bikesAllowed() { return environment -> BikesAllowedMapper.map(getSource(environment).getBikesAllowed()); } - @Override - public DataFetcher carsAllowed() { - return environment -> CarsAllowedMapper.map(getSource(environment).getCarsAllowed()); - } - @Override public DataFetcher blockId() { return environment -> getSource(environment).getGtfsBlockId(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 0a0d231299c..67944543580 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -16,7 +16,6 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertEffectType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAlertSeverityLevelType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLOccupancyStatus; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; @@ -1149,8 +1148,6 @@ public interface GraphQLTrip { public DataFetcher blockId(); - public DataFetcher carsAllowed(); - public DataFetcher departureStoptime(); public DataFetcher directionId(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java index cd04601d599..79f55f1e50a 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java @@ -1,13 +1,11 @@ package org.opentripplanner.apis.gtfs.mapping; -import javax.annotation.Nonnull; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.transit.model.network.BikeAccess; public class BikesAllowedMapper { - @Nonnull - public static GraphQLBikesAllowed map(@Nonnull BikeAccess bikesAllowed) { + public static GraphQLBikesAllowed map(BikeAccess bikesAllowed) { return switch (bikesAllowed) { case UNKNOWN -> GraphQLBikesAllowed.NO_INFORMATION; case ALLOWED -> GraphQLBikesAllowed.ALLOWED; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java index 0c479a8664c..8b35133e615 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java @@ -1,13 +1,11 @@ package org.opentripplanner.apis.gtfs.mapping; -import javax.annotation.Nonnull; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; import org.opentripplanner.transit.model.network.CarAccess; public class CarsAllowedMapper { - @Nonnull - public static GraphQLCarsAllowed map(@Nonnull CarAccess carsAllowed) { + public static GraphQLCarsAllowed map(CarAccess carsAllowed) { return switch (carsAllowed) { case UNKNOWN -> GraphQLCarsAllowed.NO_INFORMATION; case ALLOWED -> GraphQLCarsAllowed.ALLOWED; diff --git a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java index 849d39e35e7..bf9abd3a60d 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java @@ -28,6 +28,9 @@ static RequestModes mapRequestModes(Map modesInput) { ensureValueAndSet(accessMode, mBuilder::withAccessMode); ensureValueAndSet((StreetMode) modesInput.get(egressModeKey), mBuilder::withEgressMode); ensureValueAndSet((StreetMode) modesInput.get(directModeKey), mBuilder::withDirectMode); + // 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/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java index fbb65a5a850..402e1e5eedb 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -84,49 +84,11 @@ public void linkTransitStops(Graph graph, TransitModel transitModel) { LOG.info(progress.startMessage()); Set stopLocationsUsedForFlexTrips = Set.of(); - if (OTPFeature.FlexRouting.isOn()) { - stopLocationsUsedForFlexTrips = - transitModel - .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 = transitModel.getStopLocationsUsedForFlexTrips(); } - // The stops that are used by transit capable of transporting cars need to be connected to the road network (e.g. car ferries). - Set stopLocationsUsedForCarsAllowedTrips = Set.of(); - stopLocationsUsedForCarsAllowedTrips = - transitModel - .getAllTripPatterns() - .stream() - .filter(t -> - t - .getScheduledTimetable() - .getTripTimes() - .stream() - .anyMatch(tt -> tt.getTrip().getCarsAllowed() == CarAccess.ALLOWED) - ) - .flatMap(t -> t.getStops().stream()) - .collect(Collectors.toSet()); - - stopLocationsUsedForCarsAllowedTrips.addAll( - stopLocationsUsedForCarsAllowedTrips - .stream() - .filter(GroupStop.class::isInstance) - .map(GroupStop.class::cast) - .flatMap(g -> g.getChildLocations().stream().filter(RegularStop.class::isInstance)) - .toList() - ); + Set stopLocationsUsedForCarsAllowedTrips = transitModel.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 diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java index d328d1aa06e..4034814462b 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/CarAccessMapper.java @@ -10,13 +10,10 @@ class CarAccessMapper { public static CarAccess mapForTrip(Trip rhs) { int carsAllowed = rhs.getCarsAllowed(); - switch (carsAllowed) { - case 1: - return CarAccess.ALLOWED; - case 2: - return CarAccess.NOT_ALLOWED; - default: - return CarAccess.UNKNOWN; - } + return switch (carsAllowed) { + case 1 -> CarAccess.ALLOWED; + case 2 -> CarAccess.NOT_ALLOWED; + default -> CarAccess.UNKNOWN; + }; } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java index 2fa4a9dff15..a4f89c0cf7e 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java @@ -89,10 +89,6 @@ public static BikeAccess bikeAccessForTrip(Trip trip) { return trip.getRoute().getBikesAllowed(); } - public static CarAccess carAccessForTrip(Trip trip) { - return trip.getCarsAllowed(); - } - @Override public boolean tripPatternPredicate(TripPatternForDate tripPatternForDate) { for (TransitFilter filter : filters) { @@ -114,7 +110,7 @@ public boolean tripTimesPredicate(TripTimes tripTimes, boolean withFilters) { } if (requireCarsAllowed) { - if (carAccessForTrip(trip) != CarAccess.ALLOWED) { + if (trip.getCarsAllowed() != CarAccess.ALLOWED) { return false; } } diff --git a/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java b/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java index 536a01a9b14..043c93e04e6 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java +++ b/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java @@ -1,6 +1,9 @@ package org.opentripplanner.transit.model.network; /** + * This represents the state of whether bikes are allowed on board trips (or routes). + * If the state is unknown, bikes are not allowed. + *

* GTFS codes: * 0 = unknown / unspecified, 1 = bikes allowed, 2 = bikes NOT allowed */ diff --git a/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java b/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java index 0a7a4f91845..e5e15913197 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java +++ b/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java @@ -1,6 +1,9 @@ package org.opentripplanner.transit.model.network; /** + * This represents the state of whether cars are allowed on board trips. + * If the state is unknown, cars are not allowed. + *

* GTFS codes: * 0 = unknown / unspecified, 1 = cars allowed, 2 = cars NOT allowed */ diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModel.java b/src/main/java/org/opentripplanner/transit/service/TransitModel.java index 84c7597d562..99c0f3fadb8 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModel.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModel.java @@ -19,6 +19,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.ext.flex.trip.FlexTrip; @@ -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; @@ -556,6 +560,53 @@ public FlexTrip getFlexTrip(FeedScopedId tripId) { return flexTripsById.get(tripId); } + public Set getStopLocationsUsedForFlexTrips() { + Set stopLocations = 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; + } + + /** + * 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/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 0162c2090f2..618d3cb4850 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2268,8 +2268,6 @@ type Trip implements Node { "Whether bikes are allowed on board the vehicle running this trip" bikesAllowed: BikesAllowed blockId: String - "Whether cars are allowed on board the vehicle running this trip" - carsAllowed: CarsAllowed "Departure time from the first stop" departureStoptime( """ @@ -3073,8 +3071,9 @@ enum PlanAccessMode { """ BICYCLE_RENTAL """ - Driving a car from the origin to the destination. - TODO add more thorough description + 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 """ @@ -3187,8 +3186,8 @@ enum PlanEgressMode { """ BICYCLE_RENTAL """ - Driving a car from the origin to the destination. - TODO add more thorough description + Driving from a stop to the destination. Egress can use driving only if the mode + used for access and transfers is also `CAR`. """ CAR """ @@ -3230,8 +3229,8 @@ enum PlanTransferMode { """ BICYCLE """ - Driving a car from the origin to the destination. - TODO add more thorough description + 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)." diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java index 5ed267f1f4f..af1441e7dc4 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java @@ -1,8 +1,7 @@ package org.opentripplanner.apis.gtfs.mapping; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static com.google.common.truth.Truth.assertThat; -import java.util.Arrays; import org.junit.jupiter.api.Test; import org.opentripplanner.transit.model.network.BikeAccess; @@ -10,12 +9,10 @@ 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()); - }); + assertThat(BikeAccess.ALLOWED.toString()) + .isEqualTo(BikesAllowedMapper.map(BikeAccess.ALLOWED).toString()); + assertThat(BikeAccess.NOT_ALLOWED.toString()) + .isEqualTo(BikesAllowedMapper.map(BikeAccess.NOT_ALLOWED).toString()); + assertThat("NO_INFORMATION").isEqualTo(BikesAllowedMapper.map(BikeAccess.UNKNOWN).toString()); } } diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java index a2a1b40a5e8..9e942eee458 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java @@ -1,8 +1,7 @@ package org.opentripplanner.apis.gtfs.mapping; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static com.google.common.truth.Truth.assertThat; -import java.util.Arrays; import org.junit.jupiter.api.Test; import org.opentripplanner.transit.model.network.CarAccess; @@ -10,12 +9,10 @@ class CarsAllowedMapperTest { @Test void mapping() { - Arrays - .stream(CarAccess.values()) - .filter(ba -> ba != CarAccess.UNKNOWN) - .forEach(d -> { - var mapped = CarsAllowedMapper.map(d); - assertEquals(d.toString(), mapped.toString()); - }); + assertThat(CarAccess.ALLOWED.toString()) + .isEqualTo(CarsAllowedMapper.map(CarAccess.ALLOWED).toString()); + assertThat(CarAccess.NOT_ALLOWED.toString()) + .isEqualTo(CarsAllowedMapper.map(CarAccess.NOT_ALLOWED).toString()); + assertThat("NO_INFORMATION").isEqualTo(CarsAllowedMapper.map(CarAccess.UNKNOWN).toString()); } } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java index e5f77f57659..a71921148aa 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java +++ b/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; @@ -51,7 +51,7 @@ void linkingIsIdempotent() { module.buildGraph(); assertTrue(model.stopVertex().isConnectedToGraph()); - assertEquals(1, model.stopVertex().getOutgoing().size()); + assertThat(model.stopVertex().getOutgoing()).hasSize(1); } @Test @@ -63,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); @@ -87,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(); @@ -119,7 +119,7 @@ void linkCarsAllowedStop() { 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 - assertEquals(2, model.stopVertex().getOutgoing().size()); + assertThat(model.stopVertex().getOutgoing()).hasSize(2); var linkToWalk = model.outgoingLinks().getFirst(); SplitterVertex walkSplit = (SplitterVertex) linkToWalk.getToVertex(); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java index c5dea627d5a..c3fe0756bb0 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilterTest.java +++ b/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; @@ -769,23 +770,53 @@ void testBikesAllowed() { @Test void testCarsAllowed() { - RouteBuilder routeBuilder = TransitModelForTest.route("1"); - TripBuilder trip = Trip.of(TransitModelForTest.id("T1")).withRoute(routeBuilder.build()); - - assertEquals( - CarAccess.UNKNOWN, - RouteRequestTransitDataProviderFilter.carAccessForTrip(trip.build()) - ); - trip.withCarsAllowed(CarAccess.ALLOWED); - assertEquals( + TripTimes tripTimesCarsAllowed = createTestTripTimes( + TRIP_ID, + ROUTE, + BikeAccess.UNKNOWN, CarAccess.ALLOWED, - RouteRequestTransitDataProviderFilter.carAccessForTrip(trip.build()) + TransitMode.FERRY, + null, + Accessibility.NO_INFORMATION, + TripAlteration.PLANNED ); - trip.withCarsAllowed(CarAccess.NOT_ALLOWED); - assertEquals( + + TripTimes tripTimesCarsNotAllowed = createTestTripTimes( + TRIP_ID, + ROUTE, + BikeAccess.UNKNOWN, CarAccess.NOT_ALLOWED, - RouteRequestTransitDataProviderFilter.carAccessForTrip(trip.build()) + 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 From 7e1022fce9eb2d7282830bf87e916ac279c4fc7a Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 20 Sep 2024 14:12:22 +0300 Subject: [PATCH 08/12] Add changes to comments and tests. --- .../transit/model/network/BikeAccess.java | 1 - .../transit/model/network/CarAccess.java | 1 - .../apis/gtfs/mapping/BikesAllowedMapperTest.java | 11 +++++------ .../apis/gtfs/mapping/CarsAllowedMapperTest.java | 11 +++++------ 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java b/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java index 043c93e04e6..c0d850e9ce7 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java +++ b/src/main/java/org/opentripplanner/transit/model/network/BikeAccess.java @@ -2,7 +2,6 @@ /** * This represents the state of whether bikes are allowed on board trips (or routes). - * If the state is unknown, bikes are not allowed. *

* GTFS codes: * 0 = unknown / unspecified, 1 = bikes allowed, 2 = bikes NOT allowed diff --git a/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java b/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java index e5e15913197..2fca43cfb32 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java +++ b/src/main/java/org/opentripplanner/transit/model/network/CarAccess.java @@ -2,7 +2,6 @@ /** * This represents the state of whether cars are allowed on board trips. - * If the state is unknown, cars are not allowed. *

* GTFS codes: * 0 = unknown / unspecified, 1 = cars allowed, 2 = cars NOT allowed diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java index af1441e7dc4..035d3605a21 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapperTest.java @@ -1,18 +1,17 @@ package org.opentripplanner.apis.gtfs.mapping; -import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; 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() { - assertThat(BikeAccess.ALLOWED.toString()) - .isEqualTo(BikesAllowedMapper.map(BikeAccess.ALLOWED).toString()); - assertThat(BikeAccess.NOT_ALLOWED.toString()) - .isEqualTo(BikesAllowedMapper.map(BikeAccess.NOT_ALLOWED).toString()); - assertThat("NO_INFORMATION").isEqualTo(BikesAllowedMapper.map(BikeAccess.UNKNOWN).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/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java index 9e942eee458..a38df9b685b 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java @@ -1,18 +1,17 @@ package org.opentripplanner.apis.gtfs.mapping; -import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; import org.opentripplanner.transit.model.network.CarAccess; class CarsAllowedMapperTest { @Test void mapping() { - assertThat(CarAccess.ALLOWED.toString()) - .isEqualTo(CarsAllowedMapper.map(CarAccess.ALLOWED).toString()); - assertThat(CarAccess.NOT_ALLOWED.toString()) - .isEqualTo(CarsAllowedMapper.map(CarAccess.NOT_ALLOWED).toString()); - assertThat("NO_INFORMATION").isEqualTo(CarsAllowedMapper.map(CarAccess.UNKNOWN).toString()); + assertEquals(GraphQLCarsAllowed.NO_INFORMATION, CarsAllowedMapper.map(CarAccess.UNKNOWN)); + assertEquals(GraphQLCarsAllowed.NOT_ALLOWED, CarsAllowedMapper.map(CarAccess.NOT_ALLOWED)); + assertEquals(GraphQLCarsAllowed.ALLOWED, CarsAllowedMapper.map(CarAccess.ALLOWED)); } } From f3e982b244595a393cba56e6376064456429ae68 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 24 Sep 2024 09:43:26 +0300 Subject: [PATCH 09/12] Remove unused carsAllowed related information from schema. --- .../apis/gtfs/generated/GraphQLTypes.java | 6 ------ .../apis/gtfs/generated/graphql-codegen.yml | 1 - .../apis/gtfs/mapping/CarsAllowedMapper.java | 15 --------------- .../opentripplanner/apis/gtfs/schema.graphqls | 9 --------- .../gtfs/mapping/CarsAllowedMapperTest.java | 17 ----------------- 5 files changed, 48 deletions(-) delete mode 100644 src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java delete mode 100644 src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index 06f515466ac..e93fca59ac6 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -566,12 +566,6 @@ public void setGraphQLBannedNetworks(List bannedNetworks) { } } - public enum GraphQLCarsAllowed { - ALLOWED, - NOT_ALLOWED, - NO_INFORMATION, - } - public static class GraphQLCyclingOptimizationInput { private GraphQLTriangleCyclingFactorsInput triangle; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index fb72e63986c..29490a28b78 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -53,7 +53,6 @@ config: RentalVehicle: org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle#VehicleRentalVehicle VehicleRentalUris: org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris#VehicleRentalStationUris BikesAllowed: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed#GraphQLBikesAllowed - CarsAllowed: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed#GraphQLCarsAllowed BookingInfo: org.opentripplanner.transit.model.timetable.booking.BookingInfo#BookingInfo BookingTime: org.opentripplanner.transit.model.timetable.booking.BookingTime#BookingTime CarPark: org.opentripplanner.routing.vehicle_parking.VehicleParking#VehicleParking diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java deleted file mode 100644 index 8b35133e615..00000000000 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.opentripplanner.apis.gtfs.mapping; - -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; -import org.opentripplanner.transit.model.network.CarAccess; - -public class CarsAllowedMapper { - - public static GraphQLCarsAllowed map(CarAccess carsAllowed) { - return switch (carsAllowed) { - case UNKNOWN -> GraphQLCarsAllowed.NO_INFORMATION; - case ALLOWED -> GraphQLCarsAllowed.ALLOWED; - case NOT_ALLOWED -> GraphQLCarsAllowed.NOT_ALLOWED; - }; - } -} diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 618d3cb4850..01f4d30d487 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2774,15 +2774,6 @@ enum BikesAllowed { NO_INFORMATION } -enum CarsAllowed { - "The vehicle being used on this particular trip can accommodate at least one car." - ALLOWED - "No cars are allowed on this trip." - NOT_ALLOWED - "There is no car information for the trip." - NO_INFORMATION -} - """ Predefined optimization alternatives for bicycling routing. For more customization, one can use the triangle factors. diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java deleted file mode 100644 index a38df9b685b..00000000000 --- a/src/test/java/org/opentripplanner/apis/gtfs/mapping/CarsAllowedMapperTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.opentripplanner.apis.gtfs.mapping; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLCarsAllowed; -import org.opentripplanner.transit.model.network.CarAccess; - -class CarsAllowedMapperTest { - - @Test - void mapping() { - assertEquals(GraphQLCarsAllowed.NO_INFORMATION, CarsAllowedMapper.map(CarAccess.UNKNOWN)); - assertEquals(GraphQLCarsAllowed.NOT_ALLOWED, CarsAllowedMapper.map(CarAccess.NOT_ALLOWED)); - assertEquals(GraphQLCarsAllowed.ALLOWED, CarsAllowedMapper.map(CarAccess.ALLOWED)); - } -} From 1d36e7c8837c97aa8d51f6bc2bd77ef42e13b6e6 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 27 Sep 2024 13:40:34 +0300 Subject: [PATCH 10/12] Change location of function and make if statement chain more compact. --- .../module/StreetLinkerModule.java | 20 ++++++++++++++++++- ...RouteRequestTransitDataProviderFilter.java | 12 ++++------- .../transit/service/TransitModel.java | 17 ---------------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java index 402e1e5eedb..0930f5a4576 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -85,7 +85,7 @@ public void linkTransitStops(Graph graph, TransitModel transitModel) { Set stopLocationsUsedForFlexTrips = Set.of(); if (OTPFeature.FlexRouting.isOn()) { - stopLocationsUsedForFlexTrips = transitModel.getStopLocationsUsedForFlexTrips(); + stopLocationsUsedForFlexTrips = getStopLocationsUsedForFlexTrips(transitModel); } Set stopLocationsUsedForCarsAllowedTrips = transitModel.getStopLocationsUsedForCarsAllowedTrips(); @@ -326,6 +326,24 @@ private VehicleParking removeVehicleParkingEntranceVertexFromGraph( } } + private Set getStopLocationsUsedForFlexTrips(TransitModel transitModel) { + Set stopLocations = transitModel + .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/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java index a4f89c0cf7e..514a5b56f9a 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RouteRequestTransitDataProviderFilter.java @@ -103,16 +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) { - if (trip.getCarsAllowed() != CarAccess.ALLOWED) { - return false; - } + if (requireCarsAllowed && trip.getCarsAllowed() != CarAccess.ALLOWED) { + return false; } if (wheelchairEnabled) { diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModel.java b/src/main/java/org/opentripplanner/transit/service/TransitModel.java index 99c0f3fadb8..b2347bf6351 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModel.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModel.java @@ -560,23 +560,6 @@ public FlexTrip getFlexTrip(FeedScopedId tripId) { return flexTripsById.get(tripId); } - public Set getStopLocationsUsedForFlexTrips() { - Set stopLocations = 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; - } - /** * 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 From 39634b52d5d851574d2874ce0b676496727c9e64 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 27 Sep 2024 15:18:52 +0300 Subject: [PATCH 11/12] Change test to conform with new changes. --- .../graph_builder/module/StreetLinkerModuleTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java index a71921148aa..e4d62a0dbd6 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java @@ -211,17 +211,17 @@ public void withCarsAllowedTrip(Trip trip, StopLocation... stops) { }) .toList(); StopPattern stopPattern = new StopPattern(stopTimes); - TripPattern tripPattern = TransitModelForTest - .tripPattern("carsAllowedTripPattern", route) - .withStopPattern(stopPattern) - .build(); RealTimeTripTimes tripTimes = TripTimesFactory.tripTimes( trip, stopTimes, transitModel.getDeduplicator() ); + TripPattern tripPattern = TransitModelForTest + .tripPattern("carsAllowedTripPattern", route) + .withStopPattern(stopPattern) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)) + .build(); - tripPattern.add(tripTimes); transitModel.addTripPattern(tripPattern.getId(), tripPattern); } } From 9958ffc98ca2a7877610d81611fdfe3471d6f3c9 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 17 Oct 2024 12:18:23 +0300 Subject: [PATCH 12/12] Rename transitModel to timetableRepository. --- .../graph_builder/module/StreetLinkerModule.java | 10 ++++++---- .../graph_builder/module/StreetLinkerModuleTest.java | 10 +++++----- 2 files changed, 11 insertions(+), 9 deletions(-) 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 a0f37c392ce..dce445d381a 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 @@ -89,10 +89,10 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor Set stopLocationsUsedForFlexTrips = Set.of(); if (OTPFeature.FlexRouting.isOn()) { - stopLocationsUsedForFlexTrips = getStopLocationsUsedForFlexTrips(transitModel); + stopLocationsUsedForFlexTrips = getStopLocationsUsedForFlexTrips(timetableRepository); } - Set stopLocationsUsedForCarsAllowedTrips = transitModel.getStopLocationsUsedForCarsAllowedTrips(); + 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 @@ -357,8 +357,10 @@ private VehicleParking removeVehicleParkingEntranceVertexFromGraph( } } - private Set getStopLocationsUsedForFlexTrips(TransitModel transitModel) { - Set stopLocations = transitModel + private Set getStopLocationsUsedForFlexTrips( + TimetableRepository timetableRepository + ) { + Set stopLocations = timetableRepository .getAllFlexTrips() .stream() .flatMap(t -> t.getStops().stream()) 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 359ed3e938f..3973a7e506a 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 @@ -105,7 +105,7 @@ void linkFlexStop() { @Test void linkCarsAllowedStop() { var model = new TestModel(); - var carsAllowedTrip = TransitModelForTest + var carsAllowedTrip = TimetableRepositoryForTest .of() .trip("carsAllowedTrip") .withCarsAllowed(CarAccess.ALLOWED) @@ -198,7 +198,7 @@ public void withFlexTrip(UnscheduledTrip flexTrip) { } public void withCarsAllowedTrip(Trip trip, StopLocation... stops) { - Route route = TransitModelForTest.route("carsAllowedRoute").build(); + Route route = TimetableRepositoryForTest.route("carsAllowedRoute").build(); var stopTimes = Arrays .stream(stops) .map(s -> { @@ -214,15 +214,15 @@ public void withCarsAllowedTrip(Trip trip, StopLocation... stops) { RealTimeTripTimes tripTimes = TripTimesFactory.tripTimes( trip, stopTimes, - transitModel.getDeduplicator() + timetableRepository.getDeduplicator() ); - TripPattern tripPattern = TransitModelForTest + TripPattern tripPattern = TimetableRepositoryForTest .tripPattern("carsAllowedTripPattern", route) .withStopPattern(stopPattern) .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)) .build(); - transitModel.addTripPattern(tripPattern.getId(), tripPattern); + timetableRepository.addTripPattern(tripPattern.getId(), tripPattern); } } }