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 option to include stations in nearest search #5390

Merged
merged 34 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c2d17db
Add option to include stations in stop search
viljaminurminen-cgi Oct 3, 2023
b3ce4df
Fix formatting
viljaminurminen-cgi Oct 3, 2023
327067d
Add place type for station and modify handleStop logic in PlaceFinder…
viljaminurminen-cgi Oct 12, 2023
a2a3bb4
Remove unused code
viljaminurminen-cgi Oct 12, 2023
94ea8c3
Remove further unused code
viljaminurminen-cgi Oct 12, 2023
e860a68
Add tests for PlaceFinderTraverseVisitor
viljaminurminen-cgi Oct 12, 2023
699bb47
Optimize imports
viljaminurminen-cgi Oct 12, 2023
895085f
Apply suggestions from code review
nurmAV Oct 12, 2023
d367d1a
Add a nearest query to GraphQLIntegrationTest
viljaminurminen-cgi Oct 17, 2023
1dc5cf8
Merge branch 'otp-dev-2.x' into DT-5799
viljaminurminen-cgi Oct 17, 2023
a18d52d
Fix formatting
viljaminurminen-cgi Oct 17, 2023
128e738
Add test for PlaceFinderTraverseVisitor default settings
viljaminurminen-cgi Oct 17, 2023
681ab33
Prevent place finder filters from being null
viljaminurminen-cgi Oct 23, 2023
8176433
Only include stations in nearest search if explicitly requested
viljaminurminen-cgi Oct 26, 2023
0ed2305
Remove null and empty list support for filterByPlaceTypes
viljaminurminen-cgi Oct 31, 2023
1b0c379
Use all place types except STATION by default in nearest query
viljaminurminen-cgi Oct 31, 2023
925008c
Only create default list of place types once
viljaminurminen-cgi Nov 3, 2023
a54231b
Improve readability of stop handling code
viljaminurminen-cgi Nov 9, 2023
c0a9df3
Add station filter to place finder and GraphQL interfaces
viljaminurminen-cgi Nov 28, 2023
0b57c58
Merge branch 'otp-dev-2.x' into DT-5799
viljaminurminen-cgi Nov 28, 2023
b2f04a0
Fix how stops are created in tests
viljaminurminen-cgi Nov 28, 2023
a5fa1f4
Fix nearest query and response in integration test
viljaminurminen-cgi Nov 28, 2023
e5892a2
Apply feedback from review
nurmAV Nov 30, 2023
b373fc8
Remove redundant seenStops checks
viljaminurminen-cgi Nov 30, 2023
3f51bb6
Fix stop filtering when only searcing for stops
viljaminurminen-cgi Dec 5, 2023
b4dff93
Deprecate filterByIds in nearest search
viljaminurminen-cgi Dec 8, 2023
a08b8e4
Remove needless test in old API location
viljaminurminen-cgi Dec 8, 2023
bd20cba
Remove unnecessary logic
viljaminurminen-cgi Dec 12, 2023
7092574
Clean up code
viljaminurminen-cgi Dec 12, 2023
8627c7e
Apply feedback from review
nurmAV Dec 12, 2023
1bc6785
Improve readability of complex if statement
viljaminurminen-cgi Dec 12, 2023
57d064c
Apply feedback from review
nurmAV Dec 12, 2023
fb202d8
Fix formatting
viljaminurminen-cgi Dec 12, 2023
fd1973d
Merge branch 'otp-dev-2.x' into DT-5799
viljaminurminen-cgi Dec 19, 2023
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 @@ -900,6 +900,7 @@ private GraphQLSchema create() {
.argument(relay.getConnectionFieldArguments())
.dataFetcher(environment -> {
List<FeedScopedId> filterByStops = null;
List<FeedScopedId> filterByStations = null;
List<FeedScopedId> filterByRoutes = null;
List<String> filterByBikeRentalStations = null;
List<String> filterByBikeParks = null;
Expand Down Expand Up @@ -949,6 +950,7 @@ private GraphQLSchema create() {
filterByTransportModes,
filterByPlaceTypes,
filterByStops,
filterByStations,
filterByRoutes,
filterByBikeRentalStations,
GqlUtil.getTransitService(environment)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public static PlaceType toModel(GraphQLFilterPlaceType type) {
case CAR_PARK -> PlaceType.CAR_PARK;
case DEPARTURE_ROW -> PlaceType.PATTERN_AT_STOP;
case STOP -> PlaceType.STOP;
case STATION -> PlaceType.STATION;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation;
import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.site.Station;

public class PlaceInterfaceTypeResolver implements TypeResolver {

Expand Down Expand Up @@ -45,7 +46,7 @@ public GraphQLObjectType getType(TypeResolutionEnvironment environment) {
if (o instanceof PatternAtStop) {
return schema.getObjectType("DepartureRow");
}
if (o instanceof RegularStop) {
if (o instanceof RegularStop || o instanceof Station) {
return schema.getObjectType("Stop");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.locationtech.jts.geom.Coordinate;
Expand Down Expand Up @@ -75,6 +77,10 @@ public class QueryTypeImpl implements GraphQLDataFetchers.GraphQLQueryType {
DataImportIssueStore.NOOP
);

private final List<PlaceType> DEFAULT_PLACE_TYPES = List.copyOf(
EnumSet.complementOf(EnumSet.of(PlaceType.STATION))
);

@Override
public DataFetcher<Iterable<Agency>> agencies() {
return environment -> getTransitService(environment).getAgencies();
Expand Down Expand Up @@ -276,6 +282,7 @@ public DataFetcher<Trip> fuzzyTrip() {
public DataFetcher<Connection<PlaceAtDistance>> nearest() {
return environment -> {
List<FeedScopedId> filterByStops = null;
List<FeedScopedId> filterByStations = null;
List<FeedScopedId> filterByRoutes = null;
List<String> filterByBikeRentalStations = null;
// TODO implement
Expand All @@ -293,6 +300,14 @@ public DataFetcher<Connection<PlaceAtDistance>> nearest() {
filterByIds.getGraphQLStops() != null
? filterByIds.getGraphQLStops().stream().map(FeedScopedId::parse).toList()
: null;
filterByStations =
filterByIds.getGraphQLStations() != null
? filterByIds
.getGraphQLStations()
.stream()
.map(FeedScopedId::parse)
.collect(Collectors.toList())
nurmAV marked this conversation as resolved.
Show resolved Hide resolved
: null;
filterByRoutes =
filterByIds.getGraphQLRoutes() != null
? filterByIds.getGraphQLRoutes().stream().map(FeedScopedId::parse).toList()
Expand All @@ -318,7 +333,7 @@ public DataFetcher<Connection<PlaceAtDistance>> nearest() {
: null;
List<PlaceType> filterByPlaceTypes = args.getGraphQLFilterByPlaceTypes() != null
? args.getGraphQLFilterByPlaceTypes().stream().map(GraphQLUtils::toModel).toList()
: null;
: DEFAULT_PLACE_TYPES;

List<PlaceAtDistance> places;
try {
Expand All @@ -333,6 +348,7 @@ public DataFetcher<Connection<PlaceAtDistance>> nearest() {
filterByModes,
filterByPlaceTypes,
filterByStops,
filterByStations,
filterByRoutes,
filterByBikeRentalStations,
getTransitService(environment)
Expand Down
Copy link
Member

Choose a reason for hiding this comment

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

Same here

Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ public enum GraphQLFilterPlaceType {
BIKE_PARK,
CAR_PARK,
DEPARTURE_ROW,
STATION,
STOP,
VEHICLE_RENT,
}
Expand Down Expand Up @@ -372,6 +373,7 @@ public static class GraphQLInputFiltersInput {
private List<String> bikeRentalStations;
private List<String> carParks;
private List<String> routes;
private List<String> stations;
private List<String> stops;

public GraphQLInputFiltersInput(Map<String, Object> args) {
Expand All @@ -380,6 +382,7 @@ public GraphQLInputFiltersInput(Map<String, Object> args) {
this.bikeRentalStations = (List<String>) args.get("bikeRentalStations");
this.carParks = (List<String>) args.get("carParks");
this.routes = (List<String>) args.get("routes");
this.stations = (List<String>) args.get("stations");
this.stops = (List<String>) args.get("stops");
}
}
Expand All @@ -400,6 +403,10 @@ public List<String> getGraphQLRoutes() {
return this.routes;
}

public List<String> getGraphQLStations() {
return this.stations;
}

public List<String> getGraphQLStops() {
return this.stops;
}
Expand All @@ -420,6 +427,10 @@ public void setGraphQLRoutes(List<String> routes) {
this.routes = routes;
}

public void setGraphQLStations(List<String> stations) {
this.stations = stations;
}

public void setGraphQLStops(List<String> stops) {
this.stops = stops;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public List<PlaceAtDistance> findClosestPlaces(
List<TransitMode> filterByModes,
List<PlaceType> filterByPlaceTypes,
List<FeedScopedId> filterByStops,
List<FeedScopedId> filterByStations,
List<FeedScopedId> filterByRoutes,
List<String> filterByBikeRentalStations,
TransitService transitService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ List<PlaceAtDistance> findClosestPlaces(
List<TransitMode> filterByModes,
List<PlaceType> filterByPlaceTypes,
List<FeedScopedId> filterByStops,
List<FeedScopedId> filterByStations,
List<FeedScopedId> filterByRoutes,
List<String> filterByBikeRentalStations,
TransitService transitService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class PlaceFinderTraverseVisitor implements TraverseVisitor<State, Edge>
private final TransitService transitService;
private final Set<TransitMode> filterByModes;
private final Set<FeedScopedId> filterByStops;
private final Set<FeedScopedId> filterByStations;
private final Set<FeedScopedId> filterByRoutes;
private final Set<String> filterByVehicleRental;
private final Set<String> seenPatternAtStops = new HashSet<>();
Expand All @@ -40,6 +41,7 @@ public class PlaceFinderTraverseVisitor implements TraverseVisitor<State, Edge>
private final boolean includeVehicleRentals;
private final boolean includeCarParking;
private final boolean includeBikeParking;
private final boolean includeStations;
private final int maxResults;
private final double radiusMeters;

Expand All @@ -64,23 +66,31 @@ public PlaceFinderTraverseVisitor(
List<TransitMode> filterByModes,
List<PlaceType> filterByPlaceTypes,
List<FeedScopedId> filterByStops,
List<FeedScopedId> filterByStations,
List<FeedScopedId> filterByRoutes,
List<String> filterByBikeRentalStations,
int maxResults,
double radiusMeters
) {
if (filterByPlaceTypes == null || filterByPlaceTypes.isEmpty()) {
throw new IllegalArgumentException("No place type filter was included in request");
}
this.transitService = transitService;

this.filterByModes = toSet(filterByModes);
this.filterByStops = toSet(filterByStops);
this.filterByStations = toSet(filterByStations);
this.filterByRoutes = toSet(filterByRoutes);
this.filterByVehicleRental = toSet(filterByBikeRentalStations);

includeStops = shouldInclude(filterByPlaceTypes, PlaceType.STOP);

includePatternAtStops = shouldInclude(filterByPlaceTypes, PlaceType.PATTERN_AT_STOP);
includeVehicleRentals = shouldInclude(filterByPlaceTypes, PlaceType.VEHICLE_RENT);
includeCarParking = shouldInclude(filterByPlaceTypes, PlaceType.CAR_PARK);
includeBikeParking = shouldInclude(filterByPlaceTypes, PlaceType.BIKE_PARK);
includeStations = shouldInclude(filterByPlaceTypes, PlaceType.STATION);
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 special handling for this as we don't want to return stations by default.

This fact needs to also documented in the query docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there some better way to do so than to simply add a check for whether filterByPlaceTypes is empty? Where exactly are the query docs that should be changed?

Copy link
Member

Choose a reason for hiding this comment

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

No, that's fine.

The docs to mentioned this are here:

"""
Only return places that are one of these types, e.g. `STOP` or `VEHICLE_RENT`
"""
filterByPlaceTypes: [FilterPlaceType]

Copy link
Member

Choose a reason for hiding this comment

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

Please also add a test for this slightly strange behaviour.

Copy link
Member

Choose a reason for hiding this comment

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

Please make sure that stations are only included if you explicitly enable them.

this.maxResults = maxResults;

this.radiusMeters = radiusMeters;
}

Expand Down Expand Up @@ -134,7 +144,7 @@ public SkipEdgeStrategy<State, Edge> getSkipEdgeStrategy() {

private static <T> Set<T> toSet(List<T> list) {
if (list == null) {
return null;
return Set.of();
}
return Set.copyOf(list);
}
Expand All @@ -156,7 +166,7 @@ private void handleParking(VehicleParking parking, double distance) {
}

private boolean shouldInclude(List<PlaceType> filterByPlaceTypes, PlaceType type) {
return filterByPlaceTypes == null || filterByPlaceTypes.contains(type);
return filterByPlaceTypes.contains(type);
}

private boolean stopHasPatternsWithMode(RegularStop stop, Set<TransitMode> modes) {
Expand All @@ -167,17 +177,40 @@ private boolean stopHasPatternsWithMode(RegularStop stop, Set<TransitMode> modes
.anyMatch(modes::contains);
}

private boolean stopIsIncludedByStopFilter(RegularStop stop) {
return filterByStops.isEmpty() || filterByStops.contains(stop.getId());
}

private boolean stopIsIncludedByStationFilter(RegularStop stop) {
return (
((filterByStations.isEmpty() || filterByStations.contains(stop.getParentStation().getId())))
);
}

private boolean stopIsIncludedByModeFilter(RegularStop stop) {
return filterByModes.isEmpty() || stopHasPatternsWithMode(stop, filterByModes);
}

private void handleStop(RegularStop stop, double distance) {
if (filterByStops != null && !filterByStops.contains(stop.getId())) {
return;
}
// Do not consider stop if it is not included in the stop or mode filter
// or if it or its parent station has already been seen.
if (
includeStops &&
!seenStops.contains(stop.getId()) &&
(filterByModes == null || stopHasPatternsWithMode(stop, filterByModes))
(includeStations && !stop.isPartOfStation() && !stopIsIncludedByStopFilter(stop)) ||
(!includeStations && !stopIsIncludedByStopFilter(stop)) ||
(stop.isPartOfStation() && !stopIsIncludedByStationFilter(stop)) ||
Copy link
Member

Choose a reason for hiding this comment

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

This part is a bit hard to follow but I don't know what to do about it.

Copy link
Member

Choose a reason for hiding this comment

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

I would put each part in between || into a separate method which has a name and possibly a bit of Javadoc.

This doesn't make the logic less complicated but helps someone else understand what each piece does.

seenStops.contains(stop.getId()) ||
seenStops.contains(stop.getStationOrStopId()) ||
!stopIsIncludedByModeFilter(stop)
) {
placesFound.add(new PlaceAtDistance(stop, distance));
return;
}

if (includeStations && stop.getParentStation() != null) {
seenStops.add(stop.getParentStation().getId());
placesFound.add(new PlaceAtDistance(stop.getParentStation(), distance));
} else if (includeStops) {
seenStops.add(stop.getId());
placesFound.add(new PlaceAtDistance(stop, distance));
}
}

Expand All @@ -186,9 +219,9 @@ private void handlePatternsAtStop(RegularStop stop, double distance) {
List<TripPattern> patterns = transitService
.getPatternsForStop(stop)
.stream()
.filter(pattern -> filterByModes == null || filterByModes.contains(pattern.getMode()))
.filter(pattern -> filterByModes.isEmpty() || filterByModes.contains(pattern.getMode()))
.filter(pattern ->
filterByRoutes == null || filterByRoutes.contains(pattern.getRoute().getId())
filterByRoutes.isEmpty() || filterByRoutes.contains(pattern.getRoute().getId())
)
.filter(pattern -> pattern.canBoard(stop))
.toList();
Expand All @@ -209,7 +242,9 @@ private void handleVehicleRental(VehicleRentalPlace station, double distance) {
if (!includeVehicleRentals) {
return;
}
if (filterByVehicleRental != null && !filterByVehicleRental.contains(station.getStationId())) {
if (
!filterByVehicleRental.isEmpty() && !filterByVehicleRental.contains(station.getStationId())
) {
return;
}
if (seenVehicleRentalPlaces.contains(station.getId())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public enum PlaceType {
VEHICLE_RENT,
BIKE_PARK,
CAR_PARK,
STATION,
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public List<PlaceAtDistance> findClosestPlaces(
List<TransitMode> filterByModes,
List<PlaceType> filterByPlaceTypes,
List<FeedScopedId> filterByStops,
List<FeedScopedId> filterByStations,
List<FeedScopedId> filterByRoutes,
List<String> filterByBikeRentalStations,
TransitService transitService
Expand All @@ -62,6 +63,7 @@ public List<PlaceAtDistance> findClosestPlaces(
filterByModes,
filterByPlaceTypes,
filterByStops,
filterByStations,
filterByRoutes,
filterByBikeRentalStations,
maxResults,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,10 @@ enum FeedAlertType {
}

enum FilterPlaceType {
"""Stops"""
"""
Stops.
NOTE: if this is selected at the same time as `STATION`, stops that have a parent station will not be returned but their parent stations will be returned instead.
"""
STOP

"""Departure rows"""
Expand All @@ -1019,6 +1022,13 @@ enum FilterPlaceType {

"""Parking lots that contain spaces for cars"""
CAR_PARK


"""
Stations.
NOTE: if this is selected at the same time as `STOP`, stops that have a parent station will not be returned but their parent stations will be returned instead.
"""
STATION
}

type Geometry {
Expand Down Expand Up @@ -1098,6 +1108,9 @@ input InputFilters {
"""Stops to include by GTFS id."""
stops: [String]

"""Stations to include by GTFS id."""
stations: [String]
leonardehrenfried marked this conversation as resolved.
Show resolved Hide resolved

"""Routes to include by GTFS id."""
routes: [String]

Expand Down Expand Up @@ -2560,7 +2573,7 @@ type QueryType {
filterByModes: [Mode]

"""Only include places that match one of the given GTFS ids."""
filterByIds: InputFilters
filterByIds: InputFilters @deprecated(reason: "Not actively maintained")
before: String
after: String
first: Int
Expand Down
Loading
Loading