From 4b052d5398c4a9ac920ae91a0b50471f214c3669 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Mon, 2 Oct 2023 17:01:59 +0300 Subject: [PATCH 001/162] Draft upcoming cancelled trips query --- .../apis/gtfs/datafetchers/DatedTripImpl.java | 37 ++++++++++++ .../gtfs/datafetchers/NodeTypeResolver.java | 4 ++ .../apis/gtfs/datafetchers/QueryTypeImpl.java | 20 +++++++ .../gtfs/generated/GraphQLDataFetchers.java | 38 ++++++++++++ .../apis/gtfs/generated/GraphQLTypes.java | 60 +++++++++++++++++++ .../apis/gtfs/generated/graphql-codegen.yml | 3 + .../transit/model/timetable/DatedTrip.java | 8 +++ .../service/DefaultTransitService.java | 32 ++++++++++ .../transit/service/TransitService.java | 3 + .../opentripplanner/apis/gtfs/schema.graphqls | 31 ++++++++++ 10 files changed, 236 insertions(+) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java create mode 100644 src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java new file mode 100644 index 00000000000..07f7c482754 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java @@ -0,0 +1,37 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.relay.Relay; +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import org.opentripplanner.api.mapping.LocalDateMapper; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.transit.model.timetable.DatedTrip; +import org.opentripplanner.transit.model.timetable.Trip; + +public class DatedTripImpl implements GraphQLDataFetchers.GraphQLDatedTrip { + + @Override + public DataFetcher date() { + return env -> LocalDateMapper.mapToApi(getSource(env).serviceDate()); + } + + @Override + public DataFetcher trip() { + return env -> getSource(env).trip(); + } + + @Override + public DataFetcher id() { + return env -> + new Relay.ResolvedGlobalId( + "DatedTrip", + getSource(env).trip().getId().toString() + + ';' + + LocalDateMapper.mapToApi(getSource(env).serviceDate()) + ); + } + + private DatedTrip getSource(DataFetchingEnvironment environment) { + return environment.getSource(); + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java index 437d75e03e9..63f80fb643f 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java @@ -20,6 +20,7 @@ import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; +import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; public class NodeTypeResolver implements TypeResolver { @@ -85,6 +86,9 @@ public GraphQLObjectType getType(TypeResolutionEnvironment environment) { if (o instanceof Trip) { return schema.getObjectType("Trip"); } + if (o instanceof DatedTrip) { + return schema.getObjectType("DatedTrip"); + } return null; } 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 dfa4a60ce1c..0af024b284e 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -63,6 +63,7 @@ import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; @@ -422,6 +423,8 @@ public DataFetcher node() { return new PlaceAtDistance(place, Double.parseDouble(parts[0])); } + case "DatedTrip": + return null; // ???? case "Route": return transitService.getRouteForId(FeedScopedId.parse(id)); case "Stop": @@ -787,6 +790,23 @@ public DataFetcher> trips() { }; } + @Override + public DataFetcher> cancelledTrips() { + return environment -> { + // var args = new GraphQLTypes.GraphQLQueryTypeGetTripsArgs(environment.getArguments()); + + Stream tripStream = getTransitService(environment).getCancelledTrips().stream(); + + /* if (args.getGraphQLFeeds() != null) { + List feeds = args.getGraphQLFeeds(); + tripStream = tripStream.filter(datedTrip -> feeds.contains(datedTrip.getTrip().getId().getFeedId())); + }*/ + + var datedTrips = tripStream.collect(Collectors.toList()); + return new SimpleListConnection<>(datedTrips).get(environment); + }; + } + @Override public DataFetcher vehicleParking() { return environment -> { 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 d0562215f70..b4050392ed7 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -2,11 +2,16 @@ package org.opentripplanner.apis.gtfs.generated; import graphql.relay.Connection; +import graphql.relay.Connection; +import graphql.relay.Connection; +import graphql.relay.Edge; +import graphql.relay.Edge; import graphql.relay.Edge; import graphql.schema.DataFetcher; import graphql.schema.TypeResolver; import java.util.Currency; import java.util.Map; +import java.util.Map; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.api.resource.DebugOutput; @@ -20,7 +25,11 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; +import org.opentripplanner.apis.gtfs.model.RouteTypeModel; +import org.opentripplanner.apis.gtfs.model.StopOnRouteModel; +import org.opentripplanner.apis.gtfs.model.StopOnTripModel; import org.opentripplanner.apis.gtfs.model.StopPosition; +import org.opentripplanner.apis.gtfs.model.UnknownModel; import org.opentripplanner.ext.fares.model.FareRuleSet; import org.opentripplanner.ext.ridehailing.model.RideEstimate; import org.opentripplanner.model.StopTimesInPattern; @@ -42,6 +51,8 @@ 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.VehicleParking; +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.vehiclepositions.model.RealtimeVehiclePosition; @@ -50,11 +61,13 @@ 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.VehicleRentalStationUris; 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.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; public class GraphQLDataFetchers { @@ -293,6 +306,29 @@ public interface GraphQLCurrency { public DataFetcher digits(); } + /** Trip on a specific date */ + public interface GraphQLDatedTrip { + public DataFetcher date(); + + public DataFetcher id(); + + public DataFetcher trip(); + } + + /** A connection to a list of dated trips */ + public interface GraphQLDatedTripConnection { + public DataFetcher>> edges(); + + public DataFetcher pageInfo(); + } + + /** An edge for DatedTrip connection */ + public interface GraphQLDatedTripEdge { + public DataFetcher cursor(); + + public DataFetcher node(); + } + /** * The standard case of a fare product: it only has a single price to be paid by the passenger * and no discounts are applied. @@ -665,6 +701,8 @@ public interface GraphQLQueryType { public DataFetcher> cancelledTripTimes(); + public DataFetcher> cancelledTrips(); + public DataFetcher carPark(); public DataFetcher> carParks(); 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 3593145e274..202364238ae 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -1,6 +1,7 @@ // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. package org.opentripplanner.apis.gtfs.generated; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -1205,6 +1206,65 @@ public void setGraphQLTrips(List trips) { } } + public static class GraphQLQueryTypeCancelledTripsArgs { + + private String after; + private String before; + private List feeds; + private Integer first; + private Integer last; + + public GraphQLQueryTypeCancelledTripsArgs(Map args) { + if (args != null) { + this.after = (String) args.get("after"); + this.before = (String) args.get("before"); + this.feeds = (List) args.get("feeds"); + this.first = (Integer) args.get("first"); + this.last = (Integer) args.get("last"); + } + } + + public String getGraphQLAfter() { + return this.after; + } + + public String getGraphQLBefore() { + return this.before; + } + + public List getGraphQLFeeds() { + return this.feeds; + } + + public Integer getGraphQLFirst() { + return this.first; + } + + public Integer getGraphQLLast() { + return this.last; + } + + public void setGraphQLAfter(String after) { + this.after = after; + } + + public void setGraphQLBefore(String before) { + this.before = before; + } + + public void setGraphQLFeeds(List feeds) { + this.feeds = feeds; + } + + public void setGraphQLFirst(Integer first) { + this.first = first; + } + + public void setGraphQLLast(Integer last) { + this.last = last; + } + } + public static class GraphQLQueryTypeCarParkArgs { private String id; 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 14d00ec1bd4..25002da4659 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,9 @@ config: ContactInfo: org.opentripplanner.transit.model.organization.ContactInfo Cluster: Object Coordinates: org.locationtech.jts.geom.Coordinate#Coordinate + DatedTrip: org.opentripplanner.transit.model.timetable.DatedTrip#DatedTrip + DatedTripConnection: graphql.relay.Connection#Connection + DatedTripEdge: graphql.relay.Edge#Edge debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java b/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java new file mode 100644 index 00000000000..0d544bb0c72 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java @@ -0,0 +1,8 @@ +package org.opentripplanner.transit.model.timetable; + +import java.time.LocalDate; + +/** + * Class which represents a trin on a specific date + */ +public record DatedTrip(Trip trip, LocalDate serviceDate) {} diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 31c453056af..21521024384 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -8,6 +8,7 @@ import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -46,6 +47,8 @@ import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.site.StopLocationsGroup; +import org.opentripplanner.transit.model.timetable.DatedTrip; +import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; @@ -247,6 +250,35 @@ public Trip getTripForId(FeedScopedId id) { return this.transitModelIndex.getTripForId().get(id); } + @Override + public Collection getCancelledTrips() { + OTPRequestTimeoutException.checkForTimeout(); + List cancelledTrips = new ArrayList<>(); + var timetableSnapshot = lazyGetTimeTableSnapShot(); + if (timetableSnapshot == null) { + return cancelledTrips; + } + var calendarService = getCalendarService(); + var patternMap = transitModelIndex.getPatternForTrip(); + + transitModelIndex + .getTripForId() + .values() + .forEach(trip -> { + Set serviceDates = calendarService.getServiceDatesForServiceId( + trip.getServiceId() + ); + var pattern = patternMap.get(trip); + for (LocalDate date : serviceDates) { + var tt = timetableSnapshot.resolve(pattern, date); + if (tt.getTripTimes(trip).getRealTimeState() == RealTimeState.CANCELED) { + cancelledTrips.add(new DatedTrip(trip, date)); + } + } + }); + return cancelledTrips; + } + @Override public Collection getAllTrips() { OTPRequestTimeoutException.checkForTimeout(); diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index 78c867ac83e..b612462fbca 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -38,6 +38,7 @@ import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.site.StopLocationsGroup; +import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; @@ -108,6 +109,8 @@ public interface TransitService { Collection getAllTrips(); + Collection getCancelledTrips(); + Collection getAllRoutes(); TripPattern getPatternForTrip(Trip trip); diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index aaf312244d1..20837e7699c 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2649,6 +2649,16 @@ type QueryType { maxArrivalTime: Int ): [Stoptime] + """Get cancelled Trips""" + cancelledTrips( + """Feed feedIds (e.g. ["HSL"]).""" + feeds: [String] + before: String + after: String + first: Int + last: Int + ): DatedTripConnection + """Get all patterns""" patterns: [Pattern] @@ -3974,6 +3984,27 @@ type Trip implements Node { ): [Alert] } +""" +Trip on a specific date +""" +type DatedTrip implements Node { + id: ID! + trip: Trip! + date: String! +} + +"""An edge for DatedTrip connection""" +type DatedTripEdge { + node: DatedTrip + cursor: String! +} + +"""A connection to a list of dated trips""" +type DatedTripConnection { + edges: [DatedTripEdge] + pageInfo: PageInfo! +} + """Entities, which are relevant for a trip and can contain alerts""" enum TripAlertType { """Alerts affecting the trip""" From 2e6529e9699e241ccfc9d15a55105df79ec75d12 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Tue, 3 Oct 2023 07:42:42 +0300 Subject: [PATCH 002/162] Remove duplicate imports --- .../apis/gtfs/generated/GraphQLDataFetchers.java | 8 -------- 1 file changed, 8 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 b4050392ed7..5f835dbbfc7 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -2,16 +2,11 @@ package org.opentripplanner.apis.gtfs.generated; import graphql.relay.Connection; -import graphql.relay.Connection; -import graphql.relay.Connection; -import graphql.relay.Edge; -import graphql.relay.Edge; import graphql.relay.Edge; import graphql.schema.DataFetcher; import graphql.schema.TypeResolver; import java.util.Currency; import java.util.Map; -import java.util.Map; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.api.resource.DebugOutput; @@ -51,8 +46,6 @@ 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.VehicleParking; -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.vehiclepositions.model.RealtimeVehiclePosition; @@ -61,7 +54,6 @@ 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.VehicleRentalStationUris; import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.transit.model.basic.Money; import org.opentripplanner.transit.model.network.Route; From 627d251c3890fe1179a78e73ecf223ba29de027c Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Tue, 3 Oct 2023 15:20:52 +0300 Subject: [PATCH 003/162] Node resolver for DatedTrip --- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 0af024b284e..4d1fb20f5a5 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -13,6 +13,7 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.DataFetchingEnvironmentImpl; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -424,7 +425,11 @@ public DataFetcher node() { return new PlaceAtDistance(place, Double.parseDouble(parts[0])); } case "DatedTrip": - return null; // ???? + { + String[] parts = id.split(";"); + var trip = transitService.getTripForId(FeedScopedId.parse(parts[0])); + return new DatedTrip(trip, LocalDate.parse(parts[1])); + } case "Route": return transitService.getRouteForId(FeedScopedId.parse(id)); case "Stop": From 6c264348bd23c995f8badd5a786b4963df330b58 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Wed, 4 Oct 2023 12:15:03 +0300 Subject: [PATCH 004/162] Hook in DatedTrip data fetcher --- .../java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index d7b936a6198..d4ada6e4552 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -38,6 +38,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.CoordinatesImpl; import org.opentripplanner.apis.gtfs.datafetchers.CurrencyImpl; +import org.opentripplanner.apis.gtfs.datafetchers.DatedTripImpl; import org.opentripplanner.apis.gtfs.datafetchers.DefaultFareProductImpl; import org.opentripplanner.apis.gtfs.datafetchers.DepartureRowImpl; import org.opentripplanner.apis.gtfs.datafetchers.FareProductTypeResolver; @@ -170,6 +171,7 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(CurrencyImpl.class)) .type(typeWiring.build(FareProductUseImpl.class)) .type(typeWiring.build(DefaultFareProductImpl.class)) + .type(typeWiring.build(DatedTripImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring); From fb71d748a1ff84266a6948396bd87b8aff4ccca0 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Wed, 4 Oct 2023 14:33:18 +0300 Subject: [PATCH 005/162] Feed filtering parameter for cancelled trips query --- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 4d1fb20f5a5..19acfcaaef2 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -798,14 +798,15 @@ public DataFetcher> trips() { @Override public DataFetcher> cancelledTrips() { return environment -> { - // var args = new GraphQLTypes.GraphQLQueryTypeGetTripsArgs(environment.getArguments()); + var args = new GraphQLTypes.GraphQLQueryTypeCancelledTripsArgs(environment.getArguments()); Stream tripStream = getTransitService(environment).getCancelledTrips().stream(); - /* if (args.getGraphQLFeeds() != null) { - List feeds = args.getGraphQLFeeds(); - tripStream = tripStream.filter(datedTrip -> feeds.contains(datedTrip.getTrip().getId().getFeedId())); - }*/ + List feeds = args.getGraphQLFeeds(); + if (feeds != null) { + tripStream = + tripStream.filter(datedTrip -> feeds.contains(datedTrip.trip().getId().getFeedId())); + } var datedTrips = tripStream.collect(Collectors.toList()); return new SimpleListConnection<>(datedTrips).get(environment); From 184e7e2ca27a1e5534db0f6f11af313c4b489fe8 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Wed, 4 Oct 2023 15:07:53 +0300 Subject: [PATCH 006/162] More efficient iteration over all trips --- .../service/DefaultTransitService.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 21521024384..787a501adfe 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -260,22 +260,21 @@ public Collection getCancelledTrips() { } var calendarService = getCalendarService(); var patternMap = transitModelIndex.getPatternForTrip(); - - transitModelIndex - .getTripForId() - .values() - .forEach(trip -> { - Set serviceDates = calendarService.getServiceDatesForServiceId( - trip.getServiceId() - ); - var pattern = patternMap.get(trip); - for (LocalDate date : serviceDates) { - var tt = timetableSnapshot.resolve(pattern, date); - if (tt.getTripTimes(trip).getRealTimeState() == RealTimeState.CANCELED) { - cancelledTrips.add(new DatedTrip(trip, date)); - } + var trips = transitModelIndex.getTripForId(); + + for (Map.Entry entry : trips.entrySet()) { + var trip = entry.getValue(); + Set serviceDates = calendarService.getServiceDatesForServiceId( + trip.getServiceId() + ); + var pattern = patternMap.get(trip); + for (LocalDate date : serviceDates) { + var tt = timetableSnapshot.resolve(pattern, date); + if (tt.getTripTimes(trip).getRealTimeState() == RealTimeState.CANCELED) { + cancelledTrips.add(new DatedTrip(trip, date)); } - }); + } + } return cancelledTrips; } From 2787b7e2e6f68d501280354362596e294e9e424d Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Wed, 4 Oct 2023 16:12:08 +0300 Subject: [PATCH 007/162] Sort cancelled trips by ascending time --- .../service/DefaultTransitService.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 787a501adfe..05424da9208 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -10,6 +10,7 @@ import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -254,6 +255,8 @@ public Trip getTripForId(FeedScopedId id) { public Collection getCancelledTrips() { OTPRequestTimeoutException.checkForTimeout(); List cancelledTrips = new ArrayList<>(); + Map departures = new HashMap<>(); + var timetableSnapshot = lazyGetTimeTableSnapShot(); if (timetableSnapshot == null) { return cancelledTrips; @@ -269,12 +272,32 @@ public Collection getCancelledTrips() { ); var pattern = patternMap.get(trip); for (LocalDate date : serviceDates) { - var tt = timetableSnapshot.resolve(pattern, date); - if (tt.getTripTimes(trip).getRealTimeState() == RealTimeState.CANCELED) { + var timetable = timetableSnapshot.resolve(pattern, date); + var tripTimes = timetable.getTripTimes(trip); + if (tripTimes.getRealTimeState() == RealTimeState.CANCELED) { // use UPDATED for faked testing cancelledTrips.add(new DatedTrip(trip, date)); + // store departure time from first stop + departures.put(trip, tripTimes.sortIndex()); } } } + cancelledTrips.sort((t1, t2) -> { + if (t1.serviceDate().isBefore(t2.serviceDate())) { + return -1; + } else if (t2.serviceDate().isBefore(t1.serviceDate())) { + return 1; + } + var departure1 = departures.get(t1.trip()); + var departure2 = departures.get(t2.trip()); + if (departure1 < departure2) { + return -1; + } else if (departure1 > departure2) { + return 1; + } else { + // identical departure day and time, so sort by unique feedscope id + return t1.trip().getId().compareTo(t2.trip().getId()); + } + }); return cancelledTrips; } From 58f0387ef867cd6061d2311dde41e644d2850ddc Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Wed, 4 Oct 2023 16:44:31 +0300 Subject: [PATCH 008/162] Move filtering by feed into transit service It is much more efficient to filter trips in early stage --- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 14 ++++---------- .../transit/service/DefaultTransitService.java | 9 ++++++--- .../transit/service/TransitService.java | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) 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 19acfcaaef2..cf1f6c87f0f 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -799,16 +799,10 @@ public DataFetcher> trips() { public DataFetcher> cancelledTrips() { return environment -> { var args = new GraphQLTypes.GraphQLQueryTypeCancelledTripsArgs(environment.getArguments()); - - Stream tripStream = getTransitService(environment).getCancelledTrips().stream(); - - List feeds = args.getGraphQLFeeds(); - if (feeds != null) { - tripStream = - tripStream.filter(datedTrip -> feeds.contains(datedTrip.trip().getId().getFeedId())); - } - - var datedTrips = tripStream.collect(Collectors.toList()); + var datedTrips = getTransitService(environment) + .getCancelledTrips(args.getGraphQLFeeds()) + .stream() + .collect(Collectors.toList()); return new SimpleListConnection<>(datedTrips).get(environment); }; } diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 05424da9208..cecb62d7635 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -252,7 +252,7 @@ public Trip getTripForId(FeedScopedId id) { } @Override - public Collection getCancelledTrips() { + public Collection getCancelledTrips(List feeds) { OTPRequestTimeoutException.checkForTimeout(); List cancelledTrips = new ArrayList<>(); Map departures = new HashMap<>(); @@ -267,6 +267,9 @@ public Collection getCancelledTrips() { for (Map.Entry entry : trips.entrySet()) { var trip = entry.getValue(); + if (feeds != null && !feeds.contains(trip.getId().getFeedId())) { + continue; + } Set serviceDates = calendarService.getServiceDatesForServiceId( trip.getServiceId() ); @@ -276,7 +279,7 @@ public Collection getCancelledTrips() { var tripTimes = timetable.getTripTimes(trip); if (tripTimes.getRealTimeState() == RealTimeState.CANCELED) { // use UPDATED for faked testing cancelledTrips.add(new DatedTrip(trip, date)); - // store departure time from first stop + // store departure time from first stop departures.put(trip, tripTimes.sortIndex()); } } @@ -294,7 +297,7 @@ public Collection getCancelledTrips() { } else if (departure1 > departure2) { return 1; } else { - // identical departure day and time, so sort by unique feedscope id + // identical departure day and time, so sort by unique feedscope id return t1.trip().getId().compareTo(t2.trip().getId()); } }); diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index b612462fbca..bb3b1b54ab1 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -109,7 +109,7 @@ public interface TransitService { Collection getAllTrips(); - Collection getCancelledTrips(); + Collection getCancelledTrips(List feeds); Collection getAllRoutes(); From 20df02b87e3598524100587dc9fbe30ee87117e8 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Wed, 7 Aug 2024 19:52:54 +0300 Subject: [PATCH 009/162] Use LocalDate for date and include trip's fields instead of trip --- .../apis/gtfs/datafetchers/DatedTripImpl.java | 88 +++++++++++++++++-- .../gtfs/generated/GraphQLDataFetchers.java | 14 ++- .../apis/gtfs/generated/GraphQLTypes.java | 41 ++++++--- .../transit/model/timetable/DatedTrip.java | 2 +- .../opentripplanner/apis/gtfs/schema.graphqls | 28 +++++- 5 files changed, 150 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java index 599f4038190..f8bdf40dbc2 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java @@ -3,21 +3,26 @@ import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; +import java.time.Instant; +import java.time.LocalDate; +import java.util.List; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.ext.restapi.mapping.LocalDateMapper; +import org.opentripplanner.framework.time.ServiceDateUtils; +import org.opentripplanner.model.Timetable; +import org.opentripplanner.model.TripTimeOnDate; +import org.opentripplanner.transit.model.network.Route; +import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.service.TransitService; public class DatedTripImpl implements GraphQLDataFetchers.GraphQLDatedTrip { @Override - public DataFetcher date() { - return env -> LocalDateMapper.mapToApi(getSource(env).serviceDate()); - } - - @Override - public DataFetcher trip() { - return env -> getSource(env).trip(); + public DataFetcher date() { + return env -> getSource(env).serviceDate(); } @Override @@ -31,6 +36,75 @@ public DataFetcher id() { ); } + @Override + public DataFetcher pattern() { + return this::getTripPattern; + } + + @Override + public DataFetcher route() { + return environment -> getSource(environment).trip().getRoute(); + } + + @Override + public DataFetcher> stops() { + return this::getStops; + } + + @Override + public DataFetcher> stoptimes() { + return environment -> { + TransitService transitService = getTransitService(environment); + Trip trip = getSource(environment).trip(); + + var serviceDate = getSource(environment).serviceDate(); + TripPattern tripPattern = transitService.getPatternForTrip(trip, serviceDate); + // no matching pattern found + if (tripPattern == null) { + return List.of(); + } + + Instant midnight = ServiceDateUtils + .asStartOfService(serviceDate, transitService.getTimeZone()) + .toInstant(); + Timetable timetable = transitService.getTimetableForTripPattern(tripPattern, serviceDate); + if (timetable == null) { + return List.of(); + } + return TripTimeOnDate.fromTripTimes(timetable, trip, serviceDate, midnight); + }; + } + + @Override + public DataFetcher tripHeadsign() { + return environment -> + org.opentripplanner.framework.graphql.GraphQLUtils.getTranslation( + getSource(environment).trip().getHeadsign(), + environment + ); + } + + @Override + public DataFetcher tripShortName() { + return environment -> getSource(environment).trip().getShortName(); + } + + private List getStops(DataFetchingEnvironment environment) { + TripPattern tripPattern = getTripPattern(environment); + if (tripPattern == null) { + return List.of(); + } + return List.copyOf(tripPattern.getStops()); + } + + private TripPattern getTripPattern(DataFetchingEnvironment environment) { + return getTransitService(environment).getPatternForTrip(getSource(environment).trip()); + } + + private TransitService getTransitService(DataFetchingEnvironment environment) { + return environment.getContext().transitService(); + } + private DatedTrip 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 b23a3273518..5c11022cac2 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -317,11 +317,21 @@ public interface GraphQLCurrency { /** Trip on a specific date */ public interface GraphQLDatedTrip { - public DataFetcher date(); + public DataFetcher date(); public DataFetcher id(); - public DataFetcher trip(); + public DataFetcher pattern(); + + public DataFetcher route(); + + public DataFetcher> stops(); + + public DataFetcher> stoptimes(); + + public DataFetcher tripHeadsign(); + + public DataFetcher tripShortName(); } /** A connection to a list of dated trips */ 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 96212d00b39..acac4d61969 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -601,8 +601,8 @@ public void setGraphQLType(GraphQLCyclingOptimizationType type) { } /** - * Predefined optimization alternatives for bicycling routing. For more customization, one can use - * the triangle factors. + * Predefined optimization alternatives for bicycling routing. For more customization, + * one can use the triangle factors. */ public enum GraphQLCyclingOptimizationType { FLAT_STREETS, @@ -611,6 +611,25 @@ public enum GraphQLCyclingOptimizationType { SHORTEST_DURATION, } + public static class GraphQLDatedTripTripHeadsignArgs { + + private String language; + + public GraphQLDatedTripTripHeadsignArgs(Map args) { + if (args != null) { + this.language = (String) args.get("language"); + } + } + + public String getGraphQLLanguage() { + return this.language; + } + + public void setGraphQLLanguage(String language) { + this.language = language; + } + } + public static class GraphQLDepartureRowStoptimesArgs { private Integer numberOfDepartures; @@ -2007,8 +2026,8 @@ public enum GraphQLPropulsionType { } /** - * Additional qualifier for a transport mode. Note that qualifiers can only be used with certain - * transport modes. + * Additional qualifier for a transport mode. + * Note that qualifiers can only be used with certain transport modes. */ public enum GraphQLQualifier { ACCESS, @@ -4118,8 +4137,8 @@ public void setGraphQLType(GraphQLScooterOptimizationType type) { } /** - * Predefined optimization alternatives for scooter routing. For more customization, one can use - * the triangle factors. + * Predefined optimization alternatives for scooter routing. For more customization, + * one can use the triangle factors. */ public enum GraphQLScooterOptimizationType { FLAT_STREETS, @@ -4673,9 +4692,9 @@ public void setGraphQLSlack(java.time.Duration slack) { } /** - * Transit modes include modes that are used within organized transportation networks run by - * public transportation authorities, taxi companies etc. Equivalent to GTFS route_type or to - * NeTEx TransportMode. + * Transit modes include modes that are used within organized transportation networks + * run by public transportation authorities, taxi companies etc. + * Equivalent to GTFS route_type or to NeTEx TransportMode. */ public enum GraphQLTransitMode { AIRPLANE, @@ -5076,8 +5095,8 @@ public void setGraphQLUnpreferredCost(Integer unpreferredCost) { } /** - * The state of the vehicle parking. TEMPORARILY_CLOSED and CLOSED are distinct states so that - * they may be represented differently to the user. + * The state of the vehicle parking. TEMPORARILY_CLOSED and CLOSED are distinct states so that they + * may be represented differently to the user. */ public enum GraphQLVehicleParkingState { CLOSED, diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java b/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java index 0d544bb0c72..2227421ed0e 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java @@ -3,6 +3,6 @@ import java.time.LocalDate; /** - * Class which represents a trin on a specific date + * Class which represents a trip on a specific date */ public record DatedTrip(Trip trip, LocalDate serviceDate) {} diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 6725126fe9d..5dbbc397b29 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -386,9 +386,33 @@ type Currency { "Trip on a specific date" type DatedTrip implements Node { - date: String! + """ + The service date when the trip occurs. + + **Note**: A service date is a technical term useful for transit planning purposes and might not + correspond to a how a passenger thinks of a calendar date. For example, a night bus running + on Sunday morning at 1am to 3am, might have the previous Saturday's service date. + """ + date: LocalDate! + "Global object ID provided by Relay. This value can be used to refetch this object using **node** query." id: ID! - trip: Trip! + "The pattern the trip is running on" + pattern: Pattern + "The route the trip is running on" + route: Route! + "List of stops this trip passes through" + stops: [Stop!]! + "List of times when this trip arrives to or departs from a stop" + stoptimes: [Stoptime] + "Headsign of the vehicle when running on this trip" + tripHeadsign( + """ + If a translated headsign is found from GTFS translation.txt and wanted language is not same as + feed's language then returns wanted translation. Otherwise uses name from trip_headsign.txt. + """ + language: String + ): String + tripShortName: String } "A connection to a list of dated trips" From 2a9df2aaff1a4789b8fd2c1a190bfb7d65020a8c Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Thu, 8 Aug 2024 11:31:48 +0300 Subject: [PATCH 010/162] Improve documentation and mark cancelledTripTimes as deprecated --- .../gtfs/generated/GraphQLDataFetchers.java | 10 ++++-- .../opentripplanner/apis/gtfs/schema.graphqls | 35 ++++++++++++++++--- 2 files changed, 38 insertions(+), 7 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 5c11022cac2..2239c61d6cb 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -334,14 +334,20 @@ public interface GraphQLDatedTrip { public DataFetcher tripShortName(); } - /** A connection to a list of dated trips */ + /** + * A connection to a list of dated trips that follows + * [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + */ public interface GraphQLDatedTripConnection { public DataFetcher>> edges(); public DataFetcher pageInfo(); } - /** An edge for DatedTrip connection */ + /** + * An edge for DatedTrip connection. Part of the + * [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + */ public interface GraphQLDatedTripEdge { public DataFetcher cursor(); diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 5dbbc397b29..7072ef4b376 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -388,7 +388,7 @@ type Currency { type DatedTrip implements Node { """ The service date when the trip occurs. - + **Note**: A service date is a technical term useful for transit planning purposes and might not correspond to a how a passenger thinks of a calendar date. For example, a night bus running on Sunday morning at 1am to 3am, might have the previous Saturday's service date. @@ -415,15 +415,37 @@ type DatedTrip implements Node { tripShortName: String } -"A connection to a list of dated trips" +""" +A connection to a list of dated trips that follows +[GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). +""" type DatedTripConnection { + """ + Edges which contain the trips. Part of the + [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ edges: [DatedTripEdge] + """ + Contains cursors to fetch more pages of trips. + Part of the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ pageInfo: PageInfo! } -"An edge for DatedTrip connection" +""" +An edge for DatedTrip connection. Part of the +[GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). +""" type DatedTripEdge { + """ + The cursor of the edge. Part of the + [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ cursor: String! + """ + Dated trip as a node. Part of the + [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ node: DatedTrip } @@ -1170,8 +1192,11 @@ type QueryType { routes: [String], "Trip gtfsIds (e.g. [\"HSL:1098_20190405_Ma_2_1455\"])." trips: [String] - ): [Stoptime] - "Get cancelled Trips" + ): [Stoptime] @deprecated(reason : "`cancelledTripTimes` is deprecated. Use `cancelledTrips` instead.") + """ + Get pages of cancelled Trips. Follows the + [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ cancelledTrips( after: String, before: String, From b142bdf06d08eebb40f76d9ef043424bf24fe46e Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Thu, 8 Aug 2024 15:59:27 +0300 Subject: [PATCH 011/162] Add start and end to schema --- .../apis/gtfs/GraphQLUtils.java | 15 ++++ .../apis/gtfs/datafetchers/DatedTripImpl.java | 86 +++++++++++++++++-- .../apis/gtfs/datafetchers/StoptimeImpl.java | 25 ++---- .../gtfs/generated/GraphQLDataFetchers.java | 15 ++++ .../apis/gtfs/generated/graphql-codegen.yml | 1 + .../apis/gtfs/model/DatedTripTime.java | 31 +++++++ .../model/plan/ScheduledTransitLeg.java | 7 +- .../model/timetable/RealTimeTripTimes.java | 7 ++ .../model/timetable/ScheduledTripTimes.java | 5 ++ .../transit/model/timetable/TripTimes.java | 5 ++ .../opentripplanner/apis/gtfs/schema.graphqls | 15 ++++ 11 files changed, 182 insertions(+), 30 deletions(-) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/model/DatedTripTime.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java index 3fb339daa32..fe63add7d49 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java @@ -2,6 +2,7 @@ import java.time.Instant; import java.util.Locale; +import javax.annotation.Nullable; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFilterPlaceType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFormFactor; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; @@ -9,6 +10,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLWheelchairBoarding; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.model.StopTime; import org.opentripplanner.routing.api.response.InputField; import org.opentripplanner.routing.api.response.RoutingErrorCode; import org.opentripplanner.routing.graphfinder.PlaceType; @@ -109,4 +111,17 @@ public static boolean startsWith(String str, String name, Locale locale) { public static boolean startsWith(I18NString str, String name, Locale locale) { return str != null && str.toString(locale).toLowerCase(locale).startsWith(name); } + + /** + * Generally the missing values are removed during the graph build. However, for flex trips they + * are not and have to be converted to null here. + */ + @Nullable + public static Integer stopTimeToInt(int value) { + if (value == StopTime.MISSING_VALUE) { + return null; + } else { + return value; + } + } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java index f8bdf40dbc2..eb820fc06c5 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java @@ -1,13 +1,18 @@ package org.opentripplanner.apis.gtfs.datafetchers; +import static org.opentripplanner.apis.gtfs.GraphQLUtils.stopTimeToInt; + import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.time.Instant; import java.time.LocalDate; +import java.time.ZonedDateTime; import java.util.List; +import javax.annotation.Nullable; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.model.DatedTripTime; import org.opentripplanner.ext.restapi.mapping.LocalDateMapper; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.Timetable; @@ -16,6 +21,7 @@ import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.TransitService; public class DatedTripImpl implements GraphQLDataFetchers.GraphQLDatedTrip { @@ -25,6 +31,24 @@ public DataFetcher date() { return env -> getSource(env).serviceDate(); } + @Override + public DataFetcher end() { + return env -> { + var tripTimes = getTripTimes(env); + if (tripTimes == null) { + return null; + } + var stopIndex = tripTimes.getNumStops() - 1; + var scheduledTime = getZonedDateTime(env, tripTimes.getScheduledArrivalTime(stopIndex)); + if (scheduledTime == null) { + return null; + } + return tripTimes.isRealtimeUpdated(stopIndex) + ? DatedTripTime.of(scheduledTime, tripTimes.getArrivalDelay(stopIndex)) + : DatedTripTime.ofStatic(scheduledTime); + }; + } + @Override public DataFetcher id() { return env -> @@ -46,6 +70,23 @@ public DataFetcher route() { return environment -> getSource(environment).trip().getRoute(); } + @Override + public DataFetcher start() { + return env -> { + var tripTimes = getTripTimes(env); + if (tripTimes == null) { + return null; + } + var scheduledTime = getZonedDateTime(env, tripTimes.getScheduledDepartureTime(0)); + if (scheduledTime == null) { + return null; + } + return tripTimes.isRealtimeUpdated(0) + ? DatedTripTime.of(scheduledTime, tripTimes.getDepartureDelay(0)) + : DatedTripTime.ofStatic(scheduledTime); + }; + } + @Override public DataFetcher> stops() { return this::getStops; @@ -56,18 +97,12 @@ public DataFetcher> stoptimes() { return environment -> { TransitService transitService = getTransitService(environment); Trip trip = getSource(environment).trip(); - var serviceDate = getSource(environment).serviceDate(); - TripPattern tripPattern = transitService.getPatternForTrip(trip, serviceDate); - // no matching pattern found - if (tripPattern == null) { - return List.of(); - } Instant midnight = ServiceDateUtils .asStartOfService(serviceDate, transitService.getTimeZone()) .toInstant(); - Timetable timetable = transitService.getTimetableForTripPattern(tripPattern, serviceDate); + Timetable timetable = getTimetable(environment, trip, serviceDate); if (timetable == null) { return List.of(); } @@ -101,6 +136,43 @@ private TripPattern getTripPattern(DataFetchingEnvironment environment) { return getTransitService(environment).getPatternForTrip(getSource(environment).trip()); } + @Nullable + private Timetable getTimetable( + DataFetchingEnvironment environment, + Trip trip, + LocalDate serviceDate + ) { + TransitService transitService = getTransitService(environment); + TripPattern tripPattern = transitService.getPatternForTrip(trip, serviceDate); + // no matching pattern found + if (tripPattern == null) { + return null; + } + + return transitService.getTimetableForTripPattern(tripPattern, serviceDate); + } + + @Nullable + private TripTimes getTripTimes(DataFetchingEnvironment environment) { + Trip trip = getSource(environment).trip(); + var serviceDate = getSource(environment).serviceDate(); + var timetable = getTimetable(environment, trip, serviceDate); + if (timetable == null) { + return null; + } + return timetable.getTripTimes(trip); + } + + private ZonedDateTime getZonedDateTime(DataFetchingEnvironment environment, int time) { + var fixedTime = stopTimeToInt(time); + if (fixedTime == null) { + return null; + } + var serviceDate = getSource(environment).serviceDate(); + TransitService transitService = getTransitService(environment); + return ServiceDateUtils.toZonedDateTime(serviceDate, transitService.getTimeZone(), fixedTime); + } + private TransitService getTransitService(DataFetchingEnvironment environment) { return environment.getContext().transitService(); } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java index faf59ef9d6e..706c5a4fd5e 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java @@ -1,10 +1,11 @@ package org.opentripplanner.apis.gtfs.datafetchers; +import static org.opentripplanner.apis.gtfs.GraphQLUtils.stopTimeToInt; + import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.framework.graphql.GraphQLUtils; -import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.Trip; @@ -13,7 +14,7 @@ public class StoptimeImpl implements GraphQLDataFetchers.GraphQLStoptime { @Override public DataFetcher arrivalDelay() { - return environment -> missingValueToNull(getSource(environment).getArrivalDelay()); + return environment -> stopTimeToInt(getSource(environment).getArrivalDelay()); } @Override @@ -58,12 +59,12 @@ public DataFetcher realtime() { @Override public DataFetcher realtimeArrival() { - return environment -> missingValueToNull(getSource(environment).getRealtimeArrival()); + return environment -> stopTimeToInt(getSource(environment).getRealtimeArrival()); } @Override public DataFetcher realtimeDeparture() { - return environment -> missingValueToNull(getSource(environment).getRealtimeDeparture()); + return environment -> stopTimeToInt(getSource(environment).getRealtimeDeparture()); } @Override @@ -76,12 +77,12 @@ public DataFetcher realtimeState() { @Override public DataFetcher scheduledArrival() { - return environment -> missingValueToNull(getSource(environment).getScheduledArrival()); + return environment -> stopTimeToInt(getSource(environment).getScheduledArrival()); } @Override public DataFetcher scheduledDeparture() { - return environment -> missingValueToNull(getSource(environment).getScheduledDeparture()); + return environment -> stopTimeToInt(getSource(environment).getScheduledDeparture()); } @Override @@ -112,16 +113,4 @@ public DataFetcher trip() { private TripTimeOnDate getSource(DataFetchingEnvironment environment) { return environment.getSource(); } - - /** - * Generally the missing values are removed during the graph build. However, for flex trips they - * are not and have to be converted to null here. - */ - private Integer missingValueToNull(int value) { - if (value == StopTime.MISSING_VALUE) { - return null; - } else { - return value; - } - } } 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 2239c61d6cb..259d6db86b0 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -21,6 +21,7 @@ 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.DatedTripTime; import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.model.PlanPageInfo; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; @@ -319,12 +320,16 @@ public interface GraphQLCurrency { public interface GraphQLDatedTrip { public DataFetcher date(); + public DataFetcher end(); + public DataFetcher id(); public DataFetcher pattern(); public DataFetcher route(); + public DataFetcher start(); + public DataFetcher> stops(); public DataFetcher> stoptimes(); @@ -354,6 +359,16 @@ public interface GraphQLDatedTripEdge { public DataFetcher node(); } + /** + * Information about a dated trip's start or end times. May contain real-time information if + * available. + */ + public interface GraphQLDatedTripTime { + public DataFetcher estimated(); + + public DataFetcher scheduledTime(); + } + /** * The standard case of a fare product: it only has a single price to be paid by the passenger * and no discounts are applied. 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 465321a58e3..6eb98a7f95b 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 @@ -62,6 +62,7 @@ config: DatedTrip: org.opentripplanner.transit.model.timetable.DatedTrip#DatedTrip DatedTripConnection: graphql.relay.Connection#Connection DatedTripEdge: graphql.relay.Edge#Edge + DatedTripTime: org.opentripplanner.apis.gtfs.model.DatedTripTime#DatedTripTime debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/DatedTripTime.java b/src/main/java/org/opentripplanner/apis/gtfs/model/DatedTripTime.java new file mode 100644 index 00000000000..a83eb50fe1d --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/DatedTripTime.java @@ -0,0 +1,31 @@ +package org.opentripplanner.apis.gtfs.model; + +import java.time.Duration; +import java.time.ZonedDateTime; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A scheduled time of a trip's start or end with an optional realtime information. + */ +public record DatedTripTime( + @Nonnull ZonedDateTime scheduledTime, + @Nullable RealTimeEstimate estimated +) { + @Nonnull + public static DatedTripTime of(ZonedDateTime realtime, int delaySecs) { + var delay = Duration.ofSeconds(delaySecs); + return new DatedTripTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay)); + } + + @Nonnull + public static DatedTripTime ofStatic(ZonedDateTime staticTime) { + return new DatedTripTime(staticTime, null); + } + + /** + * Realtime information about a vehicle at a certain place. + * @param delay Delay or "earliness" of a vehicle. Earliness is expressed as a negative number. + */ + record RealTimeEstimate(ZonedDateTime time, Duration delay) {} +} diff --git a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java index d94ec1895c2..4a84ac6e799 100644 --- a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java +++ b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java @@ -219,11 +219,8 @@ public int getArrivalDelay() { @Override public boolean getRealTime() { return ( - !tripTimes.isScheduled() && - ( - !tripTimes.isNoDataStop(boardStopPosInPattern) || - !tripTimes.isNoDataStop(alightStopPosInPattern) - ) + tripTimes.isRealtimeUpdated(boardStopPosInPattern) || + tripTimes.isRealtimeUpdated(alightStopPosInPattern) ); } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java index 711eb1c221c..ca5897b7b24 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java @@ -198,6 +198,13 @@ public boolean isPredictionInaccurate(int stop) { return isStopRealTimeStates(stop, StopRealTimeState.INACCURATE_PREDICTIONS); } + public boolean isRealtimeUpdated(int stop) { + return ( + realTimeState != RealTimeState.SCHEDULED && + !isStopRealTimeStates(stop, StopRealTimeState.NO_DATA) + ); + } + public void setOccupancyStatus(int stop, OccupancyStatus occupancyStatus) { prepareForRealTimeUpdates(); this.occupancyStatus[stop] = occupancyStatus; diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index d367932d24d..3799fd7b140 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -234,6 +234,11 @@ public boolean isPredictionInaccurate(int stop) { return false; } + @Override + public boolean isRealtimeUpdated(int stop) { + return false; + } + @Override public I18NString getTripHeadsign() { return trip.getHeadsign(); diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java index e5cd1f1ff28..ea1a3f8ab2b 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java @@ -123,6 +123,11 @@ default int compareTo(TripTimes other) { boolean isPredictionInaccurate(int stop); + /** + * Return if trip has been updated and stop has not been given a NO_DATA update. + */ + boolean isRealtimeUpdated(int stop); + /** * @return the whole trip's headsign. Individual stops can have different headsigns. */ diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 7072ef4b376..fb287c5c117 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -394,12 +394,16 @@ type DatedTrip implements Node { on Sunday morning at 1am to 3am, might have the previous Saturday's service date. """ date: LocalDate! + "The time when the trip ends including real-time information, if available." + end: DatedTripTime "Global object ID provided by Relay. This value can be used to refetch this object using **node** query." id: ID! "The pattern the trip is running on" pattern: Pattern "The route the trip is running on" route: Route! + "The time when the trip starts including real-time information, if available." + start: DatedTripTime "List of stops this trip passes through" stops: [Stop!]! "List of times when this trip arrives to or departs from a stop" @@ -449,6 +453,17 @@ type DatedTripEdge { node: DatedTrip } +""" +Information about a dated trip's start or end times. May contain real-time information if +available. +""" +type DatedTripTime { + "The estimated time of the event. If no real-time information is available, this is null." + estimated: RealTimeEstimate + "The scheduled time of the event." + scheduledTime: OffsetDateTime +} + """ The standard case of a fare product: it only has a single price to be paid by the passenger and no discounts are applied. From 8e12d3a626c5d7c685be9c89c3f1977eece7c9d4 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 9 Aug 2024 16:08:11 +0300 Subject: [PATCH 012/162] Create DatedStopTime type and use it in DatedTrip --- .../apis/gtfs/GtfsGraphQLIndex.java | 2 + .../gtfs/datafetchers/DatedStopTimeImpl.java | 117 ++++++++++++++++++ .../apis/gtfs/datafetchers/DatedTripImpl.java | 14 +-- .../apis/gtfs/datafetchers/StoptimeImpl.java | 19 +-- .../gtfs/generated/GraphQLDataFetchers.java | 48 +++++-- .../apis/gtfs/generated/GraphQLTypes.java | 29 +++++ .../apis/gtfs/generated/graphql-codegen.yml | 4 +- .../apis/gtfs/mapping/PickDropMapper.java | 18 +++ ...ripTime.java => ArrivalDepartureTime.java} | 13 +- .../opentripplanner/model/TripTimeOnDate.java | 8 ++ .../opentripplanner/apis/gtfs/schema.graphqls | 96 +++++++++++--- 11 files changed, 311 insertions(+), 57 deletions(-) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeImpl.java create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/mapping/PickDropMapper.java rename src/main/java/org/opentripplanner/apis/gtfs/model/{DatedTripTime.java => ArrivalDepartureTime.java} (56%) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 163b32de751..a160e197611 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -35,6 +35,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.CoordinatesImpl; import org.opentripplanner.apis.gtfs.datafetchers.CurrencyImpl; +import org.opentripplanner.apis.gtfs.datafetchers.DatedStopTimeImpl; import org.opentripplanner.apis.gtfs.datafetchers.DatedTripImpl; import org.opentripplanner.apis.gtfs.datafetchers.DefaultFareProductImpl; import org.opentripplanner.apis.gtfs.datafetchers.DepartureRowImpl; @@ -180,6 +181,7 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(FareProductUseImpl.class)) .type(typeWiring.build(DefaultFareProductImpl.class)) .type(typeWiring.build(DatedTripImpl.class)) + .type(typeWiring.build(DatedStopTimeImpl.class)) .type(typeWiring.build(TripOccupancyImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeImpl.java new file mode 100644 index 00000000000..3ed12ac4fb6 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeImpl.java @@ -0,0 +1,117 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import static org.opentripplanner.apis.gtfs.GraphQLUtils.stopTimeToInt; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.time.ZonedDateTime; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.mapping.PickDropMapper; +import org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime; +import org.opentripplanner.framework.graphql.GraphQLUtils; +import org.opentripplanner.framework.time.ServiceDateUtils; +import org.opentripplanner.model.TripTimeOnDate; +import org.opentripplanner.transit.service.TransitService; + +public class DatedStopTimeImpl implements GraphQLDataFetchers.GraphQLDatedStopTime { + + @Override + public DataFetcher arrival() { + return environment -> { + var tripTime = getSource(environment); + var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledArrival()); + if (scheduledTime == null) { + return null; + } + return tripTime.isRealtime() + ? ArrivalDepartureTime.of(scheduledTime, tripTime.getArrivalDelay()) + : ArrivalDepartureTime.ofStatic(scheduledTime); + }; + } + + @Override + public DataFetcher departure() { + return environment -> { + var tripTime = getSource(environment); + var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledDeparture()); + if (scheduledTime == null) { + return null; + } + return tripTime.isRealtime() + ? ArrivalDepartureTime.of(scheduledTime, tripTime.getDepartureDelay()) + : ArrivalDepartureTime.ofStatic(scheduledTime); + }; + } + + @Override + public DataFetcher dropoffType() { + return environment -> PickDropMapper.map(getSource(environment).getDropoffType()); + } + + @Override + public DataFetcher headsign() { + return environment -> + GraphQLUtils.getTranslation(getSource(environment).getHeadsign(), environment); + } + + @Override + public DataFetcher pickupType() { + return environment -> PickDropMapper.map(getSource(environment).getPickupType()); + } + + @Override + public DataFetcher realtimeState() { + return environment -> { + var tripTime = getSource(environment); + // TODO support ADDED state + if (tripTime.isCanceledEffectively()) { + return GraphQLTypes.GraphQLStopRealTimeState.CANCELED; + } + if (tripTime.isNoDataStop()) { + return GraphQLTypes.GraphQLStopRealTimeState.NO_DATA; + } + if (tripTime.isRecordedStop()) { + return GraphQLTypes.GraphQLStopRealTimeState.RECORDED; + } + if (tripTime.isRealtime()) { + return GraphQLTypes.GraphQLStopRealTimeState.UPDATED; + } + return GraphQLTypes.GraphQLStopRealTimeState.UNUPDATED; + }; + } + + @Override + public DataFetcher stopPosition() { + return environment -> getSource(environment).getGtfsSequence(); + } + + @Override + public DataFetcher stop() { + return environment -> getSource(environment).getStop(); + } + + @Override + public DataFetcher timepoint() { + return environment -> getSource(environment).isTimepoint(); + } + + private TransitService getTransitService(DataFetchingEnvironment environment) { + return environment.getContext().transitService(); + } + + private ZonedDateTime getZonedDateTime(DataFetchingEnvironment environment, int time) { + var fixedTime = stopTimeToInt(time); + if (fixedTime == null) { + return null; + } + var serviceDate = getSource(environment).getServiceDay(); + TransitService transitService = getTransitService(environment); + return ServiceDateUtils.toZonedDateTime(serviceDate, transitService.getTimeZone(), fixedTime); + } + + private TripTimeOnDate getSource(DataFetchingEnvironment environment) { + return environment.getSource(); + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java index eb820fc06c5..5d567d83264 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java @@ -12,7 +12,7 @@ import javax.annotation.Nullable; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; -import org.opentripplanner.apis.gtfs.model.DatedTripTime; +import org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime; import org.opentripplanner.ext.restapi.mapping.LocalDateMapper; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.Timetable; @@ -32,7 +32,7 @@ public DataFetcher date() { } @Override - public DataFetcher end() { + public DataFetcher end() { return env -> { var tripTimes = getTripTimes(env); if (tripTimes == null) { @@ -44,8 +44,8 @@ public DataFetcher end() { return null; } return tripTimes.isRealtimeUpdated(stopIndex) - ? DatedTripTime.of(scheduledTime, tripTimes.getArrivalDelay(stopIndex)) - : DatedTripTime.ofStatic(scheduledTime); + ? ArrivalDepartureTime.of(scheduledTime, tripTimes.getArrivalDelay(stopIndex)) + : ArrivalDepartureTime.ofStatic(scheduledTime); }; } @@ -71,7 +71,7 @@ public DataFetcher route() { } @Override - public DataFetcher start() { + public DataFetcher start() { return env -> { var tripTimes = getTripTimes(env); if (tripTimes == null) { @@ -82,8 +82,8 @@ public DataFetcher start() { return null; } return tripTimes.isRealtimeUpdated(0) - ? DatedTripTime.of(scheduledTime, tripTimes.getDepartureDelay(0)) - : DatedTripTime.ofStatic(scheduledTime); + ? ArrivalDepartureTime.of(scheduledTime, tripTimes.getDepartureDelay(0)) + : ArrivalDepartureTime.ofStatic(scheduledTime); }; } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java index 706c5a4fd5e..2a433fadee0 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java @@ -5,6 +5,7 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.mapping.PickDropMapper; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.transit.model.timetable.RealTimeState; @@ -24,14 +25,7 @@ public DataFetcher departureDelay() { @Override public DataFetcher dropoffType() { - return environment -> - switch (getSource(environment).getDropoffType()) { - case SCHEDULED -> "SCHEDULED"; - case NONE -> "NONE"; - case CALL_AGENCY -> "CALL_AGENCY"; - case COORDINATE_WITH_DRIVER -> "COORDINATE_WITH_DRIVER"; - case CANCELLED -> null; - }; + return environment -> PickDropMapper.map(getSource(environment).getDropoffType()); } @Override @@ -42,14 +36,7 @@ public DataFetcher headsign() { @Override public DataFetcher pickupType() { - return environment -> - switch (getSource(environment).getPickupType()) { - case SCHEDULED -> "SCHEDULED"; - case NONE -> "NONE"; - case CALL_AGENCY -> "CALL_AGENCY"; - case COORDINATE_WITH_DRIVER -> "COORDINATE_WITH_DRIVER"; - case CANCELLED -> null; - }; + return environment -> PickDropMapper.map(getSource(environment).getPickupType()); } @Override 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 259d6db86b0..ff12746df68 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -20,8 +20,9 @@ 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.GraphQLStopRealTimeState; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; -import org.opentripplanner.apis.gtfs.model.DatedTripTime; +import org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime; import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.model.PlanPageInfo; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; @@ -141,6 +142,16 @@ public interface GraphQLAlert { /** Entity related to an alert */ public interface GraphQLAlertEntity extends TypeResolver {} + /** + * Timing of an arrival or a departure to or from a stop. May contain real-time information if + * available. + */ + public interface GraphQLArrivalDepartureTime { + public DataFetcher estimated(); + + public DataFetcher scheduledTime(); + } + /** Bike park represents a location where bicycles can be parked. */ public interface GraphQLBikePark { public DataFetcher bikeParkId(); @@ -316,11 +327,32 @@ public interface GraphQLCurrency { public DataFetcher digits(); } + /** Stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. */ + public interface GraphQLDatedStopTime { + public DataFetcher arrival(); + + public DataFetcher departure(); + + public DataFetcher dropoffType(); + + public DataFetcher headsign(); + + public DataFetcher pickupType(); + + public DataFetcher realtimeState(); + + public DataFetcher stop(); + + public DataFetcher stopPosition(); + + public DataFetcher timepoint(); + } + /** Trip on a specific date */ public interface GraphQLDatedTrip { public DataFetcher date(); - public DataFetcher end(); + public DataFetcher end(); public DataFetcher id(); @@ -328,7 +360,7 @@ public interface GraphQLDatedTrip { public DataFetcher route(); - public DataFetcher start(); + public DataFetcher start(); public DataFetcher> stops(); @@ -359,16 +391,6 @@ public interface GraphQLDatedTripEdge { public DataFetcher node(); } - /** - * Information about a dated trip's start or end times. May contain real-time information if - * available. - */ - public interface GraphQLDatedTripTime { - public DataFetcher estimated(); - - public DataFetcher scheduledTime(); - } - /** * The standard case of a fare product: it only has a single price to be paid by the passenger * and no discounts are applied. 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 acac4d61969..3d9b627154a 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -611,6 +611,25 @@ public enum GraphQLCyclingOptimizationType { SHORTEST_DURATION, } + public static class GraphQLDatedStopTimeHeadsignArgs { + + private String language; + + public GraphQLDatedStopTimeHeadsignArgs(Map args) { + if (args != null) { + this.language = (String) args.get("language"); + } + } + + public String getGraphQLLanguage() { + return this.language; + } + + public void setGraphQLLanguage(String language) { + this.language = language; + } + } + public static class GraphQLDatedTripTripHeadsignArgs { private String language; @@ -4584,6 +4603,16 @@ public enum GraphQLStopAlertType { TRIPS, } + /** Whether stop has been updated through a realtime update and if so, how. */ + public enum GraphQLStopRealTimeState { + ADDED, + CANCELED, + NO_DATA, + RECORDED, + UNUPDATED, + UPDATED, + } + public static class GraphQLStoptimeHeadsignArgs { private String language; 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 6eb98a7f95b..1d5ebd31a4f 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 @@ -59,10 +59,11 @@ config: ContactInfo: org.opentripplanner.transit.model.organization.ContactInfo Cluster: Object Coordinates: org.locationtech.jts.geom.Coordinate#Coordinate + DatedStopTime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate DatedTrip: org.opentripplanner.transit.model.timetable.DatedTrip#DatedTrip DatedTripConnection: graphql.relay.Connection#Connection DatedTripEdge: graphql.relay.Edge#Edge - DatedTripTime: org.opentripplanner.apis.gtfs.model.DatedTripTime#DatedTripTime + ArrivalDepartureTime: org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime#ArrivalDepartureTime debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step @@ -101,6 +102,7 @@ config: stopAtDistanceEdge: graphql.relay.Edge#Edge StopOnRoute: org.opentripplanner.apis.gtfs.model.StopOnRouteModel#StopOnRouteModel StopOnTrip: org.opentripplanner.apis.gtfs.model.StopOnTripModel#StopOnTripModel + StopRealTimeState: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLStopRealTimeState#GraphQLStopRealTimeState Stoptime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate StoptimesInPattern: org.opentripplanner.model.StopTimesInPattern#StopTimesInPattern TicketType: org.opentripplanner.ext.fares.model.FareRuleSet#FareRuleSet diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/PickDropMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/PickDropMapper.java new file mode 100644 index 00000000000..c8e4d212999 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/PickDropMapper.java @@ -0,0 +1,18 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import javax.annotation.Nullable; +import org.opentripplanner.model.PickDrop; + +public final class PickDropMapper { + + @Nullable + public static String map(PickDrop pickDrop) { + return switch (pickDrop) { + case SCHEDULED -> "SCHEDULED"; + case NONE -> "NONE"; + case CALL_AGENCY -> "CALL_AGENCY"; + case COORDINATE_WITH_DRIVER -> "COORDINATE_WITH_DRIVER"; + case CANCELLED -> null; + }; + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/DatedTripTime.java b/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java similarity index 56% rename from src/main/java/org/opentripplanner/apis/gtfs/model/DatedTripTime.java rename to src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java index a83eb50fe1d..529d83c7459 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/model/DatedTripTime.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java @@ -6,21 +6,22 @@ import javax.annotation.Nullable; /** - * A scheduled time of a trip's start or end with an optional realtime information. + * Timing of an arrival or a departure to or from a stop. May contain real-time information + * if available. */ -public record DatedTripTime( +public record ArrivalDepartureTime( @Nonnull ZonedDateTime scheduledTime, @Nullable RealTimeEstimate estimated ) { @Nonnull - public static DatedTripTime of(ZonedDateTime realtime, int delaySecs) { + public static ArrivalDepartureTime of(ZonedDateTime realtime, int delaySecs) { var delay = Duration.ofSeconds(delaySecs); - return new DatedTripTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay)); + return new ArrivalDepartureTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay)); } @Nonnull - public static DatedTripTime ofStatic(ZonedDateTime staticTime) { - return new DatedTripTime(staticTime, null); + public static ArrivalDepartureTime ofStatic(ZonedDateTime staticTime) { + return new ArrivalDepartureTime(staticTime, null); } /** diff --git a/src/main/java/org/opentripplanner/model/TripTimeOnDate.java b/src/main/java/org/opentripplanner/model/TripTimeOnDate.java index 1bfb0184138..8128c36e4db 100644 --- a/src/main/java/org/opentripplanner/model/TripTimeOnDate.java +++ b/src/main/java/org/opentripplanner/model/TripTimeOnDate.java @@ -190,6 +190,14 @@ public boolean isNoDataStop() { return tripTimes.isNoDataStop(stopIndex); } + /** + * Is the real-time time a recorded time (i.e. has the vehicle already passed the stop). + * This information is currently only available from SIRI feeds. + */ + public boolean isRecordedStop() { + return tripTimes.isRecordedStop(stopIndex); + } + public RealTimeState getRealTimeState() { return tripTimes.isNoDataStop(stopIndex) ? RealTimeState.SCHEDULED diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index fb287c5c117..dcef5f67096 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -154,6 +154,17 @@ type Alert implements Node { trip: Trip @deprecated(reason : "Alert can have multiple affected entities now instead of there being duplicate alerts\nfor different entities. This will return only one of the affected trips.\nUse entities instead.") } +""" +Timing of an arrival or a departure to or from a stop. May contain real-time information if +available. +""" +type ArrivalDepartureTime { + "The estimated time of the event. If no real-time information is available, this is null." + estimated: RealTimeEstimate + "The scheduled time of the event." + scheduledTime: OffsetDateTime +} + "Bike park represents a location where bicycles can be parked." type BikePark implements Node & PlaceInterface { "ID of the bike park" @@ -384,6 +395,55 @@ type Currency { digits: Int! } +"Stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop." +type DatedStopTime { + "Scheduled arrival time to the stop and a realtime estimate, if one exists." + arrival: ArrivalDepartureTime + "Scheduled departure time from the stop and a realtime estimate, if one exists." + departure: ArrivalDepartureTime + """ + Whether the vehicle can be disembarked at this stop. This field can also be + used to indicate if disembarkation is possible only with special arrangements. + """ + dropoffType: PickupDropoffType + """ + Vehicle headsign of the trip on this stop. Trip headsigns can change during + the trip (e.g. on routes which run on loops), so this value should be used + instead of `tripHeadsign` to display the headsign relevant to the user. + """ + headsign( + """ + If translated headsign is found from gtfs translation.txt and wanted language is not same as + feed's language then returns wanted translation. Otherwise uses name from trip_headsign.txt. + """ + language: String + ): String + """ + Whether the vehicle can be boarded at this stop. This field can also be used + to indicate if boarding is possible only with special arrangements. + """ + pickupType: PickupDropoffType + "Whether stop has been updated through a realtime update and if so, how." + realtimeState: StopRealTimeState! + "The stop where this arrival/departure happens" + stop: Stop + """ + The sequence of the stop in the pattern. This is not required to start from 0 or be consecutive - any + increasing integer sequence along the stops is valid. + + The purpose of this field is to identify the stop within the pattern so it can be cross-referenced + between it and the itinerary. It is safe to cross-reference when done quickly, i.e. within seconds. + However, it should be noted that real-time updates can change the values, so don't store it for + longer amounts of time. + + Depending on the source data, this might not be the GTFS `stop_sequence` but another value, perhaps + even generated. + """ + stopPosition: Int + "true, if this stop is used as a time equalization stop. false otherwise." + timepoint: Boolean +} + "Trip on a specific date" type DatedTrip implements Node { """ @@ -395,7 +455,7 @@ type DatedTrip implements Node { """ date: LocalDate! "The time when the trip ends including real-time information, if available." - end: DatedTripTime + end: ArrivalDepartureTime "Global object ID provided by Relay. This value can be used to refetch this object using **node** query." id: ID! "The pattern the trip is running on" @@ -403,11 +463,11 @@ type DatedTrip implements Node { "The route the trip is running on" route: Route! "The time when the trip starts including real-time information, if available." - start: DatedTripTime + start: ArrivalDepartureTime "List of stops this trip passes through" stops: [Stop!]! "List of times when this trip arrives to or departs from a stop" - stoptimes: [Stoptime] + stoptimes: [DatedStopTime] "Headsign of the vehicle when running on this trip" tripHeadsign( """ @@ -453,17 +513,6 @@ type DatedTripEdge { node: DatedTrip } -""" -Information about a dated trip's start or end times. May contain real-time information if -available. -""" -type DatedTripTime { - "The estimated time of the event. If no real-time information is available, this is null." - estimated: RealTimeEstimate - "The scheduled time of the event." - scheduledTime: OffsetDateTime -} - """ The standard case of a fare product: it only has a single price to be paid by the passenger and no discounts are applied. @@ -3517,6 +3566,25 @@ enum StopAlertType { TRIPS } +"Whether stop has been updated through a realtime update and if so, how." +enum StopRealTimeState { + "The stop has been added through a realtime update." + ADDED + "The stop has been cancelled through a realtime update." + CANCELED + "The realtime feed has indicated that there is no data available for this stop." + NO_DATA + """ + The vehicle has arrived to the stop or already visited it and the times are no longer estimates. + Note, not all realtime feeds indicate this information even if the vehicle has already passed the stop. + """ + RECORDED + "There have been no realtime updates." + UNUPDATED + "The trip's arrival and/or departure time has been updated." + UPDATED +} + """ Transit modes include modes that are used within organized transportation networks run by public transportation authorities, taxi companies etc. From 53ff5d56d991bb3ea39998f454d0c12abaab772e Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 9 Aug 2024 16:40:24 +0300 Subject: [PATCH 013/162] cancelled -> canceled --- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 6 +- .../gtfs/generated/GraphQLDataFetchers.java | 4 +- .../apis/gtfs/generated/GraphQLTypes.java | 118 +++++++++--------- .../service/DefaultTransitService.java | 12 +- .../transit/service/TransitService.java | 7 +- .../opentripplanner/apis/gtfs/schema.graphqls | 54 ++++---- 6 files changed, 103 insertions(+), 98 deletions(-) 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 7cac038a165..4d893802d02 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -814,11 +814,11 @@ public DataFetcher> trips() { } @Override - public DataFetcher> cancelledTrips() { + public DataFetcher> canceledTrips() { return environment -> { - var args = new GraphQLTypes.GraphQLQueryTypeCancelledTripsArgs(environment.getArguments()); + var args = new GraphQLTypes.GraphQLQueryTypeCanceledTripsArgs(environment.getArguments()); var datedTrips = getTransitService(environment) - .getCancelledTrips(args.getGraphQLFeeds()) + .getCanceledTrips(args.getGraphQLFeeds()) .stream() .collect(Collectors.toList()); return new SimpleListConnection<>(datedTrips).get(environment); 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 ff12746df68..3f6f7df22a0 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -840,9 +840,9 @@ public interface GraphQLQueryType { public DataFetcher> bikeRentalStations(); - public DataFetcher> cancelledTripTimes(); + public DataFetcher> canceledTrips(); - public DataFetcher> cancelledTrips(); + public DataFetcher> cancelledTripTimes(); public DataFetcher carPark(); 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 3d9b627154a..70e98ab17a0 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -2236,6 +2236,65 @@ public void setGraphQLIds(List ids) { } } + public static class GraphQLQueryTypeCanceledTripsArgs { + + private String after; + private String before; + private List feeds; + private Integer first; + private Integer last; + + public GraphQLQueryTypeCanceledTripsArgs(Map args) { + if (args != null) { + this.after = (String) args.get("after"); + this.before = (String) args.get("before"); + this.feeds = (List) args.get("feeds"); + this.first = (Integer) args.get("first"); + this.last = (Integer) args.get("last"); + } + } + + public String getGraphQLAfter() { + return this.after; + } + + public String getGraphQLBefore() { + return this.before; + } + + public List getGraphQLFeeds() { + return this.feeds; + } + + public Integer getGraphQLFirst() { + return this.first; + } + + public Integer getGraphQLLast() { + return this.last; + } + + public void setGraphQLAfter(String after) { + this.after = after; + } + + public void setGraphQLBefore(String before) { + this.before = before; + } + + public void setGraphQLFeeds(List feeds) { + this.feeds = feeds; + } + + public void setGraphQLFirst(Integer first) { + this.first = first; + } + + public void setGraphQLLast(Integer last) { + this.last = last; + } + } + public static class GraphQLQueryTypeCancelledTripTimesArgs { private List feeds; @@ -2345,65 +2404,6 @@ public void setGraphQLTrips(List trips) { } } - public static class GraphQLQueryTypeCancelledTripsArgs { - - private String after; - private String before; - private List feeds; - private Integer first; - private Integer last; - - public GraphQLQueryTypeCancelledTripsArgs(Map args) { - if (args != null) { - this.after = (String) args.get("after"); - this.before = (String) args.get("before"); - this.feeds = (List) args.get("feeds"); - this.first = (Integer) args.get("first"); - this.last = (Integer) args.get("last"); - } - } - - public String getGraphQLAfter() { - return this.after; - } - - public String getGraphQLBefore() { - return this.before; - } - - public List getGraphQLFeeds() { - return this.feeds; - } - - public Integer getGraphQLFirst() { - return this.first; - } - - public Integer getGraphQLLast() { - return this.last; - } - - public void setGraphQLAfter(String after) { - this.after = after; - } - - public void setGraphQLBefore(String before) { - this.before = before; - } - - public void setGraphQLFeeds(List feeds) { - this.feeds = feeds; - } - - public void setGraphQLFirst(Integer first) { - this.first = first; - } - - public void setGraphQLLast(Integer last) { - this.last = last; - } - } - public static class GraphQLQueryTypeCarParkArgs { private String id; diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 4d7ce68218c..8f5b86ad24e 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -289,14 +289,14 @@ public void addTripForId(FeedScopedId tripId, Trip trip) { } @Override - public Collection getCancelledTrips(List feeds) { + public Collection getCanceledTrips(List feeds) { OTPRequestTimeoutException.checkForTimeout(); - List cancelledTrips = new ArrayList<>(); + List canceledTrips = new ArrayList<>(); Map departures = new HashMap<>(); var timetableSnapshot = lazyGetTimeTableSnapShot(); if (timetableSnapshot == null) { - return cancelledTrips; + return canceledTrips; } var calendarService = getCalendarService(); var patternMap = transitModelIndex.getPatternForTrip(); @@ -315,13 +315,13 @@ public Collection getCancelledTrips(List feeds) { var timetable = timetableSnapshot.resolve(pattern, date); var tripTimes = timetable.getTripTimes(trip); if (tripTimes.getRealTimeState() == RealTimeState.CANCELED) { // use UPDATED for faked testing - cancelledTrips.add(new DatedTrip(trip, date)); + canceledTrips.add(new DatedTrip(trip, date)); // store departure time from first stop departures.put(trip, tripTimes.sortIndex()); } } } - cancelledTrips.sort((t1, t2) -> { + canceledTrips.sort((t1, t2) -> { if (t1.serviceDate().isBefore(t2.serviceDate())) { return -1; } else if (t2.serviceDate().isBefore(t1.serviceDate())) { @@ -338,7 +338,7 @@ public Collection getCancelledTrips(List feeds) { return t1.trip().getId().compareTo(t2.trip().getId()); } }); - return cancelledTrips; + return canceledTrips; } @Override diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index f867a22deff..c2edd0b2751 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -137,7 +137,12 @@ public interface TransitService { Collection getAllTrips(); - Collection getCancelledTrips(List feeds); + /** + * Get canceled trips. + * + * @param feeds If not null, used for filtering. + */ + Collection getCanceledTrips(List feeds); Collection getAllRoutes(); diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index dcef5f67096..3be30ece04f 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -1222,31 +1222,43 @@ type QueryType { """ ids: [String] ): [BikeRentalStation] @deprecated(reason : "Use rentalVehicles or vehicleRentalStations instead") - "Get cancelled TripTimes." + """ + Get pages of canceled Trips. Follows the + [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ + canceledTrips( + after: String, + before: String, + "Feed feedIds (e.g. [\"HSL\"])." + feeds: [String], + first: Int, + last: Int + ): DatedTripConnection + "Get canceled TripTimes." cancelledTripTimes( "Feed feedIds (e.g. [\"HSL\"])." feeds: [String], """ - Only cancelled trip times that have last stop arrival time at maxArrivalTime + Only canceled trip times that have last stop arrival time at maxArrivalTime or before are returned. Format: seconds since midnight of maxDate. """ maxArrivalTime: Int, - "Only cancelled trip times scheduled to run on maxDate or before are returned. Format: \"2019-12-23\" or \"20191223\"." + "Only canceled trip times scheduled to run on maxDate or before are returned. Format: \"2019-12-23\" or \"20191223\"." maxDate: String, """ - Only cancelled trip times that have first stop departure time at + Only canceled trip times that have first stop departure time at maxDepartureTime or before are returned. Format: seconds since midnight of maxDate. """ maxDepartureTime: Int, """ - Only cancelled trip times that have last stop arrival time at minArrivalTime + Only canceled trip times that have last stop arrival time at minArrivalTime or after are returned. Format: seconds since midnight of minDate. """ minArrivalTime: Int, - "Only cancelled trip times scheduled to run on minDate or after are returned. Format: \"2019-12-23\" or \"20191223\"." + "Only canceled trip times scheduled to run on minDate or after are returned. Format: \"2019-12-23\" or \"20191223\"." minDate: String, """ - Only cancelled trip times that have first stop departure time at + Only canceled trip times that have first stop departure time at minDepartureTime or after are returned. Format: seconds since midnight of minDate. """ minDepartureTime: Int, @@ -1256,19 +1268,7 @@ type QueryType { routes: [String], "Trip gtfsIds (e.g. [\"HSL:1098_20190405_Ma_2_1455\"])." trips: [String] - ): [Stoptime] @deprecated(reason : "`cancelledTripTimes` is deprecated. Use `cancelledTrips` instead.") - """ - Get pages of cancelled Trips. Follows the - [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). - """ - cancelledTrips( - after: String, - before: String, - "Feed feedIds (e.g. [\"HSL\"])." - feeds: [String], - first: Int, - last: Int - ): DatedTripConnection + ): [Stoptime] @deprecated(reason : "`cancelledTripTimes` is deprecated. Use `canceledTrips` instead.") "Get a single car park based on its ID, i.e. value of field `carParkId`" carPark(id: String!): CarPark @deprecated(reason : "carPark is deprecated. Use vehicleParking instead.") "Get all car parks" @@ -2893,7 +2893,7 @@ enum AlertSeverityLevelType { INFO """ Severe alerts are used when a significant part of public transport services is - affected, for example: All train services are cancelled due to technical problems. + affected, for example: All train services are canceled due to technical problems. """ SEVERE "Severity of alert is unknown" @@ -3570,7 +3570,7 @@ enum StopAlertType { enum StopRealTimeState { "The stop has been added through a realtime update." ADDED - "The stop has been cancelled through a realtime update." + "The stop has been canceled through a realtime update." CANCELED "The realtime feed has indicated that there is no data available for this stop." NO_DATA @@ -4342,21 +4342,21 @@ input TimetablePreferencesInput { When false, real-time updates are considered during the routing. In practice, when this option is set as true, some of the suggestions might not be realistic as the transfers could be invalid due to delays, - trips can be cancelled or stops can be skipped. + trips can be canceled or stops can be skipped. """ excludeRealTimeUpdates: Boolean """ - When true, departures that have been cancelled ahead of time will be + When true, departures that have been canceled ahead of time will be included during the routing. This means that an itinerary can include - a cancelled departure while some other alternative that contains no cancellations + a canceled departure while some other alternative that contains no cancellations could be filtered out as the alternative containing a cancellation would normally be better. """ includePlannedCancellations: Boolean """ - When true, departures that have been cancelled through a real-time feed will be + When true, departures that have been canceled through a real-time feed will be included during the routing. This means that an itinerary can include - a cancelled departure while some other alternative that contains no cancellations + a canceled departure while some other alternative that contains no cancellations could be filtered out as the alternative containing a cancellation would normally be better. This option can't be set to true while `includeRealTimeUpdates` is false. """ From 0469361d24a711bc2cc8b13750e05e4f0d6bcc8f Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 9 Aug 2024 18:09:32 +0300 Subject: [PATCH 014/162] Add graphql test --- .../apis/gtfs/GraphQLIntegrationTest.java | 30 +++- .../gtfs/expectations/canceled-trips.json | 129 ++++++++++++++++++ .../apis/gtfs/queries/canceled-trips.graphql | 80 +++++++++++ 3 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json create mode 100644 src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 79590ca2775..adbb05ddcbf 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -22,6 +22,7 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.time.Instant; +import java.time.LocalDate; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import java.util.Arrays; @@ -43,7 +44,10 @@ import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.framework.model.Grams; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.model.FeedInfo; +import org.opentripplanner.model.TimetableSnapshot; +import org.opentripplanner.model.calendar.CalendarServiceData; import org.opentripplanner.model.fare.FareMedium; import org.opentripplanner.model.fare.FareProduct; import org.opentripplanner.model.fare.ItineraryFares; @@ -161,7 +165,12 @@ static void setup() { var transitModel = new TransitModel(model, DEDUPLICATOR); final TripPattern pattern = TEST_MODEL.pattern(BUS).build(); - var trip = TransitModelForTest.trip("123").withHeadsign(I18NString.of("Trip Headsign")).build(); + var cal_id = TransitModelForTest.id("CAL_1"); + var trip = TransitModelForTest + .trip("123") + .withHeadsign(I18NString.of("Trip Headsign")) + .withServiceId(cal_id) + .build(); var stopTimes = TEST_MODEL.stopTimesEvery5Minutes(3, trip, T11_00); var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, DEDUPLICATOR); pattern.add(tripTimes); @@ -212,6 +221,25 @@ public TransitAlertService getTransitAlertService() { }; routes.forEach(transitService::addRoutes); + // Crate a calendar (needed for testing cancelled trips) + CalendarServiceData calendarServiceData = new CalendarServiceData(); + calendarServiceData.putServiceDatesForServiceId( + cal_id, + List.of(LocalDate.of(2024, 8, 8), LocalDate.of(2024, 8, 9)) + ); + transitModel.getServiceCodes().put(cal_id, 0); + transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); + transitModel.initTimetableSnapshotProvider(() -> { + TimetableSnapshot timetableSnapshot = new TimetableSnapshot(); + var canceledStopTimes = TEST_MODEL.stopTimesEvery5Minutes(3, trip, T11_30); + var canceledTripTimes = TripTimesFactory.tripTimes(trip, canceledStopTimes, DEDUPLICATOR); + pattern.add(canceledTripTimes); + canceledTripTimes.cancelTrip(); + timetableSnapshot.update(pattern, canceledTripTimes, LocalDate.now()); + + return timetableSnapshot.commit(); + }); + var step1 = walkStep("street") .withRelativeDirection(RelativeDirection.DEPART) .withAbsoluteDirection(20) diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json new file mode 100644 index 00000000000..98da1d79bae --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json @@ -0,0 +1,129 @@ +{ + "data": { + "canceledTrips": { + "pageInfo" : { + "hasNextPage" : false, + "hasPreviousPage" : false, + "startCursor" : "c2ltcGxlLWN1cnNvcjA=", + "endCursor" : "c2ltcGxlLWN1cnNvcjA=" + }, + "edges": [ + { + "node": { + "date": "2024-08-09", + "end": { + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:40:00+02:00" + }, + "scheduledTime": "2024-08-09T11:40:00+02:00" + }, + "start": { + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:30:00+02:00" + }, + "scheduledTime": "2024-08-09T11:30:00+02:00" + }, + "pattern": { + "code": "F:BUS" + }, + "route": { + "gtfsId": "F:R123" + }, + "stops": [ + { + "gtfsId": "F:Stop_0" + }, + { + "gtfsId": "F:Stop_1" + }, + { + "gtfsId": "F:Stop_2" + } + ], + "tripHeadsign": "Trip Headsign", + "tripShortName": null, + "stoptimes": [ + { + "dropoffType": null, + "headsign": "Stop headsign at stop 10", + "pickupType": null, + "realtimeState": "CANCELED", + "stop": { + "gtfsId": "F:Stop_0" + }, + "stopPosition": 10, + "timepoint": false, + "arrival": { + "scheduledTime": "2024-08-09T11:30:00+02:00", + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:30:00+02:00" + } + }, + "departure": { + "scheduledTime": "2024-08-09T11:30:00+02:00", + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:30:00+02:00" + } + } + }, + { + "dropoffType": null, + "headsign": "Stop headsign at stop 20", + "pickupType": null, + "realtimeState": "CANCELED", + "stop": { + "gtfsId": "F:Stop_1" + }, + "stopPosition": 20, + "timepoint": false, + "arrival": { + "scheduledTime": "2024-08-09T11:35:00+02:00", + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:35:00+02:00" + } + }, + "departure": { + "scheduledTime": "2024-08-09T11:35:00+02:00", + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:35:00+02:00" + } + } + }, + { + "dropoffType": null, + "headsign": "Stop headsign at stop 30", + "pickupType": null, + "realtimeState": "CANCELED", + "stop": { + "gtfsId": "F:Stop_2" + }, + "stopPosition": 30, + "timepoint": false, + "arrival": { + "scheduledTime": "2024-08-09T11:40:00+02:00", + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:40:00+02:00" + } + }, + "departure": { + "scheduledTime": "2024-08-09T11:40:00+02:00", + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:40:00+02:00" + } + } + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql new file mode 100644 index 00000000000..a92a710c540 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -0,0 +1,80 @@ +{ + canceledTrips(feeds: "F") { + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + edges { + node { + date + end { + estimated { + delay + time + } + scheduledTime + } + start { + estimated { + delay + time + } + scheduledTime + } + pattern { + code + } + route { + gtfsId + } + stops { + gtfsId + } + tripHeadsign + tripShortName + stoptimes { + dropoffType + headsign + pickupType + realtimeState + stop { + gtfsId + } + stopPosition + timepoint + arrival { + scheduledTime + estimated { + delay + time + } + } + departure { + scheduledTime + estimated { + delay + time + } + } + } + date + start { + estimated { + time + delay + } + scheduledTime + } + end { + estimated { + time + delay + } + scheduledTime + } + } + } + } +} From c0648b799b63d08965d5ce1a5bae518328be8f9d Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 9 Aug 2024 18:11:43 +0300 Subject: [PATCH 015/162] Rename method --- .../opentripplanner/ext/restapi/mapping/LegMapper.java | 2 +- .../apis/gtfs/datafetchers/LegImpl.java | 2 +- .../apis/transmodel/model/plan/LegType.java | 2 +- src/main/java/org/opentripplanner/model/plan/Leg.java | 2 +- .../model/plan/ScheduledTransitLeg.java | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/restapi/mapping/LegMapper.java b/src/ext/java/org/opentripplanner/ext/restapi/mapping/LegMapper.java index b642426cd6d..f274a87ebd3 100644 --- a/src/ext/java/org/opentripplanner/ext/restapi/mapping/LegMapper.java +++ b/src/ext/java/org/opentripplanner/ext/restapi/mapping/LegMapper.java @@ -87,7 +87,7 @@ public ApiLeg mapLeg( api.departureDelay = domain.getDepartureDelay(); api.arrivalDelay = domain.getArrivalDelay(); - api.realTime = domain.getRealTime(); + api.realTime = domain.isRealTimeUpdated(); api.isNonExactFrequency = domain.getNonExactFrequency(); api.headway = domain.getHeadway(); api.distance = round3Decimals(domain.getDistanceMeters()); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java index 9ec83a4bf67..3e3e39b6340 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java @@ -186,7 +186,7 @@ public DataFetcher pickupType() { @Override public DataFetcher realTime() { - return environment -> getSource(environment).getRealTime(); + return environment -> getSource(environment).isRealTimeUpdated(); } // TODO diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java index be05d00e16d..b794fc4703f 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java @@ -198,7 +198,7 @@ public static GraphQLObjectType create( .name("realtime") .description("Whether there is real-time data about this leg") .type(new GraphQLNonNull(Scalars.GraphQLBoolean)) - .dataFetcher(env -> leg(env).getRealTime()) + .dataFetcher(env -> leg(env).isRealTimeUpdated()) .build() ) .field( diff --git a/src/main/java/org/opentripplanner/model/plan/Leg.java b/src/main/java/org/opentripplanner/model/plan/Leg.java index 2a0b6726560..2cd8db0fc2f 100644 --- a/src/main/java/org/opentripplanner/model/plan/Leg.java +++ b/src/main/java/org/opentripplanner/model/plan/Leg.java @@ -240,7 +240,7 @@ default int getArrivalDelay() { /** * Whether there is real-time data about this Leg */ - default boolean getRealTime() { + default boolean isRealTimeUpdated() { return false; } diff --git a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java index 4a84ac6e799..c8358e19155 100644 --- a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java +++ b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java @@ -164,7 +164,7 @@ public Accessibility getTripWheelchairAccessibility() { @Override public LegTime start() { - if (getRealTime()) { + if (isRealTimeUpdated()) { return LegTime.of(startTime, getDepartureDelay()); } else { return LegTime.ofStatic(startTime); @@ -173,7 +173,7 @@ public LegTime start() { @Override public LegTime end() { - if (getRealTime()) { + if (isRealTimeUpdated()) { return LegTime.of(endTime, getArrivalDelay()); } else { return LegTime.ofStatic(endTime); @@ -217,7 +217,7 @@ public int getArrivalDelay() { } @Override - public boolean getRealTime() { + public boolean isRealTimeUpdated() { return ( tripTimes.isRealtimeUpdated(boardStopPosInPattern) || tripTimes.isRealtimeUpdated(alightStopPosInPattern) @@ -276,7 +276,7 @@ public List getIntermediateStops() { for (int i = boardStopPosInPattern + 1; i < alightStopPosInPattern; i++) { StopLocation stop = tripPattern.getStop(i); - final StopArrival visit = mapper.map(i, stop, getRealTime()); + final StopArrival visit = mapper.map(i, stop, isRealTimeUpdated()); visits.add(visit); } return visits; @@ -410,7 +410,7 @@ public String toString() { .addObj("to", getTo()) .addTime("startTime", startTime) .addTime("endTime", endTime) - .addBool("realTime", getRealTime()) + .addBool("realTime", isRealTimeUpdated()) .addNum("distance", distanceMeters, "m") .addNum("cost", generalizedCost) .addNum("routeType", getRouteType()) From 6437bd727d83535950365be2a9d694dfd7910646 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 9 Aug 2024 18:13:38 +0300 Subject: [PATCH 016/162] Add small tests --- .../model/TripTimeOnDateTest.java | 21 +++++++++++++++++++ .../model/plan/ScheduledTransitLegTest.java | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java b/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java index 78002e46eae..82e0f1a63f0 100644 --- a/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java +++ b/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java @@ -1,6 +1,8 @@ package org.opentripplanner.model; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.LocalTime; import org.junit.jupiter.api.Test; @@ -29,4 +31,23 @@ void gtfsSequence() { var departure = LocalTime.ofSecondOfDay(subject.getScheduledDeparture()); assertEquals(LocalTime.of(11, 10), departure); } + + @Test + void isRecordedStop() { + var testModel = TransitModelForTest.of(); + var pattern = testModel.pattern(TransitMode.BUS).build(); + var trip = TransitModelForTest.trip("123").build(); + var stopTimes = testModel.stopTimesEvery5Minutes(3, trip, T11_00); + + var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); + tripTimes.setRecorded(1); + + var subject = new TripTimeOnDate(tripTimes, 0, pattern); + + assertFalse(subject.isRecordedStop()); + + subject = new TripTimeOnDate(tripTimes, 1, pattern); + + assertTrue(subject.isRecordedStop()); + } } diff --git a/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegTest.java b/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegTest.java index b41ace81e8d..af497f97bfb 100644 --- a/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegTest.java +++ b/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegTest.java @@ -1,8 +1,10 @@ package org.opentripplanner.model.plan; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import java.time.OffsetDateTime; @@ -49,6 +51,7 @@ void legTimesWithoutRealTime() { assertNull(leg.start().estimated()); assertNull(leg.end().estimated()); + assertFalse(leg.isRealTimeUpdated()); } @Test @@ -67,6 +70,7 @@ void legTimesWithRealTime() { assertNotNull(leg.start().estimated()); assertNotNull(leg.end().estimated()); + assertTrue(leg.isRealTimeUpdated()); } private static ScheduledTransitLegBuilder builder() { From 6e143d9a41751ade4e24f9a7a7e80e4baeb7ca4c Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 9 Aug 2024 18:39:04 +0300 Subject: [PATCH 017/162] Realtime -> RealTime --- .../opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java | 4 ++-- .../org/opentripplanner/model/plan/ScheduledTransitLeg.java | 4 ++-- .../transit/model/timetable/RealTimeTripTimes.java | 2 +- .../transit/model/timetable/ScheduledTripTimes.java | 2 +- .../opentripplanner/transit/model/timetable/TripTimes.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java index 5d567d83264..9046fa6807c 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java @@ -43,7 +43,7 @@ public DataFetcher end() { if (scheduledTime == null) { return null; } - return tripTimes.isRealtimeUpdated(stopIndex) + return tripTimes.isRealTimeUpdated(stopIndex) ? ArrivalDepartureTime.of(scheduledTime, tripTimes.getArrivalDelay(stopIndex)) : ArrivalDepartureTime.ofStatic(scheduledTime); }; @@ -81,7 +81,7 @@ public DataFetcher start() { if (scheduledTime == null) { return null; } - return tripTimes.isRealtimeUpdated(0) + return tripTimes.isRealTimeUpdated(0) ? ArrivalDepartureTime.of(scheduledTime, tripTimes.getDepartureDelay(0)) : ArrivalDepartureTime.ofStatic(scheduledTime); }; diff --git a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java index c8358e19155..df0a84d6aaa 100644 --- a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java +++ b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java @@ -219,8 +219,8 @@ public int getArrivalDelay() { @Override public boolean isRealTimeUpdated() { return ( - tripTimes.isRealtimeUpdated(boardStopPosInPattern) || - tripTimes.isRealtimeUpdated(alightStopPosInPattern) + tripTimes.isRealTimeUpdated(boardStopPosInPattern) || + tripTimes.isRealTimeUpdated(alightStopPosInPattern) ); } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java index ca5897b7b24..b9159cb4fd7 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java @@ -198,7 +198,7 @@ public boolean isPredictionInaccurate(int stop) { return isStopRealTimeStates(stop, StopRealTimeState.INACCURATE_PREDICTIONS); } - public boolean isRealtimeUpdated(int stop) { + public boolean isRealTimeUpdated(int stop) { return ( realTimeState != RealTimeState.SCHEDULED && !isStopRealTimeStates(stop, StopRealTimeState.NO_DATA) diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index 3799fd7b140..423b0f8d9c7 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -235,7 +235,7 @@ public boolean isPredictionInaccurate(int stop) { } @Override - public boolean isRealtimeUpdated(int stop) { + public boolean isRealTimeUpdated(int stop) { return false; } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java index ea1a3f8ab2b..c1065256700 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java @@ -126,7 +126,7 @@ default int compareTo(TripTimes other) { /** * Return if trip has been updated and stop has not been given a NO_DATA update. */ - boolean isRealtimeUpdated(int stop); + boolean isRealTimeUpdated(int stop); /** * @return the whole trip's headsign. Individual stops can have different headsigns. From 1de24b2713794efcee5610da9be9f98600543145 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 9 Aug 2024 21:05:05 +0300 Subject: [PATCH 018/162] Add more tests --- .../timetable/RealTimeTripTimesTest.java | 11 +++++ .../service/DefaultTransitServiceTest.java | 42 ++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimesTest.java b/src/test/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimesTest.java index 5b3a8f76052..a47a7d61141 100644 --- a/src/test/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimesTest.java +++ b/src/test/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimesTest.java @@ -402,6 +402,17 @@ public void testNoData() { assertFalse(updatedTripTimesA.isNoDataStop(2)); } + @Test + public void testRealTimeUpdated() { + RealTimeTripTimes updatedTripTimesA = createInitialTripTimes().copyScheduledTimes(); + assertFalse(updatedTripTimesA.isRealTimeUpdated(1)); + updatedTripTimesA.setRealTimeState(RealTimeState.UPDATED); + assertTrue(updatedTripTimesA.isRealTimeUpdated(1)); + updatedTripTimesA.setNoData(1); + assertTrue(updatedTripTimesA.isRealTimeUpdated(0)); + assertFalse(updatedTripTimesA.isRealTimeUpdated(1)); + } + @Nested class GtfsStopSequence { diff --git a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index 54733f084d6..6f0591b1a2c 100644 --- a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -1,6 +1,7 @@ package org.opentripplanner.transit.service; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.model.plan.PlanTestConstants.T11_30; import static org.opentripplanner.transit.model.basic.TransitMode.BUS; import static org.opentripplanner.transit.model.basic.TransitMode.FERRY; import static org.opentripplanner.transit.model.basic.TransitMode.RAIL; @@ -12,16 +13,23 @@ import java.util.Set; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.model.TimetableSnapshot; +import org.opentripplanner.model.calendar.CalendarServiceData; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.framework.FeedScopedId; 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.Station; import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.ScheduledTripTimes; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripTimesFactory; class DefaultTransitServiceTest { @@ -46,6 +54,13 @@ class DefaultTransitServiceTest { .withCreatedByRealtimeUpdater(true) .build(); + static FeedScopedId CALENDAR_ID = TransitModelForTest.id("CAL_1"); + static Trip TRIP = TransitModelForTest + .trip("123") + .withHeadsign(I18NString.of("Trip Headsign")) + .withServiceId(CALENDAR_ID) + .build(); + @BeforeAll static void setup() { var stopModel = TEST_MODEL @@ -55,8 +70,23 @@ static void setup() { .withStation(STATION) .build(); - var transitModel = new TransitModel(stopModel, new Deduplicator()); + var deduplicator = new Deduplicator(); + var transitModel = new TransitModel(stopModel, deduplicator); + var canceledStopTimes = TEST_MODEL.stopTimesEvery5Minutes(3, TRIP, T11_30); + var canceledTripTimes = TripTimesFactory.tripTimes(TRIP, canceledStopTimes, deduplicator); + RAIL_PATTERN.add(canceledTripTimes); + canceledTripTimes.cancelTrip(); transitModel.addTripPattern(RAIL_PATTERN.getId(), RAIL_PATTERN); + + // Crate a calendar (needed for testing cancelled trips) + CalendarServiceData calendarServiceData = new CalendarServiceData(); + calendarServiceData.putServiceDatesForServiceId( + CALENDAR_ID, + List.of(LocalDate.of(2024, 8, 8), LocalDate.of(2024, 8, 9)) + ); + transitModel.getServiceCodes().put(CALENDAR_ID, 0); + transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); + transitModel.index(); transitModel.initTimetableSnapshotProvider(() -> { @@ -69,6 +99,7 @@ static void setup() { .build() ); timetableSnapshot.update(REAL_TIME_PATTERN, tripTimes, LocalDate.now()); + timetableSnapshot.update(RAIL_PATTERN, canceledTripTimes, LocalDate.now()); return timetableSnapshot.commit(); }); @@ -115,4 +146,13 @@ void getPatternForStopsWithRealTime() { Collection patternsForStop = service.getPatternsForStop(STOP_B, true); assertEquals(Set.of(FERRY_PATTERN, RAIL_PATTERN, REAL_TIME_PATTERN), patternsForStop); } + + @Test + void getCanceledTrips() { + Collection canceledTrips = service.getCanceledTrips(null); + assertEquals( + "[DatedTrip[trip=Trip{F:123 RR123}, serviceDate=2024-08-08], DatedTrip[trip=Trip{F:123 RR123}, serviceDate=2024-08-09]]", + canceledTrips.toString() + ); + } } From 7251391e55c2ba5b7d52652fc9369ad82aed9544 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Sep 2024 16:03:00 +0300 Subject: [PATCH 019/162] Refactor canceled trips fetching to be more efficient --- .../model/TimetableSnapshot.java | 30 +++++++++++++ .../service/DefaultTransitService.java | 45 ++++++------------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java index c0a7737abce..f92a16ecc56 100644 --- a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java +++ b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java @@ -5,24 +5,28 @@ import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.SetMultimap; import java.time.LocalDate; +import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.stream.Collectors; import javax.annotation.Nullable; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.updater.spi.UpdateError; @@ -176,6 +180,32 @@ public TripPattern getRealtimeAddedTripPattern(FeedScopedId tripId, LocalDate se return realtimeAddedTripPattern.get(tripIdAndServiceDate); } + /** + * Get trips which have been canceled. + * + * @param feeds if not null, only return trips from these feeds + */ + public ArrayList getCanceledTrips(List feeds) { + return timetables + .values() + .stream() + .flatMap(timetables -> + timetables + .stream() + .flatMap(timetable -> + timetable + .getTripTimes() + .stream() + .filter(tripTimes -> + tripTimes.isCanceled() && + (feeds == null || feeds.contains(tripTimes.getTrip().getId().getFeedId())) + ) + .map(tripTimes -> new DatedTrip(tripTimes.getTrip(), timetable.getServiceDate())) + ) + ) + .collect(Collectors.toCollection(ArrayList::new)); + } + /** * @return if any trip patterns were added. */ diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 8f5b86ad24e..7eb28e50c7c 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -8,10 +8,8 @@ import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -54,7 +52,6 @@ import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.site.StopLocationsGroup; import org.opentripplanner.transit.model.timetable.DatedTrip; -import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; @@ -288,47 +285,25 @@ public void addTripForId(FeedScopedId tripId, Trip trip) { transitModelIndex.getTripForId().put(tripId, trip); } + /** + * TODO This only supports realtime cancelled trips for now. + */ @Override public Collection getCanceledTrips(List feeds) { OTPRequestTimeoutException.checkForTimeout(); - List canceledTrips = new ArrayList<>(); - Map departures = new HashMap<>(); - var timetableSnapshot = lazyGetTimeTableSnapShot(); if (timetableSnapshot == null) { - return canceledTrips; - } - var calendarService = getCalendarService(); - var patternMap = transitModelIndex.getPatternForTrip(); - var trips = transitModelIndex.getTripForId(); - - for (Map.Entry entry : trips.entrySet()) { - var trip = entry.getValue(); - if (feeds != null && !feeds.contains(trip.getId().getFeedId())) { - continue; - } - Set serviceDates = calendarService.getServiceDatesForServiceId( - trip.getServiceId() - ); - var pattern = patternMap.get(trip); - for (LocalDate date : serviceDates) { - var timetable = timetableSnapshot.resolve(pattern, date); - var tripTimes = timetable.getTripTimes(trip); - if (tripTimes.getRealTimeState() == RealTimeState.CANCELED) { // use UPDATED for faked testing - canceledTrips.add(new DatedTrip(trip, date)); - // store departure time from first stop - departures.put(trip, tripTimes.sortIndex()); - } - } + return List.of(); } + List canceledTrips = timetableSnapshot.getCanceledTrips(feeds); canceledTrips.sort((t1, t2) -> { if (t1.serviceDate().isBefore(t2.serviceDate())) { return -1; } else if (t2.serviceDate().isBefore(t1.serviceDate())) { return 1; } - var departure1 = departures.get(t1.trip()); - var departure2 = departures.get(t2.trip()); + var departure1 = getDepartureTime(t1); + var departure2 = getDepartureTime(t2); if (departure1 < departure2) { return -1; } else if (departure1 > departure2) { @@ -780,4 +755,10 @@ private static Stream sortByOccurrenceAndReduce(Stream input) { .sorted(Map.Entry.comparingByValue().reversed()) .map(Map.Entry::getKey); } + + private int getDepartureTime(DatedTrip datedTrip) { + var pattern = getPatternForTrip(datedTrip.trip()); + var timetable = timetableSnapshot.resolve(pattern, datedTrip.serviceDate()); + return timetable.getTripTimes(datedTrip.trip()).getDepartureTime(0); + } } From bedee96dd764c458844df0ff849721fcfeea2cf3 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Sep 2024 16:03:15 +0300 Subject: [PATCH 020/162] Update and fix tests --- .../apis/gtfs/GraphQLIntegrationTest.java | 24 ++- .../service/DefaultTransitServiceTest.java | 9 +- .../gtfs/expectations/canceled-trips.json | 14 +- .../apis/gtfs/expectations/patterns.json | 175 +++++++++++------- 4 files changed, 140 insertions(+), 82 deletions(-) diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index adbb05ddcbf..25bb4f88eeb 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; +import static org.opentripplanner._support.time.ZoneIds.BERLIN; import static org.opentripplanner.model.plan.PlanTestConstants.D10m; import static org.opentripplanner.model.plan.PlanTestConstants.T11_00; import static org.opentripplanner.model.plan.PlanTestConstants.T11_01; @@ -36,7 +37,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.locationtech.jts.geom.Coordinate; import org.opentripplanner._support.text.I18NStrings; -import org.opentripplanner._support.time.ZoneIds; import org.opentripplanner.ext.fares.FaresToItineraryMapper; import org.opentripplanner.ext.fares.impl.DefaultFareService; import org.opentripplanner.framework.collection.ListUtils; @@ -175,6 +175,15 @@ static void setup() { var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, DEDUPLICATOR); pattern.add(tripTimes); + var trip2 = TransitModelForTest + .trip("321Canceled") + .withHeadsign(I18NString.of("Trip Headsign")) + .withServiceId(cal_id) + .build(); + var stopTimes2 = TEST_MODEL.stopTimesEvery5Minutes(3, trip2, T11_30); + var tripTimes2 = TripTimesFactory.tripTimes(trip2, stopTimes2, DEDUPLICATOR); + pattern.add(tripTimes2); + transitModel.addTripPattern(id("pattern-1"), pattern); var feedId = "testfeed"; @@ -189,7 +198,7 @@ static void setup() { .build(); transitModel.addAgency(agency); - transitModel.initTimeZone(ZoneIds.BERLIN); + transitModel.initTimeZone(BERLIN); transitModel.index(); var routes = Arrays .stream(TransitMode.values()) @@ -223,19 +232,18 @@ public TransitAlertService getTransitAlertService() { // Crate a calendar (needed for testing cancelled trips) CalendarServiceData calendarServiceData = new CalendarServiceData(); + var firstDate = LocalDate.of(2024, 8, 8); + var secondDate = LocalDate.of(2024, 8, 9); calendarServiceData.putServiceDatesForServiceId( cal_id, - List.of(LocalDate.of(2024, 8, 8), LocalDate.of(2024, 8, 9)) + List.of(firstDate, secondDate) ); transitModel.getServiceCodes().put(cal_id, 0); transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); transitModel.initTimetableSnapshotProvider(() -> { TimetableSnapshot timetableSnapshot = new TimetableSnapshot(); - var canceledStopTimes = TEST_MODEL.stopTimesEvery5Minutes(3, trip, T11_30); - var canceledTripTimes = TripTimesFactory.tripTimes(trip, canceledStopTimes, DEDUPLICATOR); - pattern.add(canceledTripTimes); - canceledTripTimes.cancelTrip(); - timetableSnapshot.update(pattern, canceledTripTimes, LocalDate.now()); + tripTimes2.cancelTrip(); + timetableSnapshot.update(pattern, tripTimes2, secondDate); return timetableSnapshot.commit(); }); diff --git a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index 6f0591b1a2c..060632be28a 100644 --- a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -80,9 +80,11 @@ static void setup() { // Crate a calendar (needed for testing cancelled trips) CalendarServiceData calendarServiceData = new CalendarServiceData(); + var firstDate = LocalDate.of(2024, 8, 8); + var secondDate = LocalDate.of(2024, 8, 9); calendarServiceData.putServiceDatesForServiceId( CALENDAR_ID, - List.of(LocalDate.of(2024, 8, 8), LocalDate.of(2024, 8, 9)) + List.of(firstDate, secondDate) ); transitModel.getServiceCodes().put(CALENDAR_ID, 0); transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); @@ -98,8 +100,9 @@ static void setup() { .withDepartureTimes(new int[] { 0, 1 }) .build() ); - timetableSnapshot.update(REAL_TIME_PATTERN, tripTimes, LocalDate.now()); - timetableSnapshot.update(RAIL_PATTERN, canceledTripTimes, LocalDate.now()); + timetableSnapshot.update(REAL_TIME_PATTERN, tripTimes, firstDate); + timetableSnapshot.update(RAIL_PATTERN, canceledTripTimes, firstDate); + timetableSnapshot.update(RAIL_PATTERN, canceledTripTimes, secondDate); return timetableSnapshot.commit(); }); diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json index 98da1d79bae..5208a38fb01 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json @@ -1,11 +1,11 @@ { "data": { "canceledTrips": { - "pageInfo" : { - "hasNextPage" : false, - "hasPreviousPage" : false, - "startCursor" : "c2ltcGxlLWN1cnNvcjA=", - "endCursor" : "c2ltcGxlLWN1cnNvcjA=" + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "c2ltcGxlLWN1cnNvcjA=", + "endCursor": "c2ltcGxlLWN1cnNvcjA=" }, "edges": [ { @@ -29,7 +29,7 @@ "code": "F:BUS" }, "route": { - "gtfsId": "F:R123" + "gtfsId": "F:R321Canceled" }, "stops": [ { @@ -126,4 +126,4 @@ ] } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json index b23ced4f954..d1f363fea5d 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json @@ -1,92 +1,139 @@ { - "data" : { - "patterns" : [ + "data": { + "patterns": [ { - "code" : "F:BUS", - "headsign" : "Trip Headsign", - "trips" : [ + "code": "F:BUS", + "headsign": "Trip Headsign", + "trips": [ { - "gtfsId" : "F:123", - "stoptimes" : [ + "gtfsId": "F:123", + "stoptimes": [ { - "stop" : { - "gtfsId" : "F:Stop_0", - "name" : "Stop_0" + "stop": { + "gtfsId": "F:Stop_0", + "name": "Stop_0" }, - "headsign" : "Stop headsign at stop 10", - "scheduledArrival" : 39600, - "scheduledDeparture" : 39600, - "stopPosition" : 10, - "realtimeState" : "SCHEDULED", - "pickupType" : "SCHEDULED", - "dropoffType" : "SCHEDULED" + "headsign": "Stop headsign at stop 10", + "scheduledArrival": 39600, + "scheduledDeparture": 39600, + "stopPosition": 10, + "realtimeState": "SCHEDULED", + "pickupType": "SCHEDULED", + "dropoffType": "SCHEDULED" }, { - "stop" : { - "gtfsId" : "F:Stop_1", - "name" : "Stop_1" + "stop": { + "gtfsId": "F:Stop_1", + "name": "Stop_1" }, - "headsign" : "Stop headsign at stop 20", - "scheduledArrival" : 39900, - "scheduledDeparture" : 39900, - "stopPosition" : 20, - "realtimeState" : "SCHEDULED", - "pickupType" : "SCHEDULED", - "dropoffType" : "SCHEDULED" + "headsign": "Stop headsign at stop 20", + "scheduledArrival": 39900, + "scheduledDeparture": 39900, + "stopPosition": 20, + "realtimeState": "SCHEDULED", + "pickupType": "SCHEDULED", + "dropoffType": "SCHEDULED" }, { - "stop" : { - "gtfsId" : "F:Stop_2", - "name" : "Stop_2" + "stop": { + "gtfsId": "F:Stop_2", + "name": "Stop_2" }, - "headsign" : "Stop headsign at stop 30", - "scheduledArrival" : 40200, - "scheduledDeparture" : 40200, - "stopPosition" : 30, - "realtimeState" : "SCHEDULED", - "pickupType" : "SCHEDULED", - "dropoffType" : "SCHEDULED" + "headsign": "Stop headsign at stop 30", + "scheduledArrival": 40200, + "scheduledDeparture": 40200, + "stopPosition": 30, + "realtimeState": "SCHEDULED", + "pickupType": "SCHEDULED", + "dropoffType": "SCHEDULED" } ], - "occupancy" : { - "occupancyStatus" : "FEW_SEATS_AVAILABLE" + "occupancy": { + "occupancyStatus": "FEW_SEATS_AVAILABLE" + } + }, + { + "gtfsId": "F:321Canceled", + "stoptimes": [ + { + "stop": { + "gtfsId": "F:Stop_0", + "name": "Stop_0" + }, + "headsign": "Stop headsign at stop 10", + "scheduledArrival": 41400, + "scheduledDeparture": 41400, + "stopPosition": 10, + "realtimeState": "CANCELED", + "pickupType": null, + "dropoffType": null + }, + { + "stop": { + "gtfsId": "F:Stop_1", + "name": "Stop_1" + }, + "headsign": "Stop headsign at stop 20", + "scheduledArrival": 41700, + "scheduledDeparture": 41700, + "stopPosition": 20, + "realtimeState": "CANCELED", + "pickupType": null, + "dropoffType": null + }, + { + "stop": { + "gtfsId": "F:Stop_2", + "name": "Stop_2" + }, + "headsign": "Stop headsign at stop 30", + "scheduledArrival": 42000, + "scheduledDeparture": 42000, + "stopPosition": 30, + "realtimeState": "CANCELED", + "pickupType": null, + "dropoffType": null + } + ], + "occupancy": { + "occupancyStatus": "NO_DATA_AVAILABLE" } } ], - "vehiclePositions" : [ + "vehiclePositions": [ { - "vehicleId" : "F:vehicle-1", - "label" : null, - "lat" : null, - "lon" : null, - "stopRelationship" : null, - "speed" : null, - "heading" : null, - "lastUpdated" : 31556889864403199, - "trip" : { - "gtfsId" : "F:123" + "vehicleId": "F:vehicle-1", + "label": null, + "lat": null, + "lon": null, + "stopRelationship": null, + "speed": null, + "heading": null, + "lastUpdated": 31556889864403199, + "trip": { + "gtfsId": "F:123" } }, { - "vehicleId" : "F:vehicle-2", - "label" : "vehicle2", - "lat" : 60.0, - "lon" : 80.0, - "stopRelationship" : { - "status" : "IN_TRANSIT_TO", - "stop" : { - "gtfsId" : "F:Stop_0" + "vehicleId": "F:vehicle-2", + "label": "vehicle2", + "lat": 60.0, + "lon": 80.0, + "stopRelationship": { + "status": "IN_TRANSIT_TO", + "stop": { + "gtfsId": "F:Stop_0" } }, - "speed" : 10.2, - "heading" : 80.0, - "lastUpdated" : -31557014167219200, - "trip" : { - "gtfsId" : "F:123" + "speed": 10.2, + "heading": 80.0, + "lastUpdated": -31557014167219200, + "trip": { + "gtfsId": "F:123" } } ] } ] } -} \ No newline at end of file +} From c0768e07c89f6d0b1d2a8d7d18852d5d02f09fe8 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Sep 2024 16:19:28 +0300 Subject: [PATCH 021/162] Create an union for dated stoptimes --- .../apis/gtfs/GtfsGraphQLIndex.java | 6 +- .../DatedStopTimeTypeResolver.java | 21 ++++ .../apis/gtfs/datafetchers/DatedTripImpl.java | 10 +- ...eImpl.java => ExactDatedStopTimeImpl.java} | 2 +- .../gtfs/generated/GraphQLDataFetchers.java | 48 ++++---- .../apis/gtfs/generated/GraphQLTypes.java | 38 +++---- .../apis/gtfs/generated/graphql-codegen.yml | 2 +- .../opentripplanner/apis/gtfs/schema.graphqls | 106 +++++++++--------- .../apis/gtfs/GraphQLIntegrationTest.java | 5 +- .../service/DefaultTransitServiceTest.java | 5 +- .../apis/gtfs/queries/canceled-trips.graphql | 42 +++---- 11 files changed, 161 insertions(+), 124 deletions(-) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeTypeResolver.java rename src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{DatedStopTimeImpl.java => ExactDatedStopTimeImpl.java} (97%) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index a160e197611..83a41e8af5c 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -35,10 +35,11 @@ import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.CoordinatesImpl; import org.opentripplanner.apis.gtfs.datafetchers.CurrencyImpl; -import org.opentripplanner.apis.gtfs.datafetchers.DatedStopTimeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.DatedStopTimeTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.DatedTripImpl; import org.opentripplanner.apis.gtfs.datafetchers.DefaultFareProductImpl; import org.opentripplanner.apis.gtfs.datafetchers.DepartureRowImpl; +import org.opentripplanner.apis.gtfs.datafetchers.ExactDatedStopTimeImpl; import org.opentripplanner.apis.gtfs.datafetchers.FareProductTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.FareProductUseImpl; import org.opentripplanner.apis.gtfs.datafetchers.FeedImpl; @@ -128,6 +129,7 @@ protected static GraphQLSchema buildSchema() { .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) + .type("DatedStopTime", type -> type.typeResolver(new DatedStopTimeTypeResolver())) .type(typeWiring.build(AgencyImpl.class)) .type(typeWiring.build(AlertImpl.class)) .type(typeWiring.build(BikeParkImpl.class)) @@ -181,7 +183,7 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(FareProductUseImpl.class)) .type(typeWiring.build(DefaultFareProductImpl.class)) .type(typeWiring.build(DatedTripImpl.class)) - .type(typeWiring.build(DatedStopTimeImpl.class)) + .type(typeWiring.build(ExactDatedStopTimeImpl.class)) .type(typeWiring.build(TripOccupancyImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeTypeResolver.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeTypeResolver.java new file mode 100644 index 00000000000..10ea6056792 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeTypeResolver.java @@ -0,0 +1,21 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.TypeResolutionEnvironment; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import graphql.schema.TypeResolver; +import org.opentripplanner.model.TripTimeOnDate; + +public class DatedStopTimeTypeResolver implements TypeResolver { + + @Override + public GraphQLObjectType getType(TypeResolutionEnvironment environment) { + Object o = environment.getObject(); + GraphQLSchema schema = environment.getSchema(); + + if (o instanceof TripTimeOnDate) { + return schema.getObjectType("ExactDatedStopTime"); + } + return null; + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java index 9046fa6807c..f33026b0e1d 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java @@ -92,8 +92,9 @@ public DataFetcher> stops() { return this::getStops; } + @SuppressWarnings("unchecked") @Override - public DataFetcher> stoptimes() { + public DataFetcher> stoptimes() { return environment -> { TransitService transitService = getTransitService(environment); Trip trip = getSource(environment).trip(); @@ -106,7 +107,12 @@ public DataFetcher> stoptimes() { if (timetable == null) { return List.of(); } - return TripTimeOnDate.fromTripTimes(timetable, trip, serviceDate, midnight); + return (Iterable) (List) TripTimeOnDate.fromTripTimes( + timetable, + trip, + serviceDate, + midnight + ); }; } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ExactDatedStopTimeImpl.java similarity index 97% rename from src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ExactDatedStopTimeImpl.java index 3ed12ac4fb6..21ec9b6e92f 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ExactDatedStopTimeImpl.java @@ -15,7 +15,7 @@ import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.transit.service.TransitService; -public class DatedStopTimeImpl implements GraphQLDataFetchers.GraphQLDatedStopTime { +public class ExactDatedStopTimeImpl implements GraphQLDataFetchers.GraphQLExactDatedStopTime { @Override public DataFetcher arrival() { 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 3f6f7df22a0..31da847eaf5 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -327,26 +327,8 @@ public interface GraphQLCurrency { public DataFetcher digits(); } - /** Stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. */ - public interface GraphQLDatedStopTime { - public DataFetcher arrival(); - - public DataFetcher departure(); - - public DataFetcher dropoffType(); - - public DataFetcher headsign(); - - public DataFetcher pickupType(); - - public DataFetcher realtimeState(); - - public DataFetcher stop(); - - public DataFetcher stopPosition(); - - public DataFetcher timepoint(); - } + /** Departure and/or arrival times to or from a stop on a specific date. */ + public interface GraphQLDatedStopTime extends TypeResolver {} /** Trip on a specific date */ public interface GraphQLDatedTrip { @@ -364,7 +346,7 @@ public interface GraphQLDatedTrip { public DataFetcher> stops(); - public DataFetcher> stoptimes(); + public DataFetcher> stoptimes(); public DataFetcher tripHeadsign(); @@ -433,6 +415,30 @@ public interface GraphQLEmissions { public DataFetcher co2(); } + /** + * Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. + * This can include realtime estimates. + */ + public interface GraphQLExactDatedStopTime { + public DataFetcher arrival(); + + public DataFetcher departure(); + + public DataFetcher dropoffType(); + + public DataFetcher headsign(); + + public DataFetcher pickupType(); + + public DataFetcher realtimeState(); + + public DataFetcher stop(); + + public DataFetcher stopPosition(); + + public DataFetcher timepoint(); + } + /** A 'medium' that a fare product applies to, for example cash, 'Oyster Card' or 'DB Navigator App'. */ public interface GraphQLFareMedium { public DataFetcher id(); 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 70e98ab17a0..2796cbc7cad 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -611,25 +611,6 @@ public enum GraphQLCyclingOptimizationType { SHORTEST_DURATION, } - public static class GraphQLDatedStopTimeHeadsignArgs { - - private String language; - - public GraphQLDatedStopTimeHeadsignArgs(Map args) { - if (args != null) { - this.language = (String) args.get("language"); - } - } - - public String getGraphQLLanguage() { - return this.language; - } - - public void setGraphQLLanguage(String language) { - this.language = language; - } - } - public static class GraphQLDatedTripTripHeadsignArgs { private String language; @@ -766,6 +747,25 @@ public void setGraphQLKeepingCost(org.opentripplanner.framework.model.Cost keepi } } + public static class GraphQLExactDatedStopTimeHeadsignArgs { + + private String language; + + public GraphQLExactDatedStopTimeHeadsignArgs(Map args) { + if (args != null) { + this.language = (String) args.get("language"); + } + } + + public String getGraphQLLanguage() { + return this.language; + } + + public void setGraphQLLanguage(String language) { + this.language = language; + } + } + public static class GraphQLFeedAlertsArgs { private List types; 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 1d5ebd31a4f..64effddf651 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 @@ -59,7 +59,7 @@ config: ContactInfo: org.opentripplanner.transit.model.organization.ContactInfo Cluster: Object Coordinates: org.locationtech.jts.geom.Coordinate#Coordinate - DatedStopTime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate + ExactDatedStopTime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate DatedTrip: org.opentripplanner.transit.model.timetable.DatedTrip#DatedTrip DatedTripConnection: graphql.relay.Connection#Connection DatedTripEdge: graphql.relay.Edge#Edge diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 3be30ece04f..e493e6ac46a 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -73,6 +73,9 @@ interface PlaceInterface { "Entity related to an alert" union AlertEntity = Agency | Pattern | Route | RouteType | Stop | StopOnRoute | StopOnTrip | Trip | Unknown +"Departure and/or arrival times to or from a stop on a specific date." +union DatedStopTime = ExactDatedStopTime + union StopPosition = PositionAtStop | PositionBetweenStops "A public transport agency" @@ -395,55 +398,6 @@ type Currency { digits: Int! } -"Stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop." -type DatedStopTime { - "Scheduled arrival time to the stop and a realtime estimate, if one exists." - arrival: ArrivalDepartureTime - "Scheduled departure time from the stop and a realtime estimate, if one exists." - departure: ArrivalDepartureTime - """ - Whether the vehicle can be disembarked at this stop. This field can also be - used to indicate if disembarkation is possible only with special arrangements. - """ - dropoffType: PickupDropoffType - """ - Vehicle headsign of the trip on this stop. Trip headsigns can change during - the trip (e.g. on routes which run on loops), so this value should be used - instead of `tripHeadsign` to display the headsign relevant to the user. - """ - headsign( - """ - If translated headsign is found from gtfs translation.txt and wanted language is not same as - feed's language then returns wanted translation. Otherwise uses name from trip_headsign.txt. - """ - language: String - ): String - """ - Whether the vehicle can be boarded at this stop. This field can also be used - to indicate if boarding is possible only with special arrangements. - """ - pickupType: PickupDropoffType - "Whether stop has been updated through a realtime update and if so, how." - realtimeState: StopRealTimeState! - "The stop where this arrival/departure happens" - stop: Stop - """ - The sequence of the stop in the pattern. This is not required to start from 0 or be consecutive - any - increasing integer sequence along the stops is valid. - - The purpose of this field is to identify the stop within the pattern so it can be cross-referenced - between it and the itinerary. It is safe to cross-reference when done quickly, i.e. within seconds. - However, it should be noted that real-time updates can change the values, so don't store it for - longer amounts of time. - - Depending on the source data, this might not be the GTFS `stop_sequence` but another value, perhaps - even generated. - """ - stopPosition: Int - "true, if this stop is used as a time equalization stop. false otherwise." - timepoint: Boolean -} - "Trip on a specific date" type DatedTrip implements Node { """ @@ -467,7 +421,7 @@ type DatedTrip implements Node { "List of stops this trip passes through" stops: [Stop!]! "List of times when this trip arrives to or departs from a stop" - stoptimes: [DatedStopTime] + stoptimes: [DatedStopTime!] "Headsign of the vehicle when running on this trip" tripHeadsign( """ @@ -573,6 +527,58 @@ type Emissions { co2: Grams } +""" +Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. +This can include realtime estimates. +""" +type ExactDatedStopTime { + "Scheduled arrival time to the stop and a realtime estimate, if one exists." + arrival: ArrivalDepartureTime + "Scheduled departure time from the stop and a realtime estimate, if one exists." + departure: ArrivalDepartureTime + """ + Whether the vehicle can be disembarked at this stop. This field can also be + used to indicate if disembarkation is possible only with special arrangements. + """ + dropoffType: PickupDropoffType + """ + Vehicle headsign of the trip on this stop. Trip headsigns can change during + the trip (e.g. on routes which run on loops), so this value should be used + instead of `tripHeadsign` to display the headsign relevant to the user. + """ + headsign( + """ + If translated headsign is found from gtfs translation.txt and wanted language is not same as + feed's language then returns wanted translation. Otherwise uses name from trip_headsign.txt. + """ + language: String + ): String + """ + Whether the vehicle can be boarded at this stop. This field can also be used + to indicate if boarding is possible only with special arrangements. + """ + pickupType: PickupDropoffType + "Whether stop has been updated through a realtime update and if so, how." + realtimeState: StopRealTimeState! + "The stop where this arrival/departure happens" + stop: Stop + """ + The sequence of the stop in the pattern. This is not required to start from 0 or be consecutive - any + increasing integer sequence along the stops is valid. + + The purpose of this field is to identify the stop within the pattern so it can be cross-referenced + between it and the itinerary. It is safe to cross-reference when done quickly, i.e. within seconds. + However, it should be noted that real-time updates can change the values, so don't store it for + longer amounts of time. + + Depending on the source data, this might not be the GTFS `stop_sequence` but another value, perhaps + even generated. + """ + stopPosition: Int + "true, if this stop is used as a time equalization stop. false otherwise." + timepoint: Boolean +} + "A 'medium' that a fare product applies to, for example cash, 'Oyster Card' or 'DB Navigator App'." type FareMedium { "ID of the medium" diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 25bb4f88eeb..594900997d9 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -234,10 +234,7 @@ public TransitAlertService getTransitAlertService() { CalendarServiceData calendarServiceData = new CalendarServiceData(); var firstDate = LocalDate.of(2024, 8, 8); var secondDate = LocalDate.of(2024, 8, 9); - calendarServiceData.putServiceDatesForServiceId( - cal_id, - List.of(firstDate, secondDate) - ); + calendarServiceData.putServiceDatesForServiceId(cal_id, List.of(firstDate, secondDate)); transitModel.getServiceCodes().put(cal_id, 0); transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); transitModel.initTimetableSnapshotProvider(() -> { diff --git a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index 060632be28a..bdf9bbb73f0 100644 --- a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -82,10 +82,7 @@ static void setup() { CalendarServiceData calendarServiceData = new CalendarServiceData(); var firstDate = LocalDate.of(2024, 8, 8); var secondDate = LocalDate.of(2024, 8, 9); - calendarServiceData.putServiceDatesForServiceId( - CALENDAR_ID, - List.of(firstDate, secondDate) - ); + calendarServiceData.putServiceDatesForServiceId(CALENDAR_ID, List.of(firstDate, secondDate)); transitModel.getServiceCodes().put(CALENDAR_ID, 0); transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql index a92a710c540..84452fd04b3 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -35,27 +35,29 @@ tripHeadsign tripShortName stoptimes { - dropoffType - headsign - pickupType - realtimeState - stop { - gtfsId - } - stopPosition - timepoint - arrival { - scheduledTime - estimated { - delay - time + ... on ExactDatedStopTime { + dropoffType + headsign + pickupType + realtimeState + stop { + gtfsId } - } - departure { - scheduledTime - estimated { - delay - time + stopPosition + timepoint + arrival { + scheduledTime + estimated { + delay + time + } + } + departure { + scheduledTime + estimated { + delay + time + } } } } From ae50bd62552994cbce7d1d907f37879aa4056732 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 19 Sep 2024 22:59:20 +0200 Subject: [PATCH 022/162] Add: GraphQL pagination to Decision Records --- DEVELOPMENT_DECISION_RECORDS.md | 4 ++++ doc/dev/decisionrecords/APIGraphQLDesign.md | 26 +++++++++++++++++++++ doc/dev/decisionrecords/_TEMPLATE.md | 2 -- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 doc/dev/decisionrecords/APIGraphQLDesign.md diff --git a/DEVELOPMENT_DECISION_RECORDS.md b/DEVELOPMENT_DECISION_RECORDS.md index 10b9e005809..ef0085f7171 100644 --- a/DEVELOPMENT_DECISION_RECORDS.md +++ b/DEVELOPMENT_DECISION_RECORDS.md @@ -104,3 +104,7 @@ Prefer immutable types over mutable. Use builders where appropriate. See [Avoid using records if you cannot encapsulate it properly](doc/dev/decisionrecords/RecordsPOJOsBuilders.md#records) +## GraphQL Best Practices - API Design + +[Follow best practices for designing GraphQL APIs. Our APIs are used by hundreds of clients +(web-pages/apps/services) and need to backwards compatible.](doc/dev/A) \ No newline at end of file diff --git a/doc/dev/decisionrecords/APIGraphQLDesign.md b/doc/dev/decisionrecords/APIGraphQLDesign.md new file mode 100644 index 00000000000..f739497c14c --- /dev/null +++ b/doc/dev/decisionrecords/APIGraphQLDesign.md @@ -0,0 +1,26 @@ +# GraphQL Best Practices - API Design + +Follow best practices for designing GraphQL APIs. Our APIs are used by hundreds of clients +(web-pages/apps/services) and need to be backwards compatible. A good reference used by several +of the OTP developers are the Production Ready GraphQL book by Marc-André Giroux. + + +## Pagination + +We use the [pagination](https://graphql.org/learn/pagination/) (a.k. Relay) specification for paging over entities like station, +stops, trips and routes. Very often OTP has a _final_ list of entities in memory. For non-entities +(Itinerary and Legs), witch does not always have an ID and is none trivial to reconstruct, it is +better to make a custom solution. + + +## Consistency + +Unfortunately, part of the GraphQL API is old and does not follow best practices. So, when adding +new features, consider what is best; To follow the existing style or follow the best practice. + + +### Context and Problem Statement + +Our APIs are used by hundreds of clients(web-pages/apps/services) and need to backwards compatible. +Correcting mistakes may not be possible or may take a long time. + diff --git a/doc/dev/decisionrecords/_TEMPLATE.md b/doc/dev/decisionrecords/_TEMPLATE.md index 45bb3b11d34..e184bdc6aaa 100644 --- a/doc/dev/decisionrecords/_TEMPLATE.md +++ b/doc/dev/decisionrecords/_TEMPLATE.md @@ -6,8 +6,6 @@ later. --> -Original pull-request: {#NNNN} - ### Context and Problem Statement From 66100b9a0f2f092594517f1a5461c178be6fb644 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 18 Oct 2024 16:37:00 +0300 Subject: [PATCH 023/162] Use TripOnServiceDate and redesign schema --- .../apis/gtfs/GtfsGraphQLIndex.java | 12 +- .../apis/gtfs/datafetchers/DatedTripImpl.java | 189 -------------- ...eImpl.java => FixedDatedStopTimeImpl.java} | 4 +- .../FixedTripOnServiceDateImpl.java | 113 +++++++++ .../gtfs/datafetchers/NodeTypeResolver.java | 4 - .../apis/gtfs/datafetchers/QueryTypeImpl.java | 25 +- ...ava => TripOnServiceDateTypeResolver.java} | 8 +- .../gtfs/generated/GraphQLDataFetchers.java | 134 +++++----- .../apis/gtfs/generated/GraphQLTypes.java | 57 ++--- .../apis/gtfs/generated/graphql-codegen.yml | 8 +- .../model/TimetableSnapshot.java | 12 +- .../opentripplanner/model/TripTimeOnDate.java | 32 +++ .../transit/model/timetable/DatedTrip.java | 8 - .../model/timetable/TripOnServiceDate.java | 3 +- .../service/DefaultTransitService.java | 19 +- .../transit/service/TransitService.java | 3 +- .../opentripplanner/apis/gtfs/schema.graphqls | 235 ++++++++---------- .../service/DefaultTransitServiceTest.java | 8 +- .../gtfs/expectations/canceled-trips.json | 54 ++-- .../apis/gtfs/queries/canceled-trips.graphql | 64 ++--- 20 files changed, 429 insertions(+), 563 deletions(-) delete mode 100644 src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java rename src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{ExactDatedStopTimeImpl.java => FixedDatedStopTimeImpl.java} (96%) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java rename src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{DatedStopTimeTypeResolver.java => TripOnServiceDateTypeResolver.java} (63%) delete mode 100644 src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 83a41e8af5c..c8587e5851a 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -35,14 +35,13 @@ import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.CoordinatesImpl; import org.opentripplanner.apis.gtfs.datafetchers.CurrencyImpl; -import org.opentripplanner.apis.gtfs.datafetchers.DatedStopTimeTypeResolver; -import org.opentripplanner.apis.gtfs.datafetchers.DatedTripImpl; import org.opentripplanner.apis.gtfs.datafetchers.DefaultFareProductImpl; import org.opentripplanner.apis.gtfs.datafetchers.DepartureRowImpl; -import org.opentripplanner.apis.gtfs.datafetchers.ExactDatedStopTimeImpl; import org.opentripplanner.apis.gtfs.datafetchers.FareProductTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.FareProductUseImpl; import org.opentripplanner.apis.gtfs.datafetchers.FeedImpl; +import org.opentripplanner.apis.gtfs.datafetchers.FixedDatedStopTimeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.FixedTripOnServiceDateImpl; import org.opentripplanner.apis.gtfs.datafetchers.GeometryImpl; import org.opentripplanner.apis.gtfs.datafetchers.ItineraryImpl; import org.opentripplanner.apis.gtfs.datafetchers.LegImpl; @@ -73,6 +72,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.TranslatedStringImpl; import org.opentripplanner.apis.gtfs.datafetchers.TripImpl; import org.opentripplanner.apis.gtfs.datafetchers.TripOccupancyImpl; +import org.opentripplanner.apis.gtfs.datafetchers.TripOnServiceDateTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.UnknownImpl; import org.opentripplanner.apis.gtfs.datafetchers.VehicleParkingImpl; import org.opentripplanner.apis.gtfs.datafetchers.VehiclePositionImpl; @@ -129,7 +129,7 @@ protected static GraphQLSchema buildSchema() { .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) - .type("DatedStopTime", type -> type.typeResolver(new DatedStopTimeTypeResolver())) + .type("TripOnServiceDate", type -> type.typeResolver(new TripOnServiceDateTypeResolver())) .type(typeWiring.build(AgencyImpl.class)) .type(typeWiring.build(AlertImpl.class)) .type(typeWiring.build(BikeParkImpl.class)) @@ -182,8 +182,8 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(CurrencyImpl.class)) .type(typeWiring.build(FareProductUseImpl.class)) .type(typeWiring.build(DefaultFareProductImpl.class)) - .type(typeWiring.build(DatedTripImpl.class)) - .type(typeWiring.build(ExactDatedStopTimeImpl.class)) + .type(typeWiring.build(FixedTripOnServiceDateImpl.class)) + .type(typeWiring.build(FixedDatedStopTimeImpl.class)) .type(typeWiring.build(TripOccupancyImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java deleted file mode 100644 index f33026b0e1d..00000000000 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedTripImpl.java +++ /dev/null @@ -1,189 +0,0 @@ -package org.opentripplanner.apis.gtfs.datafetchers; - -import static org.opentripplanner.apis.gtfs.GraphQLUtils.stopTimeToInt; - -import graphql.relay.Relay; -import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; -import java.time.Instant; -import java.time.LocalDate; -import java.time.ZonedDateTime; -import java.util.List; -import javax.annotation.Nullable; -import org.opentripplanner.apis.gtfs.GraphQLRequestContext; -import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; -import org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime; -import org.opentripplanner.ext.restapi.mapping.LocalDateMapper; -import org.opentripplanner.framework.time.ServiceDateUtils; -import org.opentripplanner.model.Timetable; -import org.opentripplanner.model.TripTimeOnDate; -import org.opentripplanner.transit.model.network.Route; -import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.model.timetable.DatedTrip; -import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripTimes; -import org.opentripplanner.transit.service.TransitService; - -public class DatedTripImpl implements GraphQLDataFetchers.GraphQLDatedTrip { - - @Override - public DataFetcher date() { - return env -> getSource(env).serviceDate(); - } - - @Override - public DataFetcher end() { - return env -> { - var tripTimes = getTripTimes(env); - if (tripTimes == null) { - return null; - } - var stopIndex = tripTimes.getNumStops() - 1; - var scheduledTime = getZonedDateTime(env, tripTimes.getScheduledArrivalTime(stopIndex)); - if (scheduledTime == null) { - return null; - } - return tripTimes.isRealTimeUpdated(stopIndex) - ? ArrivalDepartureTime.of(scheduledTime, tripTimes.getArrivalDelay(stopIndex)) - : ArrivalDepartureTime.ofStatic(scheduledTime); - }; - } - - @Override - public DataFetcher id() { - return env -> - new Relay.ResolvedGlobalId( - "DatedTrip", - getSource(env).trip().getId().toString() + - ';' + - LocalDateMapper.mapToApi(getSource(env).serviceDate()) - ); - } - - @Override - public DataFetcher pattern() { - return this::getTripPattern; - } - - @Override - public DataFetcher route() { - return environment -> getSource(environment).trip().getRoute(); - } - - @Override - public DataFetcher start() { - return env -> { - var tripTimes = getTripTimes(env); - if (tripTimes == null) { - return null; - } - var scheduledTime = getZonedDateTime(env, tripTimes.getScheduledDepartureTime(0)); - if (scheduledTime == null) { - return null; - } - return tripTimes.isRealTimeUpdated(0) - ? ArrivalDepartureTime.of(scheduledTime, tripTimes.getDepartureDelay(0)) - : ArrivalDepartureTime.ofStatic(scheduledTime); - }; - } - - @Override - public DataFetcher> stops() { - return this::getStops; - } - - @SuppressWarnings("unchecked") - @Override - public DataFetcher> stoptimes() { - return environment -> { - TransitService transitService = getTransitService(environment); - Trip trip = getSource(environment).trip(); - var serviceDate = getSource(environment).serviceDate(); - - Instant midnight = ServiceDateUtils - .asStartOfService(serviceDate, transitService.getTimeZone()) - .toInstant(); - Timetable timetable = getTimetable(environment, trip, serviceDate); - if (timetable == null) { - return List.of(); - } - return (Iterable) (List) TripTimeOnDate.fromTripTimes( - timetable, - trip, - serviceDate, - midnight - ); - }; - } - - @Override - public DataFetcher tripHeadsign() { - return environment -> - org.opentripplanner.framework.graphql.GraphQLUtils.getTranslation( - getSource(environment).trip().getHeadsign(), - environment - ); - } - - @Override - public DataFetcher tripShortName() { - return environment -> getSource(environment).trip().getShortName(); - } - - private List getStops(DataFetchingEnvironment environment) { - TripPattern tripPattern = getTripPattern(environment); - if (tripPattern == null) { - return List.of(); - } - return List.copyOf(tripPattern.getStops()); - } - - private TripPattern getTripPattern(DataFetchingEnvironment environment) { - return getTransitService(environment).getPatternForTrip(getSource(environment).trip()); - } - - @Nullable - private Timetable getTimetable( - DataFetchingEnvironment environment, - Trip trip, - LocalDate serviceDate - ) { - TransitService transitService = getTransitService(environment); - TripPattern tripPattern = transitService.getPatternForTrip(trip, serviceDate); - // no matching pattern found - if (tripPattern == null) { - return null; - } - - return transitService.getTimetableForTripPattern(tripPattern, serviceDate); - } - - @Nullable - private TripTimes getTripTimes(DataFetchingEnvironment environment) { - Trip trip = getSource(environment).trip(); - var serviceDate = getSource(environment).serviceDate(); - var timetable = getTimetable(environment, trip, serviceDate); - if (timetable == null) { - return null; - } - return timetable.getTripTimes(trip); - } - - private ZonedDateTime getZonedDateTime(DataFetchingEnvironment environment, int time) { - var fixedTime = stopTimeToInt(time); - if (fixedTime == null) { - return null; - } - var serviceDate = getSource(environment).serviceDate(); - TransitService transitService = getTransitService(environment); - return ServiceDateUtils.toZonedDateTime(serviceDate, transitService.getTimeZone(), fixedTime); - } - - private TransitService getTransitService(DataFetchingEnvironment environment) { - return environment.getContext().transitService(); - } - - private DatedTrip getSource(DataFetchingEnvironment environment) { - return environment.getSource(); - } -} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ExactDatedStopTimeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java similarity index 96% rename from src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ExactDatedStopTimeImpl.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java index 21ec9b6e92f..df2864f8580 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ExactDatedStopTimeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java @@ -15,7 +15,7 @@ import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.transit.service.TransitService; -public class ExactDatedStopTimeImpl implements GraphQLDataFetchers.GraphQLExactDatedStopTime { +public class FixedDatedStopTimeImpl implements GraphQLDataFetchers.GraphQLFixedDatedStopTime { @Override public DataFetcher arrival() { @@ -46,7 +46,7 @@ public DataFetcher departure() { } @Override - public DataFetcher dropoffType() { + public DataFetcher dropOffType() { return environment -> PickDropMapper.map(getSource(environment).getDropoffType()); } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java new file mode 100644 index 00000000000..6b78bc0d13d --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java @@ -0,0 +1,113 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.time.Instant; +import java.time.LocalDate; +import java.util.List; +import javax.annotation.Nullable; +import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.framework.time.ServiceDateUtils; +import org.opentripplanner.model.Timetable; +import org.opentripplanner.model.TripTimeOnDate; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; +import org.opentripplanner.transit.service.TransitService; + +public class FixedTripOnServiceDateImpl + implements GraphQLDataFetchers.GraphQLFixedTripOnServiceDate { + + @Override + public DataFetcher date() { + return env -> getSource(env).getServiceDate(); + } + + @Override + public DataFetcher end() { + return environment -> { + TransitService transitService = getTransitService(environment); + Trip trip = getTrip(environment); + var serviceDate = getSource(environment).getServiceDate(); + + Instant midnight = ServiceDateUtils + .asStartOfService(serviceDate, transitService.getTimeZone()) + .toInstant(); + Timetable timetable = getTimetable(environment, trip, serviceDate); + if (timetable == null) { + return null; + } + return TripTimeOnDate.lastFromTripTimes(timetable, trip, serviceDate, midnight); + }; + } + + @Override + public DataFetcher start() { + return environment -> { + TransitService transitService = getTransitService(environment); + Trip trip = getTrip(environment); + var serviceDate = getSource(environment).getServiceDate(); + + Instant midnight = ServiceDateUtils + .asStartOfService(serviceDate, transitService.getTimeZone()) + .toInstant(); + Timetable timetable = getTimetable(environment, trip, serviceDate); + if (timetable == null) { + return null; + } + return TripTimeOnDate.firstFromTripTimes(timetable, trip, serviceDate, midnight); + }; + } + + @Override + public DataFetcher> stoptimes() { + return environment -> { + TransitService transitService = getTransitService(environment); + Trip trip = getTrip(environment); + var serviceDate = getSource(environment).getServiceDate(); + + Instant midnight = ServiceDateUtils + .asStartOfService(serviceDate, transitService.getTimeZone()) + .toInstant(); + Timetable timetable = getTimetable(environment, trip, serviceDate); + if (timetable == null) { + return List.of(); + } + return TripTimeOnDate.fromTripTimes(timetable, trip, serviceDate, midnight); + }; + } + + @Override + public DataFetcher trip() { + return this::getTrip; + } + + @Nullable + private Timetable getTimetable( + DataFetchingEnvironment environment, + Trip trip, + LocalDate serviceDate + ) { + TransitService transitService = getTransitService(environment); + TripPattern tripPattern = transitService.getPatternForTrip(trip, serviceDate); + // no matching pattern found + if (tripPattern == null) { + return null; + } + + return transitService.getTimetableForTripPattern(tripPattern, serviceDate); + } + + private TransitService getTransitService(DataFetchingEnvironment environment) { + return environment.getContext().transitService(); + } + + private Trip getTrip(DataFetchingEnvironment environment) { + return getSource(environment).getTrip(); + } + + private TripOnServiceDate getSource(DataFetchingEnvironment environment) { + return environment.getSource(); + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java index 63f80fb643f..437d75e03e9 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/NodeTypeResolver.java @@ -20,7 +20,6 @@ import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; -import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; public class NodeTypeResolver implements TypeResolver { @@ -86,9 +85,6 @@ public GraphQLObjectType getType(TypeResolutionEnvironment environment) { if (o instanceof Trip) { return schema.getObjectType("Trip"); } - if (o instanceof DatedTrip) { - return schema.getObjectType("DatedTrip"); - } return null; } 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 4d893802d02..83818a3094d 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -13,7 +13,6 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.DataFetchingEnvironmentImpl; -import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -69,8 +68,8 @@ import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; -import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; @@ -193,7 +192,10 @@ public DataFetcher> bikeRentalStations() { }; } - // TODO + /** + * @deprecated Replaced by {@link #canceledTrips()}. + */ + @Deprecated @Override public DataFetcher> cancelledTripTimes() { return environment -> null; @@ -433,12 +435,6 @@ public DataFetcher node() { return new PlaceAtDistance(place, Double.parseDouble(parts[0])); } - case "DatedTrip": - { - String[] parts = id.split(";"); - var trip = transitService.getTripForId(FeedScopedId.parse(parts[0])); - return new DatedTrip(trip, LocalDate.parse(parts[1])); - } case "Route": return transitService.getRouteForId(FeedScopedId.parse(id)); case "Stop": @@ -814,14 +810,13 @@ public DataFetcher> trips() { } @Override - public DataFetcher> canceledTrips() { + public DataFetcher> canceledTrips() { return environment -> { var args = new GraphQLTypes.GraphQLQueryTypeCanceledTripsArgs(environment.getArguments()); - var datedTrips = getTransitService(environment) - .getCanceledTrips(args.getGraphQLFeeds()) - .stream() - .collect(Collectors.toList()); - return new SimpleListConnection<>(datedTrips).get(environment); + var trips = new ArrayList<>( + getTransitService(environment).getCanceledTrips(args.getGraphQLFeeds()) + ); + return new SimpleListConnection<>(trips).get(environment); }; } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeTypeResolver.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java similarity index 63% rename from src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeTypeResolver.java rename to src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java index 10ea6056792..aaf8d6dae14 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/DatedStopTimeTypeResolver.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java @@ -4,17 +4,17 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; -import org.opentripplanner.model.TripTimeOnDate; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; -public class DatedStopTimeTypeResolver implements TypeResolver { +public class TripOnServiceDateTypeResolver implements TypeResolver { @Override public GraphQLObjectType getType(TypeResolutionEnvironment environment) { Object o = environment.getObject(); GraphQLSchema schema = environment.getSchema(); - if (o instanceof TripTimeOnDate) { - return schema.getObjectType("ExactDatedStopTime"); + if (o instanceof TripOnServiceDate) { + return schema.getObjectType("FixedTripOnServiceDate"); } return null; } 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 31da847eaf5..e324e4d2494 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -66,8 +66,8 @@ 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.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.booking.BookingInfo; import org.opentripplanner.transit.model.timetable.booking.BookingTime; @@ -327,52 +327,6 @@ public interface GraphQLCurrency { public DataFetcher digits(); } - /** Departure and/or arrival times to or from a stop on a specific date. */ - public interface GraphQLDatedStopTime extends TypeResolver {} - - /** Trip on a specific date */ - public interface GraphQLDatedTrip { - public DataFetcher date(); - - public DataFetcher end(); - - public DataFetcher id(); - - public DataFetcher pattern(); - - public DataFetcher route(); - - public DataFetcher start(); - - public DataFetcher> stops(); - - public DataFetcher> stoptimes(); - - public DataFetcher tripHeadsign(); - - public DataFetcher tripShortName(); - } - - /** - * A connection to a list of dated trips that follows - * [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). - */ - public interface GraphQLDatedTripConnection { - public DataFetcher>> edges(); - - public DataFetcher pageInfo(); - } - - /** - * An edge for DatedTrip connection. Part of the - * [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). - */ - public interface GraphQLDatedTripEdge { - public DataFetcher cursor(); - - public DataFetcher node(); - } - /** * The standard case of a fare product: it only has a single price to be paid by the passenger * and no discounts are applied. @@ -415,30 +369,6 @@ public interface GraphQLEmissions { public DataFetcher co2(); } - /** - * Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. - * This can include realtime estimates. - */ - public interface GraphQLExactDatedStopTime { - public DataFetcher arrival(); - - public DataFetcher departure(); - - public DataFetcher dropoffType(); - - public DataFetcher headsign(); - - public DataFetcher pickupType(); - - public DataFetcher realtimeState(); - - public DataFetcher stop(); - - public DataFetcher stopPosition(); - - public DataFetcher timepoint(); - } - /** A 'medium' that a fare product applies to, for example cash, 'Oyster Card' or 'DB Navigator App'. */ public interface GraphQLFareMedium { public DataFetcher id(); @@ -490,6 +420,43 @@ public interface GraphQLFeedPublisher { public DataFetcher url(); } + /** + * Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. + * This can include realtime estimates. + */ + public interface GraphQLFixedDatedStopTime { + public DataFetcher arrival(); + + public DataFetcher departure(); + + public DataFetcher dropOffType(); + + public DataFetcher headsign(); + + public DataFetcher pickupType(); + + public DataFetcher realtimeState(); + + public DataFetcher stop(); + + public DataFetcher stopPosition(); + + public DataFetcher timepoint(); + } + + /** A fixed (i.e. not flexible or frequency based) trip on a specific service date */ + public interface GraphQLFixedTripOnServiceDate { + public DataFetcher date(); + + public DataFetcher end(); + + public DataFetcher start(); + + public DataFetcher> stoptimes(); + + public DataFetcher trip(); + } + public interface GraphQLGeometry { public DataFetcher length(); @@ -846,7 +813,7 @@ public interface GraphQLQueryType { public DataFetcher> bikeRentalStations(); - public DataFetcher> canceledTrips(); + public DataFetcher> canceledTrips(); public DataFetcher> cancelledTripTimes(); @@ -1280,6 +1247,29 @@ public interface GraphQLTripOccupancy { public DataFetcher occupancyStatus(); } + /** An instance of a trip on a service date. */ + public interface GraphQLTripOnServiceDate extends TypeResolver {} + + /** + * A connection to a list of trips on service dates that follows + * [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + */ + public interface GraphQLTripOnServiceDateConnection { + public DataFetcher>> edges(); + + public DataFetcher pageInfo(); + } + + /** + * An edge for TripOnServiceDate connection. Part of the + * [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + */ + public interface GraphQLTripOnServiceDateEdge { + public DataFetcher cursor(); + + public DataFetcher node(); + } + /** This is used for alert entities that we don't explicitly handle or they are missing. */ public interface GraphQLUnknown { public DataFetcher description(); 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 2796cbc7cad..5ab7e0d4017 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -611,25 +611,6 @@ public enum GraphQLCyclingOptimizationType { SHORTEST_DURATION, } - public static class GraphQLDatedTripTripHeadsignArgs { - - private String language; - - public GraphQLDatedTripTripHeadsignArgs(Map args) { - if (args != null) { - this.language = (String) args.get("language"); - } - } - - public String getGraphQLLanguage() { - return this.language; - } - - public void setGraphQLLanguage(String language) { - this.language = language; - } - } - public static class GraphQLDepartureRowStoptimesArgs { private Integer numberOfDepartures; @@ -747,25 +728,6 @@ public void setGraphQLKeepingCost(org.opentripplanner.framework.model.Cost keepi } } - public static class GraphQLExactDatedStopTimeHeadsignArgs { - - private String language; - - public GraphQLExactDatedStopTimeHeadsignArgs(Map args) { - if (args != null) { - this.language = (String) args.get("language"); - } - } - - public String getGraphQLLanguage() { - return this.language; - } - - public void setGraphQLLanguage(String language) { - this.language = language; - } - } - public static class GraphQLFeedAlertsArgs { private List types; @@ -811,6 +773,25 @@ public enum GraphQLFilterPlaceType { VEHICLE_RENT, } + public static class GraphQLFixedDatedStopTimeHeadsignArgs { + + private String language; + + public GraphQLFixedDatedStopTimeHeadsignArgs(Map args) { + if (args != null) { + this.language = (String) args.get("language"); + } + } + + public String getGraphQLLanguage() { + return this.language; + } + + public void setGraphQLLanguage(String language) { + this.language = language; + } + } + public enum GraphQLFormFactor { BICYCLE, CAR, 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 64effddf651..28c556238f0 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 @@ -59,10 +59,10 @@ config: ContactInfo: org.opentripplanner.transit.model.organization.ContactInfo Cluster: Object Coordinates: org.locationtech.jts.geom.Coordinate#Coordinate - ExactDatedStopTime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate - DatedTrip: org.opentripplanner.transit.model.timetable.DatedTrip#DatedTrip - DatedTripConnection: graphql.relay.Connection#Connection - DatedTripEdge: graphql.relay.Edge#Edge + FixedDatedStopTime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate + FixedTripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate + TripOnServiceDateConnection: graphql.relay.Connection#Connection + TripOnServiceDateEdge: graphql.relay.Edge#Edge ArrivalDepartureTime: org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime#ArrivalDepartureTime debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop diff --git a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java index f92a16ecc56..3dc68bb6968 100644 --- a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java +++ b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java @@ -26,8 +26,8 @@ import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopLocation; -import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.spi.UpdateSuccess; @@ -185,7 +185,7 @@ public TripPattern getRealtimeAddedTripPattern(FeedScopedId tripId, LocalDate se * * @param feeds if not null, only return trips from these feeds */ - public ArrayList getCanceledTrips(List feeds) { + public ArrayList getCanceledTrips(List feeds) { return timetables .values() .stream() @@ -200,7 +200,13 @@ public ArrayList getCanceledTrips(List feeds) { tripTimes.isCanceled() && (feeds == null || feeds.contains(tripTimes.getTrip().getId().getFeedId())) ) - .map(tripTimes -> new DatedTrip(tripTimes.getTrip(), timetable.getServiceDate())) + .map(tripTimes -> + TripOnServiceDate + .of(tripTimes.getTrip().getId()) + .withServiceDate(timetable.getServiceDate()) + .withTrip(tripTimes.getTrip()) + .build() + ) ) ) .collect(Collectors.toCollection(ArrayList::new)); diff --git a/src/main/java/org/opentripplanner/model/TripTimeOnDate.java b/src/main/java/org/opentripplanner/model/TripTimeOnDate.java index 8128c36e4db..ef62fa23507 100644 --- a/src/main/java/org/opentripplanner/model/TripTimeOnDate.java +++ b/src/main/java/org/opentripplanner/model/TripTimeOnDate.java @@ -89,6 +89,38 @@ public static List fromTripTimes( return out; } + /** + * Get first stop TripTimeOnDate from Timetable. + */ + public static TripTimeOnDate firstFromTripTimes( + Timetable table, + Trip trip, + LocalDate serviceDate, + Instant midnight + ) { + TripTimes times = table.getTripTimes(trip); + return new TripTimeOnDate(times, 0, table.getPattern(), serviceDate, midnight); + } + + /** + * Get last stop TripTimeOnDate from Timetable. + */ + public static TripTimeOnDate lastFromTripTimes( + Timetable table, + Trip trip, + LocalDate serviceDate, + Instant midnight + ) { + TripTimes times = table.getTripTimes(trip); + return new TripTimeOnDate( + times, + times.getNumStops() - 1, + table.getPattern(), + serviceDate, + midnight + ); + } + public static Comparator compareByDeparture() { return Comparator.comparing(t -> t.getServiceDayMidnight() + t.getRealtimeDeparture()); } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java b/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java deleted file mode 100644 index 2227421ed0e..00000000000 --- a/src/main/java/org/opentripplanner/transit/model/timetable/DatedTrip.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.opentripplanner.transit.model.timetable; - -import java.time.LocalDate; - -/** - * Class which represents a trip on a specific date - */ -public record DatedTrip(Trip trip, LocalDate serviceDate) {} diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripOnServiceDate.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripOnServiceDate.java index 1b4ecc964cf..68b442d0f49 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripOnServiceDate.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripOnServiceDate.java @@ -8,7 +8,8 @@ import org.opentripplanner.transit.model.framework.FeedScopedId; /** - * Class for holding data about a certain trip on a certain day. Essentially a DatedServiceJourney. + * Class for holding data about a certain trip on a certain day. Essentially a DatedServiceJourney + * or an instance of a generic trip on a certain service date. */ public class TripOnServiceDate extends AbstractTransitEntity { diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 7eb28e50c7c..d57951fdba6 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -51,7 +51,6 @@ import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.site.StopLocationsGroup; -import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; @@ -289,17 +288,17 @@ public void addTripForId(FeedScopedId tripId, Trip trip) { * TODO This only supports realtime cancelled trips for now. */ @Override - public Collection getCanceledTrips(List feeds) { + public Collection getCanceledTrips(List feeds) { OTPRequestTimeoutException.checkForTimeout(); var timetableSnapshot = lazyGetTimeTableSnapShot(); if (timetableSnapshot == null) { return List.of(); } - List canceledTrips = timetableSnapshot.getCanceledTrips(feeds); + List canceledTrips = timetableSnapshot.getCanceledTrips(feeds); canceledTrips.sort((t1, t2) -> { - if (t1.serviceDate().isBefore(t2.serviceDate())) { + if (t1.getServiceDate().isBefore(t2.getServiceDate())) { return -1; - } else if (t2.serviceDate().isBefore(t1.serviceDate())) { + } else if (t2.getServiceDate().isBefore(t1.getServiceDate())) { return 1; } var departure1 = getDepartureTime(t1); @@ -310,7 +309,7 @@ public Collection getCanceledTrips(List feeds) { return 1; } else { // identical departure day and time, so sort by unique feedscope id - return t1.trip().getId().compareTo(t2.trip().getId()); + return t1.getTrip().getId().compareTo(t2.getTrip().getId()); } }); return canceledTrips; @@ -756,9 +755,9 @@ private static Stream sortByOccurrenceAndReduce(Stream input) { .map(Map.Entry::getKey); } - private int getDepartureTime(DatedTrip datedTrip) { - var pattern = getPatternForTrip(datedTrip.trip()); - var timetable = timetableSnapshot.resolve(pattern, datedTrip.serviceDate()); - return timetable.getTripTimes(datedTrip.trip()).getDepartureTime(0); + private int getDepartureTime(TripOnServiceDate trip) { + var pattern = getPatternForTrip(trip.getTrip()); + var timetable = timetableSnapshot.resolve(pattern, trip.getServiceDate()); + return timetable.getTripTimes(trip.getTrip()).getDepartureTime(0); } } diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index c2edd0b2751..139df44647b 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -41,7 +41,6 @@ import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.site.StopLocationsGroup; -import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; @@ -142,7 +141,7 @@ public interface TransitService { * * @param feeds If not null, used for filtering. */ - Collection getCanceledTrips(List feeds); + Collection getCanceledTrips(List feeds); Collection getAllRoutes(); diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index e493e6ac46a..18822b6802f 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -73,11 +73,11 @@ interface PlaceInterface { "Entity related to an alert" union AlertEntity = Agency | Pattern | Route | RouteType | Stop | StopOnRoute | StopOnTrip | Trip | Unknown -"Departure and/or arrival times to or from a stop on a specific date." -union DatedStopTime = ExactDatedStopTime - union StopPosition = PositionAtStop | PositionBetweenStops +"An instance of a trip on a service date." +union TripOnServiceDate = FixedTripOnServiceDate + "A public transport agency" type Agency implements Node { """ @@ -398,75 +398,6 @@ type Currency { digits: Int! } -"Trip on a specific date" -type DatedTrip implements Node { - """ - The service date when the trip occurs. - - **Note**: A service date is a technical term useful for transit planning purposes and might not - correspond to a how a passenger thinks of a calendar date. For example, a night bus running - on Sunday morning at 1am to 3am, might have the previous Saturday's service date. - """ - date: LocalDate! - "The time when the trip ends including real-time information, if available." - end: ArrivalDepartureTime - "Global object ID provided by Relay. This value can be used to refetch this object using **node** query." - id: ID! - "The pattern the trip is running on" - pattern: Pattern - "The route the trip is running on" - route: Route! - "The time when the trip starts including real-time information, if available." - start: ArrivalDepartureTime - "List of stops this trip passes through" - stops: [Stop!]! - "List of times when this trip arrives to or departs from a stop" - stoptimes: [DatedStopTime!] - "Headsign of the vehicle when running on this trip" - tripHeadsign( - """ - If a translated headsign is found from GTFS translation.txt and wanted language is not same as - feed's language then returns wanted translation. Otherwise uses name from trip_headsign.txt. - """ - language: String - ): String - tripShortName: String -} - -""" -A connection to a list of dated trips that follows -[GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). -""" -type DatedTripConnection { - """ - Edges which contain the trips. Part of the - [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). - """ - edges: [DatedTripEdge] - """ - Contains cursors to fetch more pages of trips. - Part of the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). - """ - pageInfo: PageInfo! -} - -""" -An edge for DatedTrip connection. Part of the -[GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). -""" -type DatedTripEdge { - """ - The cursor of the edge. Part of the - [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). - """ - cursor: String! - """ - Dated trip as a node. Part of the - [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). - """ - node: DatedTrip -} - """ The standard case of a fare product: it only has a single price to be paid by the passenger and no discounts are applied. @@ -527,58 +458,6 @@ type Emissions { co2: Grams } -""" -Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. -This can include realtime estimates. -""" -type ExactDatedStopTime { - "Scheduled arrival time to the stop and a realtime estimate, if one exists." - arrival: ArrivalDepartureTime - "Scheduled departure time from the stop and a realtime estimate, if one exists." - departure: ArrivalDepartureTime - """ - Whether the vehicle can be disembarked at this stop. This field can also be - used to indicate if disembarkation is possible only with special arrangements. - """ - dropoffType: PickupDropoffType - """ - Vehicle headsign of the trip on this stop. Trip headsigns can change during - the trip (e.g. on routes which run on loops), so this value should be used - instead of `tripHeadsign` to display the headsign relevant to the user. - """ - headsign( - """ - If translated headsign is found from gtfs translation.txt and wanted language is not same as - feed's language then returns wanted translation. Otherwise uses name from trip_headsign.txt. - """ - language: String - ): String - """ - Whether the vehicle can be boarded at this stop. This field can also be used - to indicate if boarding is possible only with special arrangements. - """ - pickupType: PickupDropoffType - "Whether stop has been updated through a realtime update and if so, how." - realtimeState: StopRealTimeState! - "The stop where this arrival/departure happens" - stop: Stop - """ - The sequence of the stop in the pattern. This is not required to start from 0 or be consecutive - any - increasing integer sequence along the stops is valid. - - The purpose of this field is to identify the stop within the pattern so it can be cross-referenced - between it and the itinerary. It is safe to cross-reference when done quickly, i.e. within seconds. - However, it should be noted that real-time updates can change the values, so don't store it for - longer amounts of time. - - Depending on the source data, this might not be the GTFS `stop_sequence` but another value, perhaps - even generated. - """ - stopPosition: Int - "true, if this stop is used as a time equalization stop. false otherwise." - timepoint: Boolean -} - "A 'medium' that a fare product applies to, for example cash, 'Oyster Card' or 'DB Navigator App'." type FareMedium { "ID of the medium" @@ -671,6 +550,78 @@ type FeedPublisher { url: String! } +""" +Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. +This can include realtime estimates. +""" +type FixedDatedStopTime { + "Scheduled arrival time to the stop and a realtime estimate, if one exists." + arrival: ArrivalDepartureTime + "Scheduled departure time from the stop and a realtime estimate, if one exists." + departure: ArrivalDepartureTime + """ + Whether the vehicle can be disembarked at this stop. This field can also be + used to indicate if disembarkation is possible only with special arrangements. + """ + dropOffType: PickupDropoffType + """ + Vehicle headsign of the trip on this stop. Trip headsigns can change during + the trip (e.g. on routes which run on loops), so this value should be used + instead of `tripHeadsign` to display the headsign relevant to the user. + """ + headsign( + """ + If translated headsign is found from gtfs translation.txt and wanted language is not same as + feed's language then returns wanted translation. Otherwise uses name from trip_headsign.txt. + """ + language: String + ): String + """ + Whether the vehicle can be boarded at this stop. This field can also be used + to indicate if boarding is possible only with special arrangements. + """ + pickupType: PickupDropoffType + "Whether stop has been updated through a realtime update and if so, how." + realtimeState: StopRealTimeState! + "The stop where this arrival/departure happens" + stop: Stop + """ + The sequence of the stop in the pattern. This is not required to start from 0 or be consecutive - any + increasing integer sequence along the stops is valid. + + The purpose of this field is to identify the stop within the pattern so it can be cross-referenced + between it and the itinerary. It is safe to cross-reference when done quickly, i.e. within seconds. + However, it should be noted that real-time updates can change the values, so don't store it for + longer amounts of time. + + Depending on the source data, this might not be the GTFS `stop_sequence` but another value, perhaps + even generated. + """ + stopPosition: Int + "true, if this stop is used as a time equalization stop. false otherwise." + timepoint: Boolean +} + +"A fixed (i.e. not flexible or frequency based) trip on a specific service date" +type FixedTripOnServiceDate { + """ + The service date when the trip occurs. + + **Note**: A service date is a technical term useful for transit planning purposes and might not + correspond to a how a passenger thinks of a calendar date. For example, a night bus running + on Sunday morning at 1am to 3am, might have the previous Saturday's service date. + """ + date: LocalDate! + "The time when the trip ends at a stop and information related to the stop." + end: FixedDatedStopTime + "The time when the trip starts from a stop and information related to the stop." + start: FixedDatedStopTime + "List of times when this trip arrives to or departs from a stop and information related to the stop" + stoptimes: [FixedDatedStopTime!] + "This trip on service date is an instance of this trip." + trip: Trip +} + type Geometry { "The number of points in the string" length: Int @@ -1239,7 +1190,7 @@ type QueryType { feeds: [String], first: Int, last: Int - ): DatedTripConnection + ): TripOnServiceDateConnection "Get canceled TripTimes." cancelledTripTimes( "Feed feedIds (e.g. [\"HSL\"])." @@ -2482,6 +2433,40 @@ type TripOccupancy { occupancyStatus: OccupancyStatus } +""" +A connection to a list of trips on service dates that follows +[GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). +""" +type TripOnServiceDateConnection { + """ + Edges which contain the trips. Part of the + [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ + edges: [TripOnServiceDateEdge] + """ + Contains cursors to fetch more pages of trips. + Part of the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ + pageInfo: PageInfo! +} + +""" +An edge for TripOnServiceDate connection. Part of the +[GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). +""" +type TripOnServiceDateEdge { + """ + The cursor of the edge. Part of the + [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ + cursor: String! + """ + Trip on a service date as a node. Part of the + [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). + """ + node: TripOnServiceDate +} + "This is used for alert entities that we don't explicitly handle or they are missing." type Unknown { "Entity's description" diff --git a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index bdf9bbb73f0..fedcb3fe884 100644 --- a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -25,7 +25,6 @@ import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; -import org.opentripplanner.transit.model.timetable.DatedTrip; import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.ScheduledTripTimes; import org.opentripplanner.transit.model.timetable.Trip; @@ -149,10 +148,7 @@ void getPatternForStopsWithRealTime() { @Test void getCanceledTrips() { - Collection canceledTrips = service.getCanceledTrips(null); - assertEquals( - "[DatedTrip[trip=Trip{F:123 RR123}, serviceDate=2024-08-08], DatedTrip[trip=Trip{F:123 RR123}, serviceDate=2024-08-09]]", - canceledTrips.toString() - ); + var canceledTrips = service.getCanceledTrips(null); + assertEquals("[TripOnServiceDate{F:123}, TripOnServiceDate{F:123}]", canceledTrips.toString()); } } diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json index 5208a38fb01..1f3285d9e7d 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json @@ -12,41 +12,26 @@ "node": { "date": "2024-08-09", "end": { - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:40:00+02:00" - }, - "scheduledTime": "2024-08-09T11:40:00+02:00" + "arrival": { + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:40:00+02:00" + }, + "scheduledTime": "2024-08-09T11:40:00+02:00" + } }, "start": { - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:30:00+02:00" - }, - "scheduledTime": "2024-08-09T11:30:00+02:00" - }, - "pattern": { - "code": "F:BUS" - }, - "route": { - "gtfsId": "F:R321Canceled" - }, - "stops": [ - { - "gtfsId": "F:Stop_0" - }, - { - "gtfsId": "F:Stop_1" - }, - { - "gtfsId": "F:Stop_2" + "departure": { + "estimated": { + "delay": "PT0S", + "time": "2024-08-09T11:30:00+02:00" + }, + "scheduledTime": "2024-08-09T11:30:00+02:00" } - ], - "tripHeadsign": "Trip Headsign", - "tripShortName": null, + }, "stoptimes": [ { - "dropoffType": null, + "dropOffType": null, "headsign": "Stop headsign at stop 10", "pickupType": null, "realtimeState": "CANCELED", @@ -71,7 +56,7 @@ } }, { - "dropoffType": null, + "dropOffType": null, "headsign": "Stop headsign at stop 20", "pickupType": null, "realtimeState": "CANCELED", @@ -96,7 +81,7 @@ } }, { - "dropoffType": null, + "dropOffType": null, "headsign": "Stop headsign at stop 30", "pickupType": null, "realtimeState": "CANCELED", @@ -120,7 +105,10 @@ } } } - ] + ], + "trip": { + "gtfsId": "F:321Canceled" + } } } ] diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql index 84452fd04b3..80b1dd089bd 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -8,35 +8,28 @@ } edges { node { - date - end { - estimated { - delay - time + ... on FixedTripOnServiceDate { + date + end { + arrival { + estimated { + delay + time + } + scheduledTime + } } - scheduledTime - } - start { - estimated { - delay - time + start { + departure { + estimated { + delay + time + } + scheduledTime + } } - scheduledTime - } - pattern { - code - } - route { - gtfsId - } - stops { - gtfsId - } - tripHeadsign - tripShortName - stoptimes { - ... on ExactDatedStopTime { - dropoffType + stoptimes { + dropOffType headsign pickupType realtimeState @@ -60,21 +53,10 @@ } } } - } - date - start { - estimated { - time - delay - } - scheduledTime - } - end { - estimated { - time - delay + date + trip { + gtfsId } - scheduledTime } } } From f431d1734a189bc69e66ffed2cb3569d56aed822 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 24 Oct 2024 11:09:29 +0200 Subject: [PATCH 024/162] Apply suggestions from code review Co-authored-by: Leonard Ehrenfried --- DEVELOPMENT_DECISION_RECORDS.md | 2 +- doc/dev/decisionrecords/APIGraphQLDesign.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEVELOPMENT_DECISION_RECORDS.md b/DEVELOPMENT_DECISION_RECORDS.md index ef0085f7171..f5923092a94 100644 --- a/DEVELOPMENT_DECISION_RECORDS.md +++ b/DEVELOPMENT_DECISION_RECORDS.md @@ -107,4 +107,4 @@ Prefer immutable types over mutable. Use builders where appropriate. See ## GraphQL Best Practices - API Design [Follow best practices for designing GraphQL APIs. Our APIs are used by hundreds of clients -(web-pages/apps/services) and need to backwards compatible.](doc/dev/A) \ No newline at end of file +(web-pages/apps/services) and need to be backwards compatible.](doc/dev/A) \ No newline at end of file diff --git a/doc/dev/decisionrecords/APIGraphQLDesign.md b/doc/dev/decisionrecords/APIGraphQLDesign.md index f739497c14c..c9546ddf6f3 100644 --- a/doc/dev/decisionrecords/APIGraphQLDesign.md +++ b/doc/dev/decisionrecords/APIGraphQLDesign.md @@ -7,9 +7,9 @@ of the OTP developers are the Production Ready GraphQL book by Marc-André Girou ## Pagination -We use the [pagination](https://graphql.org/learn/pagination/) (a.k. Relay) specification for paging over entities like station, -stops, trips and routes. Very often OTP has a _final_ list of entities in memory. For non-entities -(Itinerary and Legs), witch does not always have an ID and is none trivial to reconstruct, it is +We use the [pagination](https://graphql.org/learn/pagination/) (a.k. Relay) specification for paging over entities like stations, +stops, trips and routes. Very often OTP has a _finite_ list of entities in memory. For non-entities +(Itinerary and Legs), witch do not always have an ID and is none trivial to reconstruct, it is better to make a custom solution. From 079485ed16fdd5f24c5f394f813d9d419e541249 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 25 Oct 2024 21:25:44 +0300 Subject: [PATCH 025/162] Remove stuff from new stoptime type --- .../datafetchers/FixedDatedStopTimeImpl.java | 50 ---------------- .../FixedTripOnServiceDateImpl.java | 2 +- .../gtfs/generated/GraphQLDataFetchers.java | 15 +---- .../apis/gtfs/generated/GraphQLTypes.java | 29 --------- .../apis/gtfs/generated/graphql-codegen.yml | 1 - .../opentripplanner/apis/gtfs/schema.graphqls | 60 +------------------ .../gtfs/expectations/canceled-trips.json | 20 +------ .../apis/gtfs/queries/canceled-trips.graphql | 8 +-- 8 files changed, 5 insertions(+), 180 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java index df2864f8580..ea53804298a 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java @@ -7,10 +7,7 @@ import java.time.ZonedDateTime; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; -import org.opentripplanner.apis.gtfs.mapping.PickDropMapper; import org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime; -import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.transit.service.TransitService; @@ -45,58 +42,11 @@ public DataFetcher departure() { }; } - @Override - public DataFetcher dropOffType() { - return environment -> PickDropMapper.map(getSource(environment).getDropoffType()); - } - - @Override - public DataFetcher headsign() { - return environment -> - GraphQLUtils.getTranslation(getSource(environment).getHeadsign(), environment); - } - - @Override - public DataFetcher pickupType() { - return environment -> PickDropMapper.map(getSource(environment).getPickupType()); - } - - @Override - public DataFetcher realtimeState() { - return environment -> { - var tripTime = getSource(environment); - // TODO support ADDED state - if (tripTime.isCanceledEffectively()) { - return GraphQLTypes.GraphQLStopRealTimeState.CANCELED; - } - if (tripTime.isNoDataStop()) { - return GraphQLTypes.GraphQLStopRealTimeState.NO_DATA; - } - if (tripTime.isRecordedStop()) { - return GraphQLTypes.GraphQLStopRealTimeState.RECORDED; - } - if (tripTime.isRealtime()) { - return GraphQLTypes.GraphQLStopRealTimeState.UPDATED; - } - return GraphQLTypes.GraphQLStopRealTimeState.UNUPDATED; - }; - } - - @Override - public DataFetcher stopPosition() { - return environment -> getSource(environment).getGtfsSequence(); - } - @Override public DataFetcher stop() { return environment -> getSource(environment).getStop(); } - @Override - public DataFetcher timepoint() { - return environment -> getSource(environment).isTimepoint(); - } - private TransitService getTransitService(DataFetchingEnvironment environment) { return environment.getContext().transitService(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java index 6b78bc0d13d..f6923cdc394 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java @@ -61,7 +61,7 @@ public DataFetcher start() { } @Override - public DataFetcher> stoptimes() { + public DataFetcher> stopTimes() { return environment -> { TransitService transitService = getTransitService(environment); Trip trip = getTrip(environment); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index e058705442c..f57a732dadb 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -21,7 +21,6 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRealtimeState; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLStopRealTimeState; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; import org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime; import org.opentripplanner.apis.gtfs.model.FeedPublisher; @@ -430,19 +429,7 @@ public interface GraphQLFixedDatedStopTime { public DataFetcher departure(); - public DataFetcher dropOffType(); - - public DataFetcher headsign(); - - public DataFetcher pickupType(); - - public DataFetcher realtimeState(); - public DataFetcher stop(); - - public DataFetcher stopPosition(); - - public DataFetcher timepoint(); } /** A fixed (i.e. not flexible or frequency based) trip on a specific service date */ @@ -453,7 +440,7 @@ public interface GraphQLFixedTripOnServiceDate { public DataFetcher start(); - public DataFetcher> stoptimes(); + public DataFetcher> stopTimes(); public DataFetcher trip(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index 693e613cea3..ee51eaa4e83 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -773,25 +773,6 @@ public enum GraphQLFilterPlaceType { VEHICLE_RENT, } - public static class GraphQLFixedDatedStopTimeHeadsignArgs { - - private String language; - - public GraphQLFixedDatedStopTimeHeadsignArgs(Map args) { - if (args != null) { - this.language = (String) args.get("language"); - } - } - - public String getGraphQLLanguage() { - return this.language; - } - - public void setGraphQLLanguage(String language) { - this.language = language; - } - } - public enum GraphQLFormFactor { BICYCLE, CAR, @@ -4623,16 +4604,6 @@ public enum GraphQLStopAlertType { TRIPS, } - /** Whether stop has been updated through a realtime update and if so, how. */ - public enum GraphQLStopRealTimeState { - ADDED, - CANCELED, - NO_DATA, - RECORDED, - UNUPDATED, - UPDATED, - } - public static class GraphQLStoptimeHeadsignArgs { private String language; diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index e3316e1d423..2c7fb9735f4 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -102,7 +102,6 @@ config: stopAtDistanceEdge: graphql.relay.Edge#Edge StopOnRoute: org.opentripplanner.apis.gtfs.model.StopOnRouteModel#StopOnRouteModel StopOnTrip: org.opentripplanner.apis.gtfs.model.StopOnTripModel#StopOnTripModel - StopRealTimeState: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLStopRealTimeState#GraphQLStopRealTimeState Stoptime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate StoptimesInPattern: org.opentripplanner.model.StopTimesInPattern#StopTimesInPattern TicketType: org.opentripplanner.ext.fares.model.FareRuleSet#FareRuleSet diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 212319455f3..2b15423fbc0 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -559,47 +559,8 @@ type FixedDatedStopTime { arrival: ArrivalDepartureTime "Scheduled departure time from the stop and a realtime estimate, if one exists." departure: ArrivalDepartureTime - """ - Whether the vehicle can be disembarked at this stop. This field can also be - used to indicate if disembarkation is possible only with special arrangements. - """ - dropOffType: PickupDropoffType - """ - Vehicle headsign of the trip on this stop. Trip headsigns can change during - the trip (e.g. on routes which run on loops), so this value should be used - instead of `tripHeadsign` to display the headsign relevant to the user. - """ - headsign( - """ - If translated headsign is found from gtfs translation.txt and wanted language is not same as - feed's language then returns wanted translation. Otherwise uses name from trip_headsign.txt. - """ - language: String - ): String - """ - Whether the vehicle can be boarded at this stop. This field can also be used - to indicate if boarding is possible only with special arrangements. - """ - pickupType: PickupDropoffType - "Whether stop has been updated through a realtime update and if so, how." - realtimeState: StopRealTimeState! "The stop where this arrival/departure happens" stop: Stop - """ - The sequence of the stop in the pattern. This is not required to start from 0 or be consecutive - any - increasing integer sequence along the stops is valid. - - The purpose of this field is to identify the stop within the pattern so it can be cross-referenced - between it and the itinerary. It is safe to cross-reference when done quickly, i.e. within seconds. - However, it should be noted that real-time updates can change the values, so don't store it for - longer amounts of time. - - Depending on the source data, this might not be the GTFS `stop_sequence` but another value, perhaps - even generated. - """ - stopPosition: Int - "true, if this stop is used as a time equalization stop. false otherwise." - timepoint: Boolean } "A fixed (i.e. not flexible or frequency based) trip on a specific service date" @@ -617,7 +578,7 @@ type FixedTripOnServiceDate { "The time when the trip starts from a stop and information related to the stop." start: FixedDatedStopTime "List of times when this trip arrives to or departs from a stop and information related to the stop" - stoptimes: [FixedDatedStopTime!] + stopTimes: [FixedDatedStopTime!] "This trip on service date is an instance of this trip." trip: Trip } @@ -3576,25 +3537,6 @@ enum StopAlertType { TRIPS } -"Whether stop has been updated through a realtime update and if so, how." -enum StopRealTimeState { - "The stop has been added through a realtime update." - ADDED - "The stop has been canceled through a realtime update." - CANCELED - "The realtime feed has indicated that there is no data available for this stop." - NO_DATA - """ - The vehicle has arrived to the stop or already visited it and the times are no longer estimates. - Note, not all realtime feeds indicate this information even if the vehicle has already passed the stop. - """ - RECORDED - "There have been no realtime updates." - UNUPDATED - "The trip's arrival and/or departure time has been updated." - UPDATED -} - """ Transit modes include modes that are used within organized transportation networks run by public transportation authorities, taxi companies etc. diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json index 1f3285d9e7d..5d60b684b76 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json @@ -29,17 +29,11 @@ "scheduledTime": "2024-08-09T11:30:00+02:00" } }, - "stoptimes": [ + "stopTimes": [ { - "dropOffType": null, - "headsign": "Stop headsign at stop 10", - "pickupType": null, - "realtimeState": "CANCELED", "stop": { "gtfsId": "F:Stop_0" }, - "stopPosition": 10, - "timepoint": false, "arrival": { "scheduledTime": "2024-08-09T11:30:00+02:00", "estimated": { @@ -56,15 +50,9 @@ } }, { - "dropOffType": null, - "headsign": "Stop headsign at stop 20", - "pickupType": null, - "realtimeState": "CANCELED", "stop": { "gtfsId": "F:Stop_1" }, - "stopPosition": 20, - "timepoint": false, "arrival": { "scheduledTime": "2024-08-09T11:35:00+02:00", "estimated": { @@ -81,15 +69,9 @@ } }, { - "dropOffType": null, - "headsign": "Stop headsign at stop 30", - "pickupType": null, - "realtimeState": "CANCELED", "stop": { "gtfsId": "F:Stop_2" }, - "stopPosition": 30, - "timepoint": false, "arrival": { "scheduledTime": "2024-08-09T11:40:00+02:00", "estimated": { diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql index 80b1dd089bd..cb738d4c56b 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -28,16 +28,10 @@ scheduledTime } } - stoptimes { - dropOffType - headsign - pickupType - realtimeState + stopTimes { stop { gtfsId } - stopPosition - timepoint arrival { scheduledTime estimated { From c15a29edcb7289c8188d49b60d61c82bd15465b4 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 25 Oct 2024 21:32:33 +0300 Subject: [PATCH 026/162] Mark stopTimes as non-null --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 2b15423fbc0..d7aaaf67002 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -578,7 +578,7 @@ type FixedTripOnServiceDate { "The time when the trip starts from a stop and information related to the stop." start: FixedDatedStopTime "List of times when this trip arrives to or departs from a stop and information related to the stop" - stopTimes: [FixedDatedStopTime!] + stopTimes: [FixedDatedStopTime!]! "This trip on service date is an instance of this trip." trip: Trip } From 65927c6e4670911c847055abfc3e5bf3fceda901 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 25 Oct 2024 21:36:32 +0300 Subject: [PATCH 027/162] Do less inside timetable snapshot provider initialization --- .../apis/gtfs/GraphQLIntegrationTest.java | 11 ++++--- .../service/DefaultTransitServiceTest.java | 29 +++++++++---------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 83abb6fcfdf..ec079341ad6 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -253,13 +253,12 @@ public Set getRoutesForStop(StopLocation stop) { calendarServiceData, DataImportIssueStore.NOOP ); - timetableRepository.initTimetableSnapshotProvider(() -> { - TimetableSnapshot timetableSnapshot = new TimetableSnapshot(); - tripTimes2.cancelTrip(); - timetableSnapshot.update(new RealTimeTripUpdate(pattern, tripTimes2, secondDate)); + TimetableSnapshot timetableSnapshot = new TimetableSnapshot(); + tripTimes2.cancelTrip(); + timetableSnapshot.update(new RealTimeTripUpdate(pattern, tripTimes2, secondDate)); - return timetableSnapshot.commit(); - }); + var snapshot = timetableSnapshot.commit(); + timetableRepository.initTimetableSnapshotProvider(() -> snapshot); var step1 = walkStep("street") .withRelativeDirection(RelativeDirection.DEPART) diff --git a/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index 2268b3628e6..26317a0ea80 100644 --- a/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -93,21 +93,20 @@ static void setup() { timetableRepository.addTripPattern(RAIL_PATTERN.getId(), RAIL_PATTERN); timetableRepository.index(); - timetableRepository.initTimetableSnapshotProvider(() -> { - TimetableSnapshot timetableSnapshot = new TimetableSnapshot(); - RealTimeTripTimes tripTimes = RealTimeTripTimes.of( - ScheduledTripTimes - .of() - .withTrip(TimetableRepositoryForTest.trip("REAL_TIME_TRIP").build()) - .withDepartureTimes(new int[] { 0, 1 }) - .build() - ); - timetableSnapshot.update(new RealTimeTripUpdate(REAL_TIME_PATTERN, tripTimes, firstDate)); - timetableSnapshot.update(new RealTimeTripUpdate(RAIL_PATTERN, canceledTripTimes, firstDate)); - timetableSnapshot.update(new RealTimeTripUpdate(RAIL_PATTERN, canceledTripTimes, secondDate)); - - return timetableSnapshot.commit(); - }); + TimetableSnapshot timetableSnapshot = new TimetableSnapshot(); + RealTimeTripTimes tripTimes = RealTimeTripTimes.of( + ScheduledTripTimes + .of() + .withTrip(TimetableRepositoryForTest.trip("REAL_TIME_TRIP").build()) + .withDepartureTimes(new int[] { 0, 1 }) + .build() + ); + timetableSnapshot.update(new RealTimeTripUpdate(REAL_TIME_PATTERN, tripTimes, firstDate)); + timetableSnapshot.update(new RealTimeTripUpdate(RAIL_PATTERN, canceledTripTimes, firstDate)); + timetableSnapshot.update(new RealTimeTripUpdate(RAIL_PATTERN, canceledTripTimes, secondDate)); + + var snapshot = timetableSnapshot.commit(); + timetableRepository.initTimetableSnapshotProvider(() -> snapshot); service = new DefaultTransitService(timetableRepository) { From 9c124df484601c49a5de578c5ed020d058e339bc Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 25 Oct 2024 22:08:46 +0300 Subject: [PATCH 028/162] Minor fixes --- .../apis/gtfs/datafetchers/LegImpl.java | 14 +++++++------- .../apis/gtfs/datafetchers/StoptimeImpl.java | 4 ++-- .../apis/gtfs/generated/GraphQLDataFetchers.java | 9 +++++---- .../apis/gtfs/generated/graphql-codegen.yml | 2 +- .../apis/gtfs/mapping/PickDropMapper.java | 11 ++++++----- .../apis/gtfs/model/ArrivalDepartureTime.java | 5 +---- .../org/opentripplanner/model/TripTimeOnDate.java | 4 ++-- .../street/model/vertex/StationCentroidVertex.java | 1 - .../transit/service/DefaultTransitService.java | 2 +- .../transit/service/TransitService.java | 2 +- 10 files changed, 26 insertions(+), 28 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java index 86d6d5be1a9..4db347ee784 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java @@ -10,12 +10,12 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.mapping.NumberMapper; +import org.opentripplanner.apis.gtfs.mapping.PickDropMapper; import org.opentripplanner.apis.gtfs.mapping.RealtimeStateMapper; import org.opentripplanner.ext.restapi.mapping.LocalDateMapper; import org.opentripplanner.ext.ridehailing.model.RideEstimate; import org.opentripplanner.ext.ridehailing.model.RideHailingLeg; import org.opentripplanner.framework.graphql.GraphQLUtils; -import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.LegTime; @@ -66,12 +66,12 @@ public DataFetcher dropOffBookingInfo() { } @Override - public DataFetcher dropoffType() { + public DataFetcher dropoffType() { return environment -> { if (getSource(environment).getAlightRule() == null) { - return PickDrop.SCHEDULED.name(); + return GraphQLTypes.GraphQLPickupDropoffType.SCHEDULED; } - return getSource(environment).getAlightRule().name(); + return PickDropMapper.map(getSource(environment).getAlightRule()); }; } @@ -177,12 +177,12 @@ public DataFetcher pickupBookingInfo() { } @Override - public DataFetcher pickupType() { + public DataFetcher pickupType() { return environment -> { if (getSource(environment).getBoardRule() == null) { - return PickDrop.SCHEDULED.name(); + return GraphQLTypes.GraphQLPickupDropoffType.SCHEDULED; } - return getSource(environment).getBoardRule().name(); + return PickDropMapper.map(getSource(environment).getBoardRule()); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java index 155c754cfa8..e6244124ba9 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java @@ -25,7 +25,7 @@ public DataFetcher departureDelay() { } @Override - public DataFetcher dropoffType() { + public DataFetcher dropoffType() { return environment -> PickDropMapper.map(getSource(environment).getDropoffType()); } @@ -36,7 +36,7 @@ public DataFetcher headsign() { } @Override - public DataFetcher pickupType() { + public DataFetcher pickupType() { return environment -> PickDropMapper.map(getSource(environment).getPickupType()); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index f57a732dadb..eeeac1ffd0f 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -18,6 +18,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLOccupancyStatus; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLPickupDropoffType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRealtimeState; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode; @@ -504,7 +505,7 @@ public interface GraphQLLeg { public DataFetcher dropOffBookingInfo(); - public DataFetcher dropoffType(); + public DataFetcher dropoffType(); public DataFetcher duration(); @@ -538,7 +539,7 @@ public interface GraphQLLeg { public DataFetcher pickupBookingInfo(); - public DataFetcher pickupType(); + public DataFetcher pickupType(); public DataFetcher realTime(); @@ -1110,11 +1111,11 @@ public interface GraphQLStoptime { public DataFetcher departureDelay(); - public DataFetcher dropoffType(); + public DataFetcher dropoffType(); public DataFetcher headsign(); - public DataFetcher pickupType(); + public DataFetcher pickupType(); public DataFetcher realtime(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 2c7fb9735f4..5309310e80d 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -80,7 +80,7 @@ config: TransitMode: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode#GraphQLTransitMode PageInfo: Object Pattern: org.opentripplanner.transit.model.network.TripPattern#TripPattern - PickupDropoffType: String + PickupDropoffType: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLPickupDropoffType#GraphQLPickupDropoffType Place: org.opentripplanner.model.plan.StopArrival#StopArrival placeAtDistance: org.opentripplanner.routing.graphfinder.PlaceAtDistance#PlaceAtDistance placeAtDistanceConnection: graphql.relay.Connection#Connection diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/PickDropMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/PickDropMapper.java index c8e4d212999..acb7e61cc4b 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/PickDropMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/PickDropMapper.java @@ -1,17 +1,18 @@ package org.opentripplanner.apis.gtfs.mapping; import javax.annotation.Nullable; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.model.PickDrop; public final class PickDropMapper { @Nullable - public static String map(PickDrop pickDrop) { + public static GraphQLTypes.GraphQLPickupDropoffType map(PickDrop pickDrop) { return switch (pickDrop) { - case SCHEDULED -> "SCHEDULED"; - case NONE -> "NONE"; - case CALL_AGENCY -> "CALL_AGENCY"; - case COORDINATE_WITH_DRIVER -> "COORDINATE_WITH_DRIVER"; + case SCHEDULED -> GraphQLTypes.GraphQLPickupDropoffType.SCHEDULED; + case NONE -> GraphQLTypes.GraphQLPickupDropoffType.NONE; + case CALL_AGENCY -> GraphQLTypes.GraphQLPickupDropoffType.CALL_AGENCY; + case COORDINATE_WITH_DRIVER -> GraphQLTypes.GraphQLPickupDropoffType.COORDINATE_WITH_DRIVER; case CANCELLED -> null; }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java b/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java index 529d83c7459..3a801537dd1 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java @@ -2,7 +2,6 @@ import java.time.Duration; import java.time.ZonedDateTime; -import javax.annotation.Nonnull; import javax.annotation.Nullable; /** @@ -10,16 +9,14 @@ * if available. */ public record ArrivalDepartureTime( - @Nonnull ZonedDateTime scheduledTime, + ZonedDateTime scheduledTime, @Nullable RealTimeEstimate estimated ) { - @Nonnull public static ArrivalDepartureTime of(ZonedDateTime realtime, int delaySecs) { var delay = Duration.ofSeconds(delaySecs); return new ArrivalDepartureTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay)); } - @Nonnull public static ArrivalDepartureTime ofStatic(ZonedDateTime staticTime) { return new ArrivalDepartureTime(staticTime, null); } diff --git a/application/src/main/java/org/opentripplanner/model/TripTimeOnDate.java b/application/src/main/java/org/opentripplanner/model/TripTimeOnDate.java index 4c0a4eda26c..857bb42ee51 100644 --- a/application/src/main/java/org/opentripplanner/model/TripTimeOnDate.java +++ b/application/src/main/java/org/opentripplanner/model/TripTimeOnDate.java @@ -172,14 +172,14 @@ public int getRealtimeDeparture() { * Returns the actual arrival time if available. Otherwise -1 is returned. */ public int getActualArrival() { - return tripTimes.isRecordedStop(stopIndex) ? tripTimes.getArrivalTime(stopIndex) : UNDEFINED; + return isRecordedStop() ? tripTimes.getArrivalTime(stopIndex) : UNDEFINED; } /** * Returns the actual departure time if available. Otherwise -1 is returned. */ public int getActualDeparture() { - return tripTimes.isRecordedStop(stopIndex) ? tripTimes.getDepartureTime(stopIndex) : UNDEFINED; + return isRecordedStop() ? tripTimes.getDepartureTime(stopIndex) : UNDEFINED; } public int getArrivalDelay() { diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/StationCentroidVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/StationCentroidVertex.java index f6f3b00191d..30de8e0f597 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/StationCentroidVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/StationCentroidVertex.java @@ -20,7 +20,6 @@ public Station getStation() { return this.station; } - @Nonnull @Override public I18NString getName() { return station.getName(); diff --git a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index c8cfbcaae4f..8270a0e06d1 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -298,7 +298,7 @@ public Trip getScheduledTripForId(FeedScopedId id) { * TODO This only supports realtime cancelled trips for now. */ @Override - public Collection getCanceledTrips(List feeds) { + public Collection getCanceledTrips(@Nullable List feeds) { OTPRequestTimeoutException.checkForTimeout(); var timetableSnapshot = lazyGetTimeTableSnapShot(); if (timetableSnapshot == null) { diff --git a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java index d74401c0def..c8b1cc21e63 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -174,7 +174,7 @@ public interface TransitService { * * @param feeds If not null, used for filtering. */ - Collection getCanceledTrips(List feeds); + Collection getCanceledTrips(@Nullable List feeds); /** * Return all routes, including those created by real-time updates. From 9e5eb1489819ca82f58fd91544492597367e2ef7 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 25 Oct 2024 22:17:43 +0300 Subject: [PATCH 029/162] Rename FixedDatedStopTime -> FixedStopTimeOnServiceDate --- .../org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java | 4 ++-- ...pTimeImpl.java => FixedStopTimeOnServiceDateImpl.java} | 3 ++- .../apis/gtfs/generated/graphql-codegen.yml | 2 +- .../org/opentripplanner/apis/gtfs/schema.graphqls | 8 ++++---- 4 files changed, 9 insertions(+), 8 deletions(-) rename application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{FixedDatedStopTimeImpl.java => FixedStopTimeOnServiceDateImpl.java} (95%) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index c8587e5851a..9aeed1e9472 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -40,7 +40,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.FareProductTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.FareProductUseImpl; import org.opentripplanner.apis.gtfs.datafetchers.FeedImpl; -import org.opentripplanner.apis.gtfs.datafetchers.FixedDatedStopTimeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.FixedStopTimeOnServiceDateImpl; import org.opentripplanner.apis.gtfs.datafetchers.FixedTripOnServiceDateImpl; import org.opentripplanner.apis.gtfs.datafetchers.GeometryImpl; import org.opentripplanner.apis.gtfs.datafetchers.ItineraryImpl; @@ -183,7 +183,7 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(FareProductUseImpl.class)) .type(typeWiring.build(DefaultFareProductImpl.class)) .type(typeWiring.build(FixedTripOnServiceDateImpl.class)) - .type(typeWiring.build(FixedDatedStopTimeImpl.class)) + .type(typeWiring.build(FixedStopTimeOnServiceDateImpl.class)) .type(typeWiring.build(TripOccupancyImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java similarity index 95% rename from application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java rename to application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java index ea53804298a..50cdb002a3e 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedDatedStopTimeImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java @@ -12,7 +12,8 @@ import org.opentripplanner.model.TripTimeOnDate; import org.opentripplanner.transit.service.TransitService; -public class FixedDatedStopTimeImpl implements GraphQLDataFetchers.GraphQLFixedDatedStopTime { +public class FixedStopTimeOnServiceDateImpl + implements GraphQLDataFetchers.GraphQLFixedDatedStopTime { @Override public DataFetcher arrival() { diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 5309310e80d..66b701c5e01 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -59,7 +59,7 @@ config: ContactInfo: org.opentripplanner.transit.model.organization.ContactInfo Cluster: Object Coordinates: org.locationtech.jts.geom.Coordinate#Coordinate - FixedDatedStopTime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate + FixedStopTimeOnServiceDate: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate FixedTripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate TripOnServiceDateConnection: graphql.relay.Connection#Connection TripOnServiceDateEdge: graphql.relay.Edge#Edge diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index d7aaaf67002..00aa6ab71b4 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -554,7 +554,7 @@ type FeedPublisher { Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. This can include realtime estimates. """ -type FixedDatedStopTime { +type FixedStopTimeOnServiceDate { "Scheduled arrival time to the stop and a realtime estimate, if one exists." arrival: ArrivalDepartureTime "Scheduled departure time from the stop and a realtime estimate, if one exists." @@ -574,11 +574,11 @@ type FixedTripOnServiceDate { """ date: LocalDate! "The time when the trip ends at a stop and information related to the stop." - end: FixedDatedStopTime + end: FixedStopTimeOnServiceDate "The time when the trip starts from a stop and information related to the stop." - start: FixedDatedStopTime + start: FixedStopTimeOnServiceDate "List of times when this trip arrives to or departs from a stop and information related to the stop" - stopTimes: [FixedDatedStopTime!]! + stopTimes: [FixedStopTimeOnServiceDate!]! "This trip on service date is an instance of this trip." trip: Trip } From 7f1948e4c8d4011732f19a5cb9c0cea40cf97641 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 25 Oct 2024 22:33:21 +0300 Subject: [PATCH 030/162] Run codegen --- .../apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java | 2 +- .../apis/gtfs/generated/GraphQLDataFetchers.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java index 50cdb002a3e..f6e8c62b223 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java @@ -13,7 +13,7 @@ import org.opentripplanner.transit.service.TransitService; public class FixedStopTimeOnServiceDateImpl - implements GraphQLDataFetchers.GraphQLFixedDatedStopTime { + implements GraphQLDataFetchers.GraphQLFixedStopTimeOnServiceDate { @Override public DataFetcher arrival() { diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index eeeac1ffd0f..9dc3fa6662f 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -425,7 +425,7 @@ public interface GraphQLFeedPublisher { * Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. * This can include realtime estimates. */ - public interface GraphQLFixedDatedStopTime { + public interface GraphQLFixedStopTimeOnServiceDate { public DataFetcher arrival(); public DataFetcher departure(); From 7ed48c3d556ab37bb375f8c23eb34018c30d5b39 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Thu, 31 Oct 2024 15:11:20 +0200 Subject: [PATCH 031/162] Improve schema doc and mark some fields as non-null --- .../opentripplanner/apis/gtfs/schema.graphqls | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 4f51c0e3085..2853164008c 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -165,7 +165,7 @@ type ArrivalDepartureTime { "The estimated time of the event. If no real-time information is available, this is null." estimated: RealTimeEstimate "The scheduled time of the event." - scheduledTime: OffsetDateTime + scheduledTime: OffsetDateTime! } "Bike park represents a location where bicycles can be parked." @@ -573,10 +573,10 @@ type FixedTripOnServiceDate { on Sunday morning at 1am to 3am, might have the previous Saturday's service date. """ date: LocalDate! - "The time when the trip ends at a stop and information related to the stop." - end: FixedStopTimeOnServiceDate - "The time when the trip starts from a stop and information related to the stop." - start: FixedStopTimeOnServiceDate + "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." + end: FixedStopTimeOnServiceDate! + "Information related to trip's scheduled departure from the first stop. Can contain realtime information." + start: FixedStopTimeOnServiceDate! "List of times when this trip arrives to or departs from a stop and information related to the stop" stopTimes: [FixedStopTimeOnServiceDate!]! "This trip on service date is an instance of this trip." @@ -1146,15 +1146,32 @@ type QueryType { ids: [String] ): [BikeRentalStation] @deprecated(reason : "Use rentalVehicles or vehicleRentalStations instead") """ - Get pages of canceled Trips. Follows the + Get pages of canceled Trips. Planned cancellations are not currently supported. Follows the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). """ canceledTrips( + """ + This parameter is part of the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm) + and should be used together with the `first` parameter. + """ after: String, + """ + This parameter is part of the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm) + and should be used together with the `last` parameter. + """ before: String, "Feed feedIds (e.g. [\"HSL\"])." feeds: [String], + """ + Limits how many trips are returned. This parameter is part of the + [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm) and can be used together with + the `after` parameter. + """ first: Int, + """ + This parameter is part of the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm) + and should be used together with the `before` parameter. + """ last: Int ): TripOnServiceDateConnection "Get canceled TripTimes." @@ -1191,7 +1208,7 @@ type QueryType { routes: [String], "Trip gtfsIds (e.g. [\"HSL:1098_20190405_Ma_2_1455\"])." trips: [String] - ): [Stoptime] @deprecated(reason : "`cancelledTripTimes` is deprecated. Use `canceledTrips` instead.") + ): [Stoptime] @deprecated(reason : "`cancelledTripTimes` is not implemented. Use `canceledTrips` instead.") "Get a single car park based on its ID, i.e. value of field `carParkId`" carPark(id: String!): CarPark @deprecated(reason : "carPark is deprecated. Use vehicleParking instead.") "Get all car parks" From 2797fa53f4d1f2faeff9ae2b0704c0069046b7c6 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 1 Nov 2024 13:44:40 +0200 Subject: [PATCH 032/162] Slightly refactor methods to fetch cancelled trips --- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 12 ++- .../model/TimetableSnapshot.java | 75 ++++++++++++------- .../service/DefaultTransitService.java | 65 +++++++++++----- .../transit/service/TransitService.java | 11 ++- .../service/DefaultTransitServiceTest.java | 22 +++++- 5 files changed, 131 insertions(+), 54 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java index 5b9c151557b..784dd5f51bf 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -833,9 +833,15 @@ public DataFetcher> trips() { public DataFetcher> canceledTrips() { return environment -> { var args = new GraphQLTypes.GraphQLQueryTypeCanceledTripsArgs(environment.getArguments()); - var trips = new ArrayList<>( - getTransitService(environment).getCanceledTrips(args.getGraphQLFeeds()) - ); + if (args.getGraphQLFeeds() != null && args.getGraphQLFeeds().isEmpty()) { + throw new IllegalArgumentException( + "Feeds need to be either not specified or contain elements." + ); + } + + var trips = args.getGraphQLFeeds() != null + ? getTransitService(environment).findCanceledTrips(args.getGraphQLFeeds()) + : getTransitService(environment).listCanceledTrips(); return new SimpleListConnection<>(trips).get(environment); }; } diff --git a/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java b/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java index f8365ef025a..b8aace7feb0 100644 --- a/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java +++ b/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java @@ -20,6 +20,7 @@ import java.util.Map.Entry; import java.util.SortedSet; import java.util.TreeSet; +import java.util.function.Predicate; import java.util.stream.Collectors; import javax.annotation.Nullable; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; @@ -226,35 +227,24 @@ public TripPattern getNewTripPatternForModifiedTrip(FeedScopedId tripId, LocalDa } /** - * Get trips which have been canceled. + * Find trips which have been canceled. * - * @param feeds if not null, only return trips from these feeds + * @param feeds only return trips from these feeds. Empty list is not allowed. */ - public ArrayList getCanceledTrips(List feeds) { - return timetables - .values() - .stream() - .flatMap(timetables -> - timetables - .stream() - .flatMap(timetable -> - timetable - .getTripTimes() - .stream() - .filter(tripTimes -> - tripTimes.isCanceled() && - (feeds == null || feeds.contains(tripTimes.getTrip().getId().getFeedId())) - ) - .map(tripTimes -> - TripOnServiceDate - .of(tripTimes.getTrip().getId()) - .withServiceDate(timetable.getServiceDate()) - .withTrip(tripTimes.getTrip()) - .build() - ) - ) - ) - .collect(Collectors.toCollection(ArrayList::new)); + public List findCanceledTrips(List feeds) { + if (feeds == null || feeds.isEmpty()) { + throw new IllegalArgumentException("Feeds list cannot be null or empty"); + } + return findTripsOnServiceDates(tripTimes -> + tripTimes.isCanceled() && feeds.contains(tripTimes.getTrip().getId().getFeedId()) + ); + } + + /** + * List trips which have been canceled. + */ + public List listCanceledTrips() { + return findTripsOnServiceDates(TripTimes::isCanceled); } /** @@ -679,6 +669,37 @@ private void validateNotReadOnly() { } } + private TripOnServiceDate mapToTripOnServiceDate(TripTimes tripTimes, Timetable timetable) { + return TripOnServiceDate + .of(tripTimes.getTrip().getId()) + .withServiceDate(timetable.getServiceDate()) + .withTrip(tripTimes.getTrip()) + .build(); + } + + /** + * Find trips from timetables based on filter criteria. + * + * @param filter used to filter {@link TripTimes}. + */ + private List findTripsOnServiceDates(Predicate filter) { + return timetables + .values() + .stream() + .flatMap(timetables -> + timetables + .stream() + .flatMap(timetable -> + timetable + .getTripTimes() + .stream() + .filter(filter) + .map(tripTimes -> mapToTripOnServiceDate(tripTimes, timetable)) + ) + ) + .collect(Collectors.toCollection(ArrayList::new)); + } + protected static class SortedTimetableComparator implements Comparator { @Override diff --git a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 98f73d5afcb..114414210ce 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -10,6 +10,7 @@ import java.time.ZonedDateTime; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -298,30 +299,34 @@ public Trip getScheduledTripForId(FeedScopedId id) { * TODO This only supports realtime cancelled trips for now. */ @Override - public Collection getCanceledTrips(@Nullable List feeds) { + public List findCanceledTrips(List feeds) { + if (feeds == null || feeds.isEmpty()) { + throw new IllegalArgumentException( + "Feeds list cannot be empty or null. It needs to have elements." + ); + } OTPRequestTimeoutException.checkForTimeout(); var timetableSnapshot = lazyGetTimeTableSnapShot(); if (timetableSnapshot == null) { return List.of(); } - List canceledTrips = timetableSnapshot.getCanceledTrips(feeds); - canceledTrips.sort((t1, t2) -> { - if (t1.getServiceDate().isBefore(t2.getServiceDate())) { - return -1; - } else if (t2.getServiceDate().isBefore(t1.getServiceDate())) { - return 1; - } - var departure1 = getDepartureTime(t1); - var departure2 = getDepartureTime(t2); - if (departure1 < departure2) { - return -1; - } else if (departure1 > departure2) { - return 1; - } else { - // identical departure day and time, so sort by unique feedscope id - return t1.getTrip().getId().compareTo(t2.getTrip().getId()); - } - }); + List canceledTrips = timetableSnapshot.findCanceledTrips(feeds); + canceledTrips.sort(new TripOnServiceDateComparator()); + return canceledTrips; + } + + /** + * TODO This only supports realtime cancelled trips for now. + */ + @Override + public List listCanceledTrips() { + OTPRequestTimeoutException.checkForTimeout(); + var timetableSnapshot = lazyGetTimeTableSnapShot(); + if (timetableSnapshot == null) { + return List.of(); + } + List canceledTrips = timetableSnapshot.listCanceledTrips(); + canceledTrips.sort(new TripOnServiceDateComparator()); return canceledTrips; } @@ -799,4 +804,26 @@ private int getDepartureTime(TripOnServiceDate trip) { var timetable = timetableSnapshot.resolve(pattern, trip.getServiceDate()); return timetable.getTripTimes(trip.getTrip()).getDepartureTime(0); } + + private class TripOnServiceDateComparator implements Comparator { + + @Override + public int compare(TripOnServiceDate t1, TripOnServiceDate t2) { + if (t1.getServiceDate().isBefore(t2.getServiceDate())) { + return -1; + } else if (t2.getServiceDate().isBefore(t1.getServiceDate())) { + return 1; + } + var departure1 = getDepartureTime(t1); + var departure2 = getDepartureTime(t2); + if (departure1 < departure2) { + return -1; + } else if (departure1 > departure2) { + return 1; + } else { + // identical departure day and time, so sort by unique feedscope id + return t1.getTrip().getId().compareTo(t2.getTrip().getId()); + } + } + } } diff --git a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java index c8b1cc21e63..4e0b49d559f 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -170,11 +170,16 @@ public interface TransitService { Collection getAllTrips(); /** - * Get canceled trips. + * Find canceled trips. * - * @param feeds If not null, used for filtering. + * @param feeds used for filtering. Empty list is not allowed. */ - Collection getCanceledTrips(@Nullable List feeds); + List findCanceledTrips(List feeds); + + /** + * List all canceled trips. + */ + List listCanceledTrips(); /** * Return all routes, including those created by real-time updates. diff --git a/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index 1d463955bad..534b0103854 100644 --- a/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -1,5 +1,6 @@ package org.opentripplanner.transit.service; +import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.opentripplanner.model.plan.PlanTestConstants.T11_30; import static org.opentripplanner.transit.model.basic.TransitMode.BUS; @@ -152,8 +153,25 @@ void getPatternForStopsWithRealTime() { } @Test - void getCanceledTrips() { - var canceledTrips = service.getCanceledTrips(null); + void listCanceledTrips() { + var canceledTrips = service.listCanceledTrips(); assertEquals("[TripOnServiceDate{F:123}, TripOnServiceDate{F:123}]", canceledTrips.toString()); } + + @Test + void findCanceledTrips() { + var canceledTripsForFeedWithCancellations = service.findCanceledTrips(List.of("F", "G")); + assertEquals( + "[TripOnServiceDate{F:123}, TripOnServiceDate{F:123}]", + canceledTripsForFeedWithCancellations.toString() + ); + + var canceledTripsForFeedWithoutCancellations = service.findCanceledTrips(List.of("G")); + assertEquals("[]", canceledTripsForFeedWithoutCancellations.toString()); + } + + @Test + void findCanceledTripsWithEmptyFeeds() { + assertThrows(IllegalArgumentException.class, () -> service.findCanceledTrips(List.of())); + } } From a2b30f865258b48e5fb66cde8fb5ff95a0211d06 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 1 Nov 2024 13:57:00 +0200 Subject: [PATCH 033/162] Improve schema doc --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 2853164008c..1d970162390 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -1146,7 +1146,9 @@ type QueryType { ids: [String] ): [BikeRentalStation] @deprecated(reason : "Use rentalVehicles or vehicleRentalStations instead") """ - Get pages of canceled Trips. Planned cancellations are not currently supported. Follows the + Get pages of canceled trips. Planned cancellations are not currently supported. Limiting the number of + returned trips with either `first` or `last` is highly recommended since the number of returned trips + can be really high when there is a strike affecting the transit services, for example. Follows the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). """ canceledTrips( From 6eaef7a9c345c80c6d5f422db6e1d01a96ed18b3 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 1 Nov 2024 14:02:26 +0200 Subject: [PATCH 034/162] Rename date -> serviceDate --- .../apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java | 2 +- .../apis/gtfs/generated/GraphQLDataFetchers.java | 4 ++-- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 2 +- .../apis/gtfs/expectations/canceled-trips.json | 2 +- .../opentripplanner/apis/gtfs/queries/canceled-trips.graphql | 3 +-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java index f6923cdc394..c34c25e3f1d 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java @@ -20,7 +20,7 @@ public class FixedTripOnServiceDateImpl implements GraphQLDataFetchers.GraphQLFixedTripOnServiceDate { @Override - public DataFetcher date() { + public DataFetcher serviceDate() { return env -> getSource(env).getServiceDate(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 9dc3fa6662f..60e0b1e3fbc 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -435,10 +435,10 @@ public interface GraphQLFixedStopTimeOnServiceDate { /** A fixed (i.e. not flexible or frequency based) trip on a specific service date */ public interface GraphQLFixedTripOnServiceDate { - public DataFetcher date(); - public DataFetcher end(); + public DataFetcher serviceDate(); + public DataFetcher start(); public DataFetcher> stopTimes(); diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 1d970162390..4cfe9fa6fd0 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -572,7 +572,7 @@ type FixedTripOnServiceDate { correspond to a how a passenger thinks of a calendar date. For example, a night bus running on Sunday morning at 1am to 3am, might have the previous Saturday's service date. """ - date: LocalDate! + serviceDate: LocalDate! "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." end: FixedStopTimeOnServiceDate! "Information related to trip's scheduled departure from the first stop. Can contain realtime information." diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json index 5d60b684b76..b3facd3da1f 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json @@ -10,7 +10,7 @@ "edges": [ { "node": { - "date": "2024-08-09", + "serviceDate": "2024-08-09", "end": { "arrival": { "estimated": { diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql index cb738d4c56b..a29950f7b0e 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -9,7 +9,7 @@ edges { node { ... on FixedTripOnServiceDate { - date + serviceDate end { arrival { estimated { @@ -47,7 +47,6 @@ } } } - date trip { gtfsId } From 396c43c83e7faaf6f709fcb518f73a913bdf18a5 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 1 Nov 2024 14:36:09 +0200 Subject: [PATCH 035/162] Rename ArrivalDepartureTime -> FixedArrivalDepartureTime and LegTime -> FixedArrivalDepartureTime --- .../impl/CombinedInterlinedTransitLeg.java | 6 ++-- .../ext/flex/FlexibleTransitLeg.java | 10 +++--- .../FixedStopTimeOnServiceDateImpl.java | 14 ++++---- .../apis/gtfs/datafetchers/LegImpl.java | 6 ++-- .../apis/gtfs/datafetchers/PlaceImpl.java | 6 ++-- .../gtfs/generated/GraphQLDataFetchers.java | 35 +++++++++---------- .../apis/gtfs/generated/graphql-codegen.yml | 4 +-- .../apis/gtfs/model/ArrivalDepartureTime.java | 29 --------------- ...me.java => FixedArrivalDepartureTime.java} | 21 +++++++---- .../model/plan/FrequencyTransitLeg.java | 8 +++-- .../org/opentripplanner/model/plan/Leg.java | 4 +-- .../model/plan/ScheduledTransitLeg.java | 12 +++---- .../model/plan/StopArrival.java | 8 ++--- .../model/plan/StopArrivalMapper.java | 8 ++--- .../opentripplanner/model/plan/StreetLeg.java | 8 ++--- .../model/plan/UnknownTransitPathLeg.java | 8 ++--- .../opentripplanner/apis/gtfs/schema.graphqls | 30 ++++++++-------- 17 files changed, 99 insertions(+), 118 deletions(-) delete mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java rename application/src/main/java/org/opentripplanner/model/plan/{LegTime.java => FixedArrivalDepartureTime.java} (61%) diff --git a/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java b/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java index d3608ccf8d9..5d27ea650ed 100644 --- a/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java +++ b/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java @@ -10,7 +10,7 @@ import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.LegTime; +import org.opentripplanner.model.plan.FixedArrivalDepartureTime; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.TransitLeg; @@ -55,12 +55,12 @@ public Trip getTrip() { } @Override - public LegTime start() { + public FixedArrivalDepartureTime start() { return first.start(); } @Override - public LegTime end() { + public FixedArrivalDepartureTime end() { return second.end(); } diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java b/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java index cf6c229c46f..14988f4cdc2 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java @@ -15,7 +15,7 @@ import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.LegTime; +import org.opentripplanner.model.plan.FixedArrivalDepartureTime; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.TransitLeg; @@ -87,13 +87,13 @@ public Accessibility getTripWheelchairAccessibility() { } @Override - public LegTime start() { - return LegTime.ofStatic(startTime); + public FixedArrivalDepartureTime start() { + return FixedArrivalDepartureTime.ofStatic(startTime); } @Override - public LegTime end() { - return LegTime.ofStatic(endTime); + public FixedArrivalDepartureTime end() { + return FixedArrivalDepartureTime.ofStatic(endTime); } @Override diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java index f6e8c62b223..a7551e487f5 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java @@ -7,16 +7,16 @@ import java.time.ZonedDateTime; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; -import org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.TripTimeOnDate; +import org.opentripplanner.model.plan.FixedArrivalDepartureTime; import org.opentripplanner.transit.service.TransitService; public class FixedStopTimeOnServiceDateImpl implements GraphQLDataFetchers.GraphQLFixedStopTimeOnServiceDate { @Override - public DataFetcher arrival() { + public DataFetcher arrival() { return environment -> { var tripTime = getSource(environment); var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledArrival()); @@ -24,13 +24,13 @@ public DataFetcher arrival() { return null; } return tripTime.isRealtime() - ? ArrivalDepartureTime.of(scheduledTime, tripTime.getArrivalDelay()) - : ArrivalDepartureTime.ofStatic(scheduledTime); + ? FixedArrivalDepartureTime.of(scheduledTime, tripTime.getArrivalDelay()) + : FixedArrivalDepartureTime.ofStatic(scheduledTime); }; } @Override - public DataFetcher departure() { + public DataFetcher departure() { return environment -> { var tripTime = getSource(environment); var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledDeparture()); @@ -38,8 +38,8 @@ public DataFetcher departure() { return null; } return tripTime.isRealtime() - ? ArrivalDepartureTime.of(scheduledTime, tripTime.getDepartureDelay()) - : ArrivalDepartureTime.ofStatic(scheduledTime); + ? FixedArrivalDepartureTime.of(scheduledTime, tripTime.getDepartureDelay()) + : FixedArrivalDepartureTime.ofStatic(scheduledTime); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java index 4db347ee784..59fc154a7c4 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java @@ -17,8 +17,8 @@ import org.opentripplanner.ext.ridehailing.model.RideHailingLeg; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.fare.FareProductUse; +import org.opentripplanner.model.plan.FixedArrivalDepartureTime; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.LegTime; import org.opentripplanner.model.plan.ScheduledTransitLeg; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.StreetLeg; @@ -81,7 +81,7 @@ public DataFetcher duration() { } @Override - public DataFetcher end() { + public DataFetcher end() { return environment -> getSource(environment).end(); } @@ -227,7 +227,7 @@ public DataFetcher serviceDate() { } @Override - public DataFetcher start() { + public DataFetcher start() { return environment -> getSource(environment).start(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java index 145321f809c..5c8cc44147a 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java @@ -7,7 +7,7 @@ import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.apis.gtfs.model.StopPosition.PositionAtStop; import org.opentripplanner.framework.graphql.GraphQLUtils; -import org.opentripplanner.model.plan.LegTime; +import org.opentripplanner.model.plan.FixedArrivalDepartureTime; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.VertexType; @@ -19,7 +19,7 @@ public class PlaceImpl implements GraphQLDataFetchers.GraphQLPlace { @Override - public DataFetcher arrival() { + public DataFetcher arrival() { return environment -> getSource(environment).arrival; } @@ -58,7 +58,7 @@ public DataFetcher carPark() { @Deprecated @Override - public DataFetcher departure() { + public DataFetcher departure() { return environment -> getSource(environment).departure; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 60e0b1e3fbc..7053e185d70 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -23,7 +23,6 @@ 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.ArrivalDepartureTime; import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.model.PlanPageInfo; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; @@ -40,9 +39,9 @@ import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.fare.RiderCategory; import org.opentripplanner.model.plan.Emissions; +import org.opentripplanner.model.plan.FixedArrivalDepartureTime; 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; @@ -143,16 +142,6 @@ public interface GraphQLAlert { /** Entity related to an alert */ public interface GraphQLAlertEntity extends TypeResolver {} - /** - * Timing of an arrival or a departure to or from a stop. May contain real-time information if - * available. - */ - public interface GraphQLArrivalDepartureTime { - public DataFetcher estimated(); - - public DataFetcher scheduledTime(); - } - /** Bike park represents a location where bicycles can be parked. */ public interface GraphQLBikePark { public DataFetcher bikeParkId(); @@ -421,14 +410,24 @@ public interface GraphQLFeedPublisher { public DataFetcher url(); } + /** + * Timing of an arrival or a departure to or from a stop. May contain real-time information if + * available. This is used when there is a known scheduled time. + */ + public interface GraphQLFixedArrivalDepartureTime { + public DataFetcher estimated(); + + public DataFetcher scheduledTime(); + } + /** * Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. * This can include realtime estimates. */ public interface GraphQLFixedStopTimeOnServiceDate { - public DataFetcher arrival(); + public DataFetcher arrival(); - public DataFetcher departure(); + public DataFetcher departure(); public DataFetcher stop(); } @@ -509,7 +508,7 @@ public interface GraphQLLeg { public DataFetcher duration(); - public DataFetcher end(); + public DataFetcher end(); public DataFetcher endTime(); @@ -553,7 +552,7 @@ public interface GraphQLLeg { public DataFetcher serviceDate(); - public DataFetcher start(); + public DataFetcher start(); public DataFetcher startTime(); @@ -661,7 +660,7 @@ public interface GraphQLPattern { } public interface GraphQLPlace { - public DataFetcher arrival(); + public DataFetcher arrival(); public DataFetcher arrivalTime(); @@ -671,7 +670,7 @@ public interface GraphQLPlace { public DataFetcher carPark(); - public DataFetcher departure(); + public DataFetcher departure(); public DataFetcher departureTime(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 66b701c5e01..5b02cd86f89 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -63,7 +63,7 @@ config: FixedTripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate TripOnServiceDateConnection: graphql.relay.Connection#Connection TripOnServiceDateEdge: graphql.relay.Edge#Edge - ArrivalDepartureTime: org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime#ArrivalDepartureTime + FixedArrivalDepartureTime: org.opentripplanner.model.plan.FixedArrivalDepartureTime#FixedArrivalDepartureTime debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step @@ -74,7 +74,7 @@ config: InputField: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField#GraphQLInputField Itinerary: org.opentripplanner.model.plan.Itinerary#Itinerary Leg: org.opentripplanner.model.plan.Leg#Leg - LegTime: org.opentripplanner.model.plan.LegTime#LegTime + LegTime: org.opentripplanner.model.plan.FixedArrivalDepartureTime#FixedArrivalDepartureTime Mode: String OccupancyStatus: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLOccupancyStatus#GraphQLOccupancyStatus TransitMode: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode#GraphQLTransitMode diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java b/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java deleted file mode 100644 index 3a801537dd1..00000000000 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.opentripplanner.apis.gtfs.model; - -import java.time.Duration; -import java.time.ZonedDateTime; -import javax.annotation.Nullable; - -/** - * Timing of an arrival or a departure to or from a stop. May contain real-time information - * if available. - */ -public record ArrivalDepartureTime( - ZonedDateTime scheduledTime, - @Nullable RealTimeEstimate estimated -) { - public static ArrivalDepartureTime of(ZonedDateTime realtime, int delaySecs) { - var delay = Duration.ofSeconds(delaySecs); - return new ArrivalDepartureTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay)); - } - - public static ArrivalDepartureTime ofStatic(ZonedDateTime staticTime) { - return new ArrivalDepartureTime(staticTime, null); - } - - /** - * Realtime information about a vehicle at a certain place. - * @param delay Delay or "earliness" of a vehicle. Earliness is expressed as a negative number. - */ - record RealTimeEstimate(ZonedDateTime time, Duration delay) {} -} diff --git a/application/src/main/java/org/opentripplanner/model/plan/LegTime.java b/application/src/main/java/org/opentripplanner/model/plan/FixedArrivalDepartureTime.java similarity index 61% rename from application/src/main/java/org/opentripplanner/model/plan/LegTime.java rename to application/src/main/java/org/opentripplanner/model/plan/FixedArrivalDepartureTime.java index 71ced95942a..f27644b59a6 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/LegTime.java +++ b/application/src/main/java/org/opentripplanner/model/plan/FixedArrivalDepartureTime.java @@ -6,20 +6,27 @@ import javax.annotation.Nullable; /** - * A scheduled time of a transit vehicle at a certain location with a optional realtime information. + * A scheduled time of a transit vehicle at a certain location with an optional realtime + * information. */ -public record LegTime(ZonedDateTime scheduledTime, @Nullable RealTimeEstimate estimated) { - public LegTime { +public record FixedArrivalDepartureTime( + ZonedDateTime scheduledTime, + @Nullable RealTimeEstimate estimated +) { + public FixedArrivalDepartureTime { Objects.requireNonNull(scheduledTime); } - public static LegTime of(ZonedDateTime realtime, int delaySecs) { + public static FixedArrivalDepartureTime of(ZonedDateTime realtime, int delaySecs) { var delay = Duration.ofSeconds(delaySecs); - return new LegTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay)); + return new FixedArrivalDepartureTime( + realtime.minus(delay), + new RealTimeEstimate(realtime, delay) + ); } - public static LegTime ofStatic(ZonedDateTime staticTime) { - return new LegTime(staticTime, null); + public static FixedArrivalDepartureTime ofStatic(ZonedDateTime staticTime) { + return new FixedArrivalDepartureTime(staticTime, null); } /** diff --git a/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java b/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java index 48c4b1b7b87..19a2ce2c491 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java @@ -55,8 +55,12 @@ public List getIntermediateStops() { StopArrival visit = new StopArrival( Place.forStop(stop), - LegTime.ofStatic(ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, arrivalTime)), - LegTime.ofStatic(ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, departureTime)), + FixedArrivalDepartureTime.ofStatic( + ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, arrivalTime) + ), + FixedArrivalDepartureTime.ofStatic( + ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, departureTime) + ), i, tripTimes.gtfsSequenceOfStopIndex(i) ); diff --git a/application/src/main/java/org/opentripplanner/model/plan/Leg.java b/application/src/main/java/org/opentripplanner/model/plan/Leg.java index 2a4483d7cd2..718b3ba5360 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/Leg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/Leg.java @@ -209,12 +209,12 @@ default Accessibility getTripWheelchairAccessibility() { /** * The time (including realtime information) when the leg starts. */ - LegTime start(); + FixedArrivalDepartureTime start(); /** * The time (including realtime information) when the leg ends. */ - LegTime end(); + FixedArrivalDepartureTime end(); /** * The date and time this leg begins. diff --git a/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java b/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java index 068d6675eec..784232a5060 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java @@ -161,20 +161,20 @@ public Accessibility getTripWheelchairAccessibility() { } @Override - public LegTime start() { + public FixedArrivalDepartureTime start() { if (isRealTimeUpdated()) { - return LegTime.of(startTime, getDepartureDelay()); + return FixedArrivalDepartureTime.of(startTime, getDepartureDelay()); } else { - return LegTime.ofStatic(startTime); + return FixedArrivalDepartureTime.ofStatic(startTime); } } @Override - public LegTime end() { + public FixedArrivalDepartureTime end() { if (isRealTimeUpdated()) { - return LegTime.of(endTime, getArrivalDelay()); + return FixedArrivalDepartureTime.of(endTime, getArrivalDelay()); } else { - return LegTime.ofStatic(endTime); + return FixedArrivalDepartureTime.ofStatic(endTime); } } diff --git a/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java b/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java index 489b122da09..bd0bc52cd6e 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java +++ b/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java @@ -9,8 +9,8 @@ public final class StopArrival { public final Place place; - public final LegTime arrival; - public final LegTime departure; + public final FixedArrivalDepartureTime arrival; + public final FixedArrivalDepartureTime departure; public final Integer stopPosInPattern; public final Integer gtfsStopSequence; @@ -24,8 +24,8 @@ public final class StopArrival { */ public StopArrival( Place place, - LegTime arrival, - LegTime departure, + FixedArrivalDepartureTime arrival, + FixedArrivalDepartureTime departure, Integer stopPosInPattern, Integer gtfsStopSequence ) { diff --git a/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java b/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java index 25bab2a7c3f..23d75df357c 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java +++ b/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java @@ -34,12 +34,12 @@ StopArrival map(int i, StopLocation stop, boolean realTime) { tripTimes.getDepartureTime(i) ); - var arrival = LegTime.ofStatic(arrivalTime); - var departure = LegTime.ofStatic(departureTime); + var arrival = FixedArrivalDepartureTime.ofStatic(arrivalTime); + var departure = FixedArrivalDepartureTime.ofStatic(departureTime); if (realTime) { - arrival = LegTime.of(arrivalTime, tripTimes.getArrivalDelay(i)); - departure = LegTime.of(departureTime, tripTimes.getDepartureDelay(i)); + arrival = FixedArrivalDepartureTime.of(arrivalTime, tripTimes.getArrivalDelay(i)); + departure = FixedArrivalDepartureTime.of(departureTime, tripTimes.getDepartureDelay(i)); } return new StopArrival( diff --git a/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java b/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java index 6384159a4ec..15dfda93079 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java @@ -157,13 +157,13 @@ public boolean hasSameMode(Leg other) { } @Override - public LegTime start() { - return LegTime.ofStatic(startTime); + public FixedArrivalDepartureTime start() { + return FixedArrivalDepartureTime.ofStatic(startTime); } @Override - public LegTime end() { - return LegTime.ofStatic(endTime); + public FixedArrivalDepartureTime end() { + return FixedArrivalDepartureTime.ofStatic(endTime); } @Override diff --git a/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java b/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java index 76179a8ee7c..c6ec7f8c45b 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java @@ -70,13 +70,13 @@ public boolean hasSameMode(Leg other) { } @Override - public LegTime start() { - return LegTime.ofStatic(startTime); + public FixedArrivalDepartureTime start() { + return FixedArrivalDepartureTime.ofStatic(startTime); } @Override - public LegTime end() { - return LegTime.ofStatic(endTime); + public FixedArrivalDepartureTime end() { + return FixedArrivalDepartureTime.ofStatic(endTime); } @Override diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 4cfe9fa6fd0..da7bec99199 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -157,17 +157,6 @@ type Alert implements Node { trip: Trip @deprecated(reason : "Alert can have multiple affected entities now instead of there being duplicate alerts\nfor different entities. This will return only one of the affected trips.\nUse entities instead.") } -""" -Timing of an arrival or a departure to or from a stop. May contain real-time information if -available. -""" -type ArrivalDepartureTime { - "The estimated time of the event. If no real-time information is available, this is null." - estimated: RealTimeEstimate - "The scheduled time of the event." - scheduledTime: OffsetDateTime! -} - "Bike park represents a location where bicycles can be parked." type BikePark implements Node & PlaceInterface { "ID of the bike park" @@ -550,21 +539,34 @@ type FeedPublisher { url: String! } +""" +Timing of an arrival or a departure to or from a stop. May contain real-time information if +available. This is used when there is a known scheduled time. +""" +type FixedArrivalDepartureTime { + "The estimated time of the event. If no real-time information is available, this is null." + estimated: RealTimeEstimate + "The scheduled time of the event." + scheduledTime: OffsetDateTime! +} + """ Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. This can include realtime estimates. """ type FixedStopTimeOnServiceDate { "Scheduled arrival time to the stop and a realtime estimate, if one exists." - arrival: ArrivalDepartureTime + arrival: FixedArrivalDepartureTime "Scheduled departure time from the stop and a realtime estimate, if one exists." - departure: ArrivalDepartureTime + departure: FixedArrivalDepartureTime "The stop where this arrival/departure happens" stop: Stop } "A fixed (i.e. not flexible or frequency based) trip on a specific service date" type FixedTripOnServiceDate { + "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." + end: FixedStopTimeOnServiceDate! """ The service date when the trip occurs. @@ -573,8 +575,6 @@ type FixedTripOnServiceDate { on Sunday morning at 1am to 3am, might have the previous Saturday's service date. """ serviceDate: LocalDate! - "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." - end: FixedStopTimeOnServiceDate! "Information related to trip's scheduled departure from the first stop. Can contain realtime information." start: FixedStopTimeOnServiceDate! "List of times when this trip arrives to or departs from a stop and information related to the stop" From 672a96830d0614f198334460bc52f58e29ee902c Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Wed, 13 Nov 2024 15:17:02 +0200 Subject: [PATCH 036/162] Make TripOnServiceDate an interface and rename some types --- .../impl/CombinedInterlinedTransitLeg.java | 6 +- .../ext/flex/FlexibleTransitLeg.java | 10 +- .../apis/gtfs/GtfsGraphQLIndex.java | 8 +- .../apis/gtfs/datafetchers/LegImpl.java | 6 +- .../apis/gtfs/datafetchers/PlaceImpl.java | 6 +- ....java => RegularRealTimeStopTimeImpl.java} | 18 +-- ...java => RegularTripOnServiceDateImpl.java} | 4 +- .../TripOnServiceDateTypeResolver.java | 2 +- .../gtfs/generated/GraphQLDataFetchers.java | 86 ++++++++------- .../apis/gtfs/generated/graphql-codegen.yml | 8 +- .../model/plan/FrequencyTransitLeg.java | 4 +- .../org/opentripplanner/model/plan/Leg.java | 4 +- ....java => RegularArrivalDepartureTime.java} | 12 +- .../model/plan/ScheduledTransitLeg.java | 12 +- .../model/plan/StopArrival.java | 8 +- .../model/plan/StopArrivalMapper.java | 8 +- .../opentripplanner/model/plan/StreetLeg.java | 8 +- .../model/plan/UnknownTransitPathLeg.java | 8 +- .../opentripplanner/apis/gtfs/schema.graphqls | 103 ++++++++++-------- .../apis/gtfs/queries/canceled-trips.graphql | 2 +- 20 files changed, 168 insertions(+), 155 deletions(-) rename application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{FixedStopTimeOnServiceDateImpl.java => RegularRealTimeStopTimeImpl.java} (76%) rename application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{FixedTripOnServiceDateImpl.java => RegularTripOnServiceDateImpl.java} (97%) rename application/src/main/java/org/opentripplanner/model/plan/{FixedArrivalDepartureTime.java => RegularArrivalDepartureTime.java} (74%) diff --git a/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java b/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java index 5d27ea650ed..a53c3a689c2 100644 --- a/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java +++ b/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java @@ -10,7 +10,7 @@ import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.FixedArrivalDepartureTime; +import org.opentripplanner.model.plan.RegularArrivalDepartureTime; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.TransitLeg; @@ -55,12 +55,12 @@ public Trip getTrip() { } @Override - public FixedArrivalDepartureTime start() { + public RegularArrivalDepartureTime start() { return first.start(); } @Override - public FixedArrivalDepartureTime end() { + public RegularArrivalDepartureTime end() { return second.end(); } diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java b/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java index 14988f4cdc2..9c59da7d39f 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java @@ -15,7 +15,7 @@ import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.FixedArrivalDepartureTime; +import org.opentripplanner.model.plan.RegularArrivalDepartureTime; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.TransitLeg; @@ -87,13 +87,13 @@ public Accessibility getTripWheelchairAccessibility() { } @Override - public FixedArrivalDepartureTime start() { - return FixedArrivalDepartureTime.ofStatic(startTime); + public RegularArrivalDepartureTime start() { + return RegularArrivalDepartureTime.ofStatic(startTime); } @Override - public FixedArrivalDepartureTime end() { - return FixedArrivalDepartureTime.ofStatic(endTime); + public RegularArrivalDepartureTime end() { + return RegularArrivalDepartureTime.ofStatic(endTime); } @Override diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 9aeed1e9472..014acb97398 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -40,8 +40,6 @@ import org.opentripplanner.apis.gtfs.datafetchers.FareProductTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.FareProductUseImpl; import org.opentripplanner.apis.gtfs.datafetchers.FeedImpl; -import org.opentripplanner.apis.gtfs.datafetchers.FixedStopTimeOnServiceDateImpl; -import org.opentripplanner.apis.gtfs.datafetchers.FixedTripOnServiceDateImpl; import org.opentripplanner.apis.gtfs.datafetchers.GeometryImpl; import org.opentripplanner.apis.gtfs.datafetchers.ItineraryImpl; import org.opentripplanner.apis.gtfs.datafetchers.LegImpl; @@ -54,6 +52,8 @@ import org.opentripplanner.apis.gtfs.datafetchers.PlanConnectionImpl; import org.opentripplanner.apis.gtfs.datafetchers.PlanImpl; import org.opentripplanner.apis.gtfs.datafetchers.QueryTypeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RegularRealTimeStopTimeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RegularTripOnServiceDateImpl; import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleImpl; import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleTypeImpl; import org.opentripplanner.apis.gtfs.datafetchers.RideHailingEstimateImpl; @@ -182,8 +182,8 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(CurrencyImpl.class)) .type(typeWiring.build(FareProductUseImpl.class)) .type(typeWiring.build(DefaultFareProductImpl.class)) - .type(typeWiring.build(FixedTripOnServiceDateImpl.class)) - .type(typeWiring.build(FixedStopTimeOnServiceDateImpl.class)) + .type(typeWiring.build(RegularTripOnServiceDateImpl.class)) + .type(typeWiring.build(RegularRealTimeStopTimeImpl.class)) .type(typeWiring.build(TripOccupancyImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java index 59fc154a7c4..c90339f1016 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java @@ -17,8 +17,8 @@ import org.opentripplanner.ext.ridehailing.model.RideHailingLeg; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.fare.FareProductUse; -import org.opentripplanner.model.plan.FixedArrivalDepartureTime; import org.opentripplanner.model.plan.Leg; +import org.opentripplanner.model.plan.RegularArrivalDepartureTime; import org.opentripplanner.model.plan.ScheduledTransitLeg; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.StreetLeg; @@ -81,7 +81,7 @@ public DataFetcher duration() { } @Override - public DataFetcher end() { + public DataFetcher end() { return environment -> getSource(environment).end(); } @@ -227,7 +227,7 @@ public DataFetcher serviceDate() { } @Override - public DataFetcher start() { + public DataFetcher start() { return environment -> getSource(environment).start(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java index 5c8cc44147a..b732bb14887 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java @@ -7,8 +7,8 @@ import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.apis.gtfs.model.StopPosition.PositionAtStop; import org.opentripplanner.framework.graphql.GraphQLUtils; -import org.opentripplanner.model.plan.FixedArrivalDepartureTime; import org.opentripplanner.model.plan.Place; +import org.opentripplanner.model.plan.RegularArrivalDepartureTime; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.VertexType; import org.opentripplanner.routing.vehicle_parking.VehicleParking; @@ -19,7 +19,7 @@ public class PlaceImpl implements GraphQLDataFetchers.GraphQLPlace { @Override - public DataFetcher arrival() { + public DataFetcher arrival() { return environment -> getSource(environment).arrival; } @@ -58,7 +58,7 @@ public DataFetcher carPark() { @Deprecated @Override - public DataFetcher departure() { + public DataFetcher departure() { return environment -> getSource(environment).departure; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java similarity index 76% rename from application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java rename to application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java index a7551e487f5..2f524bdea34 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedStopTimeOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java @@ -9,14 +9,14 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.TripTimeOnDate; -import org.opentripplanner.model.plan.FixedArrivalDepartureTime; +import org.opentripplanner.model.plan.RegularArrivalDepartureTime; import org.opentripplanner.transit.service.TransitService; -public class FixedStopTimeOnServiceDateImpl - implements GraphQLDataFetchers.GraphQLFixedStopTimeOnServiceDate { +public class RegularRealTimeStopTimeImpl + implements GraphQLDataFetchers.GraphQLRegularRealTimeStopTime { @Override - public DataFetcher arrival() { + public DataFetcher arrival() { return environment -> { var tripTime = getSource(environment); var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledArrival()); @@ -24,13 +24,13 @@ public DataFetcher arrival() { return null; } return tripTime.isRealtime() - ? FixedArrivalDepartureTime.of(scheduledTime, tripTime.getArrivalDelay()) - : FixedArrivalDepartureTime.ofStatic(scheduledTime); + ? RegularArrivalDepartureTime.of(scheduledTime, tripTime.getArrivalDelay()) + : RegularArrivalDepartureTime.ofStatic(scheduledTime); }; } @Override - public DataFetcher departure() { + public DataFetcher departure() { return environment -> { var tripTime = getSource(environment); var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledDeparture()); @@ -38,8 +38,8 @@ public DataFetcher departure() { return null; } return tripTime.isRealtime() - ? FixedArrivalDepartureTime.of(scheduledTime, tripTime.getDepartureDelay()) - : FixedArrivalDepartureTime.ofStatic(scheduledTime); + ? RegularArrivalDepartureTime.of(scheduledTime, tripTime.getDepartureDelay()) + : RegularArrivalDepartureTime.ofStatic(scheduledTime); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularTripOnServiceDateImpl.java similarity index 97% rename from application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java rename to application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularTripOnServiceDateImpl.java index c34c25e3f1d..54cda759fd4 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/FixedTripOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularTripOnServiceDateImpl.java @@ -16,8 +16,8 @@ import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.service.TransitService; -public class FixedTripOnServiceDateImpl - implements GraphQLDataFetchers.GraphQLFixedTripOnServiceDate { +public class RegularTripOnServiceDateImpl + implements GraphQLDataFetchers.GraphQLRegularTripOnServiceDate { @Override public DataFetcher serviceDate() { diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java index aaf8d6dae14..570177768f0 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java @@ -14,7 +14,7 @@ public GraphQLObjectType getType(TypeResolutionEnvironment environment) { GraphQLSchema schema = environment.getSchema(); if (o instanceof TripOnServiceDate) { - return schema.getObjectType("FixedTripOnServiceDate"); + return schema.getObjectType("RegularTripOnServiceDate"); } return null; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 7053e185d70..f89a94f7a31 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -39,9 +39,9 @@ import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.fare.RiderCategory; import org.opentripplanner.model.plan.Emissions; -import org.opentripplanner.model.plan.FixedArrivalDepartureTime; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; +import org.opentripplanner.model.plan.RegularArrivalDepartureTime; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.routing.alertpatch.TransitAlert; @@ -410,41 +410,6 @@ public interface GraphQLFeedPublisher { public DataFetcher url(); } - /** - * Timing of an arrival or a departure to or from a stop. May contain real-time information if - * available. This is used when there is a known scheduled time. - */ - public interface GraphQLFixedArrivalDepartureTime { - public DataFetcher estimated(); - - public DataFetcher scheduledTime(); - } - - /** - * Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. - * This can include realtime estimates. - */ - public interface GraphQLFixedStopTimeOnServiceDate { - public DataFetcher arrival(); - - public DataFetcher departure(); - - public DataFetcher stop(); - } - - /** A fixed (i.e. not flexible or frequency based) trip on a specific service date */ - public interface GraphQLFixedTripOnServiceDate { - public DataFetcher end(); - - public DataFetcher serviceDate(); - - public DataFetcher start(); - - public DataFetcher> stopTimes(); - - public DataFetcher trip(); - } - public interface GraphQLGeometry { public DataFetcher length(); @@ -508,7 +473,7 @@ public interface GraphQLLeg { public DataFetcher duration(); - public DataFetcher end(); + public DataFetcher end(); public DataFetcher endTime(); @@ -552,7 +517,7 @@ public interface GraphQLLeg { public DataFetcher serviceDate(); - public DataFetcher start(); + public DataFetcher start(); public DataFetcher startTime(); @@ -660,7 +625,7 @@ public interface GraphQLPattern { } public interface GraphQLPlace { - public DataFetcher arrival(); + public DataFetcher arrival(); public DataFetcher arrivalTime(); @@ -670,7 +635,7 @@ public interface GraphQLPlace { public DataFetcher carPark(); - public DataFetcher departure(); + public DataFetcher departure(); public DataFetcher departureTime(); @@ -881,6 +846,41 @@ public interface GraphQLRealTimeEstimate { public DataFetcher time(); } + /** + * Timing of an arrival or a departure to or from a stop. May contain real-time information if + * available. This is used when there is a known scheduled time. + */ + public interface GraphQLRegularRealTimeArrivalDepartureTime { + public DataFetcher estimated(); + + public DataFetcher scheduledTime(); + } + + /** + * Regular real-time stop time represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. + * This can include real-time estimates. + */ + public interface GraphQLRegularRealTimeStopTime { + public DataFetcher arrival(); + + public DataFetcher departure(); + + public DataFetcher stop(); + } + + /** A regular (i.e. not flexible) trip on a specific service date */ + public interface GraphQLRegularTripOnServiceDate { + public DataFetcher end(); + + public DataFetcher serviceDate(); + + public DataFetcher start(); + + public DataFetcher> stopTimes(); + + public DataFetcher trip(); + } + /** Rental vehicle represents a vehicle that belongs to a rental network. */ public interface GraphQLRentalVehicle { public DataFetcher allowPickupNow(); @@ -1240,7 +1240,11 @@ public interface GraphQLTripOccupancy { } /** An instance of a trip on a service date. */ - public interface GraphQLTripOnServiceDate extends TypeResolver {} + public interface GraphQLTripOnServiceDate extends TypeResolver { + public default DataFetcher serviceDate() { + return null; + } + } /** * A connection to a list of trips on service dates that follows diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 5b02cd86f89..1df05e0fb5a 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -59,11 +59,11 @@ config: ContactInfo: org.opentripplanner.transit.model.organization.ContactInfo Cluster: Object Coordinates: org.locationtech.jts.geom.Coordinate#Coordinate - FixedStopTimeOnServiceDate: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate - FixedTripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate + RegularRealTimeStopTime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate + RegularTripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate TripOnServiceDateConnection: graphql.relay.Connection#Connection TripOnServiceDateEdge: graphql.relay.Edge#Edge - FixedArrivalDepartureTime: org.opentripplanner.model.plan.FixedArrivalDepartureTime#FixedArrivalDepartureTime + RegularRealTimeArrivalDepartureTime: org.opentripplanner.model.plan.RegularArrivalDepartureTime#RegularArrivalDepartureTime debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step @@ -74,7 +74,7 @@ config: InputField: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField#GraphQLInputField Itinerary: org.opentripplanner.model.plan.Itinerary#Itinerary Leg: org.opentripplanner.model.plan.Leg#Leg - LegTime: org.opentripplanner.model.plan.FixedArrivalDepartureTime#FixedArrivalDepartureTime + LegTime: org.opentripplanner.model.plan.RegularArrivalDepartureTime#RegularArrivalDepartureTime Mode: String OccupancyStatus: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLOccupancyStatus#GraphQLOccupancyStatus TransitMode: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode#GraphQLTransitMode diff --git a/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java b/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java index 19a2ce2c491..042cb3ccb87 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java @@ -55,10 +55,10 @@ public List getIntermediateStops() { StopArrival visit = new StopArrival( Place.forStop(stop), - FixedArrivalDepartureTime.ofStatic( + RegularArrivalDepartureTime.ofStatic( ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, arrivalTime) ), - FixedArrivalDepartureTime.ofStatic( + RegularArrivalDepartureTime.ofStatic( ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, departureTime) ), i, diff --git a/application/src/main/java/org/opentripplanner/model/plan/Leg.java b/application/src/main/java/org/opentripplanner/model/plan/Leg.java index 718b3ba5360..18b4c04fc1f 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/Leg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/Leg.java @@ -209,12 +209,12 @@ default Accessibility getTripWheelchairAccessibility() { /** * The time (including realtime information) when the leg starts. */ - FixedArrivalDepartureTime start(); + RegularArrivalDepartureTime start(); /** * The time (including realtime information) when the leg ends. */ - FixedArrivalDepartureTime end(); + RegularArrivalDepartureTime end(); /** * The date and time this leg begins. diff --git a/application/src/main/java/org/opentripplanner/model/plan/FixedArrivalDepartureTime.java b/application/src/main/java/org/opentripplanner/model/plan/RegularArrivalDepartureTime.java similarity index 74% rename from application/src/main/java/org/opentripplanner/model/plan/FixedArrivalDepartureTime.java rename to application/src/main/java/org/opentripplanner/model/plan/RegularArrivalDepartureTime.java index f27644b59a6..370e91934e1 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/FixedArrivalDepartureTime.java +++ b/application/src/main/java/org/opentripplanner/model/plan/RegularArrivalDepartureTime.java @@ -9,24 +9,24 @@ * A scheduled time of a transit vehicle at a certain location with an optional realtime * information. */ -public record FixedArrivalDepartureTime( +public record RegularArrivalDepartureTime( ZonedDateTime scheduledTime, @Nullable RealTimeEstimate estimated ) { - public FixedArrivalDepartureTime { + public RegularArrivalDepartureTime { Objects.requireNonNull(scheduledTime); } - public static FixedArrivalDepartureTime of(ZonedDateTime realtime, int delaySecs) { + public static RegularArrivalDepartureTime of(ZonedDateTime realtime, int delaySecs) { var delay = Duration.ofSeconds(delaySecs); - return new FixedArrivalDepartureTime( + return new RegularArrivalDepartureTime( realtime.minus(delay), new RealTimeEstimate(realtime, delay) ); } - public static FixedArrivalDepartureTime ofStatic(ZonedDateTime staticTime) { - return new FixedArrivalDepartureTime(staticTime, null); + public static RegularArrivalDepartureTime ofStatic(ZonedDateTime staticTime) { + return new RegularArrivalDepartureTime(staticTime, null); } /** diff --git a/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java b/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java index 784232a5060..07c69765af7 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java @@ -161,20 +161,20 @@ public Accessibility getTripWheelchairAccessibility() { } @Override - public FixedArrivalDepartureTime start() { + public RegularArrivalDepartureTime start() { if (isRealTimeUpdated()) { - return FixedArrivalDepartureTime.of(startTime, getDepartureDelay()); + return RegularArrivalDepartureTime.of(startTime, getDepartureDelay()); } else { - return FixedArrivalDepartureTime.ofStatic(startTime); + return RegularArrivalDepartureTime.ofStatic(startTime); } } @Override - public FixedArrivalDepartureTime end() { + public RegularArrivalDepartureTime end() { if (isRealTimeUpdated()) { - return FixedArrivalDepartureTime.of(endTime, getArrivalDelay()); + return RegularArrivalDepartureTime.of(endTime, getArrivalDelay()); } else { - return FixedArrivalDepartureTime.ofStatic(endTime); + return RegularArrivalDepartureTime.ofStatic(endTime); } } diff --git a/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java b/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java index bd0bc52cd6e..d4914b0691b 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java +++ b/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java @@ -9,8 +9,8 @@ public final class StopArrival { public final Place place; - public final FixedArrivalDepartureTime arrival; - public final FixedArrivalDepartureTime departure; + public final RegularArrivalDepartureTime arrival; + public final RegularArrivalDepartureTime departure; public final Integer stopPosInPattern; public final Integer gtfsStopSequence; @@ -24,8 +24,8 @@ public final class StopArrival { */ public StopArrival( Place place, - FixedArrivalDepartureTime arrival, - FixedArrivalDepartureTime departure, + RegularArrivalDepartureTime arrival, + RegularArrivalDepartureTime departure, Integer stopPosInPattern, Integer gtfsStopSequence ) { diff --git a/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java b/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java index 23d75df357c..5377ee809b3 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java +++ b/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java @@ -34,12 +34,12 @@ StopArrival map(int i, StopLocation stop, boolean realTime) { tripTimes.getDepartureTime(i) ); - var arrival = FixedArrivalDepartureTime.ofStatic(arrivalTime); - var departure = FixedArrivalDepartureTime.ofStatic(departureTime); + var arrival = RegularArrivalDepartureTime.ofStatic(arrivalTime); + var departure = RegularArrivalDepartureTime.ofStatic(departureTime); if (realTime) { - arrival = FixedArrivalDepartureTime.of(arrivalTime, tripTimes.getArrivalDelay(i)); - departure = FixedArrivalDepartureTime.of(departureTime, tripTimes.getDepartureDelay(i)); + arrival = RegularArrivalDepartureTime.of(arrivalTime, tripTimes.getArrivalDelay(i)); + departure = RegularArrivalDepartureTime.of(departureTime, tripTimes.getDepartureDelay(i)); } return new StopArrival( diff --git a/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java b/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java index 15dfda93079..62e03ee0acb 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java @@ -157,13 +157,13 @@ public boolean hasSameMode(Leg other) { } @Override - public FixedArrivalDepartureTime start() { - return FixedArrivalDepartureTime.ofStatic(startTime); + public RegularArrivalDepartureTime start() { + return RegularArrivalDepartureTime.ofStatic(startTime); } @Override - public FixedArrivalDepartureTime end() { - return FixedArrivalDepartureTime.ofStatic(endTime); + public RegularArrivalDepartureTime end() { + return RegularArrivalDepartureTime.ofStatic(endTime); } @Override diff --git a/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java b/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java index c6ec7f8c45b..358cfd9fe05 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java @@ -70,13 +70,13 @@ public boolean hasSameMode(Leg other) { } @Override - public FixedArrivalDepartureTime start() { - return FixedArrivalDepartureTime.ofStatic(startTime); + public RegularArrivalDepartureTime start() { + return RegularArrivalDepartureTime.ofStatic(startTime); } @Override - public FixedArrivalDepartureTime end() { - return FixedArrivalDepartureTime.ofStatic(endTime); + public RegularArrivalDepartureTime end() { + return RegularArrivalDepartureTime.ofStatic(endTime); } @Override diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 0496bd4448c..8cbf5fb91fa 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -70,14 +70,23 @@ interface PlaceInterface { lon: Float } +"An instance of a trip on a service date." +interface TripOnServiceDate { + """ + The service date when the trip occurs. + + **Note**: A service date is a technical term useful for transit planning purposes and might not + correspond to a how a passenger thinks of a calendar date. For example, a night bus running + on Sunday morning at 1am to 3am, might have the previous Saturday's service date. + """ + serviceDate: LocalDate! +} + "Entity related to an alert" union AlertEntity = Agency | Pattern | Route | RouteType | Stop | StopOnRoute | StopOnTrip | Trip | Unknown union StopPosition = PositionAtStop | PositionBetweenStops -"An instance of a trip on a service date." -union TripOnServiceDate = FixedTripOnServiceDate - "A public transport agency" type Agency implements Node { """ @@ -539,50 +548,6 @@ type FeedPublisher { url: String! } -""" -Timing of an arrival or a departure to or from a stop. May contain real-time information if -available. This is used when there is a known scheduled time. -""" -type FixedArrivalDepartureTime { - "The estimated time of the event. If no real-time information is available, this is null." - estimated: RealTimeEstimate - "The scheduled time of the event." - scheduledTime: OffsetDateTime! -} - -""" -Exact dated stoptime represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. -This can include realtime estimates. -""" -type FixedStopTimeOnServiceDate { - "Scheduled arrival time to the stop and a realtime estimate, if one exists." - arrival: FixedArrivalDepartureTime - "Scheduled departure time from the stop and a realtime estimate, if one exists." - departure: FixedArrivalDepartureTime - "The stop where this arrival/departure happens" - stop: Stop -} - -"A fixed (i.e. not flexible or frequency based) trip on a specific service date" -type FixedTripOnServiceDate { - "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." - end: FixedStopTimeOnServiceDate! - """ - The service date when the trip occurs. - - **Note**: A service date is a technical term useful for transit planning purposes and might not - correspond to a how a passenger thinks of a calendar date. For example, a night bus running - on Sunday morning at 1am to 3am, might have the previous Saturday's service date. - """ - serviceDate: LocalDate! - "Information related to trip's scheduled departure from the first stop. Can contain realtime information." - start: FixedStopTimeOnServiceDate! - "List of times when this trip arrives to or departs from a stop and information related to the stop" - stopTimes: [FixedStopTimeOnServiceDate!]! - "This trip on service date is an instance of this trip." - trip: Trip -} - type Geometry { "The number of points in the string" length: Int @@ -1824,6 +1789,50 @@ type RealTimeEstimate { time: OffsetDateTime! } +""" +Timing of an arrival or a departure to or from a stop. May contain real-time information if +available. This is used when there is a known scheduled time. +""" +type RegularRealTimeArrivalDepartureTime { + "The estimated time of the event. If no real-time information is available, this is null." + estimated: RealTimeEstimate + "The scheduled time of the event." + scheduledTime: OffsetDateTime! +} + +""" +Regular real-time stop time represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. +This can include real-time estimates. +""" +type RegularRealTimeStopTime { + "Scheduled arrival time to the stop and a real-time estimate, if one exists." + arrival: RegularRealTimeArrivalDepartureTime + "Scheduled departure time from the stop and a real-time estimate, if one exists." + departure: RegularRealTimeArrivalDepartureTime + "The stop where this arrival/departure happens" + stop: Stop +} + +"A regular (i.e. not flexible) trip on a specific service date" +type RegularTripOnServiceDate implements TripOnServiceDate { + "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." + end: RegularRealTimeStopTime! + """ + The service date when the trip occurs. + + **Note**: A service date is a technical term useful for transit planning purposes and might not + correspond to a how a passenger thinks of a calendar date. For example, a night bus running + on Sunday morning at 1am to 3am, might have the previous Saturday's service date. + """ + serviceDate: LocalDate! + "Information related to trip's scheduled departure from the first stop. Can contain realtime information." + start: RegularRealTimeStopTime! + "List of times when this trip arrives to or departs from a stop and information related to the stop" + stopTimes: [RegularRealTimeStopTime!]! + "This trip on service date is an instance of this trip." + trip: Trip +} + "Rental vehicle represents a vehicle that belongs to a rental network." type RentalVehicle implements Node & PlaceInterface { "If true, vehicle is currently available for renting." diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql index a29950f7b0e..9629c4ae418 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -8,7 +8,7 @@ } edges { node { - ... on FixedTripOnServiceDate { + ... on RegularTripOnServiceDate { serviceDate end { arrival { From 8885c193c6778ead7ad75728e2bae5c5859e27db Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Sun, 17 Nov 2024 23:46:45 +0200 Subject: [PATCH 037/162] Rename RegularArrivalDepartureTime -> LegCallTime --- .../impl/CombinedInterlinedTransitLeg.java | 6 +++--- .../ext/flex/FlexibleTransitLeg.java | 10 +++++----- .../apis/gtfs/datafetchers/LegImpl.java | 6 +++--- .../apis/gtfs/datafetchers/PlaceImpl.java | 6 +++--- .../RegularRealTimeStopTimeImpl.java | 14 ++++++------- .../gtfs/generated/GraphQLDataFetchers.java | 14 ++++++------- .../apis/gtfs/generated/graphql-codegen.yml | 4 ++-- .../model/plan/FrequencyTransitLeg.java | 8 ++------ .../org/opentripplanner/model/plan/Leg.java | 4 ++-- ...valDepartureTime.java => LegCallTime.java} | 20 +++++++------------ .../model/plan/ScheduledTransitLeg.java | 12 +++++------ .../model/plan/StopArrival.java | 8 ++++---- .../model/plan/StopArrivalMapper.java | 8 ++++---- .../opentripplanner/model/plan/StreetLeg.java | 8 ++++---- .../model/plan/UnknownTransitPathLeg.java | 8 ++++---- 15 files changed, 63 insertions(+), 73 deletions(-) rename application/src/main/java/org/opentripplanner/model/plan/{RegularArrivalDepartureTime.java => LegCallTime.java} (62%) diff --git a/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java b/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java index f41126a6a60..60d03e484df 100644 --- a/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java +++ b/application/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java @@ -9,7 +9,7 @@ import org.locationtech.jts.geom.LineString; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.RegularArrivalDepartureTime; +import org.opentripplanner.model.plan.LegCallTime; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.TransitLeg; @@ -55,12 +55,12 @@ public Trip getTrip() { } @Override - public RegularArrivalDepartureTime start() { + public LegCallTime start() { return first.start(); } @Override - public RegularArrivalDepartureTime end() { + public LegCallTime end() { return second.end(); } diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java b/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java index 39a9d0375d6..769407aa05a 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java @@ -13,7 +13,7 @@ import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.RegularArrivalDepartureTime; +import org.opentripplanner.model.plan.LegCallTime; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.TransitLeg; @@ -87,13 +87,13 @@ public Accessibility getTripWheelchairAccessibility() { } @Override - public RegularArrivalDepartureTime start() { - return RegularArrivalDepartureTime.ofStatic(startTime); + public LegCallTime start() { + return LegCallTime.ofStatic(startTime); } @Override - public RegularArrivalDepartureTime end() { - return RegularArrivalDepartureTime.ofStatic(endTime); + public LegCallTime end() { + return LegCallTime.ofStatic(endTime); } @Override diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java index c90339f1016..fdd2609e17a 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java @@ -18,7 +18,7 @@ import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.RegularArrivalDepartureTime; +import org.opentripplanner.model.plan.LegCallTime; import org.opentripplanner.model.plan.ScheduledTransitLeg; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.StreetLeg; @@ -81,7 +81,7 @@ public DataFetcher duration() { } @Override - public DataFetcher end() { + public DataFetcher end() { return environment -> getSource(environment).end(); } @@ -227,7 +227,7 @@ public DataFetcher serviceDate() { } @Override - public DataFetcher start() { + public DataFetcher start() { return environment -> getSource(environment).start(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java index b732bb14887..e1924101b1b 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/PlaceImpl.java @@ -7,8 +7,8 @@ import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.apis.gtfs.model.StopPosition.PositionAtStop; import org.opentripplanner.framework.graphql.GraphQLUtils; +import org.opentripplanner.model.plan.LegCallTime; import org.opentripplanner.model.plan.Place; -import org.opentripplanner.model.plan.RegularArrivalDepartureTime; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.VertexType; import org.opentripplanner.routing.vehicle_parking.VehicleParking; @@ -19,7 +19,7 @@ public class PlaceImpl implements GraphQLDataFetchers.GraphQLPlace { @Override - public DataFetcher arrival() { + public DataFetcher arrival() { return environment -> getSource(environment).arrival; } @@ -58,7 +58,7 @@ public DataFetcher carPark() { @Deprecated @Override - public DataFetcher departure() { + public DataFetcher departure() { return environment -> getSource(environment).departure; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java index 54f99b828a1..de4476f6651 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java @@ -8,7 +8,7 @@ import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.TripTimeOnDate; -import org.opentripplanner.model.plan.RegularArrivalDepartureTime; +import org.opentripplanner.model.plan.LegCallTime; import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.utils.time.ServiceDateUtils; @@ -16,7 +16,7 @@ public class RegularRealTimeStopTimeImpl implements GraphQLDataFetchers.GraphQLRegularRealTimeStopTime { @Override - public DataFetcher arrival() { + public DataFetcher arrival() { return environment -> { var tripTime = getSource(environment); var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledArrival()); @@ -24,13 +24,13 @@ public DataFetcher arrival() { return null; } return tripTime.isRealtime() - ? RegularArrivalDepartureTime.of(scheduledTime, tripTime.getArrivalDelay()) - : RegularArrivalDepartureTime.ofStatic(scheduledTime); + ? LegCallTime.of(scheduledTime, tripTime.getArrivalDelay()) + : LegCallTime.ofStatic(scheduledTime); }; } @Override - public DataFetcher departure() { + public DataFetcher departure() { return environment -> { var tripTime = getSource(environment); var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledDeparture()); @@ -38,8 +38,8 @@ public DataFetcher departure() { return null; } return tripTime.isRealtime() - ? RegularArrivalDepartureTime.of(scheduledTime, tripTime.getDepartureDelay()) - : RegularArrivalDepartureTime.ofStatic(scheduledTime); + ? LegCallTime.of(scheduledTime, tripTime.getDepartureDelay()) + : LegCallTime.ofStatic(scheduledTime); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 0d06737b4e3..c2f2d236b41 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -41,7 +41,7 @@ import org.opentripplanner.model.plan.Emissions; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.model.plan.RegularArrivalDepartureTime; +import org.opentripplanner.model.plan.LegCallTime; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.routing.alertpatch.TransitAlert; @@ -473,7 +473,7 @@ public interface GraphQLLeg { public DataFetcher duration(); - public DataFetcher end(); + public DataFetcher end(); public DataFetcher endTime(); @@ -517,7 +517,7 @@ public interface GraphQLLeg { public DataFetcher serviceDate(); - public DataFetcher start(); + public DataFetcher start(); public DataFetcher startTime(); @@ -625,7 +625,7 @@ public interface GraphQLPattern { } public interface GraphQLPlace { - public DataFetcher arrival(); + public DataFetcher arrival(); public DataFetcher arrivalTime(); @@ -635,7 +635,7 @@ public interface GraphQLPlace { public DataFetcher carPark(); - public DataFetcher departure(); + public DataFetcher departure(); public DataFetcher departureTime(); @@ -861,9 +861,9 @@ public interface GraphQLRegularRealTimeArrivalDepartureTime { * This can include real-time estimates. */ public interface GraphQLRegularRealTimeStopTime { - public DataFetcher arrival(); + public DataFetcher arrival(); - public DataFetcher departure(); + public DataFetcher departure(); public DataFetcher stop(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 1df05e0fb5a..156053ad5dc 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -63,7 +63,7 @@ config: RegularTripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate TripOnServiceDateConnection: graphql.relay.Connection#Connection TripOnServiceDateEdge: graphql.relay.Edge#Edge - RegularRealTimeArrivalDepartureTime: org.opentripplanner.model.plan.RegularArrivalDepartureTime#RegularArrivalDepartureTime + RegularRealTimeArrivalDepartureTime: org.opentripplanner.model.plan.LegCallTime#LegCallTime debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step @@ -74,7 +74,7 @@ config: InputField: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField#GraphQLInputField Itinerary: org.opentripplanner.model.plan.Itinerary#Itinerary Leg: org.opentripplanner.model.plan.Leg#Leg - LegTime: org.opentripplanner.model.plan.RegularArrivalDepartureTime#RegularArrivalDepartureTime + LegTime: org.opentripplanner.model.plan.LegCallTime#LegCallTime Mode: String OccupancyStatus: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLOccupancyStatus#GraphQLOccupancyStatus TransitMode: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode#GraphQLTransitMode diff --git a/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java b/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java index a26b6c88951..6226b0d2b01 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/FrequencyTransitLeg.java @@ -55,12 +55,8 @@ public List getIntermediateStops() { StopArrival visit = new StopArrival( Place.forStop(stop), - RegularArrivalDepartureTime.ofStatic( - ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, arrivalTime) - ), - RegularArrivalDepartureTime.ofStatic( - ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, departureTime) - ), + LegCallTime.ofStatic(ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, arrivalTime)), + LegCallTime.ofStatic(ServiceDateUtils.toZonedDateTime(serviceDate, zoneId, departureTime)), i, tripTimes.gtfsSequenceOfStopIndex(i) ); diff --git a/application/src/main/java/org/opentripplanner/model/plan/Leg.java b/application/src/main/java/org/opentripplanner/model/plan/Leg.java index 00f1516a875..27e67400daf 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/Leg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/Leg.java @@ -209,12 +209,12 @@ default Accessibility getTripWheelchairAccessibility() { /** * The time (including realtime information) when the leg starts. */ - RegularArrivalDepartureTime start(); + LegCallTime start(); /** * The time (including realtime information) when the leg ends. */ - RegularArrivalDepartureTime end(); + LegCallTime end(); /** * The date and time this leg begins. diff --git a/application/src/main/java/org/opentripplanner/model/plan/RegularArrivalDepartureTime.java b/application/src/main/java/org/opentripplanner/model/plan/LegCallTime.java similarity index 62% rename from application/src/main/java/org/opentripplanner/model/plan/RegularArrivalDepartureTime.java rename to application/src/main/java/org/opentripplanner/model/plan/LegCallTime.java index 370e91934e1..78a6eb37694 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/RegularArrivalDepartureTime.java +++ b/application/src/main/java/org/opentripplanner/model/plan/LegCallTime.java @@ -7,26 +7,20 @@ /** * A scheduled time of a transit vehicle at a certain location with an optional realtime - * information. + * information. This is meant to be used in transit legs. */ -public record RegularArrivalDepartureTime( - ZonedDateTime scheduledTime, - @Nullable RealTimeEstimate estimated -) { - public RegularArrivalDepartureTime { +public record LegCallTime(ZonedDateTime scheduledTime, @Nullable RealTimeEstimate estimated) { + public LegCallTime { Objects.requireNonNull(scheduledTime); } - public static RegularArrivalDepartureTime of(ZonedDateTime realtime, int delaySecs) { + public static LegCallTime of(ZonedDateTime realtime, int delaySecs) { var delay = Duration.ofSeconds(delaySecs); - return new RegularArrivalDepartureTime( - realtime.minus(delay), - new RealTimeEstimate(realtime, delay) - ); + return new LegCallTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay)); } - public static RegularArrivalDepartureTime ofStatic(ZonedDateTime staticTime) { - return new RegularArrivalDepartureTime(staticTime, null); + public static LegCallTime ofStatic(ZonedDateTime staticTime) { + return new LegCallTime(staticTime, null); } /** diff --git a/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java b/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java index 635f9ca07d7..55e4bccfdec 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java @@ -161,20 +161,20 @@ public Accessibility getTripWheelchairAccessibility() { } @Override - public RegularArrivalDepartureTime start() { + public LegCallTime start() { if (isRealTimeUpdated()) { - return RegularArrivalDepartureTime.of(startTime, getDepartureDelay()); + return LegCallTime.of(startTime, getDepartureDelay()); } else { - return RegularArrivalDepartureTime.ofStatic(startTime); + return LegCallTime.ofStatic(startTime); } } @Override - public RegularArrivalDepartureTime end() { + public LegCallTime end() { if (isRealTimeUpdated()) { - return RegularArrivalDepartureTime.of(endTime, getArrivalDelay()); + return LegCallTime.of(endTime, getArrivalDelay()); } else { - return RegularArrivalDepartureTime.ofStatic(endTime); + return LegCallTime.ofStatic(endTime); } } diff --git a/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java b/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java index 6d1314ad8b4..478c8eea379 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java +++ b/application/src/main/java/org/opentripplanner/model/plan/StopArrival.java @@ -9,8 +9,8 @@ public final class StopArrival { public final Place place; - public final RegularArrivalDepartureTime arrival; - public final RegularArrivalDepartureTime departure; + public final LegCallTime arrival; + public final LegCallTime departure; public final Integer stopPosInPattern; public final Integer gtfsStopSequence; @@ -24,8 +24,8 @@ public final class StopArrival { */ public StopArrival( Place place, - RegularArrivalDepartureTime arrival, - RegularArrivalDepartureTime departure, + LegCallTime arrival, + LegCallTime departure, Integer stopPosInPattern, Integer gtfsStopSequence ) { diff --git a/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java b/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java index 80f2de3de80..a4c83e10901 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java +++ b/application/src/main/java/org/opentripplanner/model/plan/StopArrivalMapper.java @@ -34,12 +34,12 @@ StopArrival map(int i, StopLocation stop, boolean realTime) { tripTimes.getDepartureTime(i) ); - var arrival = RegularArrivalDepartureTime.ofStatic(arrivalTime); - var departure = RegularArrivalDepartureTime.ofStatic(departureTime); + var arrival = LegCallTime.ofStatic(arrivalTime); + var departure = LegCallTime.ofStatic(departureTime); if (realTime) { - arrival = RegularArrivalDepartureTime.of(arrivalTime, tripTimes.getArrivalDelay(i)); - departure = RegularArrivalDepartureTime.of(departureTime, tripTimes.getDepartureDelay(i)); + arrival = LegCallTime.of(arrivalTime, tripTimes.getArrivalDelay(i)); + departure = LegCallTime.of(departureTime, tripTimes.getDepartureDelay(i)); } return new StopArrival( diff --git a/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java b/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java index fbf2114da38..cb577b9ec49 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/StreetLeg.java @@ -157,13 +157,13 @@ public boolean hasSameMode(Leg other) { } @Override - public RegularArrivalDepartureTime start() { - return RegularArrivalDepartureTime.ofStatic(startTime); + public LegCallTime start() { + return LegCallTime.ofStatic(startTime); } @Override - public RegularArrivalDepartureTime end() { - return RegularArrivalDepartureTime.ofStatic(endTime); + public LegCallTime end() { + return LegCallTime.ofStatic(endTime); } @Override diff --git a/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java b/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java index ea07d97cb16..443621b750a 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/UnknownTransitPathLeg.java @@ -70,13 +70,13 @@ public boolean hasSameMode(Leg other) { } @Override - public RegularArrivalDepartureTime start() { - return RegularArrivalDepartureTime.ofStatic(startTime); + public LegCallTime start() { + return LegCallTime.ofStatic(startTime); } @Override - public RegularArrivalDepartureTime end() { - return RegularArrivalDepartureTime.ofStatic(endTime); + public LegCallTime end() { + return LegCallTime.ofStatic(endTime); } @Override From c7d6d4359c32438efbebde82383bc8363a936951 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 18 Nov 2024 00:32:16 +0200 Subject: [PATCH 038/162] Refactor LegCallTime --- .../model/plan/LegCallTime.java | 31 ++++++++++++------- .../model/plan/LegRealTimeEstimate.java | 30 ++++++++++++++++++ 2 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/model/plan/LegRealTimeEstimate.java diff --git a/application/src/main/java/org/opentripplanner/model/plan/LegCallTime.java b/application/src/main/java/org/opentripplanner/model/plan/LegCallTime.java index 78a6eb37694..676b45c8f92 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/LegCallTime.java +++ b/application/src/main/java/org/opentripplanner/model/plan/LegCallTime.java @@ -9,20 +9,35 @@ * A scheduled time of a transit vehicle at a certain location with an optional realtime * information. This is meant to be used in transit legs. */ -public record LegCallTime(ZonedDateTime scheduledTime, @Nullable RealTimeEstimate estimated) { - public LegCallTime { - Objects.requireNonNull(scheduledTime); +public class LegCallTime { + + private final ZonedDateTime scheduledTime; + + @Nullable + private final LegRealTimeEstimate estimated; + + private LegCallTime(ZonedDateTime scheduledTime, @Nullable LegRealTimeEstimate estimated) { + this.scheduledTime = Objects.requireNonNull(scheduledTime); + this.estimated = estimated; } public static LegCallTime of(ZonedDateTime realtime, int delaySecs) { var delay = Duration.ofSeconds(delaySecs); - return new LegCallTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay)); + return new LegCallTime(realtime.minus(delay), new LegRealTimeEstimate(realtime, delay)); } public static LegCallTime ofStatic(ZonedDateTime staticTime) { return new LegCallTime(staticTime, null); } + public ZonedDateTime scheduledTime() { + return scheduledTime; + } + + public LegRealTimeEstimate estimated() { + return estimated; + } + /** * The most up-to-date time available: if realtime data is available it is returned, if not then * the scheduled one is. @@ -31,13 +46,7 @@ public ZonedDateTime time() { if (estimated == null) { return scheduledTime; } else { - return estimated.time; + return estimated.time(); } } - - /** - * Realtime information about a vehicle at a certain place. - * @param delay Delay or "earliness" of a vehicle. Earliness is expressed as a negative number. - */ - record RealTimeEstimate(ZonedDateTime time, Duration delay) {} } diff --git a/application/src/main/java/org/opentripplanner/model/plan/LegRealTimeEstimate.java b/application/src/main/java/org/opentripplanner/model/plan/LegRealTimeEstimate.java new file mode 100644 index 00000000000..a924b0c22ae --- /dev/null +++ b/application/src/main/java/org/opentripplanner/model/plan/LegRealTimeEstimate.java @@ -0,0 +1,30 @@ +package org.opentripplanner.model.plan; + +import java.time.Duration; +import java.time.ZonedDateTime; +import java.util.Objects; + +/** + * Realtime information about a vehicle at a certain place. Meant to be used in transit legs. + */ +public class LegRealTimeEstimate { + + private final ZonedDateTime time; + private final Duration delay; + + /** + * @param delay Delay or "earliness" of a vehicle. Earliness is expressed as a negative number. + */ + public LegRealTimeEstimate(ZonedDateTime time, Duration delay) { + this.time = Objects.requireNonNull(time); + this.delay = Objects.requireNonNull(delay); + } + + public ZonedDateTime time() { + return time; + } + + public Duration delay() { + return delay; + } +} From 178c150725b78c2ebb4e3b3c089cc6b1d33bf16d Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 18 Nov 2024 12:20:10 +0200 Subject: [PATCH 039/162] Rename StopTime -> StopCall etc. and create own model for its times --- .../apis/gtfs/GtfsGraphQLIndex.java | 15 ++- ...ellableTripOnServiceDateTypeResolver.java} | 4 +- ...imeStopTimeImpl.java => StopCallImpl.java} | 17 ++- ...teImpl.java => TripOnServiceDateImpl.java} | 5 +- .../gtfs/generated/GraphQLDataFetchers.java | 84 +++++++------ .../apis/gtfs/generated/graphql-codegen.yml | 6 +- .../model/timetable/CallRealTimeEstimate.java | 31 +++++ .../transit/model/timetable/CallTime.java | 40 ++++++ .../opentripplanner/apis/gtfs/schema.graphqls | 116 +++++++++--------- .../gtfs/expectations/canceled-trips.json | 2 +- .../apis/gtfs/queries/canceled-trips.graphql | 4 +- 11 files changed, 201 insertions(+), 123 deletions(-) rename application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{TripOnServiceDateTypeResolver.java => CancellableTripOnServiceDateTypeResolver.java} (79%) rename application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{RegularRealTimeStopTimeImpl.java => StopCallImpl.java} (79%) rename application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{RegularTripOnServiceDateImpl.java => TripOnServiceDateImpl.java} (95%) create mode 100644 application/src/main/java/org/opentripplanner/transit/model/timetable/CallRealTimeEstimate.java create mode 100644 application/src/main/java/org/opentripplanner/transit/model/timetable/CallTime.java diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 014acb97398..2a7599ecfd7 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -31,6 +31,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.BikeRentalStationImpl; import org.opentripplanner.apis.gtfs.datafetchers.BookingInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.BookingTimeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.CancellableTripOnServiceDateTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.CarParkImpl; import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.CoordinatesImpl; @@ -52,14 +53,13 @@ import org.opentripplanner.apis.gtfs.datafetchers.PlanConnectionImpl; import org.opentripplanner.apis.gtfs.datafetchers.PlanImpl; import org.opentripplanner.apis.gtfs.datafetchers.QueryTypeImpl; -import org.opentripplanner.apis.gtfs.datafetchers.RegularRealTimeStopTimeImpl; -import org.opentripplanner.apis.gtfs.datafetchers.RegularTripOnServiceDateImpl; import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleImpl; import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleTypeImpl; import org.opentripplanner.apis.gtfs.datafetchers.RideHailingEstimateImpl; import org.opentripplanner.apis.gtfs.datafetchers.RouteImpl; import org.opentripplanner.apis.gtfs.datafetchers.RouteTypeImpl; import org.opentripplanner.apis.gtfs.datafetchers.RoutingErrorImpl; +import org.opentripplanner.apis.gtfs.datafetchers.StopCallImpl; import org.opentripplanner.apis.gtfs.datafetchers.StopGeometriesImpl; import org.opentripplanner.apis.gtfs.datafetchers.StopImpl; import org.opentripplanner.apis.gtfs.datafetchers.StopOnRouteImpl; @@ -72,7 +72,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.TranslatedStringImpl; import org.opentripplanner.apis.gtfs.datafetchers.TripImpl; import org.opentripplanner.apis.gtfs.datafetchers.TripOccupancyImpl; -import org.opentripplanner.apis.gtfs.datafetchers.TripOnServiceDateTypeResolver; +import org.opentripplanner.apis.gtfs.datafetchers.TripOnServiceDateImpl; import org.opentripplanner.apis.gtfs.datafetchers.UnknownImpl; import org.opentripplanner.apis.gtfs.datafetchers.VehicleParkingImpl; import org.opentripplanner.apis.gtfs.datafetchers.VehiclePositionImpl; @@ -129,7 +129,10 @@ protected static GraphQLSchema buildSchema() { .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) - .type("TripOnServiceDate", type -> type.typeResolver(new TripOnServiceDateTypeResolver())) + .type( + "CancellableTripOnServiceDate", + type -> type.typeResolver(new CancellableTripOnServiceDateTypeResolver()) + ) .type(typeWiring.build(AgencyImpl.class)) .type(typeWiring.build(AlertImpl.class)) .type(typeWiring.build(BikeParkImpl.class)) @@ -182,8 +185,8 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(CurrencyImpl.class)) .type(typeWiring.build(FareProductUseImpl.class)) .type(typeWiring.build(DefaultFareProductImpl.class)) - .type(typeWiring.build(RegularTripOnServiceDateImpl.class)) - .type(typeWiring.build(RegularRealTimeStopTimeImpl.class)) + .type(typeWiring.build(TripOnServiceDateImpl.class)) + .type(typeWiring.build(StopCallImpl.class)) .type(typeWiring.build(TripOccupancyImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CancellableTripOnServiceDateTypeResolver.java similarity index 79% rename from application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java rename to application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CancellableTripOnServiceDateTypeResolver.java index 570177768f0..4c9af823767 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateTypeResolver.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CancellableTripOnServiceDateTypeResolver.java @@ -6,7 +6,7 @@ import graphql.schema.TypeResolver; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; -public class TripOnServiceDateTypeResolver implements TypeResolver { +public class CancellableTripOnServiceDateTypeResolver implements TypeResolver { @Override public GraphQLObjectType getType(TypeResolutionEnvironment environment) { @@ -14,7 +14,7 @@ public GraphQLObjectType getType(TypeResolutionEnvironment environment) { GraphQLSchema schema = environment.getSchema(); if (o instanceof TripOnServiceDate) { - return schema.getObjectType("RegularTripOnServiceDate"); + return schema.getObjectType("TripOnServiceDate"); } return null; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopCallImpl.java similarity index 79% rename from application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java rename to application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopCallImpl.java index de4476f6651..cb5ec7e15a5 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularRealTimeStopTimeImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopCallImpl.java @@ -8,15 +8,14 @@ import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.model.TripTimeOnDate; -import org.opentripplanner.model.plan.LegCallTime; +import org.opentripplanner.transit.model.timetable.CallTime; import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.utils.time.ServiceDateUtils; -public class RegularRealTimeStopTimeImpl - implements GraphQLDataFetchers.GraphQLRegularRealTimeStopTime { +public class StopCallImpl implements GraphQLDataFetchers.GraphQLStopCall { @Override - public DataFetcher arrival() { + public DataFetcher arrival() { return environment -> { var tripTime = getSource(environment); var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledArrival()); @@ -24,13 +23,13 @@ public DataFetcher arrival() { return null; } return tripTime.isRealtime() - ? LegCallTime.of(scheduledTime, tripTime.getArrivalDelay()) - : LegCallTime.ofStatic(scheduledTime); + ? CallTime.of(scheduledTime, tripTime.getArrivalDelay()) + : CallTime.ofStatic(scheduledTime); }; } @Override - public DataFetcher departure() { + public DataFetcher departure() { return environment -> { var tripTime = getSource(environment); var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledDeparture()); @@ -38,8 +37,8 @@ public DataFetcher departure() { return null; } return tripTime.isRealtime() - ? LegCallTime.of(scheduledTime, tripTime.getDepartureDelay()) - : LegCallTime.ofStatic(scheduledTime); + ? CallTime.of(scheduledTime, tripTime.getDepartureDelay()) + : CallTime.ofStatic(scheduledTime); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularTripOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java similarity index 95% rename from application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularTripOnServiceDateImpl.java rename to application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java index f6b7107aded..22e45a9afda 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RegularTripOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java @@ -16,8 +16,7 @@ import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.utils.time.ServiceDateUtils; -public class RegularTripOnServiceDateImpl - implements GraphQLDataFetchers.GraphQLRegularTripOnServiceDate { +public class TripOnServiceDateImpl implements GraphQLDataFetchers.GraphQLTripOnServiceDate { @Override public DataFetcher serviceDate() { @@ -61,7 +60,7 @@ public DataFetcher start() { } @Override - public DataFetcher> stopTimes() { + public DataFetcher> stopCalls() { return environment -> { TransitService transitService = getTransitService(environment); Trip trip = getTrip(environment); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index c2f2d236b41..f4843a31ecf 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -66,6 +66,7 @@ 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.CallTime; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.booking.BookingInfo; @@ -239,6 +240,26 @@ public interface GraphQLBookingTime { public DataFetcher time(); } + /** Real-time estimates for an arrival or departure at a certain place. */ + public interface GraphQLCallRealTimeEstimate { + public DataFetcher delay(); + + public DataFetcher time(); + } + + /** + * Timing of an arrival or a departure to or from a stop. May contain real-time information if + * available. This is used when there is a known scheduled time. + */ + public interface GraphQLCallTime { + public DataFetcher estimated(); + + public DataFetcher scheduledTime(); + } + + /** An instance of a trip, which can be cancelled, on a service date. */ + public interface GraphQLCancellableTripOnServiceDate extends TypeResolver {} + /** Car park represents a location where cars can be parked. */ public interface GraphQLCarPark { public DataFetcher carParkId(); @@ -846,41 +867,6 @@ public interface GraphQLRealTimeEstimate { public DataFetcher time(); } - /** - * Timing of an arrival or a departure to or from a stop. May contain real-time information if - * available. This is used when there is a known scheduled time. - */ - public interface GraphQLRegularRealTimeArrivalDepartureTime { - public DataFetcher estimated(); - - public DataFetcher scheduledTime(); - } - - /** - * Regular real-time stop time represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. - * This can include real-time estimates. - */ - public interface GraphQLRegularRealTimeStopTime { - public DataFetcher arrival(); - - public DataFetcher departure(); - - public DataFetcher stop(); - } - - /** A regular (i.e. not flexible) trip on a specific service date */ - public interface GraphQLRegularTripOnServiceDate { - public DataFetcher end(); - - public DataFetcher serviceDate(); - - public DataFetcher start(); - - public DataFetcher> stopTimes(); - - public DataFetcher trip(); - } - /** Rental vehicle represents a vehicle that belongs to a rental network. */ public interface GraphQLRentalVehicle { public DataFetcher allowPickupNow(); @@ -1075,6 +1061,18 @@ public interface GraphQLStop { public DataFetcher zoneId(); } + /** + * Stop call represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. + * This can include real-time estimates. + */ + public interface GraphQLStopCall { + public DataFetcher arrival(); + + public DataFetcher departure(); + + public DataFetcher stop(); + } + public interface GraphQLStopGeometries { public DataFetcher geoJson(); @@ -1241,11 +1239,17 @@ public interface GraphQLTripOccupancy { public DataFetcher occupancyStatus(); } - /** An instance of a trip on a service date. */ - public interface GraphQLTripOnServiceDate extends TypeResolver { - public default DataFetcher serviceDate() { - return null; - } + /** A regular (i.e. not flexible) trip on a specific service date */ + public interface GraphQLTripOnServiceDate { + public DataFetcher end(); + + public DataFetcher serviceDate(); + + public DataFetcher start(); + + public DataFetcher> stopCalls(); + + public DataFetcher trip(); } /** diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 156053ad5dc..1f2733ee34b 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -59,11 +59,11 @@ config: ContactInfo: org.opentripplanner.transit.model.organization.ContactInfo Cluster: Object Coordinates: org.locationtech.jts.geom.Coordinate#Coordinate - RegularRealTimeStopTime: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate - RegularTripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate + StopCall: org.opentripplanner.model.TripTimeOnDate#TripTimeOnDate + TripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate TripOnServiceDateConnection: graphql.relay.Connection#Connection TripOnServiceDateEdge: graphql.relay.Edge#Edge - RegularRealTimeArrivalDepartureTime: org.opentripplanner.model.plan.LegCallTime#LegCallTime + CallTime: org.opentripplanner.transit.model.timetable.CallTime#CallTime debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step diff --git a/application/src/main/java/org/opentripplanner/transit/model/timetable/CallRealTimeEstimate.java b/application/src/main/java/org/opentripplanner/transit/model/timetable/CallRealTimeEstimate.java new file mode 100644 index 00000000000..eeac4a321c4 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/transit/model/timetable/CallRealTimeEstimate.java @@ -0,0 +1,31 @@ +package org.opentripplanner.transit.model.timetable; + +import java.time.Duration; +import java.time.ZonedDateTime; +import java.util.Objects; + +/** + * Realtime information about a vehicle at a certain place. This is meant to be used in timetables + * (not in transit legs). + */ +public class CallRealTimeEstimate { + + private final ZonedDateTime time; + private final Duration delay; + + /** + * @param delay Delay or "earliness" of a vehicle. Earliness is expressed as a negative number. + */ + public CallRealTimeEstimate(ZonedDateTime time, Duration delay) { + this.time = Objects.requireNonNull(time); + this.delay = Objects.requireNonNull(delay); + } + + public ZonedDateTime time() { + return time; + } + + public Duration delay() { + return delay; + } +} diff --git a/application/src/main/java/org/opentripplanner/transit/model/timetable/CallTime.java b/application/src/main/java/org/opentripplanner/transit/model/timetable/CallTime.java new file mode 100644 index 00000000000..4a7df9fb217 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/transit/model/timetable/CallTime.java @@ -0,0 +1,40 @@ +package org.opentripplanner.transit.model.timetable; + +import java.time.Duration; +import java.time.ZonedDateTime; +import java.util.Objects; +import javax.annotation.Nullable; + +/** + * A scheduled time of a transit vehicle at a certain location with an optional realtime + * information. This is meant to be used in timetables (not in transit legs). + */ +public class CallTime { + + private final ZonedDateTime scheduledTime; + + @Nullable + private final CallRealTimeEstimate estimated; + + private CallTime(ZonedDateTime scheduledTime, @Nullable CallRealTimeEstimate estimated) { + this.scheduledTime = Objects.requireNonNull(scheduledTime); + this.estimated = estimated; + } + + public static CallTime of(ZonedDateTime realtime, int delaySecs) { + var delay = Duration.ofSeconds(delaySecs); + return new CallTime(realtime.minus(delay), new CallRealTimeEstimate(realtime, delay)); + } + + public static CallTime ofStatic(ZonedDateTime staticTime) { + return new CallTime(staticTime, null); + } + + public ZonedDateTime scheduledTime() { + return scheduledTime; + } + + public CallRealTimeEstimate estimated() { + return estimated; + } +} diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 25f7a757131..07a89270370 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -70,21 +70,12 @@ interface PlaceInterface { lon: Float } -"An instance of a trip on a service date." -interface TripOnServiceDate { - """ - The service date when the trip occurs. - - **Note**: A service date is a technical term useful for transit planning purposes and might not - correspond to a how a passenger thinks of a calendar date. For example, a night bus running - on Sunday morning at 1am to 3am, might have the previous Saturday's service date. - """ - serviceDate: LocalDate! -} - "Entity related to an alert" union AlertEntity = Agency | Pattern | Route | RouteType | Stop | StopOnRoute | StopOnTrip | Trip | Unknown +"An instance of a trip, which can be cancelled, on a service date." +union CancellableTripOnServiceDate = TripOnServiceDate + union StopPosition = PositionAtStop | PositionBetweenStops "A public transport agency" @@ -306,6 +297,28 @@ type BookingTime { time: String } +"Real-time estimates for an arrival or departure at a certain place." +type CallRealTimeEstimate { + """ + The delay or "earliness" of the vehicle at a certain place. + + If the vehicle is early then this is a negative duration. + """ + delay: Duration! + time: OffsetDateTime! +} + +""" +Timing of an arrival or a departure to or from a stop. May contain real-time information if +available. This is used when there is a known scheduled time. +""" +type CallTime { + "The estimated time of the event. If no real-time information is available, this is null." + estimated: CallRealTimeEstimate + "The scheduled time of the event." + scheduledTime: OffsetDateTime! +} + "Car park represents a location where cars can be parked." type CarPark implements Node & PlaceInterface { "ID of the car park" @@ -1808,50 +1821,6 @@ type RealTimeEstimate { time: OffsetDateTime! } -""" -Timing of an arrival or a departure to or from a stop. May contain real-time information if -available. This is used when there is a known scheduled time. -""" -type RegularRealTimeArrivalDepartureTime { - "The estimated time of the event. If no real-time information is available, this is null." - estimated: RealTimeEstimate - "The scheduled time of the event." - scheduledTime: OffsetDateTime! -} - -""" -Regular real-time stop time represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. -This can include real-time estimates. -""" -type RegularRealTimeStopTime { - "Scheduled arrival time to the stop and a real-time estimate, if one exists." - arrival: RegularRealTimeArrivalDepartureTime - "Scheduled departure time from the stop and a real-time estimate, if one exists." - departure: RegularRealTimeArrivalDepartureTime - "The stop where this arrival/departure happens" - stop: Stop -} - -"A regular (i.e. not flexible) trip on a specific service date" -type RegularTripOnServiceDate implements TripOnServiceDate { - "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." - end: RegularRealTimeStopTime! - """ - The service date when the trip occurs. - - **Note**: A service date is a technical term useful for transit planning purposes and might not - correspond to a how a passenger thinks of a calendar date. For example, a night bus running - on Sunday morning at 1am to 3am, might have the previous Saturday's service date. - """ - serviceDate: LocalDate! - "Information related to trip's scheduled departure from the first stop. Can contain realtime information." - start: RegularRealTimeStopTime! - "List of times when this trip arrives to or departs from a stop and information related to the stop" - stopTimes: [RegularRealTimeStopTime!]! - "This trip on service date is an instance of this trip." - trip: Trip -} - "Rental vehicle represents a vehicle that belongs to a rental network." type RentalVehicle implements Node & PlaceInterface { "If true, vehicle is currently available for renting." @@ -2214,6 +2183,19 @@ type Stop implements Node & PlaceInterface { zoneId: String } +""" +Stop call represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. +This can include real-time estimates. +""" +type StopCall { + "Scheduled arrival time to the stop and a real-time estimate, if one exists." + arrival: CallTime + "Scheduled departure time from the stop and a real-time estimate, if one exists." + departure: CallTime + "The stop where this arrival/departure happens" + stop: Stop +} + type StopGeometries { "Representation of the stop geometries as GeoJSON (https://geojson.org/)" geoJson: GeoJson @@ -2469,6 +2451,26 @@ type TripOccupancy { occupancyStatus: OccupancyStatus } +"A regular (i.e. not flexible) trip on a specific service date" +type TripOnServiceDate { + "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." + end: StopCall! + """ + The service date when the trip occurs. + + **Note**: A service date is a technical term useful for transit planning purposes and might not + correspond to a how a passenger thinks of a calendar date. For example, a night bus running + on Sunday morning at 1am to 3am, might have the previous Saturday's service date. + """ + serviceDate: LocalDate! + "Information related to trip's scheduled departure from the first stop. Can contain realtime information." + start: StopCall! + "List of times when this trip arrives to or departs from a stop and information related to the stop" + stopCalls: [StopCall!]! + "This trip on service date is an instance of this trip." + trip: Trip +} + """ A connection to a list of trips on service dates that follows [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). @@ -2500,7 +2502,7 @@ type TripOnServiceDateEdge { Trip on a service date as a node. Part of the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). """ - node: TripOnServiceDate + node: CancellableTripOnServiceDate } "This is used for alert entities that we don't explicitly handle or they are missing." diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json index b3facd3da1f..5e32b67fc4e 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json @@ -29,7 +29,7 @@ "scheduledTime": "2024-08-09T11:30:00+02:00" } }, - "stopTimes": [ + "stopCalls": [ { "stop": { "gtfsId": "F:Stop_0" diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql index 9629c4ae418..8d5685a06ba 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -8,7 +8,7 @@ } edges { node { - ... on RegularTripOnServiceDate { + ... on TripOnServiceDate { serviceDate end { arrival { @@ -28,7 +28,7 @@ scheduledTime } } - stopTimes { + stopCalls { stop { gtfsId } From 349efb66083bc7041aa563d42c1cd34e260cb432 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 18 Nov 2024 14:38:46 +0200 Subject: [PATCH 040/162] Create implementations for call times --- .../apis/gtfs/GtfsGraphQLIndex.java | 8 ++++++ .../CallRealTimeEstimateImpl.java | 25 +++++++++++++++++++ .../apis/gtfs/datafetchers/CallTimeImpl.java | 25 +++++++++++++++++++ .../apis/gtfs/datafetchers/LegTimeImpl.java | 25 +++++++++++++++++++ .../datafetchers/RealTimeEstimateImpl.java | 25 +++++++++++++++++++ .../gtfs/generated/GraphQLDataFetchers.java | 6 +++-- .../apis/gtfs/generated/graphql-codegen.yml | 2 ++ 7 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallRealTimeEstimateImpl.java create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTimeImpl.java create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegTimeImpl.java create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RealTimeEstimateImpl.java diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 2a7599ecfd7..ddf251ea833 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -31,6 +31,8 @@ import org.opentripplanner.apis.gtfs.datafetchers.BikeRentalStationImpl; import org.opentripplanner.apis.gtfs.datafetchers.BookingInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.BookingTimeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.CallRealTimeEstimateImpl; +import org.opentripplanner.apis.gtfs.datafetchers.CallTimeImpl; import org.opentripplanner.apis.gtfs.datafetchers.CancellableTripOnServiceDateTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.CarParkImpl; import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl; @@ -44,6 +46,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.GeometryImpl; import org.opentripplanner.apis.gtfs.datafetchers.ItineraryImpl; import org.opentripplanner.apis.gtfs.datafetchers.LegImpl; +import org.opentripplanner.apis.gtfs.datafetchers.LegTimeImpl; import org.opentripplanner.apis.gtfs.datafetchers.MoneyImpl; import org.opentripplanner.apis.gtfs.datafetchers.NodeTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.OpeningHoursImpl; @@ -53,6 +56,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.PlanConnectionImpl; import org.opentripplanner.apis.gtfs.datafetchers.PlanImpl; import org.opentripplanner.apis.gtfs.datafetchers.QueryTypeImpl; +import org.opentripplanner.apis.gtfs.datafetchers.RealTimeEstimateImpl; import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleImpl; import org.opentripplanner.apis.gtfs.datafetchers.RentalVehicleTypeImpl; import org.opentripplanner.apis.gtfs.datafetchers.RideHailingEstimateImpl; @@ -188,6 +192,10 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(TripOnServiceDateImpl.class)) .type(typeWiring.build(StopCallImpl.class)) .type(typeWiring.build(TripOccupancyImpl.class)) + .type(typeWiring.build(LegTimeImpl.class)) + .type(typeWiring.build(RealTimeEstimateImpl.class)) + .type(typeWiring.build(CallTimeImpl.class)) + .type(typeWiring.build(CallRealTimeEstimateImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallRealTimeEstimateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallRealTimeEstimateImpl.java new file mode 100644 index 00000000000..09439c6e916 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallRealTimeEstimateImpl.java @@ -0,0 +1,25 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.time.Duration; +import java.time.OffsetDateTime; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.transit.model.timetable.CallRealTimeEstimate; + +public class CallRealTimeEstimateImpl implements GraphQLDataFetchers.GraphQLCallRealTimeEstimate { + + @Override + public DataFetcher delay() { + return environment -> getSource(environment).delay(); + } + + @Override + public DataFetcher time() { + return environment -> getSource(environment).time().toOffsetDateTime(); + } + + private CallRealTimeEstimate getSource(DataFetchingEnvironment environment) { + return environment.getSource(); + } +} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTimeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTimeImpl.java new file mode 100644 index 00000000000..5a2da2c2d82 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTimeImpl.java @@ -0,0 +1,25 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.time.OffsetDateTime; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.transit.model.timetable.CallRealTimeEstimate; +import org.opentripplanner.transit.model.timetable.CallTime; + +public class CallTimeImpl implements GraphQLDataFetchers.GraphQLCallTime { + + @Override + public DataFetcher estimated() { + return environment -> getSource(environment).estimated(); + } + + @Override + public DataFetcher scheduledTime() { + return environment -> getSource(environment).scheduledTime().toOffsetDateTime(); + } + + private CallTime getSource(DataFetchingEnvironment environment) { + return environment.getSource(); + } +} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegTimeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegTimeImpl.java new file mode 100644 index 00000000000..571c0f2f38c --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegTimeImpl.java @@ -0,0 +1,25 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.time.OffsetDateTime; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.model.plan.LegCallTime; +import org.opentripplanner.model.plan.LegRealTimeEstimate; + +public class LegTimeImpl implements GraphQLDataFetchers.GraphQLLegTime { + + @Override + public DataFetcher estimated() { + return environment -> getSource(environment).estimated(); + } + + @Override + public DataFetcher scheduledTime() { + return environment -> getSource(environment).scheduledTime().toOffsetDateTime(); + } + + private LegCallTime getSource(DataFetchingEnvironment environment) { + return environment.getSource(); + } +} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RealTimeEstimateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RealTimeEstimateImpl.java new file mode 100644 index 00000000000..fbbf5226691 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RealTimeEstimateImpl.java @@ -0,0 +1,25 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.time.Duration; +import java.time.OffsetDateTime; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.model.plan.LegRealTimeEstimate; + +public class RealTimeEstimateImpl implements GraphQLDataFetchers.GraphQLRealTimeEstimate { + + @Override + public DataFetcher delay() { + return environment -> getSource(environment).delay(); + } + + @Override + public DataFetcher time() { + return environment -> getSource(environment).time().toOffsetDateTime(); + } + + private LegRealTimeEstimate getSource(DataFetchingEnvironment environment) { + return environment.getSource(); + } +} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index f4843a31ecf..0210a5708dc 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -42,6 +42,7 @@ import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.LegCallTime; +import org.opentripplanner.model.plan.LegRealTimeEstimate; import org.opentripplanner.model.plan.StopArrival; import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.routing.alertpatch.TransitAlert; @@ -66,6 +67,7 @@ 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.CallRealTimeEstimate; import org.opentripplanner.transit.model.timetable.CallTime; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; @@ -252,7 +254,7 @@ public interface GraphQLCallRealTimeEstimate { * available. This is used when there is a known scheduled time. */ public interface GraphQLCallTime { - public DataFetcher estimated(); + public DataFetcher estimated(); public DataFetcher scheduledTime(); } @@ -558,7 +560,7 @@ public interface GraphQLLeg { * available. */ public interface GraphQLLegTime { - public DataFetcher estimated(); + public DataFetcher estimated(); public DataFetcher scheduledTime(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 1f2733ee34b..d5059311cac 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -130,4 +130,6 @@ config: FareMedium: org.opentripplanner.model.fare.FareMedium#FareMedium RiderCategory: org.opentripplanner.model.fare.RiderCategory#RiderCategory StopPosition: org.opentripplanner.apis.gtfs.model.StopPosition#StopPosition + RealTimeEstimate: org.opentripplanner.model.plan.LegRealTimeEstimate#LegRealTimeEstimate + CallRealTimeEstimate: org.opentripplanner.transit.model.timetable.CallRealTimeEstimate#CallRealTimeEstimate From 47300f981eb08b2c73125a3937eb77165834f799 Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Tue, 19 Nov 2024 15:47:12 +0200 Subject: [PATCH 041/162] use duration tag from osm as escalator traversal duration --- .../module/osm/EscalatorProcessor.java | 13 ++++-- .../org/opentripplanner/osm/model/OsmWay.java | 15 +++++++ .../street/model/edge/EscalatorEdge.java | 15 +++++-- .../utils/time/DurationUtils.java | 43 +++++++++++++++++++ .../utils/time/DurationUtilsTest.java | 18 ++++++++ 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java index 75e0965d82f..ff439a37f59 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java @@ -27,30 +27,35 @@ public void buildEscalatorEdge(OsmWay escalatorWay, double length) { .boxed() .toList(); + Long duration = escalatorWay.getDurationSeconds(); for (int i = 0; i < nodes.size() - 1; i++) { if (escalatorWay.isForwardEscalator()) { EscalatorEdge.createEscalatorEdge( intersectionNodes.get(nodes.get(i)), intersectionNodes.get(nodes.get(i + 1)), - length + length, + duration ); } else if (escalatorWay.isBackwardEscalator()) { EscalatorEdge.createEscalatorEdge( intersectionNodes.get(nodes.get(i + 1)), intersectionNodes.get(nodes.get(i)), - length + length, + duration ); } else { EscalatorEdge.createEscalatorEdge( intersectionNodes.get(nodes.get(i)), intersectionNodes.get(nodes.get(i + 1)), - length + length, + duration ); EscalatorEdge.createEscalatorEdge( intersectionNodes.get(nodes.get(i + 1)), intersectionNodes.get(nodes.get(i)), - length + length, + duration ); } } diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java index 7b5fbe56748..24423024311 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java @@ -2,9 +2,11 @@ import gnu.trove.list.TLongList; import gnu.trove.list.array.TLongArrayList; +import java.time.format.DateTimeParseException; import java.util.Set; import org.opentripplanner.graph_builder.module.osm.StreetTraversalPermissionPair; import org.opentripplanner.street.model.StreetTraversalPermission; +import org.opentripplanner.utils.time.DurationUtils; public class OsmWay extends OsmWithTags { @@ -130,6 +132,19 @@ public boolean isEscalator() { return (isTag("highway", "steps") && isOneOfTags("conveying", ESCALATOR_CONVEYING_TAGS)); } + public Long getDurationSeconds() { + var duration = getTag("duration"); + if (duration != null) { + try { + return DurationUtils.parseClockDuration(duration).getSeconds(); + } catch (DateTimeParseException e) { + // For malformed duration tags, just pretend they weren't there. + return null; + } + } + return null; + } + public boolean isForwardEscalator() { return isEscalator() && "forward".equals(this.getTag("conveying")); } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java index 20fae657c78..35c2db5cbc2 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java @@ -17,10 +17,12 @@ public class EscalatorEdge extends Edge { private static final double HORIZONTAL_SPEED = 0.45; private static final LocalizedString NAME = new LocalizedString("name.escalator"); private final double length; + private final Long duration; - private EscalatorEdge(Vertex v1, Vertex v2, double length) { + private EscalatorEdge(Vertex v1, Vertex v2, double length, Long duration) { super(v1, v2); this.length = length; + this.duration = duration; } @Override @@ -28,7 +30,12 @@ public State[] traverse(State s0) { // Only allow traversal by walking if (s0.currentMode() == TraverseMode.WALK && !s0.getRequest().wheelchair()) { var s1 = s0.edit(this); - var time = getDistanceMeters() / HORIZONTAL_SPEED; + double time; + if (duration == null) { + time = getDistanceMeters() / HORIZONTAL_SPEED; + } else { + time = duration; + } s1.incrementWeight(s0.getPreferences().walk().escalatorReluctance() * time); s1.incrementTimeInSeconds((int) Math.round(time)); s1.incrementWalkDistance(getDistanceMeters()); @@ -51,7 +58,7 @@ public I18NString getName() { return NAME; } - public static EscalatorEdge createEscalatorEdge(Vertex from, Vertex to, double length) { - return connectToGraph(new EscalatorEdge(from, to, length)); + public static EscalatorEdge createEscalatorEdge(Vertex from, Vertex to, double length, Long duration) { + return connectToGraph(new EscalatorEdge(from, to, length, duration)); } } diff --git a/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java b/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java index d73faecee03..10f04700f3f 100644 --- a/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java +++ b/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java @@ -11,6 +11,7 @@ import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.opentripplanner.utils.lang.StringUtils; /** * This class extend the Java {@link Duration} with utility functionality to parse and convert @@ -123,6 +124,48 @@ public static Duration duration(String duration) { } } + /** + * Parse a duration string in format hh:mm:ss. + * @param duration string in format hh:mm:ss + * @return Duration + * @throws DateTimeParseException on bad input + */ + public static Duration parseClockDuration(String duration) { + int colonCount = (int) duration.chars().filter(ch -> ch == ':').count(); + if (colonCount <= 2) { + try { + int i, j; + long hours, minutes = 0, seconds = 0; + switch (colonCount) { + case 0: + hours = Long.parseLong(duration); + break; + case 1: + i = duration.indexOf(':'); + hours = Long.parseLong(duration.substring(0, i)); + minutes = Long.parseLong(duration.substring(i + 1)); + break; + default: + //case 2: + i = duration.indexOf(':'); + j = duration.indexOf(':', i + 1); + hours = Long.parseLong(duration.substring(0, i)); + minutes = Long.parseLong(duration.substring(i + 1, j)); + seconds = Long.parseLong(duration.substring(j + 1)); + break; + } + if (hours >= 0 && minutes >= 0 && minutes < 60 && seconds >= 0 && seconds < 60) { + return Duration.ofHours(hours) + .plus(Duration.ofMinutes(minutes)) + .plus(Duration.ofSeconds(seconds)); + } + } catch (NumberFormatException e) { + // fallthrough + } + } + throw new DateTimeParseException("bad clock duration", duration, 0); + } + /** * This is used to parse a string which may be a number {@code NNNN}(number of seconds) or a * duration with format {@code NhNmNs}, where {@code N} is a decimal number and diff --git a/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java b/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java index ef2e0f50901..1e0839091cb 100644 --- a/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java +++ b/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java @@ -30,6 +30,8 @@ public class DurationUtilsTest { private final Duration D5m = Duration.ofMinutes(5); private final Duration D9s = Duration.ofSeconds(9); private final Duration D3d5m9s = D3d.plus(D5m).plus(D9s); + private final Duration D2h5m = D2h.plus(D5m); + private final Duration D2h5m9s = D2h.plus(D5m).plus(D9s); private final int I9h31m = durationSec(9, 31, 0); private final int I9h36m55s = durationSec(9, 36, 55); private final int I13h33m57s = durationSec(13, 33, 57); @@ -91,6 +93,22 @@ public void duration() { assertEquals(-D9s.toSeconds(), DurationUtils.duration("-9", ChronoUnit.SECONDS).toSeconds()); } + @Test + public void parseClockDuration() { + assertEquals(D2h, DurationUtils.parseClockDuration("2")); + assertEquals(D2h5m, DurationUtils.parseClockDuration("02:05")); + assertEquals(D2h5m9s, DurationUtils.parseClockDuration("02:05:09")); + assertThrows( + DateTimeParseException.class, + () -> DurationUtils.parseClockDuration("02:65:09")); + assertThrows( + DateTimeParseException.class, + () -> DurationUtils.parseClockDuration("02:05:09:00")); + assertThrows( + DateTimeParseException.class, + () -> DurationUtils.parseClockDuration("02:x5:09")); + } + @Test public void parseSecondsOrDuration() { assertEquals(D9s, DurationUtils.parseSecondsOrDuration("9s").orElseThrow()); From 405f580ec1d1ca7e0df3f8fa2fe5d27aa9ae19cb Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Tue, 19 Nov 2024 17:19:18 +0200 Subject: [PATCH 042/162] Remove support for feeds argument --- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 11 +--------- .../apis/gtfs/generated/GraphQLTypes.java | 10 ---------- .../model/TimetableSnapshot.java | 14 ------------- .../service/DefaultTransitService.java | 20 ------------------- .../transit/service/TransitService.java | 7 ------- .../opentripplanner/apis/gtfs/schema.graphqls | 2 -- .../service/DefaultTransitServiceTest.java | 18 ----------------- .../apis/gtfs/queries/canceled-trips.graphql | 2 +- 8 files changed, 2 insertions(+), 82 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java index 2f886f81734..b36cb0a46ba 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -833,16 +833,7 @@ public DataFetcher> trips() { @Override public DataFetcher> canceledTrips() { return environment -> { - var args = new GraphQLTypes.GraphQLQueryTypeCanceledTripsArgs(environment.getArguments()); - if (args.getGraphQLFeeds() != null && args.getGraphQLFeeds().isEmpty()) { - throw new IllegalArgumentException( - "Feeds need to be either not specified or contain elements." - ); - } - - var trips = args.getGraphQLFeeds() != null - ? getTransitService(environment).findCanceledTrips(args.getGraphQLFeeds()) - : getTransitService(environment).listCanceledTrips(); + var trips = getTransitService(environment).listCanceledTrips(); return new SimpleListConnection<>(trips).get(environment); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index 37d5ff674dd..32e78eef231 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -2410,7 +2410,6 @@ public static class GraphQLQueryTypeCanceledTripsArgs { private String after; private String before; - private List feeds; private Integer first; private Integer last; @@ -2418,7 +2417,6 @@ public GraphQLQueryTypeCanceledTripsArgs(Map args) { if (args != null) { this.after = (String) args.get("after"); this.before = (String) args.get("before"); - this.feeds = (List) args.get("feeds"); this.first = (Integer) args.get("first"); this.last = (Integer) args.get("last"); } @@ -2432,10 +2430,6 @@ public String getGraphQLBefore() { return this.before; } - public List getGraphQLFeeds() { - return this.feeds; - } - public Integer getGraphQLFirst() { return this.first; } @@ -2452,10 +2446,6 @@ public void setGraphQLBefore(String before) { this.before = before; } - public void setGraphQLFeeds(List feeds) { - this.feeds = feeds; - } - public void setGraphQLFirst(Integer first) { this.first = first; } diff --git a/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java b/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java index 77e5d1f6fbb..44ce1e5cb18 100644 --- a/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java +++ b/application/src/main/java/org/opentripplanner/model/TimetableSnapshot.java @@ -226,20 +226,6 @@ public TripPattern getNewTripPatternForModifiedTrip(FeedScopedId tripId, LocalDa return realTimeNewTripPatternsForModifiedTrips.get(tripIdAndServiceDate); } - /** - * Find trips which have been canceled. - * - * @param feeds only return trips from these feeds. Empty list is not allowed. - */ - public List findCanceledTrips(List feeds) { - if (feeds == null || feeds.isEmpty()) { - throw new IllegalArgumentException("Feeds list cannot be null or empty"); - } - return findTripsOnServiceDates(tripTimes -> - tripTimes.isCanceled() && feeds.contains(tripTimes.getTrip().getId().getFeedId()) - ); - } - /** * List trips which have been canceled. */ diff --git a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 2dd31e93da3..8b9e473d193 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -295,26 +295,6 @@ public Trip getScheduledTripForId(FeedScopedId id) { return this.timetableRepositoryIndex.getTripForId(id); } - /** - * TODO This only supports realtime cancelled trips for now. - */ - @Override - public List findCanceledTrips(List feeds) { - if (feeds == null || feeds.isEmpty()) { - throw new IllegalArgumentException( - "Feeds list cannot be empty or null. It needs to have elements." - ); - } - OTPRequestTimeoutException.checkForTimeout(); - var timetableSnapshot = lazyGetTimeTableSnapShot(); - if (timetableSnapshot == null) { - return List.of(); - } - List canceledTrips = timetableSnapshot.findCanceledTrips(feeds); - canceledTrips.sort(new TripOnServiceDateComparator()); - return canceledTrips; - } - /** * TODO This only supports realtime cancelled trips for now. */ diff --git a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java index 4e0b49d559f..e534b154dd0 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -169,13 +169,6 @@ public interface TransitService { */ Collection getAllTrips(); - /** - * Find canceled trips. - * - * @param feeds used for filtering. Empty list is not allowed. - */ - List findCanceledTrips(List feeds); - /** * List all canceled trips. */ diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 07a89270370..2bd7d42a451 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -1151,8 +1151,6 @@ type QueryType { and should be used together with the `last` parameter. """ before: String, - "Feed feedIds (e.g. [\"HSL\"])." - feeds: [String], """ Limits how many trips are returned. This parameter is part of the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm) and can be used together with diff --git a/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index 534b0103854..868d89d61d7 100644 --- a/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/application/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -1,6 +1,5 @@ package org.opentripplanner.transit.service; -import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.opentripplanner.model.plan.PlanTestConstants.T11_30; import static org.opentripplanner.transit.model.basic.TransitMode.BUS; @@ -157,21 +156,4 @@ void listCanceledTrips() { var canceledTrips = service.listCanceledTrips(); assertEquals("[TripOnServiceDate{F:123}, TripOnServiceDate{F:123}]", canceledTrips.toString()); } - - @Test - void findCanceledTrips() { - var canceledTripsForFeedWithCancellations = service.findCanceledTrips(List.of("F", "G")); - assertEquals( - "[TripOnServiceDate{F:123}, TripOnServiceDate{F:123}]", - canceledTripsForFeedWithCancellations.toString() - ); - - var canceledTripsForFeedWithoutCancellations = service.findCanceledTrips(List.of("G")); - assertEquals("[]", canceledTripsForFeedWithoutCancellations.toString()); - } - - @Test - void findCanceledTripsWithEmptyFeeds() { - assertThrows(IllegalArgumentException.class, () -> service.findCanceledTrips(List.of())); - } } diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql index 8d5685a06ba..cf767d81f10 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -1,5 +1,5 @@ { - canceledTrips(feeds: "F") { + canceledTrips(first: 2) { pageInfo { hasNextPage hasPreviousPage From ac21d8a099ea751d55ab1b0c6216e5273c8ef82e Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Thu, 21 Nov 2024 14:14:29 +0200 Subject: [PATCH 043/162] Improve doc --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 2bd7d42a451..15a877e3ed4 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -300,11 +300,12 @@ type BookingTime { "Real-time estimates for an arrival or departure at a certain place." type CallRealTimeEstimate { """ - The delay or "earliness" of the vehicle at a certain place. + The delay or "earliness" of the vehicle at a certain place. This estimate can change quite often. If the vehicle is early then this is a negative duration. """ delay: Duration! + "The estimate for a call event (such as arrival or departure) at a certain place. This estimate can change quite often." time: OffsetDateTime! } From d9ead12868cc132284260423c69b6bfdb7582ee0 Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Fri, 22 Nov 2024 12:08:22 +0200 Subject: [PATCH 044/162] make default escalator speed a configuration parameter --- .../module/osm/EscalatorProcessor.java | 2 +- .../org/opentripplanner/osm/model/OsmWay.java | 8 +- .../preference/EscalatorPreferences.java | 74 +++++++++++++++++++ .../request/preference/StreetPreferences.java | 16 ++++ .../routerequest/RouteRequestConfig.java | 11 +++ .../street/model/edge/EscalatorEdge.java | 17 +++-- .../street/model/edge/EscalatorEdgeTest.java | 23 ++++-- doc/user/RouteRequest.md | 1 + .../utils/time/DurationUtils.java | 5 +- .../utils/time/DurationUtilsTest.java | 11 +-- 10 files changed, 141 insertions(+), 27 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java index ff439a37f59..488fc96a0b3 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java @@ -27,7 +27,7 @@ public void buildEscalatorEdge(OsmWay escalatorWay, double length) { .boxed() .toList(); - Long duration = escalatorWay.getDurationSeconds(); + Integer duration = escalatorWay.getDurationSeconds(); for (int i = 0; i < nodes.size() - 1; i++) { if (escalatorWay.isForwardEscalator()) { EscalatorEdge.createEscalatorEdge( diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java index 24423024311..0819ab57b59 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java @@ -132,11 +132,15 @@ public boolean isEscalator() { return (isTag("highway", "steps") && isOneOfTags("conveying", ESCALATOR_CONVEYING_TAGS)); } - public Long getDurationSeconds() { + public Integer getDurationSeconds() { var duration = getTag("duration"); if (duration != null) { try { - return DurationUtils.parseClockDuration(duration).getSeconds(); + long seconds = DurationUtils.parseClockDuration(duration).getSeconds(); + if (seconds < 0 || seconds > Integer.MAX_VALUE) { + return null; + } + return (int) seconds; } catch (DateTimeParseException e) { // For malformed duration tags, just pretend they weren't there. return null; diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java new file mode 100644 index 00000000000..af4cbda39ef --- /dev/null +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java @@ -0,0 +1,74 @@ +package org.opentripplanner.routing.api.request.preference; + +import java.io.Serializable; +import java.util.function.Consumer; + +public class EscalatorPreferences implements Serializable { + + public static final EscalatorPreferences DEFAULT = new EscalatorPreferences(); + + private final double horizontalSpeed; + + /* A quick internet search gives escalator speed range of 0.3-0.6 m/s and angle of 30 degrees. + * Using the angle of 30 degrees and a speed of 0.5 m/s gives a horizontal component + * of approx. 0.43 m/s */ + private static final double HORIZONTAL_SPEED = 0.45; + + private EscalatorPreferences() { + this.horizontalSpeed = HORIZONTAL_SPEED; + } + + private EscalatorPreferences(Builder builder) { + this.horizontalSpeed = builder.horizontalSpeed; + } + + public static Builder of() { + return DEFAULT.copyOf(); + } + + public Builder copyOf() { + return new Builder(this); + } + + public double horizontalSpeed() { + return horizontalSpeed; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + EscalatorPreferences that = (EscalatorPreferences) o; + return horizontalSpeed == that.horizontalSpeed; + } + + public static class Builder { + + private final EscalatorPreferences original; + private double horizontalSpeed; + + public Builder(EscalatorPreferences original) { + this.original = original; + this.horizontalSpeed = original.horizontalSpeed; + } + + public EscalatorPreferences original() { + return original; + } + + public Builder withHorizontalSpeed(double horizontalSpeed) { + this.horizontalSpeed = horizontalSpeed; + return this; + } + + public Builder apply(Consumer body) { + body.accept(this); + return this; + } + + public EscalatorPreferences build() { + var value = new EscalatorPreferences(this); + return original.equals(value) ? original : value; + } + } +} diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java index bb526ceaa31..e20ed82f9a5 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java @@ -30,6 +30,7 @@ public final class StreetPreferences implements Serializable { private final double turnReluctance; private final DrivingDirection drivingDirection; private final ElevatorPreferences elevator; + private final EscalatorPreferences escalator; private final AccessEgressPreferences accessEgress; private final IntersectionTraversalModel intersectionTraversalModel; private final DurationForEnum maxDirectDuration; @@ -39,6 +40,7 @@ private StreetPreferences() { this.turnReluctance = 1.0; this.drivingDirection = DrivingDirection.RIGHT; this.elevator = ElevatorPreferences.DEFAULT; + this.escalator = EscalatorPreferences.DEFAULT; this.accessEgress = AccessEgressPreferences.DEFAULT; this.intersectionTraversalModel = IntersectionTraversalModel.SIMPLE; this.maxDirectDuration = durationForStreetModeOf(ofHours(4)); @@ -49,6 +51,7 @@ private StreetPreferences(Builder builder) { this.turnReluctance = Units.reluctance(builder.turnReluctance); this.drivingDirection = requireNonNull(builder.drivingDirection); this.elevator = requireNonNull(builder.elevator); + this.escalator = requireNonNull(builder.escalator); this.accessEgress = requireNonNull(builder.accessEgress); this.intersectionTraversalModel = requireNonNull(builder.intersectionTraversalModel); this.maxDirectDuration = requireNonNull(builder.maxDirectDuration); @@ -78,6 +81,10 @@ public ElevatorPreferences elevator() { return elevator; } + public EscalatorPreferences escalator() { + return escalator; + } + /** Preferences for access/egress routing */ public AccessEgressPreferences accessEgress() { return accessEgress; @@ -110,6 +117,7 @@ public boolean equals(Object o) { DoubleUtils.doubleEquals(that.turnReluctance, turnReluctance) && drivingDirection == that.drivingDirection && elevator.equals(that.elevator) && + escalator.equals(that.escalator) && routingTimeout.equals(that.routingTimeout) && intersectionTraversalModel == that.intersectionTraversalModel && maxDirectDuration.equals(that.maxDirectDuration) && @@ -138,6 +146,7 @@ public String toString() { .addEnum("drivingDirection", drivingDirection, DEFAULT.drivingDirection) .addDuration("routingTimeout", routingTimeout, DEFAULT.routingTimeout()) .addObj("elevator", elevator, DEFAULT.elevator) + .addObj("escalator", escalator, DEFAULT.escalator) .addObj( "intersectionTraversalModel", intersectionTraversalModel, @@ -154,6 +163,7 @@ public static class Builder { private double turnReluctance; private DrivingDirection drivingDirection; private ElevatorPreferences elevator; + private EscalatorPreferences escalator; private IntersectionTraversalModel intersectionTraversalModel; private DurationForEnum maxDirectDuration; private Duration routingTimeout; @@ -164,6 +174,7 @@ public Builder(StreetPreferences original) { this.turnReluctance = original.turnReluctance; this.drivingDirection = original.drivingDirection; this.elevator = original.elevator; + this.escalator = original.escalator; this.intersectionTraversalModel = original.intersectionTraversalModel; this.accessEgress = original.accessEgress; this.maxDirectDuration = original.maxDirectDuration; @@ -189,6 +200,11 @@ public Builder withElevator(Consumer body) { return this; } + public Builder withEscalator(Consumer body) { + this.escalator = escalator.copyOf().apply(body).build(); + return this; + } + public Builder withAccessEgress(Consumer body) { this.accessEgress = accessEgress.copyOf().apply(body).build(); return this; diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java index d05aee96ccf..9c344afc44c 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java @@ -6,6 +6,7 @@ import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_3; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_4; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_5; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; import static org.opentripplanner.standalone.config.routerequest.ItineraryFiltersConfig.mapItineraryFilterParams; import static org.opentripplanner.standalone.config.routerequest.TransferConfig.mapTransferPreferences; import static org.opentripplanner.standalone.config.routerequest.TriangleOptimizationConfig.mapOptimizationTriangle; @@ -459,6 +460,16 @@ private static void mapStreetPreferences(NodeAdapter c, StreetPreferences.Builde .asInt(dftElevator.hopTime()) ); }) + .withEscalator(escalator -> { + var dftEscalator = dft.escalator(); + escalator.withHorizontalSpeed( + c + .of("escalatorSpeed") + .since(V2_7) + .summary("How fast does an escalator move horizontally?") + .asDouble(dftEscalator.horizontalSpeed()) + ); + }) .withAccessEgress(accessEgress -> { var dftAccessEgress = dft.accessEgress(); accessEgress diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java index 35c2db5cbc2..6f9e9c74b2f 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java @@ -11,15 +11,11 @@ /** Represents an escalator. An escalator edge can only be traversed by walking */ public class EscalatorEdge extends Edge { - /* A quick internet search gives escalator speed range of 0.3-0.6 m/s and angle of 30 degrees. - * Using the angle of 30 degrees and a speed of 0.5 m/s gives a horizontal component - * of approx. 0.43 m/s */ - private static final double HORIZONTAL_SPEED = 0.45; private static final LocalizedString NAME = new LocalizedString("name.escalator"); private final double length; - private final Long duration; + private final Integer duration; - private EscalatorEdge(Vertex v1, Vertex v2, double length, Long duration) { + private EscalatorEdge(Vertex v1, Vertex v2, double length, Integer duration) { super(v1, v2); this.length = length; this.duration = duration; @@ -32,7 +28,7 @@ public State[] traverse(State s0) { var s1 = s0.edit(this); double time; if (duration == null) { - time = getDistanceMeters() / HORIZONTAL_SPEED; + time = getDistanceMeters() / s0.getPreferences().street().escalator().horizontalSpeed(); } else { time = duration; } @@ -58,7 +54,12 @@ public I18NString getName() { return NAME; } - public static EscalatorEdge createEscalatorEdge(Vertex from, Vertex to, double length, Long duration) { + public static EscalatorEdge createEscalatorEdge( + Vertex from, + Vertex to, + double length, + Integer duration + ) { return connectToGraph(new EscalatorEdge(from, to, length, duration)); } } diff --git a/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java b/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java index 1cfff635c45..9e0b4f7d297 100644 --- a/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java +++ b/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java @@ -27,20 +27,29 @@ static Stream args() { @ParameterizedTest(name = "escalatorReluctance of {0} should lead to traversal costs of {1}") @MethodSource("args") void testWalking(double escalatorReluctance, double expectedWeight) { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 45); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, null); var req = StreetSearchRequest .of() .withPreferences(p -> p.withWalk(w -> w.withEscalatorReluctance(escalatorReluctance))) .withMode(StreetMode.WALK); var res = edge.traverse(new State(from, req.build()))[0]; - assertEquals(res.weight, expectedWeight); - assertEquals(res.getTimeDeltaSeconds(), 100); + assertEquals(expectedWeight, res.weight); + assertEquals(100, res.getTimeDeltaSeconds()); + } + + @Test + void testDuration() { + // If duration is given, length does not affect timeDeltaSeconds, only duration does. + var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, 60); + var req = StreetSearchRequest.of().withMode(StreetMode.WALK); + var res = edge.traverse(new State(from, req.build()))[0]; + assertEquals(60, res.getTimeDeltaSeconds()); } @Test void testCycling() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); var req = StreetSearchRequest.of().withMode(StreetMode.BIKE); var res = edge.traverse(new State(from, req.build())); assertThat(res).isEmpty(); @@ -48,7 +57,7 @@ void testCycling() { @Test void testWheelchair() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); var req = StreetSearchRequest.of().withMode(StreetMode.WALK).withWheelchair(true); var res = edge.traverse(new State(from, req.build())); assertThat(res).isEmpty(); @@ -56,14 +65,14 @@ void testWheelchair() { @Test void name() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); assertEquals("Rolltreppe", edge.getName().toString(Locale.GERMANY)); assertEquals("escalator", edge.getName().toString(Locale.ENGLISH)); } @Test void geometry() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); assertThat(edge.getGeometry().getCoordinates()).isNotEmpty(); } } diff --git a/doc/user/RouteRequest.md b/doc/user/RouteRequest.md index ea3d0d12c74..c0c5d2bb590 100644 --- a/doc/user/RouteRequest.md +++ b/doc/user/RouteRequest.md @@ -23,6 +23,7 @@ and in the [transferRequests in build-config.json](BuildConfiguration.md#transfe | elevatorBoardTime | `integer` | How long does it take to get on an elevator, on average. | *Optional* | `90` | 2.0 | | elevatorHopCost | `integer` | What is the cost of travelling one floor on an elevator? | *Optional* | `20` | 2.0 | | elevatorHopTime | `integer` | How long does it take to advance one floor on an elevator? | *Optional* | `20` | 2.0 | +| escalatorSpeed | `double` | How fast does an escalator move horizontally? | *Optional* | `0.45` | 2.7 | | geoidElevation | `boolean` | If true, the Graph's ellipsoidToGeoidDifference is applied to all elevations returned by this query. | *Optional* | `false` | 2.0 | | ignoreRealtimeUpdates | `boolean` | When true, real-time updates are ignored during this search. | *Optional* | `false` | 2.0 | | [intersectionTraversalModel](#rd_intersectionTraversalModel) | `enum` | The model that computes the costs of turns. | *Optional* | `"simple"` | 2.2 | diff --git a/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java b/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java index 10f04700f3f..e1c0b557855 100644 --- a/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java +++ b/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java @@ -146,7 +146,7 @@ public static Duration parseClockDuration(String duration) { minutes = Long.parseLong(duration.substring(i + 1)); break; default: - //case 2: + //case 2: i = duration.indexOf(':'); j = duration.indexOf(':', i + 1); hours = Long.parseLong(duration.substring(0, i)); @@ -155,7 +155,8 @@ public static Duration parseClockDuration(String duration) { break; } if (hours >= 0 && minutes >= 0 && minutes < 60 && seconds >= 0 && seconds < 60) { - return Duration.ofHours(hours) + return Duration + .ofHours(hours) .plus(Duration.ofMinutes(minutes)) .plus(Duration.ofSeconds(seconds)); } diff --git a/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java b/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java index 1e0839091cb..15fbd6e5027 100644 --- a/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java +++ b/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java @@ -98,15 +98,12 @@ public void parseClockDuration() { assertEquals(D2h, DurationUtils.parseClockDuration("2")); assertEquals(D2h5m, DurationUtils.parseClockDuration("02:05")); assertEquals(D2h5m9s, DurationUtils.parseClockDuration("02:05:09")); + assertThrows(DateTimeParseException.class, () -> DurationUtils.parseClockDuration("02:65:09")); assertThrows( DateTimeParseException.class, - () -> DurationUtils.parseClockDuration("02:65:09")); - assertThrows( - DateTimeParseException.class, - () -> DurationUtils.parseClockDuration("02:05:09:00")); - assertThrows( - DateTimeParseException.class, - () -> DurationUtils.parseClockDuration("02:x5:09")); + () -> DurationUtils.parseClockDuration("02:05:09:00") + ); + assertThrows(DateTimeParseException.class, () -> DurationUtils.parseClockDuration("02:x5:09")); } @Test From ca5310034416fae6c6a6907cd91f8b97e12f7b4f Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 22 Nov 2024 13:03:57 +0200 Subject: [PATCH 045/162] Redesign schema --- .../apis/gtfs/GtfsGraphQLIndex.java | 7 ++----- ...ypeResolver.java => CallTypeResolver.java} | 8 +++---- .../datafetchers/TripOnServiceDateImpl.java | 12 +++++++---- .../gtfs/generated/GraphQLDataFetchers.java | 21 +++++++++++-------- .../opentripplanner/apis/gtfs/schema.graphqls | 19 ++++++++++------- .../apis/gtfs/queries/canceled-trips.graphql | 20 +++++++++++------- 6 files changed, 49 insertions(+), 38 deletions(-) rename application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{CancellableTripOnServiceDateTypeResolver.java => CallTypeResolver.java} (62%) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index ddf251ea833..10e33c0f777 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -33,7 +33,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.BookingTimeImpl; import org.opentripplanner.apis.gtfs.datafetchers.CallRealTimeEstimateImpl; import org.opentripplanner.apis.gtfs.datafetchers.CallTimeImpl; -import org.opentripplanner.apis.gtfs.datafetchers.CancellableTripOnServiceDateTypeResolver; +import org.opentripplanner.apis.gtfs.datafetchers.CallTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.CarParkImpl; import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.CoordinatesImpl; @@ -133,10 +133,7 @@ protected static GraphQLSchema buildSchema() { .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) - .type( - "CancellableTripOnServiceDate", - type -> type.typeResolver(new CancellableTripOnServiceDateTypeResolver()) - ) + .type("Call", type -> type.typeResolver(new CallTypeResolver())) .type(typeWiring.build(AgencyImpl.class)) .type(typeWiring.build(AlertImpl.class)) .type(typeWiring.build(BikeParkImpl.class)) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CancellableTripOnServiceDateTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTypeResolver.java similarity index 62% rename from application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CancellableTripOnServiceDateTypeResolver.java rename to application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTypeResolver.java index 4c9af823767..02ee6784073 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CancellableTripOnServiceDateTypeResolver.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTypeResolver.java @@ -4,17 +4,17 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; -import org.opentripplanner.transit.model.timetable.TripOnServiceDate; +import org.opentripplanner.model.TripTimeOnDate; -public class CancellableTripOnServiceDateTypeResolver implements TypeResolver { +public class CallTypeResolver implements TypeResolver { @Override public GraphQLObjectType getType(TypeResolutionEnvironment environment) { Object o = environment.getObject(); GraphQLSchema schema = environment.getSchema(); - if (o instanceof TripOnServiceDate) { - return schema.getObjectType("TripOnServiceDate"); + if (o instanceof TripTimeOnDate) { + return schema.getObjectType("StopCall"); } return null; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java index 22e45a9afda..b85f63a9d17 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java @@ -24,7 +24,7 @@ public DataFetcher serviceDate() { } @Override - public DataFetcher end() { + public DataFetcher end() { return environment -> { TransitService transitService = getTransitService(environment); Trip trip = getTrip(environment); @@ -42,7 +42,7 @@ public DataFetcher end() { } @Override - public DataFetcher start() { + public DataFetcher start() { return environment -> { TransitService transitService = getTransitService(environment); Trip trip = getTrip(environment); @@ -60,7 +60,7 @@ public DataFetcher start() { } @Override - public DataFetcher> stopCalls() { + public DataFetcher> stopCalls() { return environment -> { TransitService transitService = getTransitService(environment); Trip trip = getTrip(environment); @@ -73,7 +73,11 @@ public DataFetcher> stopCalls() { if (timetable == null) { return List.of(); } - return TripTimeOnDate.fromTripTimes(timetable, trip, serviceDate, midnight); + return TripTimeOnDate + .fromTripTimes(timetable, trip, serviceDate, midnight) + .stream() + .map(Object.class::cast) + .toList(); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 0210a5708dc..375525b1a1f 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -242,6 +242,12 @@ public interface GraphQLBookingTime { public DataFetcher time(); } + /** + * Call is a visit to a location (a stop or an area) in a trip on service date. It can contain + * exact arrival and departure times and/or a scheduled time window. + */ + public interface GraphQLCall extends TypeResolver {} + /** Real-time estimates for an arrival or departure at a certain place. */ public interface GraphQLCallRealTimeEstimate { public DataFetcher delay(); @@ -259,9 +265,6 @@ public interface GraphQLCallTime { public DataFetcher scheduledTime(); } - /** An instance of a trip, which can be cancelled, on a service date. */ - public interface GraphQLCancellableTripOnServiceDate extends TypeResolver {} - /** Car park represents a location where cars can be parked. */ public interface GraphQLCarPark { public DataFetcher carParkId(); @@ -1065,7 +1068,7 @@ public interface GraphQLStop { /** * Stop call represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. - * This can include real-time estimates. + * The times are exact (although can be changed by real-time updates), not time windows. */ public interface GraphQLStopCall { public DataFetcher arrival(); @@ -1241,15 +1244,15 @@ public interface GraphQLTripOccupancy { public DataFetcher occupancyStatus(); } - /** A regular (i.e. not flexible) trip on a specific service date */ + /** A trip on a specific service date. */ public interface GraphQLTripOnServiceDate { - public DataFetcher end(); + public DataFetcher end(); public DataFetcher serviceDate(); - public DataFetcher start(); + public DataFetcher start(); - public DataFetcher> stopCalls(); + public DataFetcher> stopCalls(); public DataFetcher trip(); } @@ -1271,7 +1274,7 @@ public interface GraphQLTripOnServiceDateConnection { public interface GraphQLTripOnServiceDateEdge { public DataFetcher cursor(); - public DataFetcher node(); + public DataFetcher node(); } /** This is used for alert entities that we don't explicitly handle or they are missing. */ diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 15a877e3ed4..b3152a71dd4 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -73,8 +73,11 @@ interface PlaceInterface { "Entity related to an alert" union AlertEntity = Agency | Pattern | Route | RouteType | Stop | StopOnRoute | StopOnTrip | Trip | Unknown -"An instance of a trip, which can be cancelled, on a service date." -union CancellableTripOnServiceDate = TripOnServiceDate +""" +Call is a visit to a location (a stop or an area) in a trip on service date. It can contain +exact arrival and departure times and/or a scheduled time window. +""" +union Call = StopCall union StopPosition = PositionAtStop | PositionBetweenStops @@ -2184,7 +2187,7 @@ type Stop implements Node & PlaceInterface { """ Stop call represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. -This can include real-time estimates. +The times are exact (although can be changed by real-time updates), not time windows. """ type StopCall { "Scheduled arrival time to the stop and a real-time estimate, if one exists." @@ -2450,10 +2453,10 @@ type TripOccupancy { occupancyStatus: OccupancyStatus } -"A regular (i.e. not flexible) trip on a specific service date" +"A trip on a specific service date." type TripOnServiceDate { "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." - end: StopCall! + end: Call! """ The service date when the trip occurs. @@ -2463,9 +2466,9 @@ type TripOnServiceDate { """ serviceDate: LocalDate! "Information related to trip's scheduled departure from the first stop. Can contain realtime information." - start: StopCall! + start: Call! "List of times when this trip arrives to or departs from a stop and information related to the stop" - stopCalls: [StopCall!]! + stopCalls: [Call!]! "This trip on service date is an instance of this trip." trip: Trip } @@ -2501,7 +2504,7 @@ type TripOnServiceDateEdge { Trip on a service date as a node. Part of the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). """ - node: CancellableTripOnServiceDate + node: TripOnServiceDate } "This is used for alert entities that we don't explicitly handle or they are missing." diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql index cf767d81f10..b3817605f37 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -8,9 +8,9 @@ } edges { node { - ... on TripOnServiceDate { - serviceDate - end { + serviceDate + end { + ... on StopCall { arrival { estimated { delay @@ -19,7 +19,9 @@ scheduledTime } } - start { + } + start { + ... on StopCall { departure { estimated { delay @@ -28,7 +30,9 @@ scheduledTime } } - stopCalls { + } + stopCalls { + ... on StopCall { stop { gtfsId } @@ -47,9 +51,9 @@ } } } - trip { - gtfsId - } + } + trip { + gtfsId } } } From 8028990ba63ac01bef999087f0e98bde5fb0c683 Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Mon, 25 Nov 2024 10:31:38 +0200 Subject: [PATCH 046/162] test the added duration tag in OsmWay --- .../java/org/opentripplanner/osm/model/OsmWayTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java index 9ac9457a9ec..a659a767ff4 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java @@ -1,6 +1,8 @@ package org.opentripplanner.osm.model; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; @@ -172,6 +174,13 @@ void escalator() { escalator.addTag("conveying", "yes"); assertTrue(escalator.isEscalator()); + assertNull(escalator.getDurationSeconds()); + + escalator.addTag("duration", "00:00:61"); + assertNull(escalator.getDurationSeconds()); + escalator.addTag("duration", "00:01:01"); + assertEquals(61, escalator.getDurationSeconds()); + escalator.addTag("conveying", "whoknows?"); assertFalse(escalator.isEscalator()); } From aa3de3a455b7c2120268b391999a29596395fc09 Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Mon, 25 Nov 2024 14:10:48 +0200 Subject: [PATCH 047/162] ignore escalator duration tags if the speed are nonsense. include duration in debug client edge popup. --- .../graph_builder/module/osm/EscalatorProcessor.java | 11 +++++++++++ .../inspector/vector/edge/EdgePropertyMapper.java | 5 ++++- .../street/model/edge/EscalatorEdge.java | 4 ++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java index 488fc96a0b3..b97458d642a 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java @@ -14,6 +14,11 @@ class EscalatorProcessor { private final Map intersectionNodes; + // If an escalator is tagged as moving less than 5 cm/s, or more than 5 m/s, + // assume it's an error and ignore it. + private static final double SLOW_ESCALATOR_ERROR_CUTOFF = 0.05; + private static final double FAST_ESCALATOR_ERROR_CUTOFF = 5.0; + public EscalatorProcessor(Map intersectionNodes) { this.intersectionNodes = intersectionNodes; } @@ -28,6 +33,12 @@ public void buildEscalatorEdge(OsmWay escalatorWay, double length) { .toList(); Integer duration = escalatorWay.getDurationSeconds(); + if (duration != null) { + double speed = length / duration; + if (speed < SLOW_ESCALATOR_ERROR_CUTOFF || speed > FAST_ESCALATOR_ERROR_CUTOFF) { + duration = null; + } + } for (int i = 0; i < nodes.size() - 1; i++) { if (escalatorWay.isForwardEscalator()) { EscalatorEdge.createEscalatorEdge( diff --git a/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java index 30763edca9e..13956af99c4 100644 --- a/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java +++ b/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -22,7 +22,10 @@ protected Collection map(Edge input) { List properties = switch (input) { case StreetEdge e -> mapStreetEdge(e); - case EscalatorEdge e -> List.of(kv("distance", e.getDistanceMeters())); + case EscalatorEdge e -> List.of( + kv("distance", e.getDistanceMeters()), + kv("duration", e.getDuration()) + ); default -> List.of(); }; return ListUtils.combine(baseProps, properties); diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java index 6f9e9c74b2f..2e69793ccf1 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java @@ -49,6 +49,10 @@ public double getDistanceMeters() { return length; } + public Integer getDuration() { + return duration; + } + @Override public I18NString getName() { return NAME; From de5758b7b502168df8e882c602d6553d145b3be0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Nov 2024 12:39:34 +0100 Subject: [PATCH 048/162] Add module test for listing canceled trips --- .../cancellation/CanceledTripTest.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 application/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CanceledTripTest.java diff --git a/application/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CanceledTripTest.java b/application/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CanceledTripTest.java new file mode 100644 index 00000000000..75c8d5caa46 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CanceledTripTest.java @@ -0,0 +1,41 @@ +package org.opentripplanner.updater.trip.moduletests.cancellation; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.CANCELED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.transit.model._data.TimetableRepositoryForTest.id; +import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.updater.trip.RealtimeTestConstants; +import org.opentripplanner.updater.trip.RealtimeTestEnvironment; +import org.opentripplanner.updater.trip.TripInput; +import org.opentripplanner.updater.trip.TripUpdateBuilder; + +public class CanceledTripTest implements RealtimeTestConstants { + + @Test + void listCanceledTrips() { + var env = RealtimeTestEnvironment + .gtfs() + .addTrip( + TripInput + .of(TRIP_1_ID) + .addStop(STOP_A1, "0:00:10", "0:00:11") + .addStop(STOP_B1, "0:00:20", "0:00:21") + .build() + ) + .build(); + + assertThat(env.getTransitService().listCanceledTrips()).isEmpty(); + + var update = new TripUpdateBuilder(TRIP_1_ID, SERVICE_DATE, CANCELED, TIME_ZONE).build(); + assertSuccess(env.applyTripUpdate(update)); + + var canceled = env.getTransitService().listCanceledTrips(); + assertThat(canceled).hasSize(1); + var trip = canceled.getFirst(); + assertEquals(id(TRIP_1_ID), trip.getTrip().getId()); + assertEquals(SERVICE_DATE, trip.getServiceDate()); + } +} From e6c003678c86007917c460e17431c7e03ac1daf8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Nov 2024 13:12:51 +0100 Subject: [PATCH 049/162] Remove duplication --- .../datafetchers/TripOnServiceDateImpl.java | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java index b85f63a9d17..ad419f0c228 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java @@ -26,55 +26,49 @@ public DataFetcher serviceDate() { @Override public DataFetcher end() { return environment -> { - TransitService transitService = getTransitService(environment); - Trip trip = getTrip(environment); - var serviceDate = getSource(environment).getServiceDate(); - - Instant midnight = ServiceDateUtils - .asStartOfService(serviceDate, transitService.getTimeZone()) - .toInstant(); - Timetable timetable = getTimetable(environment, trip, serviceDate); - if (timetable == null) { + final Result result = getResult(environment); + if (result.timetable() == null) { return null; } - return TripTimeOnDate.lastFromTripTimes(timetable, trip, serviceDate, midnight); + return TripTimeOnDate.lastFromTripTimes(result.timetable(), result.trip(), result.serviceDate(), result.midnight()); }; } + private Result getResult(DataFetchingEnvironment environment) { + TransitService transitService = getTransitService(environment); + Trip trip = getTrip(environment); + var serviceDate = getSource(environment).getServiceDate(); + + Instant midnight = ServiceDateUtils + .asStartOfService(serviceDate, transitService.getTimeZone()) + .toInstant(); + Timetable timetable = getTimetable(environment, trip, serviceDate); + return new Result(trip, serviceDate, midnight, timetable); + } + + private record Result(Trip trip, LocalDate serviceDate, Instant midnight, @Nullable Timetable timetable) { + } + @Override public DataFetcher start() { return environment -> { - TransitService transitService = getTransitService(environment); - Trip trip = getTrip(environment); - var serviceDate = getSource(environment).getServiceDate(); - - Instant midnight = ServiceDateUtils - .asStartOfService(serviceDate, transitService.getTimeZone()) - .toInstant(); - Timetable timetable = getTimetable(environment, trip, serviceDate); - if (timetable == null) { + var result = getResult(environment); + if (result.timetable() == null) { return null; } - return TripTimeOnDate.firstFromTripTimes(timetable, trip, serviceDate, midnight); + return TripTimeOnDate.firstFromTripTimes(result.timetable, result.trip, result.serviceDate, result.midnight); }; } @Override public DataFetcher> stopCalls() { return environment -> { - TransitService transitService = getTransitService(environment); - Trip trip = getTrip(environment); - var serviceDate = getSource(environment).getServiceDate(); - - Instant midnight = ServiceDateUtils - .asStartOfService(serviceDate, transitService.getTimeZone()) - .toInstant(); - Timetable timetable = getTimetable(environment, trip, serviceDate); - if (timetable == null) { + var result = getResult(environment); + if (result.timetable == null) { return List.of(); } return TripTimeOnDate - .fromTripTimes(timetable, trip, serviceDate, midnight) + .fromTripTimes(result.timetable, result.trip, result.serviceDate, result.midnight) .stream() .map(Object.class::cast) .toList(); From 456619446b332854f46f8337598847cef845accd Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Mon, 25 Nov 2024 14:29:13 +0200 Subject: [PATCH 050/162] use Optional instead of Integer which can be null --- .../module/osm/EscalatorProcessor.java | 10 ++++++---- .../java/org/opentripplanner/osm/model/OsmWay.java | 14 ++++++-------- .../street/model/edge/EscalatorEdge.java | 14 ++++++++------ 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java index b97458d642a..1d9640f0a6f 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java @@ -1,8 +1,10 @@ package org.opentripplanner.graph_builder.module.osm; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Optional; import org.opentripplanner.osm.model.OsmWay; import org.opentripplanner.street.model.edge.EscalatorEdge; import org.opentripplanner.street.model.vertex.IntersectionVertex; @@ -32,11 +34,11 @@ public void buildEscalatorEdge(OsmWay escalatorWay, double length) { .boxed() .toList(); - Integer duration = escalatorWay.getDurationSeconds(); - if (duration != null) { - double speed = length / duration; + Optional duration = escalatorWay.getDuration(); + if (duration.isPresent()) { + double speed = length / duration.get().toSeconds(); if (speed < SLOW_ESCALATOR_ERROR_CUTOFF || speed > FAST_ESCALATOR_ERROR_CUTOFF) { - duration = null; + duration = Optional.empty(); } } for (int i = 0; i < nodes.size() - 1; i++) { diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java index 0819ab57b59..e80a3ab6499 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java @@ -2,7 +2,9 @@ import gnu.trove.list.TLongList; import gnu.trove.list.array.TLongArrayList; +import java.time.Duration; import java.time.format.DateTimeParseException; +import java.util.Optional; import java.util.Set; import org.opentripplanner.graph_builder.module.osm.StreetTraversalPermissionPair; import org.opentripplanner.street.model.StreetTraversalPermission; @@ -132,21 +134,17 @@ public boolean isEscalator() { return (isTag("highway", "steps") && isOneOfTags("conveying", ESCALATOR_CONVEYING_TAGS)); } - public Integer getDurationSeconds() { + public Optional getDuration() { var duration = getTag("duration"); if (duration != null) { try { - long seconds = DurationUtils.parseClockDuration(duration).getSeconds(); - if (seconds < 0 || seconds > Integer.MAX_VALUE) { - return null; - } - return (int) seconds; + return Optional.of(DurationUtils.parseClockDuration(duration)); } catch (DateTimeParseException e) { // For malformed duration tags, just pretend they weren't there. - return null; + return Optional.empty(); } } - return null; + return Optional.empty(); } public boolean isForwardEscalator() { diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java index 2e69793ccf1..825d642373b 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java @@ -1,5 +1,7 @@ package org.opentripplanner.street.model.edge; +import java.time.Duration; +import java.util.Optional; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.I18NString; @@ -13,9 +15,9 @@ public class EscalatorEdge extends Edge { private static final LocalizedString NAME = new LocalizedString("name.escalator"); private final double length; - private final Integer duration; + private final Optional duration; - private EscalatorEdge(Vertex v1, Vertex v2, double length, Integer duration) { + private EscalatorEdge(Vertex v1, Vertex v2, double length, Optional duration) { super(v1, v2); this.length = length; this.duration = duration; @@ -27,10 +29,10 @@ public State[] traverse(State s0) { if (s0.currentMode() == TraverseMode.WALK && !s0.getRequest().wheelchair()) { var s1 = s0.edit(this); double time; - if (duration == null) { + if (duration.isEmpty()) { time = getDistanceMeters() / s0.getPreferences().street().escalator().horizontalSpeed(); } else { - time = duration; + time = duration.get().toSeconds(); } s1.incrementWeight(s0.getPreferences().walk().escalatorReluctance() * time); s1.incrementTimeInSeconds((int) Math.round(time)); @@ -49,7 +51,7 @@ public double getDistanceMeters() { return length; } - public Integer getDuration() { + public Optional getDuration() { return duration; } @@ -62,7 +64,7 @@ public static EscalatorEdge createEscalatorEdge( Vertex from, Vertex to, double length, - Integer duration + Optional duration ) { return connectToGraph(new EscalatorEdge(from, to, length, duration)); } From 3127cf35b0d8f1169034d9a86cdf571183792b3b Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Mon, 25 Nov 2024 14:34:54 +0200 Subject: [PATCH 051/162] test changes included --- .../org/opentripplanner/osm/model/OsmWayTest.java | 8 +++++--- .../street/model/edge/EscalatorEdgeTest.java | 14 ++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java index a659a767ff4..869e579e9aa 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java @@ -5,6 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.Duration; +import java.util.Optional; import org.junit.jupiter.api.Test; import org.opentripplanner.osm.wayproperty.specifier.WayTestData; @@ -174,12 +176,12 @@ void escalator() { escalator.addTag("conveying", "yes"); assertTrue(escalator.isEscalator()); - assertNull(escalator.getDurationSeconds()); + assertEquals(Optional.empty(), escalator.getDuration()); escalator.addTag("duration", "00:00:61"); - assertNull(escalator.getDurationSeconds()); + assertEquals(Optional.empty(), escalator.getDuration()); escalator.addTag("duration", "00:01:01"); - assertEquals(61, escalator.getDurationSeconds()); + assertEquals(Optional.of(Duration.ofSeconds(61)), escalator.getDuration()); escalator.addTag("conveying", "whoknows?"); assertFalse(escalator.isEscalator()); diff --git a/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java b/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java index 9e0b4f7d297..601aeb0bc27 100644 --- a/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java +++ b/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java @@ -3,7 +3,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.time.Duration; import java.util.Locale; +import java.util.Optional; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -27,7 +29,7 @@ static Stream args() { @ParameterizedTest(name = "escalatorReluctance of {0} should lead to traversal costs of {1}") @MethodSource("args") void testWalking(double escalatorReluctance, double expectedWeight) { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, null); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, Optional.empty()); var req = StreetSearchRequest .of() .withPreferences(p -> p.withWalk(w -> w.withEscalatorReluctance(escalatorReluctance))) @@ -41,7 +43,7 @@ void testWalking(double escalatorReluctance, double expectedWeight) { @Test void testDuration() { // If duration is given, length does not affect timeDeltaSeconds, only duration does. - var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, 60); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, Optional.of(Duration.ofSeconds(60))); var req = StreetSearchRequest.of().withMode(StreetMode.WALK); var res = edge.traverse(new State(from, req.build()))[0]; assertEquals(60, res.getTimeDeltaSeconds()); @@ -49,7 +51,7 @@ void testDuration() { @Test void testCycling() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, Optional.empty()); var req = StreetSearchRequest.of().withMode(StreetMode.BIKE); var res = edge.traverse(new State(from, req.build())); assertThat(res).isEmpty(); @@ -57,7 +59,7 @@ void testCycling() { @Test void testWheelchair() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, Optional.empty()); var req = StreetSearchRequest.of().withMode(StreetMode.WALK).withWheelchair(true); var res = edge.traverse(new State(from, req.build())); assertThat(res).isEmpty(); @@ -65,14 +67,14 @@ void testWheelchair() { @Test void name() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, Optional.empty()); assertEquals("Rolltreppe", edge.getName().toString(Locale.GERMANY)); assertEquals("escalator", edge.getName().toString(Locale.ENGLISH)); } @Test void geometry() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, Optional.empty()); assertThat(edge.getGeometry().getCoordinates()).isNotEmpty(); } } From bee85dee2f4991da17b4b07788fda9ae79f4c0da Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 25 Nov 2024 14:47:06 +0200 Subject: [PATCH 052/162] Renaming or ordering --- .../datafetchers/TripOnServiceDateImpl.java | 67 ++++++++++++------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java index ad419f0c228..aae18085cc1 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java @@ -26,49 +26,49 @@ public DataFetcher serviceDate() { @Override public DataFetcher end() { return environment -> { - final Result result = getResult(environment); - if (result.timetable() == null) { + var arguments = getFromTripTimesArguments(environment); + if (arguments.timetable() == null) { return null; } - return TripTimeOnDate.lastFromTripTimes(result.timetable(), result.trip(), result.serviceDate(), result.midnight()); + return TripTimeOnDate.lastFromTripTimes( + arguments.timetable(), + arguments.trip(), + arguments.serviceDate(), + arguments.midnight() + ); }; } - private Result getResult(DataFetchingEnvironment environment) { - TransitService transitService = getTransitService(environment); - Trip trip = getTrip(environment); - var serviceDate = getSource(environment).getServiceDate(); - - Instant midnight = ServiceDateUtils - .asStartOfService(serviceDate, transitService.getTimeZone()) - .toInstant(); - Timetable timetable = getTimetable(environment, trip, serviceDate); - return new Result(trip, serviceDate, midnight, timetable); - } - - private record Result(Trip trip, LocalDate serviceDate, Instant midnight, @Nullable Timetable timetable) { - } - @Override public DataFetcher start() { return environment -> { - var result = getResult(environment); - if (result.timetable() == null) { + var arguments = getFromTripTimesArguments(environment); + if (arguments.timetable() == null) { return null; } - return TripTimeOnDate.firstFromTripTimes(result.timetable, result.trip, result.serviceDate, result.midnight); + return TripTimeOnDate.firstFromTripTimes( + arguments.timetable(), + arguments.trip(), + arguments.serviceDate(), + arguments.midnight() + ); }; } @Override public DataFetcher> stopCalls() { return environment -> { - var result = getResult(environment); - if (result.timetable == null) { + var arguments = getFromTripTimesArguments(environment); + if (arguments.timetable() == null) { return List.of(); } return TripTimeOnDate - .fromTripTimes(result.timetable, result.trip, result.serviceDate, result.midnight) + .fromTripTimes( + arguments.timetable(), + arguments.trip(), + arguments.serviceDate(), + arguments.midnight() + ) .stream() .map(Object.class::cast) .toList(); @@ -107,4 +107,23 @@ private Trip getTrip(DataFetchingEnvironment environment) { private TripOnServiceDate getSource(DataFetchingEnvironment environment) { return environment.getSource(); } + + private FromTripTimesArguments getFromTripTimesArguments(DataFetchingEnvironment environment) { + TransitService transitService = getTransitService(environment); + Trip trip = getTrip(environment); + var serviceDate = getSource(environment).getServiceDate(); + + Instant midnight = ServiceDateUtils + .asStartOfService(serviceDate, transitService.getTimeZone()) + .toInstant(); + Timetable timetable = getTimetable(environment, trip, serviceDate); + return new FromTripTimesArguments(trip, serviceDate, midnight, timetable); + } + + private record FromTripTimesArguments( + Trip trip, + LocalDate serviceDate, + Instant midnight, + @Nullable Timetable timetable + ) {} } From dba65b0cbb564e0b703e1fe732522e1475f76024 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Tue, 26 Nov 2024 11:47:26 +0200 Subject: [PATCH 053/162] Update application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index fb8baa0eb9a..d7e540e1a1b 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -323,7 +323,7 @@ type CallTime { "The estimated time of the event. If no real-time information is available, this is null." estimated: CallRealTimeEstimate "The scheduled time of the event." - scheduledTime: OffsetDateTime! + scheduledTime: OffsetDateTime } "Car park represents a location where cars can be parked." From 1e5aca122c89934b9a9b5382c0bb5575ff54aa1d Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Tue, 26 Nov 2024 14:43:15 +0200 Subject: [PATCH 054/162] use Optional upwards only --- .../module/osm/EscalatorProcessor.java | 8 ++++---- .../street/model/edge/EscalatorEdge.java | 13 +++++++------ .../street/model/edge/EscalatorEdgeTest.java | 13 ++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java index 1d9640f0a6f..22a91305f22 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java @@ -47,28 +47,28 @@ public void buildEscalatorEdge(OsmWay escalatorWay, double length) { intersectionNodes.get(nodes.get(i)), intersectionNodes.get(nodes.get(i + 1)), length, - duration + duration.orElse(null) ); } else if (escalatorWay.isBackwardEscalator()) { EscalatorEdge.createEscalatorEdge( intersectionNodes.get(nodes.get(i + 1)), intersectionNodes.get(nodes.get(i)), length, - duration + duration.orElse(null) ); } else { EscalatorEdge.createEscalatorEdge( intersectionNodes.get(nodes.get(i)), intersectionNodes.get(nodes.get(i + 1)), length, - duration + duration.orElse(null) ); EscalatorEdge.createEscalatorEdge( intersectionNodes.get(nodes.get(i + 1)), intersectionNodes.get(nodes.get(i)), length, - duration + duration.orElse(null) ); } } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java index 825d642373b..7dab48dc3e2 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java @@ -2,6 +2,7 @@ import java.time.Duration; import java.util.Optional; +import javax.annotation.Nullable; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.I18NString; @@ -15,9 +16,9 @@ public class EscalatorEdge extends Edge { private static final LocalizedString NAME = new LocalizedString("name.escalator"); private final double length; - private final Optional duration; + private final Duration duration; - private EscalatorEdge(Vertex v1, Vertex v2, double length, Optional duration) { + private EscalatorEdge(Vertex v1, Vertex v2, double length, Duration duration) { super(v1, v2); this.length = length; this.duration = duration; @@ -29,10 +30,10 @@ public State[] traverse(State s0) { if (s0.currentMode() == TraverseMode.WALK && !s0.getRequest().wheelchair()) { var s1 = s0.edit(this); double time; - if (duration.isEmpty()) { + if (duration == null) { time = getDistanceMeters() / s0.getPreferences().street().escalator().horizontalSpeed(); } else { - time = duration.get().toSeconds(); + time = duration.toSeconds(); } s1.incrementWeight(s0.getPreferences().walk().escalatorReluctance() * time); s1.incrementTimeInSeconds((int) Math.round(time)); @@ -52,7 +53,7 @@ public double getDistanceMeters() { } public Optional getDuration() { - return duration; + return Optional.ofNullable(duration); } @Override @@ -64,7 +65,7 @@ public static EscalatorEdge createEscalatorEdge( Vertex from, Vertex to, double length, - Optional duration + @Nullable Duration duration ) { return connectToGraph(new EscalatorEdge(from, to, length, duration)); } diff --git a/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java b/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java index 601aeb0bc27..25199e373a0 100644 --- a/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java +++ b/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java @@ -5,7 +5,6 @@ import java.time.Duration; import java.util.Locale; -import java.util.Optional; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -29,7 +28,7 @@ static Stream args() { @ParameterizedTest(name = "escalatorReluctance of {0} should lead to traversal costs of {1}") @MethodSource("args") void testWalking(double escalatorReluctance, double expectedWeight) { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, Optional.empty()); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, null); var req = StreetSearchRequest .of() .withPreferences(p -> p.withWalk(w -> w.withEscalatorReluctance(escalatorReluctance))) @@ -43,7 +42,7 @@ void testWalking(double escalatorReluctance, double expectedWeight) { @Test void testDuration() { // If duration is given, length does not affect timeDeltaSeconds, only duration does. - var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, Optional.of(Duration.ofSeconds(60))); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, Duration.ofSeconds(60)); var req = StreetSearchRequest.of().withMode(StreetMode.WALK); var res = edge.traverse(new State(from, req.build()))[0]; assertEquals(60, res.getTimeDeltaSeconds()); @@ -51,7 +50,7 @@ void testDuration() { @Test void testCycling() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, Optional.empty()); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); var req = StreetSearchRequest.of().withMode(StreetMode.BIKE); var res = edge.traverse(new State(from, req.build())); assertThat(res).isEmpty(); @@ -59,7 +58,7 @@ void testCycling() { @Test void testWheelchair() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, Optional.empty()); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); var req = StreetSearchRequest.of().withMode(StreetMode.WALK).withWheelchair(true); var res = edge.traverse(new State(from, req.build())); assertThat(res).isEmpty(); @@ -67,14 +66,14 @@ void testWheelchair() { @Test void name() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, Optional.empty()); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); assertEquals("Rolltreppe", edge.getName().toString(Locale.GERMANY)); assertEquals("escalator", edge.getName().toString(Locale.ENGLISH)); } @Test void geometry() { - var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, Optional.empty()); + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10, null); assertThat(edge.getGeometry().getCoordinates()).isNotEmpty(); } } From 8f2c1465229654fe96e00b6843ef8a8ef4c86231 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Tue, 26 Nov 2024 21:48:28 +0200 Subject: [PATCH 055/162] Remove unused GtfsGraphQlApiRentalStationFuzzyMatching feature --- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 23 +------ .../framework/application/OTPFeature.java | 5 -- doc/user/Configuration.md | 67 +++++++++---------- 3 files changed, 34 insertions(+), 61 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java index fa9623f7c55..c0b89b81fef 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -37,7 +37,6 @@ import org.opentripplanner.ext.fares.impl.DefaultFareService; import org.opentripplanner.ext.fares.impl.GtfsFaresService; import org.opentripplanner.ext.fares.model.FareRuleSet; -import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.DirectionMapper; import org.opentripplanner.model.TripTimeOnDate; @@ -56,7 +55,6 @@ import org.opentripplanner.routing.graphfinder.PatternAtStop; import org.opentripplanner.routing.graphfinder.PlaceAtDistance; import org.opentripplanner.routing.graphfinder.PlaceType; -import org.opentripplanner.routing.services.TransitAlertService; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.service.vehiclerental.VehicleRentalService; @@ -886,11 +884,7 @@ public DataFetcher vehicleRentalStation() { return vehicleRentalStationService .getVehicleRentalStations() .stream() - .filter(vehicleRentalStation -> - OTPFeature.GtfsGraphQlApiRentalStationFuzzyMatching.isOn() - ? stationIdFuzzyMatches(vehicleRentalStation, id) - : stationIdMatches(vehicleRentalStation, id) - ) + .filter(vehicleRentalStation -> stationIdMatches(vehicleRentalStation, id)) .findAny() .orElse(null); }; @@ -961,21 +955,6 @@ private boolean stationIdMatches(VehicleRentalStation station, String feedScoped return station.getId().toString().equals(feedScopedId); } - /** - * This matches station's feedScopedId to the given string if the string is feed scoped (i.e - * contains a `:` separator) or only matches the station's id without the feed to the given - * string. This approach can lead to a random station matching the criteria if there are multiple - * stations with the same id in different feeds. - *

- * TODO this can be potentially removed after a while, only used by Digitransit as of now. - */ - private boolean stationIdFuzzyMatches(VehicleRentalStation station, String idWithoutFeed) { - if (idWithoutFeed != null && idWithoutFeed.contains(":")) { - return stationIdMatches(station, idWithoutFeed); - } - return station.getId().getId().equals(idWithoutFeed); - } - private TransitService getTransitService(DataFetchingEnvironment environment) { return environment.getContext().transitService(); } diff --git a/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java b/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java index 6631287613b..f71283b572e 100644 --- a/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java +++ b/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java @@ -50,11 +50,6 @@ public enum OTPFeature { ), FloatingBike(true, false, "Enable floating bike routing."), GtfsGraphQlApi(true, false, "Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md)."), - GtfsGraphQlApiRentalStationFuzzyMatching( - false, - false, - "Does vehicleRentalStation query also allow ids that are not feed scoped." - ), /** * If this feature flag is switched on, then the minimum transfer time is not the minimum transfer * time, but the definitive transfer time. Use this to override what we think the transfer will diff --git a/doc/user/Configuration.md b/doc/user/Configuration.md index f80966b8cb3..13ba16889b1 100644 --- a/doc/user/Configuration.md +++ b/doc/user/Configuration.md @@ -219,40 +219,39 @@ Here is a list of all features which can be toggled on/off and their default val -| Feature | Description | Enabled by default | Sandbox | -|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------:|:-------:| -| `APIBikeRental` | Enable the bike rental endpoint. | ✓️ | | -| `APIServerInfo` | Enable the server info endpoint. | ✓️ | | -| `APIUpdaterStatus` | Enable endpoint for graph updaters status. | ✓️ | | -| `IncludeEmptyRailStopsInTransfers` | Turning this on guarantees that Rail stops without scheduled departures still get included when generating transfers using `ConsiderPatternsForDirectTransfers`. It is common for stops to be assign at real-time for Rail. Turning this on will help to avoid dropping transfers which are needed, when the stop is in use later. Turning this on, if ConsiderPatternsForDirectTransfers is off has no effect. | | | -| `ConsiderPatternsForDirectTransfers` | Enable limiting transfers so that there is only a single transfer to each pattern. | ✓️ | | -| `DebugUi` | Enable the debug GraphQL client and web UI and located at the root of the web server as well as the debug map tiles it uses. Be aware that the map tiles are not a stable API and can change without notice. Use the [vector tiles feature if](sandbox/MapboxVectorTilesApi.md) you want a stable map tiles API. | ✓️ | | -| `ExtraTransferLegOnSameStop` | Should there be a transfer leg when transferring on the very same stop. Note that for in-seat/interlined transfers no transfer leg will be generated. | | | -| `FloatingBike` | Enable floating bike routing. | ✓️ | | -| `GtfsGraphQlApi` | Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md). | ✓️ | | -| `GtfsGraphQlApiRentalStationFuzzyMatching` | Does vehicleRentalStation query also allow ids that are not feed scoped. | | | -| `MinimumTransferTimeIsDefinitive` | If the minimum transfer time is a lower bound (default) or the definitive time for the transfer. Set this to `true` if you want to set a transfer time lower than what OTP derives from OSM data. | | | -| `OptimizeTransfers` | OTP will inspect all itineraries found and optimize where (which stops) the transfer will happen. Waiting time, priority and guaranteed transfers are taken into account. | ✓️ | | -| `ParallelRouting` | Enable performing parts of the trip planning in parallel. | | | -| `TransferConstraints` | Enforce transfers to happen according to the _transfers.txt_ (GTFS) and Interchanges (NeTEx). Turning this _off_ will increase the routing performance a little. | ✓️ | | -| `TransmodelGraphQlApi` | Enable the [Transmodel (NeTEx) GraphQL API](apis/TransmodelApi.md). | ✓️ | ✓️ | -| `ActuatorAPI` | Endpoint for actuators (service health status). | | ✓️ | -| `AsyncGraphQLFetchers` | Whether the @async annotation in the GraphQL schema should lead to the fetch being executed asynchronously. This allows batch or alias queries to run in parallel at the cost of consuming extra threads. | | | -| `Co2Emissions` | Enable the emissions sandbox module. | | ✓️ | -| `DataOverlay` | Enable usage of data overlay when calculating costs for the street network. | | ✓️ | -| `FaresV2` | Enable import of GTFS-Fares v2 data. | | ✓️ | -| `FlexRouting` | Enable FLEX routing. | | ✓️ | -| `GoogleCloudStorage` | Enable Google Cloud Storage integration. | | ✓️ | -| `LegacyRestApi` | Enable legacy REST API. This API will be removed in the future. | | ✓️ | -| `MultiCriteriaGroupMaxFilter` | Keep the best itinerary with respect to each criteria used in the transit-routing search. For example the itinerary with the lowest cost, fewest transfers, and each unique transit-group (transit-group-priority) is kept, even if the max-limit is exceeded. This is turned off by default for now, until this feature is well tested. | | | -| `RealtimeResolver` | When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with real-time data | | ✓️ | -| `ReportApi` | Enable the report API. | | ✓️ | -| `RestAPIPassInDefaultConfigAsJson` | Enable a default RouteRequest to be passed in as JSON on the REST API - FOR DEBUGGING ONLY! | | | -| `SandboxAPIGeocoder` | Enable the Geocoder API. | | ✓️ | -| `SandboxAPIMapboxVectorTilesApi` | Enable Mapbox vector tiles API. | | ✓️ | -| `SandboxAPIParkAndRideApi` | Enable park-and-ride endpoint. | | ✓️ | -| `Sorlandsbanen` | Include train Sørlandsbanen in results when searching in south of Norway. Only relevant in Norway. | | ✓️ | -| `TransferAnalyzer` | Analyze transfers during graph build. | | ✓️ | +| Feature | Description | Enabled by default | Sandbox | +|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------:|:-------:| +| `APIBikeRental` | Enable the bike rental endpoint. | ✓️ | | +| `APIServerInfo` | Enable the server info endpoint. | ✓️ | | +| `APIUpdaterStatus` | Enable endpoint for graph updaters status. | ✓️ | | +| `IncludeEmptyRailStopsInTransfers` | Turning this on guarantees that Rail stops without scheduled departures still get included when generating transfers using `ConsiderPatternsForDirectTransfers`. It is common for stops to be assign at real-time for Rail. Turning this on will help to avoid dropping transfers which are needed, when the stop is in use later. Turning this on, if ConsiderPatternsForDirectTransfers is off has no effect. | | | +| `ConsiderPatternsForDirectTransfers` | Enable limiting transfers so that there is only a single transfer to each pattern. | ✓️ | | +| `DebugUi` | Enable the debug GraphQL client and web UI and located at the root of the web server as well as the debug map tiles it uses. Be aware that the map tiles are not a stable API and can change without notice. Use the [vector tiles feature if](sandbox/MapboxVectorTilesApi.md) you want a stable map tiles API. | ✓️ | | +| `ExtraTransferLegOnSameStop` | Should there be a transfer leg when transferring on the very same stop. Note that for in-seat/interlined transfers no transfer leg will be generated. | | | +| `FloatingBike` | Enable floating bike routing. | ✓️ | | +| `GtfsGraphQlApi` | Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md). | ✓️ | | +| `MinimumTransferTimeIsDefinitive` | If the minimum transfer time is a lower bound (default) or the definitive time for the transfer. Set this to `true` if you want to set a transfer time lower than what OTP derives from OSM data. | | | +| `OptimizeTransfers` | OTP will inspect all itineraries found and optimize where (which stops) the transfer will happen. Waiting time, priority and guaranteed transfers are taken into account. | ✓️ | | +| `ParallelRouting` | Enable performing parts of the trip planning in parallel. | | | +| `TransferConstraints` | Enforce transfers to happen according to the _transfers.txt_ (GTFS) and Interchanges (NeTEx). Turning this _off_ will increase the routing performance a little. | ✓️ | | +| `TransmodelGraphQlApi` | Enable the [Transmodel (NeTEx) GraphQL API](apis/TransmodelApi.md). | ✓️ | ✓️ | +| `ActuatorAPI` | Endpoint for actuators (service health status). | | ✓️ | +| `AsyncGraphQLFetchers` | Whether the @async annotation in the GraphQL schema should lead to the fetch being executed asynchronously. This allows batch or alias queries to run in parallel at the cost of consuming extra threads. | | | +| `Co2Emissions` | Enable the emissions sandbox module. | | ✓️ | +| `DataOverlay` | Enable usage of data overlay when calculating costs for the street network. | | ✓️ | +| `FaresV2` | Enable import of GTFS-Fares v2 data. | | ✓️ | +| `FlexRouting` | Enable FLEX routing. | | ✓️ | +| `GoogleCloudStorage` | Enable Google Cloud Storage integration. | | ✓️ | +| `LegacyRestApi` | Enable legacy REST API. This API will be removed in the future. | | ✓️ | +| `MultiCriteriaGroupMaxFilter` | Keep the best itinerary with respect to each criteria used in the transit-routing search. For example the itinerary with the lowest cost, fewest transfers, and each unique transit-group (transit-group-priority) is kept, even if the max-limit is exceeded. This is turned off by default for now, until this feature is well tested. | | | +| `RealtimeResolver` | When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with real-time data | | ✓️ | +| `ReportApi` | Enable the report API. | | ✓️ | +| `RestAPIPassInDefaultConfigAsJson` | Enable a default RouteRequest to be passed in as JSON on the REST API - FOR DEBUGGING ONLY! | | | +| `SandboxAPIGeocoder` | Enable the Geocoder API. | | ✓️ | +| `SandboxAPIMapboxVectorTilesApi` | Enable Mapbox vector tiles API. | | ✓️ | +| `SandboxAPIParkAndRideApi` | Enable park-and-ride endpoint. | | ✓️ | +| `Sorlandsbanen` | Include train Sørlandsbanen in results when searching in south of Norway. Only relevant in Norway. | | ✓️ | +| `TransferAnalyzer` | Analyze transfers during graph build. | | ✓️ | From 627b3fd84aec882a5f68d09271941e2f1508f588 Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Thu, 28 Nov 2024 15:26:31 +0200 Subject: [PATCH 056/162] add bad duration tags to issue store, better testing of duration parsing --- .../module/osm/EscalatorProcessor.java | 28 ++++++- .../graph_builder/module/osm/OsmModule.java | 5 +- .../org/opentripplanner/osm/model/OsmWay.java | 14 +--- .../osm/model/OsmWithTags.java | 66 +++++++++++++++++ .../preference/EscalatorPreferences.java | 74 ------------------- .../request/preference/StreetPreferences.java | 16 ---- .../request/preference/WalkPreferences.java | 33 +++++++-- .../routerequest/RouteRequestConfig.java | 18 ++--- .../street/model/edge/EscalatorEdge.java | 6 +- .../opentripplanner/osm/model/OsmWayTest.java | 8 +- .../osm/model/OsmWithTagsTest.java | 23 ++++++ .../standalone/config/router-config.json | 3 +- doc/user/RouteRequest.md | 14 +++- doc/user/RouterConfiguration.md | 3 +- .../utils/time/DurationUtils.java | 43 ----------- .../utils/time/DurationUtilsTest.java | 15 ---- 16 files changed, 179 insertions(+), 190 deletions(-) delete mode 100644 application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java index 22a91305f22..c8f4e0e0042 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.graph_builder.issue.api.Issue; import org.opentripplanner.osm.model.OsmWay; import org.opentripplanner.street.model.edge.EscalatorEdge; import org.opentripplanner.street.model.vertex.IntersectionVertex; @@ -15,14 +17,19 @@ class EscalatorProcessor { private final Map intersectionNodes; + private final DataImportIssueStore issueStore; // If an escalator is tagged as moving less than 5 cm/s, or more than 5 m/s, // assume it's an error and ignore it. private static final double SLOW_ESCALATOR_ERROR_CUTOFF = 0.05; private static final double FAST_ESCALATOR_ERROR_CUTOFF = 5.0; - public EscalatorProcessor(Map intersectionNodes) { + public EscalatorProcessor( + Map intersectionNodes, + DataImportIssueStore issueStore + ) { this.intersectionNodes = intersectionNodes; + this.issueStore = issueStore; } public void buildEscalatorEdge(OsmWay escalatorWay, double length) { @@ -34,11 +41,28 @@ public void buildEscalatorEdge(OsmWay escalatorWay, double length) { .boxed() .toList(); - Optional duration = escalatorWay.getDuration(); + Optional duration = escalatorWay.getDuration(v -> + issueStore.add( + Issue.issue( + "InvalidDuration", + "Duration for osm node %d is not a valid duration: '%s'; it's replaced with 'Optional.empty()' (unknown).", + escalatorWay.getId(), + v + ) + ) + ); if (duration.isPresent()) { double speed = length / duration.get().toSeconds(); if (speed < SLOW_ESCALATOR_ERROR_CUTOFF || speed > FAST_ESCALATOR_ERROR_CUTOFF) { duration = Optional.empty(); + issueStore.add( + Issue.issue( + "InvalidDuration", + "Duration for osm node {} makes implied speed {} be outside acceptable range.", + escalatorWay.getId(), + speed + ) + ); } } for (int i = 0; i < nodes.size() - 1; i++) { diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 08d23087a45..e4a08b40177 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -247,7 +247,10 @@ private void buildBasicGraph() { long wayCount = osmdb.getWays().size(); ProgressTracker progress = ProgressTracker.track("Build street graph", 5_000, wayCount); LOG.info(progress.startMessage()); - var escalatorProcessor = new EscalatorProcessor(vertexGenerator.intersectionNodes()); + var escalatorProcessor = new EscalatorProcessor( + vertexGenerator.intersectionNodes(), + issueStore + ); WAY:for (OsmWay way : osmdb.getWays()) { WayProperties wayData = way.getOsmProvider().getWayPropertySet().getDataForWay(way); diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java index e80a3ab6499..0cc19363a0a 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java @@ -6,6 +6,7 @@ import java.time.format.DateTimeParseException; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import org.opentripplanner.graph_builder.module.osm.StreetTraversalPermissionPair; import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.utils.time.DurationUtils; @@ -134,17 +135,8 @@ public boolean isEscalator() { return (isTag("highway", "steps") && isOneOfTags("conveying", ESCALATOR_CONVEYING_TAGS)); } - public Optional getDuration() { - var duration = getTag("duration"); - if (duration != null) { - try { - return Optional.of(DurationUtils.parseClockDuration(duration)); - } catch (DateTimeParseException e) { - // For malformed duration tags, just pretend they weren't there. - return Optional.empty(); - } - } - return Optional.empty(); + public Optional getDuration(Consumer errorHandler) { + return getTagAsDuration("duration", errorHandler); } public boolean isForwardEscalator() { diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java index 67f737e4c79..bb3e1056f2f 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -1,5 +1,9 @@ package org.opentripplanner.osm.model; +import java.time.Duration; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -221,6 +225,68 @@ public OptionalInt getTagAsInt(String tag, Consumer errorHandler) { return OptionalInt.empty(); } + /** + * Parse an OSM duration tag, which is one of: + * mm + * hh:mm + * hh:mm:ss + * and where the leading value is not limited to any maximum. + * @param duration string in format mm, hh:mm, or hh:mm:ss + * @return Duration + * @throws DateTimeParseException on bad input + */ + public static Duration parseOsmDuration(String duration) { + int colonCount = (int) duration.chars().filter(ch -> ch == ':').count(); + if (colonCount <= 2) { + try { + int i, j; + long hours, minutes, seconds; + switch (colonCount) { + case 0: + minutes = Long.parseLong(duration); + if (minutes >= 0) { + return Duration.ofMinutes(minutes); + } + break; + case 1: + i = duration.indexOf(':'); + hours = Long.parseLong(duration.substring(0, i)); + minutes = Long.parseLong(duration.substring(i + 1)); + if (hours >= 0 && minutes >= 0 && minutes < 60) { + return Duration.ofHours(hours).plusMinutes(minutes); + } + break; + default: + //case 2: + i = duration.indexOf(':'); + j = duration.indexOf(':', i + 1); + hours = Long.parseLong(duration.substring(0, i)); + minutes = Long.parseLong(duration.substring(i + 1, j)); + seconds = Long.parseLong(duration.substring(j + 1)); + if (hours >= 0 && minutes >= 0 && minutes < 60 && seconds >= 0 && seconds < 60) { + return Duration.ofHours(hours).plusMinutes(minutes).plusSeconds(seconds); + } + break; + } + } catch (NumberFormatException e) { + // fallthrough + } + } + throw new DateTimeParseException("bad clock duration", duration, 0); + } + + public Optional getTagAsDuration(String tag, Consumer errorHandler) { + String value = getTag(tag); + if (value != null) { + try { + return Optional.of(parseOsmDuration(value)); + } catch (DateTimeParseException e) { + errorHandler.accept(value); + } + } + return Optional.empty(); + } + /** * Some tags are allowed to have values like 55, "true" or "false". *

diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java deleted file mode 100644 index af4cbda39ef..00000000000 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.opentripplanner.routing.api.request.preference; - -import java.io.Serializable; -import java.util.function.Consumer; - -public class EscalatorPreferences implements Serializable { - - public static final EscalatorPreferences DEFAULT = new EscalatorPreferences(); - - private final double horizontalSpeed; - - /* A quick internet search gives escalator speed range of 0.3-0.6 m/s and angle of 30 degrees. - * Using the angle of 30 degrees and a speed of 0.5 m/s gives a horizontal component - * of approx. 0.43 m/s */ - private static final double HORIZONTAL_SPEED = 0.45; - - private EscalatorPreferences() { - this.horizontalSpeed = HORIZONTAL_SPEED; - } - - private EscalatorPreferences(Builder builder) { - this.horizontalSpeed = builder.horizontalSpeed; - } - - public static Builder of() { - return DEFAULT.copyOf(); - } - - public Builder copyOf() { - return new Builder(this); - } - - public double horizontalSpeed() { - return horizontalSpeed; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - EscalatorPreferences that = (EscalatorPreferences) o; - return horizontalSpeed == that.horizontalSpeed; - } - - public static class Builder { - - private final EscalatorPreferences original; - private double horizontalSpeed; - - public Builder(EscalatorPreferences original) { - this.original = original; - this.horizontalSpeed = original.horizontalSpeed; - } - - public EscalatorPreferences original() { - return original; - } - - public Builder withHorizontalSpeed(double horizontalSpeed) { - this.horizontalSpeed = horizontalSpeed; - return this; - } - - public Builder apply(Consumer body) { - body.accept(this); - return this; - } - - public EscalatorPreferences build() { - var value = new EscalatorPreferences(this); - return original.equals(value) ? original : value; - } - } -} diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java index e20ed82f9a5..bb526ceaa31 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java @@ -30,7 +30,6 @@ public final class StreetPreferences implements Serializable { private final double turnReluctance; private final DrivingDirection drivingDirection; private final ElevatorPreferences elevator; - private final EscalatorPreferences escalator; private final AccessEgressPreferences accessEgress; private final IntersectionTraversalModel intersectionTraversalModel; private final DurationForEnum maxDirectDuration; @@ -40,7 +39,6 @@ private StreetPreferences() { this.turnReluctance = 1.0; this.drivingDirection = DrivingDirection.RIGHT; this.elevator = ElevatorPreferences.DEFAULT; - this.escalator = EscalatorPreferences.DEFAULT; this.accessEgress = AccessEgressPreferences.DEFAULT; this.intersectionTraversalModel = IntersectionTraversalModel.SIMPLE; this.maxDirectDuration = durationForStreetModeOf(ofHours(4)); @@ -51,7 +49,6 @@ private StreetPreferences(Builder builder) { this.turnReluctance = Units.reluctance(builder.turnReluctance); this.drivingDirection = requireNonNull(builder.drivingDirection); this.elevator = requireNonNull(builder.elevator); - this.escalator = requireNonNull(builder.escalator); this.accessEgress = requireNonNull(builder.accessEgress); this.intersectionTraversalModel = requireNonNull(builder.intersectionTraversalModel); this.maxDirectDuration = requireNonNull(builder.maxDirectDuration); @@ -81,10 +78,6 @@ public ElevatorPreferences elevator() { return elevator; } - public EscalatorPreferences escalator() { - return escalator; - } - /** Preferences for access/egress routing */ public AccessEgressPreferences accessEgress() { return accessEgress; @@ -117,7 +110,6 @@ public boolean equals(Object o) { DoubleUtils.doubleEquals(that.turnReluctance, turnReluctance) && drivingDirection == that.drivingDirection && elevator.equals(that.elevator) && - escalator.equals(that.escalator) && routingTimeout.equals(that.routingTimeout) && intersectionTraversalModel == that.intersectionTraversalModel && maxDirectDuration.equals(that.maxDirectDuration) && @@ -146,7 +138,6 @@ public String toString() { .addEnum("drivingDirection", drivingDirection, DEFAULT.drivingDirection) .addDuration("routingTimeout", routingTimeout, DEFAULT.routingTimeout()) .addObj("elevator", elevator, DEFAULT.elevator) - .addObj("escalator", escalator, DEFAULT.escalator) .addObj( "intersectionTraversalModel", intersectionTraversalModel, @@ -163,7 +154,6 @@ public static class Builder { private double turnReluctance; private DrivingDirection drivingDirection; private ElevatorPreferences elevator; - private EscalatorPreferences escalator; private IntersectionTraversalModel intersectionTraversalModel; private DurationForEnum maxDirectDuration; private Duration routingTimeout; @@ -174,7 +164,6 @@ public Builder(StreetPreferences original) { this.turnReluctance = original.turnReluctance; this.drivingDirection = original.drivingDirection; this.elevator = original.elevator; - this.escalator = original.escalator; this.intersectionTraversalModel = original.intersectionTraversalModel; this.accessEgress = original.accessEgress; this.maxDirectDuration = original.maxDirectDuration; @@ -200,11 +189,6 @@ public Builder withElevator(Consumer body) { return this; } - public Builder withEscalator(Consumer body) { - this.escalator = escalator.copyOf().apply(body).build(); - return this; - } - public Builder withAccessEgress(Consumer body) { this.accessEgress = accessEgress.copyOf().apply(body).build(); return this; diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/WalkPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/WalkPreferences.java index 4a5969049ba..87dcc320c83 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/WalkPreferences.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/WalkPreferences.java @@ -30,6 +30,7 @@ public final class WalkPreferences implements Serializable { private final double safetyFactor; private final double escalatorReluctance; + private final double escalatorSpeed; private WalkPreferences() { this.speed = 1.33; @@ -39,6 +40,7 @@ private WalkPreferences() { this.stairsTimeFactor = 3.0; this.safetyFactor = 1.0; this.escalatorReluctance = 1.5; + this.escalatorSpeed = 0.45; } private WalkPreferences(Builder builder) { @@ -49,6 +51,7 @@ private WalkPreferences(Builder builder) { this.stairsTimeFactor = Units.reluctance(builder.stairsTimeFactor); this.safetyFactor = Units.reluctance(builder.safetyFactor); this.escalatorReluctance = Units.reluctance(builder.escalatorReluctance); + this.escalatorSpeed = Units.speed(builder.escalatorSpeed); } public static Builder of() { @@ -108,6 +111,14 @@ public double safetyFactor() { return safetyFactor; } + public double escalatorReluctance() { + return escalatorReluctance; + } + + public double escalatorSpeed() { + return escalatorSpeed; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -120,7 +131,8 @@ public boolean equals(Object o) { doubleEquals(that.stairsReluctance, stairsReluctance) && doubleEquals(that.stairsTimeFactor, stairsTimeFactor) && doubleEquals(that.safetyFactor, safetyFactor) && - doubleEquals(that.escalatorReluctance, escalatorReluctance) + doubleEquals(that.escalatorReluctance, escalatorReluctance) && + doubleEquals(that.escalatorSpeed, escalatorSpeed) ); } @@ -133,7 +145,8 @@ public int hashCode() { stairsReluctance, stairsTimeFactor, safetyFactor, - escalatorReluctance + escalatorReluctance, + escalatorSpeed ); } @@ -148,13 +161,10 @@ public String toString() { .addNum("stairsTimeFactor", stairsTimeFactor, DEFAULT.stairsTimeFactor) .addNum("safetyFactor", safetyFactor, DEFAULT.safetyFactor) .addNum("escalatorReluctance", escalatorReluctance, DEFAULT.escalatorReluctance) + .addNum("escalatorSpeed", escalatorSpeed, DEFAULT.escalatorSpeed) .toString(); } - public double escalatorReluctance() { - return escalatorReluctance; - } - public static class Builder { private final WalkPreferences original; @@ -166,6 +176,7 @@ public static class Builder { private double safetyFactor; private double escalatorReluctance; + private double escalatorSpeed; public Builder(WalkPreferences original) { this.original = original; @@ -176,6 +187,7 @@ public Builder(WalkPreferences original) { this.stairsTimeFactor = original.stairsTimeFactor; this.safetyFactor = original.safetyFactor; this.escalatorReluctance = original.escalatorReluctance; + this.escalatorSpeed = original.escalatorSpeed; } public WalkPreferences original() { @@ -251,6 +263,15 @@ public Builder withEscalatorReluctance(double escalatorReluctance) { return this; } + public double escalatorSpeed() { + return escalatorSpeed; + } + + public Builder withEscalatorSpeed(double escalatorSpeed) { + this.escalatorSpeed = escalatorSpeed; + return this; + } + public Builder apply(Consumer body) { body.accept(this); return this; diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java index 9c344afc44c..9f0e0ba17b4 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java @@ -460,16 +460,6 @@ private static void mapStreetPreferences(NodeAdapter c, StreetPreferences.Builde .asInt(dftElevator.hopTime()) ); }) - .withEscalator(escalator -> { - var dftEscalator = dft.escalator(); - escalator.withHorizontalSpeed( - c - .of("escalatorSpeed") - .since(V2_7) - .summary("How fast does an escalator move horizontally?") - .asDouble(dftEscalator.horizontalSpeed()) - ); - }) .withAccessEgress(accessEgress -> { var dftAccessEgress = dft.accessEgress(); accessEgress @@ -828,6 +818,14 @@ private static void mapWalkPreferences(NodeAdapter root, WalkPreferences.Builder "A multiplier for how bad being in an escalator is compared to being in transit for equal lengths of time" ) .asDouble(dft.escalatorReluctance()) + ) + .withEscalatorSpeed( + c + .of("escalatorSpeed") + .since(V2_7) + .summary("How fast does an escalator move horizontally?") + .description("Horizontal speed of escalator in m/s.") + .asDouble(dft.escalatorSpeed()) ); } } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java index 7dab48dc3e2..fdfd4ee44b4 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java @@ -31,7 +31,7 @@ public State[] traverse(State s0) { var s1 = s0.edit(this); double time; if (duration == null) { - time = getDistanceMeters() / s0.getPreferences().street().escalator().horizontalSpeed(); + time = getDistanceMeters() / s0.getPreferences().walk().escalatorSpeed(); } else { time = duration.toSeconds(); } @@ -52,6 +52,10 @@ public double getDistanceMeters() { return length; } + /** + * Parsed content of duration tag in OSM, if any. Not a calculated value. + * @return Duration, or empty + */ public Optional getDuration() { return Optional.ofNullable(duration); } diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java index 869e579e9aa..277cda7ac9b 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java @@ -7,6 +7,7 @@ import java.time.Duration; import java.util.Optional; +import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.opentripplanner.osm.wayproperty.specifier.WayTestData; @@ -176,13 +177,6 @@ void escalator() { escalator.addTag("conveying", "yes"); assertTrue(escalator.isEscalator()); - assertEquals(Optional.empty(), escalator.getDuration()); - - escalator.addTag("duration", "00:00:61"); - assertEquals(Optional.empty(), escalator.getDuration()); - escalator.addTag("duration", "00:01:01"); - assertEquals(Optional.of(Duration.ofSeconds(61)), escalator.getDuration()); - escalator.addTag("conveying", "whoknows?"); assertFalse(escalator.isEscalator()); } diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java index a89f8612040..c379fc11b02 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java @@ -5,9 +5,11 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.Duration; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import org.junit.jupiter.api.Test; @@ -298,4 +300,25 @@ void parseIntOrBoolean(String value, OptionalInt expected) { var maybeInt = way.parseIntOrBoolean(key, i -> {}); assertEquals(expected, maybeInt); } + + private static List parseTagAsDurationCases() { + return List.of( + Arguments.of("00:11", Optional.of(Duration.ofMinutes(11))), + Arguments.of("11", Optional.of(Duration.ofMinutes(11))), + Arguments.of("1:22:33", Optional.of(Duration.ofHours(1).plusMinutes(22).plusSeconds(33))), + Arguments.of("22:60", Optional.empty()), + Arguments.of("10:61:40", Optional.empty()), + Arguments.of("10:59:60", Optional.empty()) + ); + } + + @ParameterizedTest + @MethodSource("parseTagAsDurationCases") + void parseTagAsDuration(String value, Optional expected) { + var way = new OsmWithTags(); + var key = "duration"; + way.addTag(key, value); + var duration = way.getTagAsDuration(key, i -> {}); + assertEquals(expected, duration); + } } diff --git a/application/src/test/resources/standalone/config/router-config.json b/application/src/test/resources/standalone/config/router-config.json index f1eac1cb41e..5539ec4de65 100644 --- a/application/src/test/resources/standalone/config/router-config.json +++ b/application/src/test/resources/standalone/config/router-config.json @@ -75,7 +75,8 @@ "reluctance": 4.0, "stairsReluctance": 1.65, "boardCost": 600, - "escalatorReluctance": 1.5 + "escalatorReluctance": 1.5, + "escalatorSpeed": 0.45 }, "waitReluctance": 1.0, "otherThanPreferredRoutesPenalty": 300, diff --git a/doc/user/RouteRequest.md b/doc/user/RouteRequest.md index c0c5d2bb590..c6cdef82c25 100644 --- a/doc/user/RouteRequest.md +++ b/doc/user/RouteRequest.md @@ -23,7 +23,6 @@ and in the [transferRequests in build-config.json](BuildConfiguration.md#transfe | elevatorBoardTime | `integer` | How long does it take to get on an elevator, on average. | *Optional* | `90` | 2.0 | | elevatorHopCost | `integer` | What is the cost of travelling one floor on an elevator? | *Optional* | `20` | 2.0 | | elevatorHopTime | `integer` | How long does it take to advance one floor on an elevator? | *Optional* | `20` | 2.0 | -| escalatorSpeed | `double` | How fast does an escalator move horizontally? | *Optional* | `0.45` | 2.7 | | geoidElevation | `boolean` | If true, the Graph's ellipsoidToGeoidDifference is applied to all elevations returned by this query. | *Optional* | `false` | 2.0 | | ignoreRealtimeUpdates | `boolean` | When true, real-time updates are ignored during this search. | *Optional* | `false` | 2.0 | | [intersectionTraversalModel](#rd_intersectionTraversalModel) | `enum` | The model that computes the costs of turns. | *Optional* | `"simple"` | 2.2 | @@ -157,6 +156,7 @@ and in the [transferRequests in build-config.json](BuildConfiguration.md#transfe | walk | `object` | Walking preferences. | *Optional* | | 2.5 | |    boardCost | `integer` | Prevents unnecessary transfers by adding a cost for boarding a vehicle. This is the cost that is used when boarding while walking. | *Optional* | `600` | 2.0 | |    escalatorReluctance | `double` | A multiplier for how bad being in an escalator is compared to being in transit for equal lengths of time | *Optional* | `1.5` | 2.4 | +|    [escalatorSpeed](#rd_walk_escalatorSpeed) | `double` | How fast does an escalator move horizontally? | *Optional* | `0.45` | 2.7 | |    [reluctance](#rd_walk_reluctance) | `double` | A multiplier for how bad walking is, compared to being in transit for equal lengths of time. | *Optional* | `2.0` | 2.0 | |    [safetyFactor](#rd_walk_safetyFactor) | `double` | Factor for how much the walk safety is considered in routing. | *Optional* | `1.0` | 2.2 | |    speed | `double` | The user's walking speed in meters/second. | *Optional* | `1.33` | 2.0 | @@ -1072,6 +1072,15 @@ The ids of the routes that incur an extra cost when being used. Format: `FeedId: How much cost is added is configured in `unpreferredCost`. +

escalatorSpeed

+ +**Since version:** `2.7` ∙ **Type:** `double` ∙ **Cardinality:** `Optional` ∙ **Default value:** `0.45` +**Path:** /routingDefaults/walk + +How fast does an escalator move horizontally? + +Horizontal speed of escalator in m/s. +

reluctance

**Since version:** `2.0` ∙ **Type:** `double` ∙ **Cardinality:** `Optional` ∙ **Default value:** `2.0` @@ -1215,7 +1224,8 @@ include stairs as a last result. "reluctance" : 4.0, "stairsReluctance" : 1.65, "boardCost" : 600, - "escalatorReluctance" : 1.5 + "escalatorReluctance" : 1.5, + "escalatorSpeed" : 0.45 }, "waitReluctance" : 1.0, "otherThanPreferredRoutesPenalty" : 300, diff --git a/doc/user/RouterConfiguration.md b/doc/user/RouterConfiguration.md index 6dbd1174397..043e4f2f485 100644 --- a/doc/user/RouterConfiguration.md +++ b/doc/user/RouterConfiguration.md @@ -528,7 +528,8 @@ Used to group requests when monitoring OTP. "reluctance" : 4.0, "stairsReluctance" : 1.65, "boardCost" : 600, - "escalatorReluctance" : 1.5 + "escalatorReluctance" : 1.5, + "escalatorSpeed" : 0.45 }, "waitReluctance" : 1.0, "otherThanPreferredRoutesPenalty" : 300, diff --git a/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java b/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java index e1c0b557855..78a6ae16885 100644 --- a/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java +++ b/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java @@ -124,49 +124,6 @@ public static Duration duration(String duration) { } } - /** - * Parse a duration string in format hh:mm:ss. - * @param duration string in format hh:mm:ss - * @return Duration - * @throws DateTimeParseException on bad input - */ - public static Duration parseClockDuration(String duration) { - int colonCount = (int) duration.chars().filter(ch -> ch == ':').count(); - if (colonCount <= 2) { - try { - int i, j; - long hours, minutes = 0, seconds = 0; - switch (colonCount) { - case 0: - hours = Long.parseLong(duration); - break; - case 1: - i = duration.indexOf(':'); - hours = Long.parseLong(duration.substring(0, i)); - minutes = Long.parseLong(duration.substring(i + 1)); - break; - default: - //case 2: - i = duration.indexOf(':'); - j = duration.indexOf(':', i + 1); - hours = Long.parseLong(duration.substring(0, i)); - minutes = Long.parseLong(duration.substring(i + 1, j)); - seconds = Long.parseLong(duration.substring(j + 1)); - break; - } - if (hours >= 0 && minutes >= 0 && minutes < 60 && seconds >= 0 && seconds < 60) { - return Duration - .ofHours(hours) - .plus(Duration.ofMinutes(minutes)) - .plus(Duration.ofSeconds(seconds)); - } - } catch (NumberFormatException e) { - // fallthrough - } - } - throw new DateTimeParseException("bad clock duration", duration, 0); - } - /** * This is used to parse a string which may be a number {@code NNNN}(number of seconds) or a * duration with format {@code NhNmNs}, where {@code N} is a decimal number and diff --git a/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java b/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java index 15fbd6e5027..ef2e0f50901 100644 --- a/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java +++ b/utils/src/test/java/org/opentripplanner/utils/time/DurationUtilsTest.java @@ -30,8 +30,6 @@ public class DurationUtilsTest { private final Duration D5m = Duration.ofMinutes(5); private final Duration D9s = Duration.ofSeconds(9); private final Duration D3d5m9s = D3d.plus(D5m).plus(D9s); - private final Duration D2h5m = D2h.plus(D5m); - private final Duration D2h5m9s = D2h.plus(D5m).plus(D9s); private final int I9h31m = durationSec(9, 31, 0); private final int I9h36m55s = durationSec(9, 36, 55); private final int I13h33m57s = durationSec(13, 33, 57); @@ -93,19 +91,6 @@ public void duration() { assertEquals(-D9s.toSeconds(), DurationUtils.duration("-9", ChronoUnit.SECONDS).toSeconds()); } - @Test - public void parseClockDuration() { - assertEquals(D2h, DurationUtils.parseClockDuration("2")); - assertEquals(D2h5m, DurationUtils.parseClockDuration("02:05")); - assertEquals(D2h5m9s, DurationUtils.parseClockDuration("02:05:09")); - assertThrows(DateTimeParseException.class, () -> DurationUtils.parseClockDuration("02:65:09")); - assertThrows( - DateTimeParseException.class, - () -> DurationUtils.parseClockDuration("02:05:09:00") - ); - assertThrows(DateTimeParseException.class, () -> DurationUtils.parseClockDuration("02:x5:09")); - } - @Test public void parseSecondsOrDuration() { assertEquals(D9s, DurationUtils.parseSecondsOrDuration("9s").orElseThrow()); From dccc6d3ddc1b2c23c6ee10f086fef37cdd3addab Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Thu, 28 Nov 2024 16:11:23 +0200 Subject: [PATCH 057/162] add tests for the osm duration tag cases where the leading unit is larger than localdate would allow --- .../java/org/opentripplanner/osm/model/OsmWithTagsTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java index c379fc11b02..a5dc168d3fc 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java @@ -306,6 +306,9 @@ private static List parseTagAsDurationCases() { Arguments.of("00:11", Optional.of(Duration.ofMinutes(11))), Arguments.of("11", Optional.of(Duration.ofMinutes(11))), Arguments.of("1:22:33", Optional.of(Duration.ofHours(1).plusMinutes(22).plusSeconds(33))), + Arguments.of("82", Optional.of(Duration.ofMinutes(82))), + Arguments.of("25:00", Optional.of(Duration.ofHours(25))), + Arguments.of("25:00:00", Optional.of(Duration.ofHours(25))), Arguments.of("22:60", Optional.empty()), Arguments.of("10:61:40", Optional.empty()), Arguments.of("10:59:60", Optional.empty()) From 718b7384d1bcd7ac1959540ff1e0139df41d9087 Mon Sep 17 00:00:00 2001 From: Ivan Aladjoff < Ivan.Aladjoff.Extern@skanetrafiken.se> Date: Mon, 25 Nov 2024 18:56:19 +0100 Subject: [PATCH 058/162] added retry mechanism for Azure service bus --- .../azure/AbstractAzureSiriUpdaterTest.java | 393 ++++++++++++++++++ .../azure/AbstractAzureSiriUpdater.java | 314 ++++++++++---- .../azure/SiriAzureUpdaterConfig.java | 8 +- doc/user/sandbox/siri/SiriAzureUpdater.md | 8 +- 4 files changed, 624 insertions(+), 99 deletions(-) create mode 100644 application/src/ext-test/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdaterTest.java diff --git a/application/src/ext-test/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdaterTest.java b/application/src/ext-test/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdaterTest.java new file mode 100644 index 00000000000..f4cfa8ceb80 --- /dev/null +++ b/application/src/ext-test/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdaterTest.java @@ -0,0 +1,393 @@ +package org.opentripplanner.ext.siri.updater.azure; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.*; + +import com.azure.core.util.ExpandableStringEnum; +import com.azure.messaging.servicebus.ServiceBusErrorContext; +import com.azure.messaging.servicebus.ServiceBusErrorSource; +import com.azure.messaging.servicebus.ServiceBusException; +import com.azure.messaging.servicebus.ServiceBusFailureReason; +import com.azure.messaging.servicebus.ServiceBusReceivedMessageContext; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URISyntaxException; +import java.time.Duration; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.opentripplanner.framework.io.OtpHttpClientException; + +class AbstractAzureSiriUpdaterTest { + + private SiriAzureUpdaterParameters mockConfig; + private AbstractAzureSiriUpdater updater; + private AbstractAzureSiriUpdater.CheckedRunnable task; + + @BeforeEach + public void setUp() throws Exception { + mockConfig = mock(SiriAzureUpdaterParameters.class); + when(mockConfig.configRef()).thenReturn("testConfigRef"); + when(mockConfig.getAuthenticationType()).thenReturn(AuthenticationType.SharedAccessKey); + when(mockConfig.getFullyQualifiedNamespace()).thenReturn("testNamespace"); + when(mockConfig.getServiceBusUrl()).thenReturn("testServiceBusUrl"); + when(mockConfig.getTopicName()).thenReturn("testTopic"); + when(mockConfig.getDataInitializationUrl()).thenReturn("http://testurl.com"); + when(mockConfig.getTimeout()).thenReturn(5000); + when(mockConfig.feedId()).thenReturn("testFeedId"); + when(mockConfig.getAutoDeleteOnIdle()).thenReturn(Duration.ofHours(1)); + when(mockConfig.getPrefetchCount()).thenReturn(10); + when(mockConfig.isFuzzyTripMatching()).thenReturn(true); + + // Create a spy on AbstractAzureSiriUpdater with the mock configuration + updater = spy(new AbstractAzureSiriUpdater(mockConfig) { + @Override + protected void messageConsumer(ServiceBusReceivedMessageContext messageContext) { + } + + @Override + protected void errorConsumer(ServiceBusErrorContext errorContext) { + } + + @Override + protected void initializeData(String url, + Consumer consumer + ) throws URISyntaxException { + } + }); + + task = mock(AbstractAzureSiriUpdater.CheckedRunnable.class); + } + + /** + * Tests the retry mechanism when a retryable ServiceBusException is thrown multiple times + * and checks that it follows the backoff sequence. + */ + @Test + void testExecuteWithRetry_FullBackoffSequence() throws Throwable { + final int totalRunCalls = 10; // 9 failures + 1 success + final int totalSleepCalls = 9; // 9 retries + + doNothing().when(updater).sleep(anyInt()); + + // Configure the task to throw a retryable exception for 9 attempts and then succeed + doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doNothing() // Succeed on the 10th attempt + .when(task).run(); + + updater.executeWithRetry(task, "Test Task"); + + verify(updater, times(totalSleepCalls)).sleep(anyInt()); + + InOrder inOrder = inOrder(updater); + inOrder.verify(updater).sleep(1000); + inOrder.verify(updater).sleep(2000); + inOrder.verify(updater).sleep(4000); + inOrder.verify(updater).sleep(8000); + inOrder.verify(updater).sleep(16000); + inOrder.verify(updater).sleep(32000); + + for (int i = 0; i < 3; i++) { + inOrder.verify(updater).sleep(60000); + } + + verify(task, times(totalRunCalls)).run(); + } + + /** + * Tests the executeWithRetry method when a non-retryable exception is thrown. + * Ensures that no further retries are attempted and sleep is not called. + */ + @Test + public void testExecuteWithRetry_NonRetryableException() throws Throwable { + doNothing().when(updater).sleep(anyInt()); + + ServiceBusException serviceBusException = createServiceBusException(ServiceBusFailureReason.MESSAGE_SIZE_EXCEEDED); + + doThrow(serviceBusException).when(task).run(); + + try { + updater.executeWithRetry(task, "Test Task"); + } catch (ServiceBusException e) { + assertEquals(ServiceBusFailureReason.MESSAGE_SIZE_EXCEEDED, e.getReason(), "Exception should have reason MESSAGE_SIZE_EXCEEDED"); + } + + verify(updater, never()).sleep(anyInt()); + verify(task, times(1)).run(); + } + + /** + * Tests the executeWithRetry method when the task fails multiple times with retryable exceptions + * and then succeeds, ensuring that sleep is called the expected number of times with correct durations. + */ + @Test + public void testExecuteWithRetry_MultipleRetriesThenSuccess() throws Throwable { + final int retriesBeforeSuccess = 3; + CountDownLatch latch = new CountDownLatch(retriesBeforeSuccess); + + doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doNothing() + .when(task).run(); + + doAnswer(invocation -> { + latch.countDown(); + return null; + }).when(updater).sleep(anyInt()); + + updater.executeWithRetry(task, "Test Task"); + + boolean completed = latch.await(5, TimeUnit.SECONDS); + assertTrue(completed, "Expected sleep calls were not made."); + + ArgumentCaptor sleepCaptor = ArgumentCaptor.forClass(Integer.class); + verify(updater, times(retriesBeforeSuccess)).sleep(sleepCaptor.capture()); + + var sleepDurations = sleepCaptor.getAllValues(); + long[] expectedBackoffSequence = {1000, 2000, 4000}; + + for (int i = 0; i < expectedBackoffSequence.length; i++) { + assertEquals(expectedBackoffSequence[i], Long.valueOf(sleepDurations.get(i)), + "Backoff duration mismatch at retry " + (i + 1)); + } + + verify(task, times(retriesBeforeSuccess + 1)).run(); + } + + /** + * Tests the executeWithRetry method when the task succeeds on the first attempt. + * Ensures that no sleep calls are made. + */ + @Test + public void testExecuteWithRetry_ImmediateSuccess() throws Throwable { + doNothing().when(task).run(); + doNothing().when(updater).sleep(anyInt()); + + updater.executeWithRetry(task, "Test Task"); + + verify(updater, never()).sleep(anyInt()); + verify(task, times(1)).run(); + } + + /** + * Tests the executeWithRetry method when the task fails once with a retryable exception + * and then succeeds on the first retry. + */ + @Test + public void testExecuteWithRetry_OneRetryThenSuccess() throws Throwable { + final int expectedSleepCalls = 1; + CountDownLatch latch = new CountDownLatch(expectedSleepCalls); + + doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doNothing() + .when(task).run(); + + doAnswer(invocation -> { + if (invocation.getArgument(0).equals(1000)) { + latch.countDown(); + } + return null; + }).when(updater).sleep(anyInt()); + + updater.executeWithRetry(task, "Test Task"); + + boolean completed = latch.await(5, TimeUnit.SECONDS); + assertTrue(completed, "Expected sleep call was not made."); + + verify(updater, times(expectedSleepCalls)).sleep(1000); + verify(task, times(2)).run(); + } + + /** + * Parameterized test to verify that shouldRetry returns the correct value for each ServiceBusFailureReason. + * + * @param reason The ServiceBusFailureReason to test. + * @param expectedRetry The expected result of shouldRetry. + */ + @ParameterizedTest(name = "shouldRetry with reason {0} should return {1}") + @MethodSource("provideServiceBusFailureReasons") + @DisplayName("Test shouldRetry for all ServiceBusFailureReason values") + void testShouldRetry_ServiceBusFailureReasons(ServiceBusFailureReason reason, boolean expectedRetry) throws Exception { + ServiceBusException serviceBusException = createServiceBusException(reason); + boolean result = updater.shouldRetry(serviceBusException); + assertEquals(expectedRetry, result, "shouldRetry should return " + expectedRetry + " for reason " + reason); + } + + /** + * Test that shouldRetry returns false for non-ServiceBus exceptions. + */ + @Test + @DisplayName("shouldRetry should return false for non-ServiceBus exceptions") + public void testShouldRetry_NonServiceBusException() { + Exception genericException = new Exception("Generic exception"); + boolean result = updater.shouldRetry(genericException); + assertFalse(result, "shouldRetry should return false for non-ServiceBus exceptions"); + } + + /** + * Test that shouldRetry handles all ServiceBusFailureReason values. + * Since enums are closed, this test ensures that the parameterized tests cover all existing values. + */ + @Test + @DisplayName("shouldRetry covers all ServiceBusFailureReason values") + public void testShouldRetry_CoversAllReasons() { + long enumCount = getExpandableStringEnumValues(ServiceBusFailureReason.class).size(); + long testCaseCount = provideServiceBusFailureReasons().count(); + assertEquals(enumCount, testCaseCount, "All ServiceBusFailureReason values should be covered by tests."); + } + + @Test + void testExecuteWithRetry_InterruptedException() throws Throwable { + final int expectedRunCalls = 2; + final int expectedSleepCalls = 1; + + doThrow(createServiceBusException(ServiceBusFailureReason.SERVICE_BUSY)) + .doThrow(new InterruptedException("Sleep interrupted")) + .when(task).run(); + + doNothing().when(updater).sleep(1000); + + InterruptedException thrownException = assertThrows(InterruptedException.class, () -> { + updater.executeWithRetry(task, "Test Task"); + }, "Expected executeWithRetry to throw InterruptedException"); + + assertEquals("Sleep interrupted", thrownException.getMessage(), "Exception message should match"); + verify(updater, times(expectedSleepCalls)).sleep(1000); + verify(task, times(expectedRunCalls)).run(); + assertTrue(Thread.currentThread().isInterrupted(), "Thread should be interrupted"); + } + + @Test + void testExecuteWithRetry_OtpHttpClientException() throws Throwable { + final int retryAttempts = 3; + final int expectedSleepCalls = retryAttempts; + + doThrow(new OtpHttpClientException("could not get historical data")) + .doThrow(new OtpHttpClientException("could not get historical data")) + .doThrow(new OtpHttpClientException("could not get historical data")) + .doNothing() + .when(task).run(); + + doNothing().when(updater).sleep(anyInt()); + + updater.executeWithRetry(task, "Test Task"); + + ArgumentCaptor sleepCaptor = ArgumentCaptor.forClass(Integer.class); + verify(updater, times(expectedSleepCalls)).sleep(sleepCaptor.capture()); + + List sleepDurations = sleepCaptor.getAllValues(); + List expectedBackoffSequence = Arrays.asList(1000, 2000, 4000); + + for (int i = 0; i < retryAttempts; i++) { + assertEquals(expectedBackoffSequence.get(i), sleepDurations.get(i), + "Backoff duration mismatch at retry " + (i + 1)); + } + + verify(task, times(retryAttempts + 1)).run(); + } + + @Test + void testExecuteWithRetry_UnexpectedException() throws Throwable { + doNothing().when(updater).sleep(anyInt()); + + Exception unexpectedException = new NullPointerException("Unexpected null value"); + doThrow(unexpectedException).when(task).run(); + + Exception thrown = assertThrows(NullPointerException.class, () -> { + updater.executeWithRetry(task, "Test Task"); + }, "Expected executeWithRetry to throw NullPointerException"); + + assertEquals("Unexpected null value", thrown.getMessage(), "Exception message should match"); + verify(updater, never()).sleep(anyInt()); + verify(task, times(1)).run(); + } + + /** + * Provides test arguments for each ServiceBusFailureReason and the expected shouldRetry outcome. + * + * @return Stream of Arguments containing ServiceBusFailureReason and expected boolean. + */ + private static Stream provideServiceBusFailureReasons() { + return Stream.of( + // Retryable (Transient) Errors + Arguments.of(ServiceBusFailureReason.SERVICE_BUSY, true), + Arguments.of(ServiceBusFailureReason.SERVICE_TIMEOUT, true), + Arguments.of(ServiceBusFailureReason.SERVICE_COMMUNICATION_ERROR, true), + Arguments.of(ServiceBusFailureReason.MESSAGE_LOCK_LOST, true), + Arguments.of(ServiceBusFailureReason.SESSION_LOCK_LOST, true), + Arguments.of(ServiceBusFailureReason.SESSION_CANNOT_BE_LOCKED, true), + Arguments.of(ServiceBusFailureReason.QUOTA_EXCEEDED, true), + Arguments.of(ServiceBusFailureReason.GENERAL_ERROR, true), + Arguments.of(ServiceBusFailureReason.UNAUTHORIZED, true), + + // Non-Retryable Errors + Arguments.of(ServiceBusFailureReason.MESSAGING_ENTITY_NOT_FOUND, false), + Arguments.of(ServiceBusFailureReason.MESSAGING_ENTITY_DISABLED, false), + Arguments.of(ServiceBusFailureReason.MESSAGE_SIZE_EXCEEDED, false), + Arguments.of(ServiceBusFailureReason.MESSAGE_NOT_FOUND, false), + Arguments.of(ServiceBusFailureReason.MESSAGING_ENTITY_ALREADY_EXISTS, false) + ); + } + + /** + * Helper method to create a ServiceBusException with a specified failure reason. + * + * @param reason The ServiceBusFailureReason to set. + * @return A ServiceBusException instance with the specified reason. + */ + private ServiceBusException createServiceBusException(ServiceBusFailureReason reason) { + ServiceBusException exception = new ServiceBusException(new Throwable(), ServiceBusErrorSource.RECEIVE); + try { + Field reasonField = ServiceBusException.class.getDeclaredField("reason"); + reasonField.setAccessible(true); + reasonField.set(exception, reason); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Failed to set ServiceBusFailureReason via reflection", e); + } + return exception; + } + + /** + * Helper method to retrieve all instances of an ExpandableStringEnum subclass. + * + * @param clazz The class of the ExpandableStringEnum subclass. + * @param The type parameter extending ExpandableStringEnum. + * @return A Collection of all registered instances. + */ + private static > Collection getExpandableStringEnumValues(Class clazz) { + try { + Method valuesMethod = ExpandableStringEnum.class.getDeclaredMethod("values", Class.class); + valuesMethod.setAccessible(true); + @SuppressWarnings("unchecked") + Collection values = (Collection) valuesMethod.invoke(null, clazz); + return values; + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Failed to retrieve values from ExpandableStringEnum.", e); + } + } +} \ No newline at end of file diff --git a/application/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java b/application/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java index 7a1b32d36e5..545b216dd56 100644 --- a/application/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java +++ b/application/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java @@ -1,5 +1,6 @@ package org.opentripplanner.ext.siri.updater.azure; + import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.messaging.servicebus.ServiceBusClientBuilder; import com.azure.messaging.servicebus.ServiceBusErrorContext; @@ -18,11 +19,14 @@ import java.time.temporal.ChronoUnit; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.opentripplanner.framework.application.ApplicationShutdownSupport; +import org.opentripplanner.framework.io.OtpHttpClientException; import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.HttpHeaders; @@ -34,6 +38,35 @@ public abstract class AbstractAzureSiriUpdater implements GraphUpdater { + /** + * custom functional interface that allows throwing checked exceptions, thereby + * preserving the exception's intent and type. + */ + @FunctionalInterface + interface CheckedRunnable { + void run() throws Exception; + } + + private static final Set RETRYABLE_REASONS = Set.of( + ServiceBusFailureReason.GENERAL_ERROR, + ServiceBusFailureReason.QUOTA_EXCEEDED, + ServiceBusFailureReason.SERVICE_BUSY, + ServiceBusFailureReason.SERVICE_COMMUNICATION_ERROR, + ServiceBusFailureReason.SERVICE_TIMEOUT, + ServiceBusFailureReason.UNAUTHORIZED, + ServiceBusFailureReason.MESSAGE_LOCK_LOST, + ServiceBusFailureReason.SESSION_LOCK_LOST, + ServiceBusFailureReason.SESSION_CANNOT_BE_LOCKED + ); + + private static final Set NON_RETRYABLE_REASONS = Set.of( + ServiceBusFailureReason.MESSAGING_ENTITY_NOT_FOUND, + ServiceBusFailureReason.MESSAGING_ENTITY_DISABLED, + ServiceBusFailureReason.MESSAGE_SIZE_EXCEEDED, + ServiceBusFailureReason.MESSAGE_NOT_FOUND, + ServiceBusFailureReason.MESSAGING_ENTITY_ALREADY_EXISTS + ); + private final Logger LOG = LoggerFactory.getLogger(getClass()); private final AuthenticationType authenticationType; private final String fullyQualifiedNamespace; @@ -64,17 +97,31 @@ public abstract class AbstractAzureSiriUpdater implements GraphUpdater { protected final int timeout; public AbstractAzureSiriUpdater(SiriAzureUpdaterParameters config) { - this.configRef = config.configRef(); - this.authenticationType = config.getAuthenticationType(); - this.fullyQualifiedNamespace = config.getFullyQualifiedNamespace(); - this.serviceBusUrl = config.getServiceBusUrl(); - this.topicName = config.getTopicName(); - this.dataInitializationUrl = config.getDataInitializationUrl(); + this.configRef = Objects.requireNonNull(config.configRef(), "configRef must not be null"); + this.authenticationType = Objects.requireNonNull(config.getAuthenticationType(), "authenticationType must not be null"); + this.topicName = Objects.requireNonNull(config.getTopicName(), "topicName must not be null"); + this.dataInitializationUrl = Objects.requireNonNull(config.getDataInitializationUrl(), "dataInitializationUrl must not be null"); this.timeout = config.getTimeout(); - this.feedId = config.feedId(); + this.feedId = Objects.requireNonNull(config.feedId(), "feedId must not be null"); this.autoDeleteOnIdle = config.getAutoDeleteOnIdle(); this.prefetchCount = config.getPrefetchCount(); this.fuzzyTripMatching = config.isFuzzyTripMatching(); + + if (authenticationType == AuthenticationType.FederatedIdentity) { + this.fullyQualifiedNamespace = Objects.requireNonNull( + config.getFullyQualifiedNamespace(), + "fullyQualifiedNamespace must not be null when using FederatedIdentity authentication" + ); + this.serviceBusUrl = null; + } else if (authenticationType == AuthenticationType.SharedAccessKey) { + this.serviceBusUrl = Objects.requireNonNull( + config.getServiceBusUrl(), + "serviceBusUrl must not be null when using SharedAccessKey authentication" + ); + this.fullyQualifiedNamespace = null; + } else { + throw new IllegalArgumentException("Unsupported authentication type: " + authenticationType); + } } /** @@ -96,10 +143,6 @@ public void setup(WriteToGraphCallback writeToGraphCallback) { @Override public void run() { - Objects.requireNonNull(topicName, "'topic' must be set"); - Objects.requireNonNull(serviceBusUrl, "'servicebus-url' must be set"); - Objects.requireNonNull(feedId, "'feedId' must be set"); - Preconditions.checkState(feedId.length() > 0, "'feedId' must be set"); // In Kubernetes this should be the POD identifier subscriptionName = System.getenv("HOSTNAME"); @@ -107,6 +150,134 @@ public void run() { subscriptionName = "otp-" + UUID.randomUUID(); } + try { + executeWithRetry( + this::setupSubscription, + "Setting up Service Bus subscription to topic" + ); + + executeWithRetry( + () -> initializeData(dataInitializationUrl, messageConsumer), + "Initializing historical Siri data" + ); + + executeWithRetry( + this::startEventProcessor, + "Starting Service Bus event processor" + ); + + setPrimed(); + + ApplicationShutdownSupport.addShutdownHook( + "azure-siri-updater-shutdown", + () -> { + LOG.info("Calling shutdownHook on AbstractAzureSiriUpdater"); + if (eventProcessor != null) { + eventProcessor.close(); + } + if (serviceBusAdmin != null) { + serviceBusAdmin.deleteSubscription(topicName, subscriptionName).block(); + LOG.info("Subscription '{}' deleted on topic '{}'.", subscriptionName, topicName); + } + } + ); + + } catch (ServiceBusException e) { + LOG.error("Service Bus encountered an error during setup: {}", e.getMessage(), e); + } catch (URISyntaxException e) { + LOG.error("Invalid URI provided for Service Bus setup: {}", e.getMessage(), e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOG.warn("Updater was interrupted during setup."); + } catch (Exception e) { + LOG.error("An unexpected error occurred during setup: {}", e.getMessage(), e); + } + } + + /** + * Sleeps. This is to be able to mock testing + * @param millis number of milliseconds + * @throws InterruptedException if sleep is interrupted + */ + protected void sleep(int millis) throws InterruptedException { + Thread.sleep(millis); + } + + /** + * Executes a task with retry logic. Retries indefinitely for retryable exceptions with exponential backoff. + * Does not retry for InterruptedException and propagates it + * @param task The task to execute. + * @param description A description of the task for logging purposes. + * @throws InterruptedException If the thread is interrupted while waiting between retries. + */ + protected void executeWithRetry(CheckedRunnable task, String description) throws Exception { + int sleepPeriod = 1000; // Start with 1-second delay + int attemptCounter = 1; + + while (true) { + try { + task.run(); + LOG.info("{} succeeded.", description); + return; + } catch (InterruptedException ie) { + LOG.warn("{} was interrupted during execution.", description); + Thread.currentThread().interrupt(); // Restore interrupted status + throw ie; + } catch (Exception e) { + LOG.warn("{} failed. Error: {} (Attempt {})", description, e.getMessage(), attemptCounter); + + if (!shouldRetry(e)) { + LOG.error("{} encountered a non-retryable error: {}.", description, e.getMessage()); + throw e; // Stop retries if the error is non-retryable + } + + LOG.warn("{} will retry in {} ms.", description, sleepPeriod); + attemptCounter++; + try { + sleep(sleepPeriod); + } catch (InterruptedException ie){ + LOG.warn("{} was interrupted during sleep.", description); + Thread.currentThread().interrupt(); // Restore interrupted status + throw ie; + } + sleepPeriod = Math.min(sleepPeriod * 2, 60 * 1000); // Exponential backoff with a cap at 60 seconds + } + } + } + + protected boolean shouldRetry(Exception e) { + if (e instanceof ServiceBusException sbException) { + ServiceBusFailureReason reason = sbException.getReason(); + + if (RETRYABLE_REASONS.contains(reason)) { + + LOG.warn("Transient error encountered: {}. Retrying...", reason); + return true; + + } else if (NON_RETRYABLE_REASONS.contains(reason)) { + + LOG.error("Non-recoverable error encountered: {}. Not retrying.", reason); + return false; + + } else { + LOG.warn("Unhandled ServiceBusFailureReason: {}. Retrying by default.", reason); + return true; + } + } + else if (ExceptionUtils.hasCause(e, OtpHttpClientException.class)){ + // retry for OtpHttpClientException as it is thrown if historical data can't be read at the moment + return true; + } + + LOG.warn("Non-ServiceBus exception encountered: {}. Not retrying.", e.getClass().getName()); + return false; + } + + /** + * Sets up the Service Bus subscription, including checking old subscription, deleting if necessary, + * and creating a new subscription. + */ + private void setupSubscription() throws ServiceBusException, URISyntaxException { // Client with permissions to create subscription if (authenticationType == AuthenticationType.FederatedIdentity) { serviceBusAdmin = @@ -121,40 +292,50 @@ public void run() { } // Set options - var options = new CreateSubscriptionOptions(); - options.setDefaultMessageTimeToLive(Duration.of(25, ChronoUnit.HOURS)); - // Set subscription to be deleted if idle for a certain time, so that orphaned instances doesn't linger. - options.setAutoDeleteOnIdle(autoDeleteOnIdle); + CreateSubscriptionOptions options = new CreateSubscriptionOptions() + .setDefaultMessageTimeToLive(Duration.of(25, ChronoUnit.HOURS)) + .setAutoDeleteOnIdle(autoDeleteOnIdle); // Make sure there is no old subscription on serviceBus - if ( - Boolean.TRUE.equals( - serviceBusAdmin.getSubscriptionExists(topicName, subscriptionName).block() - ) - ) { - LOG.info("Subscription {} already exists", subscriptionName); + if ( Boolean.TRUE.equals( serviceBusAdmin.getSubscriptionExists(topicName, subscriptionName).block())) { + LOG.info("Subscription '{}' already exists. Deleting existing subscription.", subscriptionName); serviceBusAdmin.deleteSubscription(topicName, subscriptionName).block(); LOG.info("Service Bus deleted subscription {}.", subscriptionName); } serviceBusAdmin.createSubscription(topicName, subscriptionName, options).block(); - LOG.info("Service Bus created subscription {}", subscriptionName); + LOG.info("{} created subscription {}", getClass().getSimpleName(), subscriptionName); + } - // Initialize historical Siri data - initializeData(); + /** + * Starts the Service Bus event processor. + */ + private void startEventProcessor() throws ServiceBusException { + ServiceBusClientBuilder clientBuilder = new ServiceBusClientBuilder(); - eventProcessor = - new ServiceBusClientBuilder() - .connectionString(serviceBusUrl) - .processor() - .topicName(topicName) - .subscriptionName(subscriptionName) - .receiveMode(ServiceBusReceiveMode.RECEIVE_AND_DELETE) - .disableAutoComplete() // Receive and delete does not need autocomplete - .prefetchCount(prefetchCount) - .processError(errorConsumer) - .processMessage(messageConsumer) - .buildProcessorClient(); + if (authenticationType == AuthenticationType.FederatedIdentity) { + Preconditions.checkNotNull(fullyQualifiedNamespace, "fullyQualifiedNamespace must be set for FederatedIdentity authentication"); + clientBuilder + .fullyQualifiedNamespace(fullyQualifiedNamespace) + .credential(new DefaultAzureCredentialBuilder().build()); + } else if (authenticationType == AuthenticationType.SharedAccessKey) { + Preconditions.checkNotNull(serviceBusUrl, "serviceBusUrl must be set for SharedAccessKey authentication"); + clientBuilder + .connectionString(serviceBusUrl); + } else { + throw new IllegalArgumentException("Unsupported authentication type: " + authenticationType); + } + + eventProcessor = clientBuilder + .processor() + .topicName(topicName) + .subscriptionName(subscriptionName) + .receiveMode(ServiceBusReceiveMode.RECEIVE_AND_DELETE) + .disableAutoComplete() // Receive and delete does not need autocomplete + .prefetchCount(prefetchCount) + .processError(errorConsumer) + .processMessage(messageConsumer) + .buildProcessorClient(); eventProcessor.start(); LOG.info( @@ -163,20 +344,9 @@ public void run() { subscriptionName, prefetchCount ); - - setPrimed(); - - ApplicationShutdownSupport.addShutdownHook( - "azure-siri-updater-shutdown", - () -> { - LOG.info("Calling shutdownHook on AbstractAzureSiriUpdater"); - eventProcessor.close(); - serviceBusAdmin.deleteSubscription(topicName, subscriptionName).block(); - LOG.info("Subscription '{}' deleted on topic '{}'.", subscriptionName, topicName); - } - ); } + @Override public boolean isPrimed() { return this.isPrimed; @@ -221,37 +391,6 @@ boolean fuzzyTripMatching() { return fuzzyTripMatching; } - /** - * InitializeData - wrapping method that calls an implementation of initialize data - and blocks readiness till finished - */ - private void initializeData() { - int sleepPeriod = 1000; - int attemptCounter = 1; - boolean otpIsShuttingDown = false; - - while (!otpIsShuttingDown) { - try { - initializeData(dataInitializationUrl, messageConsumer); - break; - } catch (Exception e) { - sleepPeriod = Math.min(sleepPeriod * 2, 60 * 1000); - - LOG.warn( - "Caught exception while initializing data will retry after {} ms - attempt {}. ({})", - sleepPeriod, - attemptCounter++, - e.toString() - ); - try { - Thread.sleep(sleepPeriod); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - otpIsShuttingDown = true; - LOG.info("OTP is shutting down, cancelling attempt to initialize Azure SIRI Updater."); - } - } - } - } protected abstract void initializeData( String url, @@ -260,7 +399,7 @@ protected abstract void initializeData( /** * Make some sensible logging on error and if Service Bus is busy, sleep for some time before try again to get messages. - * This code snippet is taken from Microsoft example https://docs.microsoft.com/sv-se/azure/service-bus-messaging/service-bus-java-how-to-use-queues. + * This code snippet is taken from Microsoft example .... * @param errorContext Context for errors handled by the ServiceBusProcessorClient. */ protected void defaultErrorConsumer(ServiceBusErrorContext errorContext) { @@ -270,19 +409,15 @@ protected void defaultErrorConsumer(ServiceBusErrorContext errorContext) { errorContext.getEntityPath() ); - if (!(errorContext.getException() instanceof ServiceBusException)) { + if (!(errorContext.getException() instanceof ServiceBusException e)) { LOG.error("Non-ServiceBusException occurred!", errorContext.getException()); return; } - var e = (ServiceBusException) errorContext.getException(); var reason = e.getReason(); - if ( - reason == ServiceBusFailureReason.MESSAGING_ENTITY_DISABLED || - reason == ServiceBusFailureReason.MESSAGING_ENTITY_NOT_FOUND || - reason == ServiceBusFailureReason.UNAUTHORIZED - ) { + if (reason == ServiceBusFailureReason.MESSAGING_ENTITY_DISABLED || + reason == ServiceBusFailureReason.MESSAGING_ENTITY_NOT_FOUND) { LOG.error( "An unrecoverable error occurred. Stopping processing with reason {} {}", reason, @@ -290,8 +425,9 @@ protected void defaultErrorConsumer(ServiceBusErrorContext errorContext) { ); } else if (reason == ServiceBusFailureReason.MESSAGE_LOCK_LOST) { LOG.error("Message lock lost for message", e); - } else if (reason == ServiceBusFailureReason.SERVICE_BUSY) { - LOG.error("Service Bus is busy, wait and try again"); + } else if (reason == ServiceBusFailureReason.SERVICE_BUSY || + reason == ServiceBusFailureReason.UNAUTHORIZED) { + LOG.error("Service Bus is busy or unauthorized, wait and try again"); try { // Choosing an arbitrary amount of time to wait until trying again. TimeUnit.SECONDS.sleep(5); diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/azure/SiriAzureUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/azure/SiriAzureUpdaterConfig.java index ddb6a967f92..28fb7bf9549 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/azure/SiriAzureUpdaterConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/azure/SiriAzureUpdaterConfig.java @@ -33,14 +33,10 @@ public static void populateConfig( .asString(null) ); parameters.setTopicName( - c.of("topic").since(V2_2).summary("Service Bus topic to connect to.").asString(null) + c.of("topic").since(V2_2).summary("Service Bus topic to connect to.").asString() ); parameters.setFeedId( - c - .of("feedId") - .since(V2_2) - .summary("The ID of the feed to apply the updates to.") - .asString(null) + c.of("feedId").since(V2_2).summary("The ID of the feed to apply the updates to.").asString() ); parameters.setAutoDeleteOnIdle( c diff --git a/doc/user/sandbox/siri/SiriAzureUpdater.md b/doc/user/sandbox/siri/SiriAzureUpdater.md index c8e7f4d9255..898e70d7b84 100644 --- a/doc/user/sandbox/siri/SiriAzureUpdater.md +++ b/doc/user/sandbox/siri/SiriAzureUpdater.md @@ -28,12 +28,12 @@ To enable the SIRI updater you need to add it to the updaters section of the `ro | [authenticationType](#u__11__authenticationType) | `enum` | Which authentication type to use | *Optional* | `"sharedaccesskey"` | 2.5 | | autoDeleteOnIdle | `duration` | The time after which an inactive subscription is removed. | *Optional* | `"PT1H"` | 2.5 | | [customMidnight](#u__11__customMidnight) | `integer` | Time on which time breaks into new day. | *Optional* | `0` | 2.2 | -| feedId | `string` | The ID of the feed to apply the updates to. | *Optional* | | 2.2 | +| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.2 | | [fullyQualifiedNamespace](#u__11__fullyQualifiedNamespace) | `string` | Service Bus fully qualified namespace used for authentication. | *Optional* | | 2.5 | | fuzzyTripMatching | `boolean` | Whether to apply fuzzyTripMatching on the updates | *Optional* | `false` | 2.2 | | prefetchCount | `integer` | The number of messages to fetch from the subscription at a time. | *Optional* | `10` | 2.5 | | [servicebus-url](#u__11__servicebus_url) | `string` | Service Bus connection used for authentication. | *Optional* | | 2.2 | -| topic | `string` | Service Bus topic to connect to. | *Optional* | | 2.2 | +| topic | `string` | Service Bus topic to connect to. | *Required* | | 2.2 | | history | `object` | Configuration for fetching historical data on startup | *Optional* | | 2.2 | |    fromDateTime | `string` | Datetime boundary for historical data | *Optional* | `"-P1D"` | 2.2 | |    timeout | `integer` | Timeout in milliseconds | *Optional* | `300000` | na | @@ -116,12 +116,12 @@ Has to be present for authenticationMethod SharedAccessKey. This should be Prima | [authenticationType](#u__10__authenticationType) | `enum` | Which authentication type to use | *Optional* | `"sharedaccesskey"` | 2.5 | | autoDeleteOnIdle | `duration` | The time after which an inactive subscription is removed. | *Optional* | `"PT1H"` | 2.5 | | [customMidnight](#u__10__customMidnight) | `integer` | Time on which time breaks into new day. | *Optional* | `0` | 2.2 | -| feedId | `string` | The ID of the feed to apply the updates to. | *Optional* | | 2.2 | +| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.2 | | [fullyQualifiedNamespace](#u__10__fullyQualifiedNamespace) | `string` | Service Bus fully qualified namespace used for authentication. | *Optional* | | 2.5 | | fuzzyTripMatching | `boolean` | Whether to apply fuzzyTripMatching on the updates | *Optional* | `false` | 2.2 | | prefetchCount | `integer` | The number of messages to fetch from the subscription at a time. | *Optional* | `10` | 2.5 | | [servicebus-url](#u__10__servicebus_url) | `string` | Service Bus connection used for authentication. | *Optional* | | 2.2 | -| topic | `string` | Service Bus topic to connect to. | *Optional* | | 2.2 | +| topic | `string` | Service Bus topic to connect to. | *Required* | | 2.2 | | history | `object` | Configuration for fetching historical data on startup | *Optional* | | 2.2 | |    fromDateTime | `string` | Datetime boundary for historical data. | *Optional* | `"-P1D"` | 2.2 | |    timeout | `integer` | Timeout in milliseconds | *Optional* | `300000` | na | From d345a03465f54c8ab912f1c5611eacb2d77341a9 Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Fri, 29 Nov 2024 09:45:11 +0200 Subject: [PATCH 059/162] explain the duration parser better, more tests --- .../osm/model/OsmWithTags.java | 24 +++++++++++++------ .../osm/model/OsmWithTagsTest.java | 6 ++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java index bb3e1056f2f..f5ead1da4d5 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -1,8 +1,6 @@ package org.opentripplanner.osm.model; import java.time.Duration; -import java.time.LocalTime; -import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Arrays; import java.util.HashMap; @@ -236,34 +234,46 @@ public OptionalInt getTagAsInt(String tag, Consumer errorHandler) { * @throws DateTimeParseException on bad input */ public static Duration parseOsmDuration(String duration) { + // Unfortunately DateFormatParserBuilder doesn't quite do enough for this case. + // It has the capability for expressing optional parts, so it could express hh(:mm(:ss)?)? + // but it cannot express (hh:)?mm(:ss)? where the existence of (:ss) implies the existence + // of (hh:). Even if it did, it would not be able to handle the cases where hours are + // greater than 23 or (if there is no hours part at all) minutes are greater than 59, which + // are both allowed by the spec and exist in OSM data. Durations are not LocalTimes after + // all, in parsing a LocalTime it makes sense and is correct that hours cannot be more than + // 23 or minutes more than 59, but in durations if you have capped the largest unit, it is + // reasonable for the amount of the largest unit to be as large as it needs to be. int colonCount = (int) duration.chars().filter(ch -> ch == ':').count(); if (colonCount <= 2) { try { int i, j; long hours, minutes, seconds; + // The first :-separated element can be any width, and has no maximum. It still has + // to be non-negative. The following elements must be 2 characters wide, non-negative, + // and less than 60. switch (colonCount) { - case 0: + case 0: // case "m" minutes = Long.parseLong(duration); if (minutes >= 0) { return Duration.ofMinutes(minutes); } break; - case 1: + case 1: // case "h:mm" i = duration.indexOf(':'); hours = Long.parseLong(duration.substring(0, i)); minutes = Long.parseLong(duration.substring(i + 1)); - if (hours >= 0 && minutes >= 0 && minutes < 60) { + if (duration.length() - i == 3 && hours >= 0 && minutes >= 0 && minutes < 60) { return Duration.ofHours(hours).plusMinutes(minutes); } break; - default: + default: // case "h:mm:ss" //case 2: i = duration.indexOf(':'); j = duration.indexOf(':', i + 1); hours = Long.parseLong(duration.substring(0, i)); minutes = Long.parseLong(duration.substring(i + 1, j)); seconds = Long.parseLong(duration.substring(j + 1)); - if (hours >= 0 && minutes >= 0 && minutes < 60 && seconds >= 0 && seconds < 60) { + if (j - i == 3 && duration.length() - j == 3 && hours >= 0 && minutes >= 0 && minutes < 60 && seconds >= 0 && seconds < 60) { return Duration.ofHours(hours).plusMinutes(minutes).plusSeconds(seconds); } break; diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java index a5dc168d3fc..3fe49ded403 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java @@ -311,7 +311,11 @@ private static List parseTagAsDurationCases() { Arguments.of("25:00:00", Optional.of(Duration.ofHours(25))), Arguments.of("22:60", Optional.empty()), Arguments.of("10:61:40", Optional.empty()), - Arguments.of("10:59:60", Optional.empty()) + Arguments.of("10:59:60", Optional.empty()), + Arguments.of("1:12:34", Optional.of(Duration.ofHours(1).plusMinutes(12).plusSeconds(34))), + Arguments.of("1:2:34", Optional.empty()), + Arguments.of("1:12:3", Optional.empty()), + Arguments.of("1:2", Optional.empty()) ); } From c161378b8cf478ecbcb387c978735d1077224dfc Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Fri, 29 Nov 2024 09:47:57 +0200 Subject: [PATCH 060/162] prettier reformatting --- .../org/opentripplanner/osm/model/OsmWithTags.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java index f5ead1da4d5..74d1ffd25f5 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -273,7 +273,15 @@ public static Duration parseOsmDuration(String duration) { hours = Long.parseLong(duration.substring(0, i)); minutes = Long.parseLong(duration.substring(i + 1, j)); seconds = Long.parseLong(duration.substring(j + 1)); - if (j - i == 3 && duration.length() - j == 3 && hours >= 0 && minutes >= 0 && minutes < 60 && seconds >= 0 && seconds < 60) { + if ( + j - i == 3 && + duration.length() - j == 3 && + hours >= 0 && + minutes >= 0 && + minutes < 60 && + seconds >= 0 && + seconds < 60 + ) { return Duration.ofHours(hours).plusMinutes(minutes).plusSeconds(seconds); } break; From 33189c22fd4fc74407f9c5b3f20d7425ed9ea580 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 26 Nov 2024 11:09:33 +0200 Subject: [PATCH 061/162] Rename 'transferByStopIndex' variable to 'transfersByStopIndex'. --- .../raptoradapter/transit/mappers/TransfersMapper.java | 6 +++--- .../raptoradapter/transit/mappers/TransitLayerMapper.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java index ff47cb3ea74..3de07da9e19 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java @@ -18,7 +18,7 @@ static List> mapTransfers( SiteRepository siteRepository, TransitService transitService ) { - List> transferByStopIndex = new ArrayList<>(); + List> transfersByStopIndex = new ArrayList<>(); for (int i = 0; i < siteRepository.stopIndexSize(); ++i) { var stop = siteRepository.stopByIndex(i); @@ -45,10 +45,10 @@ static List> mapTransfers( } // Create a copy to compact and make the inner lists immutable - transferByStopIndex.add(List.copyOf(list)); + transfersByStopIndex.add(List.copyOf(list)); } // Return an immutable copy - return List.copyOf(transferByStopIndex); + return List.copyOf(transfersByStopIndex); } } diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java index d6f9c0709c5..edcfa3f9dc8 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java @@ -62,7 +62,7 @@ public static TransitLayer map( private TransitLayer map(TransitTuningParameters tuningParameters) { HashMap> tripPatternsByStopByDate; - List> transferByStopIndex; + List> transfersByStopIndex; ConstrainedTransfersForPatterns constrainedTransfers = null; LOG.info("Mapping transitLayer from TimetableRepository..."); @@ -71,7 +71,7 @@ private TransitLayer map(TransitTuningParameters tuningParameters) { tripPatternsByStopByDate = mapTripPatterns(allTripPatterns); - transferByStopIndex = mapTransfers(siteRepository, transitService); + transfersByStopIndex = mapTransfers(siteRepository, transitService); TransferIndexGenerator transferIndexGenerator = null; if (OTPFeature.TransferConstraints.isOn()) { @@ -86,7 +86,7 @@ private TransitLayer map(TransitTuningParameters tuningParameters) { return new TransitLayer( tripPatternsByStopByDate, - transferByStopIndex, + transfersByStopIndex, transitService.getTransferService(), siteRepository, transferCache, From aa4927ed83734c9af78df7caacb6235aa647db3f Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 29 Nov 2024 15:09:43 +0200 Subject: [PATCH 062/162] Add changes for CAR transfers. --- .../api/parameter/QualifiedModeSet.java | 7 ++++--- .../mapping/RaptorPathToItineraryMapper.java | 2 +- ...treetModeToTransferTraverseModeMapper.java | 21 +++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StreetModeToTransferTraverseModeMapper.java diff --git a/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java b/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java index c6f1a3d74ec..98ce58c6d21 100644 --- a/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java +++ b/application/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); + // This is necessary for transfer calculations. + mBuilder.withAccessMode(StreetMode.CAR); + mBuilder.withTransferMode(StreetMode.CAR); + mBuilder.withEgressMode(StreetMode.CAR); mBuilder.withDirectMode(StreetMode.CAR); } } diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java index 64c59b71603..15e3307b4e9 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java @@ -126,7 +126,7 @@ else if (pathLeg.isTransferLeg()) { legs.addAll( mapTransferLeg( pathLeg.asTransferLeg(), - transferMode == StreetMode.BIKE ? TraverseMode.BICYCLE : TraverseMode.WALK + StreetModeToTransferTraverseModeMapper.map(transferMode) ) ); } diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StreetModeToTransferTraverseModeMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StreetModeToTransferTraverseModeMapper.java new file mode 100644 index 00000000000..65db8e87832 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StreetModeToTransferTraverseModeMapper.java @@ -0,0 +1,21 @@ +package org.opentripplanner.routing.algorithm.mapping; + +import org.opentripplanner.routing.api.request.StreetMode; +import org.opentripplanner.street.search.TraverseMode; + +/** + * Maps street mode to transfer traverse mode. + */ +public class StreetModeToTransferTraverseModeMapper { + + public static TraverseMode map(StreetMode mode) { + return switch (mode) { + case WALK -> TraverseMode.WALK; + case BIKE -> TraverseMode.BICYCLE; + case CAR -> TraverseMode.CAR; + default -> throw new IllegalArgumentException( + String.format("StreetMode %s can not be mapped to a TraverseMode for transfers.", mode) + ); + }; + } +} From 4c882f42b22c20c582ca67438171b725523ff7d4 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 29 Nov 2024 15:13:10 +0200 Subject: [PATCH 063/162] Add EnumSet of StreetModes to PathTransfers and implement mode-specific transfers. --- .../opentripplanner/ext/flex/FlexIndex.java | 14 ++++++- .../opentripplanner/ext/flex/FlexRouter.java | 2 +- .../module/DirectTransferGenerator.java | 42 +++++++++++++++---- .../opentripplanner/model/PathTransfer.java | 25 +++++++++-- .../transit/RaptorTransferIndex.java | 3 ++ .../raptoradapter/transit/Transfer.java | 13 +++++- .../transit/mappers/TransfersMapper.java | 9 +++- .../RaptorPathToItineraryMapperTest.java | 4 +- .../raptoradapter/transit/TransferTest.java | 14 ++++--- 9 files changed, 101 insertions(+), 25 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java b/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java index cd047de6d04..ccc4f7fe554 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java @@ -5,9 +5,11 @@ import com.google.common.collect.Multimap; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.model.PathTransfer; +import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.site.GroupStop; @@ -18,6 +20,8 @@ public class FlexIndex { private final Multimap transfersToStop = ArrayListMultimap.create(); + private final Multimap transfersFromStop = ArrayListMultimap.create(); + private final Multimap> flexTripsByStop = HashMultimap.create(); private final Map routeById = new HashMap<>(); @@ -25,8 +29,12 @@ public class FlexIndex { private final Map> tripById = new HashMap<>(); public FlexIndex(TimetableRepository timetableRepository) { - for (PathTransfer transfer : timetableRepository.getAllPathTransfers()) { + // Flex transfers should only use WALK mode transfers. + StreetMode mode = StreetMode.WALK; + List filteredPathTransfers = timetableRepository.getAllPathTransfers().stream().filter(pathTransfer -> pathTransfer.getModes().contains(mode)).toList(); + for (PathTransfer transfer : filteredPathTransfers) { transfersToStop.put(transfer.to, transfer); + transfersFromStop.put(transfer.from, transfer); } for (FlexTrip flexTrip : timetableRepository.getAllFlexTrips()) { routeById.put(flexTrip.getTrip().getRoute().getId(), flexTrip.getTrip().getRoute()); @@ -47,6 +55,10 @@ public Collection getTransfersToStop(StopLocation stopLocation) { return transfersToStop.get(stopLocation); } + public Collection getTransfersFromStop(StopLocation stopLocation) { + return transfersFromStop.get(stopLocation); + } + public Collection> getFlexTripsByStop(StopLocation stopLocation) { return flexTripsByStop.get(stopLocation); } diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/FlexRouter.java b/application/src/ext/java/org/opentripplanner/ext/flex/FlexRouter.java index 46d9e527980..e7f3d3544ab 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/FlexRouter.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/FlexRouter.java @@ -192,7 +192,7 @@ public TransitStopVertex getStopVertexForStopId(FeedScopedId stopId) { @Override public Collection getTransfersFromStop(StopLocation stop) { - return transitService.findPathTransfers(stop); + return transitService.getFlexIndex().getTransfersFromStop(stop); } @Override diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index c0deb932418..4c26808ab80 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -3,9 +3,12 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimaps; import java.time.Duration; +import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; @@ -17,6 +20,7 @@ import org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinder; import org.opentripplanner.model.PathTransfer; import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graphfinder.NearbyStop; import org.opentripplanner.street.model.edge.Edge; @@ -102,6 +106,7 @@ public void buildGraph() { LOG.debug("Linking stop '{}' {}", stop, ts0); for (RouteRequest transferProfile : transferRequests) { + StreetMode mode = transferProfile.journey().transfer().mode(); for (NearbyStop sd : nearbyStopFinder.findNearbyStops( ts0, transferProfile, @@ -115,12 +120,12 @@ public void buildGraph() { if (sd.stop.transfersNotAllowed()) { continue; } - distinctTransfers.put( - new TransferKey(stop, sd.stop, sd.edges), - new PathTransfer(stop, sd.stop, sd.distance, sd.edges) - ); + createPathTransfer(distinctTransfers, sd, stop, mode); } - if (OTPFeature.FlexRouting.isOn()) { + } + if (OTPFeature.FlexRouting.isOn()) { + for (RouteRequest transferProfile : transferRequests) { + StreetMode mode = transferProfile.journey().transfer().mode(); // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. for (NearbyStop sd : nearbyStopFinder.findNearbyStops( @@ -136,10 +141,7 @@ public void buildGraph() { if (sd.stop instanceof RegularStop) { continue; } - distinctTransfers.put( - new TransferKey(sd.stop, stop, sd.edges), - new PathTransfer(sd.stop, stop, sd.distance, sd.edges) - ); + createPathTransfer(distinctTransfers, sd, stop, mode); } } } @@ -174,6 +176,28 @@ public void buildGraph() { ); } + /** + * Factory method for creating a PathTransfer. + */ + private void createPathTransfer( + Map distinctTransfers, + NearbyStop sd, + RegularStop stop, + StreetMode mode + ) { + TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges); + PathTransfer pathTransfer = distinctTransfers.get(transferKey); + if (pathTransfer == null) { + EnumSet modes = EnumSet.of(mode); + distinctTransfers.put( + transferKey, + new PathTransfer(stop, sd.stop, sd.distance, sd.edges, modes) + ); + } else { + pathTransfer.addMode(mode); + } + } + /** * Factory method for creating a NearbyStopFinder. Will create different finders depending on * whether the graph has a street network and if ConsiderPatternsForDirectTransfers feature is diff --git a/application/src/main/java/org/opentripplanner/model/PathTransfer.java b/application/src/main/java/org/opentripplanner/model/PathTransfer.java index 5b42a8149e3..ed4696ab11c 100644 --- a/application/src/main/java/org/opentripplanner/model/PathTransfer.java +++ b/application/src/main/java/org/opentripplanner/model/PathTransfer.java @@ -1,19 +1,20 @@ package org.opentripplanner.model; import java.io.Serializable; +import java.util.EnumSet; import java.util.List; import org.opentripplanner.model.transfer.ConstrainedTransfer; +import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.utils.tostring.ToStringBuilder; /** - * Represents a transfer between stops with the street network path attatched to it. + * Represents a transfer for a set of modes between stops with the street network path attached to it. *

* Do not confuse this with {@link ConstrainedTransfer}. * *

- * TODO these should really have a set of valid modes in case bike vs. walk transfers are different * TODO Should we just store the NearbyStop as a field here, or even switch to using it instead * where this class is used */ @@ -27,11 +28,20 @@ public class PathTransfer implements Serializable { private final List edges; - public PathTransfer(StopLocation from, StopLocation to, double distanceMeters, List edges) { + private EnumSet modes; + + public PathTransfer( + StopLocation from, + StopLocation to, + double distanceMeters, + List edges, + EnumSet modes + ) { this.from = from; this.to = to; this.distanceMeters = distanceMeters; this.edges = edges; + this.modes = modes; } public String getName() { @@ -46,6 +56,14 @@ public List getEdges() { return this.edges; } + public EnumSet getModes() { + return this.modes; + } + + public boolean addMode(StreetMode mode) { + return this.modes.add(mode); + } + @Override public String toString() { return ToStringBuilder @@ -54,6 +72,7 @@ public String toString() { .addObj("to", to) .addNum("distance", distanceMeters) .addColSize("edges", edges) + .addColSize("modes", modes) .toString(); } } diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/RaptorTransferIndex.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/RaptorTransferIndex.java index 1d9b804067c..941e1296838 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/RaptorTransferIndex.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/RaptorTransferIndex.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.function.Function; import org.opentripplanner.raptor.api.model.RaptorTransfer; +import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.street.search.request.StreetSearchRequest; public class RaptorTransferIndex { @@ -29,6 +30,7 @@ public static RaptorTransferIndex create( ) { var forwardTransfers = new ArrayList>(transfersByStopIndex.size()); var reversedTransfers = new ArrayList>(transfersByStopIndex.size()); + StreetMode mode = request.mode(); for (int i = 0; i < transfersByStopIndex.size(); i++) { forwardTransfers.add(new ArrayList<>()); @@ -41,6 +43,7 @@ public static RaptorTransferIndex create( var transfers = transfersByStopIndex .get(fromStop) .stream() + .filter(transfer -> transfer.getModes().contains(mode)) .flatMap(s -> s.asRaptorTransfer(request).stream()) .collect( toMap(RaptorTransfer::stop, Function.identity(), (a, b) -> a.c1() < b.c1() ? a : b) diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java index 32d54787e6f..c0c923228bf 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java @@ -8,6 +8,7 @@ import org.locationtech.jts.geom.Coordinate; import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransfer; +import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.api.request.preference.WalkPreferences; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.search.request.StreetSearchRequest; @@ -31,16 +32,20 @@ public class Transfer { private final List edges; - public Transfer(int toStop, List edges) { + private final Set modes; + + public Transfer(int toStop, List edges, Set modes) { this.toStop = toStop; this.edges = edges; this.distanceMeters = (int) edges.stream().mapToDouble(Edge::getDistanceMeters).sum(); + this.modes = modes; } - public Transfer(int toStopIndex, int distanceMeters) { + public Transfer(int toStopIndex, int distanceMeters, Set modes) { this.toStop = toStopIndex; this.distanceMeters = distanceMeters; this.edges = null; + this.modes = modes; } public List getCoordinates() { @@ -68,6 +73,10 @@ public List getEdges() { return edges; } + public Set getModes() { + return modes; + } + public Optional asRaptorTransfer(StreetSearchRequest request) { WalkPreferences walkPreferences = request.preferences().walk(); if (edges == null || edges.isEmpty()) { diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java index 3de07da9e19..9fc49c5c5e1 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java @@ -34,10 +34,15 @@ static List> mapTransfers( int toStopIndex = pathTransfer.to.getIndex(); Transfer newTransfer; if (pathTransfer.getEdges() != null) { - newTransfer = new Transfer(toStopIndex, pathTransfer.getEdges()); + newTransfer = + new Transfer(toStopIndex, pathTransfer.getEdges(), pathTransfer.getModes()); } else { newTransfer = - new Transfer(toStopIndex, (int) Math.ceil(pathTransfer.getDistanceMeters())); + new Transfer( + toStopIndex, + (int) Math.ceil(pathTransfer.getDistanceMeters()), + pathTransfer.getModes() + ); } list.add(newTransfer); diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java index 8f2fc45b875..c397cc74877 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -54,6 +55,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultCostCalculator; import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.state.TestStateBuilder; @@ -199,7 +201,7 @@ void createItineraryWithOnBoardFlexAccess() { flexAccessEgress, AccessEgressType.ACCESS ); - Transfer transfer = new Transfer(S2.getIndex(), 0); + Transfer transfer = new Transfer(S2.getIndex(), 0, Set.of(StreetMode.WALK)); RaptorTransfer raptorTransfer = new DefaultRaptorTransfer(S1.getIndex(), 0, 0, transfer); RaptorAccessEgress egress = new DefaultAccessEgress(S2.getIndex(), state); PathLeg egressLeg = new EgressPathLeg<>(egress, 0, 0, 0); diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java index 8dc4d548c18..ca846148dea 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java @@ -6,11 +6,13 @@ import java.util.List; import java.util.Optional; +import java.util.Set; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.opentripplanner._support.geometry.Coordinates; import org.opentripplanner.raptor.api.model.RaptorCostConverter; import org.opentripplanner.raptor.api.model.RaptorTransfer; +import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.IntersectionVertex; import org.opentripplanner.street.search.request.StreetSearchRequest; @@ -34,7 +36,7 @@ void limitMaxCost() { // very long edge from Berlin to Boston that has of course a huge cost to traverse var edge = StreetModelForTest.streetEdge(BERLIN_V, BOSTON_V); - var veryLongTransfer = new Transfer(0, List.of(edge)); + var veryLongTransfer = new Transfer(0, List.of(edge), Set.of(StreetMode.WALK)); assertTrue(veryLongTransfer.getDistanceMeters() > 1_000_000); // cost would be too high, so it should be capped to a maximum value assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get()); @@ -43,7 +45,7 @@ void limitMaxCost() { @Test void allowLowCost() { var edge = StreetModelForTest.streetEdge(BERLIN_V, BRANDENBURG_GATE_V); - var transfer = new Transfer(0, List.of(edge)); + var transfer = new Transfer(0, List.of(edge), Set.of(StreetMode.WALK)); assertTrue(transfer.getDistanceMeters() < 4000); final Optional raptorTransfer = transfer.asRaptorTransfer( StreetSearchRequest.of().build() @@ -58,26 +60,26 @@ class WithoutEdges { @Test void overflow() { - var veryLongTransfer = new Transfer(0, Integer.MAX_VALUE); + var veryLongTransfer = new Transfer(0, Integer.MAX_VALUE, Set.of(StreetMode.WALK)); assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get()); } @Test void negativeCost() { - var veryLongTransfer = new Transfer(0, -5); + var veryLongTransfer = new Transfer(0, -5, Set.of(StreetMode.WALK)); assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get()); } @Test void limitMaxCost() { - var veryLongTransfer = new Transfer(0, 8_000_000); + var veryLongTransfer = new Transfer(0, 8_000_000, Set.of(StreetMode.WALK)); // cost would be too high, so it will be capped before passing to RAPTOR assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get()); } @Test void allowLowCost() { - var transfer = new Transfer(0, 200); + var transfer = new Transfer(0, 200, Set.of(StreetMode.WALK)); final Optional raptorTransfer = transfer.asRaptorTransfer( StreetSearchRequest.of().build() ); From 44e4d31e0f7d7945513c2d465035eb173e482f57 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 29 Nov 2024 15:13:55 +0200 Subject: [PATCH 064/162] Add test for mode-specific transfers. --- .../module/DirectTransferGeneratorTest.java | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index 8a17e5258d8..310e474ae99 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -196,7 +196,7 @@ public void testMultipleRequestsWithoutPatterns() { reqWalk.journey().transfer().setMode(StreetMode.WALK); var reqBike = new RouteRequest(); - reqWalk.journey().transfer().setMode(StreetMode.BIKE); + reqBike.journey().transfer().setMode(StreetMode.BIKE); var transferRequests = List.of(reqWalk, reqBike); @@ -223,7 +223,7 @@ public void testMultipleRequestsWithPatterns() { reqWalk.journey().transfer().setMode(StreetMode.WALK); var reqBike = new RouteRequest(); - reqWalk.journey().transfer().setMode(StreetMode.BIKE); + reqBike.journey().transfer().setMode(StreetMode.BIKE); var transferRequests = List.of(reqWalk, reqBike); @@ -250,6 +250,61 @@ public void testMultipleRequestsWithPatterns() { ); } + @Test + public void testPathTransfersWithModesForMultipleRequestsWithPatterns() { + var reqWalk = new RouteRequest(); + reqWalk.journey().transfer().setMode(StreetMode.WALK); + + var reqBike = new RouteRequest(); + reqBike.journey().transfer().setMode(StreetMode.BIKE); + + var transferRequests = List.of(reqWalk, reqBike); + + TestOtpModel model = model(true); + var graph = model.graph(); + graph.hasStreets = true; + var timetableRepository = model.timetableRepository(); + + new DirectTransferGenerator( + graph, + timetableRepository, + DataImportIssueStore.NOOP, + MAX_TRANSFER_DURATION, + transferRequests + ) + .buildGraph(); + + var walkTransfers = timetableRepository + .getAllPathTransfers() + .stream() + .filter(pathTransfer -> pathTransfer.getModes().contains(StreetMode.WALK)) + .toList(); + var bikeTransfers = timetableRepository + .getAllPathTransfers() + .stream() + .filter(pathTransfer -> pathTransfer.getModes().contains(StreetMode.BIKE)) + .toList(); + var carTransfers = timetableRepository + .getAllPathTransfers() + .stream() + .filter(pathTransfer -> pathTransfer.getModes().contains(StreetMode.CAR)) + .toList(); + + assertTransfers( + walkTransfers, + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 100, List.of(V0, V21), S21), + tr(S11, 100, List.of(V11, V21), S21) + ); + assertTransfers( + bikeTransfers, + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 100, List.of(V0, V21), S21), + tr(S11, 110, List.of(V11, V22), S22) + ); + assertTransfers(carTransfers); + } + @Test public void testTransferOnIsolatedStations() { var otpModel = model(true, false, true); From 8448e6e91539ae9015269cc8de1ecef453dbc64e Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 29 Nov 2024 16:25:55 +0200 Subject: [PATCH 065/162] Remove unused imports and small changes. --- .../module/DirectTransferGenerator.java | 2 -- .../algorithm/raptoradapter/transit/Transfer.java | 9 +++++---- .../mapping/RaptorPathToItineraryMapperTest.java | 4 ++-- .../raptoradapter/transit/TransferTest.java | 13 +++++++------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 4c26808ab80..27fc87d9fff 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -5,10 +5,8 @@ import java.time.Duration; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java index c0c923228bf..6ee0dee83c0 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -32,16 +33,16 @@ public class Transfer { private final List edges; - private final Set modes; + private final EnumSet modes; - public Transfer(int toStop, List edges, Set modes) { + public Transfer(int toStop, List edges, EnumSet modes) { this.toStop = toStop; this.edges = edges; this.distanceMeters = (int) edges.stream().mapToDouble(Edge::getDistanceMeters).sum(); this.modes = modes; } - public Transfer(int toStopIndex, int distanceMeters, Set modes) { + public Transfer(int toStopIndex, int distanceMeters, EnumSet modes) { this.toStop = toStopIndex; this.distanceMeters = distanceMeters; this.edges = null; @@ -73,7 +74,7 @@ public List getEdges() { return edges; } - public Set getModes() { + public EnumSet getModes() { return modes; } diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java index c397cc74877..1c43234bd4d 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java @@ -12,9 +12,9 @@ import java.time.LocalDateTime; import java.time.Month; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; -import java.util.Set; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -201,7 +201,7 @@ void createItineraryWithOnBoardFlexAccess() { flexAccessEgress, AccessEgressType.ACCESS ); - Transfer transfer = new Transfer(S2.getIndex(), 0, Set.of(StreetMode.WALK)); + Transfer transfer = new Transfer(S2.getIndex(), 0, EnumSet.of(StreetMode.WALK)); RaptorTransfer raptorTransfer = new DefaultRaptorTransfer(S1.getIndex(), 0, 0, transfer); RaptorAccessEgress egress = new DefaultAccessEgress(S2.getIndex(), state); PathLeg egressLeg = new EgressPathLeg<>(egress, 0, 0, 0); diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java index ca846148dea..7edda8fba9c 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.street.model._data.StreetModelForTest.intersectionVertex; +import java.util.EnumSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -36,7 +37,7 @@ void limitMaxCost() { // very long edge from Berlin to Boston that has of course a huge cost to traverse var edge = StreetModelForTest.streetEdge(BERLIN_V, BOSTON_V); - var veryLongTransfer = new Transfer(0, List.of(edge), Set.of(StreetMode.WALK)); + var veryLongTransfer = new Transfer(0, List.of(edge), EnumSet.of(StreetMode.WALK)); assertTrue(veryLongTransfer.getDistanceMeters() > 1_000_000); // cost would be too high, so it should be capped to a maximum value assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get()); @@ -45,7 +46,7 @@ void limitMaxCost() { @Test void allowLowCost() { var edge = StreetModelForTest.streetEdge(BERLIN_V, BRANDENBURG_GATE_V); - var transfer = new Transfer(0, List.of(edge), Set.of(StreetMode.WALK)); + var transfer = new Transfer(0, List.of(edge), EnumSet.of(StreetMode.WALK)); assertTrue(transfer.getDistanceMeters() < 4000); final Optional raptorTransfer = transfer.asRaptorTransfer( StreetSearchRequest.of().build() @@ -60,26 +61,26 @@ class WithoutEdges { @Test void overflow() { - var veryLongTransfer = new Transfer(0, Integer.MAX_VALUE, Set.of(StreetMode.WALK)); + var veryLongTransfer = new Transfer(0, Integer.MAX_VALUE, EnumSet.of(StreetMode.WALK)); assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get()); } @Test void negativeCost() { - var veryLongTransfer = new Transfer(0, -5, Set.of(StreetMode.WALK)); + var veryLongTransfer = new Transfer(0, -5, EnumSet.of(StreetMode.WALK)); assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get()); } @Test void limitMaxCost() { - var veryLongTransfer = new Transfer(0, 8_000_000, Set.of(StreetMode.WALK)); + var veryLongTransfer = new Transfer(0, 8_000_000, EnumSet.of(StreetMode.WALK)); // cost would be too high, so it will be capped before passing to RAPTOR assertMaxCost(veryLongTransfer.asRaptorTransfer(StreetSearchRequest.of().build()).get()); } @Test void allowLowCost() { - var transfer = new Transfer(0, 200, Set.of(StreetMode.WALK)); + var transfer = new Transfer(0, 200, EnumSet.of(StreetMode.WALK)); final Optional raptorTransfer = transfer.asRaptorTransfer( StreetSearchRequest.of().build() ); From b3bad269596f10f452c4e8a208f34b2ea10cd407 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 2 Dec 2024 10:24:07 +0200 Subject: [PATCH 066/162] Implement new schema design --- .../apis/gtfs/GtfsGraphQLIndex.java | 12 +- .../CallScheduledTimeTypeResolver.java | 21 +++ ...java => CallStopLocationTypeResolver.java} | 8 +- .../apis/gtfs/datafetchers/CallTimeImpl.java | 25 --- ...timateImpl.java => EstimatedTimeImpl.java} | 6 +- .../apis/gtfs/datafetchers/StopCallImpl.java | 41 +++-- .../datafetchers/TripOnServiceDateImpl.java | 22 +-- .../gtfs/generated/GraphQLDataFetchers.java | 65 ++++---- .../apis/gtfs/generated/graphql-codegen.yml | 5 +- .../apis/gtfs/model/ArrivalDepartureTime.java | 9 + .../apis/gtfs/model/CallRealTime.java | 6 + .../apis/gtfs/model/CallSchedule.java | 3 + .../transit/model/timetable/CallTime.java | 40 ----- ...alTimeEstimate.java => EstimatedTime.java} | 9 +- .../opentripplanner/apis/gtfs/schema.graphqls | 86 +++++----- .../gtfs/expectations/canceled-trips.json | 154 ++++++++++-------- .../apis/gtfs/queries/canceled-trips.graphql | 72 +++++--- 17 files changed, 313 insertions(+), 271 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallScheduledTimeTypeResolver.java rename application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{CallTypeResolver.java => CallStopLocationTypeResolver.java} (66%) delete mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTimeImpl.java rename application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/{CallRealTimeEstimateImpl.java => EstimatedTimeImpl.java} (69%) create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/model/CallRealTime.java create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/model/CallSchedule.java delete mode 100644 application/src/main/java/org/opentripplanner/transit/model/timetable/CallTime.java rename application/src/main/java/org/opentripplanner/transit/model/timetable/{CallRealTimeEstimate.java => EstimatedTime.java} (69%) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 28194b21856..302458ac656 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -31,15 +31,15 @@ import org.opentripplanner.apis.gtfs.datafetchers.BikeRentalStationImpl; import org.opentripplanner.apis.gtfs.datafetchers.BookingInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.BookingTimeImpl; -import org.opentripplanner.apis.gtfs.datafetchers.CallRealTimeEstimateImpl; -import org.opentripplanner.apis.gtfs.datafetchers.CallTimeImpl; -import org.opentripplanner.apis.gtfs.datafetchers.CallTypeResolver; +import org.opentripplanner.apis.gtfs.datafetchers.CallScheduledTimeTypeResolver; +import org.opentripplanner.apis.gtfs.datafetchers.CallStopLocationTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.CarParkImpl; import org.opentripplanner.apis.gtfs.datafetchers.ContactInfoImpl; import org.opentripplanner.apis.gtfs.datafetchers.CoordinatesImpl; import org.opentripplanner.apis.gtfs.datafetchers.CurrencyImpl; import org.opentripplanner.apis.gtfs.datafetchers.DefaultFareProductImpl; import org.opentripplanner.apis.gtfs.datafetchers.DepartureRowImpl; +import org.opentripplanner.apis.gtfs.datafetchers.EstimatedTimeImpl; import org.opentripplanner.apis.gtfs.datafetchers.FareProductTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.FareProductUseImpl; import org.opentripplanner.apis.gtfs.datafetchers.FeedImpl; @@ -135,7 +135,8 @@ protected static GraphQLSchema buildSchema() { .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) - .type("Call", type -> type.typeResolver(new CallTypeResolver())) + .type("CallStopLocation", type -> type.typeResolver(new CallStopLocationTypeResolver())) + .type("CallScheduledTime", type -> type.typeResolver(new CallScheduledTimeTypeResolver())) .type(typeWiring.build(AgencyImpl.class)) .type(typeWiring.build(AlertImpl.class)) .type(typeWiring.build(BikeParkImpl.class)) @@ -193,8 +194,7 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(TripOccupancyImpl.class)) .type(typeWiring.build(LegTimeImpl.class)) .type(typeWiring.build(RealTimeEstimateImpl.class)) - .type(typeWiring.build(CallTimeImpl.class)) - .type(typeWiring.build(CallRealTimeEstimateImpl.class)) + .type(typeWiring.build(EstimatedTimeImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallScheduledTimeTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallScheduledTimeTypeResolver.java new file mode 100644 index 00000000000..ac45b3766b6 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallScheduledTimeTypeResolver.java @@ -0,0 +1,21 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.TypeResolutionEnvironment; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import graphql.schema.TypeResolver; +import org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime; + +public class CallScheduledTimeTypeResolver implements TypeResolver { + + @Override + public GraphQLObjectType getType(TypeResolutionEnvironment environment) { + Object o = environment.getObject(); + GraphQLSchema schema = environment.getSchema(); + + if (o instanceof ArrivalDepartureTime) { + return schema.getObjectType("ArrivalDepartureTime"); + } + return null; + } +} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallStopLocationTypeResolver.java similarity index 66% rename from application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTypeResolver.java rename to application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallStopLocationTypeResolver.java index 02ee6784073..5a7b02320a3 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTypeResolver.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallStopLocationTypeResolver.java @@ -4,17 +4,17 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; -import org.opentripplanner.model.TripTimeOnDate; +import org.opentripplanner.transit.model.site.StopLocation; -public class CallTypeResolver implements TypeResolver { +public class CallStopLocationTypeResolver implements TypeResolver { @Override public GraphQLObjectType getType(TypeResolutionEnvironment environment) { Object o = environment.getObject(); GraphQLSchema schema = environment.getSchema(); - if (o instanceof TripTimeOnDate) { - return schema.getObjectType("StopCall"); + if (o instanceof StopLocation) { + return schema.getObjectType("Stop"); } return null; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTimeImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTimeImpl.java deleted file mode 100644 index 5a2da2c2d82..00000000000 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallTimeImpl.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.opentripplanner.apis.gtfs.datafetchers; - -import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; -import java.time.OffsetDateTime; -import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; -import org.opentripplanner.transit.model.timetable.CallRealTimeEstimate; -import org.opentripplanner.transit.model.timetable.CallTime; - -public class CallTimeImpl implements GraphQLDataFetchers.GraphQLCallTime { - - @Override - public DataFetcher estimated() { - return environment -> getSource(environment).estimated(); - } - - @Override - public DataFetcher scheduledTime() { - return environment -> getSource(environment).scheduledTime().toOffsetDateTime(); - } - - private CallTime getSource(DataFetchingEnvironment environment) { - return environment.getSource(); - } -} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallRealTimeEstimateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EstimatedTimeImpl.java similarity index 69% rename from application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallRealTimeEstimateImpl.java rename to application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EstimatedTimeImpl.java index 09439c6e916..eebb64ee625 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/CallRealTimeEstimateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EstimatedTimeImpl.java @@ -5,9 +5,9 @@ import java.time.Duration; import java.time.OffsetDateTime; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; -import org.opentripplanner.transit.model.timetable.CallRealTimeEstimate; +import org.opentripplanner.transit.model.timetable.EstimatedTime; -public class CallRealTimeEstimateImpl implements GraphQLDataFetchers.GraphQLCallRealTimeEstimate { +public class EstimatedTimeImpl implements GraphQLDataFetchers.GraphQLEstimatedTime { @Override public DataFetcher delay() { @@ -19,7 +19,7 @@ public DataFetcher time() { return environment -> getSource(environment).time().toOffsetDateTime(); } - private CallRealTimeEstimate getSource(DataFetchingEnvironment environment) { + private EstimatedTime getSource(DataFetchingEnvironment environment) { return environment.getSource(); } } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopCallImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopCallImpl.java index cb5ec7e15a5..4d3ede74c76 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopCallImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StopCallImpl.java @@ -7,43 +7,52 @@ import java.time.ZonedDateTime; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.model.ArrivalDepartureTime; +import org.opentripplanner.apis.gtfs.model.CallRealTime; +import org.opentripplanner.apis.gtfs.model.CallSchedule; import org.opentripplanner.model.TripTimeOnDate; -import org.opentripplanner.transit.model.timetable.CallTime; +import org.opentripplanner.transit.model.timetable.EstimatedTime; import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.utils.time.ServiceDateUtils; public class StopCallImpl implements GraphQLDataFetchers.GraphQLStopCall { @Override - public DataFetcher arrival() { + public DataFetcher realTime() { return environment -> { var tripTime = getSource(environment); - var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledArrival()); - if (scheduledTime == null) { + if (!tripTime.isRealtime()) { return null; } - return tripTime.isRealtime() - ? CallTime.of(scheduledTime, tripTime.getArrivalDelay()) - : CallTime.ofStatic(scheduledTime); + var scheduledArrival = getZonedDateTime(environment, tripTime.getScheduledArrival()); + var estimatedArrival = scheduledArrival == null + ? null + : EstimatedTime.of(scheduledArrival, tripTime.getArrivalDelay()); + var scheduledDeparture = getZonedDateTime(environment, tripTime.getScheduledDeparture()); + var estimatedDeparture = scheduledDeparture == null + ? null + : EstimatedTime.of(scheduledDeparture, tripTime.getDepartureDelay()); + return new CallRealTime(estimatedArrival, estimatedDeparture); }; } @Override - public DataFetcher departure() { + public DataFetcher schedule() { return environment -> { var tripTime = getSource(environment); - var scheduledTime = getZonedDateTime(environment, tripTime.getScheduledDeparture()); - if (scheduledTime == null) { - return null; - } - return tripTime.isRealtime() - ? CallTime.of(scheduledTime, tripTime.getDepartureDelay()) - : CallTime.ofStatic(scheduledTime); + var scheduledArrival = getZonedDateTime(environment, tripTime.getScheduledArrival()); + var scheduledDeparture = getZonedDateTime(environment, tripTime.getScheduledDeparture()); + return new CallSchedule( + new ArrivalDepartureTime( + scheduledArrival == null ? null : scheduledArrival.toOffsetDateTime(), + scheduledDeparture == null ? null : scheduledDeparture.toOffsetDateTime() + ) + ); }; } @Override - public DataFetcher stop() { + public DataFetcher stopLocation() { return environment -> getSource(environment).getStop(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java index aae18085cc1..3b00551835e 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/TripOnServiceDateImpl.java @@ -24,7 +24,7 @@ public DataFetcher serviceDate() { } @Override - public DataFetcher end() { + public DataFetcher end() { return environment -> { var arguments = getFromTripTimesArguments(environment); if (arguments.timetable() == null) { @@ -40,7 +40,7 @@ public DataFetcher end() { } @Override - public DataFetcher start() { + public DataFetcher start() { return environment -> { var arguments = getFromTripTimesArguments(environment); if (arguments.timetable() == null) { @@ -56,22 +56,18 @@ public DataFetcher start() { } @Override - public DataFetcher> stopCalls() { + public DataFetcher> stopCalls() { return environment -> { var arguments = getFromTripTimesArguments(environment); if (arguments.timetable() == null) { return List.of(); } - return TripTimeOnDate - .fromTripTimes( - arguments.timetable(), - arguments.trip(), - arguments.serviceDate(), - arguments.midnight() - ) - .stream() - .map(Object.class::cast) - .toList(); + return TripTimeOnDate.fromTripTimes( + arguments.timetable(), + arguments.trip(), + arguments.serviceDate(), + arguments.midnight() + ); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 986fb9b8318..fb3c0ddbb41 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -23,6 +23,8 @@ 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.CallRealTime; +import org.opentripplanner.apis.gtfs.model.CallSchedule; import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.model.PlanPageInfo; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; @@ -67,8 +69,7 @@ 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.CallRealTimeEstimate; -import org.opentripplanner.transit.model.timetable.CallTime; +import org.opentripplanner.transit.model.timetable.EstimatedTime; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.booking.BookingInfo; @@ -145,6 +146,13 @@ public interface GraphQLAlert { /** Entity related to an alert */ public interface GraphQLAlertEntity extends TypeResolver {} + /** Arrival and departure time (not relative to midnight). */ + public interface GraphQLArrivalDepartureTime { + public DataFetcher arrival(); + + public DataFetcher departure(); + } + /** Bike park represents a location where bicycles can be parked. */ public interface GraphQLBikePark { public DataFetcher bikeParkId(); @@ -242,28 +250,23 @@ public interface GraphQLBookingTime { public DataFetcher time(); } - /** - * Call is a visit to a location (a stop or an area) in a trip on service date. It can contain - * exact arrival and departure times and/or a scheduled time window. - */ - public interface GraphQLCall extends TypeResolver {} + /** Real-time estimates for arrival and departure times for a stop location. */ + public interface GraphQLCallRealTime { + public DataFetcher arrival(); - /** Real-time estimates for an arrival or departure at a certain place. */ - public interface GraphQLCallRealTimeEstimate { - public DataFetcher delay(); + public DataFetcher departure(); + } - public DataFetcher time(); + /** What is scheduled for a trip on a service date for a stop location. */ + public interface GraphQLCallSchedule { + public DataFetcher time(); } - /** - * Timing of an arrival or a departure to or from a stop. May contain real-time information if - * available. This is used when there is a known scheduled time. - */ - public interface GraphQLCallTime { - public DataFetcher estimated(); + /** Scheduled times for a trip on a service date for a stop location. */ + public interface GraphQLCallScheduledTime extends TypeResolver {} - public DataFetcher scheduledTime(); - } + /** Location where a transit vehicle stops at. */ + public interface GraphQLCallStopLocation extends TypeResolver {} /** Car park represents a location where cars can be parked. */ public interface GraphQLCarPark { @@ -385,6 +388,13 @@ public interface GraphQLEmissions { public DataFetcher co2(); } + /** Real-time estimates for an arrival or departure at a certain place. */ + public interface GraphQLEstimatedTime { + public DataFetcher delay(); + + public DataFetcher time(); + } + /** A 'medium' that a fare product applies to, for example cash, 'Oyster Card' or 'DB Navigator App'. */ public interface GraphQLFareMedium { public DataFetcher id(); @@ -1073,16 +1083,13 @@ public interface GraphQLStop { public DataFetcher zoneId(); } - /** - * Stop call represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. - * The times are exact (although can be changed by real-time updates), not time windows. - */ + /** Stop call represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop location. */ public interface GraphQLStopCall { - public DataFetcher arrival(); + public DataFetcher realTime(); - public DataFetcher departure(); + public DataFetcher schedule(); - public DataFetcher stop(); + public DataFetcher stopLocation(); } public interface GraphQLStopGeometries { @@ -1253,13 +1260,13 @@ public interface GraphQLTripOccupancy { /** A trip on a specific service date. */ public interface GraphQLTripOnServiceDate { - public DataFetcher end(); + public DataFetcher end(); public DataFetcher serviceDate(); - public DataFetcher start(); + public DataFetcher start(); - public DataFetcher> stopCalls(); + public DataFetcher> stopCalls(); public DataFetcher trip(); } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 117744d314a..a12f9ff2c64 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -63,7 +63,6 @@ config: TripOnServiceDate: org.opentripplanner.transit.model.timetable.TripOnServiceDate#TripOnServiceDate TripOnServiceDateConnection: graphql.relay.Connection#Connection TripOnServiceDateEdge: graphql.relay.Edge#Edge - CallTime: org.opentripplanner.transit.model.timetable.CallTime#CallTime debugOutput: org.opentripplanner.api.resource.DebugOutput#DebugOutput DepartureRow: org.opentripplanner.routing.graphfinder.PatternAtStop#PatternAtStop elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step @@ -131,6 +130,8 @@ config: RiderCategory: org.opentripplanner.model.fare.RiderCategory#RiderCategory StopPosition: org.opentripplanner.apis.gtfs.model.StopPosition#StopPosition RealTimeEstimate: org.opentripplanner.model.plan.LegRealTimeEstimate#LegRealTimeEstimate - CallRealTimeEstimate: org.opentripplanner.transit.model.timetable.CallRealTimeEstimate#CallRealTimeEstimate + EstimatedTime: org.opentripplanner.transit.model.timetable.EstimatedTime#EstimatedTime + CallRealTime: org.opentripplanner.apis.gtfs.model.CallRealTime#CallRealTime RentalPlace: org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace#VehicleRentalPlace + CallSchedule: org.opentripplanner.apis.gtfs.model.CallSchedule#CallSchedule diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java b/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java new file mode 100644 index 00000000000..f72bdd0b3e9 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/model/ArrivalDepartureTime.java @@ -0,0 +1,9 @@ +package org.opentripplanner.apis.gtfs.model; + +import java.time.OffsetDateTime; +import javax.annotation.Nullable; + +public record ArrivalDepartureTime( + @Nullable OffsetDateTime arrival, + @Nullable OffsetDateTime departure +) {} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/model/CallRealTime.java b/application/src/main/java/org/opentripplanner/apis/gtfs/model/CallRealTime.java new file mode 100644 index 00000000000..94caf731627 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/model/CallRealTime.java @@ -0,0 +1,6 @@ +package org.opentripplanner.apis.gtfs.model; + +import javax.annotation.Nullable; +import org.opentripplanner.transit.model.timetable.EstimatedTime; + +public record CallRealTime(@Nullable EstimatedTime arrival, @Nullable EstimatedTime departure) {} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/model/CallSchedule.java b/application/src/main/java/org/opentripplanner/apis/gtfs/model/CallSchedule.java new file mode 100644 index 00000000000..6b35d2ed425 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/model/CallSchedule.java @@ -0,0 +1,3 @@ +package org.opentripplanner.apis.gtfs.model; + +public record CallSchedule(ArrivalDepartureTime time) {} diff --git a/application/src/main/java/org/opentripplanner/transit/model/timetable/CallTime.java b/application/src/main/java/org/opentripplanner/transit/model/timetable/CallTime.java deleted file mode 100644 index 4a7df9fb217..00000000000 --- a/application/src/main/java/org/opentripplanner/transit/model/timetable/CallTime.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.opentripplanner.transit.model.timetable; - -import java.time.Duration; -import java.time.ZonedDateTime; -import java.util.Objects; -import javax.annotation.Nullable; - -/** - * A scheduled time of a transit vehicle at a certain location with an optional realtime - * information. This is meant to be used in timetables (not in transit legs). - */ -public class CallTime { - - private final ZonedDateTime scheduledTime; - - @Nullable - private final CallRealTimeEstimate estimated; - - private CallTime(ZonedDateTime scheduledTime, @Nullable CallRealTimeEstimate estimated) { - this.scheduledTime = Objects.requireNonNull(scheduledTime); - this.estimated = estimated; - } - - public static CallTime of(ZonedDateTime realtime, int delaySecs) { - var delay = Duration.ofSeconds(delaySecs); - return new CallTime(realtime.minus(delay), new CallRealTimeEstimate(realtime, delay)); - } - - public static CallTime ofStatic(ZonedDateTime staticTime) { - return new CallTime(staticTime, null); - } - - public ZonedDateTime scheduledTime() { - return scheduledTime; - } - - public CallRealTimeEstimate estimated() { - return estimated; - } -} diff --git a/application/src/main/java/org/opentripplanner/transit/model/timetable/CallRealTimeEstimate.java b/application/src/main/java/org/opentripplanner/transit/model/timetable/EstimatedTime.java similarity index 69% rename from application/src/main/java/org/opentripplanner/transit/model/timetable/CallRealTimeEstimate.java rename to application/src/main/java/org/opentripplanner/transit/model/timetable/EstimatedTime.java index eeac4a321c4..359a8c7de6a 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/timetable/CallRealTimeEstimate.java +++ b/application/src/main/java/org/opentripplanner/transit/model/timetable/EstimatedTime.java @@ -8,7 +8,7 @@ * Realtime information about a vehicle at a certain place. This is meant to be used in timetables * (not in transit legs). */ -public class CallRealTimeEstimate { +public class EstimatedTime { private final ZonedDateTime time; private final Duration delay; @@ -16,11 +16,16 @@ public class CallRealTimeEstimate { /** * @param delay Delay or "earliness" of a vehicle. Earliness is expressed as a negative number. */ - public CallRealTimeEstimate(ZonedDateTime time, Duration delay) { + private EstimatedTime(ZonedDateTime time, Duration delay) { this.time = Objects.requireNonNull(time); this.delay = Objects.requireNonNull(delay); } + public static EstimatedTime of(ZonedDateTime scheduledTime, int delaySecs) { + var delay = Duration.ofSeconds(delaySecs); + return new EstimatedTime(scheduledTime.minus(delay), delay); + } + public ZonedDateTime time() { return time; } diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index d7e540e1a1b..10aa3f8c524 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -73,11 +73,11 @@ interface PlaceInterface { "Entity related to an alert" union AlertEntity = Agency | Pattern | Route | RouteType | Stop | StopOnRoute | StopOnTrip | Trip | Unknown -""" -Call is a visit to a location (a stop or an area) in a trip on service date. It can contain -exact arrival and departure times and/or a scheduled time window. -""" -union Call = StopCall +"Scheduled times for a trip on a service date for a stop location." +union CallScheduledTime = ArrivalDepartureTime + +"Location where a transit vehicle stops at." +union CallStopLocation = Stop "Rental place union that represents either a VehicleRentalStation or a RentalVehicle" union RentalPlace = RentalVehicle | VehicleRentalStation @@ -172,6 +172,14 @@ type Alert implements Node { trip: Trip @deprecated(reason : "Alert can have multiple affected entities now instead of there being duplicate alerts\nfor different entities. This will return only one of the affected trips.\nUse entities instead.") } +"Arrival and departure time (not relative to midnight)." +type ArrivalDepartureTime { + "Arrival time as an ISO-8601-formatted datetime." + arrival: OffsetDateTime + "Departure time as an ISO-8601-formatted datetime." + departure: OffsetDateTime +} + "Bike park represents a location where bicycles can be parked." type BikePark implements Node & PlaceInterface { "ID of the bike park" @@ -303,27 +311,18 @@ type BookingTime { time: String } -"Real-time estimates for an arrival or departure at a certain place." -type CallRealTimeEstimate { - """ - The delay or "earliness" of the vehicle at a certain place. This estimate can change quite often. - - If the vehicle is early then this is a negative duration. - """ - delay: Duration! - "The estimate for a call event (such as arrival or departure) at a certain place. This estimate can change quite often." - time: OffsetDateTime! +"Real-time estimates for arrival and departure times for a stop location." +type CallRealTime { + "Real-time estimates for the arrival." + arrival: EstimatedTime + "Real-time estimates for the departure." + departure: EstimatedTime } -""" -Timing of an arrival or a departure to or from a stop. May contain real-time information if -available. This is used when there is a known scheduled time. -""" -type CallTime { - "The estimated time of the event. If no real-time information is available, this is null." - estimated: CallRealTimeEstimate - "The scheduled time of the event." - scheduledTime: OffsetDateTime +"What is scheduled for a trip on a service date for a stop location." +type CallSchedule { + "Scheduled time for a trip on a service date for a stop location." + time: CallScheduledTime } "Car park represents a location where cars can be parked." @@ -485,6 +484,18 @@ type Emissions { co2: Grams } +"Real-time estimates for an arrival or departure at a certain place." +type EstimatedTime { + """ + The delay or "earliness" of the vehicle at a certain place. This estimate can change quite often. + + If the vehicle is early then this is a negative duration. + """ + delay: Duration! + "The estimate for a call event (such as arrival or departure) at a certain place. This estimate can change quite often." + time: OffsetDateTime! +} + "A 'medium' that a fare product applies to, for example cash, 'Oyster Card' or 'DB Navigator App'." type FareMedium { "ID of the medium" @@ -2217,17 +2228,14 @@ type Stop implements Node & PlaceInterface { zoneId: String } -""" -Stop call represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop. -The times are exact (although can be changed by real-time updates), not time windows. -""" +"Stop call represents the time when a specific trip on a specific date arrives to and/or departs from a specific stop location." type StopCall { - "Scheduled arrival time to the stop and a real-time estimate, if one exists." - arrival: CallTime - "Scheduled departure time from the stop and a real-time estimate, if one exists." - departure: CallTime + "Real-time estimates for arrival and departure times for this stop location." + realTime: CallRealTime + "Scheduled arrival and departure times for this stop location." + schedule: CallSchedule "The stop where this arrival/departure happens" - stop: Stop + stopLocation: CallStopLocation! } type StopGeometries { @@ -2487,8 +2495,8 @@ type TripOccupancy { "A trip on a specific service date." type TripOnServiceDate { - "Information related to trip's scheduled arrival to the final stop. Can contain realtime information." - end: Call! + "Information related to trip's scheduled arrival to the final stop location. Can contain realtime information." + end: StopCall! """ The service date when the trip occurs. @@ -2497,10 +2505,10 @@ type TripOnServiceDate { on Sunday morning at 1am to 3am, might have the previous Saturday's service date. """ serviceDate: LocalDate! - "Information related to trip's scheduled departure from the first stop. Can contain realtime information." - start: Call! - "List of times when this trip arrives to or departs from a stop and information related to the stop" - stopCalls: [Call!]! + "Information related to trip's scheduled departure from the first stop location. Can contain realtime information." + start: StopCall! + "List of times when this trip arrives to or departs from a stop location and information related to the visit to the stop location." + stopCalls: [StopCall!]! "This trip on service date is an instance of this trip." trip: Trip } diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json index 5e32b67fc4e..1e2b4d39a3f 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/canceled-trips.json @@ -1,99 +1,119 @@ { - "data": { - "canceledTrips": { - "pageInfo": { - "hasNextPage": false, - "hasPreviousPage": false, - "startCursor": "c2ltcGxlLWN1cnNvcjA=", - "endCursor": "c2ltcGxlLWN1cnNvcjA=" + "data" : { + "canceledTrips" : { + "pageInfo" : { + "hasNextPage" : false, + "hasPreviousPage" : false, + "startCursor" : "c2ltcGxlLWN1cnNvcjA=", + "endCursor" : "c2ltcGxlLWN1cnNvcjA=" }, - "edges": [ + "edges" : [ { - "node": { - "serviceDate": "2024-08-09", - "end": { - "arrival": { - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:40:00+02:00" - }, - "scheduledTime": "2024-08-09T11:40:00+02:00" + "node" : { + "serviceDate" : "2024-08-09", + "end" : { + "schedule" : { + "time" : { + "arrival" : "2024-08-09T11:40:00+02:00" + } + }, + "realTime" : { + "arrival" : { + "delay" : "PT0S", + "time" : "2024-08-09T11:40:00+02:00" + } + }, + "stopLocation" : { + "gtfsId" : "F:Stop_2" } }, - "start": { - "departure": { - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:30:00+02:00" - }, - "scheduledTime": "2024-08-09T11:30:00+02:00" + "start" : { + "schedule" : { + "time" : { + "departure" : "2024-08-09T11:30:00+02:00" + } + }, + "realTime" : { + "departure" : { + "delay" : "PT0S", + "time" : "2024-08-09T11:30:00+02:00" + } + }, + "stopLocation" : { + "gtfsId" : "F:Stop_0" } }, - "stopCalls": [ + "stopCalls" : [ { - "stop": { - "gtfsId": "F:Stop_0" - }, - "arrival": { - "scheduledTime": "2024-08-09T11:30:00+02:00", - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:30:00+02:00" + "schedule" : { + "time" : { + "arrival" : "2024-08-09T11:30:00+02:00", + "departure" : "2024-08-09T11:30:00+02:00" } }, - "departure": { - "scheduledTime": "2024-08-09T11:30:00+02:00", - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:30:00+02:00" + "realTime" : { + "arrival" : { + "delay" : "PT0S", + "time" : "2024-08-09T11:30:00+02:00" + }, + "departure" : { + "delay" : "PT0S", + "time" : "2024-08-09T11:30:00+02:00" } + }, + "stopLocation" : { + "gtfsId" : "F:Stop_0" } }, { - "stop": { - "gtfsId": "F:Stop_1" - }, - "arrival": { - "scheduledTime": "2024-08-09T11:35:00+02:00", - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:35:00+02:00" + "schedule" : { + "time" : { + "arrival" : "2024-08-09T11:35:00+02:00", + "departure" : "2024-08-09T11:35:00+02:00" } }, - "departure": { - "scheduledTime": "2024-08-09T11:35:00+02:00", - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:35:00+02:00" + "realTime" : { + "arrival" : { + "delay" : "PT0S", + "time" : "2024-08-09T11:35:00+02:00" + }, + "departure" : { + "delay" : "PT0S", + "time" : "2024-08-09T11:35:00+02:00" } + }, + "stopLocation" : { + "gtfsId" : "F:Stop_1" } }, { - "stop": { - "gtfsId": "F:Stop_2" - }, - "arrival": { - "scheduledTime": "2024-08-09T11:40:00+02:00", - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:40:00+02:00" + "schedule" : { + "time" : { + "arrival" : "2024-08-09T11:40:00+02:00", + "departure" : "2024-08-09T11:40:00+02:00" } }, - "departure": { - "scheduledTime": "2024-08-09T11:40:00+02:00", - "estimated": { - "delay": "PT0S", - "time": "2024-08-09T11:40:00+02:00" + "realTime" : { + "arrival" : { + "delay" : "PT0S", + "time" : "2024-08-09T11:40:00+02:00" + }, + "departure" : { + "delay" : "PT0S", + "time" : "2024-08-09T11:40:00+02:00" } + }, + "stopLocation" : { + "gtfsId" : "F:Stop_2" } } ], - "trip": { - "gtfsId": "F:321Canceled" + "trip" : { + "gtfsId" : "F:321Canceled" } } } ] } } -} +} \ No newline at end of file diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql index b3817605f37..988a1e62cd9 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/canceled-trips.graphql @@ -10,45 +10,67 @@ node { serviceDate end { - ... on StopCall { - arrival { - estimated { - delay - time + schedule { + time { + ... on ArrivalDepartureTime { + arrival } - scheduledTime + } + } + realTime { + arrival { + delay + time + } + } + stopLocation { + ... on Stop { + gtfsId } } } start { - ... on StopCall { - departure { - estimated { - delay - time + schedule { + time { + ... on ArrivalDepartureTime { + departure } - scheduledTime + } + } + realTime { + departure { + delay + time + } + } + stopLocation { + ... on Stop { + gtfsId } } } stopCalls { - ... on StopCall { - stop { - gtfsId + schedule { + time { + ... on ArrivalDepartureTime { + arrival + departure + } } + } + realTime { arrival { - scheduledTime - estimated { - delay - time - } + delay + time } departure { - scheduledTime - estimated { - delay - time - } + delay + time + } + } + stopLocation { + ... on Stop { + gtfsId } } } From 6a485a35c7282c915be8e491af863be450cc787c Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 2 Dec 2024 11:05:02 +0200 Subject: [PATCH 067/162] Minor schema doc fixes --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 10aa3f8c524..04fd2220283 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2234,7 +2234,7 @@ type StopCall { realTime: CallRealTime "Scheduled arrival and departure times for this stop location." schedule: CallSchedule - "The stop where this arrival/departure happens" + "The stop where this arrival/departure happens." stopLocation: CallStopLocation! } @@ -2495,7 +2495,7 @@ type TripOccupancy { "A trip on a specific service date." type TripOnServiceDate { - "Information related to trip's scheduled arrival to the final stop location. Can contain realtime information." + "Information related to trip's scheduled arrival to the final stop location. Can contain real-time information." end: StopCall! """ The service date when the trip occurs. @@ -2505,7 +2505,7 @@ type TripOnServiceDate { on Sunday morning at 1am to 3am, might have the previous Saturday's service date. """ serviceDate: LocalDate! - "Information related to trip's scheduled departure from the first stop location. Can contain realtime information." + "Information related to trip's scheduled departure from the first stop location. Can contain real-time information." start: StopCall! "List of times when this trip arrives to or departs from a stop location and information related to the visit to the stop location." stopCalls: [StopCall!]! From 5217ce51f2c66c978b33f1f71a89d8f54a41dcaa Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 2 Dec 2024 11:05:09 +0200 Subject: [PATCH 068/162] Formatting --- .../apis/gtfs/expectations/patterns.json | 208 +++++++++--------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json index e9882981a13..4aac9295cc9 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/patterns.json @@ -1,145 +1,145 @@ { - "data" : { - "patterns" : [ + "data": { + "patterns": [ { - "code" : "F:BUS", - "headsign" : "Trip Headsign", - "trips" : [ + "code": "F:BUS", + "headsign": "Trip Headsign", + "trips": [ { - "gtfsId" : "F:123", - "stoptimes" : [ + "gtfsId": "F:123", + "stoptimes": [ { - "stop" : { - "gtfsId" : "F:Stop_0", - "name" : "Stop_0" + "stop": { + "gtfsId": "F:Stop_0", + "name": "Stop_0" }, - "headsign" : "Stop headsign at stop 10", - "scheduledArrival" : 39600, - "scheduledDeparture" : 39600, - "stopPosition" : 10, - "stopPositionInPattern" : 0, - "realtimeState" : "SCHEDULED", - "pickupType" : "SCHEDULED", - "dropoffType" : "SCHEDULED" + "headsign": "Stop headsign at stop 10", + "scheduledArrival": 39600, + "scheduledDeparture": 39600, + "stopPosition": 10, + "stopPositionInPattern": 0, + "realtimeState": "SCHEDULED", + "pickupType": "SCHEDULED", + "dropoffType": "SCHEDULED" }, { - "stop" : { - "gtfsId" : "F:Stop_1", - "name" : "Stop_1" + "stop": { + "gtfsId": "F:Stop_1", + "name": "Stop_1" }, - "headsign" : "Stop headsign at stop 20", - "scheduledArrival" : 39900, - "scheduledDeparture" : 39900, - "stopPosition" : 20, - "stopPositionInPattern" : 1, - "realtimeState" : "SCHEDULED", - "pickupType" : "SCHEDULED", - "dropoffType" : "SCHEDULED" + "headsign": "Stop headsign at stop 20", + "scheduledArrival": 39900, + "scheduledDeparture": 39900, + "stopPosition": 20, + "stopPositionInPattern": 1, + "realtimeState": "SCHEDULED", + "pickupType": "SCHEDULED", + "dropoffType": "SCHEDULED" }, { - "stop" : { - "gtfsId" : "F:Stop_2", - "name" : "Stop_2" + "stop": { + "gtfsId": "F:Stop_2", + "name": "Stop_2" }, - "headsign" : "Stop headsign at stop 30", - "scheduledArrival" : 40200, - "scheduledDeparture" : 40200, - "stopPosition" : 30, - "stopPositionInPattern" : 2, - "realtimeState" : "SCHEDULED", - "pickupType" : "SCHEDULED", - "dropoffType" : "SCHEDULED" + "headsign": "Stop headsign at stop 30", + "scheduledArrival": 40200, + "scheduledDeparture": 40200, + "stopPosition": 30, + "stopPositionInPattern": 2, + "realtimeState": "SCHEDULED", + "pickupType": "SCHEDULED", + "dropoffType": "SCHEDULED" } ], - "occupancy" : { - "occupancyStatus" : "FEW_SEATS_AVAILABLE" + "occupancy": { + "occupancyStatus": "FEW_SEATS_AVAILABLE" } }, { - "gtfsId" : "F:321Canceled", - "stoptimes" : [ + "gtfsId": "F:321Canceled", + "stoptimes": [ { - "stop" : { - "gtfsId" : "F:Stop_0", - "name" : "Stop_0" + "stop": { + "gtfsId": "F:Stop_0", + "name": "Stop_0" }, - "headsign" : "Stop headsign at stop 10", - "scheduledArrival" : 41400, - "scheduledDeparture" : 41400, - "stopPosition" : 10, - "stopPositionInPattern" : 0, - "realtimeState" : "CANCELED", - "pickupType" : null, - "dropoffType" : null + "headsign": "Stop headsign at stop 10", + "scheduledArrival": 41400, + "scheduledDeparture": 41400, + "stopPosition": 10, + "stopPositionInPattern": 0, + "realtimeState": "CANCELED", + "pickupType": null, + "dropoffType": null }, { - "stop" : { - "gtfsId" : "F:Stop_1", - "name" : "Stop_1" + "stop": { + "gtfsId": "F:Stop_1", + "name": "Stop_1" }, - "headsign" : "Stop headsign at stop 20", - "scheduledArrival" : 41700, - "scheduledDeparture" : 41700, - "stopPosition" : 20, - "stopPositionInPattern" : 1, - "realtimeState" : "CANCELED", - "pickupType" : null, - "dropoffType" : null + "headsign": "Stop headsign at stop 20", + "scheduledArrival": 41700, + "scheduledDeparture": 41700, + "stopPosition": 20, + "stopPositionInPattern": 1, + "realtimeState": "CANCELED", + "pickupType": null, + "dropoffType": null }, { - "stop" : { - "gtfsId" : "F:Stop_2", - "name" : "Stop_2" + "stop": { + "gtfsId": "F:Stop_2", + "name": "Stop_2" }, - "headsign" : "Stop headsign at stop 30", - "scheduledArrival" : 42000, - "scheduledDeparture" : 42000, - "stopPosition" : 30, - "stopPositionInPattern" : 2, - "realtimeState" : "CANCELED", - "pickupType" : null, - "dropoffType" : null + "headsign": "Stop headsign at stop 30", + "scheduledArrival": 42000, + "scheduledDeparture": 42000, + "stopPosition": 30, + "stopPositionInPattern": 2, + "realtimeState": "CANCELED", + "pickupType": null, + "dropoffType": null } ], - "occupancy" : { - "occupancyStatus" : "NO_DATA_AVAILABLE" + "occupancy": { + "occupancyStatus": "NO_DATA_AVAILABLE" } } ], - "vehiclePositions" : [ + "vehiclePositions": [ { - "vehicleId" : "F:vehicle-1", - "label" : null, - "lat" : null, - "lon" : null, - "stopRelationship" : null, - "speed" : null, - "heading" : null, - "lastUpdated" : 31556889864403199, - "trip" : { - "gtfsId" : "F:123" + "vehicleId": "F:vehicle-1", + "label": null, + "lat": null, + "lon": null, + "stopRelationship": null, + "speed": null, + "heading": null, + "lastUpdated": 31556889864403199, + "trip": { + "gtfsId": "F:123" } }, { - "vehicleId" : "F:vehicle-2", - "label" : "vehicle2", - "lat" : 60.0, - "lon" : 80.0, - "stopRelationship" : { - "status" : "IN_TRANSIT_TO", - "stop" : { - "gtfsId" : "F:Stop_0" + "vehicleId": "F:vehicle-2", + "label": "vehicle2", + "lat": 60.0, + "lon": 80.0, + "stopRelationship": { + "status": "IN_TRANSIT_TO", + "stop": { + "gtfsId": "F:Stop_0" } }, - "speed" : 10.2, - "heading" : 80.0, - "lastUpdated" : -31557014167219200, - "trip" : { - "gtfsId" : "F:123" + "speed": 10.2, + "heading": 80.0, + "lastUpdated": -31557014167219200, + "trip": { + "gtfsId": "F:123" } } ] } ] } -} \ No newline at end of file +} From 769e3805f3e77b44c222cd3960ef0e820b9dfb3b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Nov 2024 14:08:43 +0100 Subject: [PATCH 069/162] Add background tiles # Conflicts: # application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json --- .../apis/vectortiles/DebugStyleSpec.java | 42 +- .../apis/vectortiles/style.json | 1227 ++++++++++++----- 2 files changed, 888 insertions(+), 381 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index f27d252f250..971884d00a2 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.opentripplanner.apis.vectortiles.model.StyleBuilder; import org.opentripplanner.apis.vectortiles.model.StyleSpec; @@ -37,13 +36,24 @@ */ public class DebugStyleSpec { - private static final TileSource BACKGROUND_SOURCE = new RasterSource( - "background", + private static final TileSource OSM_BACKGROUND = new RasterSource( + "background-osm", List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"), 19, 256, "© OpenStreetMap Contributors" ); + private static final TileSource POSITRON_BACKGROUND = new RasterSource( + "background-carto", + List.of("https://a.basemaps.cartocdn.com/{z}/{x}/{y}"), + 19, + 256, + "© OpenStreetMap, © CARTO" + ); + private static final List BACKGROUND_LAYERS = List.of( + OSM_BACKGROUND, + POSITRON_BACKGROUND + ); private static final String MAGENTA = "#f21d52"; private static final String BRIGHT_GREEN = "#22DD9E"; private static final String DARK_GREEN = "#136b04"; @@ -94,17 +104,18 @@ static StyleSpec build( VectorSourceLayer edges, VectorSourceLayer vertices ) { - var vectorSources = Stream + List vectorSources = List .of(regularStops, edges, vertices) - .map(VectorSourceLayer::vectorSource); - var allSources = Stream - .concat(Stream.of(BACKGROUND_SOURCE), vectorSources) - .collect(Collectors.toSet()); + .stream() + .map(VectorSourceLayer::vectorSource) + .map(TileSource.class::cast) + .toList(); + var allSources = ListUtils.combine(BACKGROUND_LAYERS, vectorSources); return new StyleSpec( "OTP Debug Tiles", allSources, ListUtils.combine( - List.of(StyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0)), + backgroundLayers(), wheelchair(edges), noThruTraffic(edges), traversalPermissions(edges), @@ -115,6 +126,19 @@ static StyleSpec build( ); } + private static List backgroundLayers() { + return BACKGROUND_LAYERS + .stream() + .map(layer -> { + var builder = StyleBuilder.ofId(layer.id()).typeRaster().source(layer).minZoom(0); + if(!layer.equals(OSM_BACKGROUND)){ + builder.intiallyHidden(); + } + return builder; + }) + .toList(); + } + private static List stops( VectorSourceLayer regularStops, VectorSourceLayer areaStops, diff --git a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index b08a225fde3..fe7b546400d 100644 --- a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -1,83 +1,163 @@ { - "name": "OTP Debug Tiles", - "sources": { - "background": { - "id": "background", - "tiles": ["https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"], - "maxzoom": 19, - "tileSize": 256, - "attribution": "© OpenStreetMap Contributors", - "type": "raster" + "name" : "OTP Debug Tiles", + "sources" : { + "vectorSource" : { + "id" : "vectorSource", + "url" : "https://example.com", + "type" : "vector" }, - "vectorSource": { - "id": "vectorSource", - "url": "https://example.com", - "type": "vector" + "background-osm" : { + "id" : "background-osm", + "tiles" : [ + "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" + ], + "maxzoom" : 19, + "tileSize" : 256, + "attribution" : "© OpenStreetMap Contributors", + "type" : "raster" + }, + "background-carto" : { + "id" : "background-carto", + "tiles" : [ + "https://{s}.basemaps.cartocdn.com/{z}/{x}/{y}" + ], + "maxzoom" : 19, + "tileSize" : 256, + "attribution" : "© OpenStreetMap, © CARTO", + "type" : "raster" } }, - "layers": [ + "layers" : [ { - "id": "background", - "type": "raster", - "source": "background", - "minzoom": 0, - "metadata": { - "group": "Other" + "id" : "background-osm", + "type" : "raster", + "source" : "background-osm", + "minzoom" : 0, + "metadata" : { + "group" : "Other" } }, { - "id": "wheelchair-accessible", - "source": "vectorSource", - "source-layer": "edges", - "type": "line", - "minzoom": 6, - "maxzoom": 23, - "paint": { - "line-color": "#136b04", - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] - }, - "filter": ["==", "wheelchairAccessible", true], - "layout": { - "line-cap": "round", - "visibility": "none" - }, - "metadata": { - "group": "Wheelchair accessibility" + "id" : "background-carto", + "type" : "raster", + "source" : "background-carto", + "minzoom" : 0, + "metadata" : { + "group" : "Other" + } + }, + { + "id" : "wheelchair-accessible", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "line", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#136b04", + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] + }, + "filter" : [ + "==", + "wheelchairAccessible", + true + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Wheelchair accessibility" } }, { - "id": "wheelchair-inaccessible", - "source": "vectorSource", - "source-layer": "edges", - "type": "line", - "minzoom": 6, - "maxzoom": 23, - "paint": { - "line-color": "#fc0f2a", - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] - }, - "filter": ["==", "wheelchairAccessible", false], - "layout": { - "line-cap": "round", - "visibility": "none" - }, - "metadata": { - "group": "Wheelchair accessibility" + "id" : "wheelchair-inaccessible", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "line", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#fc0f2a", + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] + }, + "filter" : [ + "==", + "wheelchairAccessible", + false + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + }, + "metadata" : { + "group" : "Wheelchair accessibility" } }, { - "id": "no-thru-traffic PEDESTRIAN", - "source": "vectorSource", - "source-layer": "edges", - "type": "line", - "minzoom": 13, - "maxzoom": 23, - "paint": { - "line-color": [ + "id" : "no-thru-traffic PEDESTRIAN", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "line", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "line-color" : [ "match", - ["get", "noThruTraffic"], + [ + "get", + "noThruTraffic" + ], "NONE", "#140d0e", "PEDESTRIAN", @@ -96,33 +176,80 @@ "#adb2b0", "#140d0e" ], - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] }, - "filter": [ + "filter" : [ "any", - ["in", "PEDESTRIAN", ["string", ["get", "noThruTraffic"]]], - ["in", "ALL", ["string", ["get", "noThruTraffic"]]] + [ + "in", + "PEDESTRIAN", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ] ], - "layout": { - "line-cap": "butt", - "visibility": "none" + "layout" : { + "line-cap" : "butt", + "visibility" : "none" }, - "metadata": { - "group": "No-thru traffic" + "metadata" : { + "group" : "No-thru traffic" } }, { - "id": "no-thru-traffic BICYCLE", - "source": "vectorSource", - "source-layer": "edges", - "type": "line", - "minzoom": 13, - "maxzoom": 23, - "paint": { - "line-color": [ + "id" : "no-thru-traffic BICYCLE", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "line", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "line-color" : [ "match", - ["get", "noThruTraffic"], + [ + "get", + "noThruTraffic" + ], "NONE", "#140d0e", "PEDESTRIAN", @@ -141,33 +268,80 @@ "#adb2b0", "#140d0e" ], - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] }, - "filter": [ + "filter" : [ "any", - ["in", "BICYCLE", ["string", ["get", "noThruTraffic"]]], - ["in", "ALL", ["string", ["get", "noThruTraffic"]]] + [ + "in", + "BICYCLE", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ] ], - "layout": { - "line-cap": "butt", - "visibility": "none" + "layout" : { + "line-cap" : "butt", + "visibility" : "none" }, - "metadata": { - "group": "No-thru traffic" + "metadata" : { + "group" : "No-thru traffic" } }, { - "id": "no-thru-traffic CAR", - "source": "vectorSource", - "source-layer": "edges", - "type": "line", - "minzoom": 13, - "maxzoom": 23, - "paint": { - "line-color": [ + "id" : "no-thru-traffic CAR", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "line", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "line-color" : [ "match", - ["get", "noThruTraffic"], + [ + "get", + "noThruTraffic" + ], "NONE", "#140d0e", "PEDESTRIAN", @@ -186,36 +360,80 @@ "#adb2b0", "#140d0e" ], - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] }, - "filter": [ + "filter" : [ "any", - ["in", "CAR", ["string", ["get", "noThruTraffic"]]], - ["in", "ALL", ["string", ["get", "noThruTraffic"]]] + [ + "in", + "CAR", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "noThruTraffic" + ] + ] + ] ], - "layout": { - "line-cap": "butt", - "visibility": "none" + "layout" : { + "line-cap" : "butt", + "visibility" : "none" }, - "metadata": { - "group": "No-thru traffic" + "metadata" : { + "group" : "No-thru traffic" } }, { - "id": "no-thru-traffic-text", - "source": "vectorSource", - "source-layer": "edges", - "type": "symbol", - "minzoom": 17, - "maxzoom": 23, - "paint": { - "text-color": "#000", - "text-halo-color": "#fff", - "text-halo-blur": 4, - "text-halo-width": 3 - }, - "filter": [ + "id" : "no-thru-traffic-text", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "symbol", + "minzoom" : 17, + "maxzoom" : 23, + "paint" : { + "text-color" : "#000", + "text-halo-color" : "#fff", + "text-halo-blur" : 4, + "text-halo-width" : 3 + }, + "filter" : [ "in", "class", "StreetEdge", @@ -226,34 +444,54 @@ "TemporaryPartialStreetEdge", "TemporaryFreeEdge" ], - "layout": { - "symbol-placement": "line-center", - "symbol-spacing": 1000, - "text-field": "{noThruTraffic}", - "text-font": ["KlokanTech Noto Sans Regular"], - "text-size": ["interpolate", ["linear"], ["zoom"], 10, 6.0, 24, 12.0], - "text-max-width": 100, - "text-keep-upright": true, - "text-rotation-alignment": "map", - "text-overlap": "never", - "text-offset": [0, 1.0], - "visibility": "none" - }, - "metadata": { - "group": "No-thru traffic" + "layout" : { + "symbol-placement" : "line-center", + "symbol-spacing" : 1000, + "text-field" : "{noThruTraffic}", + "text-font" : [ + "KlokanTech Noto Sans Regular" + ], + "text-size" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 10, + 6.0, + 24, + 12.0 + ], + "text-max-width" : 100, + "text-keep-upright" : true, + "text-rotation-alignment" : "map", + "text-overlap" : "never", + "text-offset" : [ + 0, + 1.0 + ], + "visibility" : "none" + }, + "metadata" : { + "group" : "No-thru traffic" } }, { - "id": "permission PEDESTRIAN", - "source": "vectorSource", - "source-layer": "edges", - "type": "line", - "minzoom": 13, - "maxzoom": 23, - "paint": { - "line-color": [ + "id" : "permission PEDESTRIAN", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "line", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "line-color" : [ "match", - ["get", "permission"], + [ + "get", + "permission" + ], "NONE", "#140d0e", "PEDESTRIAN", @@ -272,33 +510,80 @@ "#adb2b0", "#140d0e" ], - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] }, - "filter": [ + "filter" : [ "any", - ["in", "PEDESTRIAN", ["string", ["get", "permission"]]], - ["in", "ALL", ["string", ["get", "permission"]]] + [ + "in", + "PEDESTRIAN", + [ + "string", + [ + "get", + "permission" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "permission" + ] + ] + ] ], - "layout": { - "line-cap": "butt", - "visibility": "none" + "layout" : { + "line-cap" : "butt", + "visibility" : "none" }, - "metadata": { - "group": "Permissions" + "metadata" : { + "group" : "Permissions" } }, { - "id": "permission BICYCLE", - "source": "vectorSource", - "source-layer": "edges", - "type": "line", - "minzoom": 13, - "maxzoom": 23, - "paint": { - "line-color": [ + "id" : "permission BICYCLE", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "line", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "line-color" : [ "match", - ["get", "permission"], + [ + "get", + "permission" + ], "NONE", "#140d0e", "PEDESTRIAN", @@ -317,33 +602,80 @@ "#adb2b0", "#140d0e" ], - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] }, - "filter": [ + "filter" : [ "any", - ["in", "BICYCLE", ["string", ["get", "permission"]]], - ["in", "ALL", ["string", ["get", "permission"]]] + [ + "in", + "BICYCLE", + [ + "string", + [ + "get", + "permission" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "permission" + ] + ] + ] ], - "layout": { - "line-cap": "butt", - "visibility": "none" + "layout" : { + "line-cap" : "butt", + "visibility" : "none" }, - "metadata": { - "group": "Permissions" + "metadata" : { + "group" : "Permissions" } }, { - "id": "permission CAR", - "source": "vectorSource", - "source-layer": "edges", - "type": "line", - "minzoom": 13, - "maxzoom": 23, - "paint": { - "line-color": [ + "id" : "permission CAR", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "line", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "line-color" : [ "match", - ["get", "permission"], + [ + "get", + "permission" + ], "NONE", "#140d0e", "PEDESTRIAN", @@ -362,36 +694,80 @@ "#adb2b0", "#140d0e" ], - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] }, - "filter": [ + "filter" : [ "any", - ["in", "CAR", ["string", ["get", "permission"]]], - ["in", "ALL", ["string", ["get", "permission"]]] + [ + "in", + "CAR", + [ + "string", + [ + "get", + "permission" + ] + ] + ], + [ + "in", + "ALL", + [ + "string", + [ + "get", + "permission" + ] + ] + ] ], - "layout": { - "line-cap": "butt", - "visibility": "none" + "layout" : { + "line-cap" : "butt", + "visibility" : "none" }, - "metadata": { - "group": "Permissions" + "metadata" : { + "group" : "Permissions" } }, { - "id": "permission-text", - "source": "vectorSource", - "source-layer": "edges", - "type": "symbol", - "minzoom": 17, - "maxzoom": 23, - "paint": { - "text-color": "#000", - "text-halo-color": "#fff", - "text-halo-blur": 4, - "text-halo-width": 3 - }, - "filter": [ + "id" : "permission-text", + "source" : "vectorSource", + "source-layer" : "edges", + "type" : "symbol", + "minzoom" : 17, + "maxzoom" : 23, + "paint" : { + "text-color" : "#000", + "text-halo-color" : "#fff", + "text-halo-blur" : 4, + "text-halo-width" : 3 + }, + "filter" : [ "in", "class", "StreetEdge", @@ -402,36 +778,77 @@ "TemporaryPartialStreetEdge", "TemporaryFreeEdge" ], - "layout": { - "symbol-placement": "line-center", - "symbol-spacing": 1000, - "text-field": "{permission}", - "text-font": ["KlokanTech Noto Sans Regular"], - "text-size": ["interpolate", ["linear"], ["zoom"], 10, 6.0, 24, 12.0], - "text-max-width": 100, - "text-keep-upright": true, - "text-rotation-alignment": "map", - "text-overlap": "never", - "text-offset": [0, 1.0], - "visibility": "none" - }, - "metadata": { - "group": "Permissions" + "layout" : { + "symbol-placement" : "line-center", + "symbol-spacing" : 1000, + "text-field" : "{permission}", + "text-font" : [ + "KlokanTech Noto Sans Regular" + ], + "text-size" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 10, + 6.0, + 24, + 12.0 + ], + "text-max-width" : 100, + "text-keep-upright" : true, + "text-rotation-alignment" : "map", + "text-overlap" : "never", + "text-offset" : [ + 0, + 1.0 + ], + "visibility" : "none" + }, + "metadata" : { + "group" : "Permissions" } }, { - "id": "edge", - "type": "line", - "source": "vectorSource", - "source-layer": "edges", - "minzoom": 6, - "maxzoom": 23, - "paint": { - "line-color": "#f21d52", - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.1, 23, 6.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] - }, - "filter": [ + "id" : "edge", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "line-color" : "#f21d52", + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.1, + 23, + 6.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] + }, + "filter" : [ "in", "class", "StreetEdge", @@ -442,28 +859,28 @@ "TemporaryPartialStreetEdge", "TemporaryFreeEdge" ], - "layout": { - "line-cap": "round", - "visibility": "none" + "layout" : { + "line-cap" : "round", + "visibility" : "none" }, - "metadata": { - "group": "Edges" + "metadata" : { + "group" : "Edges" } }, { - "id": "edge-name", - "type": "symbol", - "source": "vectorSource", - "source-layer": "edges", - "minzoom": 17, - "maxzoom": 23, - "paint": { - "text-color": "#000", - "text-halo-color": "#fff", - "text-halo-blur": 4, - "text-halo-width": 3 - }, - "filter": [ + "id" : "edge-name", + "type" : "symbol", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 17, + "maxzoom" : 23, + "paint" : { + "text-color" : "#000", + "text-halo-color" : "#fff", + "text-halo-blur" : 4, + "text-halo-width" : 3 + }, + "filter" : [ "in", "class", "StreetEdge", @@ -474,35 +891,73 @@ "TemporaryPartialStreetEdge", "TemporaryFreeEdge" ], - "layout": { - "symbol-placement": "line-center", - "symbol-spacing": 1000, - "text-field": "{name}", - "text-font": ["KlokanTech Noto Sans Regular"], - "text-size": ["interpolate", ["linear"], ["zoom"], 10, 6.0, 24, 12.0], - "text-max-width": 100, - "text-keep-upright": true, - "text-rotation-alignment": "map", - "text-overlap": "never", - "visibility": "none" - }, - "metadata": { - "group": "Edges" + "layout" : { + "symbol-placement" : "line-center", + "symbol-spacing" : 1000, + "text-field" : "{name}", + "text-font" : [ + "KlokanTech Noto Sans Regular" + ], + "text-size" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 10, + 6.0, + 24, + 12.0 + ], + "text-max-width" : 100, + "text-keep-upright" : true, + "text-rotation-alignment" : "map", + "text-overlap" : "never", + "visibility" : "none" + }, + "metadata" : { + "group" : "Edges" } }, { - "id": "link", - "type": "line", - "source": "vectorSource", - "source-layer": "edges", - "minzoom": 13, - "maxzoom": 23, - "paint": { - "line-color": "#22DD9E", - "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], - "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] - }, - "filter": [ + "id" : "link", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "line-color" : "#22DD9E", + "line-width" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.2, + 23, + 8.0 + ], + "line-offset" : [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 13, + 0.4, + 23, + 7.0 + ] + }, + "filter" : [ "in", "class", "StreetTransitStopLink", @@ -512,153 +967,181 @@ "StreetVehicleParkingLink", "StreetStationCentroidLink" ], - "layout": { - "line-cap": "round", - "visibility": "none" + "layout" : { + "line-cap" : "round", + "visibility" : "none" }, - "metadata": { - "group": "Edges" + "metadata" : { + "group" : "Edges" } }, { - "id": "vertex", - "type": "circle", - "source": "vectorSource", - "source-layer": "vertices", - "minzoom": 15, - "maxzoom": 23, - "paint": { - "circle-stroke-color": "#140d0e", - "circle-stroke-width": [ + "id" : "vertex", + "type" : "circle", + "source" : "vectorSource", + "source-layer" : "vertices", + "minzoom" : 15, + "maxzoom" : 23, + "paint" : { + "circle-stroke-color" : "#140d0e", + "circle-stroke-width" : [ "interpolate", - ["linear"], - ["zoom"], + [ + "linear" + ], + [ + "zoom" + ], 15, 0.2, 23, 3.0 ], - "circle-radius": [ + "circle-radius" : [ "interpolate", - ["linear"], - ["zoom"], + [ + "linear" + ], + [ + "zoom" + ], 15, 1.0, 23, 7.0 ], - "circle-color": "#BC55F2" + "circle-color" : "#BC55F2" }, - "layout": { - "visibility": "none" + "layout" : { + "visibility" : "none" }, - "metadata": { - "group": "Vertices" + "metadata" : { + "group" : "Vertices" } }, { - "id": "parking-vertex", - "type": "circle", - "source": "vectorSource", - "source-layer": "vertices", - "minzoom": 13, - "maxzoom": 23, - "paint": { - "circle-stroke-color": "#140d0e", - "circle-stroke-width": [ + "id" : "parking-vertex", + "type" : "circle", + "source" : "vectorSource", + "source-layer" : "vertices", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "circle-stroke-color" : "#140d0e", + "circle-stroke-width" : [ "interpolate", - ["linear"], - ["zoom"], + [ + "linear" + ], + [ + "zoom" + ], 15, 0.2, 23, 3.0 ], - "circle-radius": [ + "circle-radius" : [ "interpolate", - ["linear"], - ["zoom"], + [ + "linear" + ], + [ + "zoom" + ], 13, 1.4, 23, 10.0 ], - "circle-color": "#136b04" + "circle-color" : "#136b04" }, - "filter": ["in", "class", "VehicleParkingEntranceVertex"], - "layout": { - "visibility": "none" + "filter" : [ + "in", + "class", + "VehicleParkingEntranceVertex" + ], + "layout" : { + "visibility" : "none" }, - "metadata": { - "group": "Vertices" + "metadata" : { + "group" : "Vertices" } }, { - "id": "area-stop", - "type": "fill", - "source": "vectorSource", - "source-layer": "stops", - "minzoom": 6, - "maxzoom": 23, - "paint": { - "fill-color": "#22DD9E", - "fill-opacity": 0.5, - "fill-outline-color": "#140d0e" - }, - "metadata": { - "group": "Stops" + "id" : "area-stop", + "type" : "fill", + "source" : "vectorSource", + "source-layer" : "stops", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "fill-color" : "#22DD9E", + "fill-opacity" : 0.5, + "fill-outline-color" : "#140d0e" + }, + "metadata" : { + "group" : "Stops" } }, { - "id": "group-stop", - "type": "fill", - "source": "vectorSource", - "source-layer": "stops", - "minzoom": 6, - "maxzoom": 23, - "paint": { - "fill-color": "#22DD9E", - "fill-opacity": 0.5, - "fill-outline-color": "#140d0e" - }, - "metadata": { - "group": "Stops" + "id" : "group-stop", + "type" : "fill", + "source" : "vectorSource", + "source-layer" : "stops", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "fill-color" : "#22DD9E", + "fill-opacity" : 0.5, + "fill-outline-color" : "#140d0e" + }, + "metadata" : { + "group" : "Stops" } }, { - "id": "regular-stop", - "type": "circle", - "source": "vectorSource", - "source-layer": "stops", - "minzoom": 10, - "maxzoom": 23, - "paint": { - "circle-stroke-color": "#140d0e", - "circle-stroke-width": [ + "id" : "regular-stop", + "type" : "circle", + "source" : "vectorSource", + "source-layer" : "stops", + "minzoom" : 10, + "maxzoom" : 23, + "paint" : { + "circle-stroke-color" : "#140d0e", + "circle-stroke-width" : [ "interpolate", - ["linear"], - ["zoom"], + [ + "linear" + ], + [ + "zoom" + ], 11, 0.5, 23, 5.0 ], - "circle-radius": [ + "circle-radius" : [ "interpolate", - ["linear"], - ["zoom"], + [ + "linear" + ], + [ + "zoom" + ], 11, 0.5, 23, 10.0 ], - "circle-color": "#fcf9fa" + "circle-color" : "#fcf9fa" }, - "metadata": { - "group": "Stops" + "metadata" : { + "group" : "Stops" } } ], - "version": 8, - "glyphs": "https://cdn.jsdelivr.net/gh/klokantech/klokantech-gl-fonts@master/{fontstack}/{range}.pbf" -} + "version" : 8, + "glyphs" : "https://cdn.jsdelivr.net/gh/klokantech/klokantech-gl-fonts@master/{fontstack}/{range}.pbf" +} \ No newline at end of file From 77fdc5e66dfc5ccf8ad9a7cfc4b99f1cbc540e56 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Nov 2024 14:44:06 +0100 Subject: [PATCH 070/162] Add UI elements for selecting background map --- .../apis/vectortiles/DebugStyleSpec.java | 2 +- .../src/components/MapView/LayerControl.tsx | 109 ++++++++++++------ 2 files changed, 76 insertions(+), 35 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 971884d00a2..d29e6fdf15b 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -45,7 +45,7 @@ public class DebugStyleSpec { ); private static final TileSource POSITRON_BACKGROUND = new RasterSource( "background-carto", - List.of("https://a.basemaps.cartocdn.com/{z}/{x}/{y}"), + List.of("https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}${r}.png"), 19, 256, "© OpenStreetMap, © CARTO" diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index 61dd48e4a34..d5f9dbb6424 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -26,49 +26,90 @@ class LayerControl implements IControl { this.container.className = 'maplibregl-ctrl maplibregl-ctrl-group layer-select'; map.on('load', () => { - // clean on + // clean on rerender while (this.container.firstChild) { this.container.removeChild(this.container.firstChild); } - const title = document.createElement('h4'); - title.textContent = 'Debug layers'; - this.container.appendChild(title); - - const groups: Record = {}; - map - .getLayersOrder() - .map((l) => map.getLayer(l)) - .filter((layer) => !!layer) - .filter((layer) => this.layerInteractive(layer)) - .reverse() - .forEach((layer) => { - if (layer) { - const meta: { group: string } = layer.metadata as { group: string }; - - let groupName: string = 'Misc'; - if (meta.group) { - groupName = meta.group; - } - - const layerDiv = this.buildLayerDiv(layer as TypedStyleLayer, map); - - if (groups[groupName]) { - groups[groupName]?.appendChild(layerDiv); - } else { - const groupDiv = this.buildGroupDiv(groupName, layerDiv); - groups[groupName] = groupDiv; - this.container.appendChild(groupDiv); - } - } - }); - // initialize clickable layers (initially stops) - this.updateInteractiveLayerIds(map); + this.buildBackgroundLayers(map); + this.buildDebugLayers(map); }); return this.container; } + private buildBackgroundLayers(map: Map) { + const title = document.createElement('h4'); + title.textContent = 'Background'; + this.container.appendChild(title); + + const select = document.createElement('select'); + this.container.appendChild(select); + + let rasterLayers = map + .getLayersOrder() + .map((l) => map.getLayer(l)) + .filter((layer) => !!layer) + .filter((layer) => layer?.type == 'raster'); + + rasterLayers.forEach((layer) => { + if (layer) { + const option = document.createElement('option'); + option.textContent = layer.id; + option.id = layer.id; + + select.appendChild(option); + } + }); + select.onchange = (ev) => { + const layerId = select.value; + const layer = map.getLayer(layerId); + if (layer) { + rasterLayers.forEach((l) => { + map.setLayoutProperty(l?.id, 'visibility', 'none'); + }); + + map.setLayoutProperty(layer.id, 'visibility', 'visible'); + } + }; + } + + private buildDebugLayers(map: Map) { + const title = document.createElement('h4'); + title.textContent = 'Debug layers'; + this.container.appendChild(title); + + const groups: Record = {}; + map + .getLayersOrder() + .map((l) => map.getLayer(l)) + .filter((layer) => !!layer) + .filter((layer) => this.layerInteractive(layer)) + .reverse() + .forEach((layer) => { + if (layer) { + const meta: { group: string } = layer.metadata as { group: string }; + + let groupName: string = 'Misc'; + if (meta.group) { + groupName = meta.group; + } + + const layerDiv = this.buildLayerDiv(layer as TypedStyleLayer, map); + + if (groups[groupName]) { + groups[groupName]?.appendChild(layerDiv); + } else { + const groupDiv = this.buildGroupDiv(groupName, layerDiv); + groups[groupName] = groupDiv; + this.container.appendChild(groupDiv); + } + } + }); + // initialize clickable layers (initially stops) + this.updateInteractiveLayerIds(map); + } + private updateInteractiveLayerIds(map: Map) { const visibleInteractiveLayerIds = map .getLayersOrder() From c0b4243be4ef804b02020b06465dfab0593b0318 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 1 Dec 2024 22:23:21 +0100 Subject: [PATCH 071/162] Add fixed TriMet raster tile --- .../apis/vectortiles/DebugStyleSpec.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index d29e6fdf15b..c34a421bf5b 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -44,15 +44,24 @@ public class DebugStyleSpec { "© OpenStreetMap Contributors" ); private static final TileSource POSITRON_BACKGROUND = new RasterSource( - "background-carto", + "background-positron", List.of("https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}${r}.png"), 19, 256, "© OpenStreetMap, © CARTO" ); + private static final TileSource TRIMET_BACKGROUND = new RasterSource( + "background-trimet", + List.of("https://maps.trimet.org/wms/reflect?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.0&request=GetMap&srs=EPSG:3857&width=256&height=256&layers=aerials"), + 19, + 256, + "TriMet" + ); + private static final List BACKGROUND_LAYERS = List.of( OSM_BACKGROUND, - POSITRON_BACKGROUND + POSITRON_BACKGROUND, + TRIMET_BACKGROUND ); private static final String MAGENTA = "#f21d52"; private static final String BRIGHT_GREEN = "#22DD9E"; From 6ae498d6ac41ad429e9b0ecb660091fabf192cc4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 1 Dec 2024 23:13:21 +0100 Subject: [PATCH 072/162] Add configuration for debug ui --- .../apis/vectortiles/DebugStyleSpec.java | 41 ++++--- .../GraphInspectorVectorTileResource.java | 3 +- .../framework/application/OtpFileNames.java | 6 +- .../api/OtpServerRequestContext.java | 3 + .../standalone/config/ConfigModel.java | 24 ++++- .../standalone/config/DebugUiConfig.java | 100 ++++++++++++++++++ .../standalone/config/OtpConfigLoader.java | 9 ++ .../config/configure/ConfigModule.java | 6 ++ .../debuguiconfig/BackgroundTileLayer.java | 12 +++ .../configure/ConstructApplication.java | 5 + .../configure/ConstructApplicationModule.java | 5 +- .../server/DefaultServerRequestContext.java | 17 ++- .../opentripplanner/TestServerContext.java | 4 +- .../mapping/TripRequestMapperTest.java | 4 +- .../apis/vectortiles/DebugStyleSpecTest.java | 3 +- .../transit/speed_test/SpeedTest.java | 4 +- 16 files changed, 215 insertions(+), 31 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java create mode 100644 application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index c34a421bf5b..b66aa4fcc2b 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -13,6 +13,7 @@ import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber; import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber.ZoomStop; import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; +import org.opentripplanner.standalone.config.debuguiconfig.BackgroundTileLayer; import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; @@ -50,18 +51,10 @@ public class DebugStyleSpec { 256, "© OpenStreetMap, © CARTO" ); - private static final TileSource TRIMET_BACKGROUND = new RasterSource( - "background-trimet", - List.of("https://maps.trimet.org/wms/reflect?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.0&request=GetMap&srs=EPSG:3857&width=256&height=256&layers=aerials"), - 19, - 256, - "TriMet" - ); private static final List BACKGROUND_LAYERS = List.of( OSM_BACKGROUND, - POSITRON_BACKGROUND, - TRIMET_BACKGROUND + POSITRON_BACKGROUND ); private static final String MAGENTA = "#f21d52"; private static final String BRIGHT_GREEN = "#22DD9E"; @@ -111,20 +104,33 @@ static StyleSpec build( VectorSourceLayer areaStops, VectorSourceLayer groupStops, VectorSourceLayer edges, - VectorSourceLayer vertices + VectorSourceLayer vertices, + List extraLayers ) { - List vectorSources = List + List vectorSources = Stream .of(regularStops, edges, vertices) - .stream() .map(VectorSourceLayer::vectorSource) .map(TileSource.class::cast) .toList(); - var allSources = ListUtils.combine(BACKGROUND_LAYERS, vectorSources); + + List extraRasterSources = extraLayers + .stream() + .map(l -> + (TileSource) new RasterSource( + l.name(), + List.of(l.templateUrl()), + 19, + l.tileSize(), + l.attribution() + ) + ) + .toList(); + var allSources = ListUtils.combine(BACKGROUND_LAYERS, extraRasterSources, vectorSources); return new StyleSpec( "OTP Debug Tiles", allSources, ListUtils.combine( - backgroundLayers(), + backgroundLayers(extraRasterSources), wheelchair(edges), noThruTraffic(edges), traversalPermissions(edges), @@ -135,12 +141,13 @@ static StyleSpec build( ); } - private static List backgroundLayers() { - return BACKGROUND_LAYERS + private static List backgroundLayers(List extraLayers) { + return ListUtils + .combine(BACKGROUND_LAYERS, extraLayers) .stream() .map(layer -> { var builder = StyleBuilder.ofId(layer.id()).typeRaster().source(layer).minZoom(0); - if(!layer.equals(OSM_BACKGROUND)){ + if (!layer.equals(OSM_BACKGROUND)) { builder.intiallyHidden(); } return builder; diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index d19c25a1f47..0576e91f312 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -147,7 +147,8 @@ public StyleSpec getTileJson(@Context UriInfo uri, @Context HttpHeaders headers) AREA_STOPS.toVectorSourceLayer(stopsSource), GROUP_STOPS.toVectorSourceLayer(stopsSource), EDGES.toVectorSourceLayer(streetSource), - VERTICES.toVectorSourceLayer(streetSource) + VERTICES.toVectorSourceLayer(streetSource), + serverContext.debugUiConfig().additionalBackgroundLayers() ); } diff --git a/application/src/main/java/org/opentripplanner/framework/application/OtpFileNames.java b/application/src/main/java/org/opentripplanner/framework/application/OtpFileNames.java index 7b6852cb281..6c3a5b6a007 100644 --- a/application/src/main/java/org/opentripplanner/framework/application/OtpFileNames.java +++ b/application/src/main/java/org/opentripplanner/framework/application/OtpFileNames.java @@ -9,16 +9,18 @@ public class OtpFileNames { public static final String OTP_CONFIG_FILENAME = "otp-config.json"; public static final String BUILD_CONFIG_FILENAME = "build-config.json"; public static final String ROUTER_CONFIG_FILENAME = "router-config.json"; + public static final String DEBUG_UI_CONFIG_FILENAME = "debug-ui-config.json"; /** * Check if a file is a config file using the configuration file name. This method returns {@code - * true} if the file match {@code (otp|build|router)-config.json}. + * true} if the file match {@code (otp|build|router|debug-ui)-config.json}. */ public static boolean isConfigFile(String filename) { return ( OTP_CONFIG_FILENAME.equals(filename) || BUILD_CONFIG_FILENAME.equals(filename) || - ROUTER_CONFIG_FILENAME.equals(filename) + ROUTER_CONFIG_FILENAME.equals(filename) || + DEBUG_UI_CONFIG_FILENAME.equals(filename) ); } } diff --git a/application/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java b/application/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java index b5b39ddee18..f088a3de60e 100644 --- a/application/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java +++ b/application/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java @@ -26,6 +26,7 @@ import org.opentripplanner.service.vehicleparking.VehicleParkingService; import org.opentripplanner.service.vehiclerental.VehicleRentalService; import org.opentripplanner.service.worldenvelope.WorldEnvelopeService; +import org.opentripplanner.standalone.config.DebugUiConfig; import org.opentripplanner.standalone.config.routerconfig.VectorTileConfig; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.search.state.State; @@ -127,6 +128,8 @@ default GraphFinder graphFinder() { VectorTileConfig vectorTileConfig(); + DebugUiConfig debugUiConfig(); + /* Sandbox modules */ @Nullable diff --git a/application/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java b/application/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java index c82dd33ddbf..390333f1374 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java @@ -49,16 +49,29 @@ public class ConfigModel { */ private RouterConfig routerConfig; - public ConfigModel(OtpConfig otpConfig, BuildConfig buildConfig, RouterConfig routerConfig) { + private final DebugUiConfig debugUiConfig; + + public ConfigModel( + OtpConfig otpConfig, + BuildConfig buildConfig, + RouterConfig routerConfig, + DebugUiConfig debugUiConfig + ) { this.otpConfig = otpConfig; this.buildConfig = buildConfig; this.routerConfig = routerConfig; + this.debugUiConfig = debugUiConfig; initializeOtpFeatures(otpConfig); } public ConfigModel(OtpConfigLoader loader) { - this(loader.loadOtpConfig(), loader.loadBuildConfig(), loader.loadRouterConfig()); + this( + loader.loadOtpConfig(), + loader.loadBuildConfig(), + loader.loadRouterConfig(), + loader.loadDebugUiConfig() + ); } public void updateConfigFromSerializedGraph(BuildConfig buildConfig, RouterConfig routerConfig) { @@ -102,6 +115,10 @@ public RouterConfig routerConfig() { return routerConfig; } + public DebugUiConfig debugUiConfig() { + return debugUiConfig; + } + public static void initializeOtpFeatures(OtpConfig otpConfig) { OTPFeature.enableFeatures(otpConfig.otpFeatures); OTPFeature.logFeatureSetup(); @@ -117,7 +134,8 @@ public void abortOnUnknownParameters() { ( otpConfig.hasUnknownParameters() || buildConfig.hasUnknownParameters() || - routerConfig.hasUnknownParameters() + routerConfig.hasUnknownParameters() || + debugUiConfig.hasUnknownParameters() ) ) { throw new OtpAppException( diff --git a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java new file mode 100644 index 00000000000..c32c4dce941 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java @@ -0,0 +1,100 @@ +package org.opentripplanner.standalone.config; + +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_0; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.MissingNode; +import java.io.Serializable; +import java.util.List; +import org.opentripplanner.standalone.config.debuguiconfig.BackgroundTileLayer; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is an object representation of the 'debug-ui-config.json'. + */ +public class DebugUiConfig implements Serializable { + + private static final Logger LOG = LoggerFactory.getLogger(DebugUiConfig.class); + + public static final DebugUiConfig DEFAULT = new DebugUiConfig( + MissingNode.getInstance(), + "DEFAULT", + false + ); + + /** + * The node adaptor kept for reference and (de)serialization. + */ + private final NodeAdapter root; + private final List additionalBackgroundLayers; + + public DebugUiConfig(JsonNode node, String source, boolean logUnusedParams) { + this(new NodeAdapter(node, source), logUnusedParams); + } + + /** protected to give unit-test access */ + DebugUiConfig(NodeAdapter root, boolean logUnusedParams) { + this.root = root; + + this.additionalBackgroundLayers = + root + .of("additionalBackgroundLayers") + .asObjects( + List.of(), + node -> + new BackgroundTileLayer( + node + .of("name") + .since(V2_7) + .summary( + "Used in the url to fetch tiles, and as the layer name in the vector tiles." + ) + .asString(), + node + .of("templateUrl") + .since(V2_7) + .summary("Maximum zoom levels the layer is active for.") + .asString(), + node + .of("minZoom") + .since(V2_0) + .summary("Minimum zoom levels the layer is active for.") + .asInt(256), + node + .of("minZoom") + .since(V2_7) + .summary("Minimum zoom levels the layer is active for.") + .asString("© OpenTripPlanner") + ) + ); + + if (logUnusedParams && LOG.isWarnEnabled()) { + root.logAllWarnings(LOG::warn); + } + } + + public NodeAdapter asNodeAdapter() { + return root; + } + + public List additionalBackgroundLayers() { + return additionalBackgroundLayers; + } + + /** + * If {@code true} the config is loaded from file, in not the DEFAULT config is used. + */ + public boolean isDefault() { + return root.isEmpty(); + } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return root.hasUnknownParameters(); + } +} diff --git a/application/src/main/java/org/opentripplanner/standalone/config/OtpConfigLoader.java b/application/src/main/java/org/opentripplanner/standalone/config/OtpConfigLoader.java index d5c452b3da7..9e24e56ca12 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/OtpConfigLoader.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/OtpConfigLoader.java @@ -1,6 +1,7 @@ package org.opentripplanner.standalone.config; import static org.opentripplanner.framework.application.OtpFileNames.BUILD_CONFIG_FILENAME; +import static org.opentripplanner.framework.application.OtpFileNames.DEBUG_UI_CONFIG_FILENAME; import static org.opentripplanner.framework.application.OtpFileNames.OTP_CONFIG_FILENAME; import static org.opentripplanner.framework.application.OtpFileNames.ROUTER_CONFIG_FILENAME; @@ -105,6 +106,14 @@ public RouterConfig loadRouterConfig() { return new RouterConfig(node, ROUTER_CONFIG_FILENAME, true); } + public DebugUiConfig loadDebugUiConfig() { + JsonNode node = loadFromFile(DEBUG_UI_CONFIG_FILENAME); + if (node.isMissingNode()) { + return DebugUiConfig.DEFAULT; + } + return new DebugUiConfig(node, DEBUG_UI_CONFIG_FILENAME, true); + } + private static void logConfigVersion(String configVersion, String filename) { if (configVersion != null) { LOG.info("{} config-version is {}.", filename, configVersion); diff --git a/application/src/main/java/org/opentripplanner/standalone/config/configure/ConfigModule.java b/application/src/main/java/org/opentripplanner/standalone/config/configure/ConfigModule.java index 4f75f3984d5..9a4de91f2ab 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/configure/ConfigModule.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/configure/ConfigModule.java @@ -8,6 +8,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripSchedule; import org.opentripplanner.standalone.config.BuildConfig; import org.opentripplanner.standalone.config.ConfigModel; +import org.opentripplanner.standalone.config.DebugUiConfig; import org.opentripplanner.standalone.config.OtpConfig; import org.opentripplanner.standalone.config.RouterConfig; import org.opentripplanner.standalone.config.routerconfig.RaptorEnvironmentFactory; @@ -34,6 +35,11 @@ static RouterConfig provideRouterConfig(ConfigModel model) { return model.routerConfig(); } + @Provides + static DebugUiConfig provideDebugUiConfig(ConfigModel model) { + return model.debugUiConfig(); + } + @Provides @Singleton static RaptorConfig providesRaptorConfig( diff --git a/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java b/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java new file mode 100644 index 00000000000..b8b6a4a5f81 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java @@ -0,0 +1,12 @@ +package org.opentripplanner.standalone.config.debuguiconfig; + +public record BackgroundTileLayer( + String name, + String templateUrl, + int tileSize, + String attribution +) { + public String name() { + return name.toLowerCase().replace("_", "-").replace(" ", "-"); + } +} diff --git a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java index eb3fae5275f..b4edbb36299 100644 --- a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java +++ b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java @@ -27,6 +27,7 @@ import org.opentripplanner.standalone.config.BuildConfig; import org.opentripplanner.standalone.config.CommandLineParameters; import org.opentripplanner.standalone.config.ConfigModel; +import org.opentripplanner.standalone.config.DebugUiConfig; import org.opentripplanner.standalone.config.OtpConfig; import org.opentripplanner.standalone.config.RouterConfig; import org.opentripplanner.standalone.server.GrizzlyServer; @@ -300,6 +301,10 @@ public BuildConfig buildConfig() { return factory.config().buildConfig(); } + public DebugUiConfig debugUiConfig() { + return factory.config().debugUiConfig(); + } + public RaptorConfig raptorConfig() { return factory.raptorConfig(); } diff --git a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java index bbdd39c57d5..42bd8ee4d87 100644 --- a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java +++ b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java @@ -20,6 +20,7 @@ import org.opentripplanner.service.vehiclerental.VehicleRentalService; import org.opentripplanner.service.worldenvelope.WorldEnvelopeService; import org.opentripplanner.standalone.api.OtpServerRequestContext; +import org.opentripplanner.standalone.config.DebugUiConfig; import org.opentripplanner.standalone.config.RouterConfig; import org.opentripplanner.standalone.server.DefaultServerRequestContext; import org.opentripplanner.street.service.StreetLimitationParametersService; @@ -32,6 +33,7 @@ public class ConstructApplicationModule { @Provides OtpServerRequestContext providesServerContext( RouterConfig routerConfig, + DebugUiConfig debugUiConfig, RaptorConfig raptorConfig, Graph graph, TransitService transitService, @@ -69,7 +71,8 @@ OtpServerRequestContext providesServerContext( stopConsolidationService, streetLimitationParametersService, traverseVisitor, - luceneIndex + luceneIndex, + debugUiConfig ); } diff --git a/application/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java b/application/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java index 1427dcf1971..450b6986cb5 100644 --- a/application/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java +++ b/application/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java @@ -26,6 +26,7 @@ import org.opentripplanner.service.worldenvelope.WorldEnvelopeService; import org.opentripplanner.standalone.api.HttpRequestScoped; import org.opentripplanner.standalone.api.OtpServerRequestContext; +import org.opentripplanner.standalone.config.DebugUiConfig; import org.opentripplanner.standalone.config.routerconfig.TransitRoutingConfig; import org.opentripplanner.standalone.config.routerconfig.VectorTileConfig; import org.opentripplanner.street.service.StreetLimitationParametersService; @@ -57,6 +58,7 @@ public class DefaultServerRequestContext implements OtpServerRequestContext { private final StopConsolidationService stopConsolidationService; private final StreetLimitationParametersService streetLimitationParametersService; private final LuceneIndex luceneIndex; + private final DebugUiConfig debugUiConfig; private RouteRequest defaultRouteRequestWithTimeSet = null; @@ -83,7 +85,8 @@ private DefaultServerRequestContext( StreetLimitationParametersService streetLimitationParametersService, FlexParameters flexParameters, @Nullable TraverseVisitor traverseVisitor, - @Nullable LuceneIndex luceneIndex + @Nullable LuceneIndex luceneIndex, + DebugUiConfig debugUiConfig ) { this.graph = graph; this.transitService = transitService; @@ -105,6 +108,7 @@ private DefaultServerRequestContext( this.stopConsolidationService = stopConsolidationService; this.streetLimitationParametersService = streetLimitationParametersService; this.luceneIndex = luceneIndex; + this.debugUiConfig = debugUiConfig; } /** @@ -129,7 +133,8 @@ public static DefaultServerRequestContext create( @Nullable StopConsolidationService stopConsolidationService, StreetLimitationParametersService streetLimitationParametersService, @Nullable TraverseVisitor traverseVisitor, - @Nullable LuceneIndex luceneIndex + @Nullable LuceneIndex luceneIndex, + DebugUiConfig debugUiConfig ) { return new DefaultServerRequestContext( graph, @@ -151,7 +156,8 @@ public static DefaultServerRequestContext create( streetLimitationParametersService, flexParameters, traverseVisitor, - luceneIndex + luceneIndex, + debugUiConfig ); } @@ -262,6 +268,11 @@ public VectorTileConfig vectorTileConfig() { return vectorTileConfig; } + @Override + public DebugUiConfig debugUiConfig() { + return debugUiConfig; + } + @Nullable @Override public LuceneIndex lucenceIndex() { diff --git a/application/src/test/java/org/opentripplanner/TestServerContext.java b/application/src/test/java/org/opentripplanner/TestServerContext.java index e20720bd7d8..ca818a64a58 100644 --- a/application/src/test/java/org/opentripplanner/TestServerContext.java +++ b/application/src/test/java/org/opentripplanner/TestServerContext.java @@ -21,6 +21,7 @@ import org.opentripplanner.service.worldenvelope.internal.DefaultWorldEnvelopeService; import org.opentripplanner.service.worldenvelope.model.WorldEnvelope; import org.opentripplanner.standalone.api.OtpServerRequestContext; +import org.opentripplanner.standalone.config.DebugUiConfig; import org.opentripplanner.standalone.config.RouterConfig; import org.opentripplanner.standalone.config.routerconfig.RaptorEnvironmentFactory; import org.opentripplanner.standalone.server.DefaultServerRequestContext; @@ -65,7 +66,8 @@ public static OtpServerRequestContext createServerContext( null, createStreetLimitationParametersService(), null, - null + null, + DebugUiConfig.DEFAULT ); creatTransitLayerForRaptor(timetableRepository, routerConfig.transitTuningConfig()); return context; diff --git a/application/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java b/application/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java index b3c64e0aecb..dc96a094812 100644 --- a/application/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java +++ b/application/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java @@ -52,6 +52,7 @@ import org.opentripplanner.service.vehiclerental.internal.DefaultVehicleRentalService; import org.opentripplanner.service.worldenvelope.internal.DefaultWorldEnvelopeRepository; import org.opentripplanner.service.worldenvelope.internal.DefaultWorldEnvelopeService; +import org.opentripplanner.standalone.config.DebugUiConfig; import org.opentripplanner.standalone.config.RouterConfig; import org.opentripplanner.standalone.server.DefaultServerRequestContext; import org.opentripplanner.street.model.StreetLimitationParameters; @@ -154,7 +155,8 @@ void setup() { null, new DefaultStreetLimitationParametersService(new StreetLimitationParameters()), null, - null + null, + DebugUiConfig.DEFAULT ), null, transitService diff --git a/application/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/application/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index db056050394..f602a6cb532 100644 --- a/application/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/application/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import org.junit.jupiter.api.Test; import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; import org.opentripplanner.apis.vectortiles.model.VectorSourceLayer; @@ -27,7 +28,7 @@ void spec() throws IOException { var groupStops = new VectorSourceLayer(vectorSource, "stops"); var edges = new VectorSourceLayer(vectorSource, "edges"); var vertices = new VectorSourceLayer(vectorSource, "vertices"); - var spec = DebugStyleSpec.build(regularStops, areaStops, groupStops, edges, vertices); + var spec = DebugStyleSpec.build(regularStops, areaStops, groupStops, edges, vertices, List.of()); var json = ObjectMappers.ignoringExtraFields().valueToTree(spec); try { diff --git a/application/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java b/application/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java index c55ddff854f..ca4e85eed84 100644 --- a/application/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java +++ b/application/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java @@ -29,6 +29,7 @@ import org.opentripplanner.standalone.api.OtpServerRequestContext; import org.opentripplanner.standalone.config.BuildConfig; import org.opentripplanner.standalone.config.ConfigModel; +import org.opentripplanner.standalone.config.DebugUiConfig; import org.opentripplanner.standalone.config.OtpConfigLoader; import org.opentripplanner.standalone.config.routerconfig.RaptorEnvironmentFactory; import org.opentripplanner.standalone.config.routerconfig.VectorTileConfig; @@ -130,7 +131,8 @@ public SpeedTest( null, TestServerContext.createStreetLimitationParametersService(), null, - null + null, + DebugUiConfig.DEFAULT ); // Creating transitLayerForRaptor should be integrated into the TimetableRepository, but for now // we do it manually here From a934342baa54370f1f843f65785745d65c3eebea Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Dec 2024 10:09:50 +0100 Subject: [PATCH 073/162] Update tests --- .../apis/vectortiles/DebugStyleSpec.java | 2 +- .../apis/vectortiles/style.json | 27 ++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index b66aa4fcc2b..12632c90efb 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -46,7 +46,7 @@ public class DebugStyleSpec { ); private static final TileSource POSITRON_BACKGROUND = new RasterSource( "background-positron", - List.of("https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}${r}.png"), + List.of("https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png"), 19, 256, "© OpenStreetMap, © CARTO" diff --git a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index fe7b546400d..00cceab1b06 100644 --- a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -1,6 +1,16 @@ { "name" : "OTP Debug Tiles", "sources" : { + "background-positron" : { + "id" : "background-positron", + "tiles" : [ + "https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png" + ], + "maxzoom" : 19, + "tileSize" : 256, + "attribution" : "© OpenStreetMap, © CARTO", + "type" : "raster" + }, "vectorSource" : { "id" : "vectorSource", "url" : "https://example.com", @@ -15,16 +25,6 @@ "tileSize" : 256, "attribution" : "© OpenStreetMap Contributors", "type" : "raster" - }, - "background-carto" : { - "id" : "background-carto", - "tiles" : [ - "https://{s}.basemaps.cartocdn.com/{z}/{x}/{y}" - ], - "maxzoom" : 19, - "tileSize" : 256, - "attribution" : "© OpenStreetMap, © CARTO", - "type" : "raster" } }, "layers" : [ @@ -38,10 +38,13 @@ } }, { - "id" : "background-carto", + "id" : "background-positron", "type" : "raster", - "source" : "background-carto", + "source" : "background-positron", "minzoom" : 0, + "layout" : { + "visibility" : "none" + }, "metadata" : { "group" : "Other" } From 25b4fee19f174cb8a2c5a1765e2986d1422d7d46 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Dec 2024 10:40:24 +0100 Subject: [PATCH 074/162] Fix configuration --- .../opentripplanner/apis/vectortiles/DebugStyleSpec.java | 2 +- .../opentripplanner/standalone/config/DebugUiConfig.java | 7 +++---- .../config/debuguiconfig/BackgroundTileLayer.java | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 12632c90efb..40dce2f6f10 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -117,7 +117,7 @@ static StyleSpec build( .stream() .map(l -> (TileSource) new RasterSource( - l.name(), + l.id(), List.of(l.templateUrl()), 19, l.tileSize(), diff --git a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java index c32c4dce941..085451024b2 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java @@ -1,6 +1,5 @@ package org.opentripplanner.standalone.config; -import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_0; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; import com.fasterxml.jackson.databind.JsonNode; @@ -59,12 +58,12 @@ public DebugUiConfig(JsonNode node, String source, boolean logUnusedParams) { .summary("Maximum zoom levels the layer is active for.") .asString(), node - .of("minZoom") - .since(V2_0) + .of("tileSize") + .since(V2_7) .summary("Minimum zoom levels the layer is active for.") .asInt(256), node - .of("minZoom") + .of("attribution") .since(V2_7) .summary("Minimum zoom levels the layer is active for.") .asString("© OpenTripPlanner") diff --git a/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java b/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java index b8b6a4a5f81..7f8546e1110 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java @@ -6,7 +6,7 @@ public record BackgroundTileLayer( int tileSize, String attribution ) { - public String name() { + public String id() { return name.toLowerCase().replace("_", "-").replace(" ", "-"); } } From 9bd492ab55981929d88cf7dc02a6bf1013cecc5f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Dec 2024 11:01:31 +0100 Subject: [PATCH 075/162] Style background layer picker --- client/src/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/src/style.css b/client/src/style.css index c33f5dff711..f838dce2c7b 100644 --- a/client/src/style.css +++ b/client/src/style.css @@ -181,6 +181,10 @@ margin-left: 6px; } +.maplibregl-ctrl-group.layer-select select { + margin-bottom: 14px; +} + .maplibregl-ctrl-group.layer-select h4 { font-size: 17px; } From 441711bf58efd3c364d529b01ecbd4ed718edbda Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Dec 2024 11:42:19 +0100 Subject: [PATCH 076/162] Add nicer names to UI --- .../apis/vectortiles/DebugStyleSpec.java | 13 +- .../apis/vectortiles/model/StyleBuilder.java | 10 +- .../apis/vectortiles/model/TileSource.java | 20 +- .../debuguiconfig/BackgroundTileLayer.java | 6 +- .../apis/vectortiles/DebugStyleSpecTest.java | 9 +- .../apis/vectortiles/style.json | 1242 ++++++----------- .../src/components/MapView/LayerControl.tsx | 7 +- .../utils/lang/StringUtils.java | 8 + .../utils/lang/StringUtilsTest.java | 6 + 9 files changed, 455 insertions(+), 866 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 40dce2f6f10..0bcfba51e26 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -38,14 +38,14 @@ public class DebugStyleSpec { private static final TileSource OSM_BACKGROUND = new RasterSource( - "background-osm", + "OSM Carto", List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"), 19, 256, "© OpenStreetMap Contributors" ); private static final TileSource POSITRON_BACKGROUND = new RasterSource( - "background-positron", + "Positron", List.of("https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png"), 19, 256, @@ -117,7 +117,7 @@ static StyleSpec build( .stream() .map(l -> (TileSource) new RasterSource( - l.id(), + l.name(), List.of(l.templateUrl()), 19, l.tileSize(), @@ -146,7 +146,12 @@ private static List backgroundLayers(List extraLayers) .combine(BACKGROUND_LAYERS, extraLayers) .stream() .map(layer -> { - var builder = StyleBuilder.ofId(layer.id()).typeRaster().source(layer).minZoom(0); + var builder = StyleBuilder + .ofId(layer.id()) + .displayName(layer.name()) + .typeRaster() + .source(layer) + .minZoom(0); if (!layer.equals(OSM_BACKGROUND)) { builder.intiallyHidden(); } diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index a70acbb8685..4e75f37785d 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -30,7 +30,7 @@ public class StyleBuilder { private final Map layout = new LinkedHashMap<>(); private final Map metadata = new LinkedHashMap<>(); private final Map line = new LinkedHashMap<>(); - private List filter = List.of(); + private List filter = List.of(); public static StyleBuilder ofId(String id) { return new StyleBuilder(id); @@ -120,6 +120,14 @@ public StyleBuilder group(String group) { return this; } + /** + * A nice human-readable name for the layer. + */ + public StyleBuilder displayName(String name) { + metadata.put("name", name); + return this; + } + public StyleBuilder lineText(String name) { layout.put("symbol-placement", "line-center"); layout.put("symbol-spacing", 1000); diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java index 088ce63d10a..b7f0ce5d048 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.List; +import org.opentripplanner.utils.lang.StringUtils; /** * Represent a data source where Maplibre can fetch data for rendering directly in the browser. @@ -12,6 +13,8 @@ public sealed interface TileSource { String id(); + String name(); + /** * Represents a vector tile source which is rendered into a map in the browser. */ @@ -20,17 +23,32 @@ record VectorSource(String id, String url) implements TileSource { public String type() { return "vector"; } + + @Override + public String name() { + return id; + } } /** * Represents a raster-based source for map tiles. These are used mainly for background * map layers with vector data being rendered on top of it. */ - record RasterSource(String id, List tiles, int maxzoom, int tileSize, String attribution) + record RasterSource( + String name, + List tiles, + int maxzoom, + int tileSize, + String attribution + ) implements TileSource { @Override public String type() { return "raster"; } + + public String id() { + return StringUtils.slugify(name); + } } } diff --git a/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java b/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java index 7f8546e1110..121debcb40e 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/debuguiconfig/BackgroundTileLayer.java @@ -5,8 +5,4 @@ public record BackgroundTileLayer( String templateUrl, int tileSize, String attribution -) { - public String id() { - return name.toLowerCase().replace("_", "-").replace(" ", "-"); - } -} +) {} diff --git a/application/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/application/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index f602a6cb532..26d1044047e 100644 --- a/application/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/application/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -28,7 +28,14 @@ void spec() throws IOException { var groupStops = new VectorSourceLayer(vectorSource, "stops"); var edges = new VectorSourceLayer(vectorSource, "edges"); var vertices = new VectorSourceLayer(vectorSource, "vertices"); - var spec = DebugStyleSpec.build(regularStops, areaStops, groupStops, edges, vertices, List.of()); + var spec = DebugStyleSpec.build( + regularStops, + areaStops, + groupStops, + edges, + vertices, + List.of() + ); var json = ObjectMappers.ignoringExtraFields().valueToTree(spec); try { diff --git a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 00cceab1b06..a1a188ca76a 100644 --- a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -1,166 +1,107 @@ { - "name" : "OTP Debug Tiles", - "sources" : { - "background-positron" : { - "id" : "background-positron", - "tiles" : [ + "name": "OTP Debug Tiles", + "sources": { + "positron": { + "name": "Positron", + "tiles": [ "https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png" ], - "maxzoom" : 19, - "tileSize" : 256, - "attribution" : "© OpenStreetMap, © CARTO", - "type" : "raster" + "maxzoom": 19, + "tileSize": 256, + "attribution": "© OpenStreetMap, © CARTO", + "type": "raster" }, - "vectorSource" : { - "id" : "vectorSource", - "url" : "https://example.com", - "type" : "vector" + "vectorSource": { + "id": "vectorSource", + "url": "https://example.com", + "type": "vector" }, - "background-osm" : { - "id" : "background-osm", - "tiles" : [ - "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" - ], - "maxzoom" : 19, - "tileSize" : 256, - "attribution" : "© OpenStreetMap Contributors", - "type" : "raster" + "osm-carto": { + "name": "OSM Carto", + "tiles": ["https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"], + "maxzoom": 19, + "tileSize": 256, + "attribution": "© OpenStreetMap Contributors", + "type": "raster" } }, - "layers" : [ + "layers": [ { - "id" : "background-osm", - "type" : "raster", - "source" : "background-osm", - "minzoom" : 0, - "metadata" : { - "group" : "Other" + "id": "osm-carto", + "type": "raster", + "source": "osm-carto", + "minzoom": 0, + "metadata": { + "group": "Other", + "name": "OSM Carto" } }, { - "id" : "background-positron", - "type" : "raster", - "source" : "background-positron", - "minzoom" : 0, - "layout" : { - "visibility" : "none" - }, - "metadata" : { - "group" : "Other" + "id": "positron", + "type": "raster", + "source": "positron", + "minzoom": 0, + "layout": { + "visibility": "none" + }, + "metadata": { + "group": "Other", + "name": "Positron" } }, { - "id" : "wheelchair-accessible", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "line", - "minzoom" : 6, - "maxzoom" : 23, - "paint" : { - "line-color" : "#136b04", - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.2, - 23, - 8.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] - }, - "filter" : [ - "==", - "wheelchairAccessible", - true - ], - "layout" : { - "line-cap" : "round", - "visibility" : "none" - }, - "metadata" : { - "group" : "Wheelchair accessibility" + "id": "wheelchair-accessible", + "source": "vectorSource", + "source-layer": "edges", + "type": "line", + "minzoom": 6, + "maxzoom": 23, + "paint": { + "line-color": "#136b04", + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + }, + "filter": ["==", "wheelchairAccessible", true], + "layout": { + "line-cap": "round", + "visibility": "none" + }, + "metadata": { + "group": "Wheelchair accessibility" } }, { - "id" : "wheelchair-inaccessible", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "line", - "minzoom" : 6, - "maxzoom" : 23, - "paint" : { - "line-color" : "#fc0f2a", - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.2, - 23, - 8.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] - }, - "filter" : [ - "==", - "wheelchairAccessible", - false - ], - "layout" : { - "line-cap" : "round", - "visibility" : "none" - }, - "metadata" : { - "group" : "Wheelchair accessibility" + "id": "wheelchair-inaccessible", + "source": "vectorSource", + "source-layer": "edges", + "type": "line", + "minzoom": 6, + "maxzoom": 23, + "paint": { + "line-color": "#fc0f2a", + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + }, + "filter": ["==", "wheelchairAccessible", false], + "layout": { + "line-cap": "round", + "visibility": "none" + }, + "metadata": { + "group": "Wheelchair accessibility" } }, { - "id" : "no-thru-traffic PEDESTRIAN", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "line", - "minzoom" : 13, - "maxzoom" : 23, - "paint" : { - "line-color" : [ + "id": "no-thru-traffic PEDESTRIAN", + "source": "vectorSource", + "source-layer": "edges", + "type": "line", + "minzoom": 13, + "maxzoom": 23, + "paint": { + "line-color": [ "match", - [ - "get", - "noThruTraffic" - ], + ["get", "noThruTraffic"], "NONE", "#140d0e", "PEDESTRIAN", @@ -179,80 +120,33 @@ "#adb2b0", "#140d0e" ], - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.2, - 23, - 8.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] }, - "filter" : [ + "filter": [ "any", - [ - "in", - "PEDESTRIAN", - [ - "string", - [ - "get", - "noThruTraffic" - ] - ] - ], - [ - "in", - "ALL", - [ - "string", - [ - "get", - "noThruTraffic" - ] - ] - ] + ["in", "PEDESTRIAN", ["string", ["get", "noThruTraffic"]]], + ["in", "ALL", ["string", ["get", "noThruTraffic"]]] ], - "layout" : { - "line-cap" : "butt", - "visibility" : "none" + "layout": { + "line-cap": "butt", + "visibility": "none" }, - "metadata" : { - "group" : "No-thru traffic" + "metadata": { + "group": "No-thru traffic" } }, { - "id" : "no-thru-traffic BICYCLE", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "line", - "minzoom" : 13, - "maxzoom" : 23, - "paint" : { - "line-color" : [ + "id": "no-thru-traffic BICYCLE", + "source": "vectorSource", + "source-layer": "edges", + "type": "line", + "minzoom": 13, + "maxzoom": 23, + "paint": { + "line-color": [ "match", - [ - "get", - "noThruTraffic" - ], + ["get", "noThruTraffic"], "NONE", "#140d0e", "PEDESTRIAN", @@ -271,80 +165,33 @@ "#adb2b0", "#140d0e" ], - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.2, - 23, - 8.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] }, - "filter" : [ + "filter": [ "any", - [ - "in", - "BICYCLE", - [ - "string", - [ - "get", - "noThruTraffic" - ] - ] - ], - [ - "in", - "ALL", - [ - "string", - [ - "get", - "noThruTraffic" - ] - ] - ] + ["in", "BICYCLE", ["string", ["get", "noThruTraffic"]]], + ["in", "ALL", ["string", ["get", "noThruTraffic"]]] ], - "layout" : { - "line-cap" : "butt", - "visibility" : "none" + "layout": { + "line-cap": "butt", + "visibility": "none" }, - "metadata" : { - "group" : "No-thru traffic" + "metadata": { + "group": "No-thru traffic" } }, { - "id" : "no-thru-traffic CAR", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "line", - "minzoom" : 13, - "maxzoom" : 23, - "paint" : { - "line-color" : [ + "id": "no-thru-traffic CAR", + "source": "vectorSource", + "source-layer": "edges", + "type": "line", + "minzoom": 13, + "maxzoom": 23, + "paint": { + "line-color": [ "match", - [ - "get", - "noThruTraffic" - ], + ["get", "noThruTraffic"], "NONE", "#140d0e", "PEDESTRIAN", @@ -363,80 +210,36 @@ "#adb2b0", "#140d0e" ], - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.2, - 23, - 8.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] }, - "filter" : [ + "filter": [ "any", - [ - "in", - "CAR", - [ - "string", - [ - "get", - "noThruTraffic" - ] - ] - ], - [ - "in", - "ALL", - [ - "string", - [ - "get", - "noThruTraffic" - ] - ] - ] + ["in", "CAR", ["string", ["get", "noThruTraffic"]]], + ["in", "ALL", ["string", ["get", "noThruTraffic"]]] ], - "layout" : { - "line-cap" : "butt", - "visibility" : "none" + "layout": { + "line-cap": "butt", + "visibility": "none" }, - "metadata" : { - "group" : "No-thru traffic" + "metadata": { + "group": "No-thru traffic" } }, { - "id" : "no-thru-traffic-text", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "symbol", - "minzoom" : 17, - "maxzoom" : 23, - "paint" : { - "text-color" : "#000", - "text-halo-color" : "#fff", - "text-halo-blur" : 4, - "text-halo-width" : 3 - }, - "filter" : [ + "id": "no-thru-traffic-text", + "source": "vectorSource", + "source-layer": "edges", + "type": "symbol", + "minzoom": 17, + "maxzoom": 23, + "paint": { + "text-color": "#000", + "text-halo-color": "#fff", + "text-halo-blur": 4, + "text-halo-width": 3 + }, + "filter": [ "in", "class", "StreetEdge", @@ -447,54 +250,34 @@ "TemporaryPartialStreetEdge", "TemporaryFreeEdge" ], - "layout" : { - "symbol-placement" : "line-center", - "symbol-spacing" : 1000, - "text-field" : "{noThruTraffic}", - "text-font" : [ - "KlokanTech Noto Sans Regular" - ], - "text-size" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 10, - 6.0, - 24, - 12.0 - ], - "text-max-width" : 100, - "text-keep-upright" : true, - "text-rotation-alignment" : "map", - "text-overlap" : "never", - "text-offset" : [ - 0, - 1.0 - ], - "visibility" : "none" - }, - "metadata" : { - "group" : "No-thru traffic" + "layout": { + "symbol-placement": "line-center", + "symbol-spacing": 1000, + "text-field": "{noThruTraffic}", + "text-font": ["KlokanTech Noto Sans Regular"], + "text-size": ["interpolate", ["linear"], ["zoom"], 10, 6.0, 24, 12.0], + "text-max-width": 100, + "text-keep-upright": true, + "text-rotation-alignment": "map", + "text-overlap": "never", + "text-offset": [0, 1.0], + "visibility": "none" + }, + "metadata": { + "group": "No-thru traffic" } }, { - "id" : "permission PEDESTRIAN", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "line", - "minzoom" : 13, - "maxzoom" : 23, - "paint" : { - "line-color" : [ + "id": "permission PEDESTRIAN", + "source": "vectorSource", + "source-layer": "edges", + "type": "line", + "minzoom": 13, + "maxzoom": 23, + "paint": { + "line-color": [ "match", - [ - "get", - "permission" - ], + ["get", "permission"], "NONE", "#140d0e", "PEDESTRIAN", @@ -513,80 +296,33 @@ "#adb2b0", "#140d0e" ], - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.2, - 23, - 8.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] }, - "filter" : [ + "filter": [ "any", - [ - "in", - "PEDESTRIAN", - [ - "string", - [ - "get", - "permission" - ] - ] - ], - [ - "in", - "ALL", - [ - "string", - [ - "get", - "permission" - ] - ] - ] + ["in", "PEDESTRIAN", ["string", ["get", "permission"]]], + ["in", "ALL", ["string", ["get", "permission"]]] ], - "layout" : { - "line-cap" : "butt", - "visibility" : "none" + "layout": { + "line-cap": "butt", + "visibility": "none" }, - "metadata" : { - "group" : "Permissions" + "metadata": { + "group": "Permissions" } }, { - "id" : "permission BICYCLE", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "line", - "minzoom" : 13, - "maxzoom" : 23, - "paint" : { - "line-color" : [ + "id": "permission BICYCLE", + "source": "vectorSource", + "source-layer": "edges", + "type": "line", + "minzoom": 13, + "maxzoom": 23, + "paint": { + "line-color": [ "match", - [ - "get", - "permission" - ], + ["get", "permission"], "NONE", "#140d0e", "PEDESTRIAN", @@ -605,80 +341,33 @@ "#adb2b0", "#140d0e" ], - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.2, - 23, - 8.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] }, - "filter" : [ + "filter": [ "any", - [ - "in", - "BICYCLE", - [ - "string", - [ - "get", - "permission" - ] - ] - ], - [ - "in", - "ALL", - [ - "string", - [ - "get", - "permission" - ] - ] - ] + ["in", "BICYCLE", ["string", ["get", "permission"]]], + ["in", "ALL", ["string", ["get", "permission"]]] ], - "layout" : { - "line-cap" : "butt", - "visibility" : "none" + "layout": { + "line-cap": "butt", + "visibility": "none" }, - "metadata" : { - "group" : "Permissions" + "metadata": { + "group": "Permissions" } }, { - "id" : "permission CAR", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "line", - "minzoom" : 13, - "maxzoom" : 23, - "paint" : { - "line-color" : [ + "id": "permission CAR", + "source": "vectorSource", + "source-layer": "edges", + "type": "line", + "minzoom": 13, + "maxzoom": 23, + "paint": { + "line-color": [ "match", - [ - "get", - "permission" - ], + ["get", "permission"], "NONE", "#140d0e", "PEDESTRIAN", @@ -697,80 +386,36 @@ "#adb2b0", "#140d0e" ], - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.2, - 23, - 8.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] }, - "filter" : [ + "filter": [ "any", - [ - "in", - "CAR", - [ - "string", - [ - "get", - "permission" - ] - ] - ], - [ - "in", - "ALL", - [ - "string", - [ - "get", - "permission" - ] - ] - ] + ["in", "CAR", ["string", ["get", "permission"]]], + ["in", "ALL", ["string", ["get", "permission"]]] ], - "layout" : { - "line-cap" : "butt", - "visibility" : "none" + "layout": { + "line-cap": "butt", + "visibility": "none" }, - "metadata" : { - "group" : "Permissions" + "metadata": { + "group": "Permissions" } }, { - "id" : "permission-text", - "source" : "vectorSource", - "source-layer" : "edges", - "type" : "symbol", - "minzoom" : 17, - "maxzoom" : 23, - "paint" : { - "text-color" : "#000", - "text-halo-color" : "#fff", - "text-halo-blur" : 4, - "text-halo-width" : 3 - }, - "filter" : [ + "id": "permission-text", + "source": "vectorSource", + "source-layer": "edges", + "type": "symbol", + "minzoom": 17, + "maxzoom": 23, + "paint": { + "text-color": "#000", + "text-halo-color": "#fff", + "text-halo-blur": 4, + "text-halo-width": 3 + }, + "filter": [ "in", "class", "StreetEdge", @@ -781,77 +426,36 @@ "TemporaryPartialStreetEdge", "TemporaryFreeEdge" ], - "layout" : { - "symbol-placement" : "line-center", - "symbol-spacing" : 1000, - "text-field" : "{permission}", - "text-font" : [ - "KlokanTech Noto Sans Regular" - ], - "text-size" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 10, - 6.0, - 24, - 12.0 - ], - "text-max-width" : 100, - "text-keep-upright" : true, - "text-rotation-alignment" : "map", - "text-overlap" : "never", - "text-offset" : [ - 0, - 1.0 - ], - "visibility" : "none" - }, - "metadata" : { - "group" : "Permissions" + "layout": { + "symbol-placement": "line-center", + "symbol-spacing": 1000, + "text-field": "{permission}", + "text-font": ["KlokanTech Noto Sans Regular"], + "text-size": ["interpolate", ["linear"], ["zoom"], 10, 6.0, 24, 12.0], + "text-max-width": 100, + "text-keep-upright": true, + "text-rotation-alignment": "map", + "text-overlap": "never", + "text-offset": [0, 1.0], + "visibility": "none" + }, + "metadata": { + "group": "Permissions" } }, { - "id" : "edge", - "type" : "line", - "source" : "vectorSource", - "source-layer" : "edges", - "minzoom" : 6, - "maxzoom" : 23, - "paint" : { - "line-color" : "#f21d52", - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.1, - 23, - 6.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] - }, - "filter" : [ + "id": "edge", + "type": "line", + "source": "vectorSource", + "source-layer": "edges", + "minzoom": 6, + "maxzoom": 23, + "paint": { + "line-color": "#f21d52", + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.1, 23, 6.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + }, + "filter": [ "in", "class", "StreetEdge", @@ -862,28 +466,28 @@ "TemporaryPartialStreetEdge", "TemporaryFreeEdge" ], - "layout" : { - "line-cap" : "round", - "visibility" : "none" + "layout": { + "line-cap": "round", + "visibility": "none" }, - "metadata" : { - "group" : "Edges" + "metadata": { + "group": "Edges" } }, { - "id" : "edge-name", - "type" : "symbol", - "source" : "vectorSource", - "source-layer" : "edges", - "minzoom" : 17, - "maxzoom" : 23, - "paint" : { - "text-color" : "#000", - "text-halo-color" : "#fff", - "text-halo-blur" : 4, - "text-halo-width" : 3 - }, - "filter" : [ + "id": "edge-name", + "type": "symbol", + "source": "vectorSource", + "source-layer": "edges", + "minzoom": 17, + "maxzoom": 23, + "paint": { + "text-color": "#000", + "text-halo-color": "#fff", + "text-halo-blur": 4, + "text-halo-width": 3 + }, + "filter": [ "in", "class", "StreetEdge", @@ -894,73 +498,35 @@ "TemporaryPartialStreetEdge", "TemporaryFreeEdge" ], - "layout" : { - "symbol-placement" : "line-center", - "symbol-spacing" : 1000, - "text-field" : "{name}", - "text-font" : [ - "KlokanTech Noto Sans Regular" - ], - "text-size" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 10, - 6.0, - 24, - 12.0 - ], - "text-max-width" : 100, - "text-keep-upright" : true, - "text-rotation-alignment" : "map", - "text-overlap" : "never", - "visibility" : "none" - }, - "metadata" : { - "group" : "Edges" + "layout": { + "symbol-placement": "line-center", + "symbol-spacing": 1000, + "text-field": "{name}", + "text-font": ["KlokanTech Noto Sans Regular"], + "text-size": ["interpolate", ["linear"], ["zoom"], 10, 6.0, 24, 12.0], + "text-max-width": 100, + "text-keep-upright": true, + "text-rotation-alignment": "map", + "text-overlap": "never", + "visibility": "none" + }, + "metadata": { + "group": "Edges" } }, { - "id" : "link", - "type" : "line", - "source" : "vectorSource", - "source-layer" : "edges", - "minzoom" : 13, - "maxzoom" : 23, - "paint" : { - "line-color" : "#22DD9E", - "line-width" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.2, - 23, - 8.0 - ], - "line-offset" : [ - "interpolate", - [ - "linear" - ], - [ - "zoom" - ], - 13, - 0.4, - 23, - 7.0 - ] - }, - "filter" : [ + "id": "link", + "type": "line", + "source": "vectorSource", + "source-layer": "edges", + "minzoom": 13, + "maxzoom": 23, + "paint": { + "line-color": "#22DD9E", + "line-width": ["interpolate", ["linear"], ["zoom"], 13, 0.2, 23, 8.0], + "line-offset": ["interpolate", ["linear"], ["zoom"], 13, 0.4, 23, 7.0] + }, + "filter": [ "in", "class", "StreetTransitStopLink", @@ -970,181 +536,153 @@ "StreetVehicleParkingLink", "StreetStationCentroidLink" ], - "layout" : { - "line-cap" : "round", - "visibility" : "none" + "layout": { + "line-cap": "round", + "visibility": "none" }, - "metadata" : { - "group" : "Edges" + "metadata": { + "group": "Edges" } }, { - "id" : "vertex", - "type" : "circle", - "source" : "vectorSource", - "source-layer" : "vertices", - "minzoom" : 15, - "maxzoom" : 23, - "paint" : { - "circle-stroke-color" : "#140d0e", - "circle-stroke-width" : [ + "id": "vertex", + "type": "circle", + "source": "vectorSource", + "source-layer": "vertices", + "minzoom": 15, + "maxzoom": 23, + "paint": { + "circle-stroke-color": "#140d0e", + "circle-stroke-width": [ "interpolate", - [ - "linear" - ], - [ - "zoom" - ], + ["linear"], + ["zoom"], 15, 0.2, 23, 3.0 ], - "circle-radius" : [ + "circle-radius": [ "interpolate", - [ - "linear" - ], - [ - "zoom" - ], + ["linear"], + ["zoom"], 15, 1.0, 23, 7.0 ], - "circle-color" : "#BC55F2" + "circle-color": "#BC55F2" }, - "layout" : { - "visibility" : "none" + "layout": { + "visibility": "none" }, - "metadata" : { - "group" : "Vertices" + "metadata": { + "group": "Vertices" } }, { - "id" : "parking-vertex", - "type" : "circle", - "source" : "vectorSource", - "source-layer" : "vertices", - "minzoom" : 13, - "maxzoom" : 23, - "paint" : { - "circle-stroke-color" : "#140d0e", - "circle-stroke-width" : [ + "id": "parking-vertex", + "type": "circle", + "source": "vectorSource", + "source-layer": "vertices", + "minzoom": 13, + "maxzoom": 23, + "paint": { + "circle-stroke-color": "#140d0e", + "circle-stroke-width": [ "interpolate", - [ - "linear" - ], - [ - "zoom" - ], + ["linear"], + ["zoom"], 15, 0.2, 23, 3.0 ], - "circle-radius" : [ + "circle-radius": [ "interpolate", - [ - "linear" - ], - [ - "zoom" - ], + ["linear"], + ["zoom"], 13, 1.4, 23, 10.0 ], - "circle-color" : "#136b04" + "circle-color": "#136b04" }, - "filter" : [ - "in", - "class", - "VehicleParkingEntranceVertex" - ], - "layout" : { - "visibility" : "none" + "filter": ["in", "class", "VehicleParkingEntranceVertex"], + "layout": { + "visibility": "none" }, - "metadata" : { - "group" : "Vertices" + "metadata": { + "group": "Vertices" } }, { - "id" : "area-stop", - "type" : "fill", - "source" : "vectorSource", - "source-layer" : "stops", - "minzoom" : 6, - "maxzoom" : 23, - "paint" : { - "fill-color" : "#22DD9E", - "fill-opacity" : 0.5, - "fill-outline-color" : "#140d0e" - }, - "metadata" : { - "group" : "Stops" + "id": "area-stop", + "type": "fill", + "source": "vectorSource", + "source-layer": "stops", + "minzoom": 6, + "maxzoom": 23, + "paint": { + "fill-color": "#22DD9E", + "fill-opacity": 0.5, + "fill-outline-color": "#140d0e" + }, + "metadata": { + "group": "Stops" } }, { - "id" : "group-stop", - "type" : "fill", - "source" : "vectorSource", - "source-layer" : "stops", - "minzoom" : 6, - "maxzoom" : 23, - "paint" : { - "fill-color" : "#22DD9E", - "fill-opacity" : 0.5, - "fill-outline-color" : "#140d0e" - }, - "metadata" : { - "group" : "Stops" + "id": "group-stop", + "type": "fill", + "source": "vectorSource", + "source-layer": "stops", + "minzoom": 6, + "maxzoom": 23, + "paint": { + "fill-color": "#22DD9E", + "fill-opacity": 0.5, + "fill-outline-color": "#140d0e" + }, + "metadata": { + "group": "Stops" } }, { - "id" : "regular-stop", - "type" : "circle", - "source" : "vectorSource", - "source-layer" : "stops", - "minzoom" : 10, - "maxzoom" : 23, - "paint" : { - "circle-stroke-color" : "#140d0e", - "circle-stroke-width" : [ + "id": "regular-stop", + "type": "circle", + "source": "vectorSource", + "source-layer": "stops", + "minzoom": 10, + "maxzoom": 23, + "paint": { + "circle-stroke-color": "#140d0e", + "circle-stroke-width": [ "interpolate", - [ - "linear" - ], - [ - "zoom" - ], + ["linear"], + ["zoom"], 11, 0.5, 23, 5.0 ], - "circle-radius" : [ + "circle-radius": [ "interpolate", - [ - "linear" - ], - [ - "zoom" - ], + ["linear"], + ["zoom"], 11, 0.5, 23, 10.0 ], - "circle-color" : "#fcf9fa" + "circle-color": "#fcf9fa" }, - "metadata" : { - "group" : "Stops" + "metadata": { + "group": "Stops" } } ], - "version" : 8, - "glyphs" : "https://cdn.jsdelivr.net/gh/klokantech/klokantech-gl-fonts@master/{fontstack}/{range}.pbf" -} \ No newline at end of file + "version": 8, + "glyphs": "https://cdn.jsdelivr.net/gh/klokantech/klokantech-gl-fonts@master/{fontstack}/{range}.pbf" +} diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index d5f9dbb6424..e3e87fba7bd 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -55,14 +55,17 @@ class LayerControl implements IControl { rasterLayers.forEach((layer) => { if (layer) { const option = document.createElement('option'); - option.textContent = layer.id; + const meta = layer.metadata as { name: string }; + option.textContent = meta.name; option.id = layer.id; + option.value = layer.id; select.appendChild(option); } }); - select.onchange = (ev) => { + select.onchange = () => { const layerId = select.value; + console.log(select.value); const layer = map.getLayer(layerId); if (layer) { rasterLayers.forEach((l) => { diff --git a/utils/src/main/java/org/opentripplanner/utils/lang/StringUtils.java b/utils/src/main/java/org/opentripplanner/utils/lang/StringUtils.java index 72eb2638c13..c1a2219da72 100644 --- a/utils/src/main/java/org/opentripplanner/utils/lang/StringUtils.java +++ b/utils/src/main/java/org/opentripplanner/utils/lang/StringUtils.java @@ -132,6 +132,14 @@ public static String kebabCase(String input) { return input.toLowerCase().replace('_', '-'); } + /** + * Create a URL-friendly "slug" version of the string, so "Entur Routebanken" becomes + * "entur-routebanken". + */ + public static String slugify(String input) { + return input.toLowerCase().replace('_', '-').replaceAll("\\s+", "-"); + } + /** * Detects unprintable control characters like newlines, tabs and invisible whitespace * like 'ZERO WIDTH SPACE' (U+200B) that don't have an immediate visual representation. diff --git a/utils/src/test/java/org/opentripplanner/utils/lang/StringUtilsTest.java b/utils/src/test/java/org/opentripplanner/utils/lang/StringUtilsTest.java index 7e8f0a6217b..dda9248468e 100644 --- a/utils/src/test/java/org/opentripplanner/utils/lang/StringUtilsTest.java +++ b/utils/src/test/java/org/opentripplanner/utils/lang/StringUtilsTest.java @@ -100,4 +100,10 @@ void containsInvisibleChars(String input) { void noInvisibleChars(String input) { assertFalse(StringUtils.containsInvisibleCharacters(input)); } + + @ParameterizedTest + @ValueSource(strings = { "AAA Bbb", "aAa bbb", "aaa bbb", "aaa bbb", "AAA_BBB" }) + void slugify(String input) { + assertEquals("aaa-bbb", StringUtils.slugify(input)); + } } From 21e183f70d33b6a5f812ec688232667835713cc5 Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Mon, 2 Dec 2024 13:49:13 +0200 Subject: [PATCH 077/162] fix showing duration for escalator edges in debug client --- .../inspector/vector/edge/EdgePropertyMapper.java | 2 +- .../main/java/org/opentripplanner/utils/time/DurationUtils.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java index 13956af99c4..83b677a62ce 100644 --- a/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java +++ b/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -24,7 +24,7 @@ protected Collection map(Edge input) { case StreetEdge e -> mapStreetEdge(e); case EscalatorEdge e -> List.of( kv("distance", e.getDistanceMeters()), - kv("duration", e.getDuration()) + kv("duration", e.getDuration().map(d -> d.toString()).orElse(null)) ); default -> List.of(); }; diff --git a/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java b/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java index 78a6ae16885..d73faecee03 100644 --- a/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java +++ b/utils/src/main/java/org/opentripplanner/utils/time/DurationUtils.java @@ -11,7 +11,6 @@ import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.opentripplanner.utils.lang.StringUtils; /** * This class extend the Java {@link Duration} with utility functionality to parse and convert From 2f0692bc1341618339da98a3778f6b62783a7511 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Dec 2024 14:43:45 +0100 Subject: [PATCH 078/162] Add documentation --- .../apis/vectortiles/DebugStyleSpec.java | 2 +- .../standalone/config/DebugUiConfig.java | 28 +++++--- .../doc/DebugUiConfigurationDocTest.java | 67 +++++++++++++++++++ .../standalone/config/ExampleConfigTest.java | 12 ++++ .../apis/vectortiles/style.json | 2 +- .../standalone/config/debug-ui-config.json | 9 +++ doc/templates/DebugUiConfiguration.md | 23 +++++++ doc/user/DebugUiConfiguration.md | 66 ++++++++++++++++++ mkdocs.yml | 1 + 9 files changed, 198 insertions(+), 12 deletions(-) create mode 100644 application/src/test/java/org/opentripplanner/generate/doc/DebugUiConfigurationDocTest.java create mode 100644 application/src/test/resources/standalone/config/debug-ui-config.json create mode 100644 doc/templates/DebugUiConfiguration.md create mode 100644 doc/user/DebugUiConfiguration.md diff --git a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 0bcfba51e26..7070f8b486e 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -46,7 +46,7 @@ public class DebugStyleSpec { ); private static final TileSource POSITRON_BACKGROUND = new RasterSource( "Positron", - List.of("https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png"), + List.of("https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}{ratio}.png"), 19, 256, "© OpenStreetMap, © CARTO" diff --git a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java index 085451024b2..165bcfe28f4 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java @@ -41,6 +41,15 @@ public DebugUiConfig(JsonNode node, String source, boolean logUnusedParams) { this.additionalBackgroundLayers = root .of("additionalBackgroundLayers") + .since(V2_7) + .summary("Additional background raster map layers.") + .description( + """ + Add additional background layers that will appear in the Debug UI as one of the choices. + + Current only raster tile layers are supported. + """ + ) .asObjects( List.of(), node -> @@ -48,24 +57,23 @@ public DebugUiConfig(JsonNode node, String source, boolean logUnusedParams) { node .of("name") .since(V2_7) - .summary( - "Used in the url to fetch tiles, and as the layer name in the vector tiles." - ) + .summary("Name to appear in the layer selector.") .asString(), node .of("templateUrl") .since(V2_7) - .summary("Maximum zoom levels the layer is active for.") + .summary( + """ + The [Maplibre-compatible template URL](https://maplibre.org/maplibre-native/ios/api/tile-url-templates.html) + for the raster layer, for example `https://examples.com/tiles/{z}/{x}/{y}.png`. + """ + ) .asString(), - node - .of("tileSize") - .since(V2_7) - .summary("Minimum zoom levels the layer is active for.") - .asInt(256), + node.of("tileSize").since(V2_7).summary("Size of the tile in pixels.").asInt(256), node .of("attribution") .since(V2_7) - .summary("Minimum zoom levels the layer is active for.") + .summary("Attribution for the map data.") .asString("© OpenTripPlanner") ) ); diff --git a/application/src/test/java/org/opentripplanner/generate/doc/DebugUiConfigurationDocTest.java b/application/src/test/java/org/opentripplanner/generate/doc/DebugUiConfigurationDocTest.java new file mode 100644 index 00000000000..a5124c0d3e0 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/generate/doc/DebugUiConfigurationDocTest.java @@ -0,0 +1,67 @@ +package org.opentripplanner.generate.doc; + +import static org.opentripplanner.framework.io.FileUtils.assertFileEquals; +import static org.opentripplanner.framework.io.FileUtils.readFile; +import static org.opentripplanner.framework.io.FileUtils.writeFile; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; +import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceJsonExample; +import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersDetails; +import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersTable; +import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; +import static org.opentripplanner.utils.text.MarkdownFormatter.HEADER_3; + +import java.io.File; +import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.application.OtpFileNames; +import org.opentripplanner.generate.doc.framework.GeneratesDocumentation; +import org.opentripplanner.generate.doc.framework.ParameterDetailsList; +import org.opentripplanner.generate.doc.framework.ParameterSummaryTable; +import org.opentripplanner.generate.doc.framework.SkipNodes; +import org.opentripplanner.standalone.config.DebugUiConfig; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; + +@GeneratesDocumentation +public class DebugUiConfigurationDocTest { + + private static final String CONFIG_JSON = OtpFileNames.DEBUG_UI_CONFIG_FILENAME; + private static final File TEMPLATE = new File(TEMPLATE_PATH, "DebugUiConfiguration.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH, "DebugUiConfiguration.md"); + + private static final String CONFIG_PATH = "standalone/config/" + CONFIG_JSON; + + /** + * NOTE! This test updates the {@code doc/user/Configuration.md} document based on the latest + * version of the code. + */ + @Test + public void updateDoc() { + NodeAdapter node = readConfig(); + + // Read and close input file (same as output file) + String doc = readFile(TEMPLATE); + String original = readFile(OUT_FILE); + + doc = replaceParametersTable(doc, getParameterSummaryTable(node)); + doc = replaceParametersDetails(doc, getParameterDetailsTable(node)); + doc = replaceJsonExample(doc, node, CONFIG_JSON); + + writeFile(OUT_FILE, doc); + + assertFileEquals(original, OUT_FILE); + } + + private NodeAdapter readConfig() { + var json = jsonNodeFromResource(CONFIG_PATH); + var conf = new DebugUiConfig(json, CONFIG_PATH, true); + return conf.asNodeAdapter(); + } + + private String getParameterSummaryTable(NodeAdapter node) { + return new ParameterSummaryTable(SkipNodes.of().build()).createTable(node).toMarkdownTable(); + } + + private String getParameterDetailsTable(NodeAdapter node) { + return ParameterDetailsList.listParametersWithDetails(node, SkipNodes.of().build(), HEADER_3); + } +} diff --git a/application/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java b/application/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java index 06b0d8ed592..934da6f923d 100644 --- a/application/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java +++ b/application/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.opentripplanner.framework.application.OtpFileNames.BUILD_CONFIG_FILENAME; +import static org.opentripplanner.framework.application.OtpFileNames.DEBUG_UI_CONFIG_FILENAME; import static org.opentripplanner.framework.application.OtpFileNames.OTP_CONFIG_FILENAME; import static org.opentripplanner.framework.application.OtpFileNames.ROUTER_CONFIG_FILENAME; @@ -60,6 +61,17 @@ void otpConfig(Path filename) { testConfig(filename, nodeAdapter -> new OtpConfig(nodeAdapter, true)); } + @FilePatternSource( + pattern = { + "doc/user/examples/**/" + DEBUG_UI_CONFIG_FILENAME, + "application/src/test/resources/standalone/config/" + DEBUG_UI_CONFIG_FILENAME, + } + ) + @ParameterizedTest(name = "Check validity of {0}") + void debugUiConfig(Path filename) { + testConfig(filename, nodeAdapter -> new DebugUiConfig(nodeAdapter, true)); + } + @FilePatternSource( pattern = { "application/src/test/resources/standalone/config/invalid-config.json" } ) diff --git a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index a1a188ca76a..66858390ab5 100644 --- a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -4,7 +4,7 @@ "positron": { "name": "Positron", "tiles": [ - "https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png" + "https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}{ratio}.png" ], "maxzoom": 19, "tileSize": 256, diff --git a/application/src/test/resources/standalone/config/debug-ui-config.json b/application/src/test/resources/standalone/config/debug-ui-config.json new file mode 100644 index 00000000000..1ae690a4d37 --- /dev/null +++ b/application/src/test/resources/standalone/config/debug-ui-config.json @@ -0,0 +1,9 @@ +{ + "additionalBackgroundLayers": [ + { + "name": "TriMet aerial photos", + "templateUrl": "https://maps.trimet.org/wms/reflect?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.0&request=GetMap&srs=EPSG:3857&width=256&height=256&layers=aerials", + "attribution": "© TriMet" + } + ] +} diff --git a/doc/templates/DebugUiConfiguration.md b/doc/templates/DebugUiConfiguration.md new file mode 100644 index 00000000000..a54b1db8d2a --- /dev/null +++ b/doc/templates/DebugUiConfiguration.md @@ -0,0 +1,23 @@ + + +# Debug UI configuration + +The Debug UI is the standard interface that is bundled with OTP and available by visiting +[`http://localhost:8080`](http://localhost:8080). This page list the configuration options available +by placing a file `debug-ui-config.json` into OTP's working directory. + + + + +## Parameter Details + + + +## Config Example + + diff --git a/doc/user/DebugUiConfiguration.md b/doc/user/DebugUiConfiguration.md new file mode 100644 index 00000000000..5719a606d4a --- /dev/null +++ b/doc/user/DebugUiConfiguration.md @@ -0,0 +1,66 @@ + + +# Debug UI configuration + +The Debug UI is the standard interface that is bundled with OTP and available by visiting +[`http://localhost:8080`](http://localhost:8080). This page list the configuration options available +by placing a file `debug-ui-config.json` into OTP's working directory. + + + + +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|-----------------------------------------------------------|:----------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|-----------------------|:-----:| +| [additionalBackgroundLayers](#additionalBackgroundLayers) | `object[]` | Additional background raster map layers. | *Optional* | | 2.7 | +|       attribution | `string` | Attribution for the map data. | *Optional* | `"© OpenTripPlanner"` | 2.7 | +|       name | `string` | Name to appear in the layer selector. | *Required* | | 2.7 | +|       templateUrl | `string` | The [Maplibre-compatible template URL](https://maplibre.org/maplibre-native/ios/api/tile-url-templates.html) for the raster layer, for example `https://examples.com/tiles/{z}/{x}/{y}.png`. | *Required* | | 2.7 | +|       tileSize | `integer` | Size of the tile in pixels. | *Optional* | `256` | 2.7 | + + + + +## Parameter Details + + + + +

additionalBackgroundLayers

+ +**Since version:** `2.7` ∙ **Type:** `object[]` ∙ **Cardinality:** `Optional` +**Path:** / + +Additional background raster map layers. + +Add additional background layers that will appear in the Debug UI as one of the choices. + +Current only raster tile layers are supported. + + + + + +## Config Example + + + + +```JSON +// debug-ui-config.json +{ + "additionalBackgroundLayers" : [ + { + "name" : "TriMet aerial photos", + "templateUrl" : "https://maps.trimet.org/wms/reflect?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.0&request=GetMap&srs=EPSG:3857&width=256&height=256&layers=aerials", + "attribution" : "© TriMet" + } + ] +} +``` + + diff --git a/mkdocs.yml b/mkdocs.yml index b40f77ff3c0..51245f6c3b8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,6 +82,7 @@ nav: - Router: 'RouterConfiguration.md' - "Route Request": 'RouteRequest.md' - "Realtime Updaters": 'UpdaterConfig.md' + - "Debug UI": 'DebugUiConfiguration.md' - "Migrating between versions/builds": 'Migrating-Configuration.md' - Features explained: - "Routing modes": 'RoutingModes.md' From 57c65936bf55a8a99690bff9a145220d84419896 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Dec 2024 14:48:21 +0100 Subject: [PATCH 079/162] Update config scope docs --- doc/templates/Configuration.md | 7 ++++--- doc/user/Configuration.md | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/templates/Configuration.md b/doc/templates/Configuration.md index 45b2c36c67b..39843b57077 100644 --- a/doc/templates/Configuration.md +++ b/doc/templates/Configuration.md @@ -41,9 +41,9 @@ default behavior of scanning the base directory for input files. Scanning is ove independently for each file type, and can point to remote cloud storage with arbitrary URIs. See [the storage section](Configuration.md#Storage) for further details. -## Three Scopes of Configuration +## Four scopes of configuration -OTP is configured via three configuration JSON files which are read from the directory specified on +OTP is configured via four configuration JSON files which are read from the directory specified on its command line. We try to provide sensible defaults for every option, so all three of these files are optional, as are all the options within each file. Each configuration file corresponds to options that are relevant at a particular phase of OTP usage. @@ -51,7 +51,8 @@ options that are relevant at a particular phase of OTP usage. Options and parameters that are taken into account during the graph building process will be "baked into" the graph, and cannot be changed later in a running server. These are specified in `build-config.json`. Other details of OTP operation can be modified without rebuilding the graph. -These run-time configuration options are found in `router-config.json`. Finally, `otp-config.json` +These run-time configuration options are found in `router-config.json`. If you want to configure +the built-in debug UI add `debug-ui-config.json`. Finally, `otp-config.json` contains simple switches that enable or disable system-wide features. ## Configuration types diff --git a/doc/user/Configuration.md b/doc/user/Configuration.md index f80966b8cb3..21a8fcc61eb 100644 --- a/doc/user/Configuration.md +++ b/doc/user/Configuration.md @@ -41,9 +41,9 @@ default behavior of scanning the base directory for input files. Scanning is ove independently for each file type, and can point to remote cloud storage with arbitrary URIs. See [the storage section](Configuration.md#Storage) for further details. -## Three Scopes of Configuration +## Four scopes of configuration -OTP is configured via three configuration JSON files which are read from the directory specified on +OTP is configured via four configuration JSON files which are read from the directory specified on its command line. We try to provide sensible defaults for every option, so all three of these files are optional, as are all the options within each file. Each configuration file corresponds to options that are relevant at a particular phase of OTP usage. @@ -51,7 +51,8 @@ options that are relevant at a particular phase of OTP usage. Options and parameters that are taken into account during the graph building process will be "baked into" the graph, and cannot be changed later in a running server. These are specified in `build-config.json`. Other details of OTP operation can be modified without rebuilding the graph. -These run-time configuration options are found in `router-config.json`. Finally, `otp-config.json` +These run-time configuration options are found in `router-config.json`. If you want to configure +the built-in debug UI add `debug-ui-config.json`. Finally, `otp-config.json` contains simple switches that enable or disable system-wide features. ## Configuration types From c6c6c3bcdaedf9bdbcc0b12aa807e375d9a35e4a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 2 Dec 2024 15:09:13 +0100 Subject: [PATCH 080/162] Fix TS linting --- client/src/components/MapView/LayerControl.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index e3e87fba7bd..a41e7f133a4 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -46,7 +46,7 @@ class LayerControl implements IControl { const select = document.createElement('select'); this.container.appendChild(select); - let rasterLayers = map + const rasterLayers = map .getLayersOrder() .map((l) => map.getLayer(l)) .filter((layer) => !!layer) From 3c46be363a6912f6f221eba1aebf9a9ebbbf1a39 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 3 Dec 2024 08:10:56 +0200 Subject: [PATCH 081/162] Add logging for mode-specific transfers. --- .../module/DirectTransferGenerator.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 27fc87d9fff..0219445bb3d 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.issues.StopNotLinkedForTransfers; @@ -172,6 +173,20 @@ public void buildGraph() { nTransfersTotal, nLinkedStops ); + for (StreetMode mode : transferRequests + .stream() + .map(transferProfile -> transferProfile.journey().transfer().mode()) + .collect(Collectors.toSet())) { + LOG.info( + "Created {} transfers for mode {}.", + transfersByStop + .values() + .stream() + .filter(pathTransfer -> pathTransfer.getModes().contains(mode)) + .count(), + mode + ); + } } /** From a682d98b51c9212d788c80532fa67147fd66697b Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 3 Dec 2024 01:22:09 +0100 Subject: [PATCH 082/162] fix: Add default penalty to all car api modes. The CAR & CAR_PICKUP can be used in access/egress, but no penalty was set. --- .../preference/AccessEgressPreferences.java | 33 ++++++++++--------- .../apis/transmodel/schema.graphql | 2 +- .../preference/StreetPreferencesTest.java | 9 +++-- doc/user/RouteRequest.md | 2 ++ 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java index 42925339fd1..635c4c6c7b0 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java @@ -21,21 +21,7 @@ */ public final class AccessEgressPreferences implements Serializable { - private static final TimeAndCostPenalty DEFAULT_PENALTY = TimeAndCostPenalty.of( - TimePenalty.of(ofMinutes(20), 2f), - 1.5 - ); - private static final TimeAndCostPenalty FLEX_DEFAULT_PENALTY = TimeAndCostPenalty.of( - TimePenalty.of(ofMinutes(10), 1.3f), - 1.3 - ); - private static final TimeAndCostPenaltyForEnum DEFAULT_TIME_AND_COST = TimeAndCostPenaltyForEnum - .of(StreetMode.class) - .with(StreetMode.CAR_TO_PARK, DEFAULT_PENALTY) - .with(StreetMode.CAR_HAILING, DEFAULT_PENALTY) - .with(StreetMode.CAR_RENTAL, DEFAULT_PENALTY) - .with(StreetMode.FLEXIBLE, FLEX_DEFAULT_PENALTY) - .build(); + private static final TimeAndCostPenaltyForEnum DEFAULT_TIME_AND_COST = createDefaultCarPenalty(); public static final AccessEgressPreferences DEFAULT = new AccessEgressPreferences(); @@ -159,4 +145,21 @@ AccessEgressPreferences build() { private static DurationForEnum durationForStreetModeOf(Duration defaultValue) { return DurationForEnum.of(StreetMode.class).withDefault(defaultValue).build(); } + + private static TimeAndCostPenaltyForEnum createDefaultCarPenalty() { + var penaltyrBuilder = TimeAndCostPenaltyForEnum.of(StreetMode.class); + + var flexDefaultPenalty = TimeAndCostPenalty.of(TimePenalty.of(ofMinutes(10), 1.3f), 1.3); + penaltyrBuilder.with(StreetMode.FLEXIBLE, flexDefaultPenalty); + + // Add penalty to all car variants with access and/or egress. + var carPenalty = TimeAndCostPenalty.of(TimePenalty.of(ofMinutes(20), 2f), 1.5); + for (var it : StreetMode.values()) { + if (it.includesDriving() && (it.accessAllowed() || it.egressAllowed())) { + penaltyrBuilder.with(it, carPenalty); + } + } + + return penaltyrBuilder.build(); + } } diff --git a/application/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql b/application/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql index ed182473bb6..8468a127878 100644 --- a/application/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql +++ b/application/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql @@ -775,7 +775,7 @@ type QueryType { "Input type for executing a travel search for a trip between two locations. Returns trip patterns describing suggested alternatives for the trip." trip( "Time and cost penalty on access/egress modes." - accessEgressPenalty: [PenaltyForStreetMode!] = [{streetMode : car_park, timePenalty : "20m + 2.0 t", costFactor : 1.5}, {streetMode : car_rental, timePenalty : "20m + 2.0 t", costFactor : 1.5}, {streetMode : flexible, timePenalty : "10m + 1.30 t", costFactor : 1.3}], + accessEgressPenalty: [PenaltyForStreetMode!] = [{streetMode : car, timePenalty : "20m + 2.0 t", costFactor : 1.5}, {streetMode : car_park, timePenalty : "20m + 2.0 t", costFactor : 1.5}, {streetMode : car_pickup, timePenalty : "20m + 2.0 t", costFactor : 1.5}, {streetMode : car_rental, timePenalty : "20m + 2.0 t", costFactor : 1.5}, {streetMode : flexible, timePenalty : "10m + 1.30 t", costFactor : 1.3}], "The alightSlack is the minimum extra time after exiting a public transport vehicle. This is the default value used, if not overridden by the 'alightSlackList'." alightSlackDefault: Int = 0, "List of alightSlack for a given set of modes. Defaults: []" diff --git a/application/src/test/java/org/opentripplanner/routing/api/request/preference/StreetPreferencesTest.java b/application/src/test/java/org/opentripplanner/routing/api/request/preference/StreetPreferencesTest.java index a318474eab7..77875c44fa1 100644 --- a/application/src/test/java/org/opentripplanner/routing/api/request/preference/StreetPreferencesTest.java +++ b/application/src/test/java/org/opentripplanner/routing/api/request/preference/StreetPreferencesTest.java @@ -109,9 +109,14 @@ void testToString() { "routingTimeout: 3s, " + "elevator: ElevatorPreferences{boardTime: 2m}, " + "intersectionTraversalModel: CONSTANT, " + - "accessEgress: AccessEgressPreferences{penalty: TimeAndCostPenaltyForEnum{CAR_TO_PARK: " + + "accessEgress: AccessEgressPreferences{penalty: TimeAndCostPenaltyForEnum{" + + "CAR: (timePenalty: 20m + 2.0 t, costFactor: 1.50), " + + "CAR_TO_PARK: " + CAR_PENALTY + - ", CAR_RENTAL: (timePenalty: 20m + 2.0 t, costFactor: 1.50), CAR_HAILING: (timePenalty: 20m + 2.0 t, costFactor: 1.50), " + + ", " + + "CAR_PICKUP: (timePenalty: 20m + 2.0 t, costFactor: 1.50), " + + "CAR_RENTAL: (timePenalty: 20m + 2.0 t, costFactor: 1.50), " + + "CAR_HAILING: (timePenalty: 20m + 2.0 t, costFactor: 1.50), " + "FLEXIBLE: (timePenalty: 10m + 1.30 t, costFactor: 1.30)}, " + "maxDuration: DurationForStreetMode{default:5m}" + "}, " + diff --git a/doc/user/RouteRequest.md b/doc/user/RouteRequest.md index ea3d0d12c74..0faf1a07b00 100644 --- a/doc/user/RouteRequest.md +++ b/doc/user/RouteRequest.md @@ -445,7 +445,9 @@ performance will be better. The default values are +- `car` = (timePenalty: 20m + 2.0 t, costFactor: 1.50) - `car-to-park` = (timePenalty: 20m + 2.0 t, costFactor: 1.50) +- `car-pickup` = (timePenalty: 20m + 2.0 t, costFactor: 1.50) - `car-rental` = (timePenalty: 20m + 2.0 t, costFactor: 1.50) - `car-hailing` = (timePenalty: 20m + 2.0 t, costFactor: 1.50) - `flexible` = (timePenalty: 10m + 1.30 t, costFactor: 1.30) From 8395345b2483909862baf459c5fce0ae934f8bc8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 08:15:09 +0100 Subject: [PATCH 083/162] DebugUiConfig doesn't need to be Serializable --- .../org/opentripplanner/standalone/config/DebugUiConfig.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java index 165bcfe28f4..00f59cd342b 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.MissingNode; -import java.io.Serializable; import java.util.List; import org.opentripplanner.standalone.config.debuguiconfig.BackgroundTileLayer; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; @@ -14,7 +13,7 @@ /** * This class is an object representation of the 'debug-ui-config.json'. */ -public class DebugUiConfig implements Serializable { +public class DebugUiConfig { private static final Logger LOG = LoggerFactory.getLogger(DebugUiConfig.class); From d831ec4943c8888d308064e85188ddf5d5e369d5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 08:15:30 +0100 Subject: [PATCH 084/162] Update application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java Co-authored-by: Joel Lappalainen --- .../org/opentripplanner/standalone/config/DebugUiConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java index 00f59cd342b..079ba8ea84c 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java @@ -46,7 +46,7 @@ public DebugUiConfig(JsonNode node, String source, boolean logUnusedParams) { """ Add additional background layers that will appear in the Debug UI as one of the choices. - Current only raster tile layers are supported. + Currently only raster tile layers are supported. """ ) .asObjects( From 2ac997f8e3cefe497761361af41b4e4372418809 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 08:20:26 +0100 Subject: [PATCH 085/162] Update docs --- doc/user/DebugUiConfiguration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/DebugUiConfiguration.md b/doc/user/DebugUiConfiguration.md index 5719a606d4a..a1657796fe0 100644 --- a/doc/user/DebugUiConfiguration.md +++ b/doc/user/DebugUiConfiguration.md @@ -39,7 +39,7 @@ Additional background raster map layers. Add additional background layers that will appear in the Debug UI as one of the choices. -Current only raster tile layers are supported. +Currently only raster tile layers are supported. From a83afac6bbd88958562e35d8858c0bff717ed7f5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 08:21:34 +0100 Subject: [PATCH 086/162] Remove check for LOG.isWarnEnabled() --- .../org/opentripplanner/standalone/config/DebugUiConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java index 079ba8ea84c..cca6d2359be 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/DebugUiConfig.java @@ -77,7 +77,7 @@ public DebugUiConfig(JsonNode node, String source, boolean logUnusedParams) { ) ); - if (logUnusedParams && LOG.isWarnEnabled()) { + if (logUnusedParams) { root.logAllWarnings(LOG::warn); } } From 244c5371bc8319981906afc8ce5cca97ebd57829 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Wed, 4 Dec 2024 10:42:54 +0200 Subject: [PATCH 087/162] Fix flex routing transfer calculations and only calculate flex transfer requests for WALK mode. --- .../module/DirectTransferGenerator.java | 95 +++++++++++-------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 0219445bb3d..19e2c8227d0 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -3,6 +3,7 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimaps; import java.time.Duration; +import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -89,6 +90,17 @@ public void buildGraph() { HashMultimap.create() ); + List flexTransferRequests = new ArrayList<>(); + // Flex transfer requests only use the WALK mode. + if (OTPFeature.FlexRouting.isOn()) { + flexTransferRequests.addAll( + transferRequests + .stream() + .filter(transferProfile -> transferProfile.journey().transfer().mode() == StreetMode.WALK) + .toList() + ); + } + stops .stream() .parallel() @@ -104,6 +116,7 @@ public void buildGraph() { LOG.debug("Linking stop '{}' {}", stop, ts0); + // Calculate default transfers. for (RouteRequest transferProfile : transferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); for (NearbyStop sd : nearbyStopFinder.findNearbyStops( @@ -119,28 +132,48 @@ public void buildGraph() { if (sd.stop.transfersNotAllowed()) { continue; } - createPathTransfer(distinctTransfers, sd, stop, mode); + TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges); + PathTransfer pathTransfer = distinctTransfers.get(transferKey); + if (pathTransfer == null) { + EnumSet modes = EnumSet.of(mode); + distinctTransfers.put( + transferKey, + new PathTransfer(stop, sd.stop, sd.distance, sd.edges, modes) + ); + } else { + pathTransfer.addMode(mode); + } } } - if (OTPFeature.FlexRouting.isOn()) { - for (RouteRequest transferProfile : transferRequests) { - StreetMode mode = transferProfile.journey().transfer().mode(); - // This code is for finding transfers from AreaStops to Stops, transfers - // from Stops to AreaStops and between Stops are already covered above. - for (NearbyStop sd : nearbyStopFinder.findNearbyStops( - ts0, - transferProfile, - transferProfile.journey().transfer(), - true - )) { - // Skip the origin stop, loop transfers are not needed. - if (sd.stop == stop) { - continue; - } - if (sd.stop instanceof RegularStop) { - continue; - } - createPathTransfer(distinctTransfers, sd, stop, mode); + // Calculate flex transfers if flex routing is enabled. + for (RouteRequest transferProfile : flexTransferRequests) { + StreetMode mode = transferProfile.journey().transfer().mode(); + // This code is for finding transfers from AreaStops to Stops, transfers + // from Stops to AreaStops and between Stops are already covered above. + for (NearbyStop sd : nearbyStopFinder.findNearbyStops( + ts0, + transferProfile, + transferProfile.journey().transfer(), + true + )) { + // Skip the origin stop, loop transfers are not needed. + if (sd.stop == stop) { + continue; + } + if (sd.stop instanceof RegularStop) { + continue; + } + // The TransferKey and PathTransfer are created differently for flex routing. + TransferKey transferKey = new TransferKey(sd.stop, stop, sd.edges); + PathTransfer pathTransfer = distinctTransfers.get(transferKey); + if (pathTransfer == null) { + EnumSet modes = EnumSet.of(mode); + distinctTransfers.put( + transferKey, + new PathTransfer(sd.stop, stop, sd.distance, sd.edges, modes) + ); + } else { + pathTransfer.addMode(mode); } } } @@ -189,28 +222,6 @@ public void buildGraph() { } } - /** - * Factory method for creating a PathTransfer. - */ - private void createPathTransfer( - Map distinctTransfers, - NearbyStop sd, - RegularStop stop, - StreetMode mode - ) { - TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges); - PathTransfer pathTransfer = distinctTransfers.get(transferKey); - if (pathTransfer == null) { - EnumSet modes = EnumSet.of(mode); - distinctTransfers.put( - transferKey, - new PathTransfer(stop, sd.stop, sd.distance, sd.edges, modes) - ); - } else { - pathTransfer.addMode(mode); - } - } - /** * Factory method for creating a NearbyStopFinder. Will create different finders depending on * whether the graph has a street network and if ConsiderPatternsForDirectTransfers feature is From 20696c1d6e78902f8dd9d1e6ba31de993caf7789 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 09:56:39 +0100 Subject: [PATCH 088/162] Use 'OTP Debug' in the UI --- client/index.html | 2 +- client/src/components/SearchBar/SearchBar.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/index.html b/client/index.html index 77eb289e595..f09832636f0 100644 --- a/client/index.html +++ b/client/index.html @@ -4,7 +4,7 @@ - OTP Debug Client + OTP Debug
diff --git a/client/src/components/SearchBar/SearchBar.tsx b/client/src/components/SearchBar/SearchBar.tsx index 47781532179..e90a54eab80 100644 --- a/client/src/components/SearchBar/SearchBar.tsx +++ b/client/src/components/SearchBar/SearchBar.tsx @@ -34,7 +34,7 @@ export function SearchBar({ onRoute, tripQueryVariables, setTripQueryVariables,
setShowServerInfo((v) => !v)}>
- OTP Debug Client + OTP Debug {showServerInfo && }
From 0fe4bdd3d16298cee75b8e8366db7388ca7ee86f Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 5 Dec 2024 14:01:41 +0000 Subject: [PATCH 089/162] Add changelog entry for #6282 [ci skip] --- doc/user/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index f6f9a55fb77..f9f766bbaa7 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -57,6 +57,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Make `motorroad=yes` car-only [#6288](https://github.com/opentripplanner/OpenTripPlanner/pull/6288) - Add decision record for analysis and design documentation [#6281](https://github.com/opentripplanner/OpenTripPlanner/pull/6281) - Switch GTFS flex `safe_duration_offset` back to seconds [#6298](https://github.com/opentripplanner/OpenTripPlanner/pull/6298) +- Remove unused GtfsGraphQlApiRentalStationFuzzyMatching feature [#6282](https://github.com/opentripplanner/OpenTripPlanner/pull/6282) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From 72d5765af8599eeff2cb01983f71dfe24a9ecdfd Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 5 Dec 2024 14:35:25 +0000 Subject: [PATCH 090/162] allow valid json array in include config --- .../config/framework/file/IncludeFileDirective.java | 6 ++++-- .../framework/file/IncludeFileDirectiveTest.java | 11 +++++++++++ doc/templates/Configuration.md | 3 ++- doc/user/Configuration.md | 3 ++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirective.java b/application/src/main/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirective.java index f864bc24d99..05295c8993f 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirective.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirective.java @@ -95,10 +95,12 @@ private String includeFileDirective(String text, String source) { String directive = entry.getKey(); String fileText = loadFile(entry.getValue(), directive, source); - // If the insert text is a legal JSON object "[white-space]{ ... }[white-space]", then + // If the insert text is a legal JSON object or array, then // ignore the optional quotes matched by the directive pattern var json = fileText.trim(); - if (json.startsWith("{") && json.endsWith("}")) { + if ( + json.startsWith("{") && json.endsWith("}") || json.startsWith("[") && json.endsWith("]") + ) { text = text.replace(entry.getKey(), fileText); } else { // Add back quotes if matched part of directive pattern diff --git a/application/src/test/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirectiveTest.java b/application/src/test/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirectiveTest.java index 0fc1199236d..0628b610216 100644 --- a/application/src/test/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirectiveTest.java +++ b/application/src/test/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirectiveTest.java @@ -45,6 +45,17 @@ void includeFileWithQuotesAndProperJsonInput() throws IOException { assertEquals(json("{ 'key' : \t {\n 'foo' : 'bar' \n }\n}"), result); } + @Test + void includeFileWithQuotesAndJsonArrayInput() throws IOException { + savePartialFile(json("\t [\n 'foo', 'bar' \n ]\n")); + String result = IncludeFileDirective.includeFileDirective( + CONFIG_DIR, + json("{ 'key' : '${includeFile:" + PART_FILE_NAME + "}'}"), + PART_FILE_NAME + ); + assertEquals(json("{ 'key' : \t [\n 'foo', 'bar' \n ]\n}"), result); + } + @Test void includeFileWithQuotesWithNoJsonInput() throws IOException { savePartialFile("value"); diff --git a/doc/templates/Configuration.md b/doc/templates/Configuration.md index 45b2c36c67b..ca89be6dc2a 100644 --- a/doc/templates/Configuration.md +++ b/doc/templates/Configuration.md @@ -141,7 +141,8 @@ directory. Relative paths are not supported. To allow both files (the configuration file and the injected file) to be valid JSON files, a special case is supported. If the include file directive is quoted, then the quotes are removed, if the -text inserted is valid JSON (starts with `{` and ends with `}`). +text inserted is valid JSON object (starts with `{` and ends with `}`) or valid JSON array +(starts with `[` and ends with `]`). Variable substitution is performed on configuration file after the include file directive; Hence variable substitution is also performed on the text in the injected file. diff --git a/doc/user/Configuration.md b/doc/user/Configuration.md index f80966b8cb3..53c0955a211 100644 --- a/doc/user/Configuration.md +++ b/doc/user/Configuration.md @@ -168,7 +168,8 @@ directory. Relative paths are not supported. To allow both files (the configuration file and the injected file) to be valid JSON files, a special case is supported. If the include file directive is quoted, then the quotes are removed, if the -text inserted is valid JSON (starts with `{` and ends with `}`). +text inserted is valid JSON object (starts with `{` and ends with `}`) or valid JSON array +(starts with `[` and ends with `]`). Variable substitution is performed on configuration file after the include file directive; Hence variable substitution is also performed on the text in the injected file. From 97a47ef3dfa48dd715e341f9e24607eaddb0afea Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 5 Dec 2024 16:53:24 +0000 Subject: [PATCH 091/162] update color brightness calculation --- .../gtfs/graphbuilder/GtfsModule.java | 17 +++---- .../utils/color/Brightness.java | 6 +++ .../utils/color/ColorUtils.java | 44 +++++++++++++++++++ .../utils/color/ColorUtilsTest.java | 31 +++++++++++++ 4 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 utils/src/main/java/org/opentripplanner/utils/color/Brightness.java create mode 100644 utils/src/main/java/org/opentripplanner/utils/color/ColorUtils.java create mode 100644 utils/src/test/java/org/opentripplanner/utils/color/ColorUtilsTest.java diff --git a/application/src/main/java/org/opentripplanner/gtfs/graphbuilder/GtfsModule.java b/application/src/main/java/org/opentripplanner/gtfs/graphbuilder/GtfsModule.java index 39f88f13281..a5fe3641e3c 100644 --- a/application/src/main/java/org/opentripplanner/gtfs/graphbuilder/GtfsModule.java +++ b/application/src/main/java/org/opentripplanner/gtfs/graphbuilder/GtfsModule.java @@ -1,5 +1,7 @@ package org.opentripplanner.gtfs.graphbuilder; +import static org.opentripplanner.utils.color.ColorUtils.computeBrightness; + import java.awt.Color; import java.io.IOException; import java.io.Serializable; @@ -52,6 +54,7 @@ import org.opentripplanner.standalone.config.BuildConfig; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.TimetableRepository; +import org.opentripplanner.utils.color.Brightness; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -415,8 +418,7 @@ private boolean skipEntityClass(Class entityClass) { * If a route doesn't have color or already has routeColor and routeTextColor nothing is done. *

* textColor can be black or white. White for dark colors and black for light colors of - * routeColor. If color is light or dark is calculated based on luminance formula: sqrt( - * 0.299*Red^2 + 0.587*Green^2 + 0.114*Blue^2 ) + * routeColor. */ private void generateRouteColor(Route route) { String routeColor = route.getColor(); @@ -431,16 +433,7 @@ private void generateRouteColor(Route route) { } Color routeColorColor = Color.decode("#" + routeColor); - //gets float of RED, GREEN, BLUE in range 0...1 - float[] colorComponents = routeColorColor.getRGBColorComponents(null); - //Calculates luminance based on https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color - double newRed = 0.299 * Math.pow(colorComponents[0], 2.0); - double newGreen = 0.587 * Math.pow(colorComponents[1], 2.0); - double newBlue = 0.114 * Math.pow(colorComponents[2], 2.0); - double luminance = Math.sqrt(newRed + newGreen + newBlue); - - //For brighter colors use black text color and reverse for darker - if (luminance > 0.5) { + if (computeBrightness(routeColorColor) == Brightness.LIGHT) { textColor = "000000"; } else { textColor = "FFFFFF"; diff --git a/utils/src/main/java/org/opentripplanner/utils/color/Brightness.java b/utils/src/main/java/org/opentripplanner/utils/color/Brightness.java new file mode 100644 index 00000000000..b4a95b5c9f2 --- /dev/null +++ b/utils/src/main/java/org/opentripplanner/utils/color/Brightness.java @@ -0,0 +1,6 @@ +package org.opentripplanner.utils.color; + +public enum Brightness { + DARK, + LIGHT, +} diff --git a/utils/src/main/java/org/opentripplanner/utils/color/ColorUtils.java b/utils/src/main/java/org/opentripplanner/utils/color/ColorUtils.java new file mode 100644 index 00000000000..ea334391e38 --- /dev/null +++ b/utils/src/main/java/org/opentripplanner/utils/color/ColorUtils.java @@ -0,0 +1,44 @@ +package org.opentripplanner.utils.color; + +import java.awt.Color; + +public final class ColorUtils { + + private ColorUtils() {} + + /** + * Calculates luminance according to + * W3C Recommendation + */ + public static double computeLuminance(Color color) { + //gets float of RED, GREEN, BLUE in range 0...1 + float[] colorComponents = color.getRGBColorComponents(null); + //Calculates luminance based on https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color + double r = linearizeColorComponent(colorComponents[0]); + double g = linearizeColorComponent(colorComponents[1]); + double b = linearizeColorComponent(colorComponents[2]); + return 0.2126 * r + 0.7152 * g + 0.0722 * b; + } + + private static double linearizeColorComponent(double srgb) { + return srgb <= 0.04045 ? srgb / 12.92 : Math.pow((srgb + 0.055) / 1.055, 2.4); + } + + /** + * Determine if a color is light or dark + *

+ * A light color is a color where the contrast ratio with black is larger than with white. + *

+ * The contrast ratio is defined per Web Content Accessibility Guidelines (WCAG) 2.1. + */ + public static Brightness computeBrightness(Color color) { + // The contrast ratio between two colors is defined as (L1 + 0.05) / (L2 + 0.05) + // where L1 is the lighter of the two colors. + // + // Therefore, the contrast ratio with black is (L + 0.05) / 0.05 and the contrast ratio with + // white is 1.05 / (L + 0.05) + // + // Solving (L + 0.05) / 0.05 > 1.05 / (L + 0.05) gets L > 0.179 + return computeLuminance(color) > 0.179 ? Brightness.LIGHT : Brightness.DARK; + } +} diff --git a/utils/src/test/java/org/opentripplanner/utils/color/ColorUtilsTest.java b/utils/src/test/java/org/opentripplanner/utils/color/ColorUtilsTest.java new file mode 100644 index 00000000000..7ad2a424fae --- /dev/null +++ b/utils/src/test/java/org/opentripplanner/utils/color/ColorUtilsTest.java @@ -0,0 +1,31 @@ +package org.opentripplanner.utils.color; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.opentripplanner.utils.color.ColorUtils.computeBrightness; + +import java.awt.Color; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.FieldSource; + +public class ColorUtilsTest { + + static final Arguments[] brightnessExpectations = { + arguments(Color.black, Brightness.DARK), + arguments(Color.green, Brightness.LIGHT), + arguments(Color.blue, Brightness.DARK), + arguments(Color.red, Brightness.LIGHT), + arguments(Color.yellow, Brightness.LIGHT), + arguments(Color.white, Brightness.LIGHT), + arguments(Color.pink, Brightness.LIGHT), + arguments(Color.orange, Brightness.LIGHT), + arguments(Color.cyan, Brightness.LIGHT), + }; + + @ParameterizedTest + @FieldSource("brightnessExpectations") + void testBrightness(Color color, Brightness brightness) { + assertEquals(computeBrightness(color), brightness); + } +} From 7ce751bb89ae25e50882029928c3c1bc4bb9e1a5 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 5 Dec 2024 17:19:54 +0000 Subject: [PATCH 092/162] update the text color for the frontend as well --- client/src/util/generateTextColor.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/client/src/util/generateTextColor.ts b/client/src/util/generateTextColor.ts index 824dc87f3a2..564440c248b 100644 --- a/client/src/util/generateTextColor.ts +++ b/client/src/util/generateTextColor.ts @@ -1,18 +1,20 @@ /** * textColor can be black or white. White for dark colors and black for light colors. - * Calculated based on luminance formula: - * sqrt( 0.299*Red^2 + 0.587*Green^2 + 0.114*Blue^2 ) + * Calculated according to WCAG 2.1 */ export function generateTextColor(hexColor: string) { const color = decodeColor(hexColor); - //Calculates luminance based on https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color - const newRed = 0.299 * Math.pow(color[0] / 255.0, 2.0); - const newGreen = 0.587 * Math.pow(color[1] / 255.0, 2.0); - const newBlue = 0.114 * Math.pow(color[2] / 255.0, 2.0); - const luminance = Math.sqrt(newRed + newGreen + newBlue); + function linearizeColorComponent(srgb: number) { + return srgb <= 0.04045 ? srgb / 12.92 : Math.pow((srgb + 0.055) / 1.055, 2.4); + } + + const r = linearizeColorComponent(color[0] / 255.0); + const g = linearizeColorComponent(color[1] / 255.0); + const b = linearizeColorComponent(color[2] / 255.0); + const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b; - if (luminance > 0.66) { + if (luminance > 0.179) { return '#000'; } else { return '#fff'; From 3952cfcd8401b2bf29169e32ce870b3eaf4f933a Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Fri, 6 Dec 2024 10:06:02 +0100 Subject: [PATCH 093/162] fix: Fix references to the shaded jar for performance tests and in doc --- .github/workflows/performance-test.yml | 2 +- README.md | 2 +- doc/user/Getting-OTP.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/performance-test.yml b/.github/workflows/performance-test.yml index ad3b843dd12..1ec537a0b4f 100644 --- a/.github/workflows/performance-test.yml +++ b/.github/workflows/performance-test.yml @@ -88,7 +88,7 @@ jobs: - name: Build graph if: matrix.profile == 'core' || github.ref == 'refs/heads/dev-2.x' run: | - cp application/target/otp-*-SNAPSHOT-shaded.jar otp.jar + cp shaded-jar/target/otp-*-SNAPSHOT-shaded.jar otp.jar java -Xmx32G -jar otp.jar --build --save test/performance/${{ matrix.location }}/ - name: Run speed test diff --git a/README.md b/README.md index 41d889cf15b..ec66e694e8c 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ We run a speed test (included in the code) to measure the performance for every The main Java server code is in `application/src/main/`. OTP also includes a Javascript client based on the MapLibre mapping library in `client/src/`. This client is now used for testing, with most major deployments building custom clients from reusable components. The Maven build produces a unified ("shaded") -JAR file at `application/target/otp-VERSION.jar` containing all necessary code and dependencies to run OpenTripPlanner. +JAR file at `shaded-jar/target/otp-VERSION.jar` containing all necessary code and dependencies to run OpenTripPlanner. Additional information and instructions are available in the [main documentation](http://docs.opentripplanner.org/en/dev-2.x/), including a diff --git a/doc/user/Getting-OTP.md b/doc/user/Getting-OTP.md index 92f1e7298fc..ea0bac0df90 100644 --- a/doc/user/Getting-OTP.md +++ b/doc/user/Getting-OTP.md @@ -65,7 +65,7 @@ OTP. If all goes well you should see a success message like the following: ``` This build process should produce a JAR file called `otp-x.y.z-shaded.jar` in the -`application/target/` directory which contains all the compiled OTP classes and their dependencies +`shaded-jar/target/` directory which contains all the compiled OTP classes and their dependencies (the external libraries they use). The shell script called 'otp' in the root of the cloned repository will start the main class of that JAR file under a Java virtual machine, so after the Maven build completes you should be able to run `./otp --help` and see an OTP help message including command line From 4a63ca10ff08b6be1566a4ee9f2d85b20807dd31 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Dec 2024 11:53:39 +0100 Subject: [PATCH 094/162] Convert booking notice to durations --- .../gtfs/datafetchers/BookingInfoImpl.java | 10 ++++++ .../gtfs/generated/GraphQLDataFetchers.java | 4 +++ .../opentripplanner/apis/gtfs/schema.graphqls | 8 +++-- .../datafetchers/BookingInfoImplTest.java | 31 +++++++++++++------ 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImpl.java index 0060e6ad7e1..f6633818b3d 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImpl.java @@ -30,6 +30,11 @@ public DataFetcher latestBookingTime() { return environment -> getSource(environment).getLatestBookingTime(); } + @Override + public DataFetcher maximumBookingNotice() { + return env -> getSource(env).getMaximumBookingNotice().orElse(null); + } + @Override public DataFetcher maximumBookingNoticeSeconds() { return environment -> @@ -41,6 +46,11 @@ public DataFetcher message() { return environment -> getSource(environment).getMessage(); } + @Override + public DataFetcher minimumBookingNotice() { + return env -> getSource(env).getMinimumBookingNotice().orElse(null); + } + @Override public DataFetcher minimumBookingNoticeSeconds() { return environment -> diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index dd74347b928..6528d8ee286 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -221,10 +221,14 @@ public interface GraphQLBookingInfo { public DataFetcher latestBookingTime(); + public DataFetcher maximumBookingNotice(); + public DataFetcher maximumBookingNoticeSeconds(); public DataFetcher message(); + public DataFetcher minimumBookingNotice(); + public DataFetcher minimumBookingNoticeSeconds(); public DataFetcher pickupMessage(); diff --git a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index a131b95fc8e..f92a8191018 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -279,12 +279,16 @@ type BookingInfo { earliestBookingTime: BookingTime "When is the latest time the service can be booked" latestBookingTime: BookingTime + "Maximum duration before travel to make the request." + maximumBookingNotice: Duration "Maximum number of seconds before travel to make the request" - maximumBookingNoticeSeconds: Long + maximumBookingNoticeSeconds: Long @deprecated(reason : "Use `maximumBookingNotice`") "A general message for those booking the service" message: String + "Minimum duration before travel to make the request" + minimumBookingNotice: Duration "Minimum number of seconds before travel to make the request" - minimumBookingNoticeSeconds: Long + minimumBookingNoticeSeconds: Long @deprecated(reason : "Use `minimumBookingNotice`") "A message specific to the pick up" pickupMessage: String } diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImplTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImplTest.java index 1103024aa61..d721a73939b 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImplTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/datafetchers/BookingInfoImplTest.java @@ -15,27 +15,40 @@ class BookingInfoImplTest { private static final BookingInfoImpl SUBJECT = new BookingInfoImpl(); private static final Duration TEN_MINUTES = Duration.ofMinutes(10); + private static final BookingInfo WITH_NOTICE_DURATIONS = BookingInfo + .of() + .withMinimumBookingNotice(TEN_MINUTES) + .withMaximumBookingNotice(TEN_MINUTES) + .build(); @Test - void emptyDurations() throws Exception { + void emptyNoticeSeconds() throws Exception { var env = dataFetchingEnvironment(BookingInfo.of().build()); assertNull(SUBJECT.minimumBookingNoticeSeconds().get(env)); assertNull(SUBJECT.maximumBookingNoticeSeconds().get(env)); } @Test - void durations() throws Exception { - var env = dataFetchingEnvironment( - BookingInfo - .of() - .withMinimumBookingNotice(TEN_MINUTES) - .withMaximumBookingNotice(TEN_MINUTES) - .build() - ); + void emptyNoticeDurations() throws Exception { + var env = dataFetchingEnvironment(BookingInfo.of().build()); + assertNull(SUBJECT.minimumBookingNotice().get(env)); + assertNull(SUBJECT.maximumBookingNotice().get(env)); + } + + @Test + void seconds() throws Exception { + var env = dataFetchingEnvironment(WITH_NOTICE_DURATIONS); assertEquals(600, SUBJECT.minimumBookingNoticeSeconds().get(env)); assertEquals(600, SUBJECT.maximumBookingNoticeSeconds().get(env)); } + @Test + void durations() throws Exception { + var env = dataFetchingEnvironment(WITH_NOTICE_DURATIONS); + assertEquals(TEN_MINUTES, SUBJECT.minimumBookingNotice().get(env)); + assertEquals(TEN_MINUTES, SUBJECT.maximumBookingNotice().get(env)); + } + private DataFetchingEnvironment dataFetchingEnvironment(BookingInfo bookingInfo) { var executionContext = newExecutionContextBuilder() .executionId(ExecutionId.from(this.getClass().getName())) From 42b23c0a6d1cd429b423969a2d33d7c58b197c43 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 7 Dec 2024 05:48:09 +0000 Subject: [PATCH 095/162] chore(deps): update micrometer.version to v1.14.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index edc1ab5efd7..a502f3d57bc 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 2.18.2 3.1.9 5.11.3 - 1.13.7 + 1.14.1 5.6.0 1.5.12 9.12.0 From 540cbbd343ab19e2fe35132cde8ad520f2409ebf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Dec 2024 18:37:44 +0000 Subject: [PATCH 096/162] fix(deps): update dependency org.onebusaway:onebusaway-gtfs to v4 --- application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/pom.xml b/application/pom.xml index 4b11f1f6527..50ecf5e2549 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -296,7 +296,7 @@ org.onebusaway onebusaway-gtfs - 3.2.4 + 4.3.0 From 4cdde4bc42f9b77da11909e7755a1569b2e431b5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 06:44:44 +0100 Subject: [PATCH 097/162] fix(deps): update dependency org.mobilitydata:gbfs-java-model to v1.0.9 (#6319) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/pom.xml b/application/pom.xml index 4b11f1f6527..84d92f2b4d4 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -148,7 +148,7 @@ org.mobilitydata gbfs-java-model - 1.0.7 + 1.0.9 From 6683aed6399725b0c6b55dfbd114ceda9f81cee0 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Mon, 9 Dec 2024 09:18:30 +0000 Subject: [PATCH 098/162] Upgrade debug client to version 2024/12/2024-12-09T09:17 --- application/src/client/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/client/index.html b/application/src/client/index.html index 391458fba41..b34591918d8 100644 --- a/application/src/client/index.html +++ b/application/src/client/index.html @@ -4,9 +4,9 @@ - OTP Debug Client - - + OTP Debug + +

From ff031693965e09c156e117ee3422c7b377d0a983 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Mon, 9 Dec 2024 09:18:46 +0000 Subject: [PATCH 099/162] Add changelog entry for #6295 [ci skip] --- doc/user/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index f9f766bbaa7..7d96b53885c 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -58,6 +58,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Add decision record for analysis and design documentation [#6281](https://github.com/opentripplanner/OpenTripPlanner/pull/6281) - Switch GTFS flex `safe_duration_offset` back to seconds [#6298](https://github.com/opentripplanner/OpenTripPlanner/pull/6298) - Remove unused GtfsGraphQlApiRentalStationFuzzyMatching feature [#6282](https://github.com/opentripplanner/OpenTripPlanner/pull/6282) +- Make debug UI background layers configurable with new file `debug-ui-config.json` [#6295](https://github.com/opentripplanner/OpenTripPlanner/pull/6295) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From 0079a16a6ef3fa9bef7610df598244a8dd9795fd Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 9 Dec 2024 10:19:33 +0100 Subject: [PATCH 100/162] Remove console.log [ci skip] --- client/src/components/MapView/LayerControl.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/components/MapView/LayerControl.tsx b/client/src/components/MapView/LayerControl.tsx index a41e7f133a4..43b0258662d 100644 --- a/client/src/components/MapView/LayerControl.tsx +++ b/client/src/components/MapView/LayerControl.tsx @@ -65,7 +65,6 @@ class LayerControl implements IControl { }); select.onchange = () => { const layerId = select.value; - console.log(select.value); const layer = map.getLayer(layerId); if (layer) { rasterLayers.forEach((l) => { From da622619c0c8dcfd81e25091e1f55d57360eed32 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 09:23:18 +0000 Subject: [PATCH 101/162] chore(deps): update react monorepo to v19 --- client/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/package.json b/client/package.json index f930bcd67d5..5aa6681a44b 100644 --- a/client/package.json +++ b/client/package.json @@ -23,9 +23,9 @@ "graphql": "16.9.0", "graphql-request": "7.1.2", "maplibre-gl": "4.7.1", - "react": "18.3.1", + "react": "19.0.0", "react-bootstrap": "2.10.6", - "react-dom": "18.3.1", + "react-dom": "19.0.0", "react-map-gl": "7.1.7" }, "devDependencies": { @@ -34,8 +34,8 @@ "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.5.0", "@testing-library/react": "16.0.1", - "@types/react": "18.3.12", - "@types/react-dom": "18.3.1", + "@types/react": "19.0.1", + "@types/react-dom": "19.0.1", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.4", From 0f3f148b83e4464e8ae24186ca6660227e478677 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Dec 2024 11:46:20 +0200 Subject: [PATCH 102/162] Fix link --- DEVELOPMENT_DECISION_RECORDS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DEVELOPMENT_DECISION_RECORDS.md b/DEVELOPMENT_DECISION_RECORDS.md index f5923092a94..b3766fad29d 100644 --- a/DEVELOPMENT_DECISION_RECORDS.md +++ b/DEVELOPMENT_DECISION_RECORDS.md @@ -107,4 +107,5 @@ Prefer immutable types over mutable. Use builders where appropriate. See ## GraphQL Best Practices - API Design [Follow best practices for designing GraphQL APIs. Our APIs are used by hundreds of clients -(web-pages/apps/services) and need to be backwards compatible.](doc/dev/A) \ No newline at end of file +(web-pages/apps/services) and need to be backwards compatible.]( +doc/dev/decisionrecords/APIGraphQLDesign.md) \ No newline at end of file From 3d9924cdfbc07e88a99acf273a38edda9a158caf Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Dec 2024 11:49:36 +0200 Subject: [PATCH 103/162] Remove extra line break --- DEVELOPMENT_DECISION_RECORDS.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DEVELOPMENT_DECISION_RECORDS.md b/DEVELOPMENT_DECISION_RECORDS.md index b3766fad29d..fc12950901b 100644 --- a/DEVELOPMENT_DECISION_RECORDS.md +++ b/DEVELOPMENT_DECISION_RECORDS.md @@ -107,5 +107,4 @@ Prefer immutable types over mutable. Use builders where appropriate. See ## GraphQL Best Practices - API Design [Follow best practices for designing GraphQL APIs. Our APIs are used by hundreds of clients -(web-pages/apps/services) and need to be backwards compatible.]( -doc/dev/decisionrecords/APIGraphQLDesign.md) \ No newline at end of file +(web-pages/apps/services) and need to be backwards compatible.](doc/dev/decisionrecords/APIGraphQLDesign.md) \ No newline at end of file From f2db3f45d3e239f145b31183c9b82b8842ddd066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Erik=20St=C3=B8wer?= Date: Mon, 9 Dec 2024 11:00:15 +0100 Subject: [PATCH 104/162] Update @testing-library/react to react19 compatible version --- client/package-lock.json | 2190 +++++++++++++++++++------------------- client/package.json | 2 +- 2 files changed, 1072 insertions(+), 1120 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index bbed0b8e4fa..52d93d9d4cf 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14,9 +14,9 @@ "graphql": "16.9.0", "graphql-request": "7.1.2", "maplibre-gl": "4.7.1", - "react": "18.3.1", + "react": "19.0.0", "react-bootstrap": "2.10.6", - "react-dom": "18.3.1", + "react-dom": "19.0.0", "react-map-gl": "7.1.7" }, "devDependencies": { @@ -24,9 +24,9 @@ "@graphql-codegen/client-preset": "4.5.1", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.5.0", - "@testing-library/react": "16.0.1", - "@types/react": "18.3.12", - "@types/react-dom": "18.3.1", + "@testing-library/react": "16.1.0", + "@types/react": "19.0.1", + "@types/react-dom": "19.0.1", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.4", @@ -45,15 +45,6 @@ "vitest": "2.1.8" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -229,9 +220,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", "dev": true, "engines": { "node": ">=6.9.0" @@ -268,13 +259,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -284,12 +275,12 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -312,19 +303,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz", - "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", "semver": "^6.3.1" }, "engines": { @@ -334,37 +323,14 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz", - "integrity": "sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz", - "integrity": "sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.24.6", - "@babel/types": "^7.24.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -401,12 +367,12 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -422,14 +388,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", - "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -438,40 +404,14 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", - "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.24.6" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -518,12 +458,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "dev": true, "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -582,12 +522,12 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.1.tgz", - "integrity": "sha512-sxi2kLTI5DeW5vDtMUsk4mTPwvlUDbjOnoWayhynCwrw4QXRld4QEYwqzY8JmQXaJUtgUuCIurtSRH5sn4c7mA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", + "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -597,12 +537,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", - "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -612,12 +552,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", - "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -639,12 +579,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", - "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -654,12 +594,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", - "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -669,12 +609,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz", - "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -684,18 +624,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", - "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-split-export-declaration": "^7.22.6", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "engines": { @@ -706,13 +644,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", - "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/template": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -722,12 +660,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", - "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -737,13 +675,13 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.1.tgz", - "integrity": "sha512-iIYPIWt3dUmUKKE10s3W+jsQ3icFkw0JyRVyY1B7G4yK/nngAOHLVx8xlhA6b/Jzl/Y0nis8gjqhqKtRDQqHWQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz", + "integrity": "sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-flow": "^7.24.1" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-flow": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -753,13 +691,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", - "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -769,14 +707,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", - "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -786,12 +724,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", - "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -801,12 +739,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", - "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -816,14 +754,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", - "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -833,13 +770,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", - "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -849,12 +786,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", - "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -864,12 +801,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", - "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -879,12 +816,12 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz", - "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -894,16 +831,16 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", - "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/types": "^7.23.4" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -943,12 +880,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", - "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -958,13 +895,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", - "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -974,12 +911,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", - "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -992,7 +929,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1015,16 +951,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1033,9 +969,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -1436,24 +1372,27 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -1536,7 +1475,6 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -1551,7 +1489,6 @@ "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-5.0.3.tgz", "integrity": "sha512-SxXPmramkth8XtBlAHu4H4jYcYXM/o3p01+psU+0NADQowA8jtYkK6MW5rV6T+CxkEaNZItfSmZRPgIuypcqnA==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.3", "tslib": "~2.6.0" @@ -1560,12 +1497,17 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/add/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-codegen/cli": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-5.0.3.tgz", "integrity": "sha512-ULpF6Sbu2d7vNEOgBtE9avQp2oMgcPY/QBYcCqk0Xru5fz+ISjcovQX29V7CS7y5wWBRzNLoXwJQGeEyWbl05g==", "dev": true, - "license": "MIT", "dependencies": { "@babel/generator": "^7.18.13", "@babel/template": "^7.18.10", @@ -1627,7 +1569,6 @@ "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.5.1.tgz", "integrity": "sha512-UE2/Kz2eaxv35HIXFwlm2QwoUH77am6+qp54aeEWYq+T+WPwmIc6+YzqtGiT/VcaXgoOUSgidREGm9R6jKcf9g==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/template": "^7.20.7", @@ -1650,6 +1591,12 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/client-preset/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-codegen/core": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@graphql-codegen/core/-/core-4.0.2.tgz", @@ -1665,12 +1612,17 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/core/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-codegen/gql-tag-operations": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.12.tgz", "integrity": "sha512-v279i49FJ5dMmQXIGUgm6FtnnkxtJjVJWDNYh9JK4ppvOixdHp+PmEzW227DkLN6avhVxNnYdp/1gdRBwdWypw==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-codegen/visitor-plugin-common": "5.6.0", @@ -1685,6 +1637,12 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/gql-tag-operations/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-codegen/introspection": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@graphql-codegen/introspection/-/introspection-4.0.3.tgz", @@ -1699,12 +1657,17 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/introspection/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-codegen/plugin-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-5.1.0.tgz", "integrity": "sha512-Y7cwEAkprbTKzVIe436TIw4w03jorsMruvCvu0HJkavaKMQbWY+lQ1RIuROgszDbxAyM35twB5/sUvYG5oW+yg==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-tools/utils": "^10.0.0", "change-case-all": "1.0.15", @@ -1720,12 +1683,17 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/plugin-helpers/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-codegen/schema-ast": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-4.1.0.tgz", "integrity": "sha512-kZVn0z+th9SvqxfKYgztA6PM7mhnSZaj4fiuBWvMTqA+QqQ9BBed6Pz41KuD/jr0gJtnlr2A4++/0VlpVbCTmQ==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.3", "@graphql-tools/utils": "^10.0.0", @@ -1735,12 +1703,17 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/schema-ast/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-codegen/typed-document-node": { "version": "5.0.12", "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.0.12.tgz", "integrity": "sha512-Wsbc1AqC+MFp3maWPzrmmyHLuWCPB63qBBFLTKtO6KSsnn0KnLocBp475wkfBZnFISFvzwpJ0e6LV71gKfTofQ==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-codegen/visitor-plugin-common": "5.6.0", @@ -1755,12 +1728,17 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/typed-document-node/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-codegen/typescript": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.1.2.tgz", "integrity": "sha512-GhPgfxgWEkBrvKR2y77OThus3K8B6U3ESo68l7+sHH1XiL2WapK5DdClViblJWKQerJRjfJu8tcaxQ8Wpk6Ogw==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-codegen/schema-ast": "^4.0.2", @@ -1780,7 +1758,6 @@ "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.4.0.tgz", "integrity": "sha512-oVlos2ySx8xIbbe8r5ZI6mOpI+OTeP14RmS2MchBJ6DL+S9G16O6+9V3Y8V22fTnmBTZkTfAAaBv4HYhhDGWVA==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-codegen/typescript": "^4.1.2", @@ -1795,12 +1772,23 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/typescript-operations/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, + "node_modules/@graphql-codegen/typescript/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-codegen/visitor-plugin-common": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.6.0.tgz", "integrity": "sha512-PowcVPJbUqMC9xTJ/ZRX1p/fsdMZREc+69CM1YY+AlFng2lL0zsdBskFJSRoviQk2Ch9IPhKGyHxlJCy9X22tg==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-tools/optimize": "^2.0.0", @@ -1820,15 +1808,21 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/@graphql-codegen/visitor-plugin-common/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/@graphql-tools/apollo-engine-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.1.tgz", - "integrity": "sha512-NaPeVjtrfbPXcl+MLQCJLWtqe2/E4bbAqcauEOQ+3sizw1Fc2CNmhHRF8a6W4D0ekvTRRXAMptXYgA2uConbrA==", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.7.tgz", + "integrity": "sha512-jyQU4ZhbkUM7C3V+m15K3ch7BSCTdWw/bthjhYhMkiMoFGL/ClNL5+fCIFMcQi5xSxPPmwkBkxzQ8u8UoNPMAg==", "dev": true, "dependencies": { "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/utils": "^10.0.13", - "@whatwg-node/fetch": "^0.9.0", + "@graphql-tools/utils": "^10.6.2", + "@whatwg-node/fetch": "^0.10.0", "tslib": "^2.4.0" }, "engines": { @@ -1838,32 +1832,60 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/fetch": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", + "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", + "dev": true, + "dependencies": { + "@whatwg-node/node-fetch": "^0.7.1", + "urlpattern-polyfill": "^10.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/node-fetch": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.4.tgz", + "integrity": "sha512-rvUtU/xKKl/av5EIwyqfw7w0R+hx+tQrlhpIyFr27MwJRlUb+xcYv97kOmp7FE/WmQ8s+Tb6bcD6W8o/s2pGWw==", + "dev": true, + "dependencies": { + "@kamilkisiela/fast-url-parser": "^1.1.4", + "@whatwg-node/disposablestack": "^0.0.5", + "busboy": "^1.6.0", + "fast-querystring": "^1.1.1", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@graphql-tools/batch-execute": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.4.tgz", - "integrity": "sha512-kkebDLXgDrep5Y0gK1RN3DMUlLqNhg60OAz0lTCqrYeja6DshxLtLkj+zV4mVbBA4mQOEoBmw6g1LZs3dA84/w==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.10.tgz", + "integrity": "sha512-nCRNFq2eqy+ONDknd8DfqidY/Ljgyq67Q0Hb9SMJ3FOWpKrApqmNT9J1BA3JW4r+/zIGtM1VKi+P9FYu3zMHHA==", "dev": true, "dependencies": { - "@graphql-tools/utils": "^10.0.13", - "dataloader": "^2.2.2", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "@graphql-tools/utils": "^10.6.2", + "dataloader": "^2.2.3", + "tslib": "^2.8.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/code-file-loader": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.1.tgz", - "integrity": "sha512-q4KN25EPSUztc8rA8YUU3ufh721Yk12xXDbtUA+YstczWS7a1RJlghYMFEfR1HsHSYbF7cUqkbnTKSGM3o52bQ==", + "version": "8.1.8", + "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.8.tgz", + "integrity": "sha512-b8BTP0cVTgWgc60H7LNfY7dZcEJVsgyCm52BsWOggwWapKAdli1T7ZaLJvnTAbVd8EY8+k4OAO1Z/ti1iirVOA==", "dev": true, "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.0", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/graphql-tag-pluck": "8.3.7", + "@graphql-tools/utils": "^10.6.2", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -1876,29 +1898,31 @@ } }, "node_modules/@graphql-tools/delegate": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.0.4.tgz", - "integrity": "sha512-WswZRbQZMh/ebhc8zSomK9DIh6Pd5KbuiMsyiKkKz37TWTrlCOe+4C/fyrBFez30ksq6oFyCeSKMwfrCbeGo0Q==", + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.2.7.tgz", + "integrity": "sha512-cHNRguTi/RGxLttmDR5F4698kVtoPnYCFjgEZh/sg8MGrejTiCpQeg+aXUqcj0efWmnKIkeia5JaqqbTGpc0xA==", "dev": true, "dependencies": { - "@graphql-tools/batch-execute": "^9.0.4", - "@graphql-tools/executor": "^1.2.1", - "@graphql-tools/schema": "^10.0.3", - "@graphql-tools/utils": "^10.0.13", - "dataloader": "^2.2.2", - "tslib": "^2.5.0" + "@graphql-tools/batch-execute": "^9.0.10", + "@graphql-tools/executor": "^1.3.6", + "@graphql-tools/schema": "^10.0.11", + "@graphql-tools/utils": "^10.6.2", + "@repeaterjs/repeater": "^3.0.6", + "dataloader": "^2.2.3", + "dset": "^3.1.2", + "tslib": "^2.8.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/documents": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/documents/-/documents-1.0.0.tgz", - "integrity": "sha512-rHGjX1vg/nZ2DKqRGfDPNC55CWZBMldEVcH+91BThRa6JeT80NqXknffLLEZLRUxyikCfkwMsk6xR3UNMqG0Rg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/documents/-/documents-1.0.1.tgz", + "integrity": "sha512-aweoMH15wNJ8g7b2r4C4WRuJxZ0ca8HtNO54rkye/3duxTkW4fGBEutCx03jCIr5+a1l+4vFJNP859QnAVBVCA==", "dev": true, "dependencies": { "lodash.sortby": "^4.7.0", @@ -1912,12 +1936,12 @@ } }, "node_modules/@graphql-tools/executor": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.2.4.tgz", - "integrity": "sha512-aCO/5LEAwyTWObAAfpLlwAjaOjTxRX6YNXcGW62mglQhPBy+j0fTc4desci/4nJ49l8FWETaTG0MZ1G/PqQslg==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.3.6.tgz", + "integrity": "sha512-ZmWsWdUhTez2b4w9NkmL4wpPb8n8WZmLOMIPTXH2A2yEe2nHrK/tk653JZXvZFtx2HrBIcoZD4Fe/STYWIR74Q==", "dev": true, "dependencies": { - "@graphql-tools/utils": "^10.1.1", + "@graphql-tools/utils": "^10.6.2", "@graphql-typed-document-node/core": "3.2.0", "@repeaterjs/repeater": "^3.0.4", "tslib": "^2.4.0", @@ -1931,57 +1955,87 @@ } }, "node_modules/@graphql-tools/executor-graphql-ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-1.1.2.tgz", - "integrity": "sha512-+9ZK0rychTH1LUv4iZqJ4ESbmULJMTsv3XlFooPUngpxZkk00q6LqHKJRrsLErmQrVaC7cwQCaRBJa0teK17Lg==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-1.3.5.tgz", + "integrity": "sha512-8BZf9a9SkaJAkF5Byb4ZdiwzCNoTrfl515m206XvCkCHM7dM1AwvX1rYZTrnJWgXgQUxhPjvll5vgciOe1APaA==", "dev": true, "dependencies": { - "@graphql-tools/utils": "^10.0.13", - "@types/ws": "^8.0.0", + "@graphql-tools/utils": "^10.6.2", + "@whatwg-node/disposablestack": "^0.0.5", "graphql-ws": "^5.14.0", "isomorphic-ws": "^5.0.0", - "tslib": "^2.4.0", - "ws": "^8.13.0" + "tslib": "^2.8.1", + "ws": "^8.17.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/executor-http": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.0.9.tgz", - "integrity": "sha512-+NXaZd2MWbbrWHqU4EhXcrDbogeiCDmEbrAN+rMn4Nu2okDjn2MTFDbTIab87oEubQCH4Te1wDkWPKrzXup7+Q==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.1.14.tgz", + "integrity": "sha512-y/j+fOTgkYSEDzLQ7xMErSfg6kgejVhG4yieKy1PXBaiDNN8t9MOUxEJDDtRDr/pFnvjTtm78UFo04I7S+m7BA==", "dev": true, "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.6.2", "@repeaterjs/repeater": "^3.0.4", - "@whatwg-node/fetch": "^0.9.0", + "@whatwg-node/disposablestack": "^0.0.5", + "@whatwg-node/fetch": "^0.10.1", "extract-files": "^11.0.0", "meros": "^1.2.1", - "tslib": "^2.4.0", + "tslib": "^2.8.1", "value-or-promise": "^1.0.12" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/fetch": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", + "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", + "dev": true, + "dependencies": { + "@whatwg-node/node-fetch": "^0.7.1", + "urlpattern-polyfill": "^10.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/node-fetch": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.4.tgz", + "integrity": "sha512-rvUtU/xKKl/av5EIwyqfw7w0R+hx+tQrlhpIyFr27MwJRlUb+xcYv97kOmp7FE/WmQ8s+Tb6bcD6W8o/s2pGWw==", + "dev": true, + "dependencies": { + "@kamilkisiela/fast-url-parser": "^1.1.4", + "@whatwg-node/disposablestack": "^0.0.5", + "busboy": "^1.6.0", + "fast-querystring": "^1.1.1", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@graphql-tools/executor-legacy-ws": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.0.6.tgz", - "integrity": "sha512-lDSxz9VyyquOrvSuCCnld3256Hmd+QI2lkmkEv7d4mdzkxkK4ddAWW1geQiWrQvWmdsmcnGGlZ7gDGbhEExwqg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.5.tgz", + "integrity": "sha512-iqN3NYpv4mGTOUUkhNOL0v9kskVHXl1BrzueRtDFaWznjO7qpwAUwCAih3AMHDNadLQdppkjIhOJB+YU8KCfsQ==", "dev": true, "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.6.2", "@types/ws": "^8.0.0", "isomorphic-ws": "^5.0.0", "tslib": "^2.4.0", - "ws": "^8.15.0" + "ws": "^8.17.1" }, "engines": { "node": ">=16.0.0" @@ -1991,15 +2045,15 @@ } }, "node_modules/@graphql-tools/git-loader": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.5.tgz", - "integrity": "sha512-P97/1mhruDiA6D5WUmx3n/aeGPLWj2+4dpzDOxFGGU+z9NcI/JdygMkeFpGZNHeJfw+kHfxgPcMPnxHcyhAoVA==", + "version": "8.0.12", + "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.12.tgz", + "integrity": "sha512-B65UbwMeR6TWwTzz5OS6iGuqSa1za/lbLO3buSwDs8+zxTpqrJljeKllG2EFk7g7D2OtTt3Tu9+itWkuIbqOUw==", "dev": true, "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.0", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/graphql-tag-pluck": "8.3.7", + "@graphql-tools/utils": "^10.6.2", "is-glob": "4.0.3", - "micromatch": "^4.0.4", + "micromatch": "^4.0.8", "tslib": "^2.4.0", "unixify": "^1.0.0" }, @@ -2011,16 +2065,16 @@ } }, "node_modules/@graphql-tools/github-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.1.tgz", - "integrity": "sha512-W4dFLQJ5GtKGltvh/u1apWRFKBQOsDzFxO9cJkOYZj1VzHCpRF43uLST4VbCfWve+AwBqOuKr7YgkHoxpRMkcg==", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.7.tgz", + "integrity": "sha512-p7aGLbOkwLTCKk/hSEJJgrSIhbwNS7SBhtYFPMa1uoga4I10xDJuGrUl8l9Jq2y953rtJA6/aGyVJs87Yn2hwA==", "dev": true, "dependencies": { "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/executor-http": "^1.0.9", - "@graphql-tools/graphql-tag-pluck": "^8.0.0", - "@graphql-tools/utils": "^10.0.13", - "@whatwg-node/fetch": "^0.9.0", + "@graphql-tools/executor-http": "^1.1.9", + "@graphql-tools/graphql-tag-pluck": "^8.3.7", + "@graphql-tools/utils": "^10.6.2", + "@whatwg-node/fetch": "^0.10.0", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" }, @@ -2031,14 +2085,43 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/fetch": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", + "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", + "dev": true, + "dependencies": { + "@whatwg-node/node-fetch": "^0.7.1", + "urlpattern-polyfill": "^10.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/node-fetch": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.4.tgz", + "integrity": "sha512-rvUtU/xKKl/av5EIwyqfw7w0R+hx+tQrlhpIyFr27MwJRlUb+xcYv97kOmp7FE/WmQ8s+Tb6bcD6W8o/s2pGWw==", + "dev": true, + "dependencies": { + "@kamilkisiela/fast-url-parser": "^1.1.4", + "@whatwg-node/disposablestack": "^0.0.5", + "busboy": "^1.6.0", + "fast-querystring": "^1.1.1", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@graphql-tools/graphql-file-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.1.tgz", - "integrity": "sha512-7gswMqWBabTSmqbaNyWSmRRpStWlcCkBc73E6NZNlh4YNuiyKOwbvSkOUYFOqFMfEL+cFsXgAvr87Vz4XrYSbA==", + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.6.tgz", + "integrity": "sha512-nLOvotKcvZLXQWryYl34vHI4Fr+VTA/y6WHcZ73gXBQ//8oGKgnuDNoAdi4rXgk4iGyIMvRxZpYU27k6Z4acBw==", "dev": true, "dependencies": { - "@graphql-tools/import": "7.0.1", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/import": "7.0.6", + "@graphql-tools/utils": "^10.6.2", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -2051,9 +2134,9 @@ } }, "node_modules/@graphql-tools/graphql-tag-pluck": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.0.tgz", - "integrity": "sha512-gNqukC+s7iHC7vQZmx1SEJQmLnOguBq+aqE2zV2+o1hxkExvKqyFli1SY/9gmukFIKpKutCIj+8yLOM+jARutw==", + "version": "8.3.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.7.tgz", + "integrity": "sha512-QoGf/8oVzhMZW+EbgpkM7zUxlNyv60Twb254R0D8TxS19OznoMMZMiDJdoID/k42QRoJ7o1V/yEOHgJFcqYHVw==", "dev": true, "dependencies": { "@babel/core": "^7.22.9", @@ -2061,7 +2144,7 @@ "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/traverse": "^7.16.8", "@babel/types": "^7.16.8", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.6.2", "tslib": "^2.4.0" }, "engines": { @@ -2072,12 +2155,12 @@ } }, "node_modules/@graphql-tools/import": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.1.tgz", - "integrity": "sha512-935uAjAS8UAeXThqHfYVr4HEAp6nHJ2sximZKO1RzUTq5WoALMAhhGARl0+ecm6X+cqNUwIChJbjtaa6P/ML0w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.6.tgz", + "integrity": "sha512-F28lG9w3gckZ+ubnq3jM2s2OiyH+cVZZXvOZ8RO/EJQ0dS+BE/S9zzvpCTuOWyuZvcLvbYBDjliZTOmeSQUhMg==", "dev": true, "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.6.2", "resolve-from": "5.0.0", "tslib": "^2.4.0" }, @@ -2089,12 +2172,12 @@ } }, "node_modules/@graphql-tools/json-file-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.1.tgz", - "integrity": "sha512-lAy2VqxDAHjVyqeJonCP6TUemrpYdDuKt25a10X6zY2Yn3iFYGnuIDQ64cv3ytyGY6KPyPB+Kp+ZfOkNDG3FQA==", + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.6.tgz", + "integrity": "sha512-mjZFVMtBL9fcvovwCoXKjZxXqr92/dcPZmHlQsW9jUC9WW6KfmolwtyvRxy9CcOjjh1HDTPcNoDgW05iI1CFYQ==", "dev": true, "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.6.2", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -2107,13 +2190,13 @@ } }, "node_modules/@graphql-tools/load": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.0.2.tgz", - "integrity": "sha512-S+E/cmyVmJ3CuCNfDuNF2EyovTwdWfQScXv/2gmvJOti2rGD8jTt9GYVzXaxhblLivQR9sBUCNZu/w7j7aXUCA==", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.0.7.tgz", + "integrity": "sha512-1JmZaMxs9LOyyq7XF/knBxY+Uejnc68+nILCFYwsts9KTUOZHpJqjleIIDf7Il1yHDaujjThX4Xqg2Dwhdb/bw==", "dev": true, "dependencies": { - "@graphql-tools/schema": "^10.0.3", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/schema": "^10.0.11", + "@graphql-tools/utils": "^10.6.2", "p-limit": "3.1.0", "tslib": "^2.4.0" }, @@ -2125,12 +2208,12 @@ } }, "node_modules/@graphql-tools/merge": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.3.tgz", - "integrity": "sha512-FeKv9lKLMwqDu0pQjPpF59GY3HReUkWXKsMIuMuJQOKh9BETu7zPEFUELvcw8w+lwZkl4ileJsHXC9+AnsT2Lw==", + "version": "9.0.12", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.12.tgz", + "integrity": "sha512-ECkUdgWkizhzQ6JJg16MCYnIN2r2+q/vP5smzi3YeeJkZ/3f9ynFDkaqoMg0Ddg9MugR03hMiQQrssk5f0389Q==", "dev": true, "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.6.2", "tslib": "^2.4.0" }, "engines": { @@ -2156,16 +2239,15 @@ } }, "node_modules/@graphql-tools/prisma-loader": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-8.0.3.tgz", - "integrity": "sha512-oZhxnMr3Jw2WAW1h9FIhF27xWzIB7bXWM8olz4W12oII4NiZl7VRkFw9IT50zME2Bqi9LGh9pkmMWkjvbOpl+Q==", + "version": "8.0.17", + "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-8.0.17.tgz", + "integrity": "sha512-fnuTLeQhqRbA156pAyzJYN0KxCjKYRU5bz1q/SKOwElSnAU4k7/G1kyVsWLh7fneY78LoMNH5n+KlFV8iQlnyg==", "dev": true, "dependencies": { - "@graphql-tools/url-loader": "^8.0.2", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/url-loader": "^8.0.15", + "@graphql-tools/utils": "^10.5.6", "@types/js-yaml": "^4.0.0", - "@types/json-stable-stringify": "^1.0.32", - "@whatwg-node/fetch": "^0.9.0", + "@whatwg-node/fetch": "^0.10.0", "chalk": "^4.1.0", "debug": "^4.3.1", "dotenv": "^16.0.0", @@ -2174,7 +2256,6 @@ "https-proxy-agent": "^7.0.0", "jose": "^5.0.0", "js-yaml": "^4.0.0", - "json-stable-stringify": "^1.0.1", "lodash": "^4.17.20", "scuid": "^1.1.0", "tslib": "^2.4.0", @@ -2187,12 +2268,40 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/fetch": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", + "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", + "dev": true, + "dependencies": { + "@whatwg-node/node-fetch": "^0.7.1", + "urlpattern-polyfill": "^10.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/node-fetch": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.4.tgz", + "integrity": "sha512-rvUtU/xKKl/av5EIwyqfw7w0R+hx+tQrlhpIyFr27MwJRlUb+xcYv97kOmp7FE/WmQ8s+Tb6bcD6W8o/s2pGWw==", + "dev": true, + "dependencies": { + "@kamilkisiela/fast-url-parser": "^1.1.4", + "@whatwg-node/disposablestack": "^0.0.5", + "busboy": "^1.6.0", + "fast-querystring": "^1.1.1", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@graphql-tools/prisma-loader/node_modules/graphql-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-typed-document-node/core": "^3.2.0", "cross-fetch": "^3.1.5" @@ -2202,13 +2311,13 @@ } }, "node_modules/@graphql-tools/relay-operation-optimizer": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.1.tgz", - "integrity": "sha512-y0ZrQ/iyqWZlsS/xrJfSir3TbVYJTYmMOu4TaSz6F4FRDTQ3ie43BlKkhf04rC28pnUOS4BO9pDcAo1D30l5+A==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.6.tgz", + "integrity": "sha512-hzzH1flmvL0o7tczQbnGVmsaLruhl8rxoqszo6uBjjjPxppoT0vwqIvU5X+lGJi2U+/fv3Q2FV3XALQB5Pmeaw==", "dev": true, "dependencies": { "@ardatan/relay-compiler": "12.0.0", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.6.2", "tslib": "^2.4.0" }, "engines": { @@ -2219,13 +2328,13 @@ } }, "node_modules/@graphql-tools/schema": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.3.tgz", - "integrity": "sha512-p28Oh9EcOna6i0yLaCFOnkcBDQECVf3SCexT6ktb86QNj9idnkhI+tCxnwZDh58Qvjd2nURdkbevvoZkvxzCog==", + "version": "10.0.11", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.11.tgz", + "integrity": "sha512-cYr/7SJSKtdwPByTKHlBr0tYGf7/sYNyzKlPhPMHWoYyGxtn8ytbfF6wEUcxuaOoqksIFxOGr+WOJh1WvShb6A==", "dev": true, "dependencies": { - "@graphql-tools/merge": "^9.0.3", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/merge": "^9.0.12", + "@graphql-tools/utils": "^10.6.2", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" }, @@ -2237,24 +2346,23 @@ } }, "node_modules/@graphql-tools/url-loader": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.2.tgz", - "integrity": "sha512-1dKp2K8UuFn7DFo1qX5c1cyazQv2h2ICwA9esHblEqCYrgf69Nk8N7SODmsfWg94OEaI74IqMoM12t7eIGwFzQ==", + "version": "8.0.18", + "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.18.tgz", + "integrity": "sha512-gz6oRoZzUJyBDIVMBKFa35InRqzq3FOb/kEb+8T3/DrDZCIxFlmLBZzy9ANjKmF3ctLn0WQXopRSaG/Wq7NEwA==", "dev": true, "dependencies": { "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/delegate": "^10.0.4", - "@graphql-tools/executor-graphql-ws": "^1.1.2", - "@graphql-tools/executor-http": "^1.0.9", - "@graphql-tools/executor-legacy-ws": "^1.0.6", - "@graphql-tools/utils": "^10.0.13", - "@graphql-tools/wrap": "^10.0.2", + "@graphql-tools/executor-graphql-ws": "^1.3.2", + "@graphql-tools/executor-http": "^1.1.9", + "@graphql-tools/executor-legacy-ws": "^1.1.5", + "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/wrap": "^10.0.16", "@types/ws": "^8.0.0", - "@whatwg-node/fetch": "^0.9.0", + "@whatwg-node/fetch": "^0.10.0", "isomorphic-ws": "^5.0.0", "tslib": "^2.4.0", "value-or-promise": "^1.0.11", - "ws": "^8.12.0" + "ws": "^8.17.1" }, "engines": { "node": ">=16.0.0" @@ -2263,14 +2371,43 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/fetch": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", + "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", + "dev": true, + "dependencies": { + "@whatwg-node/node-fetch": "^0.7.1", + "urlpattern-polyfill": "^10.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/node-fetch": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.4.tgz", + "integrity": "sha512-rvUtU/xKKl/av5EIwyqfw7w0R+hx+tQrlhpIyFr27MwJRlUb+xcYv97kOmp7FE/WmQ8s+Tb6bcD6W8o/s2pGWw==", + "dev": true, + "dependencies": { + "@kamilkisiela/fast-url-parser": "^1.1.4", + "@whatwg-node/disposablestack": "^0.0.5", + "busboy": "^1.6.0", + "fast-querystring": "^1.1.1", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@graphql-tools/utils": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.1.2.tgz", - "integrity": "sha512-fX13CYsDnX4yifIyNdiN0cVygz/muvkreWWem6BBw130+ODbRRgfiVveL0NizCEnKXkpvdeTy9Bxvo9LIKlhrw==", + "version": "10.6.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.6.2.tgz", + "integrity": "sha512-ABZHTpwiVR8oE2//NI/nnU3nNhbBpqMlMYyCF5cnqjLfhlyOdFfoRuhYEATEsmMfDg0ijGreULywK/SmepVGfw==", "dev": true, "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", - "cross-inspect": "1.0.0", + "cross-inspect": "1.0.1", "dset": "^3.1.2", "tslib": "^2.4.0" }, @@ -2282,19 +2419,18 @@ } }, "node_modules/@graphql-tools/wrap": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.5.tgz", - "integrity": "sha512-Cbr5aYjr3HkwdPvetZp1cpDWTGdD1Owgsb3z/ClzhmrboiK86EnQDxDvOJiQkDCPWE9lNBwj8Y4HfxroY0D9DQ==", + "version": "10.0.25", + "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.25.tgz", + "integrity": "sha512-51Koxi6IZHF4Ns7c6jvLU2x7GJyGGDL7V6e0u4J6ci/0vSCqLBwT3YYutDlZ7uJTpbLjEbjl0R0+1fOerdIkOQ==", "dev": true, "dependencies": { - "@graphql-tools/delegate": "^10.0.4", - "@graphql-tools/schema": "^10.0.3", - "@graphql-tools/utils": "^10.1.1", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "@graphql-tools/delegate": "^10.2.7", + "@graphql-tools/schema": "^10.0.11", + "@graphql-tools/utils": "^10.6.2", + "tslib": "^2.8.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" @@ -2314,7 +2450,6 @@ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, - "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -2364,15 +2499,13 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" + "dev": true }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -2386,11 +2519,10 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -2403,7 +2535,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -2416,7 +2547,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, - "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -2434,7 +2564,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -2450,7 +2579,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -2508,8 +2636,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -2537,8 +2664,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz", "integrity": "sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@mapbox/geojson-rewind": { "version": "0.5.2", @@ -2592,10 +2718,9 @@ } }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.3.1.tgz", - "integrity": "sha512-5ueL4UDitzVtceQ8J4kY+Px3WK+eZTsmGwha3MBKHKqiHvKrjWWwBCIl1K8BuJSc5OFh83uI8IFNoFvQxX2uUw==", - "license": "ISC", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz", + "integrity": "sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", @@ -2603,7 +2728,6 @@ "minimist": "^1.2.8", "quickselect": "^2.0.0", "rw": "^1.3.3", - "sort-object": "^3.0.3", "tinyqueue": "^3.0.0" }, "bin": { @@ -2612,6 +2736,11 @@ "gl-style-validate": "dist/gl-style-validate.mjs" } }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2653,7 +2782,6 @@ "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", @@ -2691,7 +2819,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -2712,7 +2839,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -2733,7 +2859,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -2754,7 +2879,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -2775,7 +2899,6 @@ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -2796,7 +2919,6 @@ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -2817,7 +2939,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -2838,7 +2959,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -2859,7 +2979,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -2880,7 +2999,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -2901,7 +3019,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -2922,7 +3039,6 @@ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -2943,7 +3059,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -2961,7 +3076,6 @@ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -2977,9 +3091,9 @@ } }, "node_modules/@react-aria/ssr": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.2.tgz", - "integrity": "sha512-0gKkgDYdnq1w+ey8KzG9l+H5Z821qh9vVjztk55rUg71vTk/Eaebeir+WtzcLLwTjw3m/asIjx8Y59y1lJZhBw==", + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz", + "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==", "dependencies": { "@swc/helpers": "^0.5.0" }, @@ -2987,13 +3101,13 @@ "node": ">= 12" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "node_modules/@repeaterjs/repeater": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.5.tgz", - "integrity": "sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", + "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==", "dev": true }, "node_modules/@restart/hooks": { @@ -3011,7 +3125,6 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.1.tgz", "integrity": "sha512-qghR21ynHiUrpcIkKCoKYB+3rJtezY5Y7ikrwradCL+7hZHdQ2Ozc5ffxtpmpahoAGgc31gyXaSx2sXXaThmqA==", - "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", "@popperjs/core": "^2.11.8", @@ -3032,7 +3145,6 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.0.tgz", "integrity": "sha512-wS+h6IusJCPjTkmOOrRZxIPICD/mtFA3PRZviutoM23/b7akyDGfZF/WS+nIFk27u7JDhPE2+0GBdZxjSqHZkg==", - "license": "MIT", "dependencies": { "dequal": "^2.0.3" }, @@ -3049,9 +3161,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz", - "integrity": "sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", + "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==", "cpu": [ "arm" ], @@ -3062,9 +3174,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz", - "integrity": "sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz", + "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==", "cpu": [ "arm64" ], @@ -3075,9 +3187,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz", - "integrity": "sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz", + "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==", "cpu": [ "arm64" ], @@ -3088,9 +3200,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz", - "integrity": "sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz", + "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==", "cpu": [ "x64" ], @@ -3101,9 +3213,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz", - "integrity": "sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz", + "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==", "cpu": [ "arm64" ], @@ -3114,9 +3226,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz", - "integrity": "sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz", + "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==", "cpu": [ "x64" ], @@ -3127,9 +3239,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz", - "integrity": "sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz", + "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==", "cpu": [ "arm" ], @@ -3140,9 +3252,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz", - "integrity": "sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz", + "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==", "cpu": [ "arm" ], @@ -3153,9 +3265,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz", - "integrity": "sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz", + "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==", "cpu": [ "arm64" ], @@ -3166,9 +3278,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz", - "integrity": "sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz", + "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==", "cpu": [ "arm64" ], @@ -3178,10 +3290,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz", + "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz", - "integrity": "sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz", + "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==", "cpu": [ "ppc64" ], @@ -3192,9 +3317,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz", - "integrity": "sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz", + "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==", "cpu": [ "riscv64" ], @@ -3205,9 +3330,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz", - "integrity": "sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz", + "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==", "cpu": [ "s390x" ], @@ -3218,9 +3343,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz", - "integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz", + "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==", "cpu": [ "x64" ], @@ -3231,9 +3356,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz", - "integrity": "sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz", + "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==", "cpu": [ "x64" ], @@ -3244,9 +3369,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz", - "integrity": "sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz", + "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==", "cpu": [ "arm64" ], @@ -3257,9 +3382,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz", - "integrity": "sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz", + "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==", "cpu": [ "ia32" ], @@ -3270,9 +3395,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz", - "integrity": "sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz", + "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==", "cpu": [ "x64" ], @@ -3286,21 +3411,20 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@swc/helpers": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.7.tgz", - "integrity": "sha512-BVvNZhx362+l2tSwSuyEUV4h7+jk9raNdoTSdLfwTshXJSaGmYKluGRJznziCI3KX02Z19DdsQrdfrpXAU3Hfg==", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.8.0" } }, "node_modules/@testing-library/dom": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.0.0.tgz", - "integrity": "sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, "peer": true, "dependencies": { @@ -3318,11 +3442,10 @@ } }, "node_modules/@testing-library/react": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", - "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.1.0.tgz", + "integrity": "sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" }, @@ -3331,10 +3454,10 @@ }, "peerDependencies": { "@testing-library/dom": "^10.0.0", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -3385,9 +3508,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" @@ -3400,9 +3523,9 @@ "dev": true }, "node_modules/@types/geojson": { - "version": "7946.0.14", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", - "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + "version": "7946.0.15", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz", + "integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==" }, "node_modules/@types/geojson-vt": { "version": "3.2.5", @@ -3418,12 +3541,6 @@ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "dev": true }, - "node_modules/@types/json-stable-stringify": { - "version": "1.0.36", - "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.36.tgz", - "integrity": "sha512-b7bq23s4fgBB76n34m2b3RBf6M369B0Z9uRR8aHTMd8kZISRkmDEpPD8hhpYvDFzr3bJCPES96cm3Q6qRNDbQw==", - "dev": true - }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -3446,20 +3563,20 @@ } }, "node_modules/@types/mapbox-gl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-3.1.0.tgz", - "integrity": "sha512-hI6cQDjw1bkJw7MC/eHMqq5TWUamLwsujnUUeiIX2KDRjxRNSYMjnHz07+LATz9I9XIsKumOtUz4gRYnZOJ/FA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-3.4.1.tgz", + "integrity": "sha512-NsGKKtgW93B+UaLPti6B7NwlxYlES5DpV5Gzj9F75rK5ALKsqSk15CiEHbOnTr09RGbr6ZYiCdI+59NNNcAImg==", "dependencies": { "@types/geojson": "*" } }, "node_modules/@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", "dev": true, "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.20.0" } }, "node_modules/@types/pbf": { @@ -3467,35 +3584,27 @@ "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" - }, "node_modules/@types/react": { - "version": "18.3.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", - "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", - "license": "MIT", + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.1.tgz", + "integrity": "sha512-YW6614BDhqbpR5KtUYzTA+zlA7nayzJRA9ljz9CQoxthR0sDisYZLuvSMsil36t4EH/uAt8T52Xb4sVw17G+SQ==", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.1.tgz", + "integrity": "sha512-hljHij7MpWPKF6u5vojuyfV0YA4YURsQG7KT6SzV0Zs2BXAtgdTxG6A229Ub/xiWV4w/7JL8fi6aAyjshH4meA==", "dev": true, - "license": "MIT", "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", + "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", "dependencies": { "@types/react": "*" } @@ -3514,9 +3623,9 @@ "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" }, "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", "dev": true, "dependencies": { "@types/node": "*" @@ -3527,7 +3636,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -3561,7 +3669,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", @@ -3590,7 +3697,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0" @@ -3608,7 +3714,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/typescript-estree": "7.18.0", "@typescript-eslint/utils": "7.18.0", @@ -3636,7 +3741,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -3650,7 +3754,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0", @@ -3679,7 +3782,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3692,7 +3794,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -3715,7 +3816,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" @@ -3729,9 +3829,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", "dev": true }, "node_modules/@vitejs/plugin-react": { @@ -3758,7 +3858,6 @@ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz", "integrity": "sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^0.2.3", @@ -3791,7 +3890,6 @@ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", "dev": true, - "license": "MIT", "dependencies": { "@vitest/spy": "2.1.8", "@vitest/utils": "2.1.8", @@ -3807,7 +3905,6 @@ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", "dev": true, - "license": "MIT", "dependencies": { "tinyrainbow": "^1.2.0" }, @@ -3820,7 +3917,6 @@ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", "dev": true, - "license": "MIT", "dependencies": { "@vitest/utils": "2.1.8", "pathe": "^1.1.2" @@ -3834,7 +3930,6 @@ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", "dev": true, - "license": "MIT", "dependencies": { "@vitest/pretty-format": "2.1.8", "magic-string": "^0.30.12", @@ -3849,7 +3944,6 @@ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", "dev": true, - "license": "MIT", "dependencies": { "tinyspy": "^3.0.2" }, @@ -3862,7 +3956,6 @@ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", "dev": true, - "license": "MIT", "dependencies": { "@vitest/pretty-format": "2.1.8", "loupe": "^3.1.2", @@ -3872,14 +3965,25 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@whatwg-node/disposablestack": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@whatwg-node/disposablestack/-/disposablestack-0.0.5.tgz", + "integrity": "sha512-9lXugdknoIequO4OYvIjhygvfSEgnO8oASLqLelnDhkRjgBZhc39shC3QSlZuyDO9bgYSIVa2cHAiN+St3ty4w==", + "dev": true, + "dependencies": { + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@whatwg-node/fetch": { - "version": "0.9.21", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.21.tgz", - "integrity": "sha512-Wt0jPb+04JjobK0pAAN7mEHxVHcGA9HoP3OyCsZtyAecNQeADXCZ1MihFwVwjsgaRYuGVmNlsCmLxlG6mor8Gw==", + "version": "0.9.23", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.23.tgz", + "integrity": "sha512-7xlqWel9JsmxahJnYVUj/LLxWcnA93DR4c9xlw3U814jWTiYalryiH1qToik1hOxweKKRLi4haXHM5ycRksPBA==", "dev": true, - "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.5.23", + "@whatwg-node/node-fetch": "^0.6.0", "urlpattern-polyfill": "^10.0.0" }, "engines": { @@ -3887,11 +3991,10 @@ } }, "node_modules/@whatwg-node/node-fetch": { - "version": "0.5.26", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.26.tgz", - "integrity": "sha512-4jXDeZ4IH4bylZ6wu14VEx0aDXXhrN4TC279v9rPmn08g4EYekcYf8wdcOOnS9STjDkb6x77/6xBUTqxGgjr8g==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.6.0.tgz", + "integrity": "sha512-tcZAhrpx6oVlkEsRngeTEEE7I5/QdLjeEz4IlekabGaESP7+Dkm/6a9KcF1KdCBB7mO9PXtBkwCuTCt8+UPg8Q==", "dev": true, - "license": "MIT", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "busboy": "^1.6.0", @@ -3902,17 +4005,10 @@ "node": ">=18.0.0" } }, - "node_modules/@whatwg-node/node-fetch/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", - "dev": true, - "license": "0BSD" - }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3931,13 +4027,10 @@ } }, "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -4160,7 +4253,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -4205,7 +4297,6 @@ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" } @@ -4267,11 +4358,10 @@ } }, "node_modules/axe-core": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", - "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", "dev": true, - "license": "MPL-2.0", "engines": { "node": ">=4" } @@ -4281,7 +4371,6 @@ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">= 0.4" } @@ -4390,27 +4479,26 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -4426,12 +4514,11 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -4512,22 +4599,34 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, "node_modules/callsites": { @@ -4559,9 +4658,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001669", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", - "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "version": "1.0.30001687", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", + "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", "dev": true, "funding": [ { @@ -4576,8 +4675,7 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/capital-case": { "version": "1.0.4", @@ -4595,7 +4693,6 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", "dev": true, - "license": "MIT", "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", @@ -4672,7 +4769,6 @@ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 16" } @@ -4884,9 +4980,9 @@ } }, "node_modules/cross-inspect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.0.tgz", - "integrity": "sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", "dev": true, "dependencies": { "tslib": "^2.4.0" @@ -4896,9 +4992,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -4914,7 +5010,6 @@ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", "dev": true, - "license": "MIT", "dependencies": { "rrweb-cssom": "^0.7.1" }, @@ -4998,9 +5093,9 @@ } }, "node_modules/dataloader": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", - "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", + "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", "dev": true }, "node_modules/debounce": { @@ -5010,11 +5105,10 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -5047,7 +5141,6 @@ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -5202,9 +5295,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "dev": true, "engines": { "node": ">=12" @@ -5214,33 +5307,44 @@ } }, "node_modules/dset": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.3.tgz", - "integrity": "sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", "dev": true, "engines": { "node": ">=4" } }, + "node_modules/dunder-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", + "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/earcut": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.0.tgz", - "integrity": "sha512-41Fs7Q/PLq1SDbqjsgcY7GA42T0jvaCNGXgGtsNdvg+Yv8eIu06bxv4/PoREkZ9nMDNwnUSG9OFB9+yv8eKhDg==", - "license": "ISC" + "integrity": "sha512-41Fs7Q/PLq1SDbqjsgcY7GA42T0jvaCNGXgGtsNdvg+Yv8eIu06bxv4/PoREkZ9nMDNwnUSG9OFB9+yv8eKhDg==" }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.40", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.40.tgz", - "integrity": "sha512-LYm78o6if4zTasnYclgQzxEcgMoIcybWOhkATWepN95uwVVWV0/IW10v+2sIeHE+bIYWipLneTftVyQm45UY7g==", - "dev": true, - "license": "ISC" + "version": "1.5.71", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz", + "integrity": "sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==", + "dev": true }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -5270,11 +5374,10 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", + "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", @@ -5291,7 +5394,7 @@ "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.4", "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", + "globalthis": "^1.0.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", @@ -5307,10 +5410,10 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", + "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", @@ -5331,13 +5434,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -5352,11 +5452,10 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz", - "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", + "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -5366,6 +5465,7 @@ "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "globalthis": "^1.0.4", + "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", @@ -5381,8 +5481,7 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/es-object-atoms": { "version": "1.0.0", @@ -5420,14 +5519,14 @@ } }, "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -5480,7 +5579,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -5501,8 +5599,8 @@ "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5590,7 +5688,6 @@ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -5617,7 +5714,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, - "license": "MIT", "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -5651,7 +5747,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5662,7 +5757,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -5672,7 +5766,6 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -5685,7 +5778,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5698,7 +5790,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, - "license": "MIT", "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -5728,7 +5819,6 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">= 0.4" } @@ -5738,7 +5828,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5749,7 +5838,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5762,7 +5850,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -5795,7 +5882,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz", "integrity": "sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -5808,7 +5894,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.16.tgz", "integrity": "sha512-slterMlxAhov/DZO8NScf6mEeMBBXodFUolijDvrtTxyezyLoTQaa73FyYus/VbTdftd8wBgBxPMRk3poleXNQ==", "dev": true, - "license": "MIT", "peerDependencies": { "eslint": ">=8.40" } @@ -5818,7 +5903,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5829,7 +5913,6 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -5842,7 +5925,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5855,7 +5937,6 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -5901,7 +5982,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5912,7 +5992,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -5928,7 +6007,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5941,7 +6019,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -5967,9 +6044,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -6022,7 +6099,6 @@ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } @@ -6068,8 +6144,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -6122,7 +6197,6 @@ "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", "dev": true, - "license": "MIT", "dependencies": { "fast-decode-uri-component": "^1.0.1" } @@ -6203,9 +6277,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -6245,9 +6319,9 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true }, "node_modules/for-each": { @@ -6260,11 +6334,10 @@ } }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, - "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -6281,7 +6354,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "ISC", "engines": { "node": ">=14" }, @@ -6290,9 +6362,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, "dependencies": { "asynckit": "^0.4.0", @@ -6371,8 +6443,7 @@ "node_modules/geojson-vt": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", - "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", - "license": "ISC" + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==" }, "node_modules/get-caller-file": { "version": "2.0.5", @@ -6384,16 +6455,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz", + "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==", "dev": true, "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -6447,6 +6521,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -6501,7 +6576,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", - "license": "MIT", "dependencies": { "ini": "^4.1.3", "kind-of": "^6.0.3", @@ -6515,7 +6589,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "license": "ISC", "engines": { "node": ">=16" } @@ -6524,7 +6597,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -6549,7 +6621,6 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -6582,12 +6653,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6603,7 +6674,6 @@ "version": "16.9.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -6613,7 +6683,6 @@ "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-5.1.3.tgz", "integrity": "sha512-RBhejsPjrNSuwtckRlilWzLVt2j8itl74W9Gke1KejDTz7oaA5kVd6wRn9zK9TS5mcmIYGxf7zN7a1ORMdxp1Q==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-tools/graphql-file-loader": "^8.0.0", "@graphql-tools/json-file-loader": "^8.0.0", @@ -6641,11 +6710,10 @@ } }, "node_modules/graphql-config/node_modules/jiti": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.3.3.tgz", - "integrity": "sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.1.tgz", + "integrity": "sha512-yPBThwecp1wS9DmoA4x4KR2h3QoslacnDR8ypuFM962kI4/456Iy1oHx2RAgh4jfZNdn0bctsdadceiBUgpU1g==", "dev": true, - "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -6654,7 +6722,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-7.1.2.tgz", "integrity": "sha512-+XE3iuC55C2di5ZUrB4pjgwe+nIQBuXVIK9J98wrVwojzDW3GMdSBZfxUk8l4j9TieIpjpggclxhNEU9ebGF8w==", - "license": "MIT", "dependencies": { "@graphql-typed-document-node/core": "^3.2.0" }, @@ -6678,9 +6745,9 @@ } }, "node_modules/graphql-ws": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.15.0.tgz", - "integrity": "sha512-xWGAtm3fig9TIhSaNsg0FaDZ8Pyn/3re3RFlP4rhQcmjRDIPpk1EhRuNB+YSJtLzttyuToaDiNhwT1OMoGnJnw==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz", + "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", "dev": true, "engines": { "node": ">=10" @@ -6720,10 +6787,13 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -6732,9 +6802,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "engines": { "node": ">= 0.4" @@ -6812,13 +6882,12 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, - "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -6857,9 +6926,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -6933,6 +7002,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -6949,7 +7019,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -7042,7 +7111,6 @@ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", "dev": true, - "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7054,25 +7122,28 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.0.tgz", + "integrity": "sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7098,7 +7169,6 @@ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, - "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -7157,13 +7227,15 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz", + "integrity": "sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7183,7 +7255,6 @@ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dev": true, - "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7258,12 +7329,13 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.0.tgz", + "integrity": "sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7299,13 +7371,15 @@ "dev": true }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.0.tgz", + "integrity": "sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "gopd": "^1.1.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -7354,12 +7428,13 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.0.tgz", + "integrity": "sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7369,12 +7444,14 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.0.tgz", + "integrity": "sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "call-bind": "^1.0.7", + "has-symbols": "^1.0.3", + "safe-regex-test": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -7537,7 +7614,6 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", @@ -7565,7 +7641,6 @@ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "get-intrinsic": "^1.2.1", @@ -7582,7 +7657,6 @@ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -7594,18 +7668,18 @@ } }, "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true, "bin": { "jiti": "bin/jiti.js" } }, "node_modules/jose": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.3.tgz", - "integrity": "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA==", + "version": "5.9.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz", + "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/panva" @@ -7638,7 +7712,6 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", "dev": true, - "license": "MIT", "dependencies": { "cssstyle": "^4.1.0", "data-urls": "^5.0.0", @@ -7679,7 +7752,6 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, - "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -7705,24 +7777,6 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "node_modules/json-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", - "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -7759,15 +7813,6 @@ "node": ">=6" } }, - "node_modules/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -7806,9 +7851,9 @@ } }, "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "dev": true }, "node_modules/language-tags": { @@ -7985,8 +8030,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/lower-case": { "version": "2.0.2", @@ -8026,11 +8070,10 @@ } }, "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "version": "0.30.14", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.14.tgz", + "integrity": "sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } @@ -8040,7 +8083,6 @@ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", @@ -8062,26 +8104,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/make-dir/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -8089,12 +8116,6 @@ "node": ">=10" } }, - "node_modules/make-dir/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -8108,7 +8129,6 @@ "version": "4.7.1", "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.1.tgz", "integrity": "sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA==", - "license": "BSD-3-Clause", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", @@ -8145,12 +8165,6 @@ "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" } }, - "node_modules/maplibre-gl/node_modules/quickselect": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", - "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", - "license": "ISC" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -8178,12 +8192,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -8225,7 +8239,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8249,7 +8262,6 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -8258,8 +8270,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/murmurhash-js": { "version": "1.0.0", @@ -8307,13 +8318,10 @@ } }, "node_modules/node-addon-api": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", - "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", - "dev": true, - "engines": { - "node": "^16 || ^18 || >= 20" - } + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true }, "node_modules/node-fetch": { "version": "2.7.0", @@ -8367,8 +8375,7 @@ "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/normalize-path": { "version": "2.1.1", @@ -8389,11 +8396,10 @@ "dev": true }, "node_modules/nwsapi": { - "version": "2.2.12", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz", - "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==", - "dev": true, - "license": "MIT" + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "dev": true }, "node_modules/object-assign": { "version": "4.1.1", @@ -8404,10 +8410,13 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8527,17 +8536,17 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -8630,11 +8639,10 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true, - "license": "BlueOak-1.0.0" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true }, "node_modules/param-case": { "version": "3.0.4", @@ -8691,12 +8699,12 @@ } }, "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "dev": true, "dependencies": { - "entities": "^4.4.0" + "entities": "^4.5.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -8781,7 +8789,6 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -8797,8 +8804,7 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/path-type": { "version": "4.0.0", @@ -8820,7 +8826,6 @@ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 14.16" } @@ -8829,7 +8834,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", - "license": "BSD-3-Clause", "dependencies": { "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" @@ -8912,7 +8916,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", "dev": true, - "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -8997,6 +9000,15 @@ "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9018,17 +9030,14 @@ ] }, "node_modules/quickselect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", - "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==" }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "engines": { "node": ">=0.10.0" } @@ -9037,7 +9046,6 @@ "version": "2.10.6", "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.6.tgz", "integrity": "sha512-fNvKytSp0nHts1WRnRBJeBEt+I9/ZdrnhIjWOucEduRNvFRU1IXjZueDdWnBiqsTSJ7MckQJi9i/hxGolaRq+g==", - "license": "MIT", "dependencies": { "@babel/runtime": "^7.24.7", "@restart/hooks": "^0.4.9", @@ -9064,15 +9072,14 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.25.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.0.0" } }, "node_modules/react-is": { @@ -9138,7 +9145,6 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -9173,19 +9179,19 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", + "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", + "dunder-proto": "^1.0.0", + "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.0" }, "engines": { "node": ">= 0.4" @@ -9200,15 +9206,15 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -9322,15 +9328,16 @@ } }, "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.1.3" @@ -9343,9 +9350,9 @@ } }, "node_modules/rollup": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz", - "integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz", + "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==", "dev": true, "dependencies": { "@types/estree": "1.0.6" @@ -9358,24 +9365,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.27.4", - "@rollup/rollup-android-arm64": "4.27.4", - "@rollup/rollup-darwin-arm64": "4.27.4", - "@rollup/rollup-darwin-x64": "4.27.4", - "@rollup/rollup-freebsd-arm64": "4.27.4", - "@rollup/rollup-freebsd-x64": "4.27.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.27.4", - "@rollup/rollup-linux-arm-musleabihf": "4.27.4", - "@rollup/rollup-linux-arm64-gnu": "4.27.4", - "@rollup/rollup-linux-arm64-musl": "4.27.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4", - "@rollup/rollup-linux-riscv64-gnu": "4.27.4", - "@rollup/rollup-linux-s390x-gnu": "4.27.4", - "@rollup/rollup-linux-x64-gnu": "4.27.4", - "@rollup/rollup-linux-x64-musl": "4.27.4", - "@rollup/rollup-win32-arm64-msvc": "4.27.4", - "@rollup/rollup-win32-ia32-msvc": "4.27.4", - "@rollup/rollup-win32-x64-msvc": "4.27.4", + "@rollup/rollup-android-arm-eabi": "4.28.1", + "@rollup/rollup-android-arm64": "4.28.1", + "@rollup/rollup-darwin-arm64": "4.28.1", + "@rollup/rollup-darwin-x64": "4.28.1", + "@rollup/rollup-freebsd-arm64": "4.28.1", + "@rollup/rollup-freebsd-x64": "4.28.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.28.1", + "@rollup/rollup-linux-arm-musleabihf": "4.28.1", + "@rollup/rollup-linux-arm64-gnu": "4.28.1", + "@rollup/rollup-linux-arm64-musl": "4.28.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.28.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1", + "@rollup/rollup-linux-riscv64-gnu": "4.28.1", + "@rollup/rollup-linux-s390x-gnu": "4.28.1", + "@rollup/rollup-linux-x64-gnu": "4.28.1", + "@rollup/rollup-linux-x64-musl": "4.28.1", + "@rollup/rollup-win32-arm64-msvc": "4.28.1", + "@rollup/rollup-win32-ia32-msvc": "4.28.1", + "@rollup/rollup-win32-x64-msvc": "4.28.1", "fsevents": "~2.3.2" } }, @@ -9383,8 +9391,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/run-async": { "version": "2.4.1", @@ -9506,12 +9513,9 @@ } }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==" }, "node_modules/scuid": { "version": "1.1.0", @@ -9619,10 +9623,13 @@ } }, "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9733,7 +9740,6 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -9791,8 +9797,7 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/streamsearch": { "version": "1.1.0", @@ -9838,7 +9843,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -9852,8 +9856,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", @@ -9866,7 +9869,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -9907,7 +9909,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -9980,7 +9981,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -10061,7 +10061,6 @@ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", "dev": true, - "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^10.4.1", @@ -10076,7 +10075,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, - "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -10108,22 +10106,19 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/tinyexec": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/tinypool": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", - "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", "dev": true, - "license": "MIT", "engines": { "node": "^18.0.0 || >=20.0.0" } @@ -10131,15 +10126,13 @@ "node_modules/tinyqueue": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", - "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", - "license": "ISC" + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==" }, "node_modules/tinyrainbow": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -10149,7 +10142,6 @@ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -10164,24 +10156,22 @@ } }, "node_modules/tldts": { - "version": "6.1.52", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.52.tgz", - "integrity": "sha512-fgrDJXDjbAverY6XnIt0lNfv8A0cf7maTEaZxNykLGsLG7XP+5xhjBTrt/ieAsFjAlZ+G5nmXomLcZDkxXnDzw==", + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.66.tgz", + "integrity": "sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ==", "dev": true, - "license": "MIT", "dependencies": { - "tldts-core": "^6.1.52" + "tldts-core": "^6.1.66" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.52", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.52.tgz", - "integrity": "sha512-j4OxQI5rc1Ve/4m/9o2WhWSC4jGc4uVbCINdOEJRAraCi0YqTqgMcxUx7DbmuP0G3PCixoof/RZB0Q5Kh9tagw==", - "dev": true, - "license": "MIT" + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.66.tgz", + "integrity": "sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g==", + "dev": true }, "node_modules/tmp": { "version": "0.0.33", @@ -10212,7 +10202,6 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "tldts": "^6.1.32" }, @@ -10232,19 +10221,10 @@ "node": ">=18" } }, - "node_modules/tr46/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "engines": { "node": ">=16" @@ -10254,9 +10234,9 @@ } }, "node_modules/ts-log": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz", - "integrity": "sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.7.tgz", + "integrity": "sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg==", "dev": true }, "node_modules/tsconfig-paths": { @@ -10284,9 +10264,9 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/type-check": { "version": "0.4.0", @@ -10346,9 +10326,9 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz", + "integrity": "sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", @@ -10356,7 +10336,8 @@ "for-each": "^0.3.3", "gopd": "^1.0.1", "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.13", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -10366,17 +10347,17 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -10390,7 +10371,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10413,9 +10393,9 @@ "integrity": "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg==" }, "node_modules/ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.39.tgz", + "integrity": "sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==", "dev": true, "funding": [ { @@ -10431,6 +10411,9 @@ "url": "https://github.com/sponsors/faisalman" } ], + "bin": { + "ua-parser-js": "script/cli.js" + }, "engines": { "node": "*" } @@ -10474,9 +10457,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true }, "node_modules/union-value": { @@ -10524,7 +10507,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.0" @@ -10563,21 +10545,11 @@ "punycode": "^2.1.0" } }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/urlpattern-polyfill": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -10599,7 +10571,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.2.tgz", "integrity": "sha512-XdQ+VsY2tJpBsKGs0wf3U/+azx8BBpYRHFAyKm5VeEZNOJZRB63q7Sc8Iup3k0TrN3KO6QgyzFf+opSbfY1y0g==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.24.0", "postcss": "^8.4.49", @@ -10671,7 +10642,6 @@ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", "dev": true, - "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", @@ -11159,7 +11129,6 @@ "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", "dev": true, - "license": "MIT", "dependencies": { "@vitest/expect": "2.1.8", "@vitest/mocker": "2.1.8", @@ -11228,7 +11197,6 @@ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "aix" @@ -11245,7 +11213,6 @@ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -11262,7 +11229,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -11279,7 +11245,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -11296,7 +11261,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -11313,7 +11277,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -11330,7 +11293,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -11347,7 +11309,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -11364,7 +11325,6 @@ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -11381,7 +11341,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -11398,7 +11357,6 @@ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -11415,7 +11373,6 @@ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -11432,7 +11389,6 @@ "mips64el" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -11449,7 +11405,6 @@ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -11466,7 +11421,6 @@ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -11483,7 +11437,6 @@ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -11500,7 +11453,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -11517,7 +11469,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -11534,7 +11485,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -11551,7 +11501,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "sunos" @@ -11568,7 +11517,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -11585,7 +11533,6 @@ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -11602,7 +11549,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -11616,7 +11562,6 @@ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", "dev": true, - "license": "MIT", "dependencies": { "@vitest/spy": "2.1.8", "estree-walker": "^3.0.3", @@ -11644,7 +11589,6 @@ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -11682,7 +11626,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -11819,9 +11762,9 @@ } }, "node_modules/whatwg-url": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", - "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", "dev": true, "dependencies": { "tr46": "^5.0.0", @@ -11847,33 +11790,36 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz", + "integrity": "sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==", "dev": true, "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.0", + "is-number-object": "^1.1.0", + "is-string": "^1.1.0", + "is-symbol": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", - "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.0.tgz", + "integrity": "sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==", "dev": true, - "license": "MIT", "dependencies": { + "call-bind": "^1.0.7", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.1.4", "is-weakref": "^1.0.2", @@ -11914,9 +11860,9 @@ "dev": true }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", + "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", @@ -11937,7 +11883,6 @@ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, - "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" @@ -11949,6 +11894,15 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -11969,7 +11923,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -11993,7 +11946,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" }, diff --git a/client/package.json b/client/package.json index 5aa6681a44b..92747c2fbb7 100644 --- a/client/package.json +++ b/client/package.json @@ -33,7 +33,7 @@ "@graphql-codegen/client-preset": "4.5.1", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.5.0", - "@testing-library/react": "16.0.1", + "@testing-library/react": "16.1.0", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", "@typescript-eslint/eslint-plugin": "7.18.0", From b22001acea630e1a016f8780186573bb07d692b1 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Dec 2024 12:04:57 +0200 Subject: [PATCH 105/162] Add doc for refetching in GraphQL --- DEVELOPMENT_DECISION_RECORDS.md | 4 ++-- doc/dev/decisionrecords/APIGraphQLDesign.md | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/DEVELOPMENT_DECISION_RECORDS.md b/DEVELOPMENT_DECISION_RECORDS.md index fc12950901b..a0e0554d4de 100644 --- a/DEVELOPMENT_DECISION_RECORDS.md +++ b/DEVELOPMENT_DECISION_RECORDS.md @@ -106,5 +106,5 @@ Prefer immutable types over mutable. Use builders where appropriate. See ## GraphQL Best Practices - API Design -[Follow best practices for designing GraphQL APIs. Our APIs are used by hundreds of clients -(web-pages/apps/services) and need to be backwards compatible.](doc/dev/decisionrecords/APIGraphQLDesign.md) \ No newline at end of file +[Follow best practices for designing GraphQL APIs. Our APIs need to be backwards compatible as they are used +by hundreds of clients (web-pages/apps/services).](doc/dev/decisionrecords/APIGraphQLDesign.md) \ No newline at end of file diff --git a/doc/dev/decisionrecords/APIGraphQLDesign.md b/doc/dev/decisionrecords/APIGraphQLDesign.md index c9546ddf6f3..1447f7b0c96 100644 --- a/doc/dev/decisionrecords/APIGraphQLDesign.md +++ b/doc/dev/decisionrecords/APIGraphQLDesign.md @@ -1,16 +1,23 @@ # GraphQL Best Practices - API Design -Follow best practices for designing GraphQL APIs. Our APIs are used by hundreds of clients -(web-pages/apps/services) and need to be backwards compatible. A good reference used by several -of the OTP developers are the Production Ready GraphQL book by Marc-André Giroux. +Follow best practices for designing GraphQL APIs. Our APIs need to be backwards compatible as they +are used by hundreds of clients (web-pages/apps/services). A good reference used by several +of the OTP developers is the Production Ready GraphQL book by Marc-André Giroux. ## Pagination We use the [pagination](https://graphql.org/learn/pagination/) (a.k. Relay) specification for paging over entities like stations, -stops, trips and routes. Very often OTP has a _finite_ list of entities in memory. For non-entities -(Itinerary and Legs), witch do not always have an ID and is none trivial to reconstruct, it is -better to make a custom solution. +stops, trips and routes. Very often OTP has a _finite_ list of entities in memory. + + +## Refetching + +We often use `type(id)` format queries for fetching or refetching an entities or value objects of type by id. Additionally, +the GTFS GraphQL API has a node interface and query for refetching objects which follow the +[GraphQL Global Object Identification Specification](https://relay.dev/graphql/objectidentification.htm). We should not use the +node interface or query for non-entities (such as Itineraries and Legs) which do not always have an ID and/or which IDs are not +trivial to reconstruct. ## Consistency @@ -21,6 +28,6 @@ new features, consider what is best; To follow the existing style or follow the ### Context and Problem Statement -Our APIs are used by hundreds of clients(web-pages/apps/services) and need to backwards compatible. +Our APIs need to be backwards compatible as they are used by hundreds of clients (web-pages/apps/services) Correcting mistakes may not be possible or may take a long time. From e0422cd145c1a021279774e3d7a68102510f0945 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Mon, 9 Dec 2024 10:12:38 +0000 Subject: [PATCH 106/162] Upgrade debug client to version 2024/12/2024-12-09T10:11 --- application/src/client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/client/index.html b/application/src/client/index.html index b34591918d8..23748ea08a7 100644 --- a/application/src/client/index.html +++ b/application/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug - - + +
From 2e7720ff090c52ffa91c3701476a63e0789dfa75 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Dec 2024 12:41:20 +0200 Subject: [PATCH 107/162] Update doc/dev/decisionrecords/APIGraphQLDesign.md Co-authored-by: Leonard Ehrenfried --- doc/dev/decisionrecords/APIGraphQLDesign.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/dev/decisionrecords/APIGraphQLDesign.md b/doc/dev/decisionrecords/APIGraphQLDesign.md index 1447f7b0c96..c4dfc6fbd5e 100644 --- a/doc/dev/decisionrecords/APIGraphQLDesign.md +++ b/doc/dev/decisionrecords/APIGraphQLDesign.md @@ -13,7 +13,7 @@ stops, trips and routes. Very often OTP has a _finite_ list of entities in memor ## Refetching -We often use `type(id)` format queries for fetching or refetching an entities or value objects of type by id. Additionally, +We often use `type(id)` format queries for fetching or refetching entities or value objects of type by id. Additionally, the GTFS GraphQL API has a node interface and query for refetching objects which follow the [GraphQL Global Object Identification Specification](https://relay.dev/graphql/objectidentification.htm). We should not use the node interface or query for non-entities (such as Itineraries and Legs) which do not always have an ID and/or which IDs are not From 6f5b5df790faef7e95c33677b8dac3501969767d Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Mon, 9 Dec 2024 12:45:54 +0200 Subject: [PATCH 108/162] put escalator preferences in a sub object in walk preferences --- .../module/osm/EscalatorProcessor.java | 6 +- .../org/opentripplanner/osm/model/OsmWay.java | 4 +- .../osm/model/OsmWithTags.java | 54 +++++---- .../preference/EscalatorPreferences.java | 109 ++++++++++++++++++ .../request/preference/WalkPreferences.java | 50 +++----- .../routerequest/RouteRequestConfig.java | 45 +++++--- .../street/model/edge/EscalatorEdge.java | 4 +- .../opentripplanner/osm/model/OsmWayTest.java | 5 - .../osm/model/OsmWithTagsTest.java | 2 +- .../preference/WalkPreferencesTest.java | 16 +-- .../street/model/edge/EscalatorEdgeTest.java | 4 +- .../standalone/config/router-config.json | 6 +- doc/user/RouteRequest.md | 29 ++--- doc/user/RouterConfiguration.md | 6 +- 14 files changed, 228 insertions(+), 112 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java index c8f4e0e0042..40695e7c67c 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/EscalatorProcessor.java @@ -45,8 +45,8 @@ public void buildEscalatorEdge(OsmWay escalatorWay, double length) { issueStore.add( Issue.issue( "InvalidDuration", - "Duration for osm node %d is not a valid duration: '%s'; it's replaced with 'Optional.empty()' (unknown).", - escalatorWay.getId(), + "Duration for osm node {} is not a valid duration: '{}'; the value is ignored.", + escalatorWay.url(), v ) ) @@ -59,7 +59,7 @@ public void buildEscalatorEdge(OsmWay escalatorWay, double length) { Issue.issue( "InvalidDuration", "Duration for osm node {} makes implied speed {} be outside acceptable range.", - escalatorWay.getId(), + escalatorWay.url(), speed ) ); diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java index 0cc19363a0a..a620545f521 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java @@ -3,13 +3,11 @@ import gnu.trove.list.TLongList; import gnu.trove.list.array.TLongArrayList; import java.time.Duration; -import java.time.format.DateTimeParseException; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import org.opentripplanner.graph_builder.module.osm.StreetTraversalPermissionPair; import org.opentripplanner.street.model.StreetTraversalPermission; -import org.opentripplanner.utils.time.DurationUtils; public class OsmWay extends OsmWithTags { @@ -136,7 +134,7 @@ public boolean isEscalator() { } public Optional getDuration(Consumer errorHandler) { - return getTagAsDuration("duration", errorHandler); + return getTagValueAsDuration("duration", errorHandler); } public boolean isForwardEscalator() { diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java index 74d1ffd25f5..ab50fb0ec1b 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -229,36 +229,43 @@ public OptionalInt getTagAsInt(String tag, Consumer errorHandler) { * hh:mm * hh:mm:ss * and where the leading value is not limited to any maximum. + * See OSM wiki definition + * of duration. + * * @param duration string in format mm, hh:mm, or hh:mm:ss * @return Duration * @throws DateTimeParseException on bad input */ public static Duration parseOsmDuration(String duration) { - // Unfortunately DateFormatParserBuilder doesn't quite do enough for this case. - // It has the capability for expressing optional parts, so it could express hh(:mm(:ss)?)? - // but it cannot express (hh:)?mm(:ss)? where the existence of (:ss) implies the existence - // of (hh:). Even if it did, it would not be able to handle the cases where hours are - // greater than 23 or (if there is no hours part at all) minutes are greater than 59, which - // are both allowed by the spec and exist in OSM data. Durations are not LocalTimes after - // all, in parsing a LocalTime it makes sense and is correct that hours cannot be more than - // 23 or minutes more than 59, but in durations if you have capped the largest unit, it is - // reasonable for the amount of the largest unit to be as large as it needs to be. + /* + * Unfortunately DateFormatParserBuilder doesn't quite do enough for this case. + * It has the capability for expressing optional parts, so it could express hh(:mm(:ss)?)? + * but it cannot express (hh:)?mm(:ss)? where the existence of (:ss) implies the existence + * of (hh:). Even if it did, it would not be able to handle the cases where hours are + * greater than 23 or (if there is no hours part at all) minutes are greater than 59, which + * are both allowed by the spec and exist in OSM data. Durations are not LocalTimes after + * all, in parsing a LocalTime it makes sense and is correct that hours cannot be more than + * 23 or minutes more than 59, but in durations if you have capped the largest unit, it is + * reasonable for the amount of the largest unit to be as large as it needs to be. + */ int colonCount = (int) duration.chars().filter(ch -> ch == ':').count(); if (colonCount <= 2) { try { int i, j; long hours, minutes, seconds; - // The first :-separated element can be any width, and has no maximum. It still has - // to be non-negative. The following elements must be 2 characters wide, non-negative, - // and less than 60. + /* + * The first :-separated element can be any width, and has no maximum. It still has + * to be non-negative. The following elements must be 2 characters wide, non-negative, + * and less than 60. + */ switch (colonCount) { - case 0: // case "m" + case 0: /* case "m" */ minutes = Long.parseLong(duration); if (minutes >= 0) { return Duration.ofMinutes(minutes); } break; - case 1: // case "h:mm" + case 1: /* case "h:mm" */ i = duration.indexOf(':'); hours = Long.parseLong(duration.substring(0, i)); minutes = Long.parseLong(duration.substring(i + 1)); @@ -266,8 +273,7 @@ public static Duration parseOsmDuration(String duration) { return Duration.ofHours(hours).plusMinutes(minutes); } break; - default: // case "h:mm:ss" - //case 2: + default: /* case "h:mm:ss" */ i = duration.indexOf(':'); j = duration.indexOf(':', i + 1); hours = Long.parseLong(duration.substring(0, i)); @@ -287,14 +293,22 @@ public static Duration parseOsmDuration(String duration) { break; } } catch (NumberFormatException e) { - // fallthrough + /* fallthrough */ } } - throw new DateTimeParseException("bad clock duration", duration, 0); + throw new DateTimeParseException("Bad OSM duration", duration, 0); } - public Optional getTagAsDuration(String tag, Consumer errorHandler) { - String value = getTag(tag); + /** + * Gets a tag's value, assumes it is an OSM wiki spesified duration, parses and returns it. + * If parsing fails, calls the error handler. + * + * @param key + * @param errorHandler + * @return parsed Duration, or empty + */ + public Optional getTagValueAsDuration(String key, Consumer errorHandler) { + String value = getTag(key); if (value != null) { try { return Optional.of(parseOsmDuration(value)); diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java new file mode 100644 index 00000000000..9a770d99fda --- /dev/null +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java @@ -0,0 +1,109 @@ +package org.opentripplanner.routing.api.request.preference; + +import static org.opentripplanner.utils.lang.DoubleUtils.doubleEquals; + +import java.io.Serializable; +import java.util.Objects; +import java.util.function.Consumer; +import org.opentripplanner.utils.tostring.ToStringBuilder; + +public class EscalatorPreferences implements Serializable { + + public static final EscalatorPreferences DEFAULT = new EscalatorPreferences(); + + private final double reluctance; + private final double speed; + + private EscalatorPreferences() { + this.reluctance = 1.5; + this.speed = 0.45; + } + + private EscalatorPreferences(Builder builder) { + reluctance = builder.reluctance; + speed = builder.speed; + } + + public static Builder of() { + return new Builder(DEFAULT); + } + + public Builder copyOf() { + return new Builder(this); + } + + public double reluctance() { + return reluctance; + } + + public double speed() { + return speed; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + EscalatorPreferences that = (EscalatorPreferences) o; + return (doubleEquals(that.reluctance, reluctance) && doubleEquals(that.speed, speed)); + } + + @Override + public int hashCode() { + return Objects.hash(speed, reluctance); + } + + @Override + public String toString() { + return ToStringBuilder + .of(EscalatorPreferences.class) + .addNum("speed", speed, DEFAULT.speed) + .addNum("reluctance", reluctance, DEFAULT.reluctance) + .toString(); + } + + public static class Builder { + + private final EscalatorPreferences original; + private double reluctance; + private double speed; + + public Builder(EscalatorPreferences original) { + this.original = original; + this.reluctance = original.reluctance; + this.speed = original.speed; + } + + public EscalatorPreferences original() { + return original; + } + + public double speed() { + return speed; + } + + public Builder withSpeed(double speed) { + this.speed = speed; + return this; + } + + public double reluctance() { + return reluctance; + } + + public Builder withReluctance(double reluctance) { + this.reluctance = reluctance; + return this; + } + + public Builder apply(Consumer body) { + body.accept(this); + return this; + } + + public EscalatorPreferences build() { + var newObj = new EscalatorPreferences(this); + return original.equals(newObj) ? original : newObj; + } + } +} diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/WalkPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/WalkPreferences.java index 87dcc320c83..5bc0a120d32 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/WalkPreferences.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/WalkPreferences.java @@ -1,6 +1,7 @@ package org.opentripplanner.routing.api.request.preference; import static org.opentripplanner.utils.lang.DoubleUtils.doubleEquals; +import static org.opentripplanner.utils.lang.ObjectUtils.ifNotNull; import java.io.Serializable; import java.util.Objects; @@ -29,8 +30,7 @@ public final class WalkPreferences implements Serializable { private final double stairsTimeFactor; private final double safetyFactor; - private final double escalatorReluctance; - private final double escalatorSpeed; + private final EscalatorPreferences escalator; private WalkPreferences() { this.speed = 1.33; @@ -39,8 +39,7 @@ private WalkPreferences() { this.stairsReluctance = 2.0; this.stairsTimeFactor = 3.0; this.safetyFactor = 1.0; - this.escalatorReluctance = 1.5; - this.escalatorSpeed = 0.45; + this.escalator = EscalatorPreferences.DEFAULT; } private WalkPreferences(Builder builder) { @@ -50,8 +49,7 @@ private WalkPreferences(Builder builder) { this.stairsReluctance = Units.reluctance(builder.stairsReluctance); this.stairsTimeFactor = Units.reluctance(builder.stairsTimeFactor); this.safetyFactor = Units.reluctance(builder.safetyFactor); - this.escalatorReluctance = Units.reluctance(builder.escalatorReluctance); - this.escalatorSpeed = Units.speed(builder.escalatorSpeed); + this.escalator = builder.escalator; } public static Builder of() { @@ -111,12 +109,8 @@ public double safetyFactor() { return safetyFactor; } - public double escalatorReluctance() { - return escalatorReluctance; - } - - public double escalatorSpeed() { - return escalatorSpeed; + public EscalatorPreferences escalator() { + return escalator; } @Override @@ -131,8 +125,7 @@ public boolean equals(Object o) { doubleEquals(that.stairsReluctance, stairsReluctance) && doubleEquals(that.stairsTimeFactor, stairsTimeFactor) && doubleEquals(that.safetyFactor, safetyFactor) && - doubleEquals(that.escalatorReluctance, escalatorReluctance) && - doubleEquals(that.escalatorSpeed, escalatorSpeed) + escalator.equals(that.escalator) ); } @@ -145,8 +138,7 @@ public int hashCode() { stairsReluctance, stairsTimeFactor, safetyFactor, - escalatorReluctance, - escalatorSpeed + escalator ); } @@ -160,8 +152,7 @@ public String toString() { .addNum("stairsReluctance", stairsReluctance, DEFAULT.stairsReluctance) .addNum("stairsTimeFactor", stairsTimeFactor, DEFAULT.stairsTimeFactor) .addNum("safetyFactor", safetyFactor, DEFAULT.safetyFactor) - .addNum("escalatorReluctance", escalatorReluctance, DEFAULT.escalatorReluctance) - .addNum("escalatorSpeed", escalatorSpeed, DEFAULT.escalatorSpeed) + .addObj("escalator", escalator, DEFAULT.escalator) .toString(); } @@ -175,8 +166,7 @@ public static class Builder { private double stairsTimeFactor; private double safetyFactor; - private double escalatorReluctance; - private double escalatorSpeed; + private EscalatorPreferences escalator; public Builder(WalkPreferences original) { this.original = original; @@ -186,8 +176,7 @@ public Builder(WalkPreferences original) { this.stairsReluctance = original.stairsReluctance; this.stairsTimeFactor = original.stairsTimeFactor; this.safetyFactor = original.safetyFactor; - this.escalatorReluctance = original.escalatorReluctance; - this.escalatorSpeed = original.escalatorSpeed; + this.escalator = original.escalator; } public WalkPreferences original() { @@ -254,21 +243,12 @@ public Builder withSafetyFactor(double safetyFactor) { return this; } - public double escalatorReluctance() { - return escalatorReluctance; - } - - public Builder withEscalatorReluctance(double escalatorReluctance) { - this.escalatorReluctance = escalatorReluctance; - return this; - } - - public double escalatorSpeed() { - return escalatorSpeed; + public EscalatorPreferences escalator() { + return escalator; } - public Builder withEscalatorSpeed(double escalatorSpeed) { - this.escalatorSpeed = escalatorSpeed; + public Builder withEscalator(Consumer body) { + this.escalator = ifNotNull(this.escalator, original.escalator).copyOf().apply(body).build(); return this; } diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java index 9f0e0ba17b4..454ab29a68c 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java @@ -26,6 +26,7 @@ import org.opentripplanner.routing.api.request.preference.AccessEgressPreferences; import org.opentripplanner.routing.api.request.preference.BikePreferences; import org.opentripplanner.routing.api.request.preference.CarPreferences; +import org.opentripplanner.routing.api.request.preference.EscalatorPreferences; import org.opentripplanner.routing.api.request.preference.RoutingPreferences; import org.opentripplanner.routing.api.request.preference.ScooterPreferences; import org.opentripplanner.routing.api.request.preference.StreetPreferences; @@ -737,6 +738,32 @@ private static void mapSystemPreferences(NodeAdapter c, SystemPreferences.Builde } } + private static void mapEscalatorPreferences( + NodeAdapter root, + EscalatorPreferences.Builder escalator + ) { + var dft = escalator.original(); + NodeAdapter c = root.of("escalator").since(V2_7).summary("Escalator preferences.").asObject(); + escalator + .withReluctance( + c + .of("reluctance") + .since(V2_4) + .summary( + "A multiplier for how bad being in an escalator is compared to being in transit for equal lengths of time" + ) + .asDouble(dft.reluctance()) + ) + .withSpeed( + c + .of("speed") + .since(V2_7) + .summary("How fast does an escalator move horizontally?") + .description("Horizontal speed of escalator in m/s.") + .asDouble(dft.speed()) + ); + } + private static void mapWalkPreferences(NodeAdapter root, WalkPreferences.Builder walk) { var dft = walk.original(); NodeAdapter c = root.of("walk").since(V2_5).summary("Walking preferences.").asObject(); @@ -810,22 +837,6 @@ private static void mapWalkPreferences(NodeAdapter root, WalkPreferences.Builder ) .asDouble(dft.safetyFactor()) ) - .withEscalatorReluctance( - c - .of("escalatorReluctance") - .since(V2_4) - .summary( - "A multiplier for how bad being in an escalator is compared to being in transit for equal lengths of time" - ) - .asDouble(dft.escalatorReluctance()) - ) - .withEscalatorSpeed( - c - .of("escalatorSpeed") - .since(V2_7) - .summary("How fast does an escalator move horizontally?") - .description("Horizontal speed of escalator in m/s.") - .asDouble(dft.escalatorSpeed()) - ); + .withEscalator(escalator -> mapEscalatorPreferences(c, escalator)); } } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java index fdfd4ee44b4..d44b67568d6 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java @@ -31,11 +31,11 @@ public State[] traverse(State s0) { var s1 = s0.edit(this); double time; if (duration == null) { - time = getDistanceMeters() / s0.getPreferences().walk().escalatorSpeed(); + time = getDistanceMeters() / s0.getPreferences().walk().escalator().speed(); } else { time = duration.toSeconds(); } - s1.incrementWeight(s0.getPreferences().walk().escalatorReluctance() * time); + s1.incrementWeight(s0.getPreferences().walk().escalator().reluctance() * time); s1.incrementTimeInSeconds((int) Math.round(time)); s1.incrementWalkDistance(getDistanceMeters()); return s1.makeStateArray(); diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java index 277cda7ac9b..9ac9457a9ec 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java @@ -1,13 +1,8 @@ package org.opentripplanner.osm.model; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.time.Duration; -import java.util.Optional; -import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.opentripplanner.osm.wayproperty.specifier.WayTestData; diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java index 3fe49ded403..84b74b8f655 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java @@ -325,7 +325,7 @@ void parseTagAsDuration(String value, Optional expected) { var way = new OsmWithTags(); var key = "duration"; way.addTag(key, value); - var duration = way.getTagAsDuration(key, i -> {}); + var duration = way.getTagValueAsDuration(key, i -> {}); assertEquals(expected, duration); } } diff --git a/application/src/test/java/org/opentripplanner/routing/api/request/preference/WalkPreferencesTest.java b/application/src/test/java/org/opentripplanner/routing/api/request/preference/WalkPreferencesTest.java index a61a0558bb1..785b130ca7a 100644 --- a/application/src/test/java/org/opentripplanner/routing/api/request/preference/WalkPreferencesTest.java +++ b/application/src/test/java/org/opentripplanner/routing/api/request/preference/WalkPreferencesTest.java @@ -88,7 +88,7 @@ void testEqualsAndHashCodeWithNewlyConstructedPreferences() { .withReluctance(sameReluctance) .withStairsReluctance(sameStairsReluctance) .withSafetyFactor(sameSafetyFactor) - .withEscalatorReluctance(sameEscalatorReluctance) + .withEscalator(escalator -> escalator.withReluctance(sameEscalatorReluctance)) .withBoardCost(sameBoardCost) .build(); var secondEqual = WalkPreferences @@ -97,7 +97,7 @@ void testEqualsAndHashCodeWithNewlyConstructedPreferences() { .withReluctance(sameReluctance) .withStairsReluctance(sameStairsReluctance) .withSafetyFactor(sameSafetyFactor) - .withEscalatorReluctance(sameEscalatorReluctance) + .withEscalator(escalator -> escalator.withReluctance(sameEscalatorReluctance)) .withBoardCost(sameBoardCost) .build(); assertEqualsAndHashCode(firstEqual, secondEqual); @@ -110,7 +110,7 @@ void testEqualsAndHashCodeWithNewlyConstructedPreferences() { .withReluctance(sameReluctance) .withStairsReluctance(sameStairsReluctance) .withSafetyFactor(sameSafetyFactor) - .withEscalatorReluctance(sameEscalatorReluctance) + .withEscalator(escalator -> escalator.withReluctance(sameEscalatorReluctance)) .withBoardCost(sameBoardCost) .build(); assertNotEqualsAndHashCode(firstEqual, differentSpeedPreferences); @@ -123,7 +123,7 @@ void testEqualsAndHashCodeWithNewlyConstructedPreferences() { .withReluctance(notSameReluctance) .withStairsReluctance(sameStairsReluctance) .withSafetyFactor(sameSafetyFactor) - .withEscalatorReluctance(sameEscalatorReluctance) + .withEscalator(escalator -> escalator.withReluctance(sameEscalatorReluctance)) .withBoardCost(sameBoardCost) .build(); assertNotEqualsAndHashCode(firstEqual, differentReluctancePreferences); @@ -136,7 +136,7 @@ void testEqualsAndHashCodeWithNewlyConstructedPreferences() { .withReluctance(sameReluctance) .withStairsReluctance(notSameStairsReluctance) .withSafetyFactor(sameSafetyFactor) - .withEscalatorReluctance(sameEscalatorReluctance) + .withEscalator(escalator -> escalator.withReluctance(sameEscalatorReluctance)) .withBoardCost(sameBoardCost) .build(); assertNotEqualsAndHashCode(firstEqual, differentStairsReluctancePreferences); @@ -149,7 +149,7 @@ void testEqualsAndHashCodeWithNewlyConstructedPreferences() { .withReluctance(sameReluctance) .withStairsReluctance(sameStairsReluctance) .withSafetyFactor(notSameSafetyFactor) - .withEscalatorReluctance(sameEscalatorReluctance) + .withEscalator(escalator -> escalator.withReluctance(sameEscalatorReluctance)) .withBoardCost(sameBoardCost) .build(); assertNotEqualsAndHashCode(firstEqual, differentSafetyFactorPreferences); @@ -162,7 +162,7 @@ void testEqualsAndHashCodeWithNewlyConstructedPreferences() { .withReluctance(sameReluctance) .withStairsReluctance(sameStairsReluctance) .withSafetyFactor(sameSafetyFactor) - .withEscalatorReluctance(notSameEscalatorReluctance) + .withEscalator(escalator -> escalator.withReluctance(notSameEscalatorReluctance)) .withBoardCost(sameBoardCost) .build(); assertNotEqualsAndHashCode(firstEqual, differentEscalatorReluctancePreferences); @@ -175,7 +175,7 @@ void testEqualsAndHashCodeWithNewlyConstructedPreferences() { .withReluctance(sameReluctance) .withStairsReluctance(sameStairsReluctance) .withSafetyFactor(sameSafetyFactor) - .withEscalatorReluctance(sameEscalatorReluctance) + .withEscalator(escalator -> escalator.withReluctance(sameEscalatorReluctance)) .withBoardCost(notSameBoardCost) .build(); assertNotEqualsAndHashCode(firstEqual, differentBoardCostPreferences); diff --git a/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java b/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java index 25199e373a0..23ae7a567f8 100644 --- a/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java +++ b/application/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java @@ -31,7 +31,9 @@ void testWalking(double escalatorReluctance, double expectedWeight) { var edge = EscalatorEdge.createEscalatorEdge(from, to, 45, null); var req = StreetSearchRequest .of() - .withPreferences(p -> p.withWalk(w -> w.withEscalatorReluctance(escalatorReluctance))) + .withPreferences(p -> + p.withWalk(w -> w.withEscalator(escalator -> escalator.withReluctance(escalatorReluctance))) + ) .withMode(StreetMode.WALK); var res = edge.traverse(new State(from, req.build()))[0]; diff --git a/application/src/test/resources/standalone/config/router-config.json b/application/src/test/resources/standalone/config/router-config.json index 5539ec4de65..e3f5b0caad2 100644 --- a/application/src/test/resources/standalone/config/router-config.json +++ b/application/src/test/resources/standalone/config/router-config.json @@ -75,8 +75,10 @@ "reluctance": 4.0, "stairsReluctance": 1.65, "boardCost": 600, - "escalatorReluctance": 1.5, - "escalatorSpeed": 0.45 + "escalator": { + "reluctance": 1.5, + "speed": 0.45 + } }, "waitReluctance": 1.0, "otherThanPreferredRoutesPenalty": 300, diff --git a/doc/user/RouteRequest.md b/doc/user/RouteRequest.md index c6cdef82c25..5b428ff9175 100644 --- a/doc/user/RouteRequest.md +++ b/doc/user/RouteRequest.md @@ -155,13 +155,14 @@ and in the [transferRequests in build-config.json](BuildConfiguration.md#transfe |    [routes](#rd_unpreferred_routes) | `feed-scoped-id[]` | The ids of the routes that incur an extra cost when being used. Format: `FeedId:RouteId` | *Optional* | | 2.2 | | walk | `object` | Walking preferences. | *Optional* | | 2.5 | |    boardCost | `integer` | Prevents unnecessary transfers by adding a cost for boarding a vehicle. This is the cost that is used when boarding while walking. | *Optional* | `600` | 2.0 | -|    escalatorReluctance | `double` | A multiplier for how bad being in an escalator is compared to being in transit for equal lengths of time | *Optional* | `1.5` | 2.4 | -|    [escalatorSpeed](#rd_walk_escalatorSpeed) | `double` | How fast does an escalator move horizontally? | *Optional* | `0.45` | 2.7 | |    [reluctance](#rd_walk_reluctance) | `double` | A multiplier for how bad walking is, compared to being in transit for equal lengths of time. | *Optional* | `2.0` | 2.0 | |    [safetyFactor](#rd_walk_safetyFactor) | `double` | Factor for how much the walk safety is considered in routing. | *Optional* | `1.0` | 2.2 | |    speed | `double` | The user's walking speed in meters/second. | *Optional* | `1.33` | 2.0 | |    stairsReluctance | `double` | Used instead of walkReluctance for stairs. | *Optional* | `2.0` | 2.0 | |    [stairsTimeFactor](#rd_walk_stairsTimeFactor) | `double` | How much more time does it take to walk a flight of stairs compared to walking a similar horizontal length. | *Optional* | `3.0` | 2.1 | +|    escalator | `object` | Escalator preferences. | *Optional* | | 2.7 | +|       reluctance | `double` | A multiplier for how bad being in an escalator is compared to being in transit for equal lengths of time | *Optional* | `1.5` | 2.4 | +|       [speed](#rd_walk_escalator_speed) | `double` | How fast does an escalator move horizontally? | *Optional* | `0.45` | 2.7 | | wheelchairAccessibility | `object` | See [Wheelchair Accessibility](Accessibility.md) | *Optional* | | 2.2 | |    enabled | `boolean` | Enable wheelchair accessibility. | *Optional* | `false` | 2.0 | |    inaccessibleStreetReluctance | `double` | The factor to multiply the cost of traversing a street edge that is not wheelchair-accessible. | *Optional* | `25.0` | 2.2 | @@ -1072,15 +1073,6 @@ The ids of the routes that incur an extra cost when being used. Format: `FeedId: How much cost is added is configured in `unpreferredCost`. -

escalatorSpeed

- -**Since version:** `2.7` ∙ **Type:** `double` ∙ **Cardinality:** `Optional` ∙ **Default value:** `0.45` -**Path:** /routingDefaults/walk - -How fast does an escalator move horizontally? - -Horizontal speed of escalator in m/s. -

reluctance

**Since version:** `2.0` ∙ **Type:** `double` ∙ **Cardinality:** `Optional` ∙ **Default value:** `2.0` @@ -1115,6 +1107,15 @@ Default value is based on: Fujiyama, T., & Tyler, N. (2010). Predicting the walk speed of pedestrians on stairs. Transportation Planning and Technology, 33(2), 177–202. +

speed

+ +**Since version:** `2.7` ∙ **Type:** `double` ∙ **Cardinality:** `Optional` ∙ **Default value:** `0.45` +**Path:** /routingDefaults/walk/escalator + +How fast does an escalator move horizontally? + +Horizontal speed of escalator in m/s. +

maxSlope

**Since version:** `2.0` ∙ **Type:** `double` ∙ **Cardinality:** `Optional` ∙ **Default value:** `0.083` @@ -1224,8 +1225,10 @@ include stairs as a last result. "reluctance" : 4.0, "stairsReluctance" : 1.65, "boardCost" : 600, - "escalatorReluctance" : 1.5, - "escalatorSpeed" : 0.45 + "escalator" : { + "reluctance" : 1.5, + "speed" : 0.45 + } }, "waitReluctance" : 1.0, "otherThanPreferredRoutesPenalty" : 300, diff --git a/doc/user/RouterConfiguration.md b/doc/user/RouterConfiguration.md index 043e4f2f485..7dae97fd74c 100644 --- a/doc/user/RouterConfiguration.md +++ b/doc/user/RouterConfiguration.md @@ -528,8 +528,10 @@ Used to group requests when monitoring OTP. "reluctance" : 4.0, "stairsReluctance" : 1.65, "boardCost" : 600, - "escalatorReluctance" : 1.5, - "escalatorSpeed" : 0.45 + "escalator" : { + "reluctance" : 1.5, + "speed" : 0.45 + } }, "waitReluctance" : 1.0, "otherThanPreferredRoutesPenalty" : 300, From 1e64a995b777320e15a22e044eded8d50645fe12 Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Mon, 9 Dec 2024 13:19:02 +0200 Subject: [PATCH 109/162] prettier wanted less spaces --- .../java/org/opentripplanner/osm/model/OsmWithTags.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java index ab50fb0ec1b..0c44d892524 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -259,13 +259,13 @@ public static Duration parseOsmDuration(String duration) { * and less than 60. */ switch (colonCount) { - case 0: /* case "m" */ + case 0:/* case "m" */ minutes = Long.parseLong(duration); if (minutes >= 0) { return Duration.ofMinutes(minutes); } break; - case 1: /* case "h:mm" */ + case 1:/* case "h:mm" */ i = duration.indexOf(':'); hours = Long.parseLong(duration.substring(0, i)); minutes = Long.parseLong(duration.substring(i + 1)); @@ -273,7 +273,7 @@ public static Duration parseOsmDuration(String duration) { return Duration.ofHours(hours).plusMinutes(minutes); } break; - default: /* case "h:mm:ss" */ + default:/* case "h:mm:ss" */ i = duration.indexOf(':'); j = duration.indexOf(':', i + 1); hours = Long.parseLong(duration.substring(0, i)); From 9c8e928399d3e82c58816d71760df9b1640b154f Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Mon, 9 Dec 2024 13:43:30 +0200 Subject: [PATCH 110/162] comment formatting --- .../osm/model/OsmWithTags.java | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java index 0c44d892524..d6ed30be092 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -237,35 +237,33 @@ public OptionalInt getTagAsInt(String tag, Consumer errorHandler) { * @throws DateTimeParseException on bad input */ public static Duration parseOsmDuration(String duration) { - /* - * Unfortunately DateFormatParserBuilder doesn't quite do enough for this case. - * It has the capability for expressing optional parts, so it could express hh(:mm(:ss)?)? - * but it cannot express (hh:)?mm(:ss)? where the existence of (:ss) implies the existence - * of (hh:). Even if it did, it would not be able to handle the cases where hours are - * greater than 23 or (if there is no hours part at all) minutes are greater than 59, which - * are both allowed by the spec and exist in OSM data. Durations are not LocalTimes after - * all, in parsing a LocalTime it makes sense and is correct that hours cannot be more than - * 23 or minutes more than 59, but in durations if you have capped the largest unit, it is - * reasonable for the amount of the largest unit to be as large as it needs to be. - */ + // Unfortunately DateFormatParserBuilder doesn't quite do enough for this case. + // It has the capability for expressing optional parts, so it could express hh(:mm(:ss)?)? + // but it cannot express (hh:)?mm(:ss)? where the existence of (:ss) implies the existence + // of (hh:). Even if it did, it would not be able to handle the cases where hours are + // greater than 23 or (if there is no hours part at all) minutes are greater than 59, which + // are both allowed by the spec and exist in OSM data. Durations are not LocalTimes after + // all, in parsing a LocalTime it makes sense and is correct that hours cannot be more than + // 23 or minutes more than 59, but in durations if you have capped the largest unit, it is + // reasonable for the amount of the largest unit to be as large as it needs to be. int colonCount = (int) duration.chars().filter(ch -> ch == ':').count(); if (colonCount <= 2) { try { int i, j; long hours, minutes, seconds; - /* - * The first :-separated element can be any width, and has no maximum. It still has - * to be non-negative. The following elements must be 2 characters wide, non-negative, - * and less than 60. - */ + // The first :-separated element can be any width, and has no maximum. It still has + // to be non-negative. The following elements must be 2 characters wide, non-negative, + // and less than 60. switch (colonCount) { - case 0:/* case "m" */ + // case "m" + case 0: minutes = Long.parseLong(duration); if (minutes >= 0) { return Duration.ofMinutes(minutes); } break; - case 1:/* case "h:mm" */ + // case "h:mm" + case 1: i = duration.indexOf(':'); hours = Long.parseLong(duration.substring(0, i)); minutes = Long.parseLong(duration.substring(i + 1)); @@ -273,7 +271,8 @@ public static Duration parseOsmDuration(String duration) { return Duration.ofHours(hours).plusMinutes(minutes); } break; - default:/* case "h:mm:ss" */ + // case "h:mm:ss" + default: i = duration.indexOf(':'); j = duration.indexOf(':', i + 1); hours = Long.parseLong(duration.substring(0, i)); @@ -293,7 +292,7 @@ public static Duration parseOsmDuration(String duration) { break; } } catch (NumberFormatException e) { - /* fallthrough */ + // fallthrough } } throw new DateTimeParseException("Bad OSM duration", duration, 0); From 68b9c61ac442e449bda595d11e36f259834e306a Mon Sep 17 00:00:00 2001 From: Teemu Kalvas Date: Mon, 9 Dec 2024 13:50:08 +0200 Subject: [PATCH 111/162] revert explanation of default escalator speed --- .../api/request/preference/EscalatorPreferences.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java index 9a770d99fda..3c150b43417 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/EscalatorPreferences.java @@ -14,9 +14,14 @@ public class EscalatorPreferences implements Serializable { private final double reluctance; private final double speed; + /* Using the angle of 30 degrees and a speed of 0.5 m/s gives a horizontal component + * of approx. 0.43 m/s. This is typical of short escalators like those in shopping + * malls. */ + private static final double HORIZONTAL_SPEED = 0.45; + private EscalatorPreferences() { this.reluctance = 1.5; - this.speed = 0.45; + this.speed = HORIZONTAL_SPEED; } private EscalatorPreferences(Builder builder) { From 012f149376eb5ae5453a3c33e852665224bff1b5 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Mon, 9 Dec 2024 13:55:57 +0200 Subject: [PATCH 112/162] Add changes based on review comments. --- .../module/DirectTransferGenerator.java | 20 +++++++++++-------- .../opentripplanner/model/PathTransfer.java | 12 ++++++----- .../raptoradapter/transit/Transfer.java | 2 +- .../transit/service/TimetableRepository.java | 9 +++++++++ .../module/DirectTransferGeneratorTest.java | 18 +++-------------- .../raptoradapter/transit/TransferTest.java | 1 - 6 files changed, 32 insertions(+), 30 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 19e2c8227d0..626c85312a7 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -135,19 +135,21 @@ public void buildGraph() { TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges); PathTransfer pathTransfer = distinctTransfers.get(transferKey); if (pathTransfer == null) { - EnumSet modes = EnumSet.of(mode); + // If the PathTransfer can't be found, it is created. distinctTransfers.put( transferKey, - new PathTransfer(stop, sd.stop, sd.distance, sd.edges, modes) + new PathTransfer(stop, sd.stop, sd.distance, sd.edges, EnumSet.of(mode)) ); } else { - pathTransfer.addMode(mode); + // If the PathTransfer is found, a new PathTransfer with the added mode is created. + distinctTransfers.put(transferKey, pathTransfer.withAddedMode(mode)); } } } // Calculate flex transfers if flex routing is enabled. for (RouteRequest transferProfile : flexTransferRequests) { - StreetMode mode = transferProfile.journey().transfer().mode(); + // Flex transfer requests only use the WALK mode. + StreetMode mode = StreetMode.WALK; // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. for (NearbyStop sd : nearbyStopFinder.findNearbyStops( @@ -167,13 +169,14 @@ public void buildGraph() { TransferKey transferKey = new TransferKey(sd.stop, stop, sd.edges); PathTransfer pathTransfer = distinctTransfers.get(transferKey); if (pathTransfer == null) { - EnumSet modes = EnumSet.of(mode); + // If the PathTransfer can't be found, it is created. distinctTransfers.put( transferKey, - new PathTransfer(sd.stop, stop, sd.distance, sd.edges, modes) + new PathTransfer(sd.stop, stop, sd.distance, sd.edges, EnumSet.of(mode)) ); } else { - pathTransfer.addMode(mode); + // If the PathTransfer is found, a new PathTransfer with the added mode is created. + distinctTransfers.put(transferKey, pathTransfer.withAddedMode(mode)); } } } @@ -209,7 +212,8 @@ public void buildGraph() { for (StreetMode mode : transferRequests .stream() .map(transferProfile -> transferProfile.journey().transfer().mode()) - .collect(Collectors.toSet())) { + .distinct() + .toList()) { LOG.info( "Created {} transfers for mode {}.", transfersByStop diff --git a/application/src/main/java/org/opentripplanner/model/PathTransfer.java b/application/src/main/java/org/opentripplanner/model/PathTransfer.java index ed4696ab11c..a6b66548e64 100644 --- a/application/src/main/java/org/opentripplanner/model/PathTransfer.java +++ b/application/src/main/java/org/opentripplanner/model/PathTransfer.java @@ -28,7 +28,7 @@ public class PathTransfer implements Serializable { private final List edges; - private EnumSet modes; + private final EnumSet modes; public PathTransfer( StopLocation from, @@ -53,15 +53,17 @@ public double getDistanceMeters() { } public List getEdges() { - return this.edges; + return edges; } public EnumSet getModes() { - return this.modes; + return modes.clone(); } - public boolean addMode(StreetMode mode) { - return this.modes.add(mode); + public PathTransfer withAddedMode(StreetMode mode) { + EnumSet newModes = EnumSet.copyOf(modes); + newModes.add(mode); + return new PathTransfer(from, to, distanceMeters, edges, newModes); } @Override diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java index 6ee0dee83c0..2440321ab1f 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java @@ -75,7 +75,7 @@ public List getEdges() { } public EnumSet getModes() { - return modes; + return modes.clone(); } public Optional asRaptorTransfer(StreetSearchRequest request) { diff --git a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java index b81dd7d4f14..d83c3f9b996 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -35,6 +35,7 @@ import org.opentripplanner.model.transfer.DefaultTransferService; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; +import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.impl.DelegatingTransitAlertServiceImpl; import org.opentripplanner.routing.services.TransitAlertService; import org.opentripplanner.routing.util.ConcurrentPublished; @@ -433,6 +434,14 @@ public Collection getTransfersByStop(StopLocation stop) { return transfersByStop.get(stop); } + public Collection getTransfersByMode(StreetMode mode) { + return transfersByStop + .values() + .stream() + .filter(pathTransfer -> pathTransfer.getModes().contains(mode)) + .toList(); + } + public SiteRepository getSiteRepository() { return siteRepository; } diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index 310e474ae99..482cb0afd13 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -274,21 +274,9 @@ public void testPathTransfersWithModesForMultipleRequestsWithPatterns() { ) .buildGraph(); - var walkTransfers = timetableRepository - .getAllPathTransfers() - .stream() - .filter(pathTransfer -> pathTransfer.getModes().contains(StreetMode.WALK)) - .toList(); - var bikeTransfers = timetableRepository - .getAllPathTransfers() - .stream() - .filter(pathTransfer -> pathTransfer.getModes().contains(StreetMode.BIKE)) - .toList(); - var carTransfers = timetableRepository - .getAllPathTransfers() - .stream() - .filter(pathTransfer -> pathTransfer.getModes().contains(StreetMode.CAR)) - .toList(); + var walkTransfers = timetableRepository.getTransfersByMode(StreetMode.WALK); + var bikeTransfers = timetableRepository.getTransfersByMode(StreetMode.BIKE); + var carTransfers = timetableRepository.getTransfersByMode(StreetMode.CAR); assertTransfers( walkTransfers, diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java index 7edda8fba9c..ebae06f5063 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransferTest.java @@ -7,7 +7,6 @@ import java.util.EnumSet; import java.util.List; import java.util.Optional; -import java.util.Set; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.opentripplanner._support.geometry.Coordinates; From ffffb13b54cc7785d7563f79698af84100d6deb6 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Dec 2024 14:04:15 +0200 Subject: [PATCH 113/162] Update application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java Co-authored-by: Leonard Ehrenfried --- .../main/java/org/opentripplanner/osm/model/OsmWithTags.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java index d6ed30be092..3f47d4454bd 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -299,7 +299,7 @@ public static Duration parseOsmDuration(String duration) { } /** - * Gets a tag's value, assumes it is an OSM wiki spesified duration, parses and returns it. + * Gets a tag's value, assumes it is an OSM wiki specified duration, parses and returns it. * If parsing fails, calls the error handler. * * @param key From 7b38d5cdbcd12c7ff5575f9cb412496c6cc6e4e0 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Mon, 9 Dec 2024 12:04:53 +0000 Subject: [PATCH 114/162] Add changelog entry for #6268 [ci skip] --- doc/user/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index 7d96b53885c..56a58be86b2 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -59,6 +59,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Switch GTFS flex `safe_duration_offset` back to seconds [#6298](https://github.com/opentripplanner/OpenTripPlanner/pull/6298) - Remove unused GtfsGraphQlApiRentalStationFuzzyMatching feature [#6282](https://github.com/opentripplanner/OpenTripPlanner/pull/6282) - Make debug UI background layers configurable with new file `debug-ui-config.json` [#6295](https://github.com/opentripplanner/OpenTripPlanner/pull/6295) +- Better escalator duration control: specific duration from OSM duration tag, default speed from build-config.json [#6268](https://github.com/opentripplanner/OpenTripPlanner/pull/6268) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From a866234deb1d4691a6e2a6dc7ca681ff8ca944d7 Mon Sep 17 00:00:00 2001 From: OTP Serialization Version Bot Date: Mon, 9 Dec 2024 12:05:14 +0000 Subject: [PATCH 115/162] Bump serialization version id for #6268 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a502f3d57bc..c3f29ad1cee 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ - 174 + 175 32.1 2.52 From a178b9d1236b73e76160892d73ad059e84434c3a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 8 Dec 2024 22:30:48 +0100 Subject: [PATCH 116/162] Set bogus name to false when updating a StreetEdge's name --- .../opentripplanner/street/model/edge/Edge.java | 12 +++++++----- .../street/model/edge/StreetEdge.java | 14 +++++++++----- .../module/osm/naming/SidewalkNamerTest.java | 1 + .../mapping/StatesToWalkStepsMapperTest.java | 6 +++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/Edge.java b/application/src/main/java/org/opentripplanner/street/model/edge/Edge.java index 31bff5b1e15..8e4126b4e59 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/Edge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/Edge.java @@ -145,15 +145,17 @@ public String getDefaultName() { */ public abstract I18NString getName(); - // TODO Add comments about what a "bogus name" is. + /** + * Returns true if this edge has a generated name that is derived from its properties, + * like "path", "stairs" or "tunnel". + *

+ * Returns false if the field reflects the real world name, like "Fifth Avenue", + * "Hauptstraße" or "Øvre Holmegate". + */ public boolean hasBogusName() { return false; } - // The next few functions used to live in EdgeNarrative, which has now been - // removed - // @author mattwigway - public LineString getGeometry() { return null; } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index ee24e87f07c..468da359e0a 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -9,9 +9,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.stream.Stream; -import javax.annotation.Nullable; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.impl.PackedCoordinateSequence; import org.opentripplanner.framework.geometry.CompactLineStringUtils; @@ -82,14 +79,14 @@ public class StreetEdge /** * bicycleSafetyWeight = length * bicycleSafetyFactor. For example, a 100m street with a safety - * factor of 2.0 will be considered in term of safety cost as the same as a 200m street with a + * factor of 2.0 will be considered in terms of safety cost as the same as a 200m street with a * safety factor of 1.0. */ private float bicycleSafetyFactor; /** * walkSafetyFactor = length * walkSafetyFactor. For example, a 100m street with a safety - * factor of 2.0 will be considered in term of safety cost as the same as a 200m street with a + * factor of 2.0 will be considered in terms of safety cost as the same as a 200m street with a * safety factor of 1.0. */ private float walkSafetyFactor; @@ -446,8 +443,15 @@ public I18NString getName() { return this.name; } + /** + * Update the name of the edge after it has been constructed. This method also sets the bogusName + * property to false, indicating to the code that maps from edges to steps that this is a real + * street name. + * @see Edge#hasBogusName() + */ public void setName(I18NString name) { this.name = name; + this.flags = BitSetUtils.set(flags, HASBOGUSNAME_FLAG_INDEX, false); } public boolean hasBogusName() { diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java index 465eeb32ba2..cbfb7b76c6f 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java @@ -58,6 +58,7 @@ void postprocess() { assertNotEquals(sidewalk.edge.getName(), pryorStreet.edge.getName()); builder.postProcess(new SidewalkNamer()); assertEquals(sidewalk.edge.getName(), pryorStreet.edge.getName()); + assertFalse(sidewalk.edge.hasBogusName()); } private static class ModelBuilder { diff --git a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java index 6e41f6ddf2b..de9fe21718a 100644 --- a/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java +++ b/application/src/test/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapperTest.java @@ -42,7 +42,7 @@ void enterStation() { var walkSteps = buildWalkSteps(builder); assertEquals(2, walkSteps.size()); var enter = walkSteps.get(1); - assertEquals(enter.getRelativeDirection(), ENTER_STATION); + assertEquals(ENTER_STATION, enter.getRelativeDirection()); } @Test @@ -54,7 +54,7 @@ void exitStation() { var walkSteps = buildWalkSteps(builder); assertEquals(3, walkSteps.size()); var enter = walkSteps.get(2); - assertEquals(enter.getRelativeDirection(), EXIT_STATION); + assertEquals(EXIT_STATION, enter.getRelativeDirection()); } @Test @@ -64,7 +64,7 @@ void signpostedPathway() { var walkSteps = buildWalkSteps(builder); assertEquals(2, walkSteps.size()); var step = walkSteps.get(1); - assertEquals(step.getRelativeDirection(), FOLLOW_SIGNS); + assertEquals(FOLLOW_SIGNS, step.getRelativeDirection()); assertEquals(sign, step.getDirectionText().toString()); } From f0dd4f2d9457ae3b976c2da714568df0e528a094 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 9 Dec 2024 11:36:54 +0100 Subject: [PATCH 117/162] Add test for name setting --- .../street/model/edge/StreetEdgeTest.java | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java b/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java index 7de7b0c7ce6..ef2bacb91dd 100644 --- a/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java +++ b/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.street.model.StreetTraversalPermission.ALL; import static org.opentripplanner.street.model._data.StreetModelForTest.intersectionVertex; import static org.opentripplanner.street.model._data.StreetModelForTest.streetEdge; import static org.opentripplanner.street.model._data.StreetModelForTest.streetEdgeBuilder; @@ -17,6 +18,8 @@ import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.impl.PackedCoordinateSequence; +import org.opentripplanner.framework.geometry.GeometryUtils; +import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.core.VehicleRoutingOptimizeType; import org.opentripplanner.routing.util.ElevationUtils; @@ -43,7 +46,7 @@ public class StreetEdgeTest { private StreetSearchRequest proto; @BeforeEach - public void before() { + void before() { v0 = intersectionVertex("maple_0th", 0.0, 0.0); // label, X, Y v1 = intersectionVertex("maple_1st", 2.0, 2.0); v2 = intersectionVertex("maple_2nd", 2.0, 1.0); @@ -64,9 +67,9 @@ public void before() { } @Test - public void testInAndOutAngles() { + void testInAndOutAngles() { // An edge heading straight West - StreetEdge e1 = streetEdge(v1, v2, 1.0, StreetTraversalPermission.ALL); + StreetEdge e1 = streetEdge(v1, v2, 1.0, ALL); // Edge has same first and last angle. assertEquals(90, e1.getInAngle()); @@ -77,7 +80,7 @@ public void testInAndOutAngles() { StreetVertex v = intersectionVertex("test2", 2.0, 2.0); // Second edge, heading straight North - StreetEdge e2 = streetEdge(u, v, 1.0, StreetTraversalPermission.ALL); + StreetEdge e2 = streetEdge(u, v, 1.0, ALL); // 180 degrees could be expressed as 180 or -180. Our implementation happens to use -180. assertEquals(180, Math.abs(e2.getInAngle())); @@ -85,10 +88,8 @@ public void testInAndOutAngles() { } @Test - public void testTraverseAsPedestrian() { - StreetEdge e1 = streetEdgeBuilder(v1, v2, 100.0, StreetTraversalPermission.ALL) - .withCarSpeed(10.0f) - .buildAndConnect(); + void testTraverseAsPedestrian() { + StreetEdge e1 = streetEdgeBuilder(v1, v2, 100.0, ALL).withCarSpeed(10.0f).buildAndConnect(); StreetSearchRequest options = StreetSearchRequest .copyOf(proto) @@ -106,10 +107,8 @@ public void testTraverseAsPedestrian() { } @Test - public void testTraverseAsCar() { - StreetEdge e1 = streetEdgeBuilder(v1, v2, 100.0, StreetTraversalPermission.ALL) - .withCarSpeed(10.0f) - .buildAndConnect(); + void testTraverseAsCar() { + StreetEdge e1 = streetEdgeBuilder(v1, v2, 100.0, ALL).withCarSpeed(10.0f).buildAndConnect(); State s0 = new State(v1, StreetSearchRequest.copyOf(proto).withMode(StreetMode.CAR).build()); State s1 = e1.traverse(s0)[0]; @@ -122,8 +121,8 @@ public void testTraverseAsCar() { } @Test - public void testModeSetCanTraverse() { - StreetEdge e = streetEdge(v1, v2, 1.0, StreetTraversalPermission.ALL); + void testModeSetCanTraverse() { + StreetEdge e = streetEdge(v1, v2, 1.0, ALL); TraverseModeSet modes = TraverseModeSet.allModes(); assertTrue(e.canTraverse(modes)); @@ -146,7 +145,7 @@ public void testModeSetCanTraverse() { * correctly during turn cost computation. 4. Traffic light wait time is taken into account. */ @Test - public void testTraverseModeSwitchBike() { + void testTraverseModeSwitchBike() { var vWithTrafficLight = new LabelledIntersectionVertex("maple_1st", 2.0, 2.0, false, true); StreetEdge e0 = streetEdge(v0, vWithTrafficLight, 50.0, StreetTraversalPermission.PEDESTRIAN); StreetEdge e1 = streetEdge( @@ -183,7 +182,7 @@ public void testTraverseModeSwitchBike() { * the bike walking speed on the walking speed. 4. Traffic light wait time is taken into account. */ @Test - public void testTraverseModeSwitchWalk() { + void testTraverseModeSwitchWalk() { var vWithTrafficLight = new LabelledIntersectionVertex("maple_1st", 2.0, 2.0, false, true); StreetEdge e0 = streetEdge( v0, @@ -214,7 +213,7 @@ public void testTraverseModeSwitchWalk() { * Test the bike switching penalty feature, both its cost penalty and its separate time penalty. */ @Test - public void testBikeSwitch() { + void testBikeSwitch() { StreetEdge e0 = streetEdge(v0, v1, 0.0, StreetTraversalPermission.PEDESTRIAN); StreetEdge e1 = streetEdge(v1, v2, 0.0, StreetTraversalPermission.BICYCLE); StreetEdge e2 = streetEdge(v2, v0, 0.0, StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE); @@ -271,9 +270,9 @@ public void testBikeSwitch() { } @Test - public void testTurnRestriction() { - StreetEdge e0 = streetEdge(v0, v1, 50.0, StreetTraversalPermission.ALL); - StreetEdge e1 = streetEdge(v1, v2, 18.4, StreetTraversalPermission.ALL); + void testTurnRestriction() { + StreetEdge e0 = streetEdge(v0, v1, 50.0, ALL); + StreetEdge e1 = streetEdge(v1, v2, 18.4, ALL); StreetSearchRequestBuilder streetSearchRequestBuilder = StreetSearchRequest.copyOf(proto); streetSearchRequestBuilder.withArriveBy(true); StreetSearchRequest request = streetSearchRequestBuilder.withMode(StreetMode.WALK).build(); @@ -285,13 +284,13 @@ public void testTurnRestriction() { } @Test - public void testElevationProfile() { + void testElevationProfile() { var elevationProfile = new PackedCoordinateSequence.Double( new double[] { 0, 10, 50, 12 }, 2, 0 ); - var edge = streetEdge(v0, v1, 50.0, StreetTraversalPermission.ALL); + var edge = streetEdge(v0, v1, 50.0, ALL); StreetElevationExtensionBuilder .of(edge) .withElevationProfile(elevationProfile) @@ -306,7 +305,7 @@ public void testElevationProfile() { } @Test - public void testBikeOptimizeTriangle() { + void testBikeOptimizeTriangle() { // This test does not depend on the setup method - and can probably be simplified Coordinate c1 = new Coordinate(-122.575033, 45.456773); @@ -326,7 +325,7 @@ public void testBikeOptimizeTriangle() { .withGeometry(geometry) .withName("Test Lane") .withMeterLength(length) - .withPermission(StreetTraversalPermission.ALL) + .withPermission(ALL) .withBack(false) // a safe street .withBicycleSafetyFactor(0.74f) @@ -400,4 +399,25 @@ public void testBikeOptimizeTriangle() { double expectedWeight = timeWeight * 0.33 + slopeWeight * 0.33 + safetyWeight * 0.34; assertEquals(expectedWeight, result.getWeight(), DELTA); } + + @Test + void setName() { + var path = I18NString.of("path"); + var edge = new StreetEdgeBuilder<>() + .withFromVertex(v0) + .withToVertex(v1) + .withPermission(ALL) + .withGeometry(GeometryUtils.makeLineString(v0.getCoordinate(), v1.getCoordinate())) + .withName(path) + .withBogusName(true) + .buildAndConnect(); + + assertEquals(path, edge.getName()); + assertTrue(edge.hasBogusName()); + + var mainStreet = I18NString.of("Main Street"); + edge.setName(mainStreet); + assertEquals(mainStreet, edge.getName()); + assertFalse(edge.hasBogusName()); + } } From 984d34eeebfdacb8469e89705b83bfb10fd9965a Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Mon, 9 Dec 2024 14:33:37 +0200 Subject: [PATCH 118/162] Change how EnumSet is copied and make EnumSet immutable in Transfer. --- .../java/org/opentripplanner/model/PathTransfer.java | 2 +- .../algorithm/raptoradapter/transit/Transfer.java | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/model/PathTransfer.java b/application/src/main/java/org/opentripplanner/model/PathTransfer.java index a6b66548e64..01aa9af02f5 100644 --- a/application/src/main/java/org/opentripplanner/model/PathTransfer.java +++ b/application/src/main/java/org/opentripplanner/model/PathTransfer.java @@ -57,7 +57,7 @@ public List getEdges() { } public EnumSet getModes() { - return modes.clone(); + return EnumSet.copyOf(modes); } public PathTransfer withAddedMode(StreetMode mode) { diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java index 2440321ab1f..648343b9b0f 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java @@ -1,5 +1,7 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; @@ -33,20 +35,20 @@ public class Transfer { private final List edges; - private final EnumSet modes; + private final ImmutableSet modes; public Transfer(int toStop, List edges, EnumSet modes) { this.toStop = toStop; this.edges = edges; this.distanceMeters = (int) edges.stream().mapToDouble(Edge::getDistanceMeters).sum(); - this.modes = modes; + this.modes = Sets.immutableEnumSet(modes); } public Transfer(int toStopIndex, int distanceMeters, EnumSet modes) { this.toStop = toStopIndex; this.distanceMeters = distanceMeters; this.edges = null; - this.modes = modes; + this.modes = Sets.immutableEnumSet(modes); } public List getCoordinates() { @@ -74,8 +76,8 @@ public List getEdges() { return edges; } - public EnumSet getModes() { - return modes.clone(); + public ImmutableSet getModes() { + return modes; } public Optional asRaptorTransfer(StreetSearchRequest request) { From a1d44ba8b438a21d83db598b116f8df0e6f54dec Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 9 Dec 2024 16:23:49 +0100 Subject: [PATCH 119/162] Reduce geocode cluster radius --- .../org/opentripplanner/ext/geocoder/StopClusterMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java b/application/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java index daecb7f6a4e..d420d0e8784 100644 --- a/application/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java +++ b/application/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java @@ -121,7 +121,7 @@ private List buildStopClusters(Collection stopL .stream() .collect( Collectors.groupingBy(sl -> - new DeduplicationKey(sl.getName(), sl.getCoordinate().roundToApproximate100m()) + new DeduplicationKey(sl.getName(), sl.getCoordinate().roundToApproximate10m()) ) ) .values() From e70d2eb2e24e8d08b05f13209e4f968a5a19bde0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 22:46:14 +0000 Subject: [PATCH 120/162] chore(deps): update google.dagger.version to v2.53 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c3f29ad1cee..545d558b210 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 175 32.1 - 2.52 + 2.53 2.18.2 3.1.9 5.11.3 From 1415c6a13a42003e860a516b36cb11d13414cc48 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Mon, 9 Dec 2024 20:41:00 -0800 Subject: [PATCH 121/162] automatically calculate WSF fares --- .../ext/fares/impl/OrcaFareServiceTest.java | 19 ++++---- .../ext/fares/impl/OrcaFareService.java | 27 ++--------- .../ext/fares/impl/OrcaFaresData.java | 48 ------------------- .../transit/model/basic/Money.java | 5 ++ 4 files changed, 19 insertions(+), 80 deletions(-) delete mode 100644 application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFaresData.java diff --git a/application/src/ext-test/java/org/opentripplanner/ext/fares/impl/OrcaFareServiceTest.java b/application/src/ext-test/java/org/opentripplanner/ext/fares/impl/OrcaFareServiceTest.java index d1e3bd4837c..c819d4abc2e 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/fares/impl/OrcaFareServiceTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/fares/impl/OrcaFareServiceTest.java @@ -54,8 +54,7 @@ public class OrcaFareServiceTest { private static final Money ONE_DOLLAR = usDollars(1f); private static final Money TWO_DOLLARS = usDollars(2); - private static final Money FERRY_FARE = usDollars(6.50f); - private static final Money HALF_FERRY_FARE = usDollars(3.25f); + private static final Money HALF_FERRY_FARE = usDollars(1.75f); private static final Money ORCA_SPECIAL_FARE = usDollars(1.00f); public static final Money VASHON_WATER_TAXI_CASH_FARE = usDollars(6.75f); public static final Money WEST_SEATTLE_WATER_TAXI_CASH_FARE = usDollars(5.75f); @@ -219,24 +218,24 @@ void calculateFareThatIncludesNoFreeTransfers() { getLeg(KITSAP_TRANSIT_AGENCY_ID, 121), getLeg(WASHINGTON_STATE_FERRIES_AGENCY_ID, 150, "Fauntleroy-VashonIsland") ); - calculateFare(rides, regular, DEFAULT_TEST_RIDE_PRICE.times(3).plus(FERRY_FARE)); + calculateFare(rides, regular, DEFAULT_TEST_RIDE_PRICE.times(5)); calculateFare( rides, FareType.senior, - ONE_DOLLAR.plus(ONE_DOLLAR).plus(HALF_FERRY_FARE).plus(usDollars(0.5f)) + ONE_DOLLAR.plus(ONE_DOLLAR).plus(HALF_FERRY_FARE.times(2)).plus(usDollars(0.5f)) ); calculateFare(rides, FareType.youth, Money.ZERO_USD); // We don't get any fares for the skagit transit leg below here because they don't accept ORCA (electronic) - calculateFare(rides, FareType.electronicSpecial, ONE_DOLLAR.plus(ONE_DOLLAR).plus(FERRY_FARE)); + calculateFare(rides, FareType.electronicSpecial, ONE_DOLLAR.plus(ONE_DOLLAR).plus(DEFAULT_TEST_RIDE_PRICE.times(2))); calculateFare( rides, FareType.electronicRegular, - DEFAULT_TEST_RIDE_PRICE.times(2).plus(FERRY_FARE) + DEFAULT_TEST_RIDE_PRICE.times(4) ); calculateFare( rides, FareType.electronicSenior, - ONE_DOLLAR.plus(ONE_DOLLAR).plus(HALF_FERRY_FARE) + ONE_DOLLAR.plus(ONE_DOLLAR).plus(HALF_FERRY_FARE.times(2)) ); calculateFare(rides, FareType.electronicYouth, ZERO_USD); } @@ -326,11 +325,11 @@ void calculateFareForWSFPtToTahlequah() { List rides = List.of( getLeg(WASHINGTON_STATE_FERRIES_AGENCY_ID, 0, "Point Defiance - Tahlequah") ); - calculateFare(rides, regular, FERRY_FARE); + calculateFare(rides, regular, DEFAULT_TEST_RIDE_PRICE); calculateFare(rides, FareType.senior, HALF_FERRY_FARE); calculateFare(rides, FareType.youth, Money.ZERO_USD); - calculateFare(rides, FareType.electronicSpecial, FERRY_FARE); - calculateFare(rides, FareType.electronicRegular, FERRY_FARE); + calculateFare(rides, FareType.electronicSpecial, DEFAULT_TEST_RIDE_PRICE); + calculateFare(rides, FareType.electronicRegular, DEFAULT_TEST_RIDE_PRICE); calculateFare(rides, FareType.electronicSenior, HALF_FERRY_FARE); calculateFare(rides, FareType.electronicYouth, Money.ZERO_USD); } diff --git a/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java b/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java index c3c1873ad6d..1087ee6afbc 100644 --- a/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java +++ b/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java @@ -398,28 +398,11 @@ private Money getWashingtonStateFerriesFare( return defaultFare; } - var longName = routeLongName.toString().replaceAll(" ", ""); - - Map fares = OrcaFaresData.washingtonStateFerriesFares.get(longName); - // WSF doesn't support transfers so we only care about cash fares. - FareType wsfFareType; - if (fareType == FareType.electronicRegular) { - wsfFareType = FareType.regular; - } else if (fareType == FareType.electronicSenior) { - wsfFareType = FareType.senior; - } else if (fareType == FareType.electronicYouth) { - wsfFareType = FareType.youth; - } else if (fareType == FareType.electronicSpecial) { - wsfFareType = FareType.regular; - } else { - wsfFareType = fareType; - } - // WSF is free in one direction on each route - // If a fare is not found in the map, we can assume it's free. - // Route long name is reversed for the reverse direction on a single WSF route - return (fares != null && fares.get(wsfFareType) != null) - ? fares.get(wsfFareType) - : Money.ZERO_USD; + return switch (fareType) { + case youth, electronicYouth -> Money.ZERO_USD; + case regular, electronicRegular, electronicSpecial -> defaultFare; + case senior, electronicSenior -> defaultFare.half().roundDownToNearestFiveCents(); + }; } /** diff --git a/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFaresData.java b/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFaresData.java deleted file mode 100644 index acef59a7c03..00000000000 --- a/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFaresData.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.opentripplanner.ext.fares.impl; - -import java.util.Map; -import org.opentripplanner.routing.core.FareType; -import org.opentripplanner.transit.model.basic.Money; - -/** - * Class used to store all the fares for Sounder commuter rail. - * Data comes from a Python script that parses SoundTransit's website. - * A matrix or CSV parser would be a better approach to storing this data, - * but a refactor is unneeded given the proximity of GTFS Fares V2 which will render this redundant. - */ -class OrcaFaresData { - - // Spaces have been removed from the route name because of inconsistencies in the WSF GTFS route dataset. - public static Map> washingtonStateFerriesFares = Map.ofEntries( - sEntry("Seattle-BainbridgeIsland", 9.85f, 4.90f), - sEntry("Seattle-Bremerton", 9.85f, 4.90f), - sEntry("Mukilteo-Clinton", 6f, 3f), - sEntry("Fauntleroy-VashonIsland", 6.50f, 3.25f), - sEntry("Fauntleroy-Southworth", 7.70f, 3.85f), - sEntry("Edmonds-Kingston", 9.85f, 4.90f), - sEntry("PointDefiance-Tahlequah", 6.50f, 3.25f), - sEntry("Anacortes-FridayHarbor", 15.85f, 7.90f), - sEntry("Anacortes-LopezIsland", 15.85f, 7.90f), - sEntry("Anacortes-OrcasIsland", 15.85f, 7.90f), - sEntry("Anacortes-ShawIsland", 15.85f, 7.90f), - sEntry("Coupeville-PortTownsend", 4.10f, 2.05f), - sEntry("PortTownsend-Coupeville", 4.10f, 2.05f), - sEntry("Southworth-VashonIsland", 6.50f, 3.25f) - ); - - private static Map.Entry> sEntry( - String name, - float regularFare, - float seniorFare - ) { - return Map.entry( - name, - Map.of( - FareType.regular, - Money.usDollars(regularFare), - FareType.senior, - Money.usDollars(seniorFare) - ) - ); - } -} diff --git a/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java b/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java index 97d6f91be5a..ea0aac85cc8 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java +++ b/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java @@ -147,6 +147,11 @@ public Money half() { return new Money(currency, IntUtils.round(amount / 2f)); } + public Money roundDownToNearestFiveCents() { + int rounded = (this.minorUnitAmount() / 5) * 5; + return new Money(Money.USD, rounded); + } + /** * Multiplies the amount with the multiplicator. */ From 3138dccf88d9f79506eb283f0820d811784b5fe7 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Mon, 9 Dec 2024 21:09:32 -0800 Subject: [PATCH 122/162] update WSF agency ID --- .../org/opentripplanner/ext/fares/impl/OrcaFareService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java b/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java index 1087ee6afbc..6ff38ad243e 100644 --- a/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java +++ b/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java @@ -39,7 +39,7 @@ public class OrcaFareService extends DefaultFareService { public static final String PIERCE_COUNTY_TRANSIT_AGENCY_ID = "3"; public static final String SKAGIT_TRANSIT_AGENCY_ID = "e0e4541a-2714-487b-b30c-f5c6cb4a310f"; public static final String SEATTLE_STREET_CAR_AGENCY_ID = "23"; - public static final String WASHINGTON_STATE_FERRIES_AGENCY_ID = "WSF"; + public static final String WASHINGTON_STATE_FERRIES_AGENCY_ID = "95"; public static final String KITSAP_TRANSIT_AGENCY_ID = "kt"; public static final String WHATCOM_AGENCY_ID = "14"; public static final int ROUTE_TYPE_FERRY = 4; From 9e8f2f6558e8f5b824dbf1ef5bea6fc318c0deab Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 10 Dec 2024 08:10:39 +0100 Subject: [PATCH 123/162] Fix nullability --- .../configure/StopConsolidationRepositoryModule.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/stopconsolidation/configure/StopConsolidationRepositoryModule.java b/application/src/ext/java/org/opentripplanner/ext/stopconsolidation/configure/StopConsolidationRepositoryModule.java index 75cd6cb868f..cb774af1619 100644 --- a/application/src/ext/java/org/opentripplanner/ext/stopconsolidation/configure/StopConsolidationRepositoryModule.java +++ b/application/src/ext/java/org/opentripplanner/ext/stopconsolidation/configure/StopConsolidationRepositoryModule.java @@ -2,7 +2,6 @@ import dagger.Binds; import dagger.Module; -import javax.annotation.Nullable; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; import org.opentripplanner.ext.stopconsolidation.internal.DefaultStopConsolidationRepository; @@ -13,6 +12,5 @@ @Module public interface StopConsolidationRepositoryModule { @Binds - @Nullable - StopConsolidationRepository bindRepository(@Nullable DefaultStopConsolidationRepository repo); + StopConsolidationRepository bindRepository(DefaultStopConsolidationRepository repo); } From b476098db4f4c9a5dee42ba0d93b5c44bf8b7db4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 10 Dec 2024 08:25:17 +0100 Subject: [PATCH 124/162] Update magidoc --- .github/workflows/cibuild.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index 212c3934dc2..0368fc5c34a 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -134,7 +134,7 @@ jobs: - name: Build GTFS GraphQL API documentation run: | - npm install -g @magidoc/cli@6.1.0 + npm install -g @magidoc/cli@6.2.0 magidoc generate --stacktrace - name: Deploy compiled HTML to Github pages From b667e96b4be3648c258b59cca4c1d6a3f82f372f Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Tue, 10 Dec 2024 10:55:22 +0000 Subject: [PATCH 125/162] remove invalid comment --- .../main/java/org/opentripplanner/utils/color/ColorUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/src/main/java/org/opentripplanner/utils/color/ColorUtils.java b/utils/src/main/java/org/opentripplanner/utils/color/ColorUtils.java index ea334391e38..8f0a8ecf7ea 100644 --- a/utils/src/main/java/org/opentripplanner/utils/color/ColorUtils.java +++ b/utils/src/main/java/org/opentripplanner/utils/color/ColorUtils.java @@ -13,7 +13,6 @@ private ColorUtils() {} public static double computeLuminance(Color color) { //gets float of RED, GREEN, BLUE in range 0...1 float[] colorComponents = color.getRGBColorComponents(null); - //Calculates luminance based on https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color double r = linearizeColorComponent(colorComponents[0]); double g = linearizeColorComponent(colorComponents[1]); double b = linearizeColorComponent(colorComponents[2]); From 7266bdf3078105d083798581a547018982236c8e Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 10 Dec 2024 13:05:50 +0100 Subject: [PATCH 126/162] Add endpoint to download Transmodel GraphQL Schema --- .../opentripplanner/apis/transmodel/TransmodelAPI.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java b/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java index fe73e8b9887..d2d6379d19b 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java @@ -2,9 +2,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import graphql.schema.GraphQLSchema; +import graphql.schema.idl.SchemaPrinter; import io.micrometer.core.instrument.Tag; import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; @@ -138,6 +140,12 @@ public Response getGraphQL(String query, @Context HttpHeaders headers) { ); } + @GET + @Path("schema.graphql") + public Response getGraphQLSchema() { + return Response.ok().encoding("UTF-8").entity(new SchemaPrinter().print(schema)).build(); + } + private static Iterable getTagsFromHeaders(HttpHeaders headers) { return tracingHeaderTags .stream() From f765199bb730ced20212d4cc7f7e2baa574ef14d Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 10 Dec 2024 14:49:14 +0100 Subject: [PATCH 127/162] doc: Add a disclaimer to the top of the Transmodel Schema served at runtime. --- .../apis/transmodel/TransmodelAPI.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java b/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java index d2d6379d19b..66377a56390 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java @@ -33,6 +33,15 @@ @Produces(MediaType.APPLICATION_JSON) public class TransmodelAPI { + // Note, the blank line at the end is intended + private static final String SCHEMA_DOC_HEADER = + """ +# THIS IS NOT INTENDED FOR PRODUCTION USE. We recommend using the GraphQL introspection instead. +# This is intended for the OTP Debug UI and can also be used by humans to get the schema with the +# OTP configured default-values injected. + +"""; + private static final Logger LOG = LoggerFactory.getLogger(TransmodelAPI.class); private static GraphQLSchema schema; @@ -143,7 +152,8 @@ public Response getGraphQL(String query, @Context HttpHeaders headers) { @GET @Path("schema.graphql") public Response getGraphQLSchema() { - return Response.ok().encoding("UTF-8").entity(new SchemaPrinter().print(schema)).build(); + var text = SCHEMA_DOC_HEADER + new SchemaPrinter().print(schema); + return Response.ok().encoding("UTF-8").entity(text).build(); } private static Iterable getTagsFromHeaders(HttpHeaders headers) { From 15c7da207b008bff83c16cc902e15c2160d01a52 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 10 Dec 2024 16:34:29 +0100 Subject: [PATCH 128/162] refactor: Delete old comments/config in shaded-jar pom.xml The removed section was commented out by @abyrd in May 2014, and replaced later the same year with the current filter configuration (see filter config above the removed code). --- shaded-jar/pom.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/shaded-jar/pom.xml b/shaded-jar/pom.xml index 5709ef8245f..cf48c89271c 100644 --- a/shaded-jar/pom.xml +++ b/shaded-jar/pom.xml @@ -77,14 +77,6 @@ true false - - From c90aea1344534daa82f93a9abe65a31a74769757 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 10 Dec 2024 19:15:49 +0100 Subject: [PATCH 129/162] feature: Add 'otp-shaded.jar' as an Maven published artifact. --- {shaded-jar => otp-shaded}/pom.xml | 87 +++++++++++++++++------------- pom.xml | 8 ++- 2 files changed, 55 insertions(+), 40 deletions(-) rename {shaded-jar => otp-shaded}/pom.xml (57%) diff --git a/shaded-jar/pom.xml b/otp-shaded/pom.xml similarity index 57% rename from shaded-jar/pom.xml rename to otp-shaded/pom.xml index cf48c89271c..1758813b3c5 100644 --- a/shaded-jar/pom.xml +++ b/otp-shaded/pom.xml @@ -8,12 +8,10 @@ otp-root 2.7.0-SNAPSHOT - shaded-jar - pom + otp-shaded OpenTripPlanner - Shaded Jar - skip false @@ -27,39 +25,25 @@ - - - - org.apache.maven.plugins + maven-shade-plugin - - ${project.build.directory}/otp-${project.version}-shaded.jar + ${skipShadeJar} + false + false @@ -71,12 +55,6 @@ - ${skipShadeJar} - - true - - false @@ -107,6 +85,39 @@ + + + + + + + com.hubspot.maven.plugins + prettier-maven-plugin + + true + + diff --git a/pom.xml b/pom.xml index edc1ab5efd7..b3166900806 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ raptor gtfs-realtime-protobuf application - shaded-jar + otp-shaded @@ -110,6 +110,11 @@ maven-shade-plugin 3.6.0 + + com.hubspot.maven.plugins + prettier-maven-plugin + ${plugin.prettier.version} + @@ -290,7 +295,6 @@ com.hubspot.maven.plugins prettier-maven-plugin - ${plugin.prettier.version} ${plugin.prettier.skip} 2.0.0 From f85cf52e094920a5acb53d6a7b3bae9ee7a5aca8 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 10 Dec 2024 20:31:15 +0100 Subject: [PATCH 130/162] doc: Fix references to the otp-shaded jar in doc. --- .github/workflows/performance-test.yml | 2 +- README.md | 15 ++++++++++----- doc/user/Basic-Tutorial.md | 18 +++++++++--------- doc/user/Developers-Guide.md | 2 +- doc/user/Getting-OTP.md | 25 +++++++++++-------------- script/otp | 2 +- script/run-and-test-otp | 5 +++-- 7 files changed, 36 insertions(+), 33 deletions(-) diff --git a/.github/workflows/performance-test.yml b/.github/workflows/performance-test.yml index 1ec537a0b4f..5e55e158a6f 100644 --- a/.github/workflows/performance-test.yml +++ b/.github/workflows/performance-test.yml @@ -88,7 +88,7 @@ jobs: - name: Build graph if: matrix.profile == 'core' || github.ref == 'refs/heads/dev-2.x' run: | - cp shaded-jar/target/otp-*-SNAPSHOT-shaded.jar otp.jar + cp otp-shaded/target/otp-shaded-*-SNAPSHOT.jar otp.jar java -Xmx32G -jar otp.jar --build --save test/performance/${{ matrix.location }}/ - name: Run speed test diff --git a/README.md b/README.md index ec66e694e8c..4ee4e02e69b 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,11 @@ We run a speed test (included in the code) to measure the performance for every ## Repository layout -The main Java server code is in `application/src/main/`. OTP also includes a Javascript client based on the -MapLibre mapping library in `client/src/`. This client is now used for testing, with most major -deployments building custom clients from reusable components. The Maven build produces a unified ("shaded") -JAR file at `shaded-jar/target/otp-VERSION.jar` containing all necessary code and dependencies to run OpenTripPlanner. +The main Java server code is in `application/src/main/`. OTP also includes a Javascript client +based on the MapLibre mapping library in `client/src/`. This client is now used for testing, with +most major deployments building custom clients from reusable components. The Maven build produces a +unified ("shaded") JAR file at `otp-shaded/target/otp-shaded-VERSION.jar` containing all necessary +code and dependencies to run OpenTripPlanner. Additional information and instructions are available in the [main documentation](http://docs.opentripplanner.org/en/dev-2.x/), including a @@ -59,7 +60,11 @@ the world. ## Getting in touch -The fastest way to get help is to use our [Gitter chat room](https://gitter.im/opentripplanner/OpenTripPlanner) where most of the core developers are. Bug reports may be filed via the Github [issue tracker](https://github.com/openplans/OpenTripPlanner/issues). The OpenTripPlanner [mailing list](http://groups.google.com/group/opentripplanner-users) is used almost exclusively for project announcements. The mailing list and issue tracker are not intended for support questions or discussions. Please use the chat for this purpose. Other details of [project governance](http://docs.opentripplanner.org/en/dev-2.x/Governance/) can be found in the main documentation. +The fastest way to get help is to use our [Gitter chat room](https://gitter.im/opentripplanner/OpenTripPlanner) where most of the core developers +are. Bug reports may be filed via the Github [issue tracker](https://github.com/openplans/OpenTripPlanner/issues). The OpenTripPlanner [mailing list](http://groups.google.com/group/opentripplanner-users) +is used almost exclusively for project announcements. The mailing list and issue tracker are not +intended for support questions or discussions. Please use the chat for this purpose. Other details +of [project governance](http://docs.opentripplanner.org/en/dev-2.x/Governance/) can be found in the main documentation. ## OTP Ecosystem diff --git a/doc/user/Basic-Tutorial.md b/doc/user/Basic-Tutorial.md index 64eeded6b01..e51ab44232a 100644 --- a/doc/user/Basic-Tutorial.md +++ b/doc/user/Basic-Tutorial.md @@ -18,9 +18,9 @@ JAR containing all other libraries needed for OTP to work, and is available from repository. You will be able to go to [the OTP directory at Maven Central](https://repo1.maven.org/maven2/org/opentripplanner/otp/), navigate to -the [directory of releases](https://repo1.maven.org/maven2/org/opentripplanner/otp/2.6.0/), +the [directory of releases](https://repo1.maven.org/maven2/org/opentripplanner/otp-shaded/2.7.0/), and download -the [file with `shaded.jar` suffix](https://repo1.maven.org/maven2/org/opentripplanner/otp/2.6.0/otp-2.6.0-shaded.jar) +the [file with `otp-shaded` artifactId](https://repo1.maven.org/maven2/org/opentripplanner/otp-shaded/2.7.0/otp-shaded-2.7.0.jar) . You may also want to get your own copy of the OTP source code @@ -95,7 +95,7 @@ for cropping and filtering OSM data. ## Starting OTP -A typical command to start OTP looks like `java -Xmx2G -jar otp.shaded.jar `. The +A typical command to start OTP looks like `java -Xmx2G -jar otp-shaded-VERSION.jar `. The `-Xmx` parameter sets the limit on how much memory OTP is allowed to consume. GTFS and OSM data sets are often very large, and OTP is relatively memory-hungry. You will need at least 1GB of memory when working with the Portland TriMet data set, and several gigabytes for larger inputs. @@ -129,7 +129,7 @@ below and in other tutorials. The simplest way to use OTP is to build a graph in a single step and start a server immediately, without saving it to disk. The command to do so is: - $ java -Xmx2G -jar otp-2.6.0-shaded.jar --build --serve /home/username/otp + $ java -Xmx2G -jar otp-shaded-2.7.0.jar --build --serve /home/username/otp where `/home/username/otp` should be the directory where you put your configuration and input files. @@ -154,13 +154,13 @@ build a graph from street and transit data then save it to a file using the `--b command line parameters together. If for example your current working directory (`.`) contains the input files and the OTP JAR file, you can use this command: - $ java -Xmx2G -jar otp-2.6.0-shaded.jar --build --save . + $ java -Xmx2G -jar otp-shaded-2.7.0.jar --build --save . This will produce a file called `graph.obj` in the same directory as the inputs. The server can then be started later using the `--load` parameter, and will read this file instead of building the graph from scratch: - $ java -Xmx2G -jar otp-2.6.0-shaded.jar --load . + $ java -Xmx2G -jar otp-shaded-2.7.0.jar --load . Another reason to perform these two phases separately is that the building process loads the entire GTFS and OSM data sets into memory, so can require significantly more memory than just running a @@ -177,16 +177,16 @@ graph once, and then layer transit data on top of the streets to make the final Again assuming the input files and OTP JAR file are in the current working directory, you can build a street graph with OSM and elevation data only (ignoring transit input files) with this command: - $ java -Xmx2G -jar otp-2.6.0-shaded.jar --buildStreet . + $ java -Xmx2G -jar otp-shaded-2.7.0.jar --buildStreet . Then, to build a graph layering transit data on top of the saved street graph (built using the previous command): - $ java -Xmx2G -jar otp-2.6.0-shaded.jar --loadStreet --save . + $ java -Xmx2G -jar otp-shaded-2.7.0.jar --loadStreet --save . Finally, the server can be started using the `--load` parameter: - $ java -Xmx2G -jar otp-2.6.0-shaded.jar --load . + $ java -Xmx2G -jar otp-shaded-2.7.0.jar --load . ## Command Line Switches diff --git a/doc/user/Developers-Guide.md b/doc/user/Developers-Guide.md index f5633ff17b1..d109f7a9170 100644 --- a/doc/user/Developers-Guide.md +++ b/doc/user/Developers-Guide.md @@ -232,7 +232,7 @@ Releases are performed off the master branch, and are tagged with git annotated OpenTripPlanner is currently configured such that builds including releases upload JAR files to GitHub Packages. This is not the most convenient place for end users to find and download the files. -Therefore we also attach a stand-alone "shaded" JAR to the GitHub tag/release page, and have +Therefore, we also attach a stand-alone "shaded" JAR to the GitHub tag/release page. We have historically also uploaded Maven artifacts to Maven Central including compiled and source code JARs as well as the "shaded" JAR containing all dependencies, allowing stand-alone usage. This release process is handled by the Sonatype Nexus Staging plugin, which is no longer configured in the diff --git a/doc/user/Getting-OTP.md b/doc/user/Getting-OTP.md index ea0bac0df90..c9585a9caf5 100644 --- a/doc/user/Getting-OTP.md +++ b/doc/user/Getting-OTP.md @@ -6,8 +6,8 @@ OpenTripPlanner is distributed as a single stand-alone runnable JAR file. We cre release page on GitHub for each release version, and also deploy them to the Maven Central repository. You can go to the [release pages on GitHub](https://github.com/opentripplanner/OpenTripPlanner/releases) -or [the OTP directory at Maven Central](https://repo1.maven.org/maven2/org/opentripplanner/otp/), -navigate to the highest version number, and download the file whose name ends with `shaded.jar`. +or [the OTP directory at Maven Central](https://repo1.maven.org/maven2/org/opentripplanner/otp-shaded/), +navigate to the highest version number, and download the jar file `otp-shaded-VERSION.jar`. Note that version numbers like `v2.1.0-rc1` or `v2.6.0-SNAPSHOT` refer to development builds _ before_ the release version `v2.6.0`. The existence of a build `vX.Y.Z-SNAPSHOT` does not mean @@ -64,8 +64,8 @@ OTP. If all goes well you should see a success message like the following: [INFO] ------------------------------------------------------------------------ ``` -This build process should produce a JAR file called `otp-x.y.z-shaded.jar` in the -`shaded-jar/target/` directory which contains all the compiled OTP classes and their dependencies +This build process should produce a JAR file called `otp-shaded-x.y.z.jar` in the +`otp-shaded/target/` directory which contains all the compiled OTP classes and their dependencies (the external libraries they use). The shell script called 'otp' in the root of the cloned repository will start the main class of that JAR file under a Java virtual machine, so after the Maven build completes you should be able to run `./otp --help` and see an OTP help message including command line @@ -92,14 +92,11 @@ git clean -df mvn clean package -DskipTests ``` -Please note that the build process creates two distinct versions of the OTP JAR file. The one ending -in `-shaded.jar` -is much bigger because it contains copies of all the external libraries that OTP uses. It serves as -a stand-alone runnable distribution of OTP. The one with a version number but without the -word `shaded` -contains only OTP itself, without any external dependencies. This JAR is useful when OTP is included -as a component in some other project, where we want the dependency management system to gather all -the external libraries automatically. +Please note that the build process multiple jar files. The `otp-shaded-VERSION.jar` is much bigger +because it contains copies of all the external libraries that OTP uses. It serves as a stand-alone +runnable distribution of OTP. The other jar files are regular Java jar files. These JAR files are +useful when OTP is included as a component in some other project, where we want the dependency +management system to gather all the external libraries automatically. ## Maven Repository @@ -110,8 +107,8 @@ file) to the Maven repository, from which it can be automatically included in ot This repository is machine-readable (by Maven or other build systems) and also provides human readable directory listings via HTTP. You can fetch an OTP JAR from this repository by constructing -the proper URL for the release you want. For example, release 2.6.0 will be found -at `https://repo1.maven.org/maven2/org/opentripplanner/otp/2.6.0/otp-2.6.0-shaded.jar`. +the proper URL for the release you want. For example, release 2.7.0 will be found +at `https://repo1.maven.org/maven2/org/opentripplanner/otp-shaded/2.7.0/otp-shaded-2.7.0-shaded.jar`. To make use of OTP in another Maven project, you must specify it as a dependency in that project's `pom.xml`: diff --git a/script/otp b/script/otp index 58e7c7b65fa..5dae485f663 100755 --- a/script/otp +++ b/script/otp @@ -3,6 +3,6 @@ # Standalone OTP can build a graph, run an OTP API server, # or any combination of these. -JAR_FILE="$(dirname $0)/target/${project.build.finalName}-shaded.jar" +JAR_FILE="$(dirname $0)/../otp-shaded/target/otp-shaded-2.7.0-SNAPSHOT.jar" java -Xmx8G -jar "${JAR_FILE}" "$@" diff --git a/script/run-and-test-otp b/script/run-and-test-otp index 0fc322e3a86..d1c7c9bdbe1 100755 --- a/script/run-and-test-otp +++ b/script/run-and-test-otp @@ -58,6 +58,7 @@ fi # is not working as expected. OTP_LOG=target/otp.log RESPONSE_FILE=target/response.json +SHADED_TARGET=otp-shaded/target if [ "$1" != "--skipCompile" ] && [ "$1" != "-c" ]; then @@ -66,8 +67,8 @@ if [ "$1" != "--skipCompile" ] && [ "$1" != "-c" ]; then fi echo "Start OTP, output: $OTP_LOG" -mv target/otp-*-shaded.jar target/otp-shaded.jar -java -Xmx16G -jar target/otp-shaded.jar ${DATA_DIR} --build --save --serve > ${OTP_LOG} & +mv ${SHADED_TARGET}/otp-shaded-*.jar ${SHADED_TARGET}/otp-shaded.jar +java -Xmx16G -jar ${SHADED_TARGET}/otp-shaded.jar ${DATA_DIR} --build --save --serve > ${OTP_LOG} & OTP_PID=$! tail -F ${OTP_LOG} & From c43e2b9c858d30d9b49f89991dca02ba92870a01 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Mon, 9 Dec 2024 20:41:00 -0800 Subject: [PATCH 131/162] add javadoc, tests --- .../org/opentripplanner/ext/fares/impl/OrcaFareService.java | 3 +-- .../java/org/opentripplanner/transit/model/basic/Money.java | 6 +++++- .../java/org/opentripplanner/routing/core/MoneyTest.java | 6 ++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java b/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java index 6ff38ad243e..2b732c8dbc4 100644 --- a/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java +++ b/application/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java @@ -25,7 +25,6 @@ import org.opentripplanner.transit.model.basic.Money; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; -import org.opentripplanner.transit.model.organization.Agency; public class OrcaFareService extends DefaultFareService { @@ -401,7 +400,7 @@ private Money getWashingtonStateFerriesFare( return switch (fareType) { case youth, electronicYouth -> Money.ZERO_USD; case regular, electronicRegular, electronicSpecial -> defaultFare; - case senior, electronicSenior -> defaultFare.half().roundDownToNearestFiveCents(); + case senior, electronicSenior -> defaultFare.half().roundDownToNearestFiveMinorUnits(); }; } diff --git a/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java b/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java index ea0aac85cc8..5cfcf185af8 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java +++ b/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java @@ -147,7 +147,11 @@ public Money half() { return new Money(currency, IntUtils.round(amount / 2f)); } - public Money roundDownToNearestFiveCents() { + /** + * Returns the instance rounded down to the nearest multiple of 5 cents + * So $0.25 becomes $0.10 + */ + public Money roundDownToNearestFiveMinorUnits() { int rounded = (this.minorUnitAmount() / 5) * 5; return new Money(Money.USD, rounded); } diff --git a/application/src/test/java/org/opentripplanner/routing/core/MoneyTest.java b/application/src/test/java/org/opentripplanner/routing/core/MoneyTest.java index 01392e64493..4fdb74d5340 100644 --- a/application/src/test/java/org/opentripplanner/routing/core/MoneyTest.java +++ b/application/src/test/java/org/opentripplanner/routing/core/MoneyTest.java @@ -91,6 +91,12 @@ void half() { assertEquals(Money.usDollars(0.38f), Money.usDollars(0.75f).half()); } + @Test + void roundDownToNearestFiveMinorUnits() { + assertEquals(Money.usDollars(0.1f), Money.usDollars(0.11f).roundDownToNearestFiveMinorUnits()); + assertEquals(Money.usDollars(0.5f), Money.usDollars(0.54f).roundDownToNearestFiveMinorUnits()); + } + @Test void greaterThan() { assertTrue(twoDollars.greaterThan(oneDollar)); From 3ae6eedf3c2b006cbbb3bcd9ceb07a46e5c677f2 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Wed, 11 Dec 2024 16:40:38 +0200 Subject: [PATCH 132/162] Change logging in transfer calculations. --- .../module/DirectTransferGenerator.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 626c85312a7..dcd56822a09 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -209,21 +209,17 @@ public void buildGraph() { nTransfersTotal, nLinkedStops ); - for (StreetMode mode : transferRequests + transferRequests .stream() .map(transferProfile -> transferProfile.journey().transfer().mode()) .distinct() - .toList()) { - LOG.info( - "Created {} transfers for mode {}.", - transfersByStop - .values() - .stream() - .filter(pathTransfer -> pathTransfer.getModes().contains(mode)) - .count(), - mode + .forEach(mode -> + LOG.info( + "Created {} transfers for mode {}.", + timetableRepository.getTransfersByMode(mode).size(), + mode + ) ); - } } /** From ce2bf50e33091d75b56d52af18bd209f10c7606e Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Wed, 11 Dec 2024 11:55:11 -0800 Subject: [PATCH 133/162] fix silly math error in comment --- .../java/org/opentripplanner/transit/model/basic/Money.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java b/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java index 5cfcf185af8..64238ad7afd 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java +++ b/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java @@ -149,7 +149,7 @@ public Money half() { /** * Returns the instance rounded down to the nearest multiple of 5 cents - * So $0.25 becomes $0.10 + * So $0.14 becomes $0.10 */ public Money roundDownToNearestFiveMinorUnits() { int rounded = (this.minorUnitAmount() / 5) * 5; From 4141ce9e49205747aa34ec795fa547ab760c19c1 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Wed, 11 Dec 2024 13:37:02 -0800 Subject: [PATCH 134/162] use currency from instance --- .../java/org/opentripplanner/transit/model/basic/Money.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java b/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java index 64238ad7afd..8f28614069c 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java +++ b/application/src/main/java/org/opentripplanner/transit/model/basic/Money.java @@ -153,7 +153,7 @@ public Money half() { */ public Money roundDownToNearestFiveMinorUnits() { int rounded = (this.minorUnitAmount() / 5) * 5; - return new Money(Money.USD, rounded); + return new Money(currency, rounded); } /** From dfe1d412c87239e48b26b3852af90cda87774b1c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 12 Dec 2024 08:27:55 +0100 Subject: [PATCH 135/162] Add schedule for Dagger and Jackson --- renovate.json5 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/renovate.json5 b/renovate.json5 index a5838fb3ff0..557857bf54c 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -149,7 +149,8 @@ "com.fasterxml.jackson:{/,}**", "com.fasterxml.jackson.datatype::{/,}**" ], - "minimumReleaseAge": "1 week" + "minimumReleaseAge": "1 week", + "schedule": "on the 13th through 14th day of the month" }, { "description": "Geotools takes a while to publish a changelog and since it pulls in JTS it can change the serialization of the graph", From 4a01328ab31f1c49060d3df33c4b390d90cc3a01 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 12 Dec 2024 12:34:11 +0200 Subject: [PATCH 136/162] Refactor to use method. --- .../src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java | 4 +--- .../opentripplanner/transit/service/TimetableRepository.java | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java b/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java index ccc4f7fe554..73259fbc80e 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java @@ -30,9 +30,7 @@ public class FlexIndex { public FlexIndex(TimetableRepository timetableRepository) { // Flex transfers should only use WALK mode transfers. - StreetMode mode = StreetMode.WALK; - List filteredPathTransfers = timetableRepository.getAllPathTransfers().stream().filter(pathTransfer -> pathTransfer.getModes().contains(mode)).toList(); - for (PathTransfer transfer : filteredPathTransfers) { + for (PathTransfer transfer : timetableRepository.getTransfersByMode(StreetMode.WALK)) { transfersToStop.put(transfer.to, transfer); transfersFromStop.put(transfer.from, transfer); } diff --git a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java index d83c3f9b996..7ef6da379f1 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -16,6 +16,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -434,7 +435,7 @@ public Collection getTransfersByStop(StopLocation stop) { return transfersByStop.get(stop); } - public Collection getTransfersByMode(StreetMode mode) { + public List getTransfersByMode(StreetMode mode) { return transfersByStop .values() .stream() From 95e9ca770bac8f0751f951f3223ffb669d66baa4 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 12 Dec 2024 13:43:09 +0200 Subject: [PATCH 137/162] Rename getTransfersByMode to findTransfers. --- .../ext/java/org/opentripplanner/ext/flex/FlexIndex.java | 2 +- .../graph_builder/module/DirectTransferGenerator.java | 2 +- .../transit/service/TimetableRepository.java | 2 +- .../graph_builder/module/DirectTransferGeneratorTest.java | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java b/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java index 73259fbc80e..86d9766178f 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java @@ -30,7 +30,7 @@ public class FlexIndex { public FlexIndex(TimetableRepository timetableRepository) { // Flex transfers should only use WALK mode transfers. - for (PathTransfer transfer : timetableRepository.getTransfersByMode(StreetMode.WALK)) { + for (PathTransfer transfer : timetableRepository.findTransfers(StreetMode.WALK)) { transfersToStop.put(transfer.to, transfer); transfersFromStop.put(transfer.from, transfer); } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index dcd56822a09..37ac69d88ff 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -216,7 +216,7 @@ public void buildGraph() { .forEach(mode -> LOG.info( "Created {} transfers for mode {}.", - timetableRepository.getTransfersByMode(mode).size(), + timetableRepository.findTransfers(mode).size(), mode ) ); diff --git a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java index 7ef6da379f1..5c57724d90d 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -435,7 +435,7 @@ public Collection getTransfersByStop(StopLocation stop) { return transfersByStop.get(stop); } - public List getTransfersByMode(StreetMode mode) { + public List findTransfers(StreetMode mode) { return transfersByStop .values() .stream() diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index 482cb0afd13..39d2f4b5684 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -274,9 +274,9 @@ public void testPathTransfersWithModesForMultipleRequestsWithPatterns() { ) .buildGraph(); - var walkTransfers = timetableRepository.getTransfersByMode(StreetMode.WALK); - var bikeTransfers = timetableRepository.getTransfersByMode(StreetMode.BIKE); - var carTransfers = timetableRepository.getTransfersByMode(StreetMode.CAR); + var walkTransfers = timetableRepository.findTransfers(StreetMode.WALK); + var bikeTransfers = timetableRepository.findTransfers(StreetMode.BIKE); + var carTransfers = timetableRepository.findTransfers(StreetMode.CAR); assertTransfers( walkTransfers, From e5f1f2ff778ccb87046816b12d10008c6b554427 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 12 Dec 2024 13:21:12 +0000 Subject: [PATCH 138/162] apply review suggestion by @leonardehrenfried --- .../standalone/config/framework/file/IncludeFileDirective.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirective.java b/application/src/main/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirective.java index 05295c8993f..7389babd7fe 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirective.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/framework/file/IncludeFileDirective.java @@ -99,7 +99,7 @@ private String includeFileDirective(String text, String source) { // ignore the optional quotes matched by the directive pattern var json = fileText.trim(); if ( - json.startsWith("{") && json.endsWith("}") || json.startsWith("[") && json.endsWith("]") + (json.startsWith("{") && json.endsWith("}")) || (json.startsWith("[") && json.endsWith("]")) ) { text = text.replace(entry.getKey(), fileText); } else { From d12373f22544733ad6995d59be3473f267024c56 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 12 Dec 2024 13:31:24 +0000 Subject: [PATCH 139/162] use MethodSource instead of FieldSource --- .../utils/color/ColorUtilsTest.java | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/utils/src/test/java/org/opentripplanner/utils/color/ColorUtilsTest.java b/utils/src/test/java/org/opentripplanner/utils/color/ColorUtilsTest.java index 7ad2a424fae..c750a834b79 100644 --- a/utils/src/test/java/org/opentripplanner/utils/color/ColorUtilsTest.java +++ b/utils/src/test/java/org/opentripplanner/utils/color/ColorUtilsTest.java @@ -5,26 +5,29 @@ import static org.opentripplanner.utils.color.ColorUtils.computeBrightness; import java.awt.Color; +import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.FieldSource; +import org.junit.jupiter.params.provider.MethodSource; public class ColorUtilsTest { - static final Arguments[] brightnessExpectations = { - arguments(Color.black, Brightness.DARK), - arguments(Color.green, Brightness.LIGHT), - arguments(Color.blue, Brightness.DARK), - arguments(Color.red, Brightness.LIGHT), - arguments(Color.yellow, Brightness.LIGHT), - arguments(Color.white, Brightness.LIGHT), - arguments(Color.pink, Brightness.LIGHT), - arguments(Color.orange, Brightness.LIGHT), - arguments(Color.cyan, Brightness.LIGHT), - }; + private static Stream brightnessExpectations() { + return Stream.of( + arguments(Color.black, Brightness.DARK), + arguments(Color.green, Brightness.LIGHT), + arguments(Color.blue, Brightness.DARK), + arguments(Color.red, Brightness.LIGHT), + arguments(Color.yellow, Brightness.LIGHT), + arguments(Color.white, Brightness.LIGHT), + arguments(Color.pink, Brightness.LIGHT), + arguments(Color.orange, Brightness.LIGHT), + arguments(Color.cyan, Brightness.LIGHT) + ); + } @ParameterizedTest - @FieldSource("brightnessExpectations") + @MethodSource("brightnessExpectations") void testBrightness(Color color, Brightness brightness) { assertEquals(computeBrightness(color), brightness); } From b0158c86f21743e2fc29187fd8874f821abcf189 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 12 Dec 2024 14:30:26 +0000 Subject: [PATCH 140/162] Add changelog entry for #6307 [ci skip] --- doc/user/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index 56a58be86b2..decbb0a2d4b 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -60,6 +60,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Remove unused GtfsGraphQlApiRentalStationFuzzyMatching feature [#6282](https://github.com/opentripplanner/OpenTripPlanner/pull/6282) - Make debug UI background layers configurable with new file `debug-ui-config.json` [#6295](https://github.com/opentripplanner/OpenTripPlanner/pull/6295) - Better escalator duration control: specific duration from OSM duration tag, default speed from build-config.json [#6268](https://github.com/opentripplanner/OpenTripPlanner/pull/6268) +- Detect JSON array in addition to JSON objects when including a file in the config. [#6307](https://github.com/opentripplanner/OpenTripPlanner/pull/6307) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From ae85810910ceba5090ad32b4f596076c82701b45 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 12 Dec 2024 20:09:02 +0000 Subject: [PATCH 141/162] Upgrade debug client to version 2024/12/2024-12-12T20:08 --- application/src/client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/client/index.html b/application/src/client/index.html index 23748ea08a7..47c9ec2c7c9 100644 --- a/application/src/client/index.html +++ b/application/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug - - + +

From 0caa51a23956adf32544f8b217e48455d5693fea Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 12 Dec 2024 20:09:17 +0000 Subject: [PATCH 142/162] Add changelog entry for #6308 [ci skip] --- doc/user/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index decbb0a2d4b..fde54b3c21a 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -61,6 +61,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Make debug UI background layers configurable with new file `debug-ui-config.json` [#6295](https://github.com/opentripplanner/OpenTripPlanner/pull/6295) - Better escalator duration control: specific duration from OSM duration tag, default speed from build-config.json [#6268](https://github.com/opentripplanner/OpenTripPlanner/pull/6268) - Detect JSON array in addition to JSON objects when including a file in the config. [#6307](https://github.com/opentripplanner/OpenTripPlanner/pull/6307) +- Use WCAG recommendation to fill in GTFS route text color if it is missing [#6308](https://github.com/opentripplanner/OpenTripPlanner/pull/6308) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From fbd1ff17fd92510bc3ea50d44842c8b51deae889 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 12 Dec 2024 22:36:14 +0100 Subject: [PATCH 143/162] Rename 'bogusName' to 'nameIsDerived' --- .../ext/restapi/mapping/WalkStepMapper.java | 2 +- .../apis/gtfs/datafetchers/stepImpl.java | 2 +- .../transmodel/model/plan/PathGuidanceType.java | 2 +- .../module/osm/naming/PortlandCustomNamer.java | 6 +++--- .../inspector/raster/PathwayEdgeRenderer.java | 2 +- .../vector/edge/EdgePropertyMapper.java | 2 +- .../opentripplanner/model/plan/WalkStep.java | 11 ++++++----- .../model/plan/WalkStepBuilder.java | 15 +++++++++------ .../mapping/StatesToWalkStepsMapper.java | 4 ++-- .../opentripplanner/street/model/edge/Edge.java | 2 +- .../street/model/edge/ElevatorAlightEdge.java | 2 +- .../street/model/edge/ElevatorBoardEdge.java | 2 +- .../street/model/edge/PathwayEdge.java | 2 +- .../street/model/edge/StreetEdge.java | 17 +++++++++++------ .../street/model/edge/StreetEdgeBuilder.java | 4 ++-- .../module/osm/naming/SidewalkNamerTest.java | 2 +- .../model/edge/StreetEdgeBuilderTest.java | 6 +++--- .../street/model/edge/StreetEdgeTest.java | 4 ++-- 18 files changed, 48 insertions(+), 39 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/WalkStepMapper.java b/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/WalkStepMapper.java index c5c59e4dc37..8767abe7478 100644 --- a/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/WalkStepMapper.java +++ b/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/WalkStepMapper.java @@ -42,7 +42,7 @@ public ApiWalkStep mapWalkStep(WalkStep domain) { api.exit = domain.getExit(); api.stayOn = domain.isStayOn(); api.area = domain.getArea(); - api.bogusName = domain.getBogusName(); + api.bogusName = domain.nameIsDerived(); if (domain.getStartLocation() != null) { api.lon = domain.getStartLocation().longitude(); api.lat = domain.getStartLocation().latitude(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java index 6bd51ae5f29..f14db6f213f 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java @@ -35,7 +35,7 @@ public DataFetcher area() { @Override public DataFetcher bogusName() { - return environment -> getSource(environment).getBogusName(); + return environment -> getSource(environment).nameIsDerived(); } @Override diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/PathGuidanceType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/PathGuidanceType.java index c52baa0be4c..68406f57d54 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/PathGuidanceType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/plan/PathGuidanceType.java @@ -96,7 +96,7 @@ public static GraphQLObjectType create(GraphQLObjectType elevationStepType) { "The name of this street was generated by the system, so we should only display it once, and generally just display right/left directions" ) .type(Scalars.GraphQLBoolean) - .dataFetcher(environment -> ((WalkStep) environment.getSource()).getBogusName()) + .dataFetcher(environment -> ((WalkStep) environment.getSource()).nameIsDerived()) .build() ) .field( diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java index e05bc736cbb..42bd57ca13c 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java @@ -76,7 +76,7 @@ public void recordEdges(OsmWithTags way, StreetEdgePair edgePair) { edgePair .asIterable() .forEach(edge -> { - if (!edge.hasBogusName()) { + if (!edge.nameIsDerived()) { return; // this edge already has a real name so there is nothing to do } if (isHighwayLink) { @@ -149,7 +149,7 @@ private static String nameAccordingToDestination(StreetEdge e, int maxDepth) { return null; } for (StreetEdge out : e.getToVertex().getOutgoingStreetEdges()) { - if (out.hasBogusName()) { + if (out.nameIsDerived()) { String name = nameAccordingToDestination(out, maxDepth - 1); if (name == null) { continue; @@ -170,7 +170,7 @@ private static String nameAccordingToOrigin(StreetEdge e, int maxDepth) { return null; } for (StreetEdge in : e.getFromVertex().getIncomingStreetEdges()) { - if (in.hasBogusName()) { + if (in.nameIsDerived()) { String name = nameAccordingToOrigin(in, maxDepth - 1); if (name == null) { continue; diff --git a/application/src/main/java/org/opentripplanner/inspector/raster/PathwayEdgeRenderer.java b/application/src/main/java/org/opentripplanner/inspector/raster/PathwayEdgeRenderer.java index 0da19670264..27328b1ba1f 100644 --- a/application/src/main/java/org/opentripplanner/inspector/raster/PathwayEdgeRenderer.java +++ b/application/src/main/java/org/opentripplanner/inspector/raster/PathwayEdgeRenderer.java @@ -22,7 +22,7 @@ public Optional renderEdge(Edge e) { StringBuilder sb = new StringBuilder(); - if (!pwe.hasBogusName()) { + if (!pwe.nameIsDerived()) { sb.append("name=").append(pwe.getName()).append(", "); } diff --git a/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java index 83b677a62ce..1363ba7e6be 100644 --- a/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java +++ b/application/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -38,7 +38,7 @@ private static List mapStreetEdge(StreetEdge se) { kv("noThruTraffic", noThruTrafficAsString(se)), kv("wheelchairAccessible", se.isWheelchairAccessible()) ); - if (se.hasBogusName()) { + if (se.nameIsDerived()) { props.addFirst(kv("name", "%s (generated)".formatted(se.getName().toString()))); } else { props.addFirst(kv("name", se.getName().toString())); diff --git a/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java b/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java index 45f2b5da701..c2c2b2c609e 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -39,7 +39,7 @@ public final class WalkStep { private final Set streetNotes; private final boolean area; - private final boolean bogusName; + private final boolean nameIsDerived; private final double angle; private final boolean walkingBike; @@ -57,7 +57,7 @@ public final class WalkStep { Set streetNotes, String exit, ElevationProfile elevationProfile, - boolean bogusName, + boolean nameIsDerived, boolean walkingBike, boolean area, boolean stayOn, @@ -71,7 +71,7 @@ public final class WalkStep { this.directionText = directionText; this.streetNotes = Set.copyOf(Objects.requireNonNull(streetNotes)); this.startLocation = Objects.requireNonNull(startLocation); - this.bogusName = bogusName; + this.nameIsDerived = nameIsDerived; this.angle = DoubleUtils.roundTo2Decimals(angle); this.walkingBike = walkingBike; this.area = area; @@ -148,9 +148,10 @@ public boolean getArea() { /** * The name of this street was generated by the system, so we should only display it once, and * generally just display right/left directions + * @see Edge#nameIsDerived() */ - public boolean getBogusName() { - return bogusName; + public boolean nameIsDerived() { + return nameIsDerived; } /** diff --git a/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java b/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java index 8d4df6634fd..a972e62e709 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -17,7 +17,7 @@ public class WalkStepBuilder { private final Set streetNotes = new HashSet<>(); private I18NString directionText; private WgsCoordinate startLocation; - private boolean bogusName = false; + private boolean nameIsDerived = false; private double angle; private boolean walkingBike = false; private boolean area = false; @@ -44,8 +44,8 @@ public WalkStepBuilder withStartLocation(WgsCoordinate startLocation) { return this; } - public WalkStepBuilder withBogusName(boolean bogusName) { - this.bogusName = bogusName; + public WalkStepBuilder withBogusName(boolean nameIsDerived) { + this.nameIsDerived = nameIsDerived; return this; } @@ -140,8 +140,11 @@ public I18NString directionText() { return directionText; } - public boolean bogusName() { - return bogusName; + /** + * @see Edge#nameIsDerived() + */ + public boolean nameIsDerived() { + return nameIsDerived; } public RelativeDirection relativeDirection() { @@ -157,7 +160,7 @@ public WalkStep build() { streetNotes, exit, elevationProfile, - bogusName, + nameIsDerived, walkingBike, area, stayOn, diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index 32f5ccf533b..bf140dfbc53 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -430,7 +430,7 @@ private boolean continueOnSameStreet(Edge edge, String streetNameNoParens) { return !( current.directionText().toString() != null && !(java.util.Objects.equals(current.directionTextNoParens(), streetNameNoParens)) && - (!current.bogusName() || !edge.hasBogusName()) + (!current.nameIsDerived() || !edge.nameIsDerived()) ); } @@ -543,7 +543,7 @@ private WalkStepBuilder createWalkStep(State forwardState, State backState) { .builder() .withDirectionText(en.getName()) .withStartLocation(new WgsCoordinate(backState.getVertex().getCoordinate())) - .withBogusName(en.hasBogusName()) + .withBogusName(en.nameIsDerived()) .withAngle(DirectionUtils.getFirstAngle(forwardState.getBackEdge().getGeometry())) .withWalkingBike(forwardState.isBackWalkingBike()) .withArea(forwardState.getBackEdge() instanceof AreaEdge) diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/Edge.java b/application/src/main/java/org/opentripplanner/street/model/edge/Edge.java index 8e4126b4e59..b24dbb2e36e 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/Edge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/Edge.java @@ -152,7 +152,7 @@ public String getDefaultName() { * Returns false if the field reflects the real world name, like "Fifth Avenue", * "Hauptstraße" or "Øvre Holmegate". */ - public boolean hasBogusName() { + public boolean nameIsDerived() { return false; } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/ElevatorAlightEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/ElevatorAlightEdge.java index ca3b0b4b0f7..8fa88009b73 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/ElevatorAlightEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/ElevatorAlightEdge.java @@ -76,7 +76,7 @@ public I18NString getName() { * @author mattwigway */ @Override - public boolean hasBogusName() { + public boolean nameIsDerived() { return false; } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/ElevatorBoardEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/ElevatorBoardEdge.java index 9bf0d67afa5..cacc97fc2f1 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/ElevatorBoardEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/ElevatorBoardEdge.java @@ -65,7 +65,7 @@ public I18NString getName() { * never included in plans. */ @Override - public boolean hasBogusName() { + public boolean nameIsDerived() { return true; } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/PathwayEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/PathwayEdge.java index 703658fdda4..cfa82ba85b5 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/PathwayEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/PathwayEdge.java @@ -167,7 +167,7 @@ public I18NString getName() { } @Override - public boolean hasBogusName() { + public boolean nameIsDerived() { return signpostedAs == null; } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 468da359e0a..02675b1202c 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -55,7 +55,10 @@ public class StreetEdge /** If you have more than 16 flags, increase flags to short or int */ static final int BACK_FLAG_INDEX = 0; static final int ROUNDABOUT_FLAG_INDEX = 1; - static final int HASBOGUSNAME_FLAG_INDEX = 2; + /** + * @see Edge#nameIsDerived() + */ + static final int NAME_IS_DERIVED_FLAG_INDEX = 2; static final int MOTOR_VEHICLE_NOTHRUTRAFFIC = 3; static final int STAIRS_FLAG_INDEX = 4; static final int SLOPEOVERRIDE_FLAG_INDEX = 5; @@ -444,20 +447,22 @@ public I18NString getName() { } /** - * Update the name of the edge after it has been constructed. This method also sets the bogusName + * Update the name of the edge after it has been constructed. This method also sets the nameIsDerived * property to false, indicating to the code that maps from edges to steps that this is a real * street name. - * @see Edge#hasBogusName() + * @see Edge#nameIsDerived() */ public void setName(I18NString name) { this.name = name; - this.flags = BitSetUtils.set(flags, HASBOGUSNAME_FLAG_INDEX, false); + this.flags = BitSetUtils.set(flags, NAME_IS_DERIVED_FLAG_INDEX, false); } - public boolean hasBogusName() { - return BitSetUtils.get(flags, HASBOGUSNAME_FLAG_INDEX); + @Override + public boolean nameIsDerived() { + return BitSetUtils.get(flags, NAME_IS_DERIVED_FLAG_INDEX); } + @Override public LineString getGeometry() { return CompactLineStringUtils.uncompactLineString( fromv.getLon(), diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index 99a02205eb6..f189cb68f14 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -3,7 +3,7 @@ import static org.opentripplanner.street.model.edge.StreetEdge.BACK_FLAG_INDEX; import static org.opentripplanner.street.model.edge.StreetEdge.BICYCLE_NOTHRUTRAFFIC; import static org.opentripplanner.street.model.edge.StreetEdge.CLASS_LINK; -import static org.opentripplanner.street.model.edge.StreetEdge.HASBOGUSNAME_FLAG_INDEX; +import static org.opentripplanner.street.model.edge.StreetEdge.NAME_IS_DERIVED_FLAG_INDEX; import static org.opentripplanner.street.model.edge.StreetEdge.MOTOR_VEHICLE_NOTHRUTRAFFIC; import static org.opentripplanner.street.model.edge.StreetEdge.ROUNDABOUT_FLAG_INDEX; import static org.opentripplanner.street.model.edge.StreetEdge.SLOPEOVERRIDE_FLAG_INDEX; @@ -175,7 +175,7 @@ public B withLink(boolean link) { } public B withBogusName(boolean hasBogusName) { - flags = BitSetUtils.set(flags, HASBOGUSNAME_FLAG_INDEX, hasBogusName); + flags = BitSetUtils.set(flags, NAME_IS_DERIVED_FLAG_INDEX, hasBogusName); return instance(); } diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java index cbfb7b76c6f..851a71620bf 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java @@ -58,7 +58,7 @@ void postprocess() { assertNotEquals(sidewalk.edge.getName(), pryorStreet.edge.getName()); builder.postProcess(new SidewalkNamer()); assertEquals(sidewalk.edge.getName(), pryorStreet.edge.getName()); - assertFalse(sidewalk.edge.hasBogusName()); + assertFalse(sidewalk.edge.nameIsDerived()); } private static class ModelBuilder { diff --git a/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeBuilderTest.java b/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeBuilderTest.java index d447916d21d..aeddb9221c9 100644 --- a/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeBuilderTest.java +++ b/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeBuilderTest.java @@ -32,7 +32,7 @@ class StreetEdgeBuilderTest { private static final float WALK_SAFETY_FACTOR = 0.5f; private static final float BICYCLE_SAFETY_FACTOR = 0.4f; private static final boolean SLOPE_OVERRIDE = false; - private static final boolean BOGUS_NAME = true; + private static final boolean NAME_IS_DERIVED = true; private static final boolean BICYCLE_NO_THRU_TRAFFIC = true; private static final boolean MOTOR_VEHICLE_NO_THRU_TRAFFIC = true; private static final boolean WALK_NO_THRU_TRAFFIC = true; @@ -82,7 +82,7 @@ private static StreetEdge buildStreetEdge() { .withBack(BACK) .withStairs(STAIRS) .withSlopeOverride(SLOPE_OVERRIDE) - .withBogusName(BOGUS_NAME) + .withBogusName(NAME_IS_DERIVED) .withWalkNoThruTraffic(WALK_NO_THRU_TRAFFIC) .withBicycleNoThruTraffic(BICYCLE_NO_THRU_TRAFFIC) .withMotorVehicleNoThruTraffic(MOTOR_VEHICLE_NO_THRU_TRAFFIC) @@ -100,7 +100,7 @@ private static void assertAllProperties(StreetEdge streetEdge) { assertEquals(WALK_SAFETY_FACTOR, streetEdge.getWalkSafetyFactor()); assertEquals(BICYCLE_SAFETY_FACTOR, streetEdge.getBicycleSafetyFactor()); assertEquals(SLOPE_OVERRIDE, streetEdge.isSlopeOverride()); - assertEquals(BOGUS_NAME, streetEdge.hasBogusName()); + assertEquals(NAME_IS_DERIVED, streetEdge.nameIsDerived()); assertEquals(WALK_NO_THRU_TRAFFIC, streetEdge.isWalkNoThruTraffic()); assertEquals(BICYCLE_NO_THRU_TRAFFIC, streetEdge.isBicycleNoThruTraffic()); assertEquals(MOTOR_VEHICLE_NO_THRU_TRAFFIC, streetEdge.isMotorVehicleNoThruTraffic()); diff --git a/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java b/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java index ef2bacb91dd..bbc77656a23 100644 --- a/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java +++ b/application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java @@ -413,11 +413,11 @@ void setName() { .buildAndConnect(); assertEquals(path, edge.getName()); - assertTrue(edge.hasBogusName()); + assertTrue(edge.nameIsDerived()); var mainStreet = I18NString.of("Main Street"); edge.setName(mainStreet); assertEquals(mainStreet, edge.getName()); - assertFalse(edge.hasBogusName()); + assertFalse(edge.nameIsDerived()); } } From 4c6db7fa912eebce17adcc8ae06ae184b49bd67c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 13 Dec 2024 08:04:25 +0100 Subject: [PATCH 144/162] Rename missing method --- .../java/org/opentripplanner/model/plan/WalkStepBuilder.java | 2 +- .../routing/algorithm/mapping/StatesToWalkStepsMapper.java | 4 ++-- .../opentripplanner/street/model/edge/StreetEdgeBuilder.java | 2 +- .../java/org/opentripplanner/model/plan/WalkStepTest.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java b/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java index a972e62e709..b2f9e1f7510 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -44,7 +44,7 @@ public WalkStepBuilder withStartLocation(WgsCoordinate startLocation) { return this; } - public WalkStepBuilder withBogusName(boolean nameIsDerived) { + public WalkStepBuilder withNameIsDerived(boolean nameIsDerived) { this.nameIsDerived = nameIsDerived; return this; } diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index bf140dfbc53..4ce1c616e65 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -525,7 +525,7 @@ private void createAndSaveStep( addStep( createWalkStep(forwardState, backState) .withDirectionText(name) - .withBogusName(false) + .withNameIsDerived(false) .withDirections(lastAngle, DirectionUtils.getFirstAngle(edge.getGeometry()), false) .withRelativeDirection(direction) .addDistance(edge.getDistanceMeters()) @@ -543,7 +543,7 @@ private WalkStepBuilder createWalkStep(State forwardState, State backState) { .builder() .withDirectionText(en.getName()) .withStartLocation(new WgsCoordinate(backState.getVertex().getCoordinate())) - .withBogusName(en.nameIsDerived()) + .withNameIsDerived(en.nameIsDerived()) .withAngle(DirectionUtils.getFirstAngle(forwardState.getBackEdge().getGeometry())) .withWalkingBike(forwardState.isBackWalkingBike()) .withArea(forwardState.getBackEdge() instanceof AreaEdge) diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java index f189cb68f14..aa168b1453d 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeBuilder.java @@ -3,8 +3,8 @@ import static org.opentripplanner.street.model.edge.StreetEdge.BACK_FLAG_INDEX; import static org.opentripplanner.street.model.edge.StreetEdge.BICYCLE_NOTHRUTRAFFIC; import static org.opentripplanner.street.model.edge.StreetEdge.CLASS_LINK; -import static org.opentripplanner.street.model.edge.StreetEdge.NAME_IS_DERIVED_FLAG_INDEX; import static org.opentripplanner.street.model.edge.StreetEdge.MOTOR_VEHICLE_NOTHRUTRAFFIC; +import static org.opentripplanner.street.model.edge.StreetEdge.NAME_IS_DERIVED_FLAG_INDEX; import static org.opentripplanner.street.model.edge.StreetEdge.ROUNDABOUT_FLAG_INDEX; import static org.opentripplanner.street.model.edge.StreetEdge.SLOPEOVERRIDE_FLAG_INDEX; import static org.opentripplanner.street.model.edge.StreetEdge.STAIRS_FLAG_INDEX; diff --git a/application/src/test/java/org/opentripplanner/model/plan/WalkStepTest.java b/application/src/test/java/org/opentripplanner/model/plan/WalkStepTest.java index 7199c7bea60..4556cb90de0 100644 --- a/application/src/test/java/org/opentripplanner/model/plan/WalkStepTest.java +++ b/application/src/test/java/org/opentripplanner/model/plan/WalkStepTest.java @@ -17,7 +17,7 @@ public void testRelativeDirection() { WalkStepBuilder builder = new WalkStepBuilder() .withDirectionText(new NonLocalizedString("Any")) .withStartLocation(new WgsCoordinate(3.0, 4.0)) - .withBogusName(false) + .withNameIsDerived(false) .withAngle(0.0) .withWalkingBike(false) .withArea(false); From f384f07e2b41d1f14973114790f617bc0a97d093 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Fri, 13 Dec 2024 12:44:42 +0000 Subject: [PATCH 145/162] Add changelog entry for #6331 [ci skip] --- doc/user/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index fde54b3c21a..414e99d69ec 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -62,6 +62,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Better escalator duration control: specific duration from OSM duration tag, default speed from build-config.json [#6268](https://github.com/opentripplanner/OpenTripPlanner/pull/6268) - Detect JSON array in addition to JSON objects when including a file in the config. [#6307](https://github.com/opentripplanner/OpenTripPlanner/pull/6307) - Use WCAG recommendation to fill in GTFS route text color if it is missing [#6308](https://github.com/opentripplanner/OpenTripPlanner/pull/6308) +- Add shaded jar artifact [#6331](https://github.com/opentripplanner/OpenTripPlanner/pull/6331) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From 2592135227598b50d5faf23a86520811a38f4af1 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Fri, 13 Dec 2024 13:59:35 +0100 Subject: [PATCH 146/162] doc: Improve changelog name for PR: #6331 Rename `otp-shaded-jar` artifact and fix deployment to Maven Central --- doc/user/Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index 414e99d69ec..e652c7600d9 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -62,7 +62,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Better escalator duration control: specific duration from OSM duration tag, default speed from build-config.json [#6268](https://github.com/opentripplanner/OpenTripPlanner/pull/6268) - Detect JSON array in addition to JSON objects when including a file in the config. [#6307](https://github.com/opentripplanner/OpenTripPlanner/pull/6307) - Use WCAG recommendation to fill in GTFS route text color if it is missing [#6308](https://github.com/opentripplanner/OpenTripPlanner/pull/6308) -- Add shaded jar artifact [#6331](https://github.com/opentripplanner/OpenTripPlanner/pull/6331) +- Rename `otp-shaded-jar` artifact and fix deployment to Maven Central [#6331](https://github.com/opentripplanner/OpenTripPlanner/pull/6331) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From f9b33c5b23dfdadbeca8dc3c445d69db298bb40d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 13 Dec 2024 14:41:42 +0100 Subject: [PATCH 147/162] Also exclude container build from shaded jar --- otp-shaded/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/otp-shaded/pom.xml b/otp-shaded/pom.xml index 1758813b3c5..129f108e1af 100644 --- a/otp-shaded/pom.xml +++ b/otp-shaded/pom.xml @@ -118,6 +118,13 @@ true + + com.google.cloud.tools + jib-maven-plugin + + true + + From df64c5a7f5ffc4453db5f74d21536770c27cb3ff Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 13 Dec 2024 16:03:01 +0200 Subject: [PATCH 148/162] Changes based on review comments and small optimization. --- .../org/opentripplanner/ext/flex/FlexIndex.java | 1 - .../raptoradapter/transit/RaptorTransferIndex.java | 5 ++--- .../algorithm/raptoradapter/transit/Transfer.java | 13 ++++++------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java b/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java index 86d9766178f..8097bd05c6e 100644 --- a/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java +++ b/application/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java @@ -5,7 +5,6 @@ import com.google.common.collect.Multimap; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.model.PathTransfer; diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/RaptorTransferIndex.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/RaptorTransferIndex.java index 941e1296838..8676e863911 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/RaptorTransferIndex.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/RaptorTransferIndex.java @@ -33,7 +33,6 @@ public static RaptorTransferIndex create( StreetMode mode = request.mode(); for (int i = 0; i < transfersByStopIndex.size(); i++) { - forwardTransfers.add(new ArrayList<>()); reversedTransfers.add(new ArrayList<>()); } @@ -43,14 +42,14 @@ public static RaptorTransferIndex create( var transfers = transfersByStopIndex .get(fromStop) .stream() - .filter(transfer -> transfer.getModes().contains(mode)) + .filter(transfer -> transfer.allowsMode(mode)) .flatMap(s -> s.asRaptorTransfer(request).stream()) .collect( toMap(RaptorTransfer::stop, Function.identity(), (a, b) -> a.c1() < b.c1() ? a : b) ) .values(); - forwardTransfers.get(fromStop).addAll(transfers); + forwardTransfers.add(new ArrayList<>(transfers)); for (RaptorTransfer forwardTransfer : transfers) { reversedTransfers diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java index 648343b9b0f..7e66498f349 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java @@ -1,9 +1,8 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Optional; @@ -35,20 +34,20 @@ public class Transfer { private final List edges; - private final ImmutableSet modes; + private final Set modes; public Transfer(int toStop, List edges, EnumSet modes) { this.toStop = toStop; this.edges = edges; this.distanceMeters = (int) edges.stream().mapToDouble(Edge::getDistanceMeters).sum(); - this.modes = Sets.immutableEnumSet(modes); + this.modes = Collections.unmodifiableSet(modes); } public Transfer(int toStopIndex, int distanceMeters, EnumSet modes) { this.toStop = toStopIndex; this.distanceMeters = distanceMeters; this.edges = null; - this.modes = Sets.immutableEnumSet(modes); + this.modes = Collections.unmodifiableSet(modes); } public List getCoordinates() { @@ -76,8 +75,8 @@ public List getEdges() { return edges; } - public ImmutableSet getModes() { - return modes; + public boolean allowsMode(StreetMode mode) { + return modes.contains(mode); } public Optional asRaptorTransfer(StreetSearchRequest request) { From 9b44df082e097cd13f591c89bbfc1633983a837a Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 13 Dec 2024 16:12:23 +0200 Subject: [PATCH 149/162] Add comments. --- .../src/main/java/org/opentripplanner/model/PathTransfer.java | 1 + .../org/opentripplanner/transit/service/TimetableRepository.java | 1 + 2 files changed, 2 insertions(+) diff --git a/application/src/main/java/org/opentripplanner/model/PathTransfer.java b/application/src/main/java/org/opentripplanner/model/PathTransfer.java index 01aa9af02f5..c4676fdf06f 100644 --- a/application/src/main/java/org/opentripplanner/model/PathTransfer.java +++ b/application/src/main/java/org/opentripplanner/model/PathTransfer.java @@ -60,6 +60,7 @@ public EnumSet getModes() { return EnumSet.copyOf(modes); } + /** Create a new PathTransfer based on the current one with the mode added to the valid modes. */ public PathTransfer withAddedMode(StreetMode mode) { EnumSet newModes = EnumSet.copyOf(modes); newModes.add(mode); diff --git a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java index 5c57724d90d..004391602ae 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -435,6 +435,7 @@ public Collection getTransfersByStop(StopLocation stop) { return transfersByStop.get(stop); } + /** Pre-generated transfers between all stops filtered based on the modes in the PathTransfer's mode field. */ public List findTransfers(StreetMode mode) { return transfersByStop .values() From c75b0a2dee17484a99bc8dc4fe2c3eeb86dbace2 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 13 Dec 2024 16:14:39 +0200 Subject: [PATCH 150/162] Fix spelling. --- .../opentripplanner/transit/service/TimetableRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java index 004391602ae..ff8607f3818 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -435,7 +435,7 @@ public Collection getTransfersByStop(StopLocation stop) { return transfersByStop.get(stop); } - /** Pre-generated transfers between all stops filtered based on the modes in the PathTransfer's mode field. */ + /** Pre-generated transfers between all stops filtered based on the modes in the PathTransfer. */ public List findTransfers(StreetMode mode) { return transfersByStop .values() From 6dc9a4ee878a78d8f7edd9a8095a61ef51c5ec29 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Fri, 13 Dec 2024 13:49:40 +0100 Subject: [PATCH 151/162] review: Apply fixes from review --- .../request/preference/AccessEgressPreferences.java | 8 ++++---- .../api/request/preference/StreetPreferencesTest.java | 11 +++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java index 635c4c6c7b0..289e06e6e02 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java @@ -147,19 +147,19 @@ private static DurationForEnum durationForStreetModeOf(Duration defa } private static TimeAndCostPenaltyForEnum createDefaultCarPenalty() { - var penaltyrBuilder = TimeAndCostPenaltyForEnum.of(StreetMode.class); + var penaltyBuilder = TimeAndCostPenaltyForEnum.of(StreetMode.class); var flexDefaultPenalty = TimeAndCostPenalty.of(TimePenalty.of(ofMinutes(10), 1.3f), 1.3); - penaltyrBuilder.with(StreetMode.FLEXIBLE, flexDefaultPenalty); + penaltyBuilder.with(StreetMode.FLEXIBLE, flexDefaultPenalty); // Add penalty to all car variants with access and/or egress. var carPenalty = TimeAndCostPenalty.of(TimePenalty.of(ofMinutes(20), 2f), 1.5); for (var it : StreetMode.values()) { if (it.includesDriving() && (it.accessAllowed() || it.egressAllowed())) { - penaltyrBuilder.with(it, carPenalty); + penaltyBuilder.with(it, carPenalty); } } - return penaltyrBuilder.build(); + return penaltyBuilder.build(); } } diff --git a/application/src/test/java/org/opentripplanner/routing/api/request/preference/StreetPreferencesTest.java b/application/src/test/java/org/opentripplanner/routing/api/request/preference/StreetPreferencesTest.java index 77875c44fa1..2f2bdd9a5b5 100644 --- a/application/src/test/java/org/opentripplanner/routing/api/request/preference/StreetPreferencesTest.java +++ b/application/src/test/java/org/opentripplanner/routing/api/request/preference/StreetPreferencesTest.java @@ -23,7 +23,7 @@ class StreetPreferencesTest { private static final int ELEVATOR_BOARD_TIME = (int) Duration.ofMinutes(2).toSeconds(); private static final IntersectionTraversalModel INTERSECTION_TRAVERSAL_MODEL = IntersectionTraversalModel.CONSTANT; - private static final TimeAndCostPenalty CAR_PENALTY = TimeAndCostPenalty.of( + private static final TimeAndCostPenalty CAR_TO_PARK_PENALTY = TimeAndCostPenalty.of( TimePenalty.of("2m + 1.5t"), 3.5 ); @@ -34,7 +34,7 @@ class StreetPreferencesTest { .withTurnReluctance(TURN_RELUCTANCE) .withElevator(it -> it.withBoardTime(ELEVATOR_BOARD_TIME)) .withIntersectionTraversalModel(INTERSECTION_TRAVERSAL_MODEL) - .withAccessEgress(it -> it.withPenalty(Map.of(StreetMode.CAR_TO_PARK, CAR_PENALTY))) + .withAccessEgress(it -> it.withPenalty(Map.of(StreetMode.CAR_TO_PARK, CAR_TO_PARK_PENALTY))) .withAccessEgress(it -> it.withMaxDuration(MAX_ACCESS_EGRESS, Map.of())) .withMaxDirectDuration(MAX_DIRECT, Map.of()) .withRoutingTimeout(ROUTING_TIMEOUT) @@ -56,7 +56,10 @@ void accessEgressPenalty() { TimeAndCostPenalty.ZERO, subject.accessEgress().penalty().valueOf(StreetMode.WALK) ); - assertEquals(CAR_PENALTY, subject.accessEgress().penalty().valueOf(StreetMode.CAR_TO_PARK)); + assertEquals( + CAR_TO_PARK_PENALTY, + subject.accessEgress().penalty().valueOf(StreetMode.CAR_TO_PARK) + ); } @Test @@ -112,7 +115,7 @@ void testToString() { "accessEgress: AccessEgressPreferences{penalty: TimeAndCostPenaltyForEnum{" + "CAR: (timePenalty: 20m + 2.0 t, costFactor: 1.50), " + "CAR_TO_PARK: " + - CAR_PENALTY + + CAR_TO_PARK_PENALTY + ", " + "CAR_PICKUP: (timePenalty: 20m + 2.0 t, costFactor: 1.50), " + "CAR_RENTAL: (timePenalty: 20m + 2.0 t, costFactor: 1.50), " + From d881c9d187eab594a49afab9d22e0ceacdd49f8a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 15 Dec 2024 12:51:41 +0000 Subject: [PATCH 152/162] fix(deps): update dependency graphql to v16.10.0 --- .../org/opentripplanner/apis/gtfs/generated/package.json | 2 +- .../org/opentripplanner/apis/gtfs/generated/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/package.json b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/package.json index cd5cbf005f0..e76f41b0cee 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/package.json +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/package.json @@ -14,6 +14,6 @@ "@graphql-codegen/cli": "5.0.3", "@graphql-codegen/java": "4.0.1", "@graphql-codegen/java-resolvers": "3.0.0", - "graphql": "16.9.0" + "graphql": "16.10.0" } } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock index 8ec320cb0e6..6b382a7babb 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock @@ -2152,10 +2152,10 @@ graphql-ws@^5.14.0: resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.14.0.tgz#766f249f3974fc2c48fae0d1fb20c2c4c79cd591" integrity sha512-itrUTQZP/TgswR4GSSYuwWUzrE/w5GhbwM2GX3ic2U7aw33jgEsayfIlvaj7/GcIvZgNMzsPTrE5hqPuFUiE5g== -graphql@16.9.0: - version "16.9.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" - integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== +graphql@16.10.0: + version "16.10.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.10.0.tgz#24c01ae0af6b11ea87bf55694429198aaa8e220c" + integrity sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ== has-flag@^3.0.0: version "3.0.0" From 18215a995a15875e39a20ad29cbf6262bc6291a1 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Sun, 15 Dec 2024 18:03:46 +0200 Subject: [PATCH 153/162] Add comment. --- .../routing/algorithm/raptoradapter/transit/Transfer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java index 7e66498f349..2643067398e 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java @@ -75,6 +75,7 @@ public List getEdges() { return edges; } + /** Check if the given mode is a valid mode for the transfer. */ public boolean allowsMode(StreetMode mode) { return modes.contains(mode); } From 5a3ef6a739cfb46ba8e75c767fe4a74301e8a09d Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Mon, 16 Dec 2024 10:05:47 +0000 Subject: [PATCH 154/162] Add changelog entry for #5393 [ci skip] --- doc/user/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index e652c7600d9..24418e1094d 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -63,6 +63,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Detect JSON array in addition to JSON objects when including a file in the config. [#6307](https://github.com/opentripplanner/OpenTripPlanner/pull/6307) - Use WCAG recommendation to fill in GTFS route text color if it is missing [#6308](https://github.com/opentripplanner/OpenTripPlanner/pull/6308) - Rename `otp-shaded-jar` artifact and fix deployment to Maven Central [#6331](https://github.com/opentripplanner/OpenTripPlanner/pull/6331) +- Add query for cancelled trips to GTFS GraphQL API [#5393](https://github.com/opentripplanner/OpenTripPlanner/pull/5393) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From d305306ae606d8de72a581c1649fcd72f92564b3 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 16 Dec 2024 12:32:12 +0100 Subject: [PATCH 155/162] Show generalized cost in debug UI --- .../ItineraryList/ItineraryDetails.tsx | 4 ++++ .../ItineraryList/ItineraryLegDetails.tsx | 2 +- client/src/static/query/tripQuery.tsx | 2 ++ client/src/style.css | 6 ++++++ client/src/util/formatDuration.ts | 18 ++++++------------ 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/client/src/components/ItineraryList/ItineraryDetails.tsx b/client/src/components/ItineraryList/ItineraryDetails.tsx index 11fe14c73fb..58b705abf52 100644 --- a/client/src/components/ItineraryList/ItineraryDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryDetails.tsx @@ -10,6 +10,10 @@ export function ItineraryDetails({ tripPattern }: { tripPattern: TripPattern }) {tripPattern.legs.map((leg, i) => ( ))} + +
+ Generalized cost: ${tripPattern.generalizedCost} +
); } diff --git a/client/src/components/ItineraryList/ItineraryLegDetails.tsx b/client/src/components/ItineraryList/ItineraryLegDetails.tsx index 65d790902a8..2239fd8d8bf 100644 --- a/client/src/components/ItineraryList/ItineraryLegDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryLegDetails.tsx @@ -21,7 +21,7 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean return (
- {formatDistance(leg.distance)}, {formatDuration(leg.duration)} + {formatDistance(leg.distance)}, {formatDuration(leg.duration)}, ${leg.generalizedCost}
1) { - formatted = `${formatted}${hrs} hrs `; + if (hrs > 0) { + formatted = `${formatted}${hrs}h `; } - if (mins === 1) { - formatted = `${formatted}${mins} min `; - } else if (mins > 1) { - formatted = `${formatted}${mins} mins `; + if (mins > 0) { + formatted = `${formatted}${mins}min `; } - if (secs === 1) { - formatted = `${formatted}${secs} sec `; - } else if (secs > 1) { - formatted = `${formatted}${secs} secs `; + if (secs > 1) { + formatted = `${formatted}${secs}s`; } return formatted; From 8d321e7f1884f18e9214067dc3e4175d886e0c9c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 16 Dec 2024 12:36:07 +0100 Subject: [PATCH 156/162] Add tooltip --- client/src/components/ItineraryList/ItineraryDetails.tsx | 4 +--- client/src/components/ItineraryList/ItineraryLegDetails.tsx | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/client/src/components/ItineraryList/ItineraryDetails.tsx b/client/src/components/ItineraryList/ItineraryDetails.tsx index 58b705abf52..ba2caf4d15d 100644 --- a/client/src/components/ItineraryList/ItineraryDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryDetails.tsx @@ -11,9 +11,7 @@ export function ItineraryDetails({ tripPattern }: { tripPattern: TripPattern }) ))} -
- Generalized cost: ${tripPattern.generalizedCost} -
+
Generalized cost: ${tripPattern.generalizedCost}
); } diff --git a/client/src/components/ItineraryList/ItineraryLegDetails.tsx b/client/src/components/ItineraryList/ItineraryLegDetails.tsx index 2239fd8d8bf..86d2d4db7a4 100644 --- a/client/src/components/ItineraryList/ItineraryLegDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryLegDetails.tsx @@ -21,7 +21,8 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean return (
- {formatDistance(leg.distance)}, {formatDuration(leg.duration)}, ${leg.generalizedCost} + {formatDistance(leg.distance)}, {formatDuration(leg.duration)},{' '} + ${leg.generalizedCost}
Date: Mon, 16 Dec 2024 11:50:08 +0000 Subject: [PATCH 157/162] Add changelog entry for #6293 [ci skip] --- doc/user/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index 24418e1094d..639ada44ac9 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -64,6 +64,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Use WCAG recommendation to fill in GTFS route text color if it is missing [#6308](https://github.com/opentripplanner/OpenTripPlanner/pull/6308) - Rename `otp-shaded-jar` artifact and fix deployment to Maven Central [#6331](https://github.com/opentripplanner/OpenTripPlanner/pull/6331) - Add query for cancelled trips to GTFS GraphQL API [#5393](https://github.com/opentripplanner/OpenTripPlanner/pull/5393) +- Enable mode-specific transfers by storing mode information in transfers [#6293](https://github.com/opentripplanner/OpenTripPlanner/pull/6293) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From 74f2f220eb1a3b22fc42edb17a5c291e630ec5a2 Mon Sep 17 00:00:00 2001 From: OTP Serialization Version Bot Date: Mon, 16 Dec 2024 11:50:35 +0000 Subject: [PATCH 158/162] Bump serialization version id for #6293 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 735cecb3f94..1da8c407bc9 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ - 175 + 176 32.1 2.53 From 65e976af0e9c5e346612096349dfbcbee1b2b79a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 07:38:17 +0000 Subject: [PATCH 159/162] fix(deps): update test dependencies (#6338) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1da8c407bc9..27eada4007b 100644 --- a/pom.xml +++ b/pom.xml @@ -64,7 +64,7 @@ 2.53 2.18.2 3.1.9 - 5.11.3 + 5.11.4 1.14.1 5.6.0 1.5.12 @@ -196,7 +196,7 @@ me.fabriciorby maven-surefire-junit5-tree-reporter - 1.3.0 + 1.4.0 From e130997f19abee98ad0c70a31e9b2dd554106df0 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 17 Dec 2024 09:39:52 +0000 Subject: [PATCH 160/162] Add changelog entry for #6302 [ci skip] --- doc/user/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index 639ada44ac9..1ff69082268 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -65,6 +65,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Rename `otp-shaded-jar` artifact and fix deployment to Maven Central [#6331](https://github.com/opentripplanner/OpenTripPlanner/pull/6331) - Add query for cancelled trips to GTFS GraphQL API [#5393](https://github.com/opentripplanner/OpenTripPlanner/pull/5393) - Enable mode-specific transfers by storing mode information in transfers [#6293](https://github.com/opentripplanner/OpenTripPlanner/pull/6293) +- Add default penalty to all car API modes [#6302](https://github.com/opentripplanner/OpenTripPlanner/pull/6302) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From edcba5a2aa1f9175780d6da3c606cc95cd5176fc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 17 Dec 2024 10:45:41 +0100 Subject: [PATCH 161/162] Use cent sign for generalized cost --- client/src/components/ItineraryList/ItineraryDetails.tsx | 2 +- client/src/components/ItineraryList/ItineraryLegDetails.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/ItineraryList/ItineraryDetails.tsx b/client/src/components/ItineraryList/ItineraryDetails.tsx index ba2caf4d15d..23e67ce9a95 100644 --- a/client/src/components/ItineraryList/ItineraryDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryDetails.tsx @@ -11,7 +11,7 @@ export function ItineraryDetails({ tripPattern }: { tripPattern: TripPattern }) ))} -
Generalized cost: ${tripPattern.generalizedCost}
+
Generalized cost: ¢{tripPattern.generalizedCost}
); } diff --git a/client/src/components/ItineraryList/ItineraryLegDetails.tsx b/client/src/components/ItineraryList/ItineraryLegDetails.tsx index 86d2d4db7a4..04ddc55c574 100644 --- a/client/src/components/ItineraryList/ItineraryLegDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryLegDetails.tsx @@ -22,7 +22,7 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean
{formatDistance(leg.distance)}, {formatDuration(leg.duration)},{' '} - ${leg.generalizedCost} + ¢{leg.generalizedCost}
Date: Tue, 17 Dec 2024 11:51:27 +0000 Subject: [PATCH 162/162] Upgrade debug client to version 2024/12/2024-12-17T11:50 --- application/src/client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/client/index.html b/application/src/client/index.html index 47c9ec2c7c9..f65d9a153ba 100644 --- a/application/src/client/index.html +++ b/application/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug - - + +