Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rental system to GraphQL API #5909

Merged
merged 21 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import org.opentripplanner.apis.gtfs.datafetchers.VehicleParkingImpl;
import org.opentripplanner.apis.gtfs.datafetchers.VehiclePositionImpl;
import org.opentripplanner.apis.gtfs.datafetchers.VehicleRentalStationImpl;
import org.opentripplanner.apis.gtfs.datafetchers.VehicleRentalSystemImpl;
import org.opentripplanner.apis.gtfs.datafetchers.debugOutputImpl;
import org.opentripplanner.apis.gtfs.datafetchers.elevationProfileComponentImpl;
import org.opentripplanner.apis.gtfs.datafetchers.placeAtDistanceImpl;
Expand Down Expand Up @@ -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(VehicleRentalSystemImpl.class))
.type(typeWiring.build(RentalVehicleImpl.class))
.type(typeWiring.build(RentalVehicleTypeImpl.class))
.type(typeWiring.build(StopOnRouteImpl.class))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ public DataFetcher<Connection<PlaceAtDistance>> nearest() {
List<PlaceType> filterByPlaceTypes = args.getGraphQLFilterByPlaceTypes() != null
? args.getGraphQLFilterByPlaceTypes().stream().map(GraphQLUtils::toModel).toList()
: DEFAULT_PLACE_TYPES;
List<String> filterByNetwork = args.getGraphQLFilterByNetwork();

List<PlaceAtDistance> places;
try {
Expand All @@ -347,6 +348,7 @@ public DataFetcher<Connection<PlaceAtDistance>> nearest() {
filterByStations,
filterByRoutes,
filterByBikeRentalStations,
filterByNetwork,
getTransitService(environment)
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -61,6 +62,11 @@ public DataFetcher<RentalVehicleType> vehicleType() {
return environment -> getSource(environment).vehicleType;
}

@Override
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to expose core model classes directly in the API. Can you please add an Impl (=mapper) class just like RentalVehicleImpl?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a mapper.

public DataFetcher<VehicleRentalSystem> vehicleRentalSystem() {
return environment -> getSource(environment).getVehicleRentalSystem();
}

private VehicleRentalVehicle getSource(DataFetchingEnvironment environment) {
return environment.getSource();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -107,6 +108,11 @@ public DataFetcher<RentalVehicleEntityCounts> availableSpaces() {
return environment -> getSource(environment).getVehicleSpaceCounts();
}

@Override
public DataFetcher<VehicleRentalSystem> vehicleRentalSystem() {
return environment -> getSource(environment).getVehicleRentalSystem();
}

private VehicleRentalStation getSource(DataFetchingEnvironment environment) {
return environment.getSource();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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 VehicleRentalSystemImpl implements GraphQLDataFetchers.GraphQLVehicleRentalSystem {

@Override
public DataFetcher<String> url() {
return environment -> getSource(environment).url;
}

private VehicleRentalSystem getSource(DataFetchingEnvironment environment) {
return environment.getSource();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -857,6 +858,8 @@ public interface GraphQLRentalVehicle {
public DataFetcher<String> vehicleId();

public DataFetcher<RentalVehicleType> vehicleType();

DataFetcher<VehicleRentalSystem> vehicleRentalSystem();
}

public interface GraphQLRentalVehicleEntityCounts {
Expand Down Expand Up @@ -1300,13 +1303,19 @@ public interface GraphQLVehicleRentalStation {

public DataFetcher<VehicleRentalStationUris> rentalUris();

public DataFetcher<VehicleRentalSystem> vehicleRentalSystem();

public DataFetcher<Integer> spacesAvailable();

public DataFetcher<String> stationId();

public DataFetcher<Integer> vehiclesAvailable();
}

public interface GraphQLVehicleRentalSystem {
public DataFetcher<String> url();
}

public interface GraphQLVehicleRentalUris {
public DataFetcher<String> android();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2416,6 +2416,7 @@ public static class GraphQLQueryTypeNearestArgs {
private Double lon;
private Integer maxDistance;
private Integer maxResults;
private List<String> filterByNetwork;

public GraphQLQueryTypeNearestArgs(Map<String, Object> args) {
if (args != null) {
Expand Down Expand Up @@ -2447,6 +2448,7 @@ public GraphQLQueryTypeNearestArgs(Map<String, Object> args) {
this.lon = (Double) args.get("lon");
this.maxDistance = (Integer) args.get("maxDistance");
this.maxResults = (Integer) args.get("maxResults");
this.filterByNetwork = (List<String>) args.get("filterByNetwork");
}
}

Expand Down Expand Up @@ -2537,6 +2539,14 @@ public void setGraphQLMaxDistance(Integer maxDistance) {
public void setGraphQLMaxResults(Integer maxResults) {
this.maxResults = maxResults;
}

public List<String> getGraphQLFilterByNetwork() {
return this.filterByNetwork;
}

public void setGraphQLFilterByNetwork(List<String> networks) {
this.filterByNetwork = networks;
}
}

public static class GraphQLQueryTypeNodeArgs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
VehicleRentalSystem: 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,7 @@ private GraphQLSchema create() {
List<String> filterByBikeRentalStations = null;
List<String> filterByBikeParks = null;
List<String> filterByCarParks = null;
List<String> filterByNetwork = null;
@SuppressWarnings("rawtypes")
Map filterByIds = environment.getArgument("filterByIds");
if (filterByIds != null) {
Expand Down Expand Up @@ -960,6 +961,7 @@ private GraphQLSchema create() {
filterByStations,
filterByRoutes,
filterByBikeRentalStations,
filterByNetwork,
GqlUtil.getTransitService(environment)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ public interface LayerParameters<T extends Enum<T>> {
int MAX_ZOOM = 20;
int CACHE_MAX_SECONDS = -1;
double EXPANSION_FACTOR = 0.25d;

/**
* User-visible name of the layer
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public List<PlaceAtDistance> findClosestPlaces(
List<FeedScopedId> filterByStations,
List<FeedScopedId> filterByRoutes,
List<String> filterByBikeRentalStations,
List<String> filterByNetwork,
TransitService transitService
) {
throw new UnsupportedOperationException("Not implemented");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ List<PlaceAtDistance> findClosestPlaces(
List<FeedScopedId> filterByStations,
List<FeedScopedId> filterByRoutes,
List<String> filterByBikeRentalStations,
List<String> filterByNetwork,
TransitService transitService
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class PlaceFinderTraverseVisitor implements TraverseVisitor<State, Edge>
private final Set<FeedScopedId> filterByStops;
private final Set<FeedScopedId> filterByStations;
private final Set<FeedScopedId> filterByRoutes;
private final Set<String> filterByNetwork;
private final Set<String> filterByVehicleRental;
private final Set<String> seenPatternAtStops = new HashSet<>();
private final Set<FeedScopedId> seenStops = new HashSet<>();
Expand Down Expand Up @@ -69,6 +70,7 @@ public PlaceFinderTraverseVisitor(
List<FeedScopedId> filterByStations,
List<FeedScopedId> filterByRoutes,
List<String> filterByBikeRentalStations,
List<String> filterByNetwork,
int maxResults,
double radiusMeters
) {
Expand All @@ -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);
Expand Down Expand Up @@ -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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public List<PlaceAtDistance> findClosestPlaces(
List<FeedScopedId> filterByStations,
List<FeedScopedId> filterByRoutes,
List<String> filterByBikeRentalStations,
List<String> filterByNetwork,
TransitService transitService
) {
PlaceFinderTraverseVisitor visitor = new PlaceFinderTraverseVisitor(
Expand All @@ -66,6 +67,7 @@ public List<PlaceAtDistance> findClosestPlaces(
filterByStations,
filterByRoutes,
filterByBikeRentalStations,
filterByNetwork,
maxResults,
radiusMeters
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ public VehicleRentalStationUris getRentalUris() {
return rentalUris;
}

@Override
public VehicleRentalSystem getVehicleRentalSystem() {
return system;
}

@Override
public String toString() {
return String.format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,9 @@ public boolean isRealTimeData() {
public VehicleRentalStationUris getRentalUris() {
return rentalUris;
}

@Override
public VehicleRentalSystem getVehicleRentalSystem() {
return system;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 names."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the term "network name" is confusing here. Is it the network id?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While updating it, I noticed we could also make the list items non null so I did it in the same commit 87bda74

filterByNetwork: [String],
"Only return places that are one of these types, e.g. `STOP` or `VEHICLE_RENT`"
filterByPlaceTypes: [FilterPlaceType],
first: Int,
Expand Down Expand Up @@ -1746,6 +1748,8 @@ type RentalVehicle implements Node & PlaceInterface {
rentalUris: VehicleRentalUris
"ID of the vehicle in the format of network:id"
vehicleId: String
"The vehicle rental system information."
vehicleRentalSystem: VehicleRentalSystem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to discuss in the developer meeting what to do with the naming of this field/type and if it's already too late to create a type for this.

"The type of the rental vehicle (scooter, bicycle, car...)"
vehicleType: RentalVehicleType
}
Expand Down Expand Up @@ -2464,13 +2468,20 @@ type VehicleRentalStation implements Node & PlaceInterface {
spacesAvailable: Int @deprecated(reason : "Use `availableSpaces` instead, which also contains the space vehicle types")
"ID of the vehicle in the format of network:id"
stationId: String
"The vehicle rental system information."
vehicleRentalSystem: VehicleRentalSystem
"""
Number of vehicles currently available on the rental station.
See field `allowPickupNow` to know if is currently possible to pick up a vehicle.
"""
vehiclesAvailable: Int @deprecated(reason : "Use `availableVehicles` instead, which also contains vehicle types")
}

type VehicleRentalSystem {
"The rental vehicle operator's system URL."
url: String
}

type VehicleRentalUris {
"""
A URI that can be passed to an Android app with an {@code android.intent.action.VIEW} Android
Expand Down
Loading
Loading