diff --git a/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java b/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java index 3876ff44651..b65c22b4727 100644 --- a/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java +++ b/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java @@ -8,6 +8,7 @@ import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.updater.spi.DataSource; import org.opentripplanner.updater.spi.GenericJsonDataSource; @@ -32,6 +33,7 @@ public class SmooveBikeRentalDataSource private final String networkName; private final RentalVehicleType vehicleType; + private final VehicleRentalSystem system; public SmooveBikeRentalDataSource(SmooveBikeRentalDataSourceParameters config) { this(config, new OtpHttpClientFactory()); @@ -45,6 +47,24 @@ public SmooveBikeRentalDataSource( networkName = config.getNetwork(DEFAULT_NETWORK_NAME); vehicleType = RentalVehicleType.getDefaultType(networkName); overloadingAllowed = config.overloadingAllowed(); + system = + new VehicleRentalSystem( + networkName, + "fi", + "Helsinki/Espoo", + null, + null, + null, + null, + null, + null, + null, + null, + "Europe/Helsinki", + null, + null, + null + ); } /** @@ -94,6 +114,7 @@ protected VehicleRentalStation parseElement(JsonNode node) { station.vehicleTypesAvailable = Map.of(vehicleType, station.vehiclesAvailable); station.vehicleSpacesAvailable = Map.of(vehicleType, station.spacesAvailable); station.overloadingAllowed = overloadingAllowed; + station.system = system; return station; } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index e309346339b..ca059723acd 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -75,6 +75,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.UnknownImpl; import org.opentripplanner.apis.gtfs.datafetchers.VehicleParkingImpl; import org.opentripplanner.apis.gtfs.datafetchers.VehiclePositionImpl; +import org.opentripplanner.apis.gtfs.datafetchers.VehicleRentalNetworkImpl; import org.opentripplanner.apis.gtfs.datafetchers.VehicleRentalStationImpl; import org.opentripplanner.apis.gtfs.datafetchers.debugOutputImpl; import org.opentripplanner.apis.gtfs.datafetchers.elevationProfileComponentImpl; @@ -166,6 +167,7 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(BookingTimeImpl.class)) .type(typeWiring.build(BookingInfoImpl.class)) .type(typeWiring.build(VehicleRentalStationImpl.class)) + .type(typeWiring.build(VehicleRentalNetworkImpl.class)) .type(typeWiring.build(RentalVehicleImpl.class)) .type(typeWiring.build(RentalVehicleTypeImpl.class)) .type(typeWiring.build(StopOnRouteImpl.class)) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java index eae4bc2ad33..f187a49d9c7 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -330,6 +330,7 @@ public DataFetcher> nearest() { List filterByPlaceTypes = args.getGraphQLFilterByPlaceTypes() != null ? args.getGraphQLFilterByPlaceTypes().stream().map(GraphQLUtils::toModel).toList() : DEFAULT_PLACE_TYPES; + List filterByNetwork = args.getGraphQLFilterByNetwork(); List places; try { @@ -347,6 +348,7 @@ public DataFetcher> nearest() { filterByStations, filterByRoutes, filterByBikeRentalStations, + filterByNetwork, getTransitService(environment) ) ); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java index 53c3ba1343d..c4fb92c0ef4 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RentalVehicleImpl.java @@ -6,6 +6,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; public class RentalVehicleImpl implements GraphQLDataFetchers.GraphQLRentalVehicle { @@ -61,6 +62,11 @@ public DataFetcher vehicleType() { return environment -> getSource(environment).vehicleType; } + @Override + public DataFetcher rentalNetwork() { + return environment -> getSource(environment).getVehicleRentalSystem(); + } + private VehicleRentalVehicle getSource(DataFetchingEnvironment environment) { return environment.getSource(); } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalNetworkImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalNetworkImpl.java new file mode 100644 index 00000000000..75f771eed83 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalNetworkImpl.java @@ -0,0 +1,23 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; + +public class VehicleRentalNetworkImpl implements GraphQLDataFetchers.GraphQLVehicleRentalNetwork { + + @Override + public DataFetcher networkId() { + return environment -> getSource(environment).systemId; + } + + @Override + public DataFetcher url() { + return environment -> getSource(environment).url; + } + + private VehicleRentalSystem getSource(DataFetchingEnvironment environment) { + return environment.getSource(); + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalStationImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalStationImpl.java index dc60a7c76e8..0603d19e412 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalStationImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/VehicleRentalStationImpl.java @@ -7,6 +7,7 @@ import org.opentripplanner.service.vehiclerental.model.RentalVehicleEntityCounts; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; public class VehicleRentalStationImpl implements GraphQLDataFetchers.GraphQLVehicleRentalStation { @@ -107,6 +108,11 @@ public DataFetcher availableSpaces() { return environment -> getSource(environment).getVehicleSpaceCounts(); } + @Override + public DataFetcher rentalNetwork() { + return environment -> getSource(environment).getVehicleRentalSystem(); + } + private VehicleRentalStation getSource(DataFetchingEnvironment environment) { return environment.getSource(); } 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 6c66d3992f4..67944543580 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -58,6 +58,7 @@ 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; @@ -852,6 +853,8 @@ public interface GraphQLRentalVehicle { public DataFetcher operative(); + public DataFetcher rentalNetwork(); + public DataFetcher rentalUris(); public DataFetcher vehicleId(); @@ -1266,6 +1269,17 @@ public interface GraphQLVehiclePosition { public DataFetcher vehicleId(); } + /** + * Vehicle rental network, which is referred as system in the GBFS terminology. Note, the same operator can operate in multiple + * regions either with the same network/system or with a different one. This can contain information about either the rental brand + * or about the operator. + */ + public interface GraphQLVehicleRentalNetwork { + public DataFetcher networkId(); + + public DataFetcher url(); + } + /** Vehicle rental station represents a location where users can rent bicycles etc. for a fee. */ public interface GraphQLVehicleRentalStation { public DataFetcher allowDropoff(); @@ -1298,6 +1312,8 @@ public interface GraphQLVehicleRentalStation { public DataFetcher realtime(); + public DataFetcher rentalNetwork(); + public DataFetcher rentalUris(); public DataFetcher spacesAvailable(); 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 3c187ca3bbe..541219481ef 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -2409,6 +2409,7 @@ public static class GraphQLQueryTypeNearestArgs { private String before; private GraphQLInputFiltersInput filterByIds; private List filterByModes; + private List filterByNetwork; private List filterByPlaceTypes; private Integer first; private Integer last; @@ -2430,6 +2431,7 @@ public GraphQLQueryTypeNearestArgs(Map args) { .map(GraphQLMode.class::cast) .collect(Collectors.toList()); } + this.filterByNetwork = (List) args.get("filterByNetwork"); if (args.get("filterByPlaceTypes") != null) { this.filterByPlaceTypes = ((List) args.get("filterByPlaceTypes")).stream() @@ -2466,6 +2468,10 @@ public List getGraphQLFilterByModes() { return this.filterByModes; } + public List getGraphQLFilterByNetwork() { + return this.filterByNetwork; + } + public List getGraphQLFilterByPlaceTypes() { return this.filterByPlaceTypes; } @@ -2510,6 +2516,10 @@ public void setGraphQLFilterByModes(List filterByModes) { this.filterByModes = filterByModes; } + public void setGraphQLFilterByNetwork(List filterByNetwork) { + this.filterByNetwork = filterByNetwork; + } + public void setGraphQLFilterByPlaceTypes(List filterByPlaceTypes) { this.filterByPlaceTypes = filterByPlaceTypes; } 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 ff9af6f6aa0..b9ee0ac3e16 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 @@ -47,6 +47,7 @@ config: BikeRentalStation: org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace#VehicleRentalPlace BikeRentalStationUris: org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris#VehicleRentalStationUris VehicleRentalStation: org.opentripplanner.service.vehiclerental.model.VehicleRentalStation#VehicleRentalStation + VehicleRentalNetwork: org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem#VehicleRentalSystem RentalVehicleEntityCounts: org.opentripplanner.service.vehiclerental.model.RentalVehicleEntityCounts#RentalVehicleEntityCounts RentalVehicleTypeCount: org.opentripplanner.service.vehiclerental.model.RentalVehicleTypeCount#RentalVehicleTypeCount RentalVehicle: org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle#VehicleRentalVehicle diff --git a/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java b/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java index a88c36ac039..9ad43606420 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java @@ -912,6 +912,7 @@ private GraphQLSchema create() { List filterByBikeRentalStations = null; List filterByBikeParks = null; List filterByCarParks = null; + List filterByNetwork = null; @SuppressWarnings("rawtypes") Map filterByIds = environment.getArgument("filterByIds"); if (filterByIds != null) { @@ -960,6 +961,7 @@ private GraphQLSchema create() { filterByStations, filterByRoutes, filterByBikeRentalStations, + filterByNetwork, GqlUtil.getTransitService(environment) ); diff --git a/src/main/java/org/opentripplanner/inspector/vector/LayerParameters.java b/src/main/java/org/opentripplanner/inspector/vector/LayerParameters.java index ca4a6a64c76..cb849e0f0ac 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/LayerParameters.java +++ b/src/main/java/org/opentripplanner/inspector/vector/LayerParameters.java @@ -10,7 +10,6 @@ public interface LayerParameters> { int MAX_ZOOM = 20; int CACHE_MAX_SECONDS = -1; double EXPANSION_FACTOR = 0.25d; - /** * User-visible name of the layer */ diff --git a/src/main/java/org/opentripplanner/routing/graphfinder/DirectGraphFinder.java b/src/main/java/org/opentripplanner/routing/graphfinder/DirectGraphFinder.java index 5ffa7cd2301..c36efc59e5b 100644 --- a/src/main/java/org/opentripplanner/routing/graphfinder/DirectGraphFinder.java +++ b/src/main/java/org/opentripplanner/routing/graphfinder/DirectGraphFinder.java @@ -63,6 +63,7 @@ public List findClosestPlaces( List filterByStations, List filterByRoutes, List filterByBikeRentalStations, + List filterByNetwork, TransitService transitService ) { throw new UnsupportedOperationException("Not implemented"); diff --git a/src/main/java/org/opentripplanner/routing/graphfinder/GraphFinder.java b/src/main/java/org/opentripplanner/routing/graphfinder/GraphFinder.java index 4c0b0c81144..063ed221dd5 100644 --- a/src/main/java/org/opentripplanner/routing/graphfinder/GraphFinder.java +++ b/src/main/java/org/opentripplanner/routing/graphfinder/GraphFinder.java @@ -69,6 +69,7 @@ List findClosestPlaces( List filterByStations, List filterByRoutes, List filterByBikeRentalStations, + List filterByNetwork, TransitService transitService ); } diff --git a/src/main/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitor.java b/src/main/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitor.java index e71504d58f3..16420a0d9eb 100644 --- a/src/main/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitor.java +++ b/src/main/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitor.java @@ -31,6 +31,7 @@ public class PlaceFinderTraverseVisitor implements TraverseVisitor private final Set filterByStops; private final Set filterByStations; private final Set filterByRoutes; + private final Set filterByNetwork; private final Set filterByVehicleRental; private final Set seenPatternAtStops = new HashSet<>(); private final Set seenStops = new HashSet<>(); @@ -69,6 +70,7 @@ public PlaceFinderTraverseVisitor( List filterByStations, List filterByRoutes, List filterByBikeRentalStations, + List filterByNetwork, int maxResults, double radiusMeters ) { @@ -82,6 +84,7 @@ public PlaceFinderTraverseVisitor( this.filterByStations = toSet(filterByStations); this.filterByRoutes = toSet(filterByRoutes); this.filterByVehicleRental = toSet(filterByBikeRentalStations); + this.filterByNetwork = toSet(filterByNetwork); includeStops = shouldInclude(filterByPlaceTypes, PlaceType.STOP); includePatternAtStops = shouldInclude(filterByPlaceTypes, PlaceType.PATTERN_AT_STOP); @@ -264,6 +267,9 @@ private void handleVehicleRental(VehicleRentalPlace station, double distance) { if (seenVehicleRentalPlaces.contains(station.getId())) { return; } + if (!filterByNetwork.isEmpty() && !filterByNetwork.contains(station.getNetwork())) { + return; + } seenVehicleRentalPlaces.add(station.getId()); placesFound.add(new PlaceAtDistance(station, distance)); } diff --git a/src/main/java/org/opentripplanner/routing/graphfinder/StreetGraphFinder.java b/src/main/java/org/opentripplanner/routing/graphfinder/StreetGraphFinder.java index 1b2b1d8f522..71f65209ddf 100644 --- a/src/main/java/org/opentripplanner/routing/graphfinder/StreetGraphFinder.java +++ b/src/main/java/org/opentripplanner/routing/graphfinder/StreetGraphFinder.java @@ -56,6 +56,7 @@ public List findClosestPlaces( List filterByStations, List filterByRoutes, List filterByBikeRentalStations, + List filterByNetwork, TransitService transitService ) { PlaceFinderTraverseVisitor visitor = new PlaceFinderTraverseVisitor( @@ -66,6 +67,7 @@ public List findClosestPlaces( filterByStations, filterByRoutes, filterByBikeRentalStations, + filterByNetwork, maxResults, radiusMeters ); diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalPlace.java b/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalPlace.java index 1725e7df2dd..cd806603c9d 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalPlace.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalPlace.java @@ -80,6 +80,9 @@ public interface VehicleRentalPlace { /** Deep links for this rental station or individual vehicle */ VehicleRentalStationUris getRentalUris(); + /** System information for the vehicle rental provider */ + VehicleRentalSystem getVehicleRentalSystem(); + default boolean networkIsNotAllowed(VehicleRentalPreferences preferences) { if ( getNetwork() == null && diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalStation.java b/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalStation.java index a31e5e88a0e..d6e72023a31 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalStation.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalStation.java @@ -172,6 +172,11 @@ public VehicleRentalStationUris getRentalUris() { return rentalUris; } + @Override + public VehicleRentalSystem getVehicleRentalSystem() { + return system; + } + @Override public String toString() { return String.format( diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalVehicle.java b/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalVehicle.java index 7dba5e714b4..042e608c88f 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalVehicle.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/model/VehicleRentalVehicle.java @@ -128,4 +128,9 @@ public boolean isRealTimeData() { public VehicleRentalStationUris getRentalUris() { return rentalUris; } + + @Override + public VehicleRentalSystem getVehicleRentalSystem() { + return system; + } } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsGeofencingZoneMapper.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsGeofencingZoneMapper.java index caf5d97c0d4..22a4131f338 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsGeofencingZoneMapper.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsGeofencingZoneMapper.java @@ -10,6 +10,7 @@ import org.mobilitydata.gbfs.v2_3.geofencing_zones.GBFSGeofencingZones; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.UnsupportedGeometryException; +import org.opentripplanner.framework.lang.StringUtils; import org.opentripplanner.service.vehiclerental.model.GeofencingZone; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.slf4j.Logger; @@ -52,6 +53,9 @@ private GeofencingZone toInternalModel(GBFSFeature f) { return null; } var name = Objects.requireNonNullElseGet(f.getProperties().getName(), () -> fallbackId(g)); + if (!StringUtils.hasValue(name)) { + name = fallbackId(g); + } var dropOffBanned = !f.getProperties().getRules().get(0).getRideAllowed(); var passThroughBanned = !f.getProperties().getRules().get(0).getRideThroughAllowed(); return new GeofencingZone( diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 57fe17a55ab..6e1195f5901 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -1183,6 +1183,8 @@ type QueryType { nearest places related to bicycling. """ filterByModes: [Mode], + "Only include vehicle rental networks that match one of the given network ids." + filterByNetwork: [String!], "Only return places that are one of these types, e.g. `STOP` or `VEHICLE_RENT`" filterByPlaceTypes: [FilterPlaceType], first: Int, @@ -1739,9 +1741,11 @@ type RentalVehicle implements Node & PlaceInterface { "Name of the vehicle" name: String! "ID of the rental network." - network: String + network: String @deprecated(reason : "Use `networkId` from `rentalNetwork` instead.") "If true, vehicle is not disabled." operative: Boolean + "The vehicle rental network information. This is referred as system in the GBFS terminology." + rentalNetwork: VehicleRentalNetwork! "Platform-specific URLs to begin the vehicle." rentalUris: VehicleRentalUris "ID of the vehicle in the format of network:id" @@ -2413,6 +2417,21 @@ type VehiclePosition { vehicleId: String } +""" +Vehicle rental network, which is referred as system in the GBFS terminology. Note, the same operator can operate in multiple +regions either with the same network/system or with a different one. This can contain information about either the rental brand +or about the operator. +""" +type VehicleRentalNetwork { + """ + ID of the vehicle rental network. In GBFS, this is the `system_id` field from the system information, but it can + be overridden in the configuration to have a different value so this field doesn't necessarily match the source data. + """ + networkId: String! + "The rental vehicle operator's network/system URL. In GBFS, this is the `url` field from the system information." + url: String +} + "Vehicle rental station represents a location where users can rent bicycles etc. for a fee." type VehicleRentalStation implements Node & PlaceInterface { """ @@ -2443,7 +2462,7 @@ type VehicleRentalStation implements Node & PlaceInterface { "Name of the vehicle rental station" name: String! "ID of the rental network." - network: String + network: String @deprecated(reason : "Use `networkId` from `rentalNetwork` instead.") "If true, station is on and in service." operative: Boolean """ @@ -2452,6 +2471,8 @@ type VehicleRentalStation implements Node & PlaceInterface { are always the total capacity divided by two. """ realtime: Boolean + "The vehicle rental network information. This is referred as system in the GBFS terminology." + rentalNetwork: VehicleRentalNetwork! "Platform-specific URLs to begin renting a vehicle from this station." rentalUris: VehicleRentalUris """ diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 614c8778c6b..2fb7a8d9db2 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -73,8 +73,10 @@ import org.opentripplanner.service.realtimevehicles.internal.DefaultRealtimeVehicleService; import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle; import org.opentripplanner.service.vehiclerental.internal.DefaultVehicleRentalService; +import org.opentripplanner.service.vehiclerental.model.TestFreeFloatingRentalVehicleBuilder; import org.opentripplanner.service.vehiclerental.model.TestVehicleRentalStationBuilder; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.standalone.config.framework.json.JsonSupport; import org.opentripplanner.test.support.FilePatternSource; import org.opentripplanner.transit.model._data.TransitModelForTest; @@ -112,6 +114,18 @@ class GraphQLIntegrationTest { .map(p -> (RegularStop) p.stop) .toList(); + private static VehicleRentalStation VEHICLE_RENTAL_STATION = new TestVehicleRentalStationBuilder() + .withVehicles(10) + .withSpaces(10) + .withVehicleTypeBicycle(5, 7) + .withVehicleTypeElectricBicycle(5, 3) + .withSystem("Network-1", "https://foo.bar") + .build(); + + private static VehicleRentalVehicle RENTAL_VEHICLE = new TestFreeFloatingRentalVehicleBuilder() + .withSystem("Network-1", "https://foo.bar") + .build(); + static final Graph GRAPH = new Graph(); static final Instant ALERT_START_TIME = OffsetDateTime @@ -280,13 +294,8 @@ public TransitAlertService getTransitAlertService() { realtimeVehicleService.setRealtimeVehicles(pattern, List.of(occypancyVehicle, positionVehicle)); DefaultVehicleRentalService defaultVehicleRentalService = new DefaultVehicleRentalService(); - VehicleRentalStation vehicleRentalStation = new TestVehicleRentalStationBuilder() - .withVehicles(10) - .withSpaces(10) - .withVehicleTypeBicycle(5, 7) - .withVehicleTypeElectricBicycle(5, 3) - .build(); - defaultVehicleRentalService.addVehicleRentalStation(vehicleRentalStation); + defaultVehicleRentalService.addVehicleRentalStation(VEHICLE_RENTAL_STATION); + defaultVehicleRentalService.addVehicleRentalStation(RENTAL_VEHICLE); context = new GraphQLRequestContext( @@ -448,13 +457,15 @@ public List findClosestPlaces( List filterByStations, List filterByRoutes, List filterByBikeRentalStations, + List filterByNetwork, TransitService transitService ) { - return List - .of(TransitModelForTest.of().stop("A").build()) - .stream() - .map(stop -> new PlaceAtDistance(stop, 0)) - .toList(); + var stop = TransitModelForTest.of().stop("A").build(); + return List.of( + new PlaceAtDistance(stop, 0), + new PlaceAtDistance(VEHICLE_RENTAL_STATION, 30), + new PlaceAtDistance(RENTAL_VEHICLE, 50) + ); } }; } diff --git a/src/test/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitorTest.java b/src/test/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitorTest.java index 1ef675c8101..b69a6533334 100644 --- a/src/test/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitorTest.java +++ b/src/test/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitorTest.java @@ -13,6 +13,7 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.model.StopTime; +import org.opentripplanner.service.vehiclerental.model.TestVehicleRentalStationBuilder; import org.opentripplanner.street.search.state.TestStateBuilder; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.basic.TransitMode; @@ -96,6 +97,7 @@ void stopsOnly() { null, null, null, + null, 1, 500 ); @@ -124,6 +126,7 @@ void stationsOnly() { null, null, null, + null, 1, 500 ); @@ -152,6 +155,7 @@ void stopsAndStations() { null, null, null, + null, 1, 500 ); @@ -183,6 +187,7 @@ void stopsAndStationsWithStationFilter() { List.of(STATION1.getId()), null, null, + null, 1, 500 ); @@ -217,6 +222,7 @@ void stopsAndStationsWithStopFilter() { null, null, null, + null, 1, 500 ); @@ -250,6 +256,7 @@ void stopsAndStationsWithStopAndStationFilter() { List.of(STATION1.getId()), null, null, + null, 1, 500 ); @@ -274,4 +281,74 @@ void stopsAndStationsWithStopAndStationFilter() { visitor.visitVertex(state1); } + + @Test + void rentalStation() { + var visitor = new PlaceFinderTraverseVisitor( + transitService, + null, + List.of(PlaceType.VEHICLE_RENT), + null, + null, + null, + null, + null, + 1, + 500 + ); + var station = new TestVehicleRentalStationBuilder().build(); + assertEquals(List.of(), visitor.placesFound); + var state1 = TestStateBuilder.ofWalking().rentalStation(station).build(); + visitor.visitVertex(state1); + + var res = visitor.placesFound.stream().map(PlaceAtDistance::place).toList(); + + assertEquals(List.of(station), res); + } + + @Test + void rentalStationWithNetworksFilter() { + var visitor = new PlaceFinderTraverseVisitor( + transitService, + null, + List.of(PlaceType.VEHICLE_RENT), + null, + null, + null, + null, + List.of("Network-1"), + 1, + 500 + ); + var station = new TestVehicleRentalStationBuilder().build(); + assertEquals(List.of(), visitor.placesFound); + var state1 = TestStateBuilder.ofWalking().rentalStation(station).build(); + visitor.visitVertex(state1); + + var res = visitor.placesFound.stream().map(PlaceAtDistance::place).toList(); + + assertEquals(List.of(station), res); + + visitor = + new PlaceFinderTraverseVisitor( + transitService, + null, + List.of(PlaceType.VEHICLE_RENT), + null, + null, + null, + null, + List.of("Network-2"), + 1, + 500 + ); + + assertEquals(List.of(), visitor.placesFound); + state1 = TestStateBuilder.ofWalking().rentalStation(station).build(); + visitor.visitVertex(state1); + + res = visitor.placesFound.stream().map(PlaceAtDistance::place).toList(); + + assertEquals(List.of(), res); + } } diff --git a/src/test/java/org/opentripplanner/routing/graphfinder/StreetGraphFinderTest.java b/src/test/java/org/opentripplanner/routing/graphfinder/StreetGraphFinderTest.java index 3285e27594c..76f231577dd 100644 --- a/src/test/java/org/opentripplanner/routing/graphfinder/StreetGraphFinderTest.java +++ b/src/test/java/org/opentripplanner/routing/graphfinder/StreetGraphFinderTest.java @@ -156,6 +156,7 @@ void findClosestPlacesLimiting() { null, null, null, + null, transitService ) ); @@ -179,6 +180,7 @@ void findClosestPlacesLimiting() { null, null, null, + null, transitService ) ); @@ -196,6 +198,7 @@ void findClosestPlacesLimiting() { null, null, null, + null, transitService ) ); @@ -220,6 +223,7 @@ void findClosestPlacesWithAModeFilter() { null, null, null, + null, transitService ) ); @@ -237,6 +241,7 @@ void findClosestPlacesWithAModeFilter() { null, null, null, + null, transitService ) ); @@ -262,6 +267,7 @@ void findClosestPlacesWithAStopFilter() { null, null, null, + null, transitService ) ); @@ -279,6 +285,7 @@ void findClosestPlacesWithAStopFilter() { null, null, null, + null, transitService ) ); @@ -304,6 +311,7 @@ void findClosestPlacesWithAStopAndRouteFilter() { null, null, null, + null, transitService ) ); @@ -321,6 +329,7 @@ void findClosestPlacesWithAStopAndRouteFilter() { null, List.of(R1.getId()), null, + null, transitService ) ); @@ -347,6 +356,7 @@ void findClosestPlacesWithARouteFilter() { null, null, null, + null, transitService ) ); @@ -364,6 +374,7 @@ void findClosestPlacesWithARouteFilter() { null, List.of(R2.getId()), null, + null, transitService ) ); @@ -387,6 +398,7 @@ void findClosestPlacesWithAVehicleRentalFilter() { null, null, null, + null, transitService ) ); @@ -404,6 +416,7 @@ void findClosestPlacesWithAVehicleRentalFilter() { null, null, List.of("BR2"), + null, transitService ) ); @@ -426,6 +439,7 @@ void findClosestPlacesWithABikeParkFilter() { null, null, null, + null, transitService ) ); @@ -448,6 +462,7 @@ void findClosestPlacesWithACarParkFilter() { null, null, null, + null, transitService ) ); diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java index 4864fab5e43..c3837942426 100644 --- a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java +++ b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java @@ -13,6 +13,7 @@ public class TestFreeFloatingRentalVehicleBuilder { private double latitude = DEFAULT_LATITUDE; private double longitude = DEFAULT_LONGITUDE; + private VehicleRentalSystem system = null; private RentalVehicleType vehicleType = RentalVehicleType.getDefaultType(NETWORK_1); @@ -30,6 +31,28 @@ public TestFreeFloatingRentalVehicleBuilder withLongitude(double longitude) { return this; } + public TestFreeFloatingRentalVehicleBuilder withSystem(String id, String url) { + this.system = + new VehicleRentalSystem( + id, + null, + null, + null, + null, + url, + null, + null, + null, + null, + null, + null, + null, + null, + null + ); + return this; + } + public TestFreeFloatingRentalVehicleBuilder withVehicleScooter() { return buildVehicleType(RentalFormFactor.SCOOTER); } @@ -63,6 +86,7 @@ public VehicleRentalVehicle build() { vehicle.latitude = latitude; vehicle.longitude = longitude; vehicle.vehicleType = vehicleType; + vehicle.system = system; return vehicle; } } diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java index 33f922ff0b9..0fb9f8b620f 100644 --- a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java +++ b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java @@ -19,6 +19,7 @@ public class TestVehicleRentalStationBuilder { private int spaces = 10; private boolean overloadingAllowed = false; private boolean stationOn = false; + private VehicleRentalSystem system = null; private final Map vehicleTypesAvailable = new HashMap<>(); private final Map vehicleSpacesAvailable = new HashMap<>(); @@ -52,6 +53,28 @@ public TestVehicleRentalStationBuilder withStationOn(boolean stationOn) { return this; } + public TestVehicleRentalStationBuilder withSystem(String id, String url) { + this.system = + new VehicleRentalSystem( + id, + null, + null, + null, + null, + url, + null, + null, + null, + null, + null, + null, + null, + null, + null + ); + return this; + } + public TestVehicleRentalStationBuilder withVehicleTypeBicycle(int numAvailable, int numSpaces) { return buildVehicleType( RentalFormFactor.BICYCLE, @@ -127,6 +150,7 @@ public VehicleRentalStation build() { station.isRenting = stationOn; station.isReturning = stationOn; station.realTimeData = true; + station.system = system; return station; } } diff --git a/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java b/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java index e43c5a769d0..3750c4619b9 100644 --- a/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java +++ b/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java @@ -16,6 +16,7 @@ import org.opentripplanner.service.vehiclerental.model.TestFreeFloatingRentalVehicleBuilder; import org.opentripplanner.service.vehiclerental.model.TestVehicleRentalStationBuilder; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; import org.opentripplanner.service.vehiclerental.street.VehicleRentalEdge; import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex; @@ -219,6 +220,19 @@ public TestStateBuilder stop() { return arriveAtStop(testModel.stop("stop", count, count).build()); } + /** + * Add a state that arrives at a rental station. + */ + public TestStateBuilder rentalStation(VehicleRentalStation station) { + count++; + var from = (StreetVertex) currentState.vertex; + var to = new VehicleRentalPlaceVertex(station); + + var link = StreetVehicleRentalLink.createStreetVehicleRentalLink(from, to); + currentState = link.traverse(currentState)[0]; + return this; + } + public TestStateBuilder enterStation(String id) { count++; var from = (StreetVertex) currentState.vertex; diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/nearest.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/nearest.json index 82d929adeb1..b6b5b7ee674 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/nearest.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/nearest.json @@ -10,6 +10,20 @@ "parentStation" : null } } + }, + { + "node" : { + "place" : { + "stationId" : "Network-1:FooStation" + } + } + }, + { + "node" : { + "place" : { + "vehicleId" : "Network-1:free-floating-bicycle" + } + } } ] } diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/rental-vehicle.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/rental-vehicle.json new file mode 100644 index 00000000000..9017fe77a93 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/rental-vehicle.json @@ -0,0 +1,21 @@ +{ + "data": { + "rentalVehicle": { + "vehicleId":"Network-1:free-floating-bicycle", + "name":"free-floating-bicycle", + "allowPickupNow":true, + "lon":19.01, + "lat":47.52, + "rentalUris":null, + "operative":true, + "vehicleType": { + "formFactor":"BICYCLE", + "propulsionType":"HUMAN" + }, + "rentalNetwork": { + "networkId":"Network-1", + "url":"https://foo.bar" + } + } + } +} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rental-station.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rental-station.json index ef1284c5c5e..ad1ce76d9be 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rental-station.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/vehicle-rental-station.json @@ -47,13 +47,16 @@ "allowPickup" : false, "allowDropoffNow" : false, "allowPickupNow" : false, - "network" : "Network-1", "lon" : 18.99, "lat" : 47.51, "capacity" : null, "allowOverloading" : false, "rentalUris" : null, - "operative" : false + "operative" : false, + "rentalNetwork" : { + "networkId" : "Network-1", + "url" : "https://foo.bar" + } } } } diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/nearest.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/nearest.graphql index c7f8eed4213..469a117bab4 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/nearest.graphql +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/nearest.graphql @@ -1,5 +1,11 @@ { - nearest(lat: 60.19915, lon: 24.94089, maxDistance: 500) { + nearest( + lat: 60.19915 + lon: 24.94089 + maxDistance: 500 + filterByPlaceTypes: [STOP, VEHICLE_RENT] + filterByNetwork: ["Network-1"] + ) { edges { node { place { @@ -10,6 +16,12 @@ id } } + ... on RentalVehicle { + vehicleId + } + ... on VehicleRentalStation { + stationId + } } } } diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/rental-vehicle.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/rental-vehicle.graphql new file mode 100644 index 00000000000..9a912781c56 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/rental-vehicle.graphql @@ -0,0 +1,23 @@ +{ + rentalVehicle(id: "Network-1:free-floating-bicycle") { + vehicleId + name + allowPickupNow + lon + lat + rentalUris { + android + ios + web + } + operative + vehicleType { + formFactor + propulsionType + } + rentalNetwork { + networkId + url + } + } +} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rental-station.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rental-station.graphql index 8e555ffdbdd..a2200465912 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rental-station.graphql +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/vehicle-rental-station.graphql @@ -28,7 +28,6 @@ allowPickup allowDropoffNow allowPickupNow - network lon lat capacity @@ -39,5 +38,9 @@ web } operative + rentalNetwork { + networkId + url + } } }