From 4b052d5398c4a9ac920ae91a0b50471f214c3669 Mon Sep 17 00:00:00 2001 From: Vesa Meskanen Date: Mon, 2 Oct 2023 17:01:59 +0300 Subject: [PATCH 001/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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 d65d5ddd24eb5f497664aeeac549b8114798e7a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 06:46:17 +0000 Subject: [PATCH 009/269] Update typescript-eslint monorepo to v8 --- client/package-lock.json | 115 +++++++++++++++++++-------------------- client/package.json | 4 +- 2 files changed, 58 insertions(+), 61 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 9e2798660ec..03a7844b07b 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -26,8 +26,8 @@ "@testing-library/react": "16.0.0", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@typescript-eslint/eslint-plugin": "7.18.0", - "@typescript-eslint/parser": "7.18.0", + "@typescript-eslint/eslint-plugin": "8.0.0", + "@typescript-eslint/parser": "8.0.0", "@vitejs/plugin-react": "4.3.1", "@vitest/coverage-v8": "2.0.5", "eslint": "8.57.0", @@ -3943,32 +3943,32 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0.tgz", + "integrity": "sha512-STIZdwEQRXAHvNUS6ILDf5z3u95Gc8jzywunxSNqX00OooIemaaNIA0vEgynJlycL5AjabYLLrIyHd4iazyvtg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/scope-manager": "8.0.0", + "@typescript-eslint/type-utils": "8.0.0", + "@typescript-eslint/utils": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -3977,27 +3977,27 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0.tgz", + "integrity": "sha512-pS1hdZ+vnrpDIxuFXYQpLTILglTjSYJ9MbetZctrUawogUsPdz31DIIRZ9+rab0LhYNTsk88w4fIzVheiTbWOQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/scope-manager": "8.0.0", + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/typescript-estree": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0", "debug": "^4.3.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -4006,17 +4006,17 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0.tgz", + "integrity": "sha512-V0aa9Csx/ZWWv2IPgTfY7T4agYwJyILESu/PVqFtTFz9RIS823mAze+NbnBI8xiwdX3iqeQbcTYlvB04G9wyQw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -4024,27 +4024,24 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0.tgz", + "integrity": "sha512-mJAFP2mZLTBwAn5WI4PMakpywfWFH5nQZezUQdSKV23Pqo6o9iShQg1hP2+0hJJXP2LnZkWPphdIq4juYYwCeg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/typescript-estree": "8.0.0", + "@typescript-eslint/utils": "8.0.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependencies": { - "eslint": "^8.56.0" - }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -4052,13 +4049,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0.tgz", + "integrity": "sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -4066,14 +4063,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0.tgz", + "integrity": "sha512-5b97WpKMX+Y43YKi4zVcCVLtK5F98dFls3Oxui8LbnmRsseKenbbDinmvxrWegKDMmlkIq/XHuyy0UGLtpCDKg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4082,7 +4079,7 @@ "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -4108,40 +4105,40 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0.tgz", + "integrity": "sha512-k/oS/A/3QeGLRvOWCg6/9rATJL5rec7/5s1YmdS0ZU6LHveJyGFwBvLhSRBv6i9xaj7etmosp+l+ViN1I9Aj/Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" + "@typescript-eslint/scope-manager": "8.0.0", + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/typescript-estree": "8.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0.tgz", + "integrity": "sha512-oN0K4nkHuOyF3PVMyETbpP5zp6wfyOvm7tWhTMfoqxSSsPmJIh6JNASuZDlODE8eE+0EB9uar+6+vxr9DBTYOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/types": "8.0.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", diff --git a/client/package.json b/client/package.json index fe8836e8d36..6a8844c3198 100644 --- a/client/package.json +++ b/client/package.json @@ -35,8 +35,8 @@ "@testing-library/react": "16.0.0", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@typescript-eslint/eslint-plugin": "7.18.0", - "@typescript-eslint/parser": "7.18.0", + "@typescript-eslint/eslint-plugin": "8.0.0", + "@typescript-eslint/parser": "8.0.0", "@vitejs/plugin-react": "4.3.1", "@vitest/coverage-v8": "2.0.5", "eslint": "8.57.0", From 46a40ef0b2a53057eb84b5b833a4ae1f6ff469e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Erik=20St=C3=B8wer?= Date: Thu, 1 Aug 2024 08:51:01 +0200 Subject: [PATCH 010/269] Upgrade to eslint v9 --- client/package-lock.json | 292 +++++++++++++++++---------------------- client/package.json | 2 +- 2 files changed, 127 insertions(+), 167 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 03a7844b07b..f425e205f26 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -30,7 +30,7 @@ "@typescript-eslint/parser": "8.0.0", "@vitejs/plugin-react": "4.3.1", "@vitest/coverage-v8": "2.0.5", - "eslint": "8.57.0", + "eslint": "9.8.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-jsx-a11y": "6.9.0", @@ -1694,24 +1694,60 @@ } }, "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.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", + "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -1719,7 +1755,7 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1736,15 +1772,12 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1762,25 +1795,22 @@ "node": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@eslint/js": { + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", + "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@googlemaps/polyline-codec": { @@ -2744,42 +2774,6 @@ "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 || ^17.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -2793,11 +2787,18 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -4145,12 +4146,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "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==", - "dev": true - }, "node_modules/@vitejs/plugin-react": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", @@ -4318,9 +4313,9 @@ } }, "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.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -5617,18 +5612,6 @@ "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -5984,41 +5967,37 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", + "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.17.1", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.8.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -6032,10 +6011,10 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" } }, "node_modules/eslint-config-prettier": { @@ -6356,16 +6335,16 @@ } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -6393,19 +6372,16 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/minimatch": { @@ -6420,30 +6396,30 @@ "node": "*" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/espree": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", "dev": true, + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -6746,15 +6722,15 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -6786,17 +6762,16 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -10050,21 +10025,6 @@ "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", "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==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/rollup": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", diff --git a/client/package.json b/client/package.json index 6a8844c3198..3f783001bb3 100644 --- a/client/package.json +++ b/client/package.json @@ -39,7 +39,7 @@ "@typescript-eslint/parser": "8.0.0", "@vitejs/plugin-react": "4.3.1", "@vitest/coverage-v8": "2.0.5", - "eslint": "8.57.0", + "eslint": "9.8.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-jsx-a11y": "6.9.0", From f162ce8f3d39b6be92e8c46cd3c5b222a5999287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Erik=20St=C3=B8wer?= Date: Thu, 1 Aug 2024 09:06:05 +0200 Subject: [PATCH 011/269] Migrate to new config --- client/.eslintrc.cjs | 42 ------------------------ client/eslint.config.mjs | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 42 deletions(-) delete mode 100644 client/.eslintrc.cjs create mode 100644 client/eslint.config.mjs diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs deleted file mode 100644 index 516e639be09..00000000000 --- a/client/.eslintrc.cjs +++ /dev/null @@ -1,42 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react-hooks/recommended', - 'plugin:import/recommended', - 'plugin:jsx-a11y/recommended', - 'plugin:@typescript-eslint/recommended', - 'eslint-config-prettier', - ], - ignorePatterns: ['node_modules', 'dist', '.prettierrc.js', '.eslintrc.cjs', 'src/gql/**/*'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - settings: { - react: { - // Tells eslint-plugin-react to automatically detect the version of React to use. - version: 'detect', - }, - // Tells eslint how to resolve imports - 'import/resolver': { - node: { - paths: ['src'], - extensions: ['.js', '.jsx', '.ts', '.tsx'], - }, - }, - }, - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - "react/jsx-uses-react": "off", - "react/react-in-jsx-scope": "off", - '@typescript-eslint/ban-ts-comment': "off", - - // TODO: this is a temporary fix for - // https://github.com/typescript-eslint/typescript-eslint/issues/154 - "import/named": "off" - }, -} diff --git a/client/eslint.config.mjs b/client/eslint.config.mjs new file mode 100644 index 00000000000..3c743fdcba6 --- /dev/null +++ b/client/eslint.config.mjs @@ -0,0 +1,70 @@ +import { fixupConfigRules } from "@eslint/compat"; +import reactRefresh from "eslint-plugin-react-refresh"; +import globals from "globals"; +import tsParser from "@typescript-eslint/parser"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import js from "@eslint/js"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}); + +export default [{ + ignores: [ + "**/node_modules", + "**/dist", + "**/.prettierrc.js", + "**/.eslintrc.cjs", + "src/gql/**/*", + ], +}, ...fixupConfigRules(compat.extends( + "eslint:recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended", + "plugin:import/recommended", + "plugin:jsx-a11y/recommended", + "plugin:@typescript-eslint/recommended", + "eslint-config-prettier", +)), { + plugins: { + "react-refresh": reactRefresh, + }, + + languageOptions: { + globals: { + ...globals.browser, + }, + + parser: tsParser, + }, + + settings: { + react: { + version: "detect", + }, + + "import/resolver": { + node: { + paths: ["src"], + extensions: [".js", ".jsx", ".ts", ".tsx"], + }, + }, + }, + + rules: { + "react-refresh/only-export-components": ["warn", { + allowConstantExport: true, + }], + + "react/jsx-uses-react": "off", + "react/react-in-jsx-scope": "off", + "@typescript-eslint/ban-ts-comment": "off", + "import/named": "off", + }, +}]; \ No newline at end of file From 20df02b87e3598524100587dc9fbe30ee87117e8 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Wed, 7 Aug 2024 19:52:54 +0300 Subject: [PATCH 012/269] 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 013/269] 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 014/269] 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 015/269] 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 016/269] 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 017/269] 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 018/269] 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 019/269] 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 020/269] 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 021/269] 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 022/269] 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 023/269] 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 024/269] 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 1d19a052a8bf2523c3af537edfc92fe9a72e8c62 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Wed, 18 Sep 2024 17:22:19 +0300 Subject: [PATCH 025/269] Add subway station entrances to walk steps --- .../apis/gtfs/datafetchers/stepImpl.java | 5 +++++ .../gtfs/generated/GraphQLDataFetchers.java | 11 +++++++++++ .../apis/gtfs/generated/GraphQLTypes.java | 1 + .../module/osm/VertexGenerator.java | 9 +++++++++ .../opentripplanner/model/plan/WalkStep.java | 10 ++++++++++ .../model/plan/WalkStepBuilder.java | 7 +++++++ .../openstreetmap/model/OSMNode.java | 9 +++++++++ .../mapping/StatesToWalkStepsMapper.java | 18 ++++++++++++++++++ .../model/vertex/StationEntranceVertex.java | 19 +++++++++++++++++++ .../street/model/vertex/VertexFactory.java | 9 +++++++++ .../opentripplanner/apis/gtfs/schema.graphqls | 2 ++ 11 files changed, 100 insertions(+) create mode 100644 src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java index 6bd51ae5f29..d79e224e51e 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java @@ -53,6 +53,11 @@ public DataFetcher exit() { return environment -> getSource(environment).getExit(); } + @Override + public DataFetcher entrance() { + return environment -> getSource(environment).getEntrance(); + } + @Override public DataFetcher lat() { return environment -> getSource(environment).getStartLocation().latitude(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index 67944543580..3c162b14112 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -1,9 +1,11 @@ //THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. package org.opentripplanner.apis.gtfs.generated; +import graphql.relay.Connection; import graphql.relay.Connection; import graphql.relay.DefaultEdge; import graphql.relay.Edge; +import graphql.relay.Edge; import graphql.schema.DataFetcher; import graphql.schema.TypeResolver; import java.util.Currency; @@ -24,8 +26,12 @@ import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.model.PlanPageInfo; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; +import org.opentripplanner.apis.gtfs.model.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.TripOccupancy; +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; @@ -48,6 +54,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.realtimevehicles.model.RealtimeVehicle; @@ -58,6 +66,7 @@ import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalStationUris; import org.opentripplanner.service.vehiclerental.model.VehicleRentalSystem; import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.transit.model.basic.Money; @@ -1419,6 +1428,8 @@ public interface GraphQLStep { public DataFetcher> elevationProfile(); + public DataFetcher entrance(); + public DataFetcher exit(); public DataFetcher lat(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index 67051444cdf..8edc0cce870 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; diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java index 14489777dd4..df9c4376871 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java @@ -95,6 +95,15 @@ IntersectionVertex getVertexForOsmNode(OSMNode node, OSMWithTags way) { iv = bv; } + if (node.isSubwayEntrance()) { + String ref = node.getTag("ref"); + if (ref != null) { + iv = vertexFactory.stationEntrance(nid, coordinate, ref); + } else { + iv = vertexFactory.stationEntrance(nid, coordinate, "MAIN_ENTRANCE"); + } + } + if (iv == null) { iv = vertexFactory.osm( diff --git a/src/main/java/org/opentripplanner/model/plan/WalkStep.java b/src/main/java/org/opentripplanner/model/plan/WalkStep.java index 13249d5da52..9a650ecef3e 100644 --- a/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -44,6 +44,7 @@ public final class WalkStep { private final boolean walkingBike; private final String exit; + private final String entrance; private final ElevationProfile elevationProfile; private final boolean stayOn; @@ -56,6 +57,7 @@ public final class WalkStep { I18NString directionText, Set streetNotes, String exit, + String entrance, ElevationProfile elevationProfile, boolean bogusName, boolean walkingBike, @@ -76,6 +78,7 @@ public final class WalkStep { this.walkingBike = walkingBike; this.area = area; this.exit = exit; + this.entrance = entrance; this.elevationProfile = elevationProfile; this.stayOn = stayOn; this.edges = List.copyOf(Objects.requireNonNull(edges)); @@ -130,6 +133,13 @@ public String getExit() { return exit; } + /** + * When entering or exiting a public transport station, the entrance name + */ + public String getEntrance() { + return entrance; + } + /** * Indicates whether a street changes direction at an intersection. */ diff --git a/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java b/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java index 25c6ee25b6b..8b5d2cedb11 100644 --- a/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -25,6 +25,7 @@ public class WalkStepBuilder { private RelativeDirection relativeDirection; private ElevationProfile elevationProfile; private String exit; + private String entrance; private boolean stayOn = false; /** * Distance used for appending elevation profiles @@ -74,6 +75,11 @@ public WalkStepBuilder withExit(String exit) { return this; } + public WalkStepBuilder withEntrance(String entrance) { + this.entrance = entrance; + return this; + } + public WalkStepBuilder withStayOn(boolean stayOn) { this.stayOn = stayOn; return this; @@ -156,6 +162,7 @@ public WalkStep build() { directionText, streetNotes, exit, + entrance, elevationProfile, bogusName, walkingBike, diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java index d181cde4564..371751d5e4a 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java @@ -63,6 +63,15 @@ public boolean isBarrier() { ); } + /** + * Checks if this node is an subway station entrance + * + * @return true if it does + */ + public boolean isSubwayEntrance() { + return hasTag("railway") && "subway_entrance".equals(getTag("railway")); + } + /** * Consider barrier tag in permissions. Leave the rest for the super class. */ diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index 94905bb840a..d49755bb548 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -26,6 +26,7 @@ import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetTransitEntranceLink; import org.opentripplanner.street.model.vertex.ExitVertex; +import org.opentripplanner.street.model.vertex.StationEntranceVertex; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.state.State; @@ -258,6 +259,8 @@ private void processState(State backState, State forwardState) { setMotorwayExit(backState); + setStationEntrance(backState); + if (createdNewStep && !modeTransition) { // check last three steps for zag int lastIndex = steps.size() - 1; @@ -380,6 +383,21 @@ private void setMotorwayExit(State backState) { } } + /** + * Update the walk step with the name of the station entrance if set from OSM + */ + private void setStationEntrance(State backState) { + State entranceState = backState; + Edge entranceEdge = entranceState.getBackEdge(); + while (entranceEdge instanceof FreeEdge) { + entranceState = entranceState.getBackState(); + entranceEdge = entranceState.getBackEdge(); + } + if (entranceState.getVertex() instanceof StationEntranceVertex) { + current.withEntrance(((StationEntranceVertex) entranceState.getVertex()).getEntranceName()); + } + } + /** * Is it possible to turn to another street from this previous state */ diff --git a/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java new file mode 100644 index 00000000000..af1e824a7bd --- /dev/null +++ b/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java @@ -0,0 +1,19 @@ +package org.opentripplanner.street.model.vertex; + +public class StationEntranceVertex extends OsmVertex { + + private final String entranceName; + + public StationEntranceVertex(double x, double y, long nodeId, String entranceName) { + super(x, y, nodeId); + this.entranceName = entranceName; + } + + public String getEntranceName() { + return entranceName; + } + + public String toString() { + return "StationEntranceVertex(" + super.toString() + ")"; + } +} diff --git a/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java b/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java index c7e38ca0032..3fe201070d4 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java @@ -103,6 +103,15 @@ public ExitVertex exit(long nid, Coordinate coordinate, String exitName) { return addToGraph(new ExitVertex(coordinate.x, coordinate.y, nid, exitName)); } + @Nonnull + public StationEntranceVertex stationEntrance( + long nid, + Coordinate coordinate, + String entranceName + ) { + return addToGraph(new StationEntranceVertex(coordinate.x, coordinate.y, nid, entranceName)); + } + @Nonnull public OsmVertex osm( Coordinate coordinate, diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 927af19f8b1..12decc1e3d5 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2628,6 +2628,8 @@ type step { elevationProfile: [elevationProfileComponent] "When exiting a highway or traffic circle, the exit name/number." exit: String + "Name of entrance to a public transport station" + entrance: String "The latitude of the start of the step." lat: Float "The longitude of the start of the step." From ae50bd62552994cbce7d1d907f37879aa4056732 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 19 Sep 2024 22:59:20 +0200 Subject: [PATCH 026/269] 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 67f4b1b54ef64b40c0853bfd8e5d10a172534e72 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Wed, 2 Oct 2024 16:03:23 +0300 Subject: [PATCH 027/269] Add entity to walk steps --- .../apis/gtfs/GtfsGraphQLIndex.java | 2 ++ .../apis/gtfs/datafetchers/stepImpl.java | 4 ++-- .../gtfs/generated/GraphQLDataFetchers.java | 10 +++++++++- .../apis/gtfs/model/StepEntity.java | 19 +++++++++++++++++++ .../opentripplanner/model/plan/WalkStep.java | 13 +++++++------ .../model/plan/WalkStepBuilder.java | 10 ++++++---- .../mapping/StatesToWalkStepsMapper.java | 5 ++++- .../opentripplanner/apis/gtfs/schema.graphqls | 13 +++++++++++-- 8 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 43a8399e70c..ed3242b5823 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -81,6 +81,7 @@ import org.opentripplanner.apis.gtfs.datafetchers.serviceTimeRangeImpl; import org.opentripplanner.apis.gtfs.datafetchers.stepImpl; import org.opentripplanner.apis.gtfs.datafetchers.stopAtDistanceImpl; +import org.opentripplanner.apis.gtfs.model.StepEntity; import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.apis.support.graphql.LoggingDataFetcherExceptionHandler; import org.opentripplanner.ext.actuator.MicrometerGraphQLInstrumentation; @@ -124,6 +125,7 @@ protected static GraphQLSchema buildSchema() { .type("Node", type -> type.typeResolver(new NodeTypeResolver())) .type("PlaceInterface", type -> type.typeResolver(new PlaceInterfaceTypeResolver())) .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) + .type("StepEntity", type -> type.typeResolver(new StepEntity() {})) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) .type(typeWiring.build(AgencyImpl.class)) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java index d79e224e51e..658f2d321ad 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/stepImpl.java @@ -54,8 +54,8 @@ public DataFetcher exit() { } @Override - public DataFetcher entrance() { - return environment -> getSource(environment).getEntrance(); + public DataFetcher entity() { + return environment -> getSource(environment).getEntity(); } @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 3c162b14112..efc59f72d42 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -365,6 +365,11 @@ public interface GraphQLEmissions { public DataFetcher co2(); } + /** Station entrance/exit */ + public interface GraphQLEntrance { + public DataFetcher name(); + } + /** A 'medium' that a fare product applies to, for example cash, 'Oyster Card' or 'DB Navigator App'. */ public interface GraphQLFareMedium { public DataFetcher id(); @@ -977,6 +982,9 @@ public interface GraphQLRoutingError { public DataFetcher inputField(); } + /** Entity to a step */ + public interface GraphQLStepEntity extends TypeResolver {} + /** * Stop can represent either a single public transport stop, where passengers can * board and/or disembark vehicles, or a station, which contains multiple stops. @@ -1428,7 +1436,7 @@ public interface GraphQLStep { public DataFetcher> elevationProfile(); - public DataFetcher entrance(); + public DataFetcher entity(); public DataFetcher exit(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java b/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java new file mode 100644 index 00000000000..5e10f4e08a6 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java @@ -0,0 +1,19 @@ +package org.opentripplanner.apis.gtfs.model; + +import graphql.TypeResolutionEnvironment; +import graphql.schema.GraphQLObjectType; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; + +public interface StepEntity extends GraphQLDataFetchers.GraphQLStepEntity { + record Entrance(String name) implements StepEntity {} + + @Override + default GraphQLObjectType getType(TypeResolutionEnvironment env) { + var schema = env.getSchema(); + Object o = env.getObject(); + if (o instanceof Entrance) { + return schema.getObjectType("Entrance"); + } + return null; + } +} diff --git a/src/main/java/org/opentripplanner/model/plan/WalkStep.java b/src/main/java/org/opentripplanner/model/plan/WalkStep.java index 9a650ecef3e..3e9e9fe14dc 100644 --- a/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -4,6 +4,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import org.opentripplanner.apis.gtfs.model.StepEntity; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.lang.DoubleUtils; @@ -44,7 +45,7 @@ public final class WalkStep { private final boolean walkingBike; private final String exit; - private final String entrance; + private final StepEntity entity; private final ElevationProfile elevationProfile; private final boolean stayOn; @@ -57,7 +58,7 @@ public final class WalkStep { I18NString directionText, Set streetNotes, String exit, - String entrance, + StepEntity entity, ElevationProfile elevationProfile, boolean bogusName, boolean walkingBike, @@ -78,7 +79,7 @@ public final class WalkStep { this.walkingBike = walkingBike; this.area = area; this.exit = exit; - this.entrance = entrance; + this.entity = entity; this.elevationProfile = elevationProfile; this.stayOn = stayOn; this.edges = List.copyOf(Objects.requireNonNull(edges)); @@ -134,10 +135,10 @@ public String getExit() { } /** - * When entering or exiting a public transport station, the entrance name + * Entity related to a step e.g. building entrance/exit. */ - public String getEntrance() { - return entrance; + public Object getEntity() { + return entity; } /** diff --git a/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java b/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java index 8b5d2cedb11..d020f8d0113 100644 --- a/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Set; import javax.annotation.Nullable; +import org.opentripplanner.apis.gtfs.model.StepEntity; +import org.opentripplanner.apis.gtfs.model.StepEntity.Entrance; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.lang.DoubleUtils; @@ -25,7 +27,7 @@ public class WalkStepBuilder { private RelativeDirection relativeDirection; private ElevationProfile elevationProfile; private String exit; - private String entrance; + private StepEntity entity; private boolean stayOn = false; /** * Distance used for appending elevation profiles @@ -75,8 +77,8 @@ public WalkStepBuilder withExit(String exit) { return this; } - public WalkStepBuilder withEntrance(String entrance) { - this.entrance = entrance; + public WalkStepBuilder withEntrance(Entrance entrance) { + this.entity = entrance; return this; } @@ -162,7 +164,7 @@ public WalkStep build() { directionText, streetNotes, exit, - entrance, + entity, elevationProfile, bogusName, walkingBike, diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index d49755bb548..7e0ff3696ca 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -10,6 +10,7 @@ import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; +import org.opentripplanner.apis.gtfs.model.StepEntity.Entrance; import org.opentripplanner.framework.geometry.DirectionUtils; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; @@ -394,7 +395,9 @@ private void setStationEntrance(State backState) { entranceEdge = entranceState.getBackEdge(); } if (entranceState.getVertex() instanceof StationEntranceVertex) { - current.withEntrance(((StationEntranceVertex) entranceState.getVertex()).getEntranceName()); + current.withEntrance( + new Entrance(((StationEntranceVertex) entranceState.getVertex()).getEntranceName()) + ); } } diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 12decc1e3d5..b9b3fddb6e4 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2607,6 +2607,15 @@ type serviceTimeRange { start: Long } +"Station entrance/exit" +type Entrance { + "Name of a station entrance/exit" + name: String +} + +"Entity to a step" +union StepEntity = Entrance + type step { "The cardinal (compass) direction (e.g. north, northeast) taken when engaging this step." absoluteDirection: AbsoluteDirection @@ -2628,8 +2637,6 @@ type step { elevationProfile: [elevationProfileComponent] "When exiting a highway or traffic circle, the exit name/number." exit: String - "Name of entrance to a public transport station" - entrance: String "The latitude of the start of the step." lat: Float "The longitude of the start of the step." @@ -2642,6 +2649,8 @@ type step { streetName: String "Is this step walking with a bike?" walkingBike: Boolean + "Step entity e.g. an entrance" + entity: StepEntity } type stopAtDistance implements Node { From 9d18269db5311d5802b79e0410ece8fd24e6583b Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Thu, 3 Oct 2024 14:52:42 +0300 Subject: [PATCH 028/269] Add more parameters to Entrance --- .../apis/gtfs/generated/GraphQLDataFetchers.java | 4 ++++ .../org/opentripplanner/apis/gtfs/model/StepEntity.java | 6 +++++- .../routing/algorithm/mapping/StatesToWalkStepsMapper.java | 6 ++++-- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 6 +++++- 4 files changed, 18 insertions(+), 4 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 efc59f72d42..dfe2527715f 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -367,6 +367,10 @@ public interface GraphQLEmissions { /** Station entrance/exit */ public interface GraphQLEntrance { + public DataFetcher code(); + + public DataFetcher gtfsId(); + public DataFetcher name(); } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java b/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java index 5e10f4e08a6..e1f53cf6842 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java @@ -5,7 +5,11 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; public interface StepEntity extends GraphQLDataFetchers.GraphQLStepEntity { - record Entrance(String name) implements StepEntity {} + record Entrance(String code, String name, String gtfsId) implements StepEntity { + public static Entrance withCode(String code) { + return new Entrance(code, null, null); + } + } @Override default GraphQLObjectType getType(TypeResolutionEnvironment env) { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index 7e0ff3696ca..eeb091125aa 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -10,7 +10,7 @@ import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.apis.gtfs.model.StepEntity.Entrance; +import org.opentripplanner.apis.gtfs.model.StepEntity; import org.opentripplanner.framework.geometry.DirectionUtils; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; @@ -396,7 +396,9 @@ private void setStationEntrance(State backState) { } if (entranceState.getVertex() instanceof StationEntranceVertex) { current.withEntrance( - new Entrance(((StationEntranceVertex) entranceState.getVertex()).getEntranceName()) + StepEntity.Entrance.withCode( + ((StationEntranceVertex) entranceState.getVertex()).getEntranceName() + ) ); } } diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index b9b3fddb6e4..9fef54c3965 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2609,8 +2609,12 @@ type serviceTimeRange { "Station entrance/exit" type Entrance { - "Name of a station entrance/exit" + "Code of entrance/exit eg A or B" + code: String + "Name of entrance/exit" name: String + "Gtfs id of entrance/exit" + gtfsId: String } "Entity to a step" From 3b8828831c3e0409fbe3da49a7d238fa0888a884 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Mon, 7 Oct 2024 17:22:54 +0300 Subject: [PATCH 029/269] Move StepEntity classes --- .../apis/gtfs/GtfsGraphQLIndex.java | 4 +-- .../datafetchers/StepEntityTypeResolver.java | 30 +++++++++++++++++++ .../apis/gtfs/model/StepEntity.java | 23 -------------- .../opentripplanner/model/plan/Entrance.java | 18 +++++++++++ .../model/plan/StepEntity.java | 3 ++ .../opentripplanner/model/plan/WalkStep.java | 2 +- .../model/plan/WalkStepBuilder.java | 4 +-- .../mapping/StatesToWalkStepsMapper.java | 6 ++-- 8 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java delete mode 100644 src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java create mode 100644 src/main/java/org/opentripplanner/model/plan/Entrance.java create mode 100644 src/main/java/org/opentripplanner/model/plan/StepEntity.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index ed3242b5823..5b288762262 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -58,6 +58,7 @@ 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.StepEntityTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.StopGeometriesImpl; import org.opentripplanner.apis.gtfs.datafetchers.StopImpl; import org.opentripplanner.apis.gtfs.datafetchers.StopOnRouteImpl; @@ -81,7 +82,6 @@ import org.opentripplanner.apis.gtfs.datafetchers.serviceTimeRangeImpl; import org.opentripplanner.apis.gtfs.datafetchers.stepImpl; import org.opentripplanner.apis.gtfs.datafetchers.stopAtDistanceImpl; -import org.opentripplanner.apis.gtfs.model.StepEntity; import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.apis.support.graphql.LoggingDataFetcherExceptionHandler; import org.opentripplanner.ext.actuator.MicrometerGraphQLInstrumentation; @@ -125,7 +125,7 @@ protected static GraphQLSchema buildSchema() { .type("Node", type -> type.typeResolver(new NodeTypeResolver())) .type("PlaceInterface", type -> type.typeResolver(new PlaceInterfaceTypeResolver())) .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) - .type("StepEntity", type -> type.typeResolver(new StepEntity() {})) + .type("StepEntity", type -> type.typeResolver(new StepEntityTypeResolver())) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) .type(typeWiring.build(AgencyImpl.class)) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java new file mode 100644 index 00000000000..5fc8a123226 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java @@ -0,0 +1,30 @@ +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.RouteTypeModel; +import org.opentripplanner.apis.gtfs.model.StopOnRouteModel; +import org.opentripplanner.apis.gtfs.model.StopOnTripModel; +import org.opentripplanner.apis.gtfs.model.UnknownModel; +import org.opentripplanner.model.plan.Entrance; +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.site.RegularStop; +import org.opentripplanner.transit.model.timetable.Trip; + +public class StepEntityTypeResolver implements TypeResolver { + + @Override + public GraphQLObjectType getType(TypeResolutionEnvironment environment) { + Object o = environment.getObject(); + GraphQLSchema schema = environment.getSchema(); + + if (o instanceof Entrance) { + return schema.getObjectType("Entrance"); + } + return null; + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java b/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java deleted file mode 100644 index e1f53cf6842..00000000000 --- a/src/main/java/org/opentripplanner/apis/gtfs/model/StepEntity.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.opentripplanner.apis.gtfs.model; - -import graphql.TypeResolutionEnvironment; -import graphql.schema.GraphQLObjectType; -import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; - -public interface StepEntity extends GraphQLDataFetchers.GraphQLStepEntity { - record Entrance(String code, String name, String gtfsId) implements StepEntity { - public static Entrance withCode(String code) { - return new Entrance(code, null, null); - } - } - - @Override - default GraphQLObjectType getType(TypeResolutionEnvironment env) { - var schema = env.getSchema(); - Object o = env.getObject(); - if (o instanceof Entrance) { - return schema.getObjectType("Entrance"); - } - return null; - } -} diff --git a/src/main/java/org/opentripplanner/model/plan/Entrance.java b/src/main/java/org/opentripplanner/model/plan/Entrance.java new file mode 100644 index 00000000000..7b28ee992a4 --- /dev/null +++ b/src/main/java/org/opentripplanner/model/plan/Entrance.java @@ -0,0 +1,18 @@ +package org.opentripplanner.model.plan; + +public final class Entrance extends StepEntity { + + private final String code; + private final String gtfsId; + private final String name; + + public Entrance(String code, String gtfsId, String name) { + this.code = code; + this.gtfsId = gtfsId; + this.name = name; + } + + public static Entrance withCode(String code) { + return new Entrance(code, null, null); + } +} diff --git a/src/main/java/org/opentripplanner/model/plan/StepEntity.java b/src/main/java/org/opentripplanner/model/plan/StepEntity.java new file mode 100644 index 00000000000..e6bfd587bfc --- /dev/null +++ b/src/main/java/org/opentripplanner/model/plan/StepEntity.java @@ -0,0 +1,3 @@ +package org.opentripplanner.model.plan; + +public abstract class StepEntity {} diff --git a/src/main/java/org/opentripplanner/model/plan/WalkStep.java b/src/main/java/org/opentripplanner/model/plan/WalkStep.java index 3e9e9fe14dc..8114246d7e8 100644 --- a/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -4,11 +4,11 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import org.opentripplanner.apis.gtfs.model.StepEntity; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.lang.DoubleUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; +import org.opentripplanner.model.plan.StepEntity; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.note.StreetNote; diff --git a/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java b/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java index d020f8d0113..02b73c0ce15 100644 --- a/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -5,12 +5,12 @@ import java.util.List; import java.util.Set; import javax.annotation.Nullable; -import org.opentripplanner.apis.gtfs.model.StepEntity; -import org.opentripplanner.apis.gtfs.model.StepEntity.Entrance; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.lang.DoubleUtils; import org.opentripplanner.framework.lang.IntUtils; +import org.opentripplanner.model.plan.Entrance; +import org.opentripplanner.model.plan.StepEntity; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.note.StreetNote; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index eeb091125aa..158979f5d91 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -10,11 +10,11 @@ import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.apis.gtfs.model.StepEntity; import org.opentripplanner.framework.geometry.DirectionUtils; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.plan.ElevationProfile; +import org.opentripplanner.model.plan.Entrance; import org.opentripplanner.model.plan.RelativeDirection; import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.model.plan.WalkStepBuilder; @@ -396,9 +396,7 @@ private void setStationEntrance(State backState) { } if (entranceState.getVertex() instanceof StationEntranceVertex) { current.withEntrance( - StepEntity.Entrance.withCode( - ((StationEntranceVertex) entranceState.getVertex()).getEntranceName() - ) + Entrance.withCode(((StationEntranceVertex) entranceState.getVertex()).getEntranceName()) ); } } From 0a62bf81363030d990ed9e0e5e596a479c3a5644 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Wed, 16 Oct 2024 18:19:55 +0300 Subject: [PATCH 030/269] Remove default name for subway station entrances --- .../graph_builder/module/osm/VertexGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java index df9c4376871..633d4343b83 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java @@ -100,7 +100,7 @@ IntersectionVertex getVertexForOsmNode(OSMNode node, OSMWithTags way) { if (ref != null) { iv = vertexFactory.stationEntrance(nid, coordinate, ref); } else { - iv = vertexFactory.stationEntrance(nid, coordinate, "MAIN_ENTRANCE"); + iv = vertexFactory.stationEntrance(nid, coordinate, null); } } From 66100b9a0f2f092594517f1a5461c178be6fb644 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 18 Oct 2024 16:37:00 +0300 Subject: [PATCH 031/269] 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 528ab55927be6269d8dee5bfec52e1b0dcbf999d Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Fri, 18 Oct 2024 17:39:43 +0300 Subject: [PATCH 032/269] Add option to turn on osm subway entrances in osmDefaults --- doc/user/BuildConfiguration.md | 2 ++ .../module/configure/GraphBuilderModules.java | 2 ++ .../graph_builder/module/osm/OsmModule.java | 8 ++++++- .../module/osm/OsmModuleBuilder.java | 9 +++++++- .../module/osm/VertexGenerator.java | 11 ++++++++-- .../osm/parameters/OsmExtractParameters.java | 21 +++++++++++++++++-- .../OsmExtractParametersBuilder.java | 15 +++++++++++++ .../parameters/OsmProcessingParameters.java | 4 +++- .../openstreetmap/OsmProvider.java | 9 ++++++++ .../config/buildconfig/OsmConfig.java | 8 +++++++ .../module/osm/WalkableAreaBuilderTest.java | 2 +- 11 files changed, 83 insertions(+), 8 deletions(-) diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index b311991120e..7af5bbad1c5 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -84,10 +84,12 @@ Sections follow that describe particular settings in more depth. |    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | |    [ferryIdsNotAllowedForBicycle](#nd_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | | [osm](#osm) | `object[]` | Configure properties for a given OpenStreetMap feed. | *Optional* | | 2.2 | +|       includeOsmSubwayEntrances | `boolean` | Whether to include subway entrances in the OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | `false` | 2.2 | |       [osmTagMapping](#osm_0_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. Overrides the value specified in `osmDefaults`. | *Optional* | `"default"` | 2.2 | |       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | |       timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | | 2.2 | | osmDefaults | `object` | Default properties for OpenStreetMap feeds. | *Optional* | | 2.2 | +|    includeOsmSubwayEntrances | `boolean` | Whether to include subway entrances in the OSM data. | *Optional* | `false` | 2.2 | |    [osmTagMapping](#od_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. | *Optional* | `"default"` | 2.2 | |    timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. | *Optional* | | 2.2 | | [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | diff --git a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java index 10d3a997579..169ac351c87 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java @@ -68,6 +68,7 @@ static OsmModule provideOpenStreetMapModule( osmConfiguredDataSource.dataSource(), osmConfiguredDataSource.config().osmTagMapper(), osmConfiguredDataSource.config().timeZone(), + osmConfiguredDataSource.config().includeOsmSubwayEntrances(), config.osmCacheDataInMem, issueStore ) @@ -83,6 +84,7 @@ static OsmModule provideOpenStreetMapModule( .withStaticBikeParkAndRide(config.staticBikeParkAndRide) .withMaxAreaNodes(config.maxAreaNodes) .withBoardingAreaRefTags(config.boardingLocationTags) + .withIncludeOsmSubwayEntrances(config.osmDefaults.includeOsmSubwayEntrances()) .withIssueStore(issueStore) .withStreetLimitationParameters(streetLimitationParameters) .build(); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 10c215ee448..cbdd3d7a7a2 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -71,7 +71,13 @@ public class OsmModule implements GraphBuilderModule { this.issueStore = issueStore; this.params = params; this.osmdb = new OsmDatabase(issueStore); - this.vertexGenerator = new VertexGenerator(osmdb, graph, params.boardingAreaRefTags()); + this.vertexGenerator = + new VertexGenerator( + osmdb, + graph, + params.boardingAreaRefTags(), + params.includeOsmSubwayEntrances() + ); this.normalizer = new SafetyValueNormalizer(graph, issueStore); this.streetLimitationParameters = Objects.requireNonNull(streetLimitationParameters); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java index f0a40fa678f..29e8f8a1ae5 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java @@ -24,6 +24,7 @@ public class OsmModuleBuilder { private boolean platformEntriesLinking = false; private boolean staticParkAndRide = false; private boolean staticBikeParkAndRide = false; + private boolean includeOsmSubwayEntrances = false; private int maxAreaNodes; private StreetLimitationParameters streetLimitationParameters = new StreetLimitationParameters(); @@ -72,6 +73,11 @@ public OsmModuleBuilder withMaxAreaNodes(int maxAreaNodes) { return this; } + public OsmModuleBuilder withIncludeOsmSubwayEntrances(boolean includeOsmSubwayEntrances) { + this.includeOsmSubwayEntrances = includeOsmSubwayEntrances; + return this; + } + public OsmModuleBuilder withStreetLimitationParameters(StreetLimitationParameters parameters) { this.streetLimitationParameters = parameters; return this; @@ -90,7 +96,8 @@ public OsmModule build() { areaVisibility, platformEntriesLinking, staticParkAndRide, - staticBikeParkAndRide + staticBikeParkAndRide, + includeOsmSubwayEntrances ) ); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java index 633d4343b83..87cd88a5227 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java @@ -33,12 +33,19 @@ class VertexGenerator { private final HashMap> multiLevelNodes = new HashMap<>(); private final OsmDatabase osmdb; private final Set boardingAreaRefTags; + private final Boolean includeOsmSubwayEntrances; private final VertexFactory vertexFactory; - public VertexGenerator(OsmDatabase osmdb, Graph graph, Set boardingAreaRefTags) { + public VertexGenerator( + OsmDatabase osmdb, + Graph graph, + Set boardingAreaRefTags, + boolean includeOsmSubwayEntrances + ) { this.osmdb = osmdb; this.vertexFactory = new VertexFactory(graph); this.boardingAreaRefTags = boardingAreaRefTags; + this.includeOsmSubwayEntrances = includeOsmSubwayEntrances; } /** @@ -95,7 +102,7 @@ IntersectionVertex getVertexForOsmNode(OSMNode node, OSMWithTags way) { iv = bv; } - if (node.isSubwayEntrance()) { + if (includeOsmSubwayEntrances && node.isSubwayEntrance()) { String ref = node.getTag("ref"); if (ref != null) { iv = vertexFactory.stationEntrance(nid, coordinate, ref); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java index 9d2eead5f7e..1cae389d9c4 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java @@ -11,16 +11,28 @@ * Example: {@code "osm" : [ {source: "file:///path/to/otp/norway.pbf"} ] } * */ -public record OsmExtractParameters(URI source, OsmTagMapperSource osmTagMapper, ZoneId timeZone) +public record OsmExtractParameters( + URI source, + OsmTagMapperSource osmTagMapper, + ZoneId timeZone, + boolean includeOsmSubwayEntrances +) implements DataSourceConfig { public static final OsmTagMapperSource DEFAULT_OSM_TAG_MAPPER = OsmTagMapperSource.DEFAULT; public static final ZoneId DEFAULT_TIME_ZONE = null; + public static final boolean DEFAULT_INCLUDE_OSM_SUBWAY_ENTRANCES = false; + public static final OsmExtractParameters DEFAULT = new OsmExtractParametersBuilder().build(); OsmExtractParameters(OsmExtractParametersBuilder builder) { - this(builder.getSource(), builder.getOsmTagMapper(), builder.getTimeZone()); + this( + builder.getSource(), + builder.getOsmTagMapper(), + builder.getTimeZone(), + builder.getIncludeOsmSubwayEntrances() + ); } @Override @@ -37,6 +49,11 @@ public ZoneId timeZone() { return timeZone; } + @Nullable + public boolean includeOsmSubwayEntrances() { + return includeOsmSubwayEntrances; + } + public OsmExtractParametersBuilder copyOf() { return new OsmExtractParametersBuilder(this); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParametersBuilder.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParametersBuilder.java index 03fd7eaec4e..0bbc184569d 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParametersBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParametersBuilder.java @@ -24,14 +24,18 @@ public class OsmExtractParametersBuilder { */ private ZoneId timeZone; + private boolean includeOsmSubwayEntrances; + public OsmExtractParametersBuilder() { this.osmTagMapper = OsmExtractParameters.DEFAULT_OSM_TAG_MAPPER; this.timeZone = OsmExtractParameters.DEFAULT_TIME_ZONE; + this.includeOsmSubwayEntrances = OsmExtractParameters.DEFAULT_INCLUDE_OSM_SUBWAY_ENTRANCES; } public OsmExtractParametersBuilder(OsmExtractParameters original) { this.osmTagMapper = original.osmTagMapper(); this.timeZone = original.timeZone(); + this.includeOsmSubwayEntrances = original.includeOsmSubwayEntrances(); } public OsmExtractParametersBuilder withSource(URI source) { @@ -49,6 +53,13 @@ public OsmExtractParametersBuilder withTimeZone(ZoneId timeZone) { return this; } + public OsmExtractParametersBuilder withIncludeOsmSubwayEntrances( + boolean includeOsmSubwayEntrances + ) { + this.includeOsmSubwayEntrances = includeOsmSubwayEntrances; + return this; + } + public URI getSource() { return source; } @@ -61,6 +72,10 @@ public ZoneId getTimeZone() { return timeZone; } + public boolean getIncludeOsmSubwayEntrances() { + return includeOsmSubwayEntrances; + } + public OsmExtractParameters build() { return new OsmExtractParameters(this); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmProcessingParameters.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmProcessingParameters.java index 52bf8d65314..a3fd14020e8 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmProcessingParameters.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmProcessingParameters.java @@ -13,6 +13,7 @@ * @param platformEntriesLinking Whether platform entries should be linked * @param staticParkAndRide Whether we should create car P+R stations from OSM data. * @param staticBikeParkAndRide Whether we should create bike P+R stations from OSM data. + * @param includeOsmSubwayEntrances Whether we should create subway entrances from OSM data. */ public record OsmProcessingParameters( Set boardingAreaRefTags, @@ -21,7 +22,8 @@ public record OsmProcessingParameters( boolean areaVisibility, boolean platformEntriesLinking, boolean staticParkAndRide, - boolean staticBikeParkAndRide + boolean staticBikeParkAndRide, + boolean includeOsmSubwayEntrances ) { public OsmProcessingParameters { boardingAreaRefTags = Set.copyOf(Objects.requireNonNull(boardingAreaRefTags)); diff --git a/src/main/java/org/opentripplanner/openstreetmap/OsmProvider.java b/src/main/java/org/opentripplanner/openstreetmap/OsmProvider.java index e35a846cbbd..d66f5db394b 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/OsmProvider.java +++ b/src/main/java/org/opentripplanner/openstreetmap/OsmProvider.java @@ -37,6 +37,8 @@ public class OsmProvider { private final OsmTagMapper osmTagMapper; + private boolean includeOsmSubwayEntrances = false; + private final WayPropertySet wayPropertySet; private byte[] cachedBytes = null; @@ -46,6 +48,7 @@ public OsmProvider(File file, boolean cacheDataInMem) { new FileDataSource(file, FileType.OSM), OsmTagMapperSource.DEFAULT, null, + false, cacheDataInMem, DataImportIssueStore.NOOP ); @@ -55,11 +58,13 @@ public OsmProvider( DataSource dataSource, OsmTagMapperSource tagMapperSource, ZoneId zoneId, + boolean includeOsmSubwayEntrances, boolean cacheDataInMem, DataImportIssueStore issueStore ) { this.source = dataSource; this.zoneId = zoneId; + this.includeOsmSubwayEntrances = includeOsmSubwayEntrances; this.osmTagMapper = tagMapperSource.getInstance(); this.wayPropertySet = new WayPropertySet(issueStore); osmTagMapper.populateProperties(wayPropertySet); @@ -152,6 +157,10 @@ public OsmTagMapper getOsmTagMapper() { return osmTagMapper; } + public boolean getIncludeOsmSubwayEntrances() { + return includeOsmSubwayEntrances; + } + public WayPropertySet getWayPropertySet() { return wayPropertySet; } diff --git a/src/main/java/org/opentripplanner/standalone/config/buildconfig/OsmConfig.java b/src/main/java/org/opentripplanner/standalone/config/buildconfig/OsmConfig.java index 1b2ec0ed74d..c1a3963c6a5 100644 --- a/src/main/java/org/opentripplanner/standalone/config/buildconfig/OsmConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/buildconfig/OsmConfig.java @@ -84,6 +84,14 @@ public static OsmExtractParametersBuilder mapOsmGenericParameters( ) .docDefaultValue(docDefaults.timeZone()) .asZoneId(defaults.timeZone()) + ) + .withIncludeOsmSubwayEntrances( + node + .of("includeOsmSubwayEntrances") + .since(V2_2) + .summary("Whether to include subway entrances from the OSM data." + documentationAddition) + .docDefaultValue(docDefaults.includeOsmSubwayEntrances()) + .asBoolean(defaults.includeOsmSubwayEntrances()) ); } } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java index 4906bce930d..26500299062 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilderTest.java @@ -48,7 +48,7 @@ public Graph buildGraph(final TestInfo testInfo) { final WalkableAreaBuilder walkableAreaBuilder = new WalkableAreaBuilder( graph, osmdb, - new VertexGenerator(osmdb, graph, Set.of()), + new VertexGenerator(osmdb, graph, Set.of(), false), new DefaultNamer(), new SafetyValueNormalizer(graph, DataImportIssueStore.NOOP), DataImportIssueStore.NOOP, From 401a405cd9000a5f1ebb92bb9ba130f8a6ff0fcd Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Mon, 21 Oct 2024 18:41:18 +0300 Subject: [PATCH 033/269] Fix walk step generation --- .../mapping/StatesToWalkStepsMapper.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) 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 9202c94c76a..6bcef1b0da9 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 @@ -177,6 +177,9 @@ private void processState(State backState, State forwardState) { if (edge instanceof ElevatorAlightEdge) { addStep(createElevatorWalkStep(backState, forwardState, edge)); return; + } else if (backState.getVertex() instanceof StationEntranceVertex) { + addStep(createStationEntranceWalkStep(backState, forwardState, edge)); + return; } else if (edge instanceof PathwayEdge pwe && pwe.signpostedAs().isPresent()) { createAndSaveStep(backState, forwardState, pwe.signpostedAs().get(), FOLLOW_SIGNS, edge); return; @@ -259,8 +262,6 @@ private void processState(State backState, State forwardState) { setMotorwayExit(backState); - setStationEntrance(backState); - if (createdNewStep && !modeTransition) { // check last three steps for zag int lastIndex = steps.size() - 1; @@ -382,23 +383,6 @@ private void setMotorwayExit(State backState) { } } - /** - * Update the walk step with the name of the station entrance if set from OSM - */ - private void setStationEntrance(State backState) { - State entranceState = backState; - Edge entranceEdge = entranceState.getBackEdge(); - while (entranceEdge instanceof FreeEdge) { - entranceState = entranceState.getBackState(); - entranceEdge = entranceState.getBackEdge(); - } - if (entranceState.getVertex() instanceof StationEntranceVertex) { - current.withEntrance( - Entrance.withCode(((StationEntranceVertex) entranceState.getVertex()).getEntranceName()) - ); - } - } - /** * Is it possible to turn to another street from this previous state */ @@ -536,6 +520,22 @@ private WalkStepBuilder createElevatorWalkStep(State backState, State forwardSta return step; } + private WalkStepBuilder createStationEntranceWalkStep( + State backState, + State forwardState, + Edge edge + ) { + // don't care what came before or comes after + var step = createWalkStep(forwardState, backState); + + step.withRelativeDirection(RelativeDirection.CONTINUE); + + step.withEntrance( + Entrance.withCode(((StationEntranceVertex) backState.getVertex()).getEntranceName()) + ); + return step; + } + private void createAndSaveStep( State backState, State forwardState, From 438bc318faa6ef96c1ef78f997005b631910c6fd Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Wed, 23 Oct 2024 20:14:01 +0300 Subject: [PATCH 034/269] Add step entity to graphql tests --- .../apis/gtfs/GraphQLIntegrationTest.java | 8 +++++++- .../apis/gtfs/expectations/walk-steps.json | 17 +++++++++++++++-- .../apis/gtfs/queries/walk-steps.graphql | 6 ++++++ 3 files changed, 28 insertions(+), 3 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 663ce2d5f3f..020a1ec6113 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -49,6 +49,7 @@ import org.opentripplanner.model.fare.ItineraryFares; import org.opentripplanner.model.fare.RiderCategory; import org.opentripplanner.model.plan.Emissions; +import org.opentripplanner.model.plan.Entrance; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.RelativeDirection; @@ -232,8 +233,13 @@ public Set getRoutesForStop(StopLocation stop) { .build(); var step2 = walkStep("elevator").withRelativeDirection(RelativeDirection.ELEVATOR).build(); + var step3 = walkStep("entrance") + .withRelativeDirection(RelativeDirection.CONTINUE) + .withEntrance(Entrance.withCode("A")) + .build(); + Itinerary i1 = newItinerary(A, T11_00) - .walk(20, B, List.of(step1, step2)) + .walk(20, B, List.of(step1, step2, step3)) .bus(busRoute, 122, T11_01, T11_15, C) .rail(439, T11_30, T11_50, D) .carHail(D10m, E) diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json index be584a875be..b4172c0f70f 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json @@ -11,13 +11,26 @@ "streetName" : "street", "area" : false, "relativeDirection" : "DEPART", - "absoluteDirection" : "NORTHEAST" + "absoluteDirection" : "NORTHEAST", + "entity" : null }, { "streetName" : "elevator", "area" : false, "relativeDirection" : "ELEVATOR", - "absoluteDirection" : null + "absoluteDirection" : null, + "entity" : null + + }, + { + "streetName" : "entrance", + "area" : false, + "relativeDirection" : "CONTINUE", + "absoluteDirection" : null, + "entity" : { + "__typename" : "Entrance", + "code": "A" + } } ] }, diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql index dd2b96395ad..74cbe0c599e 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql @@ -20,6 +20,12 @@ area relativeDirection absoluteDirection + entity { + __typename + ... on Entrance { + code + } + } } } } From f431d1734a189bc69e66ffed2cb3569d56aed822 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 24 Oct 2024 11:09:29 +0200 Subject: [PATCH 035/269] 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 97c2de6ac34d7315ffb07555f011b4aeacd52cfa Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Fri, 25 Oct 2024 15:15:02 +0300 Subject: [PATCH 036/269] Rename variables to match with graphql --- .../algorithm/mapping/StatesToWalkStepsMapper.java | 6 ++---- .../street/model/vertex/StationEntranceVertex.java | 12 ++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) 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 6bcef1b0da9..8a289713532 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 @@ -529,10 +529,8 @@ private WalkStepBuilder createStationEntranceWalkStep( var step = createWalkStep(forwardState, backState); step.withRelativeDirection(RelativeDirection.CONTINUE); - - step.withEntrance( - Entrance.withCode(((StationEntranceVertex) backState.getVertex()).getEntranceName()) - ); + System.out.println(backState.getVertex().toString()); + step.withEntrance(Entrance.withCode(((StationEntranceVertex) backState.getVertex()).getCode())); return step; } diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java index af1e824a7bd..083c0d52a0f 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java @@ -2,18 +2,18 @@ public class StationEntranceVertex extends OsmVertex { - private final String entranceName; + private final String code; - public StationEntranceVertex(double x, double y, long nodeId, String entranceName) { + public StationEntranceVertex(double x, double y, long nodeId, String code) { super(x, y, nodeId); - this.entranceName = entranceName; + this.code = code; } - public String getEntranceName() { - return entranceName; + public String getCode() { + return code; } public String toString() { - return "StationEntranceVertex(" + super.toString() + ")"; + return "StationEntranceVertex(" + super.toString() + ", code=" + code + ")"; } } From d844561b9d4a5f98d11a4f8eaabe63fe1b461a42 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Fri, 25 Oct 2024 16:10:14 +0300 Subject: [PATCH 037/269] Rename variables --- .../street/model/vertex/VertexFactory.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java b/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java index 007d3119a55..61f74841245 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java @@ -94,12 +94,8 @@ public ExitVertex exit(long nid, Coordinate coordinate, String exitName) { return addToGraph(new ExitVertex(coordinate.x, coordinate.y, nid, exitName)); } - public StationEntranceVertex stationEntrance( - long nid, - Coordinate coordinate, - String entranceName - ) { - return addToGraph(new StationEntranceVertex(coordinate.x, coordinate.y, nid, entranceName)); + public StationEntranceVertex stationEntrance(long nid, Coordinate coordinate, String code) { + return addToGraph(new StationEntranceVertex(coordinate.x, coordinate.y, nid, code)); } public OsmVertex osm( From 079485ed16fdd5f24c5f394f813d9d419e541249 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 25 Oct 2024 21:25:44 +0300 Subject: [PATCH 038/269] 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 039/269] 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 040/269] 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 041/269] 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 042/269] 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 043/269] 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 02a07ea2ed91ade71e5d4e5384a5e93fcf31ea67 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Sun, 27 Oct 2024 12:39:45 +0200 Subject: [PATCH 044/269] Rename function --- .../module/osm/parameters/OsmExtractParameters.java | 2 +- .../module/osm/parameters/OsmExtractParametersBuilder.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java index 6e8ff0f793c..37edaf687ab 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java @@ -31,7 +31,7 @@ public record OsmExtractParameters( builder.getSource(), builder.getOsmTagMapper(), builder.getTimeZone(), - builder.getIncludeOsmSubwayEntrances() + builder.includeOsmSubwayEntrances() ); } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParametersBuilder.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParametersBuilder.java index 9abc6e6bcc7..66c65e05d81 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParametersBuilder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParametersBuilder.java @@ -72,7 +72,7 @@ public ZoneId getTimeZone() { return timeZone; } - public boolean getIncludeOsmSubwayEntrances() { + public boolean includeOsmSubwayEntrances() { return includeOsmSubwayEntrances; } From 5dc74dd48555935dcd1deddcf39de0d4b73eaac0 Mon Sep 17 00:00:00 2001 From: Henrik Sundell <47221103+HenrikSundell@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:05:39 +0200 Subject: [PATCH 045/269] Fix comments Co-authored-by: Joel Lappalainen --- .../java/org/opentripplanner/osm/model/OsmNode.java | 4 ++-- .../org/opentripplanner/apis/gtfs/schema.graphqls | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmNode.java b/application/src/main/java/org/opentripplanner/osm/model/OsmNode.java index 2c343401adf..cb9fcd679f0 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmNode.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmNode.java @@ -64,9 +64,9 @@ public boolean isBarrier() { } /** - * Checks if this node is an subway station entrance + * Checks if this node is a subway station entrance. * - * @return true if it does + * @return true if it is */ public boolean isSubwayEntrance() { return hasTag("railway") && "subway_entrance".equals(getTag("railway")); 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 9c4d0e4dab1..b2d48f7e0b1 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -447,13 +447,13 @@ type Emissions { co2: Grams } -"Station entrance/exit" +"Station entrance or exit." type Entrance { - "Code of entrance/exit eg A or B" + "Short text or a number that identifies the entrance or exit for passengers. For example, `A` or `B`." code: String - "Gtfs id of entrance/exit" + "ID of the entrance in the format of `FeedId:EntranceId`." gtfsId: String - "Name of entrance/exit" + "Name of the entrance or exit." name: String } @@ -2658,7 +2658,7 @@ type step { distance: Float "The elevation profile as a list of { distance, elevation } values." elevationProfile: [elevationProfileComponent] - "Step entity e.g. an entrance" + "Step entity, e.g. an entrance." entity: StepEntity "When exiting a highway or traffic circle, the exit name/number." exit: String From 5d55646ab1861c7553d7a7b3b9f91a5d328bbd59 Mon Sep 17 00:00:00 2001 From: Henrik Sundell <47221103+HenrikSundell@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:06:35 +0200 Subject: [PATCH 046/269] Remove println Co-authored-by: Joel Lappalainen --- .../routing/algorithm/mapping/StatesToWalkStepsMapper.java | 1 - 1 file changed, 1 deletion(-) 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 8a289713532..f04a6a5a2d9 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 @@ -529,7 +529,6 @@ private WalkStepBuilder createStationEntranceWalkStep( var step = createWalkStep(forwardState, backState); step.withRelativeDirection(RelativeDirection.CONTINUE); - System.out.println(backState.getVertex().toString()); step.withEntrance(Entrance.withCode(((StationEntranceVertex) backState.getVertex()).getCode())); return step; } From d1067c6679a68ee252f8bda96300d28dee8cfe75 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Mon, 28 Oct 2024 16:06:52 +0200 Subject: [PATCH 047/269] Remove unnecessary imports --- .../apis/gtfs/datafetchers/StepEntityTypeResolver.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java index 5fc8a123226..5e7d6098344 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java @@ -4,16 +4,7 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; -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.UnknownModel; import org.opentripplanner.model.plan.Entrance; -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.site.RegularStop; -import org.opentripplanner.transit.model.timetable.Trip; public class StepEntityTypeResolver implements TypeResolver { From 5c97ad17f610d7458d6ff5e1a79c1b03ac643f18 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Mon, 28 Oct 2024 18:40:41 +0200 Subject: [PATCH 048/269] Add accessibilty information to entrances --- .../apis/gtfs/generated/GraphQLDataFetchers.java | 13 +++---------- .../graph_builder/module/osm/VertexGenerator.java | 8 +++----- .../org/opentripplanner/model/plan/Entrance.java | 8 +++++--- .../algorithm/mapping/StatesToWalkStepsMapper.java | 4 +++- .../street/model/vertex/StationEntranceVertex.java | 8 +++++++- .../street/model/vertex/VertexFactory.java | 9 +++++++-- .../org/opentripplanner/apis/gtfs/schema.graphqls | 2 ++ .../apis/gtfs/GraphQLIntegrationTest.java | 2 +- .../apis/gtfs/expectations/walk-steps.json | 3 ++- .../apis/gtfs/queries/walk-steps.graphql | 1 + 10 files changed, 34 insertions(+), 24 deletions(-) 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 179758bd78d..ec0e573d705 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 @@ -1,11 +1,9 @@ //THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. package org.opentripplanner.apis.gtfs.generated; -import graphql.relay.Connection; import graphql.relay.Connection; import graphql.relay.DefaultEdge; import graphql.relay.Edge; -import graphql.relay.Edge; import graphql.schema.DataFetcher; import graphql.schema.TypeResolver; import java.util.Currency; @@ -27,12 +25,8 @@ import org.opentripplanner.apis.gtfs.model.FeedPublisher; import org.opentripplanner.apis.gtfs.model.PlanPageInfo; import org.opentripplanner.apis.gtfs.model.RideHailingProvider; -import org.opentripplanner.apis.gtfs.model.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.TripOccupancy; -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; @@ -55,8 +49,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.realtimevehicles.model.RealtimeVehicle; @@ -67,7 +59,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.VehicleRentalSystem; import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.transit.model.basic.Money; @@ -366,8 +357,10 @@ public interface GraphQLEmissions { public DataFetcher co2(); } - /** Station entrance/exit */ + /** Station entrance or exit. */ public interface GraphQLEntrance { + public DataFetcher accessible(); + public DataFetcher code(); public DataFetcher gtfsId(); diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java index 42bbaf659c1..b4c3c08aa8b 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java @@ -104,11 +104,9 @@ IntersectionVertex getVertexForOsmNode(OsmNode node, OsmWithTags way) { if (includeOsmSubwayEntrances && node.isSubwayEntrance()) { String ref = node.getTag("ref"); - if (ref != null) { - iv = vertexFactory.stationEntrance(nid, coordinate, ref); - } else { - iv = vertexFactory.stationEntrance(nid, coordinate, null); - } + + boolean accessible = node.isTag("wheelchair", "yes"); + iv = vertexFactory.stationEntrance(nid, coordinate, ref, accessible); } if (iv == null) { diff --git a/application/src/main/java/org/opentripplanner/model/plan/Entrance.java b/application/src/main/java/org/opentripplanner/model/plan/Entrance.java index 7b28ee992a4..fa1e0fe5e57 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/Entrance.java +++ b/application/src/main/java/org/opentripplanner/model/plan/Entrance.java @@ -5,14 +5,16 @@ public final class Entrance extends StepEntity { private final String code; private final String gtfsId; private final String name; + private final boolean accessible; - public Entrance(String code, String gtfsId, String name) { + public Entrance(String code, String gtfsId, String name, boolean accessible) { this.code = code; this.gtfsId = gtfsId; this.name = name; + this.accessible = accessible; } - public static Entrance withCode(String code) { - return new Entrance(code, null, null); + public static Entrance withCodeAndAccessible(String code, boolean accessible) { + return new Entrance(code, null, null, accessible); } } 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 f04a6a5a2d9..14ed2a553e4 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 @@ -529,7 +529,9 @@ private WalkStepBuilder createStationEntranceWalkStep( var step = createWalkStep(forwardState, backState); step.withRelativeDirection(RelativeDirection.CONTINUE); - step.withEntrance(Entrance.withCode(((StationEntranceVertex) backState.getVertex()).getCode())); + + StationEntranceVertex vertex = (StationEntranceVertex) backState.getVertex(); + step.withEntrance(Entrance.withCodeAndAccessible(vertex.getCode(), vertex.isAccessible())); return step; } diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java index 083c0d52a0f..3f83a356dea 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java @@ -3,16 +3,22 @@ public class StationEntranceVertex extends OsmVertex { private final String code; + private final boolean accessible; - public StationEntranceVertex(double x, double y, long nodeId, String code) { + public StationEntranceVertex(double x, double y, long nodeId, String code, boolean accessible) { super(x, y, nodeId); this.code = code; + this.accessible = accessible; } public String getCode() { return code; } + public boolean isAccessible() { + return accessible; + } + public String toString() { return "StationEntranceVertex(" + super.toString() + ", code=" + code + ")"; } diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java b/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java index 61f74841245..1c37d39adb6 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java @@ -94,8 +94,13 @@ public ExitVertex exit(long nid, Coordinate coordinate, String exitName) { return addToGraph(new ExitVertex(coordinate.x, coordinate.y, nid, exitName)); } - public StationEntranceVertex stationEntrance(long nid, Coordinate coordinate, String code) { - return addToGraph(new StationEntranceVertex(coordinate.x, coordinate.y, nid, code)); + public StationEntranceVertex stationEntrance( + long nid, + Coordinate coordinate, + String code, + boolean accessible + ) { + return addToGraph(new StationEntranceVertex(coordinate.x, coordinate.y, nid, code, accessible)); } public OsmVertex osm( 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 b2d48f7e0b1..83c32e2c1ca 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -449,6 +449,8 @@ type Emissions { "Station entrance or exit." type Entrance { + "True if the entrance is wheelchair accessible." + accessible: Boolean "Short text or a number that identifies the entrance or exit for passengers. For example, `A` or `B`." code: String "ID of the entrance in the format of `FeedId:EntranceId`." 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 020a1ec6113..d2448a92c59 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -235,7 +235,7 @@ public Set getRoutesForStop(StopLocation stop) { var step3 = walkStep("entrance") .withRelativeDirection(RelativeDirection.CONTINUE) - .withEntrance(Entrance.withCode("A")) + .withEntrance(Entrance.withCodeAndAccessible("A", true)) .build(); Itinerary i1 = newItinerary(A, T11_00) diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json index b4172c0f70f..3a8e952880f 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json @@ -29,7 +29,8 @@ "absoluteDirection" : null, "entity" : { "__typename" : "Entrance", - "code": "A" + "code": "A", + "accessible": true } } ] diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql index 74cbe0c599e..5e7c0493c35 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql @@ -24,6 +24,7 @@ __typename ... on Entrance { code + accessible } } } From 7ed48c3d556ab37bb375f8c23eb34018c30d5b39 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Thu, 31 Oct 2024 15:11:20 +0200 Subject: [PATCH 049/269] 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 050/269] 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 051/269] 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 052/269] 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 053/269] 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 e10e0a2fae17e16cfebb1e888652a531e7fef493 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Thu, 7 Nov 2024 13:22:19 +0200 Subject: [PATCH 054/269] Use existing entrance class for walk steps --- .../mapping/RelativeDirectionMapper.java | 1 + .../restapi/model/ApiRelativeDirection.java | 1 + .../apis/gtfs/GtfsGraphQLIndex.java | 4 +- .../apis/gtfs/datafetchers/EntranceImpl.java | 53 +++++++++++++++++++ .../datafetchers/StepEntityTypeResolver.java | 2 +- .../apis/gtfs/datafetchers/stepImpl.java | 4 +- .../gtfs/generated/GraphQLDataFetchers.java | 9 ++-- .../apis/gtfs/generated/GraphQLTypes.java | 1 + .../apis/gtfs/mapping/DirectionMapper.java | 1 + .../opentripplanner/model/plan/Entrance.java | 20 ------- .../model/plan/RelativeDirection.java | 1 + .../model/plan/StepEntity.java | 3 -- .../opentripplanner/model/plan/WalkStep.java | 14 ++--- .../model/plan/WalkStepBuilder.java | 9 ++-- .../mapping/StatesToWalkStepsMapper.java | 17 ++++-- .../framework/AbstractTransitEntity.java | 2 +- .../opentripplanner/apis/gtfs/schema.graphqls | 12 ++--- .../apis/gtfs/GraphQLIntegrationTest.java | 14 +++-- .../apis/gtfs/expectations/walk-steps.json | 11 ++-- .../apis/gtfs/queries/walk-steps.graphql | 9 ++-- 20 files changed, 115 insertions(+), 73 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java delete mode 100644 application/src/main/java/org/opentripplanner/model/plan/Entrance.java delete mode 100644 application/src/main/java/org/opentripplanner/model/plan/StepEntity.java diff --git a/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/RelativeDirectionMapper.java b/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/RelativeDirectionMapper.java index a1bdd145a55..708da1fd6c3 100644 --- a/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/RelativeDirectionMapper.java +++ b/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/RelativeDirectionMapper.java @@ -25,6 +25,7 @@ public static ApiRelativeDirection mapRelativeDirection(RelativeDirection domain case UTURN_RIGHT -> ApiRelativeDirection.UTURN_RIGHT; case ENTER_STATION -> ApiRelativeDirection.ENTER_STATION; case EXIT_STATION -> ApiRelativeDirection.EXIT_STATION; + case ENTER_OR_EXIT_STATION -> ApiRelativeDirection.ENTER_OR_EXIT_STATION; case FOLLOW_SIGNS -> ApiRelativeDirection.FOLLOW_SIGNS; }; } diff --git a/application/src/ext/java/org/opentripplanner/ext/restapi/model/ApiRelativeDirection.java b/application/src/ext/java/org/opentripplanner/ext/restapi/model/ApiRelativeDirection.java index 02a530f06de..eb624df5ea6 100644 --- a/application/src/ext/java/org/opentripplanner/ext/restapi/model/ApiRelativeDirection.java +++ b/application/src/ext/java/org/opentripplanner/ext/restapi/model/ApiRelativeDirection.java @@ -21,5 +21,6 @@ public enum ApiRelativeDirection { UTURN_RIGHT, ENTER_STATION, EXIT_STATION, + ENTER_OR_EXIT_STATION, FOLLOW_SIGNS, } 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 5b288762262..a5eedb4c71c 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -37,6 +37,7 @@ 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.EntranceImpl; import org.opentripplanner.apis.gtfs.datafetchers.FareProductTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.FareProductUseImpl; import org.opentripplanner.apis.gtfs.datafetchers.FeedImpl; @@ -58,7 +59,6 @@ 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.StepEntityTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.StopGeometriesImpl; import org.opentripplanner.apis.gtfs.datafetchers.StopImpl; import org.opentripplanner.apis.gtfs.datafetchers.StopOnRouteImpl; @@ -125,7 +125,6 @@ protected static GraphQLSchema buildSchema() { .type("Node", type -> type.typeResolver(new NodeTypeResolver())) .type("PlaceInterface", type -> type.typeResolver(new PlaceInterfaceTypeResolver())) .type("StopPosition", type -> type.typeResolver(new StopPosition() {})) - .type("StepEntity", type -> type.typeResolver(new StepEntityTypeResolver())) .type("FareProduct", type -> type.typeResolver(new FareProductTypeResolver())) .type("AlertEntity", type -> type.typeResolver(new AlertEntityTypeResolver())) .type(typeWiring.build(AgencyImpl.class)) @@ -181,6 +180,7 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(FareProductUseImpl.class)) .type(typeWiring.build(DefaultFareProductImpl.class)) .type(typeWiring.build(TripOccupancyImpl.class)) + .type(typeWiring.build(EntranceImpl.class)) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java new file mode 100644 index 00000000000..89f80286eed --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java @@ -0,0 +1,53 @@ +package org.opentripplanner.apis.gtfs.datafetchers; + +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import org.opentripplanner.apis.gtfs.GraphQLUtils; +import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.transit.model.site.Entrance; + +public class EntranceImpl implements GraphQLDataFetchers.GraphQLEntrance { + + @Override + public DataFetcher code() { + return environment -> { + Entrance entrance = getEntrance(environment); + return entrance != null && entrance.getCode() != null ? entrance.getCode() : null; + }; + } + + @Override + public DataFetcher gtfsId() { + return environment -> { + Entrance entrance = getEntrance(environment); + return entrance != null && entrance.getId() != null ? entrance.getId().toString() : null; + }; + } + + @Override + public DataFetcher name() { + return environment -> { + Entrance entrance = getEntrance(environment); + return entrance != null && entrance.getName() != null ? entrance.getName().toString() : null; + }; + } + + @Override + public DataFetcher wheelchairAccessible() { + return environment -> { + Entrance entrance = getEntrance(environment); + return entrance != null + ? GraphQLUtils.toGraphQL(entrance.getWheelchairAccessibility()) + : null; + }; + } + + /** + * Helper method to retrieve the Entrance object from the DataFetchingEnvironment. + */ + private Entrance getEntrance(DataFetchingEnvironment environment) { + Object source = environment.getSource(); + return source instanceof Entrance ? (Entrance) source : null; + } +} diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java index 5e7d6098344..43762c97a7d 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java @@ -4,7 +4,7 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; -import org.opentripplanner.model.plan.Entrance; +import org.opentripplanner.transit.model.site.Entrance; public class StepEntityTypeResolver implements TypeResolver { 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 658f2d321ad..994434b4e4c 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 @@ -54,8 +54,8 @@ public DataFetcher exit() { } @Override - public DataFetcher entity() { - return environment -> getSource(environment).getEntity(); + public DataFetcher entrance() { + return environment -> getSource(environment).getEntrance(); } @Override 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 ec0e573d705..f99fceb410c 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 @@ -359,13 +359,13 @@ public interface GraphQLEmissions { /** Station entrance or exit. */ public interface GraphQLEntrance { - public DataFetcher accessible(); - public DataFetcher code(); public DataFetcher gtfsId(); public DataFetcher name(); + + public DataFetcher wheelchairAccessible(); } /** A 'medium' that a fare product applies to, for example cash, 'Oyster Card' or 'DB Navigator App'. */ @@ -984,9 +984,6 @@ public interface GraphQLRoutingError { public DataFetcher inputField(); } - /** Entity to a step */ - public interface GraphQLStepEntity extends TypeResolver {} - /** * Stop can represent either a single public transport stop, where passengers can * board and/or disembark vehicles, or a station, which contains multiple stops. @@ -1438,7 +1435,7 @@ public interface GraphQLStep { public DataFetcher> elevationProfile(); - public DataFetcher entity(); + public DataFetcher entrance(); public DataFetcher exit(); 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 bf413f5fca4..9e4119100c0 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 @@ -3942,6 +3942,7 @@ public enum GraphQLRelativeDirection { CONTINUE, DEPART, ELEVATOR, + ENTER_OR_EXIT_STATION, ENTER_STATION, EXIT_STATION, FOLLOW_SIGNS, diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java index 69a78b05f55..1439cdd34c3 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java @@ -38,6 +38,7 @@ public static GraphQLRelativeDirection map(RelativeDirection relativeDirection) case UTURN_RIGHT -> GraphQLRelativeDirection.UTURN_RIGHT; case ENTER_STATION -> GraphQLRelativeDirection.ENTER_STATION; case EXIT_STATION -> GraphQLRelativeDirection.EXIT_STATION; + case ENTER_OR_EXIT_STATION -> GraphQLRelativeDirection.ENTER_OR_EXIT_STATION; case FOLLOW_SIGNS -> GraphQLRelativeDirection.FOLLOW_SIGNS; }; } diff --git a/application/src/main/java/org/opentripplanner/model/plan/Entrance.java b/application/src/main/java/org/opentripplanner/model/plan/Entrance.java deleted file mode 100644 index fa1e0fe5e57..00000000000 --- a/application/src/main/java/org/opentripplanner/model/plan/Entrance.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opentripplanner.model.plan; - -public final class Entrance extends StepEntity { - - private final String code; - private final String gtfsId; - private final String name; - private final boolean accessible; - - public Entrance(String code, String gtfsId, String name, boolean accessible) { - this.code = code; - this.gtfsId = gtfsId; - this.name = name; - this.accessible = accessible; - } - - public static Entrance withCodeAndAccessible(String code, boolean accessible) { - return new Entrance(code, null, null, accessible); - } -} diff --git a/application/src/main/java/org/opentripplanner/model/plan/RelativeDirection.java b/application/src/main/java/org/opentripplanner/model/plan/RelativeDirection.java index ffc8993d0db..fbdb836ab6a 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/RelativeDirection.java +++ b/application/src/main/java/org/opentripplanner/model/plan/RelativeDirection.java @@ -21,6 +21,7 @@ public enum RelativeDirection { UTURN_RIGHT, ENTER_STATION, EXIT_STATION, + ENTER_OR_EXIT_STATION, FOLLOW_SIGNS; public static RelativeDirection calculate( diff --git a/application/src/main/java/org/opentripplanner/model/plan/StepEntity.java b/application/src/main/java/org/opentripplanner/model/plan/StepEntity.java deleted file mode 100644 index e6bfd587bfc..00000000000 --- a/application/src/main/java/org/opentripplanner/model/plan/StepEntity.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.opentripplanner.model.plan; - -public abstract class StepEntity {} 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 8114246d7e8..1f72a2960df 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -8,9 +8,9 @@ import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.lang.DoubleUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; -import org.opentripplanner.model.plan.StepEntity; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.note.StreetNote; +import org.opentripplanner.transit.model.site.Entrance; /** * Represents one instruction in walking directions. Three examples from New York City: @@ -45,7 +45,7 @@ public final class WalkStep { private final boolean walkingBike; private final String exit; - private final StepEntity entity; + private final Entrance entrance; private final ElevationProfile elevationProfile; private final boolean stayOn; @@ -58,7 +58,7 @@ public final class WalkStep { I18NString directionText, Set streetNotes, String exit, - StepEntity entity, + Entrance entrance, ElevationProfile elevationProfile, boolean bogusName, boolean walkingBike, @@ -79,7 +79,7 @@ public final class WalkStep { this.walkingBike = walkingBike; this.area = area; this.exit = exit; - this.entity = entity; + this.entrance = entrance; this.elevationProfile = elevationProfile; this.stayOn = stayOn; this.edges = List.copyOf(Objects.requireNonNull(edges)); @@ -135,10 +135,10 @@ public String getExit() { } /** - * Entity related to a step e.g. building entrance/exit. + * Get information about building entrance or exit. */ - public Object getEntity() { - return entity; + public Entrance getEntrance() { + return entrance; } /** 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 02b73c0ce15..b8055125051 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -9,10 +9,9 @@ import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.lang.DoubleUtils; import org.opentripplanner.framework.lang.IntUtils; -import org.opentripplanner.model.plan.Entrance; -import org.opentripplanner.model.plan.StepEntity; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.note.StreetNote; +import org.opentripplanner.transit.model.site.Entrance; public class WalkStepBuilder { @@ -27,7 +26,7 @@ public class WalkStepBuilder { private RelativeDirection relativeDirection; private ElevationProfile elevationProfile; private String exit; - private StepEntity entity; + private Entrance entrance; private boolean stayOn = false; /** * Distance used for appending elevation profiles @@ -78,7 +77,7 @@ public WalkStepBuilder withExit(String exit) { } public WalkStepBuilder withEntrance(Entrance entrance) { - this.entity = entrance; + this.entrance = entrance; return this; } @@ -164,7 +163,7 @@ public WalkStep build() { directionText, streetNotes, exit, - entity, + entrance, elevationProfile, bogusName, walkingBike, 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 14ed2a553e4..67b76578d9b 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 @@ -13,7 +13,6 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.plan.ElevationProfile; -import org.opentripplanner.model.plan.Entrance; import org.opentripplanner.model.plan.RelativeDirection; import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.model.plan.WalkStepBuilder; @@ -30,6 +29,8 @@ import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.state.State; +import org.opentripplanner.transit.model.basic.Accessibility; +import org.opentripplanner.transit.model.site.Entrance; /** * Process a list of states into a list of walking/driving instructions for a street leg. @@ -528,10 +529,20 @@ private WalkStepBuilder createStationEntranceWalkStep( // don't care what came before or comes after var step = createWalkStep(forwardState, backState); - step.withRelativeDirection(RelativeDirection.CONTINUE); + step.withRelativeDirection(RelativeDirection.ENTER_OR_EXIT_STATION); StationEntranceVertex vertex = (StationEntranceVertex) backState.getVertex(); - step.withEntrance(Entrance.withCodeAndAccessible(vertex.getCode(), vertex.isAccessible())); + + Entrance entrance = Entrance + .of(null) + .withCode(vertex.getCode()) + .withCoordinate(new WgsCoordinate(vertex.getCoordinate())) + .withWheelchairAccessibility( + vertex.isAccessible() ? Accessibility.POSSIBLE : Accessibility.NOT_POSSIBLE + ) + .build(); + + step.withEntrance(entrance); return step; } diff --git a/application/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java b/application/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java index 258d505b53c..6f233aa9b31 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java +++ b/application/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java @@ -30,7 +30,7 @@ public abstract class AbstractTransitEntity< private final FeedScopedId id; public AbstractTransitEntity(FeedScopedId id) { - this.id = Objects.requireNonNull(id); + this.id = id; // TODO IS THIS WORNG } public final FeedScopedId getId() { 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 83c32e2c1ca..de1a748c1e9 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -73,9 +73,6 @@ interface PlaceInterface { "Entity related to an alert" union AlertEntity = Agency | Pattern | Route | RouteType | Stop | StopOnRoute | StopOnTrip | Trip | Unknown -"Entity to a step" -union StepEntity = Entrance - union StopPosition = PositionAtStop | PositionBetweenStops "A public transport agency" @@ -449,14 +446,14 @@ type Emissions { "Station entrance or exit." type Entrance { - "True if the entrance is wheelchair accessible." - accessible: Boolean "Short text or a number that identifies the entrance or exit for passengers. For example, `A` or `B`." code: String "ID of the entrance in the format of `FeedId:EntranceId`." gtfsId: String "Name of the entrance or exit." name: String + "Whether the entrance or exit is accessible by wheelchair" + wheelchairAccessible: WheelchairBoarding } "A 'medium' that a fare product applies to, for example cash, 'Oyster Card' or 'DB Navigator App'." @@ -2660,8 +2657,8 @@ type step { distance: Float "The elevation profile as a list of { distance, elevation } values." elevationProfile: [elevationProfileComponent] - "Step entity, e.g. an entrance." - entity: StepEntity + "Information about an station entrance or exit" + entrance: Entrance "When exiting a highway or traffic circle, the exit name/number." exit: String "The latitude of the start of the step." @@ -3329,6 +3326,7 @@ enum RelativeDirection { CONTINUE DEPART ELEVATOR + ENTER_OR_EXIT_STATION ENTER_STATION EXIT_STATION FOLLOW_SIGNS 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 d2448a92c59..a672ff3db3f 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -49,7 +49,6 @@ import org.opentripplanner.model.fare.ItineraryFares; import org.opentripplanner.model.fare.RiderCategory; import org.opentripplanner.model.plan.Emissions; -import org.opentripplanner.model.plan.Entrance; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.RelativeDirection; @@ -81,6 +80,7 @@ import org.opentripplanner.standalone.config.framework.json.JsonSupport; import org.opentripplanner.test.support.FilePatternSource; import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; +import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.basic.Money; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.AbstractBuilder; @@ -90,6 +90,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.site.Entrance; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; @@ -232,10 +233,15 @@ public Set getRoutesForStop(StopLocation stop) { .withAbsoluteDirection(20) .build(); var step2 = walkStep("elevator").withRelativeDirection(RelativeDirection.ELEVATOR).build(); - + Entrance entrance = Entrance + .of(null) + .withCoordinate(new WgsCoordinate(60, 80)) + .withCode("A") + .withWheelchairAccessibility(Accessibility.POSSIBLE) + .build(); var step3 = walkStep("entrance") - .withRelativeDirection(RelativeDirection.CONTINUE) - .withEntrance(Entrance.withCodeAndAccessible("A", true)) + .withRelativeDirection(RelativeDirection.ENTER_OR_EXIT_STATION) + .withEntrance(entrance) .build(); Itinerary i1 = newItinerary(A, T11_00) diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json index 3a8e952880f..7cb304d6bb1 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json @@ -12,25 +12,24 @@ "area" : false, "relativeDirection" : "DEPART", "absoluteDirection" : "NORTHEAST", - "entity" : null + "entrance" : null }, { "streetName" : "elevator", "area" : false, "relativeDirection" : "ELEVATOR", "absoluteDirection" : null, - "entity" : null + "entrance" : null }, { "streetName" : "entrance", "area" : false, - "relativeDirection" : "CONTINUE", + "relativeDirection" : "ENTER_OR_EXIT_STATION", "absoluteDirection" : null, - "entity" : { - "__typename" : "Entrance", + "entrance": { "code": "A", - "accessible": true + "wheelchairAccessible": "POSSIBLE" } } ] diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql index 5e7c0493c35..c7de3f22ee1 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql @@ -20,12 +20,9 @@ area relativeDirection absoluteDirection - entity { - __typename - ... on Entrance { - code - accessible - } + entrance { + code + wheelchairAccessible } } } From 34761fe1b48d7ec93f735785a58f13e3b993e100 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Thu, 7 Nov 2024 14:41:35 +0200 Subject: [PATCH 055/269] Fix EntranceImpl --- .../apis/gtfs/datafetchers/EntranceImpl.java | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java index 89f80286eed..16cb3f5f6e7 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java @@ -1,7 +1,6 @@ package org.opentripplanner.apis.gtfs.datafetchers; import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; import org.opentripplanner.apis.gtfs.GraphQLUtils; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; @@ -12,42 +11,37 @@ public class EntranceImpl implements GraphQLDataFetchers.GraphQLEntrance { @Override public DataFetcher code() { return environment -> { - Entrance entrance = getEntrance(environment); - return entrance != null && entrance.getCode() != null ? entrance.getCode() : null; + Entrance entrance = environment.getSource(); + return entrance.getCode(); }; } @Override public DataFetcher gtfsId() { return environment -> { - Entrance entrance = getEntrance(environment); - return entrance != null && entrance.getId() != null ? entrance.getId().toString() : null; + Entrance entrance = environment.getSource(); + return entrance.getId() != null ? entrance.getId().toString() : null; }; } @Override public DataFetcher name() { return environment -> { - Entrance entrance = getEntrance(environment); - return entrance != null && entrance.getName() != null ? entrance.getName().toString() : null; + Entrance entrance = environment.getSource(); + return entrance.getName() != null + ? org.opentripplanner.framework.graphql.GraphQLUtils.getTranslation( + entrance.getName(), + environment + ) + : null; }; } @Override public DataFetcher wheelchairAccessible() { return environment -> { - Entrance entrance = getEntrance(environment); - return entrance != null - ? GraphQLUtils.toGraphQL(entrance.getWheelchairAccessibility()) - : null; + Entrance entrance = environment.getSource(); + return GraphQLUtils.toGraphQL(entrance.getWheelchairAccessibility()); }; } - - /** - * Helper method to retrieve the Entrance object from the DataFetchingEnvironment. - */ - private Entrance getEntrance(DataFetchingEnvironment environment) { - Object source = environment.getSource(); - return source instanceof Entrance ? (Entrance) source : null; - } } From c84b7cf878974075ccd0049d813b33f94c9bf2e3 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Fri, 8 Nov 2024 10:09:53 +0200 Subject: [PATCH 056/269] Add id to walk step entrances --- .../opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java | 2 +- .../apis/gtfs/generated/GraphQLDataFetchers.java | 2 +- .../routing/algorithm/mapping/StatesToWalkStepsMapper.java | 5 ++++- .../street/model/vertex/StationEntranceVertex.java | 4 ++++ .../transit/model/framework/AbstractTransitEntity.java | 2 +- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 2 +- .../opentripplanner/apis/gtfs/GraphQLIntegrationTest.java | 3 ++- .../opentripplanner/apis/gtfs/expectations/walk-steps.json | 3 ++- .../org/opentripplanner/apis/gtfs/queries/walk-steps.graphql | 1 + 9 files changed, 17 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java index 16cb3f5f6e7..c062f62d07a 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java @@ -17,7 +17,7 @@ public DataFetcher code() { } @Override - public DataFetcher gtfsId() { + public DataFetcher entranceId() { return environment -> { Entrance entrance = environment.getSource(); return entrance.getId() != null ? entrance.getId().toString() : 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 f99fceb410c..0d547b4cf10 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 @@ -361,7 +361,7 @@ public interface GraphQLEmissions { public interface GraphQLEntrance { public DataFetcher code(); - public DataFetcher gtfsId(); + public DataFetcher entranceId(); public DataFetcher name(); 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 67b76578d9b..81f6ffcb461 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 @@ -30,6 +30,7 @@ import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.state.State; import org.opentripplanner.transit.model.basic.Accessibility; +import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.Entrance; /** @@ -533,8 +534,10 @@ private WalkStepBuilder createStationEntranceWalkStep( StationEntranceVertex vertex = (StationEntranceVertex) backState.getVertex(); + FeedScopedId entranceId = new FeedScopedId("osm", vertex.getId()); + Entrance entrance = Entrance - .of(null) + .of(entranceId) .withCode(vertex.getCode()) .withCoordinate(new WgsCoordinate(vertex.getCoordinate())) .withWheelchairAccessibility( diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java index 3f83a356dea..e55ac7078db 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java @@ -19,6 +19,10 @@ public boolean isAccessible() { return accessible; } + public String getId() { + return Long.toString(nodeId); + } + public String toString() { return "StationEntranceVertex(" + super.toString() + ", code=" + code + ")"; } diff --git a/application/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java b/application/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java index 6f233aa9b31..258d505b53c 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java +++ b/application/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java @@ -30,7 +30,7 @@ public abstract class AbstractTransitEntity< private final FeedScopedId id; public AbstractTransitEntity(FeedScopedId id) { - this.id = id; // TODO IS THIS WORNG + this.id = Objects.requireNonNull(id); } public final FeedScopedId getId() { 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 de1a748c1e9..b4a5ce6da15 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -449,7 +449,7 @@ type Entrance { "Short text or a number that identifies the entrance or exit for passengers. For example, `A` or `B`." code: String "ID of the entrance in the format of `FeedId:EntranceId`." - gtfsId: String + entranceId: String "Name of the entrance or exit." name: String "Whether the entrance or exit is accessible by wheelchair" 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 a672ff3db3f..d67038ec3f6 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -233,8 +233,9 @@ public Set getRoutesForStop(StopLocation stop) { .withAbsoluteDirection(20) .build(); var step2 = walkStep("elevator").withRelativeDirection(RelativeDirection.ELEVATOR).build(); + FeedScopedId entranceId = new FeedScopedId("osm", "123"); Entrance entrance = Entrance - .of(null) + .of(entranceId) .withCoordinate(new WgsCoordinate(60, 80)) .withCode("A") .withWheelchairAccessibility(Accessibility.POSSIBLE) diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json index 7cb304d6bb1..a0a781153f1 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json @@ -29,7 +29,8 @@ "absoluteDirection" : null, "entrance": { "code": "A", - "wheelchairAccessible": "POSSIBLE" + "wheelchairAccessible": "POSSIBLE", + "entranceId": "osm:123" } } ] diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql index c7de3f22ee1..45e2eed904a 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql @@ -23,6 +23,7 @@ entrance { code wheelchairAccessible + entranceId } } } From 2ea8a522e6616f2832b7410ed896c6434758c238 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Fri, 8 Nov 2024 10:37:19 +0200 Subject: [PATCH 057/269] Remove old file --- .../datafetchers/StepEntityTypeResolver.java | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java deleted file mode 100644 index 43762c97a7d..00000000000 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepEntityTypeResolver.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.opentripplanner.apis.gtfs.datafetchers; - -import graphql.TypeResolutionEnvironment; -import graphql.schema.GraphQLObjectType; -import graphql.schema.GraphQLSchema; -import graphql.schema.TypeResolver; -import org.opentripplanner.transit.model.site.Entrance; - -public class StepEntityTypeResolver implements TypeResolver { - - @Override - public GraphQLObjectType getType(TypeResolutionEnvironment environment) { - Object o = environment.getObject(); - GraphQLSchema schema = environment.getSchema(); - - if (o instanceof Entrance) { - return schema.getObjectType("Entrance"); - } - return null; - } -} From 39b0db37f86b28fe5f03f7da2716f47bdbfed3cc Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Fri, 8 Nov 2024 10:38:59 +0200 Subject: [PATCH 058/269] Fix otp version --- .../standalone/config/buildconfig/OsmConfig.java | 3 ++- doc/user/BuildConfiguration.md | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/OsmConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/OsmConfig.java index c1a3963c6a5..5cc67844b50 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/OsmConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/OsmConfig.java @@ -1,6 +1,7 @@ package org.opentripplanner.standalone.config.buildconfig; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; import org.opentripplanner.graph_builder.module.osm.parameters.OsmExtractParameters; import org.opentripplanner.graph_builder.module.osm.parameters.OsmExtractParametersBuilder; @@ -88,7 +89,7 @@ public static OsmExtractParametersBuilder mapOsmGenericParameters( .withIncludeOsmSubwayEntrances( node .of("includeOsmSubwayEntrances") - .since(V2_2) + .since(V2_7) .summary("Whether to include subway entrances from the OSM data." + documentationAddition) .docDefaultValue(docDefaults.includeOsmSubwayEntrances()) .asBoolean(defaults.includeOsmSubwayEntrances()) diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index 8b38da6fdbb..c5fdfa8095b 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -84,12 +84,12 @@ Sections follow that describe particular settings in more depth. |    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | |    [ferryIdsNotAllowedForBicycle](#nd_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | | [osm](#osm) | `object[]` | Configure properties for a given OpenStreetMap feed. | *Optional* | | 2.2 | -|       includeOsmSubwayEntrances | `boolean` | Whether to include subway entrances from the OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | `false` | 2.2 | +|       includeOsmSubwayEntrances | `boolean` | Whether to include subway entrances from the OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | `false` | 2.7 | |       [osmTagMapping](#osm_0_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. Overrides the value specified in `osmDefaults`. | *Optional* | `"default"` | 2.2 | |       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | |       timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | | 2.2 | | osmDefaults | `object` | Default properties for OpenStreetMap feeds. | *Optional* | | 2.2 | -|    includeOsmSubwayEntrances | `boolean` | Whether to include subway entrances from the OSM data. | *Optional* | `false` | 2.2 | +|    includeOsmSubwayEntrances | `boolean` | Whether to include subway entrances from the OSM data. | *Optional* | `false` | 2.7 | |    [osmTagMapping](#od_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. | *Optional* | `"default"` | 2.2 | |    timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. | *Optional* | | 2.2 | | [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | From 3b6bf3fbab24a97c67957effb7838b8383b652ff Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Fri, 8 Nov 2024 10:39:31 +0200 Subject: [PATCH 059/269] Remove unused import --- .../org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java | 1 - 1 file changed, 1 deletion(-) 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 9e4119100c0..32e1fc69870 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 @@ -1,7 +1,6 @@ // 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; From c9df52d9e8e3a2359ef39c64e78295230090ff2e Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Sun, 10 Nov 2024 17:02:44 +0200 Subject: [PATCH 060/269] Require entranceId --- .../opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java | 2 +- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java index c062f62d07a..d7e2e6aa3ac 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java @@ -20,7 +20,7 @@ public DataFetcher code() { public DataFetcher entranceId() { return environment -> { Entrance entrance = environment.getSource(); - return entrance.getId() != null ? entrance.getId().toString() : null; + return entrance.getId().toString(); }; } 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 80094ecfd3f..c7cb1a6e346 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -449,7 +449,7 @@ type Entrance { "Short text or a number that identifies the entrance or exit for passengers. For example, `A` or `B`." code: String "ID of the entrance in the format of `FeedId:EntranceId`." - entranceId: String + entranceId: String! "Name of the entrance or exit." name: String "Whether the entrance or exit is accessible by wheelchair" From 7a9a8f694ad29a782827605bdc70e7cd8adea370 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Sun, 10 Nov 2024 17:59:58 +0200 Subject: [PATCH 061/269] Rename methods --- .../opentripplanner/ext/restapi/mapping/WalkStepMapper.java | 2 +- .../opentripplanner/apis/gtfs/datafetchers/stepImpl.java | 4 ++-- .../apis/transmodel/model/plan/PathGuidanceType.java | 2 +- .../main/java/org/opentripplanner/model/plan/WalkStep.java | 6 +++--- 4 files changed, 7 insertions(+), 7 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..1df3d7e6fa7 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 @@ -39,7 +39,7 @@ public ApiWalkStep mapWalkStep(WalkStep domain) { api.streetName = domain.getDirectionText().toString(locale); api.absoluteDirection = domain.getAbsoluteDirection().map(AbsoluteDirectionMapper::mapAbsoluteDirection).orElse(null); - api.exit = domain.getExit(); + api.exit = domain.getHighwayExit(); api.stayOn = domain.isStayOn(); api.area = domain.getArea(); api.bogusName = domain.getBogusName(); 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 994434b4e4c..4414fc7b4cd 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 @@ -50,12 +50,12 @@ public DataFetcher> elevationProfile() { @Override public DataFetcher exit() { - return environment -> getSource(environment).getExit(); + return environment -> getSource(environment).getHighwayExit(); } @Override public DataFetcher entrance() { - return environment -> getSource(environment).getEntrance(); + return environment -> getSource(environment).getStationEntrance(); } @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..12eaa089a50 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 @@ -65,7 +65,7 @@ public static GraphQLObjectType create(GraphQLObjectType elevationStepType) { .name("exit") .description("When exiting a highway or traffic circle, the exit name/number.") .type(Scalars.GraphQLString) - .dataFetcher(environment -> ((WalkStep) environment.getSource()).getExit()) + .dataFetcher(environment -> ((WalkStep) environment.getSource()).getHighwayExit()) .build() ) .field( 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 9e055e33559..96bde6d1ef3 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -130,14 +130,14 @@ public Optional getAbsoluteDirection() { /** * When exiting a highway or traffic circle, the exit name/number. */ - public String getExit() { + public String getHighwayExit() { return exit; } /** - * Get information about building entrance or exit. + * Get information about a subway station entrance or exit. */ - public Entrance getEntrance() { + public Entrance getStationEntrance() { return entrance; } From c9139e31f60662e7cae7cf35ace311ec9adc1d37 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Mon, 11 Nov 2024 15:04:33 +0200 Subject: [PATCH 062/269] Update dosumentation --- .../routing/algorithm/mapping/StatesToWalkStepsMapper.java | 3 ++- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) 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 81f6ffcb461..b0df9658301 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 @@ -529,7 +529,8 @@ private WalkStepBuilder createStationEntranceWalkStep( ) { // don't care what came before or comes after var step = createWalkStep(forwardState, backState); - + // There is not a way to definitively determine if a user is entering or exiting the station, + // since the doors might be between or inside stations. step.withRelativeDirection(RelativeDirection.ENTER_OR_EXIT_STATION); StationEntranceVertex vertex = (StationEntranceVertex) backState.getVertex(); 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 c7cb1a6e346..13b576a46c7 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -444,11 +444,11 @@ type Emissions { co2: Grams } -"Station entrance or exit." +"Station entrance or exit, originating from OSM or GTFS data." type Entrance { "Short text or a number that identifies the entrance or exit for passengers. For example, `A` or `B`." code: String - "ID of the entrance in the format of `FeedId:EntranceId`." + "ID of the entrance in the format of `FeedId:EntranceId`. If the `FeedId` is `osm`, the entrance originates from OSM data." entranceId: String! "Name of the entrance or exit." name: String From 7b210246ee0c5be3b54b4eff66722ffdd7e39e90 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Mon, 11 Nov 2024 15:18:55 +0200 Subject: [PATCH 063/269] Update documentation --- .../routing/algorithm/mapping/StatesToWalkStepsMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b0df9658301..39941b72ecd 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 @@ -529,7 +529,7 @@ private WalkStepBuilder createStationEntranceWalkStep( ) { // don't care what came before or comes after var step = createWalkStep(forwardState, backState); - // There is not a way to definitively determine if a user is entering or exiting the station, + // There is not a way to definitively determine if a user is entering or exiting the station, // since the doors might be between or inside stations. step.withRelativeDirection(RelativeDirection.ENTER_OR_EXIT_STATION); From ce7719cf50c103b24a4a25530ec9be29aaa67d5a Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Mon, 11 Nov 2024 15:20:23 +0200 Subject: [PATCH 064/269] Remove redundant null check --- .../apis/gtfs/datafetchers/EntranceImpl.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java index d7e2e6aa3ac..9891d107479 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java @@ -28,12 +28,10 @@ public DataFetcher entranceId() { public DataFetcher name() { return environment -> { Entrance entrance = environment.getSource(); - return entrance.getName() != null - ? org.opentripplanner.framework.graphql.GraphQLUtils.getTranslation( - entrance.getName(), - environment - ) - : null; + return org.opentripplanner.framework.graphql.GraphQLUtils.getTranslation( + entrance.getName(), + environment + ); }; } From f7ed65d74e8a3e26b0091b5b56444c3a1fdb2055 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Mon, 11 Nov 2024 15:55:04 +0000 Subject: [PATCH 065/269] add Platform and PlatformEdge into the graph --- .../graph_builder/module/osm/OsmModule.java | 53 ++++++++++++++++--- .../street/model/edge/LinearPlatform.java | 9 ++++ .../street/model/edge/LinearPlatformEdge.java | 11 ++++ .../model/edge/LinearPlatformEdgeBuilder.java | 20 +++++++ 4 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatform.java create mode 100644 application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdge.java create mode 100644 application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdgeBuilder.java 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..11c8428b0de 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 @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; @@ -28,6 +29,8 @@ import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.street.model.StreetLimitationParameters; import org.opentripplanner.street.model.StreetTraversalPermission; +import org.opentripplanner.street.model.edge.LinearPlatform; +import org.opentripplanner.street.model.edge.LinearPlatformEdgeBuilder; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetEdgeBuilder; import org.opentripplanner.street.model.vertex.BarrierVertex; @@ -304,6 +307,8 @@ private void buildBasicGraph() { // where the current edge should start OsmNode osmStartNode = null; + var platform = getPlatform(way); + for (int i = 0; i < nodes.size() - 1; i++) { OsmNode segmentStartOsmNode = osmdb.getNode(nodes.get(i)); @@ -384,7 +389,8 @@ private void buildBasicGraph() { way, i, permissions, - geometry + geometry, + platform ); params.edgeNamer().recordEdges(way, streets); @@ -407,6 +413,33 @@ private void buildBasicGraph() { LOG.info(progress.completeMessage()); } + private Optional getPlatform(OsmWay way) { + if (way.isBoardingLocation()) { + var nodeRefs = way.getNodeRefs(); + var size = nodeRefs.size(); + var nodes = new Coordinate[size]; + for (int i = 0; i < size; i++) { + nodes[i] = osmdb.getNode(nodeRefs.get(i)).getCoordinate(); + } + + var geometryFactory = GeometryUtils.getGeometryFactory(); + + var geometry = geometryFactory.createLineString(nodes); + + var references = way.getMultiTagValues(params.boardingAreaRefTags()); + + return Optional.of( + new LinearPlatform( + params.edgeNamer().getNameForWay(way, "platform " + way.getId()), + geometry, + references + ) + ); + } else { + return Optional.empty(); + } + } + private void validateBarriers() { List vertices = graph.getVerticesOfType(BarrierVertex.class); vertices.forEach(bv -> bv.makeBarrierAtEndReachable()); @@ -464,7 +497,8 @@ private StreetEdgePair getEdgesForStreet( OsmWay way, int index, StreetTraversalPermission permissions, - LineString geometry + LineString geometry, + Optional platform ) { // No point in returning edges that can't be traversed by anyone. if (permissions.allowsNothing()) { @@ -490,7 +524,8 @@ private StreetEdgePair getEdgesForStreet( length, permissionsFront, geometry, - false + false, + platform ); } if (permissionsBack.allowsAnything()) { @@ -503,7 +538,8 @@ private StreetEdgePair getEdgesForStreet( length, permissionsBack, backGeometry, - true + true, + platform ); } if (street != null && backStreet != null) { @@ -520,14 +556,19 @@ private StreetEdge getEdgeForStreet( double length, StreetTraversalPermission permissions, LineString geometry, - boolean back + boolean back, + Optional platform ) { String label = "way " + way.getId() + " from " + index; label = label.intern(); I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); - StreetEdgeBuilder seb = new StreetEdgeBuilder<>() + var seb = platform + .>map(p -> new LinearPlatformEdgeBuilder().withPlatform(p)) + .orElse(new StreetEdgeBuilder<>()); + + seb .withFromVertex(startEndpoint) .withToVertex(endEndpoint) .withGeometry(geometry) diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatform.java b/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatform.java new file mode 100644 index 00000000000..688d4418530 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatform.java @@ -0,0 +1,9 @@ +package org.opentripplanner.street.model.edge; + +import java.io.Serializable; +import java.util.Set; +import org.locationtech.jts.geom.LineString; +import org.opentripplanner.framework.i18n.I18NString; + +public record LinearPlatform(I18NString name, LineString geometry, Set references) + implements Serializable {} diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdge.java new file mode 100644 index 00000000000..849266166a2 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdge.java @@ -0,0 +1,11 @@ +package org.opentripplanner.street.model.edge; + +public class LinearPlatformEdge extends StreetEdge { + + public final LinearPlatform platform; + + protected LinearPlatformEdge(LinearPlatformEdgeBuilder builder) { + super(builder); + platform = builder.platform(); + } +} diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdgeBuilder.java b/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdgeBuilder.java new file mode 100644 index 00000000000..7057f9a7e42 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdgeBuilder.java @@ -0,0 +1,20 @@ +package org.opentripplanner.street.model.edge; + +public class LinearPlatformEdgeBuilder extends StreetEdgeBuilder { + + private LinearPlatform platform; + + @Override + public LinearPlatformEdge buildAndConnect() { + return Edge.connectToGraph(new LinearPlatformEdge(this)); + } + + public LinearPlatform platform() { + return platform; + } + + public LinearPlatformEdgeBuilder withPlatform(LinearPlatform platform) { + this.platform = platform; + return this; + } +} From 2d423a896f599c4acc77fcfc19766732fc374ff9 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Mon, 11 Nov 2024 17:12:28 +0000 Subject: [PATCH 066/269] link linear platforms to stops --- .../module/OsmBoardingLocationsModule.java | 114 +++++++++++++----- .../routing/linking/VertexLinker.java | 49 +++++++- .../edge/BoardingLocationToStopLink.java | 10 +- 3 files changed, 137 insertions(+), 36 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java index c4acabefd6c..eb91d0b1d30 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java @@ -1,8 +1,12 @@ package org.opentripplanner.graph_builder.module; import jakarta.inject.Inject; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; +import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -17,6 +21,8 @@ import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; import org.opentripplanner.street.model.edge.Edge; +import org.opentripplanner.street.model.edge.LinearPlatform; +import org.opentripplanner.street.model.edge.LinearPlatformEdge; import org.opentripplanner.street.model.edge.NamedArea; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetEdgeBuilder; @@ -24,9 +30,11 @@ import org.opentripplanner.street.model.vertex.OsmBoardingLocationVertex; import org.opentripplanner.street.model.vertex.StreetVertex; import org.opentripplanner.street.model.vertex.TransitStopVertex; +import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.model.vertex.VertexFactory; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; +import org.opentripplanner.transit.model.site.StationElement; import org.opentripplanner.transit.service.TimetableRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,14 +107,15 @@ public void buildGraph() { } private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { - var stopCode = ts.getStop().getCode(); - var stopId = ts.getStop().getId().getId(); + var stop = ts.getStop(); + var stopCode = stop.getCode(); + var stopId = stop.getId().getId(); Envelope envelope = new Envelope(ts.getCoordinate()); double xscale = Math.cos(ts.getCoordinate().y * Math.PI / 180); envelope.expandBy(searchRadiusDegrees / xscale, searchRadiusDegrees); - // if the boarding location is an OSM node it's generated in the OSM processing step but we need + // if the boarding location is a node it's generated in the OSM processing step but we need // link it here var nearbyBoardingLocations = index .getVerticesForEnvelope(envelope) @@ -116,26 +125,14 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { .collect(Collectors.toSet()); for (var boardingLocation : nearbyBoardingLocations) { - if ( - (stopCode != null && boardingLocation.references.contains(stopCode)) || - boardingLocation.references.contains(stopId) - ) { + if (matchesReference(stop, boardingLocation.references)) { if (!boardingLocation.isConnectedToStreetNetwork()) { linker.linkVertexPermanently( boardingLocation, new TraverseModeSet(TraverseMode.WALK), LinkingDirection.BOTH_WAYS, - (osmBoardingLocationVertex, splitVertex) -> { - if (osmBoardingLocationVertex == splitVertex) { - return List.of(); - } - // the OSM boarding location vertex is not connected to the street network, so we - // need to link it first - return List.of( - linkBoardingLocationToStreetNetwork(boardingLocation, splitVertex), - linkBoardingLocationToStreetNetwork(splitVertex, boardingLocation) - ); - } + (osmBoardingLocationVertex, splitVertex) -> + getConnectingEdges(boardingLocation, osmBoardingLocationVertex, splitVertex) ); } linkBoardingLocationToStop(ts, stopCode, boardingLocation); @@ -143,9 +140,50 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { } } - // if the boarding location is an OSM way (an area) then we are generating the vertex here and + // if the boarding location is a non-area way we are finding the vertex representing the + // center of the way, splitting if needed + var nearbyLinearPlatformEdges = new HashMap>(); + + for (var edge : index.getEdgesForEnvelope(envelope)) { + if (edge instanceof LinearPlatformEdge platformEdge) { + var platform = platformEdge.platform; + if (matchesReference(stop, platform.references())) { + if (!nearbyLinearPlatformEdges.containsKey(platform)) { + var list = new ArrayList(); + list.add(platformEdge); + nearbyLinearPlatformEdges.put(platform, list); + } else { + nearbyLinearPlatformEdges.get(platform).add(platformEdge); + } + } + } + } + + for (var platformEdgeList : nearbyLinearPlatformEdges.entrySet()) { + LinearPlatform platform = platformEdgeList.getKey(); + var name = platform.name(); + var label = "platform-centroid/%s".formatted(stop.getId().toString()); + var centroid = platform.geometry().getCentroid(); + var boardingLocation = vertexFactory.osmBoardingLocation( + new Coordinate(centroid.getX(), centroid.getY()), + label, + platform.references(), + name + ); + for (var vertex : linker.linkToSpecificStreetEdgesPermanently( + boardingLocation, + new TraverseModeSet(TraverseMode.WALK), + LinkingDirection.BOTH_WAYS, + platformEdgeList.getValue().stream().map(StreetEdge.class::cast).collect(Collectors.toSet()) + )) { + linkBoardingLocationToStop(ts, stopCode, vertex); + } + return true; + } + + // if the boarding location is an area then we are generating the vertex here and // use the AreaEdgeList to link it to the correct vertices of the platform edge - var nearbyEdgeLists = index + var nearbyAreaEdgeList = index .getEdgesForEnvelope(envelope) .stream() .filter(AreaEdge.class::isInstance) @@ -155,18 +193,15 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { // Iterate over all nearby areas representing transit stops in OSM, linking to them if they have a stop code or id // in their ref= tag that matches the GTFS stop code of this StopVertex. - for (var edgeList : nearbyEdgeLists) { - if ( - (stopCode != null && edgeList.references.contains(stopCode)) || - edgeList.references.contains(stopId) - ) { + for (var edgeList : nearbyAreaEdgeList) { + if (matchesReference(stop, edgeList.references)) { var name = edgeList .getAreas() .stream() .findFirst() .map(NamedArea::getName) .orElse(LOCALIZED_PLATFORM_NAME); - var label = "platform-centroid/%s".formatted(ts.getStop().getId().toString()); + var label = "platform-centroid/%s".formatted(stop.getId().toString()); var centroid = edgeList.getGeometry().getCentroid(); var boardingLocation = vertexFactory.osmBoardingLocation( new Coordinate(centroid.getX(), centroid.getY()), @@ -182,6 +217,22 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { return false; } + private List getConnectingEdges( + OsmBoardingLocationVertex boardingLocation, + Vertex osmBoardingLocationVertex, + StreetVertex splitVertex + ) { + if (osmBoardingLocationVertex == splitVertex) { + return List.of(); + } + // the OSM boarding location vertex is not connected to the street network, so we + // need to link it first + return List.of( + linkBoardingLocationToStreetNetwork(boardingLocation, splitVertex), + linkBoardingLocationToStreetNetwork(splitVertex, boardingLocation) + ); + } + private StreetEdge linkBoardingLocationToStreetNetwork(StreetVertex from, StreetVertex to) { var line = GeometryUtils.makeLineString(List.of(from.getCoordinate(), to.getCoordinate())); return new StreetEdgeBuilder<>() @@ -197,8 +248,8 @@ private StreetEdge linkBoardingLocationToStreetNetwork(StreetVertex from, Street private void linkBoardingLocationToStop( TransitStopVertex ts, - String stopCode, - OsmBoardingLocationVertex boardingLocation + @Nullable String stopCode, + StreetVertex boardingLocation ) { BoardingLocationToStopLink.createBoardingLocationToStopLink(ts, boardingLocation); BoardingLocationToStopLink.createBoardingLocationToStopLink(boardingLocation, ts); @@ -210,4 +261,11 @@ private void linkBoardingLocationToStop( boardingLocation.getCoordinate() ); } + + private boolean matchesReference(StationElement stop, Set references) { + var stopCode = stop.getCode(); + var stopId = stop.getId().getId(); + + return (stopCode != null && references.contains(stopCode)) || references.contains(stopId); + } } diff --git a/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java b/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java index 48f5ff997c8..a433f3882e1 100644 --- a/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java +++ b/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java @@ -7,6 +7,7 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; +import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; @@ -226,20 +227,38 @@ private DisposableEdgeCollection link( return tempEdges; } + public Set linkToSpecificStreetEdgesPermanently( + Vertex vertex, + TraverseModeSet traverseModes, + LinkingDirection direction, + Set edges + ) { + var xscale = getXscale(vertex); + return linkToCandidateEdges( + vertex, + traverseModes, + direction, + Scope.PERMANENT, + null, + edges.stream().map(e -> new DistanceTo<>(e, distance(vertex, e, xscale))).toList(), + xscale + ); + } + private Set linkToStreetEdges( Vertex vertex, TraverseModeSet traverseModes, LinkingDirection direction, Scope scope, int radiusMeters, - DisposableEdgeCollection tempEdges + @Nullable DisposableEdgeCollection tempEdges ) { final double radiusDeg = SphericalDistanceLibrary.metersToDegrees(radiusMeters); Envelope env = new Envelope(vertex.getCoordinate()); // Perform a simple local equirectangular projection, so distances are expressed in degrees latitude. - final double xscale = Math.cos(vertex.getLat() * Math.PI / 180); + final double xscale = getXscale(vertex); // Expand more in the longitude direction than the latitude direction to account for converging meridians. env.expandBy(radiusDeg / xscale, radiusDeg); @@ -257,6 +276,30 @@ private Set linkToStreetEdges( .filter(ead -> ead.distanceDegreesLat < radiusDeg) .toList(); + return linkToCandidateEdges( + vertex, + traverseModes, + direction, + scope, + tempEdges, + candidateEdges, + xscale + ); + } + + private static double getXscale(Vertex vertex) { + return Math.cos(vertex.getLat() * Math.PI / 180); + } + + private Set linkToCandidateEdges( + Vertex vertex, + TraverseModeSet traverseModes, + LinkingDirection direction, + Scope scope, + @Nullable DisposableEdgeCollection tempEdges, + List> candidateEdges, + double xscale + ) { if (candidateEdges.isEmpty()) { return Set.of(); } @@ -269,7 +312,7 @@ private Set linkToStreetEdges( return closestEdges .stream() .map(ce -> link(vertex, ce.item, xscale, scope, direction, tempEdges, linkedAreas)) - .filter(v -> v != null) + .filter(Objects::nonNull) .collect(Collectors.toSet()); } diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/BoardingLocationToStopLink.java b/application/src/main/java/org/opentripplanner/street/model/edge/BoardingLocationToStopLink.java index 2b306b63cf3..235ec7c6be5 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/BoardingLocationToStopLink.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/BoardingLocationToStopLink.java @@ -3,7 +3,7 @@ import java.util.List; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.GeometryUtils; -import org.opentripplanner.street.model.vertex.OsmBoardingLocationVertex; +import org.opentripplanner.street.model.vertex.StreetVertex; import org.opentripplanner.street.model.vertex.TransitStopVertex; /** @@ -12,16 +12,16 @@ */ public class BoardingLocationToStopLink extends StreetTransitEntityLink { - private BoardingLocationToStopLink(OsmBoardingLocationVertex fromv, TransitStopVertex tov) { + private BoardingLocationToStopLink(StreetVertex fromv, TransitStopVertex tov) { super(fromv, tov, tov.getWheelchairAccessibility()); } - private BoardingLocationToStopLink(TransitStopVertex fromv, OsmBoardingLocationVertex tov) { + private BoardingLocationToStopLink(TransitStopVertex fromv, StreetVertex tov) { super(fromv, tov, fromv.getWheelchairAccessibility()); } public static BoardingLocationToStopLink createBoardingLocationToStopLink( - OsmBoardingLocationVertex fromv, + StreetVertex fromv, TransitStopVertex tov ) { return connectToGraph(new BoardingLocationToStopLink(fromv, tov)); @@ -29,7 +29,7 @@ public static BoardingLocationToStopLink createBoardingLocationToStopLink( public static BoardingLocationToStopLink createBoardingLocationToStopLink( TransitStopVertex fromv, - OsmBoardingLocationVertex tov + StreetVertex tov ) { return connectToGraph(new BoardingLocationToStopLink(fromv, tov)); } From 89b3ac7e1c193c8e9bfa274a4c5142b84f9eba45 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Mon, 11 Nov 2024 17:32:03 +0000 Subject: [PATCH 067/269] render PlatformEdge on debug layer --- .../org/opentripplanner/apis/vectortiles/DebugStyleSpec.java | 2 ++ .../resources/org/opentripplanner/apis/vectortiles/style.json | 3 +++ 2 files changed, 5 insertions(+) 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 3c8423c5270..079012e7404 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -18,6 +18,7 @@ import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.edge.ElevatorHopEdge; import org.opentripplanner.street.model.edge.EscalatorEdge; +import org.opentripplanner.street.model.edge.LinearPlatformEdge; import org.opentripplanner.street.model.edge.PathwayEdge; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetStationCentroidLink; @@ -61,6 +62,7 @@ public class DebugStyleSpec { private static final Class[] EDGES_TO_DISPLAY = new Class[] { StreetEdge.class, AreaEdge.class, + LinearPlatformEdge.class, EscalatorEdge.class, PathwayEdge.class, ElevatorHopEdge.class, 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 8a0e457396e..e349a886ec4 100644 --- a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -437,6 +437,7 @@ "class", "StreetEdge", "AreaEdge", + "LinearPlatformEdge", "EscalatorEdge", "PathwayEdge", "ElevatorHopEdge", @@ -518,6 +519,7 @@ "class", "StreetEdge", "AreaEdge", + "LinearPlatformEdge", "EscalatorEdge", "PathwayEdge", "ElevatorHopEdge", @@ -550,6 +552,7 @@ "class", "StreetEdge", "AreaEdge", + "LinearPlatformEdge", "EscalatorEdge", "PathwayEdge", "ElevatorHopEdge", From 672a96830d0614f198334460bc52f58e29ee902c Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Wed, 13 Nov 2024 15:17:02 +0200 Subject: [PATCH 068/269] 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 b73741100d9cb5a6a3691ded6976691880ed7682 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Thu, 14 Nov 2024 09:49:28 +0200 Subject: [PATCH 069/269] Add feature union to steps --- .../apis/gtfs/GtfsGraphQLIndex.java | 2 ++ .../apis/gtfs/datafetchers/EntranceImpl.java | 13 +++++++--- .../datafetchers/StepFeatureTypeResolver.java | 25 +++++++++++++++++++ .../apis/gtfs/datafetchers/stepImpl.java | 4 +-- .../gtfs/generated/GraphQLDataFetchers.java | 8 +++--- .../apis/gtfs/model/StepFeature.java | 20 +++++++++++++++ .../opentripplanner/model/plan/WalkStep.java | 14 +++++------ .../model/plan/WalkStepBuilder.java | 7 +++--- .../opentripplanner/apis/gtfs/schema.graphqls | 7 ++++-- .../apis/gtfs/expectations/walk-steps.json | 11 ++++---- .../apis/gtfs/queries/walk-steps.graphql | 11 +++++--- 11 files changed, 92 insertions(+), 30 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepFeatureTypeResolver.java create mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.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 a5eedb4c71c..22e8f2e6fa7 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -59,6 +59,7 @@ 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.StepFeatureTypeResolver; import org.opentripplanner.apis.gtfs.datafetchers.StopGeometriesImpl; import org.opentripplanner.apis.gtfs.datafetchers.StopImpl; import org.opentripplanner.apis.gtfs.datafetchers.StopOnRouteImpl; @@ -127,6 +128,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("StepFeature", type -> type.typeResolver(new StepFeatureTypeResolver())) .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/EntranceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java index 9891d107479..bcf37d42cc7 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java @@ -4,6 +4,7 @@ import org.opentripplanner.apis.gtfs.GraphQLUtils; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.model.StepFeature; import org.opentripplanner.transit.model.site.Entrance; public class EntranceImpl implements GraphQLDataFetchers.GraphQLEntrance { @@ -11,7 +12,8 @@ public class EntranceImpl implements GraphQLDataFetchers.GraphQLEntrance { @Override public DataFetcher code() { return environment -> { - Entrance entrance = environment.getSource(); + StepFeature feature = environment.getSource(); + Entrance entrance = (Entrance) feature.getFeature(); return entrance.getCode(); }; } @@ -19,7 +21,8 @@ public DataFetcher code() { @Override public DataFetcher entranceId() { return environment -> { - Entrance entrance = environment.getSource(); + StepFeature feature = environment.getSource(); + Entrance entrance = (Entrance) feature.getFeature(); return entrance.getId().toString(); }; } @@ -27,7 +30,8 @@ public DataFetcher entranceId() { @Override public DataFetcher name() { return environment -> { - Entrance entrance = environment.getSource(); + StepFeature feature = environment.getSource(); + Entrance entrance = (Entrance) feature.getFeature(); return org.opentripplanner.framework.graphql.GraphQLUtils.getTranslation( entrance.getName(), environment @@ -38,7 +42,8 @@ public DataFetcher name() { @Override public DataFetcher wheelchairAccessible() { return environment -> { - Entrance entrance = environment.getSource(); + StepFeature feature = environment.getSource(); + Entrance entrance = (Entrance) feature.getFeature(); return GraphQLUtils.toGraphQL(entrance.getWheelchairAccessibility()); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepFeatureTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepFeatureTypeResolver.java new file mode 100644 index 00000000000..8748d87700d --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepFeatureTypeResolver.java @@ -0,0 +1,25 @@ +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.StepFeature; +import org.opentripplanner.transit.model.site.Entrance; + +public class StepFeatureTypeResolver implements TypeResolver { + + @Override + public GraphQLObjectType getType(TypeResolutionEnvironment environment) { + Object o = environment.getObject(); + GraphQLSchema schema = environment.getSchema(); + + if (o instanceof StepFeature) { + Object feature = ((StepFeature) o).getFeature(); + if (feature instanceof Entrance) { + return schema.getObjectType("Entrance"); + } + } + return null; + } +} 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 4414fc7b4cd..74453d6e7c4 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 @@ -54,8 +54,8 @@ public DataFetcher exit() { } @Override - public DataFetcher entrance() { - return environment -> getSource(environment).getStationEntrance(); + public DataFetcher feature() { + return environment -> getSource(environment).getStepFeature(); } @Override 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 0d547b4cf10..b7889c8be3a 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 @@ -357,7 +357,7 @@ public interface GraphQLEmissions { public DataFetcher co2(); } - /** Station entrance or exit. */ + /** Station entrance or exit, originating from OSM or GTFS data. */ public interface GraphQLEntrance { public DataFetcher code(); @@ -984,6 +984,8 @@ public interface GraphQLRoutingError { public DataFetcher inputField(); } + public interface GraphQLStepFeature extends TypeResolver {} + /** * Stop can represent either a single public transport stop, where passengers can * board and/or disembark vehicles, or a station, which contains multiple stops. @@ -1435,10 +1437,10 @@ public interface GraphQLStep { public DataFetcher> elevationProfile(); - public DataFetcher entrance(); - public DataFetcher exit(); + public DataFetcher feature(); + public DataFetcher lat(); public DataFetcher lon(); diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java b/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java new file mode 100644 index 00000000000..c4842e25476 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java @@ -0,0 +1,20 @@ +package org.opentripplanner.apis.gtfs.model; + +import org.opentripplanner.transit.model.site.Entrance; + +/** + * A generic wrapper class for features in Walk steps. + * At the moment only subway station entrances. + **/ +public class StepFeature { + + private final Object feature; + + public StepFeature(Entrance entrance) { + this.feature = entrance; + } + + public Object getFeature() { + return feature; + } +} 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 96bde6d1ef3..660b973a8a8 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -4,11 +4,11 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import org.opentripplanner.apis.gtfs.model.StepFeature; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.note.StreetNote; -import org.opentripplanner.transit.model.site.Entrance; import org.opentripplanner.utils.lang.DoubleUtils; import org.opentripplanner.utils.tostring.ToStringBuilder; @@ -45,7 +45,7 @@ public final class WalkStep { private final boolean walkingBike; private final String exit; - private final Entrance entrance; + private final StepFeature feature; private final ElevationProfile elevationProfile; private final boolean stayOn; @@ -58,7 +58,7 @@ public final class WalkStep { I18NString directionText, Set streetNotes, String exit, - Entrance entrance, + StepFeature feature, ElevationProfile elevationProfile, boolean bogusName, boolean walkingBike, @@ -79,7 +79,7 @@ public final class WalkStep { this.walkingBike = walkingBike; this.area = area; this.exit = exit; - this.entrance = entrance; + this.feature = feature; this.elevationProfile = elevationProfile; this.stayOn = stayOn; this.edges = List.copyOf(Objects.requireNonNull(edges)); @@ -135,10 +135,10 @@ public String getHighwayExit() { } /** - * Get information about a subway station entrance or exit. + * Get information about feature e.g. a subway station entrance or exit. */ - public Entrance getStationEntrance() { - return entrance; + public StepFeature getStepFeature() { + return feature; } /** 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 7ef5012e145..6752eba95e9 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Set; import javax.annotation.Nullable; +import org.opentripplanner.apis.gtfs.model.StepFeature; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.street.model.edge.Edge; @@ -26,7 +27,7 @@ public class WalkStepBuilder { private RelativeDirection relativeDirection; private ElevationProfile elevationProfile; private String exit; - private Entrance entrance; + private StepFeature feature; private boolean stayOn = false; /** * Distance used for appending elevation profiles @@ -77,7 +78,7 @@ public WalkStepBuilder withExit(String exit) { } public WalkStepBuilder withEntrance(Entrance entrance) { - this.entrance = entrance; + this.feature = new StepFeature(entrance); return this; } @@ -163,7 +164,7 @@ public WalkStep build() { directionText, streetNotes, exit, - entrance, + feature, elevationProfile, bogusName, walkingBike, 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 13b576a46c7..83fe952ce7e 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/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 +"A feature for a step" +union StepFeature = Entrance + union StopPosition = PositionAtStop | PositionBetweenStops "A public transport agency" @@ -2657,10 +2660,10 @@ type step { distance: Float "The elevation profile as a list of { distance, elevation } values." elevationProfile: [elevationProfileComponent] - "Information about an station entrance or exit" - entrance: Entrance "When exiting a highway or traffic circle, the exit name/number." exit: String + "Information about an feature associated with a step e.g. an station entrance or exit" + feature: StepFeature "The latitude of the start of the step." lat: Float "The longitude of the start of the step." diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json index a0a781153f1..8d79102fc59 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json @@ -12,14 +12,14 @@ "area" : false, "relativeDirection" : "DEPART", "absoluteDirection" : "NORTHEAST", - "entrance" : null + "feature" : null }, { "streetName" : "elevator", "area" : false, "relativeDirection" : "ELEVATOR", "absoluteDirection" : null, - "entrance" : null + "feature" : null }, { @@ -27,10 +27,11 @@ "area" : false, "relativeDirection" : "ENTER_OR_EXIT_STATION", "absoluteDirection" : null, - "entrance": { + "feature": { + "__typename": "Entrance", "code": "A", - "wheelchairAccessible": "POSSIBLE", - "entranceId": "osm:123" + "entranceId": "osm:123", + "wheelchairAccessible": "POSSIBLE" } } ] diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql index 45e2eed904a..565e620fed3 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql @@ -20,10 +20,13 @@ area relativeDirection absoluteDirection - entrance { - code - wheelchairAccessible - entranceId + feature { + __typename + ... on Entrance { + code + entranceId + wheelchairAccessible + } } } } From f547e074f2879c9e4f6bec37759a7d64fc927258 Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Thu, 14 Nov 2024 12:42:47 +0200 Subject: [PATCH 070/269] Return feature based on relativeDirection --- .../apis/gtfs/datafetchers/EntranceImpl.java | 13 ++++--------- .../gtfs/datafetchers/StepFeatureTypeResolver.java | 8 ++------ .../apis/gtfs/datafetchers/stepImpl.java | 9 ++++++++- .../apis/gtfs/model/StepFeature.java | 8 ++++---- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java index bcf37d42cc7..9891d107479 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java @@ -4,7 +4,6 @@ import org.opentripplanner.apis.gtfs.GraphQLUtils; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; -import org.opentripplanner.apis.gtfs.model.StepFeature; import org.opentripplanner.transit.model.site.Entrance; public class EntranceImpl implements GraphQLDataFetchers.GraphQLEntrance { @@ -12,8 +11,7 @@ public class EntranceImpl implements GraphQLDataFetchers.GraphQLEntrance { @Override public DataFetcher code() { return environment -> { - StepFeature feature = environment.getSource(); - Entrance entrance = (Entrance) feature.getFeature(); + Entrance entrance = environment.getSource(); return entrance.getCode(); }; } @@ -21,8 +19,7 @@ public DataFetcher code() { @Override public DataFetcher entranceId() { return environment -> { - StepFeature feature = environment.getSource(); - Entrance entrance = (Entrance) feature.getFeature(); + Entrance entrance = environment.getSource(); return entrance.getId().toString(); }; } @@ -30,8 +27,7 @@ public DataFetcher entranceId() { @Override public DataFetcher name() { return environment -> { - StepFeature feature = environment.getSource(); - Entrance entrance = (Entrance) feature.getFeature(); + Entrance entrance = environment.getSource(); return org.opentripplanner.framework.graphql.GraphQLUtils.getTranslation( entrance.getName(), environment @@ -42,8 +38,7 @@ public DataFetcher name() { @Override public DataFetcher wheelchairAccessible() { return environment -> { - StepFeature feature = environment.getSource(); - Entrance entrance = (Entrance) feature.getFeature(); + Entrance entrance = environment.getSource(); return GraphQLUtils.toGraphQL(entrance.getWheelchairAccessibility()); }; } diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepFeatureTypeResolver.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepFeatureTypeResolver.java index 8748d87700d..714518cb9ea 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepFeatureTypeResolver.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StepFeatureTypeResolver.java @@ -4,7 +4,6 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; -import org.opentripplanner.apis.gtfs.model.StepFeature; import org.opentripplanner.transit.model.site.Entrance; public class StepFeatureTypeResolver implements TypeResolver { @@ -14,11 +13,8 @@ public GraphQLObjectType getType(TypeResolutionEnvironment environment) { Object o = environment.getObject(); GraphQLSchema schema = environment.getSchema(); - if (o instanceof StepFeature) { - Object feature = ((StepFeature) o).getFeature(); - if (feature instanceof Entrance) { - return schema.getObjectType("Entrance"); - } + if (o instanceof Entrance) { + return schema.getObjectType("Entrance"); } return null; } 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 74453d6e7c4..6a1c180fad6 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 @@ -7,6 +7,7 @@ import org.opentripplanner.apis.gtfs.mapping.DirectionMapper; import org.opentripplanner.apis.gtfs.mapping.StreetNoteMapper; import org.opentripplanner.model.plan.ElevationProfile.Step; +import org.opentripplanner.model.plan.RelativeDirection; import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.routing.alertpatch.TransitAlert; @@ -55,7 +56,13 @@ public DataFetcher exit() { @Override public DataFetcher feature() { - return environment -> getSource(environment).getStepFeature(); + return environment -> { + WalkStep source = getSource(environment); + if (source.getRelativeDirection() == RelativeDirection.ENTER_OR_EXIT_STATION) { + return source.getStepFeature().getEntrance(); + } + return null; + }; } @Override diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java b/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java index c4842e25476..bf5fd8cc104 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java @@ -8,13 +8,13 @@ **/ public class StepFeature { - private final Object feature; + private final Entrance entranceFeature; public StepFeature(Entrance entrance) { - this.feature = entrance; + this.entranceFeature = entrance; } - public Object getFeature() { - return feature; + public Entrance getEntrance() { + return entranceFeature; } } From 18b84f00a2f83841ef5a993cd5d958899a3805fd Mon Sep 17 00:00:00 2001 From: Henrik Sundell Date: Thu, 14 Nov 2024 14:20:00 +0200 Subject: [PATCH 071/269] Remove StepFeature class --- .../apis/gtfs/datafetchers/stepImpl.java | 2 +- .../apis/gtfs/model/StepFeature.java | 20 ------------------- .../opentripplanner/model/plan/WalkStep.java | 14 ++++++------- .../model/plan/WalkStepBuilder.java | 7 +++---- 4 files changed, 11 insertions(+), 32 deletions(-) delete mode 100644 application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java 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 6a1c180fad6..c98237f78c5 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 @@ -59,7 +59,7 @@ public DataFetcher feature() { return environment -> { WalkStep source = getSource(environment); if (source.getRelativeDirection() == RelativeDirection.ENTER_OR_EXIT_STATION) { - return source.getStepFeature().getEntrance(); + return source.getEntrance(); } return null; }; diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java b/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java deleted file mode 100644 index bf5fd8cc104..00000000000 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/model/StepFeature.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opentripplanner.apis.gtfs.model; - -import org.opentripplanner.transit.model.site.Entrance; - -/** - * A generic wrapper class for features in Walk steps. - * At the moment only subway station entrances. - **/ -public class StepFeature { - - private final Entrance entranceFeature; - - public StepFeature(Entrance entrance) { - this.entranceFeature = entrance; - } - - public Entrance getEntrance() { - return entranceFeature; - } -} 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 660b973a8a8..2efe6e36aff 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -4,11 +4,11 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import org.opentripplanner.apis.gtfs.model.StepFeature; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.note.StreetNote; +import org.opentripplanner.transit.model.site.Entrance; import org.opentripplanner.utils.lang.DoubleUtils; import org.opentripplanner.utils.tostring.ToStringBuilder; @@ -45,7 +45,7 @@ public final class WalkStep { private final boolean walkingBike; private final String exit; - private final StepFeature feature; + private final Entrance entrance; private final ElevationProfile elevationProfile; private final boolean stayOn; @@ -58,7 +58,7 @@ public final class WalkStep { I18NString directionText, Set streetNotes, String exit, - StepFeature feature, + Entrance entrance, ElevationProfile elevationProfile, boolean bogusName, boolean walkingBike, @@ -79,7 +79,7 @@ public final class WalkStep { this.walkingBike = walkingBike; this.area = area; this.exit = exit; - this.feature = feature; + this.entrance = entrance; this.elevationProfile = elevationProfile; this.stayOn = stayOn; this.edges = List.copyOf(Objects.requireNonNull(edges)); @@ -135,10 +135,10 @@ public String getHighwayExit() { } /** - * Get information about feature e.g. a subway station entrance or exit. + * Get information about a subway station entrance or exit. */ - public StepFeature getStepFeature() { - return feature; + public Entrance getEntrance() { + return entrance; } /** 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 6752eba95e9..7ef5012e145 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.Set; import javax.annotation.Nullable; -import org.opentripplanner.apis.gtfs.model.StepFeature; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.street.model.edge.Edge; @@ -27,7 +26,7 @@ public class WalkStepBuilder { private RelativeDirection relativeDirection; private ElevationProfile elevationProfile; private String exit; - private StepFeature feature; + private Entrance entrance; private boolean stayOn = false; /** * Distance used for appending elevation profiles @@ -78,7 +77,7 @@ public WalkStepBuilder withExit(String exit) { } public WalkStepBuilder withEntrance(Entrance entrance) { - this.feature = new StepFeature(entrance); + this.entrance = entrance; return this; } @@ -164,7 +163,7 @@ public WalkStep build() { directionText, streetNotes, exit, - feature, + entrance, elevationProfile, bogusName, walkingBike, From 8885c193c6778ead7ad75728e2bae5c5859e27db Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Sun, 17 Nov 2024 23:46:45 +0200 Subject: [PATCH 072/269] 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 073/269] 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 074/269] 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 075/269] 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 12b49957ece1db3f3295c251c93186ac0527d964 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Tue, 19 Nov 2024 11:37:22 +0000 Subject: [PATCH 076/269] make all polling updater wait for graph update finish --- .../alert/GtfsRealtimeAlertsUpdater.java | 45 +++++++++---------- .../updater/trip/PollingTripUpdater.java | 5 ++- .../VehicleParkingAvailabilityUpdater.java | 5 ++- .../VehicleParkingUpdater.java | 5 ++- .../PollingVehiclePositionUpdater.java | 5 ++- .../vehicle_rental/VehicleRentalUpdater.java | 5 ++- 6 files changed, 36 insertions(+), 34 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java index b71dad6a656..0e7ab35cb13 100644 --- a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java @@ -2,6 +2,7 @@ import com.google.transit.realtime.GtfsRealtime.FeedMessage; import java.net.URI; +import java.util.concurrent.ExecutionException; import org.opentripplanner.framework.io.OtpHttpClient; import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.routing.impl.TransitAlertServiceImpl; @@ -63,32 +64,28 @@ public String toString() { } @Override - protected void runPolling() { - try { - final FeedMessage feed = otpHttpClient.getAndMap( - URI.create(url), - this.headers.asMap(), - FeedMessage.PARSER::parseFrom - ); + protected void runPolling() throws InterruptedException, ExecutionException { + final FeedMessage feed = otpHttpClient.getAndMap( + URI.create(url), + this.headers.asMap(), + FeedMessage.PARSER::parseFrom + ); - long feedTimestamp = feed.getHeader().getTimestamp(); - if (feedTimestamp == lastTimestamp) { - LOG.debug("Ignoring feed with a timestamp that has not been updated from {}", url); - return; - } - if (feedTimestamp < lastTimestamp) { - LOG.info("Ignoring feed with older than previous timestamp from {}", url); - return; - } + long feedTimestamp = feed.getHeader().getTimestamp(); + if (feedTimestamp == lastTimestamp) { + LOG.debug("Ignoring feed with a timestamp that has not been updated from {}", url); + return; + } + if (feedTimestamp < lastTimestamp) { + LOG.info("Ignoring feed with older than previous timestamp from {}", url); + return; + } - // Handle update in graph writer runnable - saveResultOnGraph.execute(context -> - updateHandler.update(feed, context.gtfsRealtimeFuzzyTripMatcher()) - ); + // Handle update in graph writer runnable + saveResultOnGraph + .execute(context -> updateHandler.update(feed, context.gtfsRealtimeFuzzyTripMatcher())) + .get(); - lastTimestamp = feedTimestamp; - } catch (Exception e) { - LOG.error("Error reading gtfs-realtime feed from " + url, e); - } + lastTimestamp = feedTimestamp; } } diff --git a/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java b/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java index c725c8b1088..42f24031839 100644 --- a/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java @@ -2,6 +2,7 @@ import com.google.transit.realtime.GtfsRealtime.TripUpdate; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.UpdateResult; @@ -73,7 +74,7 @@ public void setup(WriteToGraphCallback writeToGraphCallback) { * applies those updates to the graph. */ @Override - public void runPolling() { + public void runPolling() throws InterruptedException, ExecutionException { // Get update lists from update source List updates = updateSource.getUpdates(); var incrementality = updateSource.incrementalityOfLastUpdates(); @@ -89,7 +90,7 @@ public void runPolling() { feedId, recordMetrics ); - saveResultOnGraph.execute(runnable); + saveResultOnGraph.execute(runnable).get(); } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java index b23f46522c3..bc5217f7534 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.function.Function; import java.util.stream.Collectors; import org.opentripplanner.routing.vehicle_parking.VehicleParking; @@ -49,12 +50,12 @@ public void setup(WriteToGraphCallback writeToGraphCallback) { } @Override - protected void runPolling() { + protected void runPolling() throws InterruptedException, ExecutionException { if (source.update()) { var updates = source.getUpdates(); var graphWriterRunnable = new AvailabilityUpdater(updates); - saveResultOnGraph.execute(graphWriterRunnable); + saveResultOnGraph.execute(graphWriterRunnable).get(); } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java index 830429151b4..60a7b306052 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.function.Function; import java.util.stream.Collectors; import org.opentripplanner.routing.graph.Graph; @@ -69,7 +70,7 @@ public void setup(WriteToGraphCallback writeToGraphCallback) { } @Override - protected void runPolling() { + protected void runPolling() throws InterruptedException, ExecutionException { LOG.debug("Updating vehicle parkings from {}", source); if (!source.update()) { LOG.debug("No updates"); @@ -81,7 +82,7 @@ protected void runPolling() { VehicleParkingGraphWriterRunnable graphWriterRunnable = new VehicleParkingGraphWriterRunnable( vehicleParkings ); - saveResultOnGraph.execute(graphWriterRunnable); + saveResultOnGraph.execute(graphWriterRunnable).get(); } private class VehicleParkingGraphWriterRunnable implements GraphWriterRunnable { diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java index 4c487ac997b..a4d36f1d1c3 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java @@ -3,6 +3,7 @@ import com.google.transit.realtime.GtfsRealtime.VehiclePosition; import java.util.List; import java.util.Set; +import java.util.concurrent.ExecutionException; import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository; import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle; import org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig; @@ -64,7 +65,7 @@ public void setup(WriteToGraphCallback writeToGraphCallback) { * applies those updates to the graph. */ @Override - public void runPolling() { + public void runPolling() throws InterruptedException, ExecutionException { // Get update lists from update source List updates = vehiclePositionSource.getPositions(); @@ -77,7 +78,7 @@ public void runPolling() { fuzzyTripMatching, updates ); - saveResultOnGraph.execute(runnable); + saveResultOnGraph.execute(runnable).get(); } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java index 24686edce6c..c8030e5492a 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import java.util.stream.Stream; import org.opentripplanner.routing.linking.DisposableEdgeCollection; @@ -124,7 +125,7 @@ public String getConfigRef() { } @Override - protected void runPolling() { + protected void runPolling() throws InterruptedException, ExecutionException { LOG.debug("Updating vehicle rental stations from {}", nameForLogging); if (!source.update()) { LOG.debug("No updates from {}", nameForLogging); @@ -138,7 +139,7 @@ protected void runPolling() { stations, geofencingZones ); - saveResultOnGraph.execute(graphWriterRunnable); + saveResultOnGraph.execute(graphWriterRunnable).get(); } private class VehicleRentalGraphWriterRunnable implements GraphWriterRunnable { From 405f580ec1d1ca7e0df3f8fa2fe5d27aa9ae19cb Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Tue, 19 Nov 2024 17:19:18 +0200 Subject: [PATCH 077/269] 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 078/269] 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 ca5310034416fae6c6a6907cd91f8b97e12f7b4f Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 22 Nov 2024 13:03:57 +0200 Subject: [PATCH 079/269] 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 de5758b7b502168df8e882c602d6553d145b3be0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Nov 2024 12:39:34 +0100 Subject: [PATCH 080/269] 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 081/269] 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 bee85dee2f4991da17b4b07788fda9ae79f4c0da Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 25 Nov 2024 14:47:06 +0200 Subject: [PATCH 082/269] 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 083/269] 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 364c40112f9241b76732af9077f3e7042a48fc6f Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Wed, 27 Nov 2024 12:27:49 +0000 Subject: [PATCH 084/269] fix test after merge --- .../resources/org/opentripplanner/apis/vectortiles/style.json | 1 + 1 file changed, 1 insertion(+) 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 e3c15aee0e4..98765ed757f 100644 --- a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -419,6 +419,7 @@ "class", "StreetEdge", "AreaEdge", + "LinearPlatformEdge", "EscalatorEdge", "PathwayEdge", "ElevatorHopEdge", From 901f96a362c4bc6f818948213f883373dcb8319d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 20 Nov 2024 14:43:40 +0100 Subject: [PATCH 085/269] Add SIRI light --- .../config/routerconfig/UpdatersConfig.java | 9 ++ .../updaters/SiriETLightUpdaterConfig.java | 41 +++++++++ .../updater/UpdatersParameters.java | 3 + .../configure/UpdaterConfigurator.java | 19 +++- .../SiriETLightHttpTripUpdateSource.java | 89 +++++++++++++++++++ .../updater/SiriETLightUpdaterParameters.java | 30 +++++++ .../updater/siri/updater/SiriETUpdater.java | 14 ++- .../siri/updater/SiriETUpdaterParameters.java | 5 +- .../siri/updater/SiriLightHttpLoader.java | 55 ++++++++++++ 9 files changed, 257 insertions(+), 8 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java create mode 100644 application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightHttpTripUpdateSource.java create mode 100644 application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightUpdaterParameters.java create mode 100644 application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index ac69b43e275..a3092a35b55 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -8,6 +8,7 @@ import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_AZURE_ET_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_AZURE_SX_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_ET_GOOGLE_PUBSUB_UPDATER; +import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_ET_LIGHT; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_ET_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_SX_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.STOP_TIME_UPDATER; @@ -30,6 +31,7 @@ import org.opentripplanner.standalone.config.routerconfig.updaters.MqttGtfsRealtimeUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.PollingTripUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETGooglePubsubUpdaterConfig; +import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETLightUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriSXUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.VehicleParkingUpdaterConfig; @@ -41,6 +43,7 @@ import org.opentripplanner.updater.TimetableSnapshotSourceParameters; import org.opentripplanner.updater.UpdatersParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdaterParameters; +import org.opentripplanner.updater.siri.updater.SiriETLightUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriETUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; @@ -182,6 +185,11 @@ public List getSiriSXUpdaterParameters() { return getParameters(SIRI_SX_UPDATER); } + @Override + public List getSiriETLightUpdaterParameters() { + return getParameters(SIRI_ET_LIGHT); + } + @Override public List getMqttGtfsRealtimeUpdaterParameters() { return getParameters(MQTT_GTFS_RT_UPDATER); @@ -218,6 +226,7 @@ public enum Type { REAL_TIME_ALERTS(GtfsRealtimeAlertsUpdaterConfig::create), VEHICLE_POSITIONS(VehiclePositionsUpdaterConfig::create), SIRI_ET_UPDATER(SiriETUpdaterConfig::create), + SIRI_ET_LIGHT(SiriETLightUpdaterConfig::create), SIRI_ET_GOOGLE_PUBSUB_UPDATER(SiriETGooglePubsubUpdaterConfig::create), SIRI_SX_UPDATER(SiriSXUpdaterConfig::create), SIRI_AZURE_ET_UPDATER(SiriAzureETUpdaterConfig::create), diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java new file mode 100644 index 00000000000..c6152e7131f --- /dev/null +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java @@ -0,0 +1,41 @@ +package org.opentripplanner.standalone.config.routerconfig.updaters; + +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_0; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_3; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; + +import java.time.Duration; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; +import org.opentripplanner.updater.siri.updater.SiriETLightUpdaterParameters; + +public class SiriETLightUpdaterConfig { + + public static SiriETLightUpdaterParameters create(String configRef, NodeAdapter c) { + return new SiriETLightUpdaterParameters( + configRef, + c.of("feedId").since(V2_7).summary("The ID of the feed to apply the updates to.").asString(), + c + .of("url") + .since(V2_7) + .summary("The URL to send the HTTP requests to.") + .description(SiriSXUpdaterConfig.URL_DESCRIPTION) + .asUri(), + c + .of("frequency") + .since(V2_0) + .summary("How often the updates should be retrieved.") + .asDuration(Duration.ofMinutes(1)), + c + .of("timeout") + .since(V2_7) + .summary("The HTTP timeout to download the updates.") + .asDuration(Duration.ofSeconds(15)), + c + .of("fuzzyTripMatching") + .since(V2_7) + .summary("If the fuzzy trip matcher should be used to match trips.") + .asBoolean(false), + HttpHeadersConfig.headers(c, V2_3) + ); + } +} diff --git a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java index 312fa3e2fbc..9db4de7f7b0 100644 --- a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java @@ -5,6 +5,7 @@ import org.opentripplanner.ext.siri.updater.azure.SiriAzureSXUpdaterParameters; import org.opentripplanner.ext.vehiclerentalservicedirectory.api.VehicleRentalServiceDirectoryFetcherParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdaterParameters; +import org.opentripplanner.updater.siri.updater.SiriETLightUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriETUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; @@ -33,6 +34,8 @@ public interface UpdatersParameters { List getSiriSXUpdaterParameters(); + List getSiriETLightUpdaterParameters(); + List getMqttGtfsRealtimeUpdaterParameters(); List getVehicleParkingUpdaterParameters(); diff --git a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 1106d621873..a9a092a33ab 100644 --- a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -20,6 +20,8 @@ import org.opentripplanner.updater.UpdatersParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdater; import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; +import org.opentripplanner.updater.siri.updater.SiriETHttpTripUpdateSource; +import org.opentripplanner.updater.siri.updater.SiriETLightHttpTripUpdateSource; import org.opentripplanner.updater.siri.updater.SiriETUpdater; import org.opentripplanner.updater.siri.updater.SiriSXUpdater; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdater; @@ -182,7 +184,22 @@ private List createUpdatersFromConfig() { updaters.add(new PollingVehiclePositionUpdater(configItem, realtimeVehicleRepository)); } for (var configItem : updatersParameters.getSiriETUpdaterParameters()) { - updaters.add(new SiriETUpdater(configItem, provideSiriTimetableSnapshot())); + updaters.add( + new SiriETUpdater( + configItem, + provideSiriTimetableSnapshot(), + new SiriETHttpTripUpdateSource(configItem.sourceParameters()) + ) + ); + } + for (var configItem : updatersParameters.getSiriETLightUpdaterParameters()) { + updaters.add( + new SiriETUpdater( + configItem, + provideSiriTimetableSnapshot(), + new SiriETLightHttpTripUpdateSource(configItem.sourceParameters()) + ) + ); } for (var configItem : updatersParameters.getSiriETGooglePubsubUpdaterParameters()) { updaters.add(new SiriETGooglePubsubUpdater(configItem, provideSiriTimetableSnapshot())); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightHttpTripUpdateSource.java new file mode 100644 index 00000000000..b16742ddeec --- /dev/null +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightHttpTripUpdateSource.java @@ -0,0 +1,89 @@ +package org.opentripplanner.updater.siri.updater; + +import static org.opentripplanner.updater.trip.UpdateIncrementality.FULL_DATASET; + +import java.net.URI; +import java.time.Duration; +import java.util.Optional; +import org.opentripplanner.framework.io.OtpHttpClientException; +import org.opentripplanner.updater.spi.HttpHeaders; +import org.opentripplanner.updater.trip.UpdateIncrementality; +import org.opentripplanner.utils.tostring.ToStringBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import uk.org.siri.siri20.Siri; + +public class SiriETLightHttpTripUpdateSource implements EstimatedTimetableSource { + + private static final Logger LOG = LoggerFactory.getLogger(SiriETLightHttpTripUpdateSource.class); + private static final String DUMMY_REQUESTOR_REF = "OTP"; + + private final Parameters parameters; + + private final SiriLoader siriLoader; + + public SiriETLightHttpTripUpdateSource(Parameters parameters) { + this.parameters = parameters; + + this.siriLoader = createLoader(parameters); + } + + @Override + public Optional getUpdates() { + try { + var siri = siriLoader.fetchETFeed(DUMMY_REQUESTOR_REF); + if (siri.map(Siri::getServiceDelivery).isEmpty()) { + return Optional.empty(); + } + return siri; + } catch (OtpHttpClientException e) { + LOG.warn("Could not get SIRI-ET data from {}", parameters.uri(), e); + } catch (Exception e) { + LOG.warn("Failed to parse SIRI-ET feed from {}", parameters.uri(), e); + } + return Optional.empty(); + } + + @Override + public UpdateIncrementality incrementalityOfLastUpdates() { + return FULL_DATASET; + } + + @Override + public String getFeedId() { + return this.parameters.feedId(); + } + + public String toString() { + return ToStringBuilder + .of(this.getClass()) + .addStr("feedId", parameters.feedId()) + .addStr("uri", parameters.toString()) + .toString(); + } + + private static SiriLoader createLoader(Parameters parameters) { + // Load real-time updates from a file. + if (SiriFileLoader.matchesUrl(parameters.uri().toString())) { + return new SiriFileLoader(parameters.uri().toString()); + } + // Fallback to default loader + else { + return new SiriLightHttpLoader( + parameters.uri(), + parameters.timeout(), + parameters.httpRequestHeaders() + ); + } + } + + public interface Parameters { + URI uri(); + + String feedId(); + + Duration timeout(); + + HttpHeaders httpRequestHeaders(); + } +} diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightUpdaterParameters.java new file mode 100644 index 00000000000..f49212aab74 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightUpdaterParameters.java @@ -0,0 +1,30 @@ +package org.opentripplanner.updater.siri.updater; + +import java.net.URI; +import java.time.Duration; +import org.opentripplanner.updater.spi.HttpHeaders; + +public record SiriETLightUpdaterParameters( + String configRef, + String feedId, + URI uri, + Duration frequency, + Duration timeout, + boolean fuzzyTripMatching, + HttpHeaders httpRequestHeaders +) + implements SiriETUpdater.SiriUpdaterParameters, SiriETLightHttpTripUpdateSource.Parameters { + public SiriETLightHttpTripUpdateSource.Parameters sourceParameters() { + return this; + } + + @Override + public String url() { + return uri.toString(); + } + + @Override + public boolean blockReadinessUntilInitialized() { + return false; + } +} diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index 087bf28e875..2025d394026 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -4,9 +4,11 @@ import java.util.function.Consumer; import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; import org.opentripplanner.updater.spi.PollingGraphUpdater; +import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; import org.opentripplanner.updater.spi.ResultLogger; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.WriteToGraphCallback; +import org.opentripplanner.updater.trip.UrlUpdaterParameters; import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,14 +40,15 @@ public class SiriETUpdater extends PollingGraphUpdater { private final Consumer recordMetrics; public SiriETUpdater( - SiriETUpdaterParameters config, - SiriTimetableSnapshotSource timetableSnapshotSource + SiriUpdaterParameters config, + SiriTimetableSnapshotSource timetableSnapshotSource, + EstimatedTimetableSource source ) { super(config); // Create update streamer from preferences this.feedId = config.feedId(); - this.updateSource = new SiriETHttpTripUpdateSource(config.sourceParameters()); + this.updateSource = source; this.blockReadinessUntilInitialized = config.blockReadinessUntilInitialized(); @@ -101,4 +104,9 @@ public String toString() { String s = (updateSource == null) ? "NONE" : updateSource.toString(); return "Polling SIRI ET updater with update source = " + s; } + + interface SiriUpdaterParameters extends UrlUpdaterParameters, PollingGraphUpdaterParameters { + boolean blockReadinessUntilInitialized(); + boolean fuzzyTripMatching(); + } } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java index dc479c034e1..ced2d90d456 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java @@ -2,8 +2,6 @@ import java.time.Duration; import org.opentripplanner.updater.spi.HttpHeaders; -import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; -import org.opentripplanner.updater.trip.UrlUpdaterParameters; public record SiriETUpdaterParameters( String configRef, @@ -18,8 +16,7 @@ public record SiriETUpdaterParameters( HttpHeaders httpRequestHeaders, boolean producerMetrics ) - implements - PollingGraphUpdaterParameters, UrlUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { + implements SiriETUpdater.SiriUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { public SiriETHttpTripUpdateSource.Parameters sourceParameters() { return this; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java new file mode 100644 index 00000000000..ca8a38dbcb0 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java @@ -0,0 +1,55 @@ +package org.opentripplanner.updater.siri.updater; + +import java.net.URI; +import java.time.Duration; +import java.util.Optional; +import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; +import org.opentripplanner.updater.spi.HttpHeaders; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import uk.org.siri.siri20.Siri; + +/** + * Load real-time updates from SIRI-SX and SIRI-ET feeds over HTTP. + */ +public class SiriLightHttpLoader implements SiriLoader { + + private static final Logger LOG = LoggerFactory.getLogger(SiriHttpLoader.class); + private final HttpHeaders headers; + private final URI uri; + private final Duration timeout; + private final OtpHttpClient otpHttpClient; + + public SiriLightHttpLoader(URI uri, Duration timeout, HttpHeaders headers) { + this.uri = uri; + this.timeout = timeout; + this.headers = HttpHeaders.of().acceptApplicationXML().add(headers).build(); + this.otpHttpClient = new OtpHttpClientFactory(timeout, timeout).create(LOG); + } + + /** + * Send a HTTP GET request and unmarshal the response as JAXB. + */ + @Override + public Optional fetchSXFeed(String ignored) { + return fetchFeed(); + } + + /** + * Send a HTTP GET service request and unmarshal the response as JAXB. + */ + @Override + public Optional fetchETFeed(String ignored) { + return fetchFeed(); + } + + private Optional fetchFeed() { + return otpHttpClient.getAndMap( + uri, + timeout, + headers.asMap(), + is -> Optional.of(SiriHelper.unmarshal(is)) + ); + } +} From 1b824ac72b9bc9b886fb14dd65364be02bc195ea Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 26 Nov 2024 15:42:09 +0100 Subject: [PATCH 086/269] Update logger name --- .../updater/siri/updater/SiriLightHttpLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java index ca8a38dbcb0..f3e86d24e5b 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java @@ -15,7 +15,7 @@ */ public class SiriLightHttpLoader implements SiriLoader { - private static final Logger LOG = LoggerFactory.getLogger(SiriHttpLoader.class); + private static final Logger LOG = LoggerFactory.getLogger(SiriLightHttpLoader.class); private final HttpHeaders headers; private final URI uri; private final Duration timeout; From 9eed5be0e3d205663dc585e8696ed2c51b6b8028 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 26 Nov 2024 18:05:50 +0100 Subject: [PATCH 087/269] Improve documentation --- .../config/routerconfig/UpdatersConfig.java | 2 +- .../updaters/SiriETLightUpdaterConfig.java | 8 +- .../updater/UpdatersParameters.java | 2 +- .../configure/UpdaterConfigurator.java | 2 +- .../updater/siri/updater/SiriETUpdater.java | 7 -- .../siri/updater/SiriETUpdaterParameters.java | 2 +- .../siri/updater/SiriUpdaterParameters.java | 10 ++ .../SiriETLightHttpTripUpdateSource.java | 5 +- .../SiriETLightUpdaterParameters.java | 5 +- .../{ => light}/SiriLightHttpLoader.java | 7 +- .../generate/doc/SiriConfigDocTest.java | 6 +- .../generate/doc/UpdaterConfigDocTest.java | 3 +- .../standalone/config/router-config.json | 6 ++ doc/templates/sandbox/siri/SiriUpdater.md | 42 ++++---- doc/user/RouterConfiguration.md | 6 ++ doc/user/sandbox/siri/SiriUpdater.md | 98 +++++++++++++++---- 16 files changed, 154 insertions(+), 57 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/updater/siri/updater/SiriUpdaterParameters.java rename application/src/main/java/org/opentripplanner/updater/siri/updater/{ => light}/SiriETLightHttpTripUpdateSource.java (90%) rename application/src/main/java/org/opentripplanner/updater/siri/updater/{ => light}/SiriETLightUpdaterParameters.java (72%) rename application/src/main/java/org/opentripplanner/updater/siri/updater/{ => light}/SiriLightHttpLoader.java (86%) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index a3092a35b55..27868cc182c 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -43,10 +43,10 @@ import org.opentripplanner.updater.TimetableSnapshotSourceParameters; import org.opentripplanner.updater.UpdatersParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdaterParameters; -import org.opentripplanner.updater.siri.updater.SiriETLightUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriETUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriETLightUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java index c6152e7131f..3fa40a5f030 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java @@ -1,12 +1,10 @@ package org.opentripplanner.standalone.config.routerconfig.updaters; -import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_0; -import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_3; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; import java.time.Duration; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; -import org.opentripplanner.updater.siri.updater.SiriETLightUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriETLightUpdaterParameters; public class SiriETLightUpdaterConfig { @@ -22,7 +20,7 @@ public static SiriETLightUpdaterParameters create(String configRef, NodeAdapter .asUri(), c .of("frequency") - .since(V2_0) + .since(V2_7) .summary("How often the updates should be retrieved.") .asDuration(Duration.ofMinutes(1)), c @@ -35,7 +33,7 @@ public static SiriETLightUpdaterParameters create(String configRef, NodeAdapter .since(V2_7) .summary("If the fuzzy trip matcher should be used to match trips.") .asBoolean(false), - HttpHeadersConfig.headers(c, V2_3) + HttpHeadersConfig.headers(c, V2_7) ); } } diff --git a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java index 9db4de7f7b0..e98c730df47 100644 --- a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java @@ -5,10 +5,10 @@ import org.opentripplanner.ext.siri.updater.azure.SiriAzureSXUpdaterParameters; import org.opentripplanner.ext.vehiclerentalservicedirectory.api.VehicleRentalServiceDirectoryFetcherParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdaterParameters; -import org.opentripplanner.updater.siri.updater.SiriETLightUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriETUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriETLightUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; diff --git a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index a9a092a33ab..2c7aa32a7b3 100644 --- a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -21,10 +21,10 @@ import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdater; import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; import org.opentripplanner.updater.siri.updater.SiriETHttpTripUpdateSource; -import org.opentripplanner.updater.siri.updater.SiriETLightHttpTripUpdateSource; import org.opentripplanner.updater.siri.updater.SiriETUpdater; import org.opentripplanner.updater.siri.updater.SiriSXUpdater; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdater; +import org.opentripplanner.updater.siri.updater.light.SiriETLightHttpTripUpdateSource; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.TimetableSnapshotFlush; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdater; diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index 2025d394026..66dc8d9be09 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -4,11 +4,9 @@ import java.util.function.Consumer; import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; import org.opentripplanner.updater.spi.PollingGraphUpdater; -import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; import org.opentripplanner.updater.spi.ResultLogger; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.WriteToGraphCallback; -import org.opentripplanner.updater.trip.UrlUpdaterParameters; import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -104,9 +102,4 @@ public String toString() { String s = (updateSource == null) ? "NONE" : updateSource.toString(); return "Polling SIRI ET updater with update source = " + s; } - - interface SiriUpdaterParameters extends UrlUpdaterParameters, PollingGraphUpdaterParameters { - boolean blockReadinessUntilInitialized(); - boolean fuzzyTripMatching(); - } } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java index ced2d90d456..395e7930f17 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java @@ -16,7 +16,7 @@ public record SiriETUpdaterParameters( HttpHeaders httpRequestHeaders, boolean producerMetrics ) - implements SiriETUpdater.SiriUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { + implements SiriUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { public SiriETHttpTripUpdateSource.Parameters sourceParameters() { return this; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriUpdaterParameters.java new file mode 100644 index 00000000000..0d9b8ea79bb --- /dev/null +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriUpdaterParameters.java @@ -0,0 +1,10 @@ +package org.opentripplanner.updater.siri.updater; + +import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; +import org.opentripplanner.updater.trip.UrlUpdaterParameters; + +public interface SiriUpdaterParameters extends UrlUpdaterParameters, PollingGraphUpdaterParameters { + boolean blockReadinessUntilInitialized(); + + boolean fuzzyTripMatching(); +} diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java similarity index 90% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightHttpTripUpdateSource.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java index b16742ddeec..ad072cb56c1 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java @@ -1,4 +1,4 @@ -package org.opentripplanner.updater.siri.updater; +package org.opentripplanner.updater.siri.updater.light; import static org.opentripplanner.updater.trip.UpdateIncrementality.FULL_DATASET; @@ -6,6 +6,9 @@ import java.time.Duration; import java.util.Optional; import org.opentripplanner.framework.io.OtpHttpClientException; +import org.opentripplanner.updater.siri.updater.EstimatedTimetableSource; +import org.opentripplanner.updater.siri.updater.SiriFileLoader; +import org.opentripplanner.updater.siri.updater.SiriLoader; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.trip.UpdateIncrementality; import org.opentripplanner.utils.tostring.ToStringBuilder; diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java similarity index 72% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightUpdaterParameters.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java index f49212aab74..26fe959bc8c 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETLightUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java @@ -1,7 +1,8 @@ -package org.opentripplanner.updater.siri.updater; +package org.opentripplanner.updater.siri.updater.light; import java.net.URI; import java.time.Duration; +import org.opentripplanner.updater.siri.updater.SiriUpdaterParameters; import org.opentripplanner.updater.spi.HttpHeaders; public record SiriETLightUpdaterParameters( @@ -13,7 +14,7 @@ public record SiriETLightUpdaterParameters( boolean fuzzyTripMatching, HttpHeaders httpRequestHeaders ) - implements SiriETUpdater.SiriUpdaterParameters, SiriETLightHttpTripUpdateSource.Parameters { + implements SiriUpdaterParameters, SiriETLightHttpTripUpdateSource.Parameters { public SiriETLightHttpTripUpdateSource.Parameters sourceParameters() { return this; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoader.java similarity index 86% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoader.java index f3e86d24e5b..45be81d74e2 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLightHttpLoader.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoader.java @@ -1,17 +1,20 @@ -package org.opentripplanner.updater.siri.updater; +package org.opentripplanner.updater.siri.updater.light; import java.net.URI; import java.time.Duration; import java.util.Optional; import org.opentripplanner.framework.io.OtpHttpClient; import org.opentripplanner.framework.io.OtpHttpClientFactory; +import org.opentripplanner.updater.siri.updater.SiriHelper; +import org.opentripplanner.updater.siri.updater.SiriLoader; import org.opentripplanner.updater.spi.HttpHeaders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.Siri; /** - * Load real-time updates from SIRI-SX and SIRI-ET feeds over HTTP. + * Load real-time updates from SIRI-SX and SIRI-ET feeds over HTTP via a single request + * that contains all updates. */ public class SiriLightHttpLoader implements SiriLoader { diff --git a/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java b/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java index b53c5370b53..17dac32c82c 100644 --- a/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java +++ b/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java @@ -29,7 +29,11 @@ public class SiriConfigDocTest { private static final File OUT_FILE = new File(USER_DOC_PATH, "sandbox/siri/SiriUpdater.md"); private static final String ROUTER_CONFIG_PATH = "standalone/config/" + ROUTER_CONFIG_FILENAME; - private static final Set INCLUDE_UPDATERS = Set.of("siri-et-updater", "siri-sx-updater"); + private static final Set INCLUDE_UPDATERS = Set.of( + "siri-et-updater", + "siri-et-light", + "siri-sx-updater" + ); private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); public static final ObjectMapper mapper = new ObjectMapper(); diff --git a/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java b/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java index 234d4c68b33..989eee18cf5 100644 --- a/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java +++ b/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java @@ -35,7 +35,8 @@ public class UpdaterConfigDocTest { "vehicle-parking", "siri-et-updater", "siri-et-google-pubsub-updater", - "siri-sx-updater" + "siri-sx-updater", + "siri-et-light" ); private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); public static final ObjectMapper mapper = new ObjectMapper(); diff --git a/application/src/test/resources/standalone/config/router-config.json b/application/src/test/resources/standalone/config/router-config.json index 4b6cf30f45d..8b5fde4bd00 100644 --- a/application/src/test/resources/standalone/config/router-config.json +++ b/application/src/test/resources/standalone/config/router-config.json @@ -431,6 +431,12 @@ "feedId": "parking", "sourceType": "siri-fm", "url": "https://transmodel.api.opendatahub.com/siri-lite/fm/parking" + }, + { + "type": "siri-et-light", + "feedId": "sta", + "url": "https://example.com/siri-lite/estimated-timetable/xml", + "fuzzyTripMatching": true } ], "rideHailingServices": [ diff --git a/doc/templates/sandbox/siri/SiriUpdater.md b/doc/templates/sandbox/siri/SiriUpdater.md index f695d304475..f1f77620444 100644 --- a/doc/templates/sandbox/siri/SiriUpdater.md +++ b/doc/templates/sandbox/siri/SiriUpdater.md @@ -1,35 +1,43 @@ -# Siri Updater +# SIRI Updater -Support for consuming SIRI ET and SX messages. The updater is developed to support the Nordic -SIRI profile which is a subset of the SIRI specification. +Support for consuming SIRI ET and SX messages via HTTPS. The updaters aim to support the [Nordic +and EPIP SIRI profiles](../../features-explained/Netex-Siri-Compatibility.md) which +are subsets of the SIRI specification. + +For more documentation about the Norwegian profile and data, go to +the [Entur Real-Time Data](https://developer.entur.org/pages-real-time-intro) documentation and +the [Norwegian SIRI profile](https://enturas.atlassian.net/wiki/spaces/PUBLIC/pages/637370420/Norwegian+SIRI+profile). ## Contact Info - Lasse Tyrihjell, Entur, Norway - -## Documentation - -This updater consumes SIRI real time information. It is developed by Entur and supports the Nordic -Profile for SIRI. It should be possible to develop it further to support a broader set of the SIRI -specification. - -For more documentation goto -the [Entur Real-Time Data](https://developer.entur.org/pages-real-time-intro) documentation and -the [Norwegian SIRI profile](https://enturas.atlassian.net/wiki/spaces/PUBLIC/pages/637370420/Norwegian+SIRI+profile) -. +- Leonard Ehrenfried, Germany ## Configuration -To enable the SIRI updater you need to add it to the updaters section of the `router-config.json`. +To enable the SIRI updater you need to add it to the `updaters` section of the `router-config.json`. -### Siri-ET via HTTPS +### SIRI-ET Request/Response via HTTPS -### Siri-SX via HTTPS +### SIRI-SX Request/Response via HTTPS +### SIRI-ET Light via HTTPS + +SIRI Light is [not very well](https://nextcloud.leonard.io/s/2tdYdmYBGtLQMfi/download?path=&files=Proposition-Profil-SIRI-Lite-initial-v1-3%20en.pdf) +[specified](https://normes.transport.data.gouv.fr/normes/siri/profil-france/#protocoles-d%C3%A9change-des-donn%C3%A9es-siri), +but this updater supports the following definition: + +Fetching XML-formatted SIRI messages as single GET request rather than the more common request/response +flow. This means that the XML feed must contain all updates for all trips, just like it is the case +for GTFS-RT TripUpdates. + + + + ## Changelog - Initial version of SIRI updater (October 2019) diff --git a/doc/user/RouterConfiguration.md b/doc/user/RouterConfiguration.md index 6dbd1174397..6bf00fb6629 100644 --- a/doc/user/RouterConfiguration.md +++ b/doc/user/RouterConfiguration.md @@ -875,6 +875,12 @@ Used to group requests when monitoring OTP. "feedId" : "parking", "sourceType" : "siri-fm", "url" : "https://transmodel.api.opendatahub.com/siri-lite/fm/parking" + }, + { + "type" : "siri-et-light", + "feedId" : "sta", + "url" : "https://example.com/siri-lite/estimated-timetable/xml", + "fuzzyTripMatching" : true } ], "rideHailingServices" : [ diff --git a/doc/user/sandbox/siri/SiriUpdater.md b/doc/user/sandbox/siri/SiriUpdater.md index 28f2f9a85db..db8465fa998 100644 --- a/doc/user/sandbox/siri/SiriUpdater.md +++ b/doc/user/sandbox/siri/SiriUpdater.md @@ -1,28 +1,23 @@ -# Siri Updater +# SIRI Updater -Support for consuming SIRI ET and SX messages. The updater is developed to support the Nordic -SIRI profile which is a subset of the SIRI specification. +Support for consuming SIRI ET and SX messages via HTTPS. The updaters aim to support the [Nordic +and EPIP SIRI profiles](../../features-explained/Netex-Siri-Compatibility.md) which +are subsets of the SIRI specification. + +For more documentation about the Norwegian profile and data, go to +the [Entur Real-Time Data](https://developer.entur.org/pages-real-time-intro) documentation and +the [Norwegian SIRI profile](https://enturas.atlassian.net/wiki/spaces/PUBLIC/pages/637370420/Norwegian+SIRI+profile). ## Contact Info - Lasse Tyrihjell, Entur, Norway - -## Documentation - -This updater consumes SIRI real time information. It is developed by Entur and supports the Nordic -Profile for SIRI. It should be possible to develop it further to support a broader set of the SIRI -specification. - -For more documentation goto -the [Entur Real-Time Data](https://developer.entur.org/pages-real-time-intro) documentation and -the [Norwegian SIRI profile](https://enturas.atlassian.net/wiki/spaces/PUBLIC/pages/637370420/Norwegian+SIRI+profile) -. +- Leonard Ehrenfried, Germany ## Configuration -To enable the SIRI updater you need to add it to the updaters section of the `router-config.json`. +To enable the SIRI updater you need to add it to the `updaters` section of the `router-config.json`. -### Siri-ET via HTTPS +### SIRI-ET Request/Response via HTTPS @@ -89,7 +84,7 @@ HTTP headers to add to the request. Any header key, value can be inserted. -### Siri-SX via HTTPS +### SIRI-SX Request/Response via HTTPS @@ -166,6 +161,75 @@ HTTP headers to add to the request. Any header key, value can be inserted. +### SIRI-ET Light via HTTPS + +SIRI Light is [not very well](https://nextcloud.leonard.io/s/2tdYdmYBGtLQMfi/download?path=&files=Proposition-Profil-SIRI-Lite-initial-v1-3%20en.pdf) +[specified](https://normes.transport.data.gouv.fr/normes/siri/profil-france/#protocoles-d%C3%A9change-des-donn%C3%A9es-siri), +but this updater supports the following definition: + +Fetching XML-formatted SIRI messages as single GET request rather than the more common request/response +flow. This means that the XML feed must contain all updates for all trips, just like it is the case +for GTFS-RT TripUpdates. + + + + +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|----------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|---------------|:-----:| +| type = "siri-et-light" | `enum` | The type of the updater. | *Required* | | 1.5 | +| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.7 | +| frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.7 | +| fuzzyTripMatching | `boolean` | If the fuzzy trip matcher should be used to match trips. | *Optional* | `false` | 2.7 | +| timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.7 | +| [url](#u__15__url) | `uri` | The URL to send the HTTP requests to. | *Required* | | 2.7 | +| [headers](#u__15__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.7 | + + +##### Parameter details + +

url

+ +**Since version:** `2.7` ∙ **Type:** `uri` ∙ **Cardinality:** `Required` +**Path:** /updaters/[15] + +The URL to send the HTTP requests to. + +Use the file protocol to set a directory for reading updates from a directory. The file +loader will look for xml files: '*.xml' in the configured directory. The files are +renamed by the loader when processed: + +    _a.xml_   ➞   _a.xml.inProgress_   ➞   _a.xml.ok_   or   _a.xml.failed_ + + + +

headers

+ +**Since version:** `2.7` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` +**Path:** /updaters/[15] + +HTTP headers to add to the request. Any header key, value can be inserted. + + + +##### Example configuration + +```JSON +// router-config.json +{ + "updaters" : [ + { + "type" : "siri-et-light", + "feedId" : "sta", + "url" : "https://example.com/siri-lite/estimated-timetable/xml", + "fuzzyTripMatching" : true + } + ] +} +``` + + + + ## Changelog - Initial version of SIRI updater (October 2019) From 96b8f1eb49f5964b6bed53085b1d42c3c54d0cfd Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 26 Nov 2024 23:34:27 +0100 Subject: [PATCH 088/269] Update docs --- doc/templates/sandbox/siri/SiriUpdater.md | 2 +- doc/user/sandbox/siri/SiriUpdater.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/templates/sandbox/siri/SiriUpdater.md b/doc/templates/sandbox/siri/SiriUpdater.md index f1f77620444..8a949f63ce5 100644 --- a/doc/templates/sandbox/siri/SiriUpdater.md +++ b/doc/templates/sandbox/siri/SiriUpdater.md @@ -1,4 +1,4 @@ -# SIRI Updater +# SIRI Updaters Support for consuming SIRI ET and SX messages via HTTPS. The updaters aim to support the [Nordic and EPIP SIRI profiles](../../features-explained/Netex-Siri-Compatibility.md) which diff --git a/doc/user/sandbox/siri/SiriUpdater.md b/doc/user/sandbox/siri/SiriUpdater.md index db8465fa998..0aeef704cdb 100644 --- a/doc/user/sandbox/siri/SiriUpdater.md +++ b/doc/user/sandbox/siri/SiriUpdater.md @@ -1,4 +1,4 @@ -# SIRI Updater +# SIRI Updaters Support for consuming SIRI ET and SX messages via HTTPS. The updaters aim to support the [Nordic and EPIP SIRI profiles](../../features-explained/Netex-Siri-Compatibility.md) which From 8fdebbb105c14770d9ea88dd44fda02553932f8b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Nov 2024 08:18:05 +0100 Subject: [PATCH 089/269] Make spelling of SIRI consistent across docs --- doc/templates/StopConsolidation.md | 2 +- doc/templates/UpdaterConfig.md | 6 +++--- doc/templates/sandbox/siri/SiriAzureUpdater.md | 10 +++++----- doc/templates/sandbox/siri/SiriGooglePubSubUpdater.md | 4 ++-- doc/user/Changelog.md | 4 ++-- doc/user/UpdaterConfig.md | 6 +++--- doc/user/examples/skanetrafiken/Readme.md | 4 ++-- doc/user/sandbox/StopConsolidation.md | 2 +- doc/user/sandbox/siri/SiriAzureUpdater.md | 10 +++++----- doc/user/sandbox/siri/SiriGooglePubSubUpdater.md | 4 ++-- 10 files changed, 26 insertions(+), 26 deletions(-) diff --git a/doc/templates/StopConsolidation.md b/doc/templates/StopConsolidation.md index 70866882bd1..ef32a3c29d6 100644 --- a/doc/templates/StopConsolidation.md +++ b/doc/templates/StopConsolidation.md @@ -32,7 +32,7 @@ This has the following consequences - It makes real-time trip updates referencing a stop id much more complicated and in many cases impossible to resolve. - You can only reference a stop by its sequence, which only works in GTFS-RT, not Siri. + You can only reference a stop by its sequence, which only works in GTFS-RT, not SIRI. - Fare calculation and transfers are unlikely to work as expected. diff --git a/doc/templates/UpdaterConfig.md b/doc/templates/UpdaterConfig.md index aab5631e6e2..21db7bdb6a4 100644 --- a/doc/templates/UpdaterConfig.md +++ b/doc/templates/UpdaterConfig.md @@ -82,8 +82,8 @@ GBFS form factors: ## Other updaters in sandboxes - [Vehicle parking](sandbox/VehicleParking.md) -- [Siri over HTTP](sandbox/siri/SiriUpdater.md) -- [Siri over Google Cloud PubSub](sandbox/siri/SiriGooglePubSubUpdater.md) -- [Siri over Azure Message Bus](sandbox/siri/SiriAzureUpdater.md) +- [SIRI over HTTP](sandbox/siri/SiriUpdater.md) +- [SIRI over Google Cloud PubSub](sandbox/siri/SiriGooglePubSubUpdater.md) +- [SIRI over Azure Message Bus](sandbox/siri/SiriAzureUpdater.md) - [VehicleRentalServiceDirectory](sandbox/VehicleRentalServiceDirectory.md) diff --git a/doc/templates/sandbox/siri/SiriAzureUpdater.md b/doc/templates/sandbox/siri/SiriAzureUpdater.md index 85e22e30bda..646484c9aec 100644 --- a/doc/templates/sandbox/siri/SiriAzureUpdater.md +++ b/doc/templates/sandbox/siri/SiriAzureUpdater.md @@ -1,6 +1,6 @@ -# Siri Azure Updater +# SIRI Azure Updater -This is a sandbox extension developed by Skånetrafiken that allows OTP to fetch Siri ET & SX messages +This is a sandbox extension developed by Skånetrafiken that allows OTP to fetch SIRI ET & SX messages through *Azure Service Bus*. It also enables OTP to download historical data from en HTTP endpoint on startup. @@ -17,11 +17,11 @@ Documentation available [here](../../examples/skanetrafiken/Readme.md). To enable the SIRI updater you need to add it to the updaters section of the `router-config.json`. -### Siri Azure ET Updater +### SIRI Azure ET Updater -### Siri Azure SX Updater +### SIRI Azure SX Updater @@ -30,5 +30,5 @@ To enable the SIRI updater you need to add it to the updaters section of the `ro - Initial version (April 2022) - Minor changes in logging (November 2022) - Retry fetch from history endpoint if it failed (February 2023) -- Solve a bug in SiriAzureETUpdater and improve error logging (March 2023) +- Solve a bug in SIRIAzureETUpdater and improve error logging (March 2023) - Add support with federated identity authentication (February 2024) \ No newline at end of file diff --git a/doc/templates/sandbox/siri/SiriGooglePubSubUpdater.md b/doc/templates/sandbox/siri/SiriGooglePubSubUpdater.md index 09fd3996bef..9d3fff5e00d 100644 --- a/doc/templates/sandbox/siri/SiriGooglePubSubUpdater.md +++ b/doc/templates/sandbox/siri/SiriGooglePubSubUpdater.md @@ -1,4 +1,4 @@ -# Siri-ET Google PubSub Updater +# SIRI-ET Google PubSub Updater Support for consuming SIRI-ET messages over a Google Cloud PubSub subscription. Similarly to the SIRI-ET HTTP updater, this updater is developed to support the Nordic SIRI profile @@ -23,7 +23,7 @@ the [Norwegian SIRI profile](https://enturas.atlassian.net/wiki/spaces/PUBLIC/pa To enable the SIRI-ET Google PubSub updater you need to add it to the updaters section of the `router-config.json`. -### Siri-ET via Google PubSub +### SIRI-ET via Google PubSub diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index 17a54ede4ae..35cf127b114 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -399,7 +399,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Preserve language in SIRI/GTFS-RT alert messages [#4117](https://github.com/opentripplanner/OpenTripPlanner/pull/4117) - Use board/alight cost only for transits [#4079](https://github.com/opentripplanner/OpenTripPlanner/pull/4079) - Improve SIRI real-time performance by reducing stopPattern duplicates [#4038](https://github.com/opentripplanner/OpenTripPlanner/pull/4038) -- Siri updaters for Azure ServiceBus [#4106](https://github.com/opentripplanner/OpenTripPlanner/pull/4106) +- SIRI updaters for Azure ServiceBus [#4106](https://github.com/opentripplanner/OpenTripPlanner/pull/4106) - Fallback to recorded/expected arrival/departure time if other one is missing in SIRI-ET [#4055](https://github.com/opentripplanner/OpenTripPlanner/pull/4055) - Allow overriding GBFS system_id with configuration [#4147](https://github.com/opentripplanner/OpenTripPlanner/pull/4147) - Fix error with transfer-slack and GTFS minTransferTime [#4120](https://github.com/opentripplanner/OpenTripPlanner/pull/4120) @@ -407,7 +407,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Don't indicate stop has been updated when NO_DATA is defined [#3962](https://github.com/opentripplanner/OpenTripPlanner/pull/3962) - Implement nearby searches for car and bicycle parking [#4165](https://github.com/opentripplanner/OpenTripPlanner/pull/4165) - Do not link cars to stop vertices in routing [#4166](https://github.com/opentripplanner/OpenTripPlanner/pull/4166) -- Add Siri real-time occupancy info [#4180](https://github.com/opentripplanner/OpenTripPlanner/pull/4180) +- Add SIRI real-time occupancy info [#4180](https://github.com/opentripplanner/OpenTripPlanner/pull/4180) - Add gtfs stop description translations [#4158](https://github.com/opentripplanner/OpenTripPlanner/pull/4158) - Add option to discard min transfer times [#4195](https://github.com/opentripplanner/OpenTripPlanner/pull/4195) - Use negative delay from first stop in a GTFS RT update in previous stop times when required [#4035](https://github.com/opentripplanner/OpenTripPlanner/pull/4035) diff --git a/doc/user/UpdaterConfig.md b/doc/user/UpdaterConfig.md index f61cce12f54..068b61f112e 100644 --- a/doc/user/UpdaterConfig.md +++ b/doc/user/UpdaterConfig.md @@ -427,8 +427,8 @@ This is temporary and will be removed in a future version of OTP. Use this to sp ## Other updaters in sandboxes - [Vehicle parking](sandbox/VehicleParking.md) -- [Siri over HTTP](sandbox/siri/SiriUpdater.md) -- [Siri over Google Cloud PubSub](sandbox/siri/SiriGooglePubSubUpdater.md) -- [Siri over Azure Message Bus](sandbox/siri/SiriAzureUpdater.md) +- [SIRI over HTTP](sandbox/siri/SiriUpdater.md) +- [SIRI over Google Cloud PubSub](sandbox/siri/SiriGooglePubSubUpdater.md) +- [SIRI over Azure Message Bus](sandbox/siri/SiriAzureUpdater.md) - [VehicleRentalServiceDirectory](sandbox/VehicleRentalServiceDirectory.md) diff --git a/doc/user/examples/skanetrafiken/Readme.md b/doc/user/examples/skanetrafiken/Readme.md index a611c839140..acaf212719a 100644 --- a/doc/user/examples/skanetrafiken/Readme.md +++ b/doc/user/examples/skanetrafiken/Readme.md @@ -38,7 +38,7 @@ To reduce graph size, only data for northern part of Denmark is used. ## Real-time The **Azure Service Bus** is used to propagate SIRI SX and ET real-time messages to OTP. -This is solved through Siri Azure updaters that Skånetrafiken had implemented in OTP. There are +This is solved through SIRI Azure updaters that Skånetrafiken had implemented in OTP. There are separate updaters for SIRI SX and ET. Those updaters are used to provide data for Swedish traffic (NeTEx). Right now, there is no connection to any real-time source for danish traffic (GTFS data). @@ -77,7 +77,7 @@ Those two parameters are used to define time boundaries for the messages. Both endpoints generate XML response which is an SIRI object containing SX or ET messages. Messages are -formatted according to Siri Nordic Profile. +formatted according to SIRI Nordic Profile. Since in SIRI ET standard each messages contains all necessary data, Skånetrafikens implementation of the endpoint returns only the last message diff --git a/doc/user/sandbox/StopConsolidation.md b/doc/user/sandbox/StopConsolidation.md index d0e18a9ce30..547f3200296 100644 --- a/doc/user/sandbox/StopConsolidation.md +++ b/doc/user/sandbox/StopConsolidation.md @@ -32,7 +32,7 @@ This has the following consequences - It makes real-time trip updates referencing a stop id much more complicated and in many cases impossible to resolve. - You can only reference a stop by its sequence, which only works in GTFS-RT, not Siri. + You can only reference a stop by its sequence, which only works in GTFS-RT, not SIRI. - Fare calculation and transfers are unlikely to work as expected. diff --git a/doc/user/sandbox/siri/SiriAzureUpdater.md b/doc/user/sandbox/siri/SiriAzureUpdater.md index c8e7f4d9255..5dabd8e6c20 100644 --- a/doc/user/sandbox/siri/SiriAzureUpdater.md +++ b/doc/user/sandbox/siri/SiriAzureUpdater.md @@ -1,6 +1,6 @@ -# Siri Azure Updater +# SIRI Azure Updater -This is a sandbox extension developed by Skånetrafiken that allows OTP to fetch Siri ET & SX messages +This is a sandbox extension developed by Skånetrafiken that allows OTP to fetch SIRI ET & SX messages through *Azure Service Bus*. It also enables OTP to download historical data from en HTTP endpoint on startup. @@ -17,7 +17,7 @@ Documentation available [here](../../examples/skanetrafiken/Readme.md). To enable the SIRI updater you need to add it to the updaters section of the `router-config.json`. -### Siri Azure ET Updater +### SIRI Azure ET Updater @@ -105,7 +105,7 @@ Has to be present for authenticationMethod SharedAccessKey. This should be Prima -### Siri Azure SX Updater +### SIRI Azure SX Updater @@ -198,5 +198,5 @@ Has to be present for authenticationMethod SharedAccessKey. This should be Prima - Initial version (April 2022) - Minor changes in logging (November 2022) - Retry fetch from history endpoint if it failed (February 2023) -- Solve a bug in SiriAzureETUpdater and improve error logging (March 2023) +- Solve a bug in SIRIAzureETUpdater and improve error logging (March 2023) - Add support with federated identity authentication (February 2024) \ No newline at end of file diff --git a/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md b/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md index 5232696ad9b..ed09522b224 100644 --- a/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md +++ b/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md @@ -1,4 +1,4 @@ -# Siri-ET Google PubSub Updater +# SIRI-ET Google PubSub Updater Support for consuming SIRI-ET messages over a Google Cloud PubSub subscription. Similarly to the SIRI-ET HTTP updater, this updater is developed to support the Nordic SIRI profile @@ -23,7 +23,7 @@ the [Norwegian SIRI profile](https://enturas.atlassian.net/wiki/spaces/PUBLIC/pa To enable the SIRI-ET Google PubSub updater you need to add it to the updaters section of the `router-config.json`. -### Siri-ET via Google PubSub +### SIRI-ET via Google PubSub From af138ab72a4b96f7b0f96d188564428d83d953f6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Nov 2024 08:18:14 +0100 Subject: [PATCH 090/269] Add metrics to SIRI light --- .../updater/configure/UpdaterConfigurator.java | 7 +++++-- .../updater/siri/updater/SiriETUpdater.java | 13 ++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 2c7aa32a7b3..544e5a0c4f5 100644 --- a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -30,6 +30,7 @@ import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdater; import org.opentripplanner.updater.trip.PollingTripUpdater; import org.opentripplanner.updater.trip.TimetableSnapshotSource; +import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics; import org.opentripplanner.updater.vehicle_parking.AvailabilityDatasourceFactory; import org.opentripplanner.updater.vehicle_parking.VehicleParkingAvailabilityUpdater; import org.opentripplanner.updater.vehicle_parking.VehicleParkingDataSourceFactory; @@ -188,7 +189,8 @@ private List createUpdatersFromConfig() { new SiriETUpdater( configItem, provideSiriTimetableSnapshot(), - new SiriETHttpTripUpdateSource(configItem.sourceParameters()) + new SiriETHttpTripUpdateSource(configItem.sourceParameters()), + TripUpdateMetrics.streaming(configItem) ) ); } @@ -197,7 +199,8 @@ private List createUpdatersFromConfig() { new SiriETUpdater( configItem, provideSiriTimetableSnapshot(), - new SiriETLightHttpTripUpdateSource(configItem.sourceParameters()) + new SiriETLightHttpTripUpdateSource(configItem.sourceParameters()), + TripUpdateMetrics.batch(configItem) ) ); } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index 66dc8d9be09..b11c26471c2 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -7,7 +7,6 @@ import org.opentripplanner.updater.spi.ResultLogger; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.WriteToGraphCallback; -import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.EstimatedTimetableDeliveryStructure; @@ -35,15 +34,15 @@ public class SiriETUpdater extends PollingGraphUpdater { private final EstimatedTimetableHandler estimatedTimetableHandler; - private final Consumer recordMetrics; + private final Consumer metricsConsumer; public SiriETUpdater( SiriUpdaterParameters config, SiriTimetableSnapshotSource timetableSnapshotSource, - EstimatedTimetableSource source + EstimatedTimetableSource source, + Consumer metricsConsumer ) { super(config); - // Create update streamer from preferences this.feedId = config.feedId(); this.updateSource = source; @@ -51,7 +50,7 @@ public SiriETUpdater( this.blockReadinessUntilInitialized = config.blockReadinessUntilInitialized(); LOG.info( - "Creating stop time updater (SIRI ET) running every {} seconds : {}", + "Creating SIRI ET updater running every {}: {}", pollingPeriod(), updateSource ); @@ -59,7 +58,7 @@ public SiriETUpdater( estimatedTimetableHandler = new EstimatedTimetableHandler(timetableSnapshotSource, config.fuzzyTripMatching(), feedId); - recordMetrics = TripUpdateMetrics.streaming(config); + this.metricsConsumer = metricsConsumer; } @Override @@ -88,7 +87,7 @@ public void runPolling() { saveResultOnGraph.execute(context -> { var result = estimatedTimetableHandler.applyUpdate(etds, incrementality, context); ResultLogger.logUpdateResult(feedId, "siri-et", result); - recordMetrics.accept(result); + metricsConsumer.accept(result); if (markPrimed) { primed = true; } From 240c70efec84a3fc9239b80c02e4699ff60d743b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Nov 2024 09:35:35 +0100 Subject: [PATCH 091/269] Add SIRI SX --- .../config/routerconfig/UpdatersConfig.java | 11 ++++- .../updaters/SiriSXLightUpdaterConfig.java | 46 +++++++++++++++++++ .../updater/UpdatersParameters.java | 3 ++ .../configure/UpdaterConfigurator.java | 23 +++++++++- ....java => BaseSiriETUpdaterParameters.java} | 3 +- .../updater/siri/updater/SiriETUpdater.java | 8 +--- .../siri/updater/SiriETUpdaterParameters.java | 2 +- .../updater/siri/updater/SiriHttpLoader.java | 3 +- .../updater/siri/updater/SiriSXUpdater.java | 21 +++++++-- .../siri/updater/SiriSXUpdaterParameters.java | 3 +- .../SiriETLightHttpTripUpdateSource.java | 2 +- .../light/SiriETLightUpdaterParameters.java | 4 +- .../light/SiriSXLightUpdaterParameters.java | 32 +++++++++++++ doc/user/sandbox/siri/SiriUpdater.md | 29 ++++++++---- 14 files changed, 161 insertions(+), 29 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLightUpdaterConfig.java rename application/src/main/java/org/opentripplanner/updater/siri/updater/{SiriUpdaterParameters.java => BaseSiriETUpdaterParameters.java} (70%) create mode 100644 application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index 27868cc182c..98611bea96e 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -31,8 +31,8 @@ import org.opentripplanner.standalone.config.routerconfig.updaters.MqttGtfsRealtimeUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.PollingTripUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETGooglePubsubUpdaterConfig; -import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETLightUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETUpdaterConfig; +import org.opentripplanner.standalone.config.routerconfig.updaters.SiriSXLightUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriSXUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.VehicleParkingUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig; @@ -47,6 +47,7 @@ import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; import org.opentripplanner.updater.siri.updater.light.SiriETLightUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriSXLightUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; @@ -190,6 +191,11 @@ public List getSiriETLightUpdaterParameters() { return getParameters(SIRI_ET_LIGHT); } + @Override + public List getSiriSXLightUpdaterParameters() { + return getParameters(Type.SIRI_SX_LIGHT); + } + @Override public List getMqttGtfsRealtimeUpdaterParameters() { return getParameters(MQTT_GTFS_RT_UPDATER); @@ -226,7 +232,8 @@ public enum Type { REAL_TIME_ALERTS(GtfsRealtimeAlertsUpdaterConfig::create), VEHICLE_POSITIONS(VehiclePositionsUpdaterConfig::create), SIRI_ET_UPDATER(SiriETUpdaterConfig::create), - SIRI_ET_LIGHT(SiriETLightUpdaterConfig::create), + SIRI_ET_LIGHT(SiriSXLightUpdaterConfig::create), + SIRI_SX_LIGHT(SiriSXLightUpdaterConfig::create), SIRI_ET_GOOGLE_PUBSUB_UPDATER(SiriETGooglePubsubUpdaterConfig::create), SIRI_SX_UPDATER(SiriSXUpdaterConfig::create), SIRI_AZURE_ET_UPDATER(SiriAzureETUpdaterConfig::create), diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLightUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLightUpdaterConfig.java new file mode 100644 index 00000000000..39db86aead5 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLightUpdaterConfig.java @@ -0,0 +1,46 @@ +package org.opentripplanner.standalone.config.routerconfig.updaters; + +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_0; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; + +import java.time.Duration; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; +import org.opentripplanner.updater.siri.updater.light.SiriSXLightUpdaterParameters; + +public class SiriSXLightUpdaterConfig { + + public static SiriSXLightUpdaterParameters create(String configRef, NodeAdapter c) { + return new SiriSXLightUpdaterParameters( + configRef, + c.of("feedId").since(V2_7).summary("The ID of the feed to apply the updates to.").asString(), + c + .of("url") + .since(V2_7) + .summary("The URL to send the HTTP requests to.") + .description(SiriSXUpdaterConfig.URL_DESCRIPTION) + .asUri(), + c + .of("frequency") + .since(V2_7) + .summary("How often the updates should be retrieved.") + .asDuration(Duration.ofMinutes(1)), + c + .of("timeout") + .since(V2_7) + .summary("The HTTP timeout to download the updates.") + .asDuration(Duration.ofSeconds(15)), + c + .of("earlyStart") + .since(V2_0) + .summary("This value is subtracted from the actual validity defined in the message.") + .description( + """ + Normally the planned departure time is used, so setting this to 10s will cause the + SX-message to be included in trip-results 10 seconds before the the planned departure + time.""" + ) + .asDuration(Duration.ZERO), + HttpHeadersConfig.headers(c, V2_7) + ); + } +} diff --git a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java index e98c730df47..32a9500236a 100644 --- a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java @@ -9,6 +9,7 @@ import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; import org.opentripplanner.updater.siri.updater.light.SiriETLightUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriSXLightUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; @@ -36,6 +37,8 @@ public interface UpdatersParameters { List getSiriETLightUpdaterParameters(); + List getSiriSXLightUpdaterParameters(); + List getMqttGtfsRealtimeUpdaterParameters(); List getVehicleParkingUpdaterParameters(); diff --git a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 544e5a0c4f5..fe0c5af3e4c 100644 --- a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -22,9 +22,11 @@ import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; import org.opentripplanner.updater.siri.updater.SiriETHttpTripUpdateSource; import org.opentripplanner.updater.siri.updater.SiriETUpdater; +import org.opentripplanner.updater.siri.updater.SiriHttpLoader; import org.opentripplanner.updater.siri.updater.SiriSXUpdater; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdater; import org.opentripplanner.updater.siri.updater.light.SiriETLightHttpTripUpdateSource; +import org.opentripplanner.updater.siri.updater.light.SiriLightHttpLoader; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.TimetableSnapshotFlush; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdater; @@ -208,7 +210,26 @@ private List createUpdatersFromConfig() { updaters.add(new SiriETGooglePubsubUpdater(configItem, provideSiriTimetableSnapshot())); } for (var configItem : updatersParameters.getSiriSXUpdaterParameters()) { - updaters.add(new SiriSXUpdater(configItem, timetableRepository)); + updaters.add( + new SiriSXUpdater( + configItem, + timetableRepository, + new SiriHttpLoader(configItem.url(), configItem.timeout(), configItem.requestHeaders()) + ) + ); + } + for (var configItem : updatersParameters.getSiriSXLightUpdaterParameters()) { + updaters.add( + new SiriSXUpdater( + configItem, + timetableRepository, + new SiriLightHttpLoader( + configItem.uri(), + configItem.timeout(), + configItem.requestHeaders() + ) + ) + ); } for (var configItem : updatersParameters.getMqttGtfsRealtimeUpdaterParameters()) { updaters.add(new MqttGtfsRealtimeUpdater(configItem, provideGtfsTimetableSnapshot())); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/BaseSiriETUpdaterParameters.java similarity index 70% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/SiriUpdaterParameters.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/BaseSiriETUpdaterParameters.java index 0d9b8ea79bb..bad6e7583f0 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/BaseSiriETUpdaterParameters.java @@ -3,7 +3,8 @@ import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; import org.opentripplanner.updater.trip.UrlUpdaterParameters; -public interface SiriUpdaterParameters extends UrlUpdaterParameters, PollingGraphUpdaterParameters { +public interface BaseSiriETUpdaterParameters + extends UrlUpdaterParameters, PollingGraphUpdaterParameters { boolean blockReadinessUntilInitialized(); boolean fuzzyTripMatching(); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index b11c26471c2..916e563b0dd 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -37,7 +37,7 @@ public class SiriETUpdater extends PollingGraphUpdater { private final Consumer metricsConsumer; public SiriETUpdater( - SiriUpdaterParameters config, + BaseSiriETUpdaterParameters config, SiriTimetableSnapshotSource timetableSnapshotSource, EstimatedTimetableSource source, Consumer metricsConsumer @@ -49,11 +49,7 @@ public SiriETUpdater( this.blockReadinessUntilInitialized = config.blockReadinessUntilInitialized(); - LOG.info( - "Creating SIRI ET updater running every {}: {}", - pollingPeriod(), - updateSource - ); + LOG.info("Creating SIRI ET updater running every {}: {}", pollingPeriod(), updateSource); estimatedTimetableHandler = new EstimatedTimetableHandler(timetableSnapshotSource, config.fuzzyTripMatching(), feedId); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java index 395e7930f17..b04fc3a4116 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java @@ -16,7 +16,7 @@ public record SiriETUpdaterParameters( HttpHeaders httpRequestHeaders, boolean producerMetrics ) - implements SiriUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { + implements BaseSiriETUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { public SiriETHttpTripUpdateSource.Parameters sourceParameters() { return this; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriHttpLoader.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriHttpLoader.java index 0ad9309e354..29de28efaee 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriHttpLoader.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriHttpLoader.java @@ -11,7 +11,8 @@ import uk.org.siri.siri20.Siri; /** - * Load real-time updates from SIRI-SX and SIRI-ET feeds over HTTP. + * Load real-time updates from SIRI-SX and SIRI-ET feeds over HTTP using the request/response + * flow, which asks the server to only send the latest updates for a given requestor ref. */ public class SiriHttpLoader implements SiriLoader { diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java index 83200db30d3..b8b6e36ecf7 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java @@ -13,7 +13,9 @@ import org.opentripplanner.updater.alert.TransitAlertProvider; import org.opentripplanner.updater.siri.SiriAlertsUpdateHandler; import org.opentripplanner.updater.spi.PollingGraphUpdater; +import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; import org.opentripplanner.updater.spi.WriteToGraphCallback; +import org.opentripplanner.updater.trip.UrlUpdaterParameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.ServiceDelivery; @@ -40,10 +42,14 @@ public class SiriSXUpdater extends PollingGraphUpdater implements TransitAlertPr * Global retry counter used to create a new unique requestorRef after each retry. */ private int retryCount = 0; - private final SiriHttpLoader siriHttpLoader; + private final SiriLoader siriHttpLoader; private final OtpRetry retry; - public SiriSXUpdater(SiriSXUpdaterParameters config, TimetableRepository timetableRepository) { + public SiriSXUpdater( + BaseSiriSXUpdaterParameters config, + TimetableRepository timetableRepository, + SiriLoader siriLoader + ) { super(config); // TODO: add options to choose different patch services this.url = config.url(); @@ -59,7 +65,7 @@ public SiriSXUpdater(SiriSXUpdaterParameters config, TimetableRepository timetab this.transitAlertService = new TransitAlertServiceImpl(timetableRepository); this.updateHandler = new SiriAlertsUpdateHandler(config.feedId(), transitAlertService, config.earlyStart()); - siriHttpLoader = new SiriHttpLoader(url, config.timeout(), config.requestHeaders()); + siriHttpLoader = siriLoader; retry = new OtpRetryBuilder() @@ -200,4 +206,13 @@ private void updateRequestorRef() { retryCount++; requestorRef = originalRequestorRef + "-retry-" + retryCount; } + + public interface BaseSiriSXUpdaterParameters + extends PollingGraphUpdaterParameters, UrlUpdaterParameters { + String requestorRef(); + + boolean blockReadinessUntilInitialized(); + + Duration earlyStart(); + } } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java index f51584f86f5..c17ee21fffc 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java @@ -2,7 +2,6 @@ import java.time.Duration; import org.opentripplanner.updater.spi.HttpHeaders; -import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; public record SiriSXUpdaterParameters( String configRef, @@ -15,4 +14,4 @@ public record SiriSXUpdaterParameters( boolean blockReadinessUntilInitialized, HttpHeaders requestHeaders ) - implements PollingGraphUpdaterParameters {} + implements SiriSXUpdater.BaseSiriSXUpdaterParameters {} diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java index ad072cb56c1..5a30ac1c5df 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java @@ -61,7 +61,7 @@ public String toString() { return ToStringBuilder .of(this.getClass()) .addStr("feedId", parameters.feedId()) - .addStr("uri", parameters.toString()) + .addStr("uri", parameters.uri().toString()) .toString(); } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java index 26fe959bc8c..07db1ed6f77 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java @@ -2,7 +2,7 @@ import java.net.URI; import java.time.Duration; -import org.opentripplanner.updater.siri.updater.SiriUpdaterParameters; +import org.opentripplanner.updater.siri.updater.BaseSiriETUpdaterParameters; import org.opentripplanner.updater.spi.HttpHeaders; public record SiriETLightUpdaterParameters( @@ -14,7 +14,7 @@ public record SiriETLightUpdaterParameters( boolean fuzzyTripMatching, HttpHeaders httpRequestHeaders ) - implements SiriUpdaterParameters, SiriETLightHttpTripUpdateSource.Parameters { + implements BaseSiriETUpdaterParameters, SiriETLightHttpTripUpdateSource.Parameters { public SiriETLightHttpTripUpdateSource.Parameters sourceParameters() { return this; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java new file mode 100644 index 00000000000..553dbd99744 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java @@ -0,0 +1,32 @@ +package org.opentripplanner.updater.siri.updater.light; + +import java.net.URI; +import java.time.Duration; +import org.opentripplanner.updater.siri.updater.SiriSXUpdater; +import org.opentripplanner.updater.spi.HttpHeaders; + +public record SiriSXLightUpdaterParameters( + String configRef, + String feedId, + URI uri, + Duration frequency, + Duration earlyStart, + Duration timeout, + HttpHeaders requestHeaders +) + implements SiriSXUpdater.BaseSiriSXUpdaterParameters { + @Override + public String requestorRef() { + return "OpenTripPlanner"; + } + + @Override + public boolean blockReadinessUntilInitialized() { + return false; + } + + @Override + public String url() { + return uri.toString(); + } +} diff --git a/doc/user/sandbox/siri/SiriUpdater.md b/doc/user/sandbox/siri/SiriUpdater.md index 0aeef704cdb..eedd4feb45e 100644 --- a/doc/user/sandbox/siri/SiriUpdater.md +++ b/doc/user/sandbox/siri/SiriUpdater.md @@ -174,19 +174,30 @@ for GTFS-RT TripUpdates. -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|----------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|---------------|:-----:| -| type = "siri-et-light" | `enum` | The type of the updater. | *Required* | | 1.5 | -| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.7 | -| frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.7 | -| fuzzyTripMatching | `boolean` | If the fuzzy trip matcher should be used to match trips. | *Optional* | `false` | 2.7 | -| timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.7 | -| [url](#u__15__url) | `uri` | The URL to send the HTTP requests to. | *Required* | | 2.7 | -| [headers](#u__15__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.7 | +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|----------------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|---------------|:-----:| +| type = "siri-et-light" | `enum` | The type of the updater. | *Required* | | 1.5 | +| [earlyStart](#u__15__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | +| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.7 | +| frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.7 | +| timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.7 | +| [url](#u__15__url) | `uri` | The URL to send the HTTP requests to. | *Required* | | 2.7 | +| [headers](#u__15__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.7 | ##### Parameter details +

earlyStart

+ +**Since version:** `2.0` ∙ **Type:** `duration` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"PT0S"` +**Path:** /updaters/[15] + +This value is subtracted from the actual validity defined in the message. + +Normally the planned departure time is used, so setting this to 10s will cause the +SX-message to be included in trip-results 10 seconds before the the planned departure +time. +

url

**Since version:** `2.7` ∙ **Type:** `uri` ∙ **Cardinality:** `Required` From 24bcf97e3331923c29a2958b1365102148ec0e2a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Nov 2024 11:15:15 +0100 Subject: [PATCH 092/269] Add documentation about SIRI SX --- .../updater/siri/updater/SiriSXUpdater.java | 2 +- .../generate/doc/SiriConfigDocTest.java | 3 +- .../standalone/config/router-config.json | 5 ++ doc/templates/sandbox/siri/SiriUpdater.md | 8 ++- .../Netex-Siri-Compatibility.md | 4 +- doc/user/sandbox/siri/SiriUpdater.md | 72 ++++++++++++++++++- 6 files changed, 88 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java index b8b6e36ecf7..827a8fb1ff6 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java @@ -78,7 +78,7 @@ public SiriSXUpdater( .build(); LOG.info( - "Creating real-time alert updater (SIRI SX) running every {} seconds : {}", + "Creating SIRI-SX updater running every {}: {}", pollingPeriod(), url ); diff --git a/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java b/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java index 17dac32c82c..4119704ba2b 100644 --- a/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java +++ b/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java @@ -31,8 +31,9 @@ public class SiriConfigDocTest { private static final String ROUTER_CONFIG_PATH = "standalone/config/" + ROUTER_CONFIG_FILENAME; private static final Set INCLUDE_UPDATERS = Set.of( "siri-et-updater", + "siri-sx-updater", "siri-et-light", - "siri-sx-updater" + "siri-sx-light" ); private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); public static final ObjectMapper mapper = new ObjectMapper(); diff --git a/application/src/test/resources/standalone/config/router-config.json b/application/src/test/resources/standalone/config/router-config.json index 8b5fde4bd00..6c27f3e06dd 100644 --- a/application/src/test/resources/standalone/config/router-config.json +++ b/application/src/test/resources/standalone/config/router-config.json @@ -437,6 +437,11 @@ "feedId": "sta", "url": "https://example.com/siri-lite/estimated-timetable/xml", "fuzzyTripMatching": true + }, + { + "type": "siri-sx-light", + "feedId": "sta", + "url": "https://example.com/siri-lite/situation-exchange/xml" } ], "rideHailingServices": [ diff --git a/doc/templates/sandbox/siri/SiriUpdater.md b/doc/templates/sandbox/siri/SiriUpdater.md index 8a949f63ce5..a9ef179c7c6 100644 --- a/doc/templates/sandbox/siri/SiriUpdater.md +++ b/doc/templates/sandbox/siri/SiriUpdater.md @@ -25,7 +25,7 @@ To enable the SIRI updater you need to add it to the `updaters` section of the ` -### SIRI-ET Light via HTTPS +### SIRI-ET Light SIRI Light is [not very well](https://nextcloud.leonard.io/s/2tdYdmYBGtLQMfi/download?path=&files=Proposition-Profil-SIRI-Lite-initial-v1-3%20en.pdf) [specified](https://normes.transport.data.gouv.fr/normes/siri/profil-france/#protocoles-d%C3%A9change-des-donn%C3%A9es-siri), @@ -37,6 +37,12 @@ for GTFS-RT TripUpdates. +### SIRI-SX Light + +This updater follows the same definition of SIRI light + + + ## Changelog diff --git a/doc/user/features-explained/Netex-Siri-Compatibility.md b/doc/user/features-explained/Netex-Siri-Compatibility.md index 731011be8e0..10c64f6a5c0 100644 --- a/doc/user/features-explained/Netex-Siri-Compatibility.md +++ b/doc/user/features-explained/Netex-Siri-Compatibility.md @@ -12,7 +12,7 @@ Different countries are currently using different incompatible "profiles" of NeT underway to converge on a single European standard profile. This is based in large part on the Nordic profile used by Entur. -The Nordic profile is the only profile that has been thoroughly tested in production in OTP and is +The Nordic profile is the only one that has been thoroughly tested in production in OTP and is used in Norway, Finland and Sweden. ### EPIP @@ -22,7 +22,7 @@ is an attempt to unify other country profiles and support in OTP is adequate, bu to tell how much of EPIP is supported since it is a very large profile. The current status of the support is tracked on [Github](https://github.com/opentripplanner/OpenTripPlanner/issues/3640). -Sometimes it is difficult to tell if a file conforms to EPIP so to find out, you can run the following +Sometimes it is not easy to figure out if a file conforms to EPIP so to find out, you can run the following commands: ``` diff --git a/doc/user/sandbox/siri/SiriUpdater.md b/doc/user/sandbox/siri/SiriUpdater.md index eedd4feb45e..0ce0af57b55 100644 --- a/doc/user/sandbox/siri/SiriUpdater.md +++ b/doc/user/sandbox/siri/SiriUpdater.md @@ -161,7 +161,7 @@ HTTP headers to add to the request. Any header key, value can be inserted. -### SIRI-ET Light via HTTPS +### SIRI-ET Light SIRI Light is [not very well](https://nextcloud.leonard.io/s/2tdYdmYBGtLQMfi/download?path=&files=Proposition-Profil-SIRI-Lite-initial-v1-3%20en.pdf) [specified](https://normes.transport.data.gouv.fr/normes/siri/profil-france/#protocoles-d%C3%A9change-des-donn%C3%A9es-siri), @@ -240,6 +240,76 @@ HTTP headers to add to the request. Any header key, value can be inserted. +### SIRI-SX Light + + + + +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|----------------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|---------------|:-----:| +| type = "siri-sx-light" | `enum` | The type of the updater. | *Required* | | 1.5 | +| [earlyStart](#u__16__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | +| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.7 | +| frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.7 | +| timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.7 | +| [url](#u__16__url) | `uri` | The URL to send the HTTP requests to. | *Required* | | 2.7 | +| [headers](#u__16__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.7 | + + +##### Parameter details + +

earlyStart

+ +**Since version:** `2.0` ∙ **Type:** `duration` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"PT0S"` +**Path:** /updaters/[16] + +This value is subtracted from the actual validity defined in the message. + +Normally the planned departure time is used, so setting this to 10s will cause the +SX-message to be included in trip-results 10 seconds before the the planned departure +time. + +

url

+ +**Since version:** `2.7` ∙ **Type:** `uri` ∙ **Cardinality:** `Required` +**Path:** /updaters/[16] + +The URL to send the HTTP requests to. + +Use the file protocol to set a directory for reading updates from a directory. The file +loader will look for xml files: '*.xml' in the configured directory. The files are +renamed by the loader when processed: + +    _a.xml_   ➞   _a.xml.inProgress_   ➞   _a.xml.ok_   or   _a.xml.failed_ + + + +

headers

+ +**Since version:** `2.7` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` +**Path:** /updaters/[16] + +HTTP headers to add to the request. Any header key, value can be inserted. + + + +##### Example configuration + +```JSON +// router-config.json +{ + "updaters" : [ + { + "type" : "siri-sx-light", + "feedId" : "sta", + "url" : "https://example.com/siri-lite/situation-exchange/xml" + } + ] +} +``` + + + ## Changelog From 464ca30284f3f41dc3645be752c5c3de4ca4abc0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Nov 2024 11:24:05 +0100 Subject: [PATCH 093/269] Move config interfaces into updaters --- .../siri/updater/BaseSiriETUpdaterParameters.java | 11 ----------- .../updater/siri/updater/SiriETUpdater.java | 11 ++++++++++- .../updater/siri/updater/SiriETUpdaterParameters.java | 2 +- .../updater/siri/updater/SiriSXUpdater.java | 4 ++-- .../updater/siri/updater/SiriSXUpdaterParameters.java | 2 +- .../light/SiriETLightHttpTripUpdateSource.java | 8 +++++++- .../updater/light/SiriETLightUpdaterParameters.java | 4 ++-- .../updater/light/SiriSXLightUpdaterParameters.java | 2 +- 8 files changed, 24 insertions(+), 20 deletions(-) delete mode 100644 application/src/main/java/org/opentripplanner/updater/siri/updater/BaseSiriETUpdaterParameters.java diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/BaseSiriETUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/BaseSiriETUpdaterParameters.java deleted file mode 100644 index bad6e7583f0..00000000000 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/BaseSiriETUpdaterParameters.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.opentripplanner.updater.siri.updater; - -import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; -import org.opentripplanner.updater.trip.UrlUpdaterParameters; - -public interface BaseSiriETUpdaterParameters - extends UrlUpdaterParameters, PollingGraphUpdaterParameters { - boolean blockReadinessUntilInitialized(); - - boolean fuzzyTripMatching(); -} diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index 916e563b0dd..7b4d64da324 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -4,9 +4,11 @@ import java.util.function.Consumer; import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; import org.opentripplanner.updater.spi.PollingGraphUpdater; +import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; import org.opentripplanner.updater.spi.ResultLogger; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.WriteToGraphCallback; +import org.opentripplanner.updater.trip.UrlUpdaterParameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.EstimatedTimetableDeliveryStructure; @@ -37,7 +39,7 @@ public class SiriETUpdater extends PollingGraphUpdater { private final Consumer metricsConsumer; public SiriETUpdater( - BaseSiriETUpdaterParameters config, + SiriETUpdaterParameters config, SiriTimetableSnapshotSource timetableSnapshotSource, EstimatedTimetableSource source, Consumer metricsConsumer @@ -97,4 +99,11 @@ public String toString() { String s = (updateSource == null) ? "NONE" : updateSource.toString(); return "Polling SIRI ET updater with update source = " + s; } + + public interface SiriETUpdaterParameters + extends UrlUpdaterParameters, PollingGraphUpdaterParameters { + boolean blockReadinessUntilInitialized(); + + boolean fuzzyTripMatching(); + } } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java index b04fc3a4116..a3477e3854e 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java @@ -16,7 +16,7 @@ public record SiriETUpdaterParameters( HttpHeaders httpRequestHeaders, boolean producerMetrics ) - implements BaseSiriETUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { + implements SiriETUpdater.SiriETUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { public SiriETHttpTripUpdateSource.Parameters sourceParameters() { return this; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java index 827a8fb1ff6..64ef9720426 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java @@ -46,7 +46,7 @@ public class SiriSXUpdater extends PollingGraphUpdater implements TransitAlertPr private final OtpRetry retry; public SiriSXUpdater( - BaseSiriSXUpdaterParameters config, + SiriSXUpdaterParameters config, TimetableRepository timetableRepository, SiriLoader siriLoader ) { @@ -207,7 +207,7 @@ private void updateRequestorRef() { requestorRef = originalRequestorRef + "-retry-" + retryCount; } - public interface BaseSiriSXUpdaterParameters + public interface SiriSXUpdaterParameters extends PollingGraphUpdaterParameters, UrlUpdaterParameters { String requestorRef(); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java index c17ee21fffc..539e1333fa8 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java @@ -14,4 +14,4 @@ public record SiriSXUpdaterParameters( boolean blockReadinessUntilInitialized, HttpHeaders requestHeaders ) - implements SiriSXUpdater.BaseSiriSXUpdaterParameters {} + implements SiriSXUpdater.SiriSXUpdaterParameters {} diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java index 5a30ac1c5df..896a5b6cdea 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java @@ -16,10 +16,16 @@ import org.slf4j.LoggerFactory; import uk.org.siri.siri20.Siri; +/** + * SIRI Light downloads periodically all messages as a single GET request. + */ public class SiriETLightHttpTripUpdateSource implements EstimatedTimetableSource { private static final Logger LOG = LoggerFactory.getLogger(SiriETLightHttpTripUpdateSource.class); - private static final String DUMMY_REQUESTOR_REF = "OTP"; + /** + * The framework code requires a requestor ref but in SIRI Light this is not used. + */ + private static final String DUMMY_REQUESTOR_REF = "OpenTripPlanner"; private final Parameters parameters; diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java index 07db1ed6f77..d4aa6fcaa37 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java @@ -2,7 +2,7 @@ import java.net.URI; import java.time.Duration; -import org.opentripplanner.updater.siri.updater.BaseSiriETUpdaterParameters; +import org.opentripplanner.updater.siri.updater.SiriETUpdater; import org.opentripplanner.updater.spi.HttpHeaders; public record SiriETLightUpdaterParameters( @@ -14,7 +14,7 @@ public record SiriETLightUpdaterParameters( boolean fuzzyTripMatching, HttpHeaders httpRequestHeaders ) - implements BaseSiriETUpdaterParameters, SiriETLightHttpTripUpdateSource.Parameters { + implements SiriETUpdater.SiriETUpdaterParameters, SiriETLightHttpTripUpdateSource.Parameters { public SiriETLightHttpTripUpdateSource.Parameters sourceParameters() { return this; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java index 553dbd99744..540fee7b919 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java @@ -14,7 +14,7 @@ public record SiriSXLightUpdaterParameters( Duration timeout, HttpHeaders requestHeaders ) - implements SiriSXUpdater.BaseSiriSXUpdaterParameters { + implements SiriSXUpdater.SiriSXUpdaterParameters { @Override public String requestorRef() { return "OpenTripPlanner"; From 01241535fb465675ac3da0299f86d92953e5d266 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Nov 2024 12:02:06 +0100 Subject: [PATCH 094/269] Small cleanup of the SIRI code --- .../config/routerconfig/UpdatersConfig.java | 3 ++- .../updater/EstimatedTimetableSource.java | 2 -- .../updater/SiriETHttpTripUpdateSource.java | 13 ++---------- .../updater/siri/updater/SiriETUpdater.java | 9 ++++++-- .../updater/siri/updater/SiriSXUpdater.java | 21 ++++++++++--------- .../SiriETLightHttpTripUpdateSource.java | 5 ----- .../sandbox/siri/SiriAzureUpdater.md | 2 +- 7 files changed, 23 insertions(+), 32 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index 98611bea96e..0e3658f0ca8 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -31,6 +31,7 @@ import org.opentripplanner.standalone.config.routerconfig.updaters.MqttGtfsRealtimeUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.PollingTripUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETGooglePubsubUpdaterConfig; +import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETLightUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriSXLightUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriSXUpdaterConfig; @@ -232,7 +233,7 @@ public enum Type { REAL_TIME_ALERTS(GtfsRealtimeAlertsUpdaterConfig::create), VEHICLE_POSITIONS(VehiclePositionsUpdaterConfig::create), SIRI_ET_UPDATER(SiriETUpdaterConfig::create), - SIRI_ET_LIGHT(SiriSXLightUpdaterConfig::create), + SIRI_ET_LIGHT(SiriETLightUpdaterConfig::create), SIRI_SX_LIGHT(SiriSXLightUpdaterConfig::create), SIRI_ET_GOOGLE_PUBSUB_UPDATER(SiriETGooglePubsubUpdaterConfig::create), SIRI_SX_UPDATER(SiriSXUpdaterConfig::create), diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/EstimatedTimetableSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/EstimatedTimetableSource.java index f8a1b549c96..3636fd2b96f 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/EstimatedTimetableSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/EstimatedTimetableSource.java @@ -23,6 +23,4 @@ public interface EstimatedTimetableSource { * {@link UpdateIncrementality} */ UpdateIncrementality incrementalityOfLastUpdates(); - - String getFeedId(); } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java index fec6e2885a7..c16ede42ac3 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java @@ -11,6 +11,7 @@ import org.opentripplanner.framework.io.OtpHttpClientException; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.trip.UpdateIncrementality; +import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.Siri; @@ -19,11 +20,6 @@ public class SiriETHttpTripUpdateSource implements EstimatedTimetableSource { private static final Logger LOG = LoggerFactory.getLogger(SiriETHttpTripUpdateSource.class); - /** - * Feed id that is used to match trip ids in the TripUpdates - */ - private final String feedId; - private final String url; private final SiriLoader siriLoader; @@ -36,7 +32,6 @@ public class SiriETHttpTripUpdateSource implements EstimatedTimetableSource { private ZonedDateTime lastTimestamp = ZonedDateTime.now().minusMonths(1); public SiriETHttpTripUpdateSource(Parameters parameters) { - this.feedId = parameters.feedId(); this.url = parameters.url(); this.requestorRef = @@ -82,12 +77,8 @@ public UpdateIncrementality incrementalityOfLastUpdates() { } @Override - public String getFeedId() { - return this.feedId; - } - public String toString() { - return "SiriETHttpTripUpdateSource(" + url + ")"; + return ToStringBuilder.of(this.getClass()).addStr("url", url).toString(); } private static SiriLoader createLoader(String url, Parameters parameters) { diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index 7b4d64da324..f1d3941cf46 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -9,6 +9,7 @@ import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.WriteToGraphCallback; import org.opentripplanner.updater.trip.UrlUpdaterParameters; +import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.EstimatedTimetableDeliveryStructure; @@ -95,9 +96,13 @@ public void runPolling() { } while (moreData); } + @Override public String toString() { - String s = (updateSource == null) ? "NONE" : updateSource.toString(); - return "Polling SIRI ET updater with update source = " + s; + return ToStringBuilder + .of(this.getClass()) + .addStr("source", updateSource.toString()) + .addDuration("frequency", pollingPeriod()) + .toString(); } public interface SiriETUpdaterParameters diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java index 64ef9720426..253338161fb 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java @@ -16,6 +16,7 @@ import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; import org.opentripplanner.updater.spi.WriteToGraphCallback; import org.opentripplanner.updater.trip.UrlUpdaterParameters; +import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.ServiceDelivery; @@ -51,7 +52,6 @@ public SiriSXUpdater( SiriLoader siriLoader ) { super(config); - // TODO: add options to choose different patch services this.url = config.url(); this.requestorRef = config.requestorRef(); @@ -77,11 +77,7 @@ public SiriSXUpdater( .withOnRetry(this::updateRequestorRef) .build(); - LOG.info( - "Creating SIRI-SX updater running every {}: {}", - pollingPeriod(), - url - ); + LOG.info("Creating SIRI-SX updater running every {}: {}", pollingPeriod(), url); } @Override @@ -93,15 +89,20 @@ public TransitAlertService getTransitAlertService() { return transitAlertService; } - public String toString() { - return "SiriSXUpdater (" + url + ")"; - } - @Override protected void runPolling() throws InterruptedException { retry.execute(this::updateSiri); } + @Override + public String toString() { + return ToStringBuilder + .of(this.getClass()) + .addStr("url", url) + .addDuration("frequency", pollingPeriod()) + .toString(); + } + /** * This part of the update process has been factored out to allow repeated retries of the HTTP * fetching operation in case the connection fails or some other disruption happens. diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java index 896a5b6cdea..f60ee8e1c35 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java @@ -58,11 +58,6 @@ public UpdateIncrementality incrementalityOfLastUpdates() { return FULL_DATASET; } - @Override - public String getFeedId() { - return this.parameters.feedId(); - } - public String toString() { return ToStringBuilder .of(this.getClass()) diff --git a/doc/templates/sandbox/siri/SiriAzureUpdater.md b/doc/templates/sandbox/siri/SiriAzureUpdater.md index 646484c9aec..5e9e1065519 100644 --- a/doc/templates/sandbox/siri/SiriAzureUpdater.md +++ b/doc/templates/sandbox/siri/SiriAzureUpdater.md @@ -30,5 +30,5 @@ To enable the SIRI updater you need to add it to the updaters section of the `ro - Initial version (April 2022) - Minor changes in logging (November 2022) - Retry fetch from history endpoint if it failed (February 2023) -- Solve a bug in SIRIAzureETUpdater and improve error logging (March 2023) +- Solve a bug in SiriAzureETUpdater and improve error logging (March 2023) - Add support with federated identity authentication (February 2024) \ No newline at end of file From 30e12f986f5f2a3318041f17770d631b80ddc9b3 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Nov 2024 13:56:05 +0100 Subject: [PATCH 095/269] Add test and example --- .../light/SiriLightHttpLoaderTest.java | 33 +++ .../updater/siri/updater/light/siri-sx.xml | 243 ++++++++++++++++++ doc/templates/sandbox/siri/SiriUpdater.md | 3 +- doc/user/RouterConfiguration.md | 5 + doc/user/sandbox/siri/SiriAzureUpdater.md | 2 +- doc/user/sandbox/siri/SiriUpdater.md | 32 +-- 6 files changed, 296 insertions(+), 22 deletions(-) create mode 100644 application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoaderTest.java create mode 100644 application/src/test/resources/org/opentripplanner/updater/siri/updater/light/siri-sx.xml diff --git a/application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoaderTest.java b/application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoaderTest.java new file mode 100644 index 00000000000..3886d90309a --- /dev/null +++ b/application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoaderTest.java @@ -0,0 +1,33 @@ +package org.opentripplanner.updater.siri.updater.light; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.Duration; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner.test.support.ResourceLoader; +import org.opentripplanner.updater.spi.HttpHeaders; +import uk.org.siri.siri20.NaturalLanguageStringStructure; + +class SiriLightHttpLoaderTest { + + private static final Duration ONE_MIN = Duration.ofMinutes(1); + + @Test + void test() { + var uri = ResourceLoader.of(this).uri("siri-sx.xml"); + var loader = new SiriLightHttpLoader(uri, ONE_MIN, HttpHeaders.empty()); + var siri = loader.fetchETFeed("OTP"); + var delivery = siri.get().getServiceDelivery().getSituationExchangeDeliveries().getFirst(); + var element = delivery.getSituations().getPtSituationElements().getFirst(); + assertEquals( + List.of( + "Hindernis auf Strecke", + "Obstacle on the route", + "Ostacolo sul percorso", + "Ostacul su la via" + ), + element.getReasonNames().stream().map(NaturalLanguageStringStructure::getValue).toList() + ); + } +} diff --git a/application/src/test/resources/org/opentripplanner/updater/siri/updater/light/siri-sx.xml b/application/src/test/resources/org/opentripplanner/updater/siri/updater/light/siri-sx.xml new file mode 100644 index 00000000000..be63b158bc6 --- /dev/null +++ b/application/src/test/resources/org/opentripplanner/updater/siri/updater/light/siri-sx.xml @@ -0,0 +1,243 @@ + + + + 2024-11-27T11:48:04.308988Z + DDIP-M + + 2024-11-27T11:48:04.308988Z + + + 2024-10-04T13:11:34.547+02:00 + STA + 2024-3443167 + 6 + + directReport + + published + + 2024-10-04T13:08:00+02:00 + 2024-11-30T13:08:00+01:00 + + + 2024-10-04T00:00:00+02:00 + 2024-11-30T23:59:00+01:00 + + routeBlockage + Hindernis auf Strecke + Obstacle on the route + Ostacolo sul percorso + Ostacul su la via + 3 + line + de + Straßen-/Streckenhindernis + The busroute: 120 obstacle on the track + 120 Linea 120 . Ostacolo su strada/pista + + + + + it:apb:Line:80120_.24a + 120 + + 28554 + Salorno, Autostazione + + + + + + + + + 2024-10-04T13:08:00+02:00 + 2024-11-30T13:08:00+01:00 + + tripCancellation + normal + + + + + it:apb:Line:80120_.24a + 120 + + 28554 + Salorno, Autostazione + + + + + + + + + + + + line + + + + + it:apb:Line:80120_.24a + 120 + + 28554 + Salorno, Autostazione + + + + + + + + + published + 2024-3443167 + 2024-10-04T13:11:34.547+02:00 + 6 + ControlCenter + STA + general + 3 + + L + + Straßen-/Streckenhindernis + + The busroute: 120 obstacle on the + track + + 120 Linea 120 . Ostacolo su + strada/pista + + + + Hindernis auf Strecke + Obstacle on the route + Ostacolo sul percorso + Ostacul su la via + + + Freitext + Free text + Testo libero + + + + Umsteigen auf andere + Linie + + Change to another line + + Cambiare linea + + + + Dauer der Beeinträchtigung bis + vsl. 2024-11-30 13:08:00 + + + + + + M + + Straßen-/Streckenhindernis + + The busroute: 120 obstacle on the + track + + 120 Linea 120 . Ostacolo su + strada/pista + + + + Hindernis auf Strecke + Obstacle on the route + Ostacolo sul percorso + Ostacul su la via + + + Freitext + Free text + Testo libero + + + + Umsteigen auf andere + Linie + + Change to another line + + Cambiare linea + + + + Dauer der Beeinträchtigung bis + vsl. 2024-11-30 13:08:00 + + + + + + S + + Straßen-/Streckenhindernis + + The busroute: 120 obstacle on the + track + + 120 Linea 120 . Ostacolo su + strada/pista + + + + Hindernis auf Strecke + Obstacle on the route + Ostacolo sul percorso + Ostacul su la via + + + Aufgrund Stand: 04.10.2024 - + 30.11.2024 ; 13:08 + + Due to Update: 04.10.2024 ; + 13:08 + + A causa di Aggiornamento: + 04.10.2024 ; 13:08 + + + + Freitext + Free text + Testo libero + + + + Umsteigen auf andere + Linie + + Change to another line + + Cambiare linea + + + + Dauer der Beeinträchtigung bis + vsl. 2024-11-30 13:08:00 + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/templates/sandbox/siri/SiriUpdater.md b/doc/templates/sandbox/siri/SiriUpdater.md index a9ef179c7c6..d1999e04f7d 100644 --- a/doc/templates/sandbox/siri/SiriUpdater.md +++ b/doc/templates/sandbox/siri/SiriUpdater.md @@ -39,7 +39,8 @@ for GTFS-RT TripUpdates. ### SIRI-SX Light -This updater follows the same definition of SIRI light +This updater follows the same definition of SIRI Light as the SIRI-ET one: it downloads the entire +feed in a single HTTP GET request. diff --git a/doc/user/RouterConfiguration.md b/doc/user/RouterConfiguration.md index 6bf00fb6629..d72cdab3d6f 100644 --- a/doc/user/RouterConfiguration.md +++ b/doc/user/RouterConfiguration.md @@ -881,6 +881,11 @@ Used to group requests when monitoring OTP. "feedId" : "sta", "url" : "https://example.com/siri-lite/estimated-timetable/xml", "fuzzyTripMatching" : true + }, + { + "type" : "siri-sx-light", + "feedId" : "sta", + "url" : "https://example.com/siri-lite/situation-exchange/xml" } ], "rideHailingServices" : [ diff --git a/doc/user/sandbox/siri/SiriAzureUpdater.md b/doc/user/sandbox/siri/SiriAzureUpdater.md index 5dabd8e6c20..76018ffbc7c 100644 --- a/doc/user/sandbox/siri/SiriAzureUpdater.md +++ b/doc/user/sandbox/siri/SiriAzureUpdater.md @@ -198,5 +198,5 @@ Has to be present for authenticationMethod SharedAccessKey. This should be Prima - Initial version (April 2022) - Minor changes in logging (November 2022) - Retry fetch from history endpoint if it failed (February 2023) -- Solve a bug in SIRIAzureETUpdater and improve error logging (March 2023) +- Solve a bug in SiriAzureETUpdater and improve error logging (March 2023) - Add support with federated identity authentication (February 2024) \ No newline at end of file diff --git a/doc/user/sandbox/siri/SiriUpdater.md b/doc/user/sandbox/siri/SiriUpdater.md index 0ce0af57b55..5cc2221289c 100644 --- a/doc/user/sandbox/siri/SiriUpdater.md +++ b/doc/user/sandbox/siri/SiriUpdater.md @@ -174,30 +174,19 @@ for GTFS-RT TripUpdates. -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|----------------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|---------------|:-----:| -| type = "siri-et-light" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [earlyStart](#u__15__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | -| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.7 | -| frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.7 | -| timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.7 | -| [url](#u__15__url) | `uri` | The URL to send the HTTP requests to. | *Required* | | 2.7 | -| [headers](#u__15__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.7 | +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|----------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|---------------|:-----:| +| type = "siri-et-light" | `enum` | The type of the updater. | *Required* | | 1.5 | +| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.7 | +| frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.7 | +| fuzzyTripMatching | `boolean` | If the fuzzy trip matcher should be used to match trips. | *Optional* | `false` | 2.7 | +| timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.7 | +| [url](#u__15__url) | `uri` | The URL to send the HTTP requests to. | *Required* | | 2.7 | +| [headers](#u__15__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.7 | ##### Parameter details -

earlyStart

- -**Since version:** `2.0` ∙ **Type:** `duration` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"PT0S"` -**Path:** /updaters/[15] - -This value is subtracted from the actual validity defined in the message. - -Normally the planned departure time is used, so setting this to 10s will cause the -SX-message to be included in trip-results 10 seconds before the the planned departure -time. -

url

**Since version:** `2.7` ∙ **Type:** `uri` ∙ **Cardinality:** `Required` @@ -242,6 +231,9 @@ HTTP headers to add to the request. Any header key, value can be inserted. ### SIRI-SX Light +This updater follows the same definition of SIRI Light as the SIRI-ET one: it downloads the entire +feed in a single HTTP GET request. + From 6703d620019153a9e1ab729e5774b6c4085d69fe Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Nov 2024 14:04:34 +0100 Subject: [PATCH 096/269] Skip updater in main docs --- .../org/opentripplanner/generate/doc/UpdaterConfigDocTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java b/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java index 989eee18cf5..29097941d01 100644 --- a/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java +++ b/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java @@ -36,7 +36,8 @@ public class UpdaterConfigDocTest { "siri-et-updater", "siri-et-google-pubsub-updater", "siri-sx-updater", - "siri-et-light" + "siri-et-light", + "siri-sx-light" ); private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); public static final ObjectMapper mapper = new ObjectMapper(); From 33189c22fd4fc74407f9c5b3f20d7425ed9ea580 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 26 Nov 2024 11:09:33 +0200 Subject: [PATCH 097/269] 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 098/269] 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 099/269] 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 100/269] 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 101/269] 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 102/269] 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 103/269] 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 104/269] 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 3c46be363a6912f6f221eba1aebf9a9ebbbf1a39 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 3 Dec 2024 08:10:56 +0200 Subject: [PATCH 105/269] 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 106/269] 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 244c5371bc8319981906afc8ce5cca97ebd57829 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Wed, 4 Dec 2024 10:42:54 +0200 Subject: [PATCH 107/269] 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 86a447fa6ef965e502e23e2384b36a8dedda5e7b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 10:18:32 +0100 Subject: [PATCH 108/269] Change spelling from 'Light' to 'Lite' --- .../config/routerconfig/UpdatersConfig.java | 23 +++++++++--------- ...nfig.java => SiriETLiteUpdaterConfig.java} | 8 +++---- ...nfig.java => SiriSXLiteUpdaterConfig.java} | 8 +++---- .../updater/UpdatersParameters.java | 8 +++---- .../configure/UpdaterConfigurator.java | 8 +++---- ...va => SiriETLiteHttpTripUpdateSource.java} | 8 +++---- ....java => SiriETLiteUpdaterParameters.java} | 6 ++--- ...ttpLoader.java => SiriLiteHttpLoader.java} | 6 ++--- ....java => SiriSXLiteUpdaterParameters.java} | 2 +- .../generate/doc/SiriConfigDocTest.java | 4 ++-- .../generate/doc/UpdaterConfigDocTest.java | 4 ++-- ...rTest.java => SiriLiteHttpLoaderTest.java} | 4 ++-- .../standalone/config/router-config.json | 4 ++-- doc/templates/sandbox/siri/SiriUpdater.md | 12 +++++----- doc/user/RouterConfiguration.md | 4 ++-- doc/user/sandbox/siri/SiriUpdater.md | 24 +++++++++---------- 16 files changed, 67 insertions(+), 66 deletions(-) rename application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/{SiriETLightUpdaterConfig.java => SiriETLiteUpdaterConfig.java} (81%) rename application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/{SiriSXLightUpdaterConfig.java => SiriSXLiteUpdaterConfig.java} (85%) rename application/src/main/java/org/opentripplanner/updater/siri/updater/light/{SiriETLightHttpTripUpdateSource.java => SiriETLiteHttpTripUpdateSource.java} (92%) rename application/src/main/java/org/opentripplanner/updater/siri/updater/light/{SiriETLightUpdaterParameters.java => SiriETLiteUpdaterParameters.java} (72%) rename application/src/main/java/org/opentripplanner/updater/siri/updater/light/{SiriLightHttpLoader.java => SiriLiteHttpLoader.java} (90%) rename application/src/main/java/org/opentripplanner/updater/siri/updater/light/{SiriSXLightUpdaterParameters.java => SiriSXLiteUpdaterParameters.java} (93%) rename application/src/test/java/org/opentripplanner/updater/siri/updater/light/{SiriLightHttpLoaderTest.java => SiriLiteHttpLoaderTest.java} (90%) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index 0e3658f0ca8..1ee1d8bbf09 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -8,8 +8,9 @@ import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_AZURE_ET_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_AZURE_SX_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_ET_GOOGLE_PUBSUB_UPDATER; -import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_ET_LIGHT; +import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_ET_LITE; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_ET_UPDATER; +import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_SX_LITE; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.SIRI_SX_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.STOP_TIME_UPDATER; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.VEHICLE_PARKING; @@ -31,9 +32,9 @@ import org.opentripplanner.standalone.config.routerconfig.updaters.MqttGtfsRealtimeUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.PollingTripUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETGooglePubsubUpdaterConfig; -import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETLightUpdaterConfig; +import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETLiteUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriETUpdaterConfig; -import org.opentripplanner.standalone.config.routerconfig.updaters.SiriSXLightUpdaterConfig; +import org.opentripplanner.standalone.config.routerconfig.updaters.SiriSXLiteUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.SiriSXUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.VehicleParkingUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig; @@ -47,8 +48,8 @@ import org.opentripplanner.updater.siri.updater.SiriETUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; -import org.opentripplanner.updater.siri.updater.light.SiriETLightUpdaterParameters; -import org.opentripplanner.updater.siri.updater.light.SiriSXLightUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriETLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriSXLiteUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; @@ -188,13 +189,13 @@ public List getSiriSXUpdaterParameters() { } @Override - public List getSiriETLightUpdaterParameters() { - return getParameters(SIRI_ET_LIGHT); + public List getSiriETLightUpdaterParameters() { + return getParameters(SIRI_ET_LITE); } @Override - public List getSiriSXLightUpdaterParameters() { - return getParameters(Type.SIRI_SX_LIGHT); + public List getSiriSXLightUpdaterParameters() { + return getParameters(SIRI_SX_LITE); } @Override @@ -233,10 +234,10 @@ public enum Type { REAL_TIME_ALERTS(GtfsRealtimeAlertsUpdaterConfig::create), VEHICLE_POSITIONS(VehiclePositionsUpdaterConfig::create), SIRI_ET_UPDATER(SiriETUpdaterConfig::create), - SIRI_ET_LIGHT(SiriETLightUpdaterConfig::create), - SIRI_SX_LIGHT(SiriSXLightUpdaterConfig::create), + SIRI_ET_LITE(SiriETLiteUpdaterConfig::create), SIRI_ET_GOOGLE_PUBSUB_UPDATER(SiriETGooglePubsubUpdaterConfig::create), SIRI_SX_UPDATER(SiriSXUpdaterConfig::create), + SIRI_SX_LITE(SiriSXLiteUpdaterConfig::create), SIRI_AZURE_ET_UPDATER(SiriAzureETUpdaterConfig::create), SIRI_AZURE_SX_UPDATER(SiriAzureSXUpdaterConfig::create); diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLiteUpdaterConfig.java similarity index 81% rename from application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java rename to application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLiteUpdaterConfig.java index 3fa40a5f030..0311cf0dd37 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLightUpdaterConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLiteUpdaterConfig.java @@ -4,12 +4,12 @@ import java.time.Duration; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; -import org.opentripplanner.updater.siri.updater.light.SiriETLightUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriETLiteUpdaterParameters; -public class SiriETLightUpdaterConfig { +public class SiriETLiteUpdaterConfig { - public static SiriETLightUpdaterParameters create(String configRef, NodeAdapter c) { - return new SiriETLightUpdaterParameters( + public static SiriETLiteUpdaterParameters create(String configRef, NodeAdapter c) { + return new SiriETLiteUpdaterParameters( configRef, c.of("feedId").since(V2_7).summary("The ID of the feed to apply the updates to.").asString(), c diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLightUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLiteUpdaterConfig.java similarity index 85% rename from application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLightUpdaterConfig.java rename to application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLiteUpdaterConfig.java index 39db86aead5..fbc9d1971c4 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLightUpdaterConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLiteUpdaterConfig.java @@ -5,12 +5,12 @@ import java.time.Duration; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; -import org.opentripplanner.updater.siri.updater.light.SiriSXLightUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriSXLiteUpdaterParameters; -public class SiriSXLightUpdaterConfig { +public class SiriSXLiteUpdaterConfig { - public static SiriSXLightUpdaterParameters create(String configRef, NodeAdapter c) { - return new SiriSXLightUpdaterParameters( + public static SiriSXLiteUpdaterParameters create(String configRef, NodeAdapter c) { + return new SiriSXLiteUpdaterParameters( configRef, c.of("feedId").since(V2_7).summary("The ID of the feed to apply the updates to.").asString(), c diff --git a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java index 32a9500236a..c960175ff6e 100644 --- a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java @@ -8,8 +8,8 @@ import org.opentripplanner.updater.siri.updater.SiriETUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; -import org.opentripplanner.updater.siri.updater.light.SiriETLightUpdaterParameters; -import org.opentripplanner.updater.siri.updater.light.SiriSXLightUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriETLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.light.SiriSXLiteUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; @@ -35,9 +35,9 @@ public interface UpdatersParameters { List getSiriSXUpdaterParameters(); - List getSiriETLightUpdaterParameters(); + List getSiriETLightUpdaterParameters(); - List getSiriSXLightUpdaterParameters(); + List getSiriSXLightUpdaterParameters(); List getMqttGtfsRealtimeUpdaterParameters(); diff --git a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index fe0c5af3e4c..c9323d4fc59 100644 --- a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -25,8 +25,8 @@ import org.opentripplanner.updater.siri.updater.SiriHttpLoader; import org.opentripplanner.updater.siri.updater.SiriSXUpdater; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdater; -import org.opentripplanner.updater.siri.updater.light.SiriETLightHttpTripUpdateSource; -import org.opentripplanner.updater.siri.updater.light.SiriLightHttpLoader; +import org.opentripplanner.updater.siri.updater.light.SiriETLiteHttpTripUpdateSource; +import org.opentripplanner.updater.siri.updater.light.SiriLiteHttpLoader; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.TimetableSnapshotFlush; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdater; @@ -201,7 +201,7 @@ private List createUpdatersFromConfig() { new SiriETUpdater( configItem, provideSiriTimetableSnapshot(), - new SiriETLightHttpTripUpdateSource(configItem.sourceParameters()), + new SiriETLiteHttpTripUpdateSource(configItem.sourceParameters()), TripUpdateMetrics.batch(configItem) ) ); @@ -223,7 +223,7 @@ private List createUpdatersFromConfig() { new SiriSXUpdater( configItem, timetableRepository, - new SiriLightHttpLoader( + new SiriLiteHttpLoader( configItem.uri(), configItem.timeout(), configItem.requestHeaders() diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteHttpTripUpdateSource.java similarity index 92% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteHttpTripUpdateSource.java index f60ee8e1c35..1c05a873303 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteHttpTripUpdateSource.java @@ -19,9 +19,9 @@ /** * SIRI Light downloads periodically all messages as a single GET request. */ -public class SiriETLightHttpTripUpdateSource implements EstimatedTimetableSource { +public class SiriETLiteHttpTripUpdateSource implements EstimatedTimetableSource { - private static final Logger LOG = LoggerFactory.getLogger(SiriETLightHttpTripUpdateSource.class); + private static final Logger LOG = LoggerFactory.getLogger(SiriETLiteHttpTripUpdateSource.class); /** * The framework code requires a requestor ref but in SIRI Light this is not used. */ @@ -31,7 +31,7 @@ public class SiriETLightHttpTripUpdateSource implements EstimatedTimetableSource private final SiriLoader siriLoader; - public SiriETLightHttpTripUpdateSource(Parameters parameters) { + public SiriETLiteHttpTripUpdateSource(Parameters parameters) { this.parameters = parameters; this.siriLoader = createLoader(parameters); @@ -73,7 +73,7 @@ private static SiriLoader createLoader(Parameters parameters) { } // Fallback to default loader else { - return new SiriLightHttpLoader( + return new SiriLiteHttpLoader( parameters.uri(), parameters.timeout(), parameters.httpRequestHeaders() diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteUpdaterParameters.java similarity index 72% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteUpdaterParameters.java index d4aa6fcaa37..6c62e37798f 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLightUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteUpdaterParameters.java @@ -5,7 +5,7 @@ import org.opentripplanner.updater.siri.updater.SiriETUpdater; import org.opentripplanner.updater.spi.HttpHeaders; -public record SiriETLightUpdaterParameters( +public record SiriETLiteUpdaterParameters( String configRef, String feedId, URI uri, @@ -14,8 +14,8 @@ public record SiriETLightUpdaterParameters( boolean fuzzyTripMatching, HttpHeaders httpRequestHeaders ) - implements SiriETUpdater.SiriETUpdaterParameters, SiriETLightHttpTripUpdateSource.Parameters { - public SiriETLightHttpTripUpdateSource.Parameters sourceParameters() { + implements SiriETUpdater.SiriETUpdaterParameters, SiriETLiteHttpTripUpdateSource.Parameters { + public SiriETLiteHttpTripUpdateSource.Parameters sourceParameters() { return this; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoader.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoader.java similarity index 90% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoader.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoader.java index 45be81d74e2..1737847ce7f 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoader.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoader.java @@ -16,15 +16,15 @@ * Load real-time updates from SIRI-SX and SIRI-ET feeds over HTTP via a single request * that contains all updates. */ -public class SiriLightHttpLoader implements SiriLoader { +public class SiriLiteHttpLoader implements SiriLoader { - private static final Logger LOG = LoggerFactory.getLogger(SiriLightHttpLoader.class); + private static final Logger LOG = LoggerFactory.getLogger(SiriLiteHttpLoader.class); private final HttpHeaders headers; private final URI uri; private final Duration timeout; private final OtpHttpClient otpHttpClient; - public SiriLightHttpLoader(URI uri, Duration timeout, HttpHeaders headers) { + public SiriLiteHttpLoader(URI uri, Duration timeout, HttpHeaders headers) { this.uri = uri; this.timeout = timeout; this.headers = HttpHeaders.of().acceptApplicationXML().add(headers).build(); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLiteUpdaterParameters.java similarity index 93% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLiteUpdaterParameters.java index 540fee7b919..6c4b666952f 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLightUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLiteUpdaterParameters.java @@ -5,7 +5,7 @@ import org.opentripplanner.updater.siri.updater.SiriSXUpdater; import org.opentripplanner.updater.spi.HttpHeaders; -public record SiriSXLightUpdaterParameters( +public record SiriSXLiteUpdaterParameters( String configRef, String feedId, URI uri, diff --git a/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java b/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java index 4119704ba2b..5b65ca228c9 100644 --- a/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java +++ b/application/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java @@ -32,8 +32,8 @@ public class SiriConfigDocTest { private static final Set INCLUDE_UPDATERS = Set.of( "siri-et-updater", "siri-sx-updater", - "siri-et-light", - "siri-sx-light" + "siri-et-lite", + "siri-sx-lite" ); private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); public static final ObjectMapper mapper = new ObjectMapper(); diff --git a/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java b/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java index 29097941d01..b88b0a38e80 100644 --- a/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java +++ b/application/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java @@ -36,8 +36,8 @@ public class UpdaterConfigDocTest { "siri-et-updater", "siri-et-google-pubsub-updater", "siri-sx-updater", - "siri-et-light", - "siri-sx-light" + "siri-et-lite", + "siri-sx-lite" ); private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); public static final ObjectMapper mapper = new ObjectMapper(); diff --git a/application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoaderTest.java b/application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoaderTest.java similarity index 90% rename from application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoaderTest.java rename to application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoaderTest.java index 3886d90309a..adef9f242a5 100644 --- a/application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLightHttpLoaderTest.java +++ b/application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoaderTest.java @@ -9,14 +9,14 @@ import org.opentripplanner.updater.spi.HttpHeaders; import uk.org.siri.siri20.NaturalLanguageStringStructure; -class SiriLightHttpLoaderTest { +class SiriLiteHttpLoaderTest { private static final Duration ONE_MIN = Duration.ofMinutes(1); @Test void test() { var uri = ResourceLoader.of(this).uri("siri-sx.xml"); - var loader = new SiriLightHttpLoader(uri, ONE_MIN, HttpHeaders.empty()); + var loader = new SiriLiteHttpLoader(uri, ONE_MIN, HttpHeaders.empty()); var siri = loader.fetchETFeed("OTP"); var delivery = siri.get().getServiceDelivery().getSituationExchangeDeliveries().getFirst(); var element = delivery.getSituations().getPtSituationElements().getFirst(); diff --git a/application/src/test/resources/standalone/config/router-config.json b/application/src/test/resources/standalone/config/router-config.json index 6c27f3e06dd..e4ea29dfd9e 100644 --- a/application/src/test/resources/standalone/config/router-config.json +++ b/application/src/test/resources/standalone/config/router-config.json @@ -433,13 +433,13 @@ "url": "https://transmodel.api.opendatahub.com/siri-lite/fm/parking" }, { - "type": "siri-et-light", + "type": "siri-et-lite", "feedId": "sta", "url": "https://example.com/siri-lite/estimated-timetable/xml", "fuzzyTripMatching": true }, { - "type": "siri-sx-light", + "type": "siri-sx-lite", "feedId": "sta", "url": "https://example.com/siri-lite/situation-exchange/xml" } diff --git a/doc/templates/sandbox/siri/SiriUpdater.md b/doc/templates/sandbox/siri/SiriUpdater.md index d1999e04f7d..bed90ded3a3 100644 --- a/doc/templates/sandbox/siri/SiriUpdater.md +++ b/doc/templates/sandbox/siri/SiriUpdater.md @@ -25,9 +25,9 @@ To enable the SIRI updater you need to add it to the `updaters` section of the ` -### SIRI-ET Light +### SIRI-ET Lite -SIRI Light is [not very well](https://nextcloud.leonard.io/s/2tdYdmYBGtLQMfi/download?path=&files=Proposition-Profil-SIRI-Lite-initial-v1-3%20en.pdf) +SIRI Lite is [not very well](https://nextcloud.leonard.io/s/2tdYdmYBGtLQMfi/download?path=&files=Proposition-Profil-SIRI-Lite-initial-v1-3%20en.pdf) [specified](https://normes.transport.data.gouv.fr/normes/siri/profil-france/#protocoles-d%C3%A9change-des-donn%C3%A9es-siri), but this updater supports the following definition: @@ -35,14 +35,14 @@ Fetching XML-formatted SIRI messages as single GET request rather than the more flow. This means that the XML feed must contain all updates for all trips, just like it is the case for GTFS-RT TripUpdates. - + -### SIRI-SX Light +### SIRI-SX Lite -This updater follows the same definition of SIRI Light as the SIRI-ET one: it downloads the entire +This updater follows the same definition of SIRI Lite as the SIRI-ET one: it downloads the entire feed in a single HTTP GET request. - + ## Changelog diff --git a/doc/user/RouterConfiguration.md b/doc/user/RouterConfiguration.md index d72cdab3d6f..9b6bbf685c4 100644 --- a/doc/user/RouterConfiguration.md +++ b/doc/user/RouterConfiguration.md @@ -877,13 +877,13 @@ Used to group requests when monitoring OTP. "url" : "https://transmodel.api.opendatahub.com/siri-lite/fm/parking" }, { - "type" : "siri-et-light", + "type" : "siri-et-lite", "feedId" : "sta", "url" : "https://example.com/siri-lite/estimated-timetable/xml", "fuzzyTripMatching" : true }, { - "type" : "siri-sx-light", + "type" : "siri-sx-lite", "feedId" : "sta", "url" : "https://example.com/siri-lite/situation-exchange/xml" } diff --git a/doc/user/sandbox/siri/SiriUpdater.md b/doc/user/sandbox/siri/SiriUpdater.md index 5cc2221289c..a6f7781e293 100644 --- a/doc/user/sandbox/siri/SiriUpdater.md +++ b/doc/user/sandbox/siri/SiriUpdater.md @@ -161,9 +161,9 @@ HTTP headers to add to the request. Any header key, value can be inserted. -### SIRI-ET Light +### SIRI-ET Lite -SIRI Light is [not very well](https://nextcloud.leonard.io/s/2tdYdmYBGtLQMfi/download?path=&files=Proposition-Profil-SIRI-Lite-initial-v1-3%20en.pdf) +SIRI Lite is [not very well](https://nextcloud.leonard.io/s/2tdYdmYBGtLQMfi/download?path=&files=Proposition-Profil-SIRI-Lite-initial-v1-3%20en.pdf) [specified](https://normes.transport.data.gouv.fr/normes/siri/profil-france/#protocoles-d%C3%A9change-des-donn%C3%A9es-siri), but this updater supports the following definition: @@ -171,12 +171,12 @@ Fetching XML-formatted SIRI messages as single GET request rather than the more flow. This means that the XML feed must contain all updates for all trips, just like it is the case for GTFS-RT TripUpdates. - + | Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | |----------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|---------------|:-----:| -| type = "siri-et-light" | `enum` | The type of the updater. | *Required* | | 1.5 | +| type = "siri-et-lite" | `enum` | The type of the updater. | *Required* | | 1.5 | | feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.7 | | frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.7 | | fuzzyTripMatching | `boolean` | If the fuzzy trip matcher should be used to match trips. | *Optional* | `false` | 2.7 | @@ -218,7 +218,7 @@ HTTP headers to add to the request. Any header key, value can be inserted. { "updaters" : [ { - "type" : "siri-et-light", + "type" : "siri-et-lite", "feedId" : "sta", "url" : "https://example.com/siri-lite/estimated-timetable/xml", "fuzzyTripMatching" : true @@ -227,19 +227,19 @@ HTTP headers to add to the request. Any header key, value can be inserted. } ``` - + -### SIRI-SX Light +### SIRI-SX Lite -This updater follows the same definition of SIRI Light as the SIRI-ET one: it downloads the entire +This updater follows the same definition of SIRI Lite as the SIRI-ET one: it downloads the entire feed in a single HTTP GET request. - + | Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | |----------------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|---------------|:-----:| -| type = "siri-sx-light" | `enum` | The type of the updater. | *Required* | | 1.5 | +| type = "siri-sx-lite" | `enum` | The type of the updater. | *Required* | | 1.5 | | [earlyStart](#u__16__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | | feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.7 | | frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.7 | @@ -292,7 +292,7 @@ HTTP headers to add to the request. Any header key, value can be inserted. { "updaters" : [ { - "type" : "siri-sx-light", + "type" : "siri-sx-lite", "feedId" : "sta", "url" : "https://example.com/siri-lite/situation-exchange/xml" } @@ -300,7 +300,7 @@ HTTP headers to add to the request. Any header key, value can be inserted. } ``` - + ## Changelog From f7e30ec948771b4d7d8d1385915ee87565bbd0db Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 11:36:30 +0100 Subject: [PATCH 109/269] Change package name --- .../standalone/config/routerconfig/UpdatersConfig.java | 4 ++-- .../config/routerconfig/updaters/SiriETLiteUpdaterConfig.java | 2 +- .../config/routerconfig/updaters/SiriSXLiteUpdaterConfig.java | 2 +- .../java/org/opentripplanner/updater/UpdatersParameters.java | 4 ++-- .../updater/configure/UpdaterConfigurator.java | 4 ++-- .../{light => lite}/SiriETLiteHttpTripUpdateSource.java | 2 +- .../updater/{light => lite}/SiriETLiteUpdaterParameters.java | 2 +- .../siri/updater/{light => lite}/SiriLiteHttpLoader.java | 2 +- .../updater/{light => lite}/SiriSXLiteUpdaterParameters.java | 2 +- .../siri/updater/{light => lite}/SiriLiteHttpLoaderTest.java | 2 +- .../updater/siri/updater/{light => lite}/siri-sx.xml | 0 11 files changed, 13 insertions(+), 13 deletions(-) rename application/src/main/java/org/opentripplanner/updater/siri/updater/{light => lite}/SiriETLiteHttpTripUpdateSource.java (97%) rename application/src/main/java/org/opentripplanner/updater/siri/updater/{light => lite}/SiriETLiteUpdaterParameters.java (92%) rename application/src/main/java/org/opentripplanner/updater/siri/updater/{light => lite}/SiriLiteHttpLoader.java (96%) rename application/src/main/java/org/opentripplanner/updater/siri/updater/{light => lite}/SiriSXLiteUpdaterParameters.java (91%) rename application/src/test/java/org/opentripplanner/updater/siri/updater/{light => lite}/SiriLiteHttpLoaderTest.java (95%) rename application/src/test/resources/org/opentripplanner/updater/siri/updater/{light => lite}/siri-sx.xml (100%) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index 1ee1d8bbf09..c2773dbcd46 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -48,8 +48,8 @@ import org.opentripplanner.updater.siri.updater.SiriETUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; -import org.opentripplanner.updater.siri.updater.light.SiriETLiteUpdaterParameters; -import org.opentripplanner.updater.siri.updater.light.SiriSXLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.lite.SiriETLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.lite.SiriSXLiteUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLiteUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLiteUpdaterConfig.java index 0311cf0dd37..b26bdd2a727 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLiteUpdaterConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriETLiteUpdaterConfig.java @@ -4,7 +4,7 @@ import java.time.Duration; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; -import org.opentripplanner.updater.siri.updater.light.SiriETLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.lite.SiriETLiteUpdaterParameters; public class SiriETLiteUpdaterConfig { diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLiteUpdaterConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLiteUpdaterConfig.java index fbc9d1971c4..27c1bcba0be 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLiteUpdaterConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/SiriSXLiteUpdaterConfig.java @@ -5,7 +5,7 @@ import java.time.Duration; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; -import org.opentripplanner.updater.siri.updater.light.SiriSXLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.lite.SiriSXLiteUpdaterParameters; public class SiriSXLiteUpdaterConfig { diff --git a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java index c960175ff6e..cfe0a12c8cc 100644 --- a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java @@ -8,8 +8,8 @@ import org.opentripplanner.updater.siri.updater.SiriETUpdaterParameters; import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdaterParameters; -import org.opentripplanner.updater.siri.updater.light.SiriETLiteUpdaterParameters; -import org.opentripplanner.updater.siri.updater.light.SiriSXLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.lite.SiriETLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.lite.SiriSXLiteUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; diff --git a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index c9323d4fc59..283197585fe 100644 --- a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -25,8 +25,8 @@ import org.opentripplanner.updater.siri.updater.SiriHttpLoader; import org.opentripplanner.updater.siri.updater.SiriSXUpdater; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdater; -import org.opentripplanner.updater.siri.updater.light.SiriETLiteHttpTripUpdateSource; -import org.opentripplanner.updater.siri.updater.light.SiriLiteHttpLoader; +import org.opentripplanner.updater.siri.updater.lite.SiriETLiteHttpTripUpdateSource; +import org.opentripplanner.updater.siri.updater.lite.SiriLiteHttpLoader; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.TimetableSnapshotFlush; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdater; diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java similarity index 97% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteHttpTripUpdateSource.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java index 1c05a873303..92428b85aad 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java @@ -1,4 +1,4 @@ -package org.opentripplanner.updater.siri.updater.light; +package org.opentripplanner.updater.siri.updater.lite; import static org.opentripplanner.updater.trip.UpdateIncrementality.FULL_DATASET; diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteUpdaterParameters.java similarity index 92% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteUpdaterParameters.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteUpdaterParameters.java index 6c62e37798f..9dd1e31a2b5 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriETLiteUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteUpdaterParameters.java @@ -1,4 +1,4 @@ -package org.opentripplanner.updater.siri.updater.light; +package org.opentripplanner.updater.siri.updater.lite; import java.net.URI; import java.time.Duration; diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoader.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriLiteHttpLoader.java similarity index 96% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoader.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriLiteHttpLoader.java index 1737847ce7f..c9b35be7b19 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoader.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriLiteHttpLoader.java @@ -1,4 +1,4 @@ -package org.opentripplanner.updater.siri.updater.light; +package org.opentripplanner.updater.siri.updater.lite; import java.net.URI; import java.time.Duration; diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLiteUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriSXLiteUpdaterParameters.java similarity index 91% rename from application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLiteUpdaterParameters.java rename to application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriSXLiteUpdaterParameters.java index 6c4b666952f..e2f6a7a63eb 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/light/SiriSXLiteUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriSXLiteUpdaterParameters.java @@ -1,4 +1,4 @@ -package org.opentripplanner.updater.siri.updater.light; +package org.opentripplanner.updater.siri.updater.lite; import java.net.URI; import java.time.Duration; diff --git a/application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoaderTest.java b/application/src/test/java/org/opentripplanner/updater/siri/updater/lite/SiriLiteHttpLoaderTest.java similarity index 95% rename from application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoaderTest.java rename to application/src/test/java/org/opentripplanner/updater/siri/updater/lite/SiriLiteHttpLoaderTest.java index adef9f242a5..c05e248747a 100644 --- a/application/src/test/java/org/opentripplanner/updater/siri/updater/light/SiriLiteHttpLoaderTest.java +++ b/application/src/test/java/org/opentripplanner/updater/siri/updater/lite/SiriLiteHttpLoaderTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.updater.siri.updater.light; +package org.opentripplanner.updater.siri.updater.lite; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/application/src/test/resources/org/opentripplanner/updater/siri/updater/light/siri-sx.xml b/application/src/test/resources/org/opentripplanner/updater/siri/updater/lite/siri-sx.xml similarity index 100% rename from application/src/test/resources/org/opentripplanner/updater/siri/updater/light/siri-sx.xml rename to application/src/test/resources/org/opentripplanner/updater/siri/updater/lite/siri-sx.xml From ee0e79d06e21ed96d93db861e8f7494f1512ee25 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 11:37:14 +0100 Subject: [PATCH 110/269] More spelling correction --- .../standalone/config/routerconfig/UpdatersConfig.java | 4 ++-- .../java/org/opentripplanner/updater/UpdatersParameters.java | 4 ++-- .../updater/configure/UpdaterConfigurator.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index c2773dbcd46..a71d5ee40f8 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -189,12 +189,12 @@ public List getSiriSXUpdaterParameters() { } @Override - public List getSiriETLightUpdaterParameters() { + public List getSiriETLiteUpdaterParameters() { return getParameters(SIRI_ET_LITE); } @Override - public List getSiriSXLightUpdaterParameters() { + public List getSiriSXLiteUpdaterParameters() { return getParameters(SIRI_SX_LITE); } diff --git a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java index cfe0a12c8cc..cb613580495 100644 --- a/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/UpdatersParameters.java @@ -35,9 +35,9 @@ public interface UpdatersParameters { List getSiriSXUpdaterParameters(); - List getSiriETLightUpdaterParameters(); + List getSiriETLiteUpdaterParameters(); - List getSiriSXLightUpdaterParameters(); + List getSiriSXLiteUpdaterParameters(); List getMqttGtfsRealtimeUpdaterParameters(); diff --git a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 283197585fe..6c628d11a0e 100644 --- a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -196,7 +196,7 @@ private List createUpdatersFromConfig() { ) ); } - for (var configItem : updatersParameters.getSiriETLightUpdaterParameters()) { + for (var configItem : updatersParameters.getSiriETLiteUpdaterParameters()) { updaters.add( new SiriETUpdater( configItem, @@ -218,7 +218,7 @@ private List createUpdatersFromConfig() { ) ); } - for (var configItem : updatersParameters.getSiriSXLightUpdaterParameters()) { + for (var configItem : updatersParameters.getSiriSXLiteUpdaterParameters()) { updaters.add( new SiriSXUpdater( configItem, From 15656d586aae8ab56f225b2937cfe823ff90e2ff Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 15:41:33 +0100 Subject: [PATCH 111/269] Add fallback names for corridors --- .../osm/tagmapping/OsmTagMapper.java | 3 ++- .../main/resources/WayProperties.properties | 1 + .../main/resources/WayProperties_de.properties | 1 + .../osm/tagmapping/OsmTagMapperTest.java | 18 +++++++++++++++--- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapper.java index 55bc87f5cad..e2debf79916 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/OsmTagMapper.java @@ -624,7 +624,7 @@ public void populateProperties(WayPropertySet props) { props.setSlopeOverride(new BestMatchSpecifier("indoor=yes"), true); } - public void populateNotesAndNames(WayPropertySet props) { + static void populateNotesAndNames(WayPropertySet props) { /* and the notes */ // TODO: The curly brackets in the string below mean that the CreativeNamer should substitute in OSM tag values. // However they are not taken into account when passed to the translation function. @@ -667,6 +667,7 @@ public void populateNotesAndNames(WayPropertySet props) { props.createNames("highway=footway", "name.pedestrian_path"); props.createNames("highway=bridleway", "name.bridleway"); props.createNames("highway=footway;bicycle=no", "name.pedestrian_path"); + props.createNames("highway=corridor", "name.corridor"); // Platforms props.createNames("otp:route_ref=*", "name.otp_route_ref"); diff --git a/application/src/main/resources/WayProperties.properties b/application/src/main/resources/WayProperties.properties index a7e7d73fc34..8ff0590e0d4 100644 --- a/application/src/main/resources/WayProperties.properties +++ b/application/src/main/resources/WayProperties.properties @@ -8,6 +8,7 @@ name.pedestrian_path=path name.pedestrian_area=open area name.path=path name.bridleway=bridleway +name.corridor=corridor name.otp_route_ref=Route {otp:route_ref} name.platform_ref=Platform {ref} diff --git a/application/src/main/resources/WayProperties_de.properties b/application/src/main/resources/WayProperties_de.properties index c741c8dc991..ecb537a5186 100644 --- a/application/src/main/resources/WayProperties_de.properties +++ b/application/src/main/resources/WayProperties_de.properties @@ -9,6 +9,7 @@ name.pedestrian_path=Fu\u00dfweg name.pedestrian_area=Fu\u00dfg\u00e4ngerzone name.path=Weg name.bridleway=Reitweg +name.corridor=Korridor name.otp_route_ref=Route {otp:route_ref} name.platform_ref=Plattform {ref} diff --git a/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java b/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java index 5448f84294d..8d699e38843 100644 --- a/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java +++ b/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java @@ -184,9 +184,7 @@ public static List roadCases() { @ParameterizedTest @MethodSource("roadCases") void motorroad(OsmWithTags way) { - OsmTagMapper osmTagMapper = new OsmTagMapper(); - WayPropertySet wps = new WayPropertySet(); - osmTagMapper.populateProperties(wps); + final WayPropertySet wps = wayProperySet(); assertEquals(ALL, wps.getDataForWay(way).getPermission()); @@ -194,9 +192,23 @@ void motorroad(OsmWithTags way) { assertEquals(CAR, wps.getDataForWay(way).getPermission()); } + @Test + void corridorName() { + final WayPropertySet wps = wayProperySet(); + var way = way("highway", "corridor"); + assertEquals("corridor", wps.getCreativeNameForWay(way).toString()); + } + public OsmWithTags way(String key, String value) { var way = new OsmWithTags(); way.addTag(key, value); return way; } + + private static WayPropertySet wayProperySet() { + OsmTagMapper osmTagMapper = new OsmTagMapper(); + WayPropertySet wps = new WayPropertySet(); + osmTagMapper.populateProperties(wps); + return wps; + } } From d7fd1d85050fe5c1708aaa6a26fb6ae34e5aa56c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 4 Dec 2024 21:57:41 +0100 Subject: [PATCH 112/269] Add translations for all languages --- application/src/main/resources/WayProperties_fi.properties | 1 + application/src/main/resources/WayProperties_fr.properties | 1 + application/src/main/resources/WayProperties_hu.properties | 1 + application/src/main/resources/WayProperties_nl.properties | 1 + application/src/main/resources/WayProperties_no.properties | 1 + application/src/main/resources/WayProperties_sv.properties | 1 + .../org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java | 3 +++ 7 files changed, 9 insertions(+) diff --git a/application/src/main/resources/WayProperties_fi.properties b/application/src/main/resources/WayProperties_fi.properties index b4f7330b737..725580c6c1a 100644 --- a/application/src/main/resources/WayProperties_fi.properties +++ b/application/src/main/resources/WayProperties_fi.properties @@ -8,6 +8,7 @@ name.pedestrian_path=polku name.pedestrian_area=aukio name.path=polku name.bridleway=ratsupolku +name.corridor=käytävä name.otp_route_ref=linja {otp:route_ref} name.platform_ref=laituri {ref} diff --git a/application/src/main/resources/WayProperties_fr.properties b/application/src/main/resources/WayProperties_fr.properties index 0c926a8c357..566ba586e18 100644 --- a/application/src/main/resources/WayProperties_fr.properties +++ b/application/src/main/resources/WayProperties_fr.properties @@ -9,6 +9,7 @@ name.pedestrian_path=chemin pi\u00e9tonnier name.pedestrian_area=plateau pi\u00e9tonnier name.path=chemin name.bridleway=sentier \u00e9questre +name.corridor=couloir name.otp_route_ref=Ligne {otp:route_ref} name.platform_ref=Plate-forme {ref} diff --git a/application/src/main/resources/WayProperties_hu.properties b/application/src/main/resources/WayProperties_hu.properties index 13539374eb2..e61bc8dbf1a 100644 --- a/application/src/main/resources/WayProperties_hu.properties +++ b/application/src/main/resources/WayProperties_hu.properties @@ -8,6 +8,7 @@ name.pedestrian_path=gyalog\u00FAt name.pedestrian_area=t\u00E9r name.path=path name.bridleway=nyomvonal +name.corridor=folyoso name.otp_route_ref={otp:route_ref} name.platform_ref={ref} diff --git a/application/src/main/resources/WayProperties_nl.properties b/application/src/main/resources/WayProperties_nl.properties index 501287506d2..dc7597c898e 100644 --- a/application/src/main/resources/WayProperties_nl.properties +++ b/application/src/main/resources/WayProperties_nl.properties @@ -9,6 +9,7 @@ name.pedestrian_path=voetpad name.pedestrian_area=open ruimte name.path=pad name.bridleway=ruiterpad +name.corridor=gang name.otp_route_ref=Lijn {otp:route_ref} name.platform_ref=Spoor {ref} diff --git a/application/src/main/resources/WayProperties_no.properties b/application/src/main/resources/WayProperties_no.properties index 95883c60af3..3cb19cdac76 100644 --- a/application/src/main/resources/WayProperties_no.properties +++ b/application/src/main/resources/WayProperties_no.properties @@ -8,6 +8,7 @@ name.pedestrian_path=gangvei name.pedestrian_area=plass name.path=sti name.bridleway=bridleway +name.corridor=korridor name.otp_route_ref=Rute {otp:route_ref} name.platform_ref=Plattform {ref} diff --git a/application/src/main/resources/WayProperties_sv.properties b/application/src/main/resources/WayProperties_sv.properties index 31529e1416b..3ba8bda6849 100644 --- a/application/src/main/resources/WayProperties_sv.properties +++ b/application/src/main/resources/WayProperties_sv.properties @@ -8,6 +8,7 @@ name.pedestrian_path=gångbanan name.pedestrian_area=torget name.path=stigen name.bridleway=ridvägem +name.corridor=korridor name.otp_route_ref=linje {otp:route_ref} name.platform_ref=plattform {ref} diff --git a/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java b/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java index 8d699e38843..0c8583c02db 100644 --- a/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java +++ b/application/src/test/java/org/opentripplanner/osm/tagmapping/OsmTagMapperTest.java @@ -7,6 +7,7 @@ import static org.opentripplanner.street.model.StreetTraversalPermission.CAR; import java.util.List; +import java.util.Locale; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -197,6 +198,8 @@ void corridorName() { final WayPropertySet wps = wayProperySet(); var way = way("highway", "corridor"); assertEquals("corridor", wps.getCreativeNameForWay(way).toString()); + assertEquals("Korridor", wps.getCreativeNameForWay(way).toString(Locale.GERMANY)); + assertEquals("käytävä", wps.getCreativeNameForWay(way).toString(Locale.of("FI"))); } public OsmWithTags way(String key, String value) { From 72d5765af8599eeff2cb01983f71dfe24a9ecdfd Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 5 Dec 2024 14:35:25 +0000 Subject: [PATCH 113/269] 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 114/269] 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 115/269] 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 e1bb7d6be66eb65d698db7ccea643814ce5345dc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Dec 2024 09:32:00 +0100 Subject: [PATCH 116/269] Make flex linking work together with boarding locations --- .../module/StreetLinkerModule.java | 20 ++++++- .../model/vertex/TransitStopVertex.java | 26 +++++++++ .../module/StreetLinkerModuleTest.java | 56 ++++++++++++++++++- 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java index 83a6f282204..bfd35c2f025 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -103,7 +103,7 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor continue; } // check if stop is already linked, to allow multiple idempotent linking cycles - if (tStop.isConnectedToGraph()) { + if (isAlreadyLinked(tStop, stopLocationsUsedForFlexTrips)) { continue; } @@ -127,6 +127,24 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor LOG.info(progress.completeMessage()); } + /** + * Determines if a given transit stop vertex is already linked to the street network, taking into + * account that flex stop need special linking to both a walkable and drivable edge. + * + * @param stopVertex The transit stop vertex to be checked. + * @param stopLocationsUsedForFlexTrips A set of stop locations that are used for flexible trips. + */ + private static boolean isAlreadyLinked( + TransitStopVertex stopVertex, + Set stopLocationsUsedForFlexTrips + ) { + if (stopLocationsUsedForFlexTrips.contains(stopVertex.getStop())) { + return stopVertex.isLinkedToDrivableEdge() && stopVertex.isLinkedToWalkableEdge(); + } else { + return stopVertex.isConnectedToGraph(); + } + } + /** * Link a stop to the nearest "relevant" edges. *

diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java index 82c977902b0..d43f587280c 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java @@ -1,9 +1,14 @@ package org.opentripplanner.street.model.vertex; +import static org.opentripplanner.street.search.TraverseMode.CAR; +import static org.opentripplanner.street.search.TraverseMode.WALK; + import java.util.HashSet; import java.util.Set; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.edge.PathwayEdge; +import org.opentripplanner.street.model.edge.StreetTransitEntityLink; +import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.site.RegularStop; @@ -94,4 +99,25 @@ public StationElement getStationElement() { public boolean isConnectedToGraph() { return getDegreeOut() + getDegreeIn() > 0; } + + public boolean isLinkedToDrivableEdge() { + return isLinkedToEdgeWhichAllows(CAR); + } + + public boolean isLinkedToWalkableEdge() { + return isLinkedToEdgeWhichAllows(WALK); + } + + private boolean isLinkedToEdgeWhichAllows(TraverseMode traverseMode) { + return getOutgoing() + .stream() + .anyMatch(edge -> + edge instanceof StreetTransitEntityLink link && + link + .getToVertex() + .getOutgoingStreetEdges() + .stream() + .anyMatch(se -> se.canTraverse(traverseMode)) + ); + } } diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java index 4f54581fdb8..3a9452e022a 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.Test; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; import org.opentripplanner.framework.application.OTPFeature; @@ -20,8 +21,10 @@ import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.street.model._data.StreetModelForTest; +import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.edge.StreetTransitStopLink; +import org.opentripplanner.street.model.vertex.OsmBoardingLocationVertex; import org.opentripplanner.street.model.vertex.SplitterVertex; import org.opentripplanner.street.model.vertex.TransitStopVertex; import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; @@ -103,6 +106,38 @@ void linkFlexStop() { }); } + @Test + void linkFlexStopWithBoardingLocation() { + OTPFeature.FlexRouting.testOn(() -> { + var model = new TestModel().withStopLinkedToBoardingLocation(); + var flexTrip = TimetableRepositoryForTest.of().unscheduledTrip("flex", model.stop()); + model.withFlexTrip(flexTrip); + + var module = model.streetLinkerModule(); + + module.buildGraph(); + + assertTrue(model.stopVertex().isConnectedToGraph()); + + // stop is used by a flex trip, needs to be linked to both the walk and car edge, + // also linked to the boarding location + assertThat(model.stopVertex().getOutgoing()).hasSize(3); + var links = model.outgoingLinks(); + assertInstanceOf(BoardingLocationToStopLink.class, links.getFirst()); + var linkToWalk = links.get(1); + SplitterVertex walkSplit = (SplitterVertex) linkToWalk.getToVertex(); + + assertTrue(walkSplit.isConnectedToWalkingEdge()); + assertFalse(walkSplit.isConnectedToDriveableEdge()); + + var linkToCar = links.getLast(); + SplitterVertex carSplit = (SplitterVertex) linkToCar.getToVertex(); + + assertFalse(carSplit.isConnectedToWalkingEdge()); + assertTrue(carSplit.isConnectedToDriveableEdge()); + }); + } + @Test void linkCarsAllowedStop() { var model = new TestModel(); @@ -140,6 +175,7 @@ private static class TestModel { private final StreetLinkerModule module; private final RegularStop stop; private final TimetableRepository timetableRepository; + private final Graph graph; public TestModel() { var from = StreetModelForTest.intersectionVertex( @@ -151,7 +187,7 @@ public TestModel() { KONGSBERG_PLATFORM_1.x + DELTA ); - Graph graph = new Graph(); + this.graph = new Graph(); graph.addVertex(from); graph.addVertex(to); @@ -232,5 +268,23 @@ public void withCarsAllowedTrip(Trip trip, StopLocation... stops) { timetableRepository.addTripPattern(tripPattern.getId(), tripPattern); } + + /** + * Links the stop to a boarding location as can happen during regular graph build. + */ + public TestModel withStopLinkedToBoardingLocation() { + var boardingLocation = new OsmBoardingLocationVertex( + "boarding-location", + KONGSBERG_PLATFORM_1.x - 0.0001, + KONGSBERG_PLATFORM_1.y - 0.0001, + null, + Set.of(stop.getId().getId()) + ); + graph.addVertex(boardingLocation); + + BoardingLocationToStopLink.createBoardingLocationToStopLink(boardingLocation, stopVertex); + BoardingLocationToStopLink.createBoardingLocationToStopLink(stopVertex, boardingLocation); + return this; + } } } From 22a414cc9f445448df663d5be7e06deed31f22be Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Dec 2024 09:53:50 +0100 Subject: [PATCH 117/269] Add javadoc --- .../street/model/vertex/TransitStopVertex.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java index d43f587280c..7d5d52f02b3 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java @@ -100,10 +100,18 @@ public boolean isConnectedToGraph() { return getDegreeOut() + getDegreeIn() > 0; } + /** + * Determines if this vertex is linked (via a {@link StreetTransitEntityLink}) to a drivable edge + * in the street network. + */ public boolean isLinkedToDrivableEdge() { return isLinkedToEdgeWhichAllows(CAR); } + /** + * Determines if this vertex is linked (via a {@link StreetTransitEntityLink}) to a walkable edge + * in the street network. + */ public boolean isLinkedToWalkableEdge() { return isLinkedToEdgeWhichAllows(WALK); } From 47d444165dcd0f911314071d73f8f3ae55d24c0d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 6 Dec 2024 15:02:26 +0100 Subject: [PATCH 118/269] Add warnings --- .../street/model/vertex/TransitStopVertex.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java index 7d5d52f02b3..5b1b1dfa6a8 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java @@ -103,6 +103,8 @@ public boolean isConnectedToGraph() { /** * Determines if this vertex is linked (via a {@link StreetTransitEntityLink}) to a drivable edge * in the street network. + *

+ * This method is slow: only use this during graph build. */ public boolean isLinkedToDrivableEdge() { return isLinkedToEdgeWhichAllows(CAR); @@ -111,6 +113,8 @@ public boolean isLinkedToDrivableEdge() { /** * Determines if this vertex is linked (via a {@link StreetTransitEntityLink}) to a walkable edge * in the street network. + *

+ * This method is slow: only use this during graph build. */ public boolean isLinkedToWalkableEdge() { return isLinkedToEdgeWhichAllows(WALK); From ee5b8421309be1c674ecf08e82ca5341409c8e64 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 9 Dec 2024 08:11:44 +0100 Subject: [PATCH 119/269] Improve comment --- .../graph_builder/module/StreetLinkerModule.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java index bfd35c2f025..b59b2aee20f 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -129,7 +129,9 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor /** * Determines if a given transit stop vertex is already linked to the street network, taking into - * account that flex stop need special linking to both a walkable and drivable edge. + * account that flex stop need special linking to both a walkable and drivable edge. For example, + * the {@link OsmBoardingLocationsModule}, which runs before this one, links stops often to + * walkable edges only. * * @param stopVertex The transit stop vertex to be checked. * @param stopLocationsUsedForFlexTrips A set of stop locations that are used for flexible trips. From 0f3f148b83e4464e8ae24186ca6660227e478677 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Dec 2024 11:46:20 +0200 Subject: [PATCH 120/269] 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 121/269] 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 b22001acea630e1a016f8780186573bb07d692b1 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Dec 2024 12:04:57 +0200 Subject: [PATCH 122/269] 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 2e7720ff090c52ffa91c3701476a63e0789dfa75 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 9 Dec 2024 12:41:20 +0200 Subject: [PATCH 123/269] 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 be21d1a08e10a765be1f24692f2f86d8430be811 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 9 Dec 2024 12:55:40 +0100 Subject: [PATCH 124/269] Fix spelling of 'Lite' Co-authored-by: Henrik Abrahamsson <127481124+habrahamsson-skanetrafiken@users.noreply.github.com> --- .../siri/updater/lite/SiriETLiteHttpTripUpdateSource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java index 92428b85aad..d6eef736a33 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java @@ -17,13 +17,13 @@ import uk.org.siri.siri20.Siri; /** - * SIRI Light downloads periodically all messages as a single GET request. + * SIRI Lite downloads periodically all messages as a single GET request. */ public class SiriETLiteHttpTripUpdateSource implements EstimatedTimetableSource { private static final Logger LOG = LoggerFactory.getLogger(SiriETLiteHttpTripUpdateSource.class); /** - * The framework code requires a requestor ref but in SIRI Light this is not used. + * The framework code requires a requestor ref but in SIRI Lite this is not used. */ private static final String DUMMY_REQUESTOR_REF = "OpenTripPlanner"; From 012f149376eb5ae5453a3c33e852665224bff1b5 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Mon, 9 Dec 2024 13:55:57 +0200 Subject: [PATCH 125/269] 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 a178b9d1236b73e76160892d73ad059e84434c3a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 8 Dec 2024 22:30:48 +0100 Subject: [PATCH 126/269] 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 127/269] 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 128/269] 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 ebbba525dd2d40d550a7e1ffb2d3f664b9cdfd06 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 9 Dec 2024 16:20:42 +0100 Subject: [PATCH 129/269] Apply review feedback --- .../graph_builder/module/StreetLinkerModule.java | 4 ++-- .../graph_builder/module/StreetLinkerModuleTest.java | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java index b59b2aee20f..d50659f275c 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -129,8 +129,8 @@ public void linkTransitStops(Graph graph, TimetableRepository timetableRepositor /** * Determines if a given transit stop vertex is already linked to the street network, taking into - * account that flex stop need special linking to both a walkable and drivable edge. For example, - * the {@link OsmBoardingLocationsModule}, which runs before this one, links stops often to + * account that flex stops need special linking to both a walkable and drivable edge. For example, + * the {@link OsmBoardingLocationsModule}, which runs before this one, often links stops to * walkable edges only. * * @param stopVertex The transit stop vertex to be checked. diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java index 3a9452e022a..10427812ac2 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java @@ -122,14 +122,23 @@ void linkFlexStopWithBoardingLocation() { // stop is used by a flex trip, needs to be linked to both the walk and car edge, // also linked to the boarding location assertThat(model.stopVertex().getOutgoing()).hasSize(3); + + // while the order of the link doesn't matter, it _is_ deterministic. + // first we have the link to the boarding location where the passengers are expected + // to wait. var links = model.outgoingLinks(); assertInstanceOf(BoardingLocationToStopLink.class, links.getFirst()); + + // the second link is the link to the walkable street network. this is not really necessary + // because the boarding location is walkable. this will be refactored away in the future. var linkToWalk = links.get(1); SplitterVertex walkSplit = (SplitterVertex) linkToWalk.getToVertex(); assertTrue(walkSplit.isConnectedToWalkingEdge()); assertFalse(walkSplit.isConnectedToDriveableEdge()); + // lastly we have the link to the drivable street network because vehicles also need to + // reach the stop if it's part of a flex trip. var linkToCar = links.getLast(); SplitterVertex carSplit = (SplitterVertex) linkToCar.getToVertex(); From a1d44ba8b438a21d83db598b116f8df0e6f54dec Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 9 Dec 2024 16:23:49 +0100 Subject: [PATCH 130/269] 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 1415c6a13a42003e860a516b36cb11d13414cc48 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Mon, 9 Dec 2024 20:41:00 -0800 Subject: [PATCH 131/269] 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 132/269] 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 b667e96b4be3648c258b59cca4c1d6a3f82f372f Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Tue, 10 Dec 2024 10:55:22 +0000 Subject: [PATCH 133/269] 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 134/269] 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 135/269] 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 136/269] 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 137/269] 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 138/269] 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 139/269] 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 140/269] 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 6e17055eff27179f637a5f2cb6e11b868713e099 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 11 Dec 2024 17:59:09 +0100 Subject: [PATCH 141/269] Update application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java Co-authored-by: Thomas Gran --- .../org/opentripplanner/updater/siri/updater/SiriETUpdater.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index f1d3941cf46..7dfd596dd21 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -99,7 +99,7 @@ public void runPolling() { @Override public String toString() { return ToStringBuilder - .of(this.getClass()) + .of(SiriETUpdater.class) .addStr("source", updateSource.toString()) .addDuration("frequency", pollingPeriod()) .toString(); From 497422364d0d128fc94085a724ede853c1bffa25 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 11 Dec 2024 18:00:49 +0100 Subject: [PATCH 142/269] Update application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java Co-authored-by: Thomas Gran --- .../updater/siri/updater/SiriETHttpTripUpdateSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java index c16ede42ac3..886b338f470 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java @@ -78,7 +78,7 @@ public UpdateIncrementality incrementalityOfLastUpdates() { @Override public String toString() { - return ToStringBuilder.of(this.getClass()).addStr("url", url).toString(); + return ToStringBuilder.of(SiriETHttpTripUpdateSource.class).addStr("url", url).toString(); } private static SiriLoader createLoader(String url, Parameters parameters) { From ed76a4da3ca1ed9ee1c548829f3ed7e92dbdd897 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 11 Dec 2024 18:01:30 +0100 Subject: [PATCH 143/269] Don't use getClass() --- .../org/opentripplanner/updater/siri/updater/SiriSXUpdater.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java index 253338161fb..d4ad863c207 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java @@ -97,7 +97,7 @@ protected void runPolling() throws InterruptedException { @Override public String toString() { return ToStringBuilder - .of(this.getClass()) + .of(SiriSXUpdater.class) .addStr("url", url) .addDuration("frequency", pollingPeriod()) .toString(); From 3b06b85bb1df699b5ecb4170a8b9765c880c892a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 11 Dec 2024 18:07:45 +0100 Subject: [PATCH 144/269] Use shorter name for parameters --- .../opentripplanner/updater/siri/updater/SiriETUpdater.java | 5 ++--- .../updater/siri/updater/SiriETUpdaterParameters.java | 2 +- .../siri/updater/lite/SiriETLiteUpdaterParameters.java | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index 7dfd596dd21..f9a982fb468 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -40,7 +40,7 @@ public class SiriETUpdater extends PollingGraphUpdater { private final Consumer metricsConsumer; public SiriETUpdater( - SiriETUpdaterParameters config, + Parameters config, SiriTimetableSnapshotSource timetableSnapshotSource, EstimatedTimetableSource source, Consumer metricsConsumer @@ -105,8 +105,7 @@ public String toString() { .toString(); } - public interface SiriETUpdaterParameters - extends UrlUpdaterParameters, PollingGraphUpdaterParameters { + public interface Parameters extends UrlUpdaterParameters, PollingGraphUpdaterParameters { boolean blockReadinessUntilInitialized(); boolean fuzzyTripMatching(); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java index a3477e3854e..71217225da1 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdaterParameters.java @@ -16,7 +16,7 @@ public record SiriETUpdaterParameters( HttpHeaders httpRequestHeaders, boolean producerMetrics ) - implements SiriETUpdater.SiriETUpdaterParameters, SiriETHttpTripUpdateSource.Parameters { + implements SiriETUpdater.Parameters, SiriETHttpTripUpdateSource.Parameters { public SiriETHttpTripUpdateSource.Parameters sourceParameters() { return this; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteUpdaterParameters.java index 9dd1e31a2b5..1eed702e625 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteUpdaterParameters.java @@ -14,7 +14,7 @@ public record SiriETLiteUpdaterParameters( boolean fuzzyTripMatching, HttpHeaders httpRequestHeaders ) - implements SiriETUpdater.SiriETUpdaterParameters, SiriETLiteHttpTripUpdateSource.Parameters { + implements SiriETUpdater.Parameters, SiriETLiteHttpTripUpdateSource.Parameters { public SiriETLiteHttpTripUpdateSource.Parameters sourceParameters() { return this; } From 71fae3dfc4742e4e2e1d1e425014515c1852a7fb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 11 Dec 2024 18:10:37 +0100 Subject: [PATCH 145/269] Use shorter name for SIRI-SX --- .../opentripplanner/updater/siri/updater/SiriSXUpdater.java | 5 ++--- .../updater/siri/updater/SiriSXUpdaterParameters.java | 2 +- .../siri/updater/lite/SiriSXLiteUpdaterParameters.java | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java index d4ad863c207..edfdf0d878d 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java @@ -47,7 +47,7 @@ public class SiriSXUpdater extends PollingGraphUpdater implements TransitAlertPr private final OtpRetry retry; public SiriSXUpdater( - SiriSXUpdaterParameters config, + Parameters config, TimetableRepository timetableRepository, SiriLoader siriLoader ) { @@ -208,8 +208,7 @@ private void updateRequestorRef() { requestorRef = originalRequestorRef + "-retry-" + retryCount; } - public interface SiriSXUpdaterParameters - extends PollingGraphUpdaterParameters, UrlUpdaterParameters { + public interface Parameters extends PollingGraphUpdaterParameters, UrlUpdaterParameters { String requestorRef(); boolean blockReadinessUntilInitialized(); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java index 539e1333fa8..d1796557658 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdaterParameters.java @@ -14,4 +14,4 @@ public record SiriSXUpdaterParameters( boolean blockReadinessUntilInitialized, HttpHeaders requestHeaders ) - implements SiriSXUpdater.SiriSXUpdaterParameters {} + implements SiriSXUpdater.Parameters {} diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriSXLiteUpdaterParameters.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriSXLiteUpdaterParameters.java index e2f6a7a63eb..5c48ca3e04b 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriSXLiteUpdaterParameters.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriSXLiteUpdaterParameters.java @@ -14,7 +14,7 @@ public record SiriSXLiteUpdaterParameters( Duration timeout, HttpHeaders requestHeaders ) - implements SiriSXUpdater.SiriSXUpdaterParameters { + implements SiriSXUpdater.Parameters { @Override public String requestorRef() { return "OpenTripPlanner"; From ce2bf50e33091d75b56d52af18bd209f10c7606e Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Wed, 11 Dec 2024 11:55:11 -0800 Subject: [PATCH 146/269] 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 790bdc63f7f227811865bb51bbec2a2df760c663 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 11 Dec 2024 21:40:34 +0100 Subject: [PATCH 147/269] Build SIRI loader in factory, inject into source --- .../configure/UpdaterConfigurator.java | 11 ++++-- .../updater/SiriETHttpTripUpdateSource.java | 20 ++--------- .../updater/siri/updater/SiriETUpdater.java | 4 +++ .../siri/updater/SiriLoaderFactory.java | 34 +++++++++++++++++++ .../lite/SiriETLiteHttpTripUpdateSource.java | 21 ++---------- 5 files changed, 51 insertions(+), 39 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLoaderFactory.java diff --git a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 6c628d11a0e..82902f447d3 100644 --- a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -23,6 +23,7 @@ import org.opentripplanner.updater.siri.updater.SiriETHttpTripUpdateSource; import org.opentripplanner.updater.siri.updater.SiriETUpdater; import org.opentripplanner.updater.siri.updater.SiriHttpLoader; +import org.opentripplanner.updater.siri.updater.SiriLoaderFactory; import org.opentripplanner.updater.siri.updater.SiriSXUpdater; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdater; import org.opentripplanner.updater.siri.updater.lite.SiriETLiteHttpTripUpdateSource; @@ -191,7 +192,10 @@ private List createUpdatersFromConfig() { new SiriETUpdater( configItem, provideSiriTimetableSnapshot(), - new SiriETHttpTripUpdateSource(configItem.sourceParameters()), + new SiriETHttpTripUpdateSource( + configItem.sourceParameters(), + SiriLoaderFactory.createLoader(configItem) + ), TripUpdateMetrics.streaming(configItem) ) ); @@ -201,7 +205,10 @@ private List createUpdatersFromConfig() { new SiriETUpdater( configItem, provideSiriTimetableSnapshot(), - new SiriETLiteHttpTripUpdateSource(configItem.sourceParameters()), + new SiriETLiteHttpTripUpdateSource( + configItem.sourceParameters(), + SiriLoaderFactory.createLoader(configItem) + ), TripUpdateMetrics.batch(configItem) ) ); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java index 886b338f470..9d0373e1a16 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETHttpTripUpdateSource.java @@ -31,7 +31,7 @@ public class SiriETHttpTripUpdateSource implements EstimatedTimetableSource { private UpdateIncrementality updateIncrementality = FULL_DATASET; private ZonedDateTime lastTimestamp = ZonedDateTime.now().minusMonths(1); - public SiriETHttpTripUpdateSource(Parameters parameters) { + public SiriETHttpTripUpdateSource(Parameters parameters, SiriLoader siriLoader) { this.url = parameters.url(); this.requestorRef = @@ -39,7 +39,7 @@ public SiriETHttpTripUpdateSource(Parameters parameters) { ? "otp-" + UUID.randomUUID() : parameters.requestorRef(); - this.siriLoader = createLoader(url, parameters); + this.siriLoader = siriLoader; } @Override @@ -81,22 +81,6 @@ public String toString() { return ToStringBuilder.of(SiriETHttpTripUpdateSource.class).addStr("url", url).toString(); } - private static SiriLoader createLoader(String url, Parameters parameters) { - // Load real-time updates from a file. - if (SiriFileLoader.matchesUrl(url)) { - return new SiriFileLoader(url); - } - // Fallback to default loader - else { - return new SiriHttpLoader( - url, - parameters.timeout(), - parameters.httpRequestHeaders(), - parameters.previewInterval() - ); - } - } - public interface Parameters { String url(); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index f9a982fb468..e4b946bf21d 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -3,6 +3,8 @@ import java.util.List; import java.util.function.Consumer; import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; +import org.opentripplanner.updater.siri.updater.lite.SiriETLiteHttpTripUpdateSource; +import org.opentripplanner.updater.siri.updater.lite.SiriETLiteUpdaterParameters; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; import org.opentripplanner.updater.spi.ResultLogger; @@ -106,6 +108,8 @@ public String toString() { } public interface Parameters extends UrlUpdaterParameters, PollingGraphUpdaterParameters { + String url(); + boolean blockReadinessUntilInitialized(); boolean fuzzyTripMatching(); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLoaderFactory.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLoaderFactory.java new file mode 100644 index 00000000000..b9b43d9341e --- /dev/null +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLoaderFactory.java @@ -0,0 +1,34 @@ +package org.opentripplanner.updater.siri.updater; + +import org.opentripplanner.updater.siri.updater.lite.SiriETLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.lite.SiriLiteHttpLoader; + +/** + * Constructs a SiriLoader from the parameters of the updater. + */ +public class SiriLoaderFactory { + + public static SiriLoader createLoader(SiriETUpdater.Parameters parameters) { + // Load real-time updates from a file. + if (SiriFileLoader.matchesUrl(parameters.url())) { + return new SiriFileLoader(parameters.url()); + } + // Fallback to default loader + else { + return switch (parameters) { + case SiriETUpdaterParameters p -> new SiriHttpLoader( + p.url(), + p.timeout(), + p.httpRequestHeaders(), + p.previewInterval() + ); + case SiriETLiteUpdaterParameters p -> new SiriLiteHttpLoader( + p.uri(), + p.timeout(), + p.httpRequestHeaders() + ); + default -> throw new IllegalArgumentException("Unexpected value: " + parameters); + }; + } + } +} diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java index d6eef736a33..7a1e8c988f2 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java @@ -7,7 +7,6 @@ import java.util.Optional; import org.opentripplanner.framework.io.OtpHttpClientException; import org.opentripplanner.updater.siri.updater.EstimatedTimetableSource; -import org.opentripplanner.updater.siri.updater.SiriFileLoader; import org.opentripplanner.updater.siri.updater.SiriLoader; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.trip.UpdateIncrementality; @@ -31,10 +30,9 @@ public class SiriETLiteHttpTripUpdateSource implements EstimatedTimetableSource private final SiriLoader siriLoader; - public SiriETLiteHttpTripUpdateSource(Parameters parameters) { + public SiriETLiteHttpTripUpdateSource(Parameters parameters, SiriLoader siriLoader) { this.parameters = parameters; - - this.siriLoader = createLoader(parameters); + this.siriLoader = siriLoader; } @Override @@ -66,21 +64,6 @@ public String toString() { .toString(); } - private static SiriLoader createLoader(Parameters parameters) { - // Load real-time updates from a file. - if (SiriFileLoader.matchesUrl(parameters.uri().toString())) { - return new SiriFileLoader(parameters.uri().toString()); - } - // Fallback to default loader - else { - return new SiriLiteHttpLoader( - parameters.uri(), - parameters.timeout(), - parameters.httpRequestHeaders() - ); - } - } - public interface Parameters { URI uri(); From ec9e7246e72aee18a91ecad61f560a46d53ba2a8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 11 Dec 2024 21:53:45 +0100 Subject: [PATCH 148/269] Fix spelling of SIRI-ET --- .../org/opentripplanner/updater/siri/updater/SiriETUpdater.java | 2 +- .../siri/updater/lite/SiriETLiteHttpTripUpdateSource.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index e4b946bf21d..db60b34f55a 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -54,7 +54,7 @@ public SiriETUpdater( this.blockReadinessUntilInitialized = config.blockReadinessUntilInitialized(); - LOG.info("Creating SIRI ET updater running every {}: {}", pollingPeriod(), updateSource); + LOG.info("Creating SIRI-ET updater running every {}: {}", pollingPeriod(), updateSource); estimatedTimetableHandler = new EstimatedTimetableHandler(timetableSnapshotSource, config.fuzzyTripMatching(), feedId); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java index 7a1e8c988f2..30801a64c7f 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/lite/SiriETLiteHttpTripUpdateSource.java @@ -16,7 +16,7 @@ import uk.org.siri.siri20.Siri; /** - * SIRI Lite downloads periodically all messages as a single GET request. + * SIRI Lite periodically downloads all messages as a single GET request. */ public class SiriETLiteHttpTripUpdateSource implements EstimatedTimetableSource { From 4141ce9e49205747aa34ec795fa547ab760c19c1 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Wed, 11 Dec 2024 13:37:02 -0800 Subject: [PATCH 149/269] 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 150/269] 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 151/269] 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 0944921250cfc2a36d422a24c9af341865162ad0 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 12 Dec 2024 10:41:40 +0000 Subject: [PATCH 152/269] extract future handling logic to a new method --- .../updater/alert/GtfsRealtimeAlertsUpdater.java | 8 +++++--- .../opentripplanner/updater/spi/PollingGraphUpdater.java | 7 +++++++ .../opentripplanner/updater/trip/PollingTripUpdater.java | 2 +- .../VehicleParkingAvailabilityUpdater.java | 2 +- .../updater/vehicle_parking/VehicleParkingUpdater.java | 2 +- .../vehicle_position/PollingVehiclePositionUpdater.java | 2 +- .../updater/vehicle_rental/VehicleRentalUpdater.java | 2 +- 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java index 0e7ab35cb13..bd20d8ddcc3 100644 --- a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java @@ -82,9 +82,11 @@ protected void runPolling() throws InterruptedException, ExecutionException { } // Handle update in graph writer runnable - saveResultOnGraph - .execute(context -> updateHandler.update(feed, context.gtfsRealtimeFuzzyTripMatcher())) - .get(); + processGraphUpdaterResult( + saveResultOnGraph.execute(context -> + updateHandler.update(feed, context.gtfsRealtimeFuzzyTripMatcher()) + ) + ); lastTimestamp = feedTimestamp; } diff --git a/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java b/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java index e0859371de8..21eb60c2737 100644 --- a/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java @@ -2,6 +2,8 @@ import java.time.Duration; import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -100,4 +102,9 @@ public String getConfigRef() { * with pauses in between. The length of the pause is defined in the preference frequency. */ protected abstract void runPolling() throws Exception; + + protected void processGraphUpdaterResult(Future result) + throws ExecutionException, InterruptedException { + result.get(); + } } diff --git a/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java b/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java index 42f24031839..1f88044d51a 100644 --- a/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java @@ -90,7 +90,7 @@ public void runPolling() throws InterruptedException, ExecutionException { feedId, recordMetrics ); - saveResultOnGraph.execute(runnable).get(); + processGraphUpdaterResult(saveResultOnGraph.execute(runnable)); } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java index 64f8ca81763..c65ff7bc4c1 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java @@ -55,7 +55,7 @@ protected void runPolling() throws InterruptedException, ExecutionException { var updates = source.getUpdates(); var graphWriterRunnable = new AvailabilityUpdater(updates); - saveResultOnGraph.execute(graphWriterRunnable).get(); + processGraphUpdaterResult(saveResultOnGraph.execute(graphWriterRunnable)); } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java index 4116486ee2d..ea356200f3b 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java @@ -82,7 +82,7 @@ protected void runPolling() throws InterruptedException, ExecutionException { VehicleParkingGraphWriterRunnable graphWriterRunnable = new VehicleParkingGraphWriterRunnable( vehicleParkings ); - saveResultOnGraph.execute(graphWriterRunnable).get(); + processGraphUpdaterResult(saveResultOnGraph.execute(graphWriterRunnable)); } private class VehicleParkingGraphWriterRunnable implements GraphWriterRunnable { diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java index a4d36f1d1c3..c787989c5d2 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java @@ -78,7 +78,7 @@ public void runPolling() throws InterruptedException, ExecutionException { fuzzyTripMatching, updates ); - saveResultOnGraph.execute(runnable).get(); + processGraphUpdaterResult(saveResultOnGraph.execute(runnable)); } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java index c8030e5492a..efcf9812feb 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java @@ -139,7 +139,7 @@ protected void runPolling() throws InterruptedException, ExecutionException { stations, geofencingZones ); - saveResultOnGraph.execute(graphWriterRunnable).get(); + processGraphUpdaterResult(saveResultOnGraph.execute(graphWriterRunnable)); } private class VehicleRentalGraphWriterRunnable implements GraphWriterRunnable { From 4df65a19cc68d495e5aedfecea0b134b798c39eb Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 12 Dec 2024 10:55:00 +0000 Subject: [PATCH 153/269] add feature flag --- .../opentripplanner/framework/application/OTPFeature.java | 6 ++++++ doc/user/Configuration.md | 1 + 2 files changed, 7 insertions(+) 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..add5d690e2e 100644 --- a/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java +++ b/application/src/main/java/org/opentripplanner/framework/application/OTPFeature.java @@ -97,6 +97,12 @@ public enum OTPFeature { false, "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." ), + WaitForGraphUpdateInPollingUpdaters( + true, + false, + "Make all polling updaters wait for graph updates to complete before finishing. " + + "If this is not enabled, the updaters will finish after submitting the task to update the graph." + ), Co2Emissions(false, true, "Enable the emissions sandbox module."), DataOverlay( false, diff --git a/doc/user/Configuration.md b/doc/user/Configuration.md index f80966b8cb3..1d65c2af939 100644 --- a/doc/user/Configuration.md +++ b/doc/user/Configuration.md @@ -238,6 +238,7 @@ Here is a list of all features which can be toggled on/off and their default val | `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. | | | +| `WaitForGraphUpdateInPollingUpdaters` | Make all polling updaters wait for graph updates to complete before finishing. If this is not enabled, the updaters will finish after submitting the task to update the graph. | ✓️ | | | `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. | | ✓️ | From 95e9ca770bac8f0751f951f3223ffb669d66baa4 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 12 Dec 2024 13:43:09 +0200 Subject: [PATCH 154/269] 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 d8a34ddcfe80dabfba1c08a47f95adaa900d643d Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 12 Dec 2024 12:50:06 +0000 Subject: [PATCH 155/269] refactor update graph logic to PollingGraphUpdater --- .../updater/alert/GtfsRealtimeAlertsUpdater.java | 12 +----------- .../updater/siri/updater/SiriETUpdater.java | 9 --------- .../updater/siri/updater/SiriSXUpdater.java | 9 +-------- .../updater/spi/PollingGraphUpdater.java | 15 ++++++++++++--- .../updater/trip/PollingTripUpdater.java | 12 +----------- .../VehicleParkingAvailabilityUpdater.java | 10 +--------- .../vehicle_parking/VehicleParkingUpdater.java | 9 +-------- .../PollingVehiclePositionUpdater.java | 12 +----------- .../vehicle_rental/VehicleRentalUpdater.java | 10 +--------- 9 files changed, 19 insertions(+), 79 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java index bd20d8ddcc3..b217c60a05b 100644 --- a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java @@ -27,7 +27,6 @@ public class GtfsRealtimeAlertsUpdater extends PollingGraphUpdater implements Tr private final TransitAlertService transitAlertService; private final HttpHeaders headers; private final OtpHttpClient otpHttpClient; - private WriteToGraphCallback saveResultOnGraph; private Long lastTimestamp = Long.MIN_VALUE; public GtfsRealtimeAlertsUpdater( @@ -49,11 +48,6 @@ public GtfsRealtimeAlertsUpdater( LOG.info("Creating real-time alert updater running every {}: {}", pollingPeriod(), url); } - @Override - public void setup(WriteToGraphCallback writeToGraphCallback) { - this.saveResultOnGraph = writeToGraphCallback; - } - public TransitAlertService getTransitAlertService() { return transitAlertService; } @@ -82,11 +76,7 @@ protected void runPolling() throws InterruptedException, ExecutionException { } // Handle update in graph writer runnable - processGraphUpdaterResult( - saveResultOnGraph.execute(context -> - updateHandler.update(feed, context.gtfsRealtimeFuzzyTripMatcher()) - ) - ); + updateGraph(context -> updateHandler.update(feed, context.gtfsRealtimeFuzzyTripMatcher())); lastTimestamp = feedTimestamp; } diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index 087bf28e875..38efa18c177 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -28,10 +28,6 @@ public class SiriETUpdater extends PollingGraphUpdater { * Feed id that is used for the trip ids in the TripUpdates */ private final String feedId; - /** - * Parent update manager. Is used to execute graph writer runnables. - */ - protected WriteToGraphCallback saveResultOnGraph; private final EstimatedTimetableHandler estimatedTimetableHandler; @@ -61,11 +57,6 @@ public SiriETUpdater( recordMetrics = TripUpdateMetrics.streaming(config); } - @Override - public void setup(WriteToGraphCallback writeToGraphCallback) { - this.saveResultOnGraph = writeToGraphCallback; - } - /** * Repeatedly makes blocking calls to an UpdateStreamer to retrieve new stop time updates, and * applies those updates to the graph. diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java index 83200db30d3..8311a385e9c 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriSXUpdater.java @@ -13,7 +13,6 @@ import org.opentripplanner.updater.alert.TransitAlertProvider; import org.opentripplanner.updater.siri.SiriAlertsUpdateHandler; import org.opentripplanner.updater.spi.PollingGraphUpdater; -import org.opentripplanner.updater.spi.WriteToGraphCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.ServiceDelivery; @@ -33,7 +32,6 @@ public class SiriSXUpdater extends PollingGraphUpdater implements TransitAlertPr // TODO RT_AB: Document why SiriAlertsUpdateHandler is a separate instance that persists across // many graph update operations. private final SiriAlertsUpdateHandler updateHandler; - private WriteToGraphCallback writeToGraphCallback; private ZonedDateTime lastTimestamp = ZonedDateTime.now().minusWeeks(1); private String requestorRef; /** @@ -78,11 +76,6 @@ public SiriSXUpdater(SiriSXUpdaterParameters config, TimetableRepository timetab ); } - @Override - public void setup(WriteToGraphCallback writeToGraphCallback) { - this.writeToGraphCallback = writeToGraphCallback; - } - public TransitAlertService getTransitAlertService() { return transitAlertService; } @@ -141,7 +134,7 @@ private void updateSiri() { // All that said, out of all the update types, Alerts (and SIRI SX) are probably the ones // that would be most tolerant of non-versioned application-wide storage since they don't // participate in routing and are tacked on to already-completed routing responses. - writeToGraphCallback.execute(context -> { + saveResultOnGraph.execute(context -> { updateHandler.update(serviceDelivery, context); if (markPrimed) { primed = true; diff --git a/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java b/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java index 21eb60c2737..0525406505e 100644 --- a/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java @@ -3,7 +3,7 @@ import java.time.Duration; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; +import org.opentripplanner.updater.GraphWriterRunnable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,6 +36,10 @@ public abstract class PollingGraphUpdater implements GraphUpdater { * removed that. */ protected volatile boolean primed; + /** + * Parent update manager. Is used to execute graph writer runnables. + */ + protected WriteToGraphCallback saveResultOnGraph; /** Shared configuration code for all polling graph updaters. */ protected PollingGraphUpdater(PollingGraphUpdaterParameters config) { @@ -97,14 +101,19 @@ public String getConfigRef() { return configRef; } + @Override + public final void setup(WriteToGraphCallback writeToGraphCallback) { + this.saveResultOnGraph = writeToGraphCallback; + } + /** * Mirrors GraphUpdater.run method. Only difference is that runPolling will be run multiple times * with pauses in between. The length of the pause is defined in the preference frequency. */ protected abstract void runPolling() throws Exception; - protected void processGraphUpdaterResult(Future result) + protected final void updateGraph(GraphWriterRunnable task) throws ExecutionException, InterruptedException { - result.get(); + saveResultOnGraph.execute(task).get(); } } diff --git a/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java b/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java index 1f88044d51a..8331bf19d3b 100644 --- a/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java @@ -6,7 +6,6 @@ import java.util.function.Consumer; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.UpdateResult; -import org.opentripplanner.updater.spi.WriteToGraphCallback; import org.opentripplanner.updater.trip.metrics.BatchTripUpdateMetrics; import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; @@ -34,10 +33,6 @@ public class PollingTripUpdater extends PollingGraphUpdater { private final BackwardsDelayPropagationType backwardsDelayPropagationType; private final Consumer recordMetrics; - /** - * Parent update manager. Is used to execute graph writer runnables. - */ - private WriteToGraphCallback saveResultOnGraph; /** * Set only if we should attempt to match the trip_id from other data in TripDescriptor */ @@ -64,11 +59,6 @@ public PollingTripUpdater( ); } - @Override - public void setup(WriteToGraphCallback writeToGraphCallback) { - this.saveResultOnGraph = writeToGraphCallback; - } - /** * Repeatedly makes blocking calls to an UpdateStreamer to retrieve new stop time updates, and * applies those updates to the graph. @@ -90,7 +80,7 @@ public void runPolling() throws InterruptedException, ExecutionException { feedId, recordMetrics ); - processGraphUpdaterResult(saveResultOnGraph.execute(runnable)); + updateGraph(runnable); } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java index c65ff7bc4c1..29e820ff952 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java @@ -13,7 +13,6 @@ import org.opentripplanner.updater.RealTimeUpdateContext; import org.opentripplanner.updater.spi.DataSource; import org.opentripplanner.updater.spi.PollingGraphUpdater; -import org.opentripplanner.updater.spi.WriteToGraphCallback; import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,8 +27,6 @@ public class VehicleParkingAvailabilityUpdater extends PollingGraphUpdater { VehicleParkingAvailabilityUpdater.class ); private final DataSource source; - private WriteToGraphCallback saveResultOnGraph; - private final VehicleParkingRepository repository; public VehicleParkingAvailabilityUpdater( @@ -44,18 +41,13 @@ public VehicleParkingAvailabilityUpdater( LOG.info("Creating vehicle-parking updater running every {}: {}", pollingPeriod(), source); } - @Override - public void setup(WriteToGraphCallback writeToGraphCallback) { - this.saveResultOnGraph = writeToGraphCallback; - } - @Override protected void runPolling() throws InterruptedException, ExecutionException { if (source.update()) { var updates = source.getUpdates(); var graphWriterRunnable = new AvailabilityUpdater(updates); - processGraphUpdaterResult(saveResultOnGraph.execute(graphWriterRunnable)); + updateGraph(graphWriterRunnable); } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java index ea356200f3b..5513e224f57 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java @@ -27,7 +27,6 @@ import org.opentripplanner.updater.RealTimeUpdateContext; import org.opentripplanner.updater.spi.DataSource; import org.opentripplanner.updater.spi.PollingGraphUpdater; -import org.opentripplanner.updater.spi.WriteToGraphCallback; import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,7 +42,6 @@ public class VehicleParkingUpdater extends PollingGraphUpdater { private final Map> tempEdgesByPark = new HashMap<>(); private final DataSource source; private final List oldVehicleParkings = new ArrayList<>(); - private WriteToGraphCallback saveResultOnGraph; private final VertexLinker linker; private final VehicleParkingRepository parkingRepository; @@ -64,11 +62,6 @@ public VehicleParkingUpdater( LOG.info("Creating vehicle-parking updater running every {}: {}", pollingPeriod(), source); } - @Override - public void setup(WriteToGraphCallback writeToGraphCallback) { - this.saveResultOnGraph = writeToGraphCallback; - } - @Override protected void runPolling() throws InterruptedException, ExecutionException { LOG.debug("Updating vehicle parkings from {}", source); @@ -82,7 +75,7 @@ protected void runPolling() throws InterruptedException, ExecutionException { VehicleParkingGraphWriterRunnable graphWriterRunnable = new VehicleParkingGraphWriterRunnable( vehicleParkings ); - processGraphUpdaterResult(saveResultOnGraph.execute(graphWriterRunnable)); + updateGraph(graphWriterRunnable); } private class VehicleParkingGraphWriterRunnable implements GraphWriterRunnable { diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java index c787989c5d2..f53d3010e59 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java @@ -8,7 +8,6 @@ import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle; import org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig; import org.opentripplanner.updater.spi.PollingGraphUpdater; -import org.opentripplanner.updater.spi.WriteToGraphCallback; import org.opentripplanner.utils.tostring.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,10 +27,6 @@ public class PollingVehiclePositionUpdater extends PollingGraphUpdater { private final GtfsRealtimeHttpVehiclePositionSource vehiclePositionSource; private final Set vehiclePositionFeatures; - /** - * Parent update manager. Is used to execute graph writer runnables. - */ - private WriteToGraphCallback saveResultOnGraph; private final String feedId; private final RealtimeVehicleRepository realtimeVehicleRepository; private final boolean fuzzyTripMatching; @@ -55,11 +50,6 @@ public PollingVehiclePositionUpdater( ); } - @Override - public void setup(WriteToGraphCallback writeToGraphCallback) { - this.saveResultOnGraph = writeToGraphCallback; - } - /** * Repeatedly makes blocking calls to an UpdateStreamer to retrieve new stop time updates, and * applies those updates to the graph. @@ -78,7 +68,7 @@ public void runPolling() throws InterruptedException, ExecutionException { fuzzyTripMatching, updates ); - processGraphUpdaterResult(saveResultOnGraph.execute(runnable)); + updateGraph(runnable); } } diff --git a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java index efcf9812feb..8419d004138 100644 --- a/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java @@ -31,7 +31,6 @@ import org.opentripplanner.updater.RealTimeUpdateContext; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.UpdaterConstructionException; -import org.opentripplanner.updater.spi.WriteToGraphCallback; import org.opentripplanner.updater.vehicle_rental.datasources.VehicleRentalDatasource; import org.opentripplanner.utils.lang.ObjectUtils; import org.opentripplanner.utils.logging.Throttle; @@ -54,8 +53,6 @@ public class VehicleRentalUpdater extends PollingGraphUpdater { private final VehicleRentalDatasource source; private final String nameForLogging; - private WriteToGraphCallback saveResultOnGraph; - private Map latestModifiedEdges = Map.of(); private Set latestAppliedGeofencingZones = Set.of(); private final Map verticesByStation = new HashMap<>(); @@ -109,11 +106,6 @@ public VehicleRentalUpdater( } } - @Override - public void setup(WriteToGraphCallback writeToGraphCallback) { - this.saveResultOnGraph = writeToGraphCallback; - } - @Override public String toString() { return ToStringBuilder.of(VehicleRentalUpdater.class).addObj("source", source).toString(); @@ -139,7 +131,7 @@ protected void runPolling() throws InterruptedException, ExecutionException { stations, geofencingZones ); - processGraphUpdaterResult(saveResultOnGraph.execute(graphWriterRunnable)); + updateGraph(graphWriterRunnable); } private class VehicleRentalGraphWriterRunnable implements GraphWriterRunnable { From 69ac4854678bb964715d4b7d143e457cc6b77dda Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 12 Dec 2024 12:50:17 +0000 Subject: [PATCH 156/269] add test for desired feature flag --- .../updater/spi/PollingGraphUpdaterTest.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 application/src/test/java/org/opentripplanner/updater/spi/PollingGraphUpdaterTest.java diff --git a/application/src/test/java/org/opentripplanner/updater/spi/PollingGraphUpdaterTest.java b/application/src/test/java/org/opentripplanner/updater/spi/PollingGraphUpdaterTest.java new file mode 100644 index 00000000000..6132988d92b --- /dev/null +++ b/application/src/test/java/org/opentripplanner/updater/spi/PollingGraphUpdaterTest.java @@ -0,0 +1,78 @@ +package org.opentripplanner.updater.spi; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.application.OTPFeature; +import org.opentripplanner.updater.GraphWriterRunnable; + +public class PollingGraphUpdaterTest { + + private static final PollingGraphUpdaterParameters config = new PollingGraphUpdaterParameters() { + @Override + public Duration frequency() { + return Duration.ZERO; + } + + @Override + public String configRef() { + return ""; + } + }; + + private static final PollingGraphUpdater subject = new PollingGraphUpdater(config) { + @Override + protected void runPolling() {} + }; + + private boolean updateCompleted; + + @BeforeAll + static void beforeAll() { + subject.setup(runnable -> CompletableFuture.runAsync(() -> runnable.run(null))); + } + + @BeforeEach + void setUp() { + updateCompleted = false; + } + + private final GraphWriterRunnable graphWriterRunnable = context -> { + try { + Thread.sleep(100); + updateCompleted = true; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }; + + @Test + void testUpdateGraphWithWaitFeatureOn() { + OTPFeature.WaitForGraphUpdateInPollingUpdaters.testOn(() -> { + callUpdater(); + assertTrue(updateCompleted); + }); + } + + @Test + void testProcessGraphUpdaterResultWithWaitFeatureOff() { + OTPFeature.WaitForGraphUpdateInPollingUpdaters.testOff(() -> { + callUpdater(); + assertFalse(updateCompleted); + }); + } + + private void callUpdater() { + try { + subject.updateGraph(graphWriterRunnable); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + } +} From 0037b5aab805b8eb719593387881fd242556744c Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 12 Dec 2024 12:55:27 +0000 Subject: [PATCH 157/269] implement feature flag --- .../opentripplanner/updater/spi/PollingGraphUpdater.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java b/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java index 0525406505e..e8faa4b6aba 100644 --- a/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/spi/PollingGraphUpdater.java @@ -3,6 +3,7 @@ import java.time.Duration; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; +import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.updater.GraphWriterRunnable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,6 +115,9 @@ public final void setup(WriteToGraphCallback writeToGraphCallback) { protected final void updateGraph(GraphWriterRunnable task) throws ExecutionException, InterruptedException { - saveResultOnGraph.execute(task).get(); + var result = saveResultOnGraph.execute(task); + if (OTPFeature.WaitForGraphUpdateInPollingUpdaters.isOn()) { + result.get(); + } } } From e5f1f2ff778ccb87046816b12d10008c6b554427 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 12 Dec 2024 13:21:12 +0000 Subject: [PATCH 158/269] 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 159/269] 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 160/269] 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 161/269] 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 162/269] 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 163/269] 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 defdf5fef7c122b509b0a398fed739895417cbb9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 13 Dec 2024 08:02:56 +0100 Subject: [PATCH 164/269] Introduce factory --- .../configure/UpdaterConfigurator.java | 46 +------- .../updater/siri/updater/SiriETUpdater.java | 2 - .../siri/updater/SiriLoaderFactory.java | 34 ------ .../updater/configure/SiriUpdaterModule.java | 107 ++++++++++++++++++ 4 files changed, 112 insertions(+), 77 deletions(-) delete mode 100644 application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLoaderFactory.java create mode 100644 application/src/main/java/org/opentripplanner/updater/siri/updater/configure/SiriUpdaterModule.java diff --git a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 82902f447d3..11be185fa2a 100644 --- a/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/application/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -20,20 +20,16 @@ import org.opentripplanner.updater.UpdatersParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdater; import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; -import org.opentripplanner.updater.siri.updater.SiriETHttpTripUpdateSource; -import org.opentripplanner.updater.siri.updater.SiriETUpdater; import org.opentripplanner.updater.siri.updater.SiriHttpLoader; -import org.opentripplanner.updater.siri.updater.SiriLoaderFactory; import org.opentripplanner.updater.siri.updater.SiriSXUpdater; +import org.opentripplanner.updater.siri.updater.configure.SiriUpdaterModule; import org.opentripplanner.updater.siri.updater.google.SiriETGooglePubsubUpdater; -import org.opentripplanner.updater.siri.updater.lite.SiriETLiteHttpTripUpdateSource; import org.opentripplanner.updater.siri.updater.lite.SiriLiteHttpLoader; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.TimetableSnapshotFlush; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdater; import org.opentripplanner.updater.trip.PollingTripUpdater; import org.opentripplanner.updater.trip.TimetableSnapshotSource; -import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics; import org.opentripplanner.updater.vehicle_parking.AvailabilityDatasourceFactory; import org.opentripplanner.updater.vehicle_parking.VehicleParkingAvailabilityUpdater; import org.opentripplanner.updater.vehicle_parking.VehicleParkingDataSourceFactory; @@ -189,54 +185,22 @@ private List createUpdatersFromConfig() { } for (var configItem : updatersParameters.getSiriETUpdaterParameters()) { updaters.add( - new SiriETUpdater( - configItem, - provideSiriTimetableSnapshot(), - new SiriETHttpTripUpdateSource( - configItem.sourceParameters(), - SiriLoaderFactory.createLoader(configItem) - ), - TripUpdateMetrics.streaming(configItem) - ) + SiriUpdaterModule.createSiriETUpdater(configItem, provideSiriTimetableSnapshot()) ); } for (var configItem : updatersParameters.getSiriETLiteUpdaterParameters()) { updaters.add( - new SiriETUpdater( - configItem, - provideSiriTimetableSnapshot(), - new SiriETLiteHttpTripUpdateSource( - configItem.sourceParameters(), - SiriLoaderFactory.createLoader(configItem) - ), - TripUpdateMetrics.batch(configItem) - ) + SiriUpdaterModule.createSiriETUpdater(configItem, provideSiriTimetableSnapshot()) ); } for (var configItem : updatersParameters.getSiriETGooglePubsubUpdaterParameters()) { updaters.add(new SiriETGooglePubsubUpdater(configItem, provideSiriTimetableSnapshot())); } for (var configItem : updatersParameters.getSiriSXUpdaterParameters()) { - updaters.add( - new SiriSXUpdater( - configItem, - timetableRepository, - new SiriHttpLoader(configItem.url(), configItem.timeout(), configItem.requestHeaders()) - ) - ); + updaters.add(SiriUpdaterModule.createSiriSXUpdater(configItem, timetableRepository)); } for (var configItem : updatersParameters.getSiriSXLiteUpdaterParameters()) { - updaters.add( - new SiriSXUpdater( - configItem, - timetableRepository, - new SiriLiteHttpLoader( - configItem.uri(), - configItem.timeout(), - configItem.requestHeaders() - ) - ) - ); + updaters.add(SiriUpdaterModule.createSiriSXUpdater(configItem, timetableRepository)); } for (var configItem : updatersParameters.getMqttGtfsRealtimeUpdaterParameters()) { updaters.add(new MqttGtfsRealtimeUpdater(configItem, provideGtfsTimetableSnapshot())); diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java index db60b34f55a..663d3ab906b 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriETUpdater.java @@ -3,8 +3,6 @@ import java.util.List; import java.util.function.Consumer; import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; -import org.opentripplanner.updater.siri.updater.lite.SiriETLiteHttpTripUpdateSource; -import org.opentripplanner.updater.siri.updater.lite.SiriETLiteUpdaterParameters; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.PollingGraphUpdaterParameters; import org.opentripplanner.updater.spi.ResultLogger; diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLoaderFactory.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLoaderFactory.java deleted file mode 100644 index b9b43d9341e..00000000000 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/SiriLoaderFactory.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.opentripplanner.updater.siri.updater; - -import org.opentripplanner.updater.siri.updater.lite.SiriETLiteUpdaterParameters; -import org.opentripplanner.updater.siri.updater.lite.SiriLiteHttpLoader; - -/** - * Constructs a SiriLoader from the parameters of the updater. - */ -public class SiriLoaderFactory { - - public static SiriLoader createLoader(SiriETUpdater.Parameters parameters) { - // Load real-time updates from a file. - if (SiriFileLoader.matchesUrl(parameters.url())) { - return new SiriFileLoader(parameters.url()); - } - // Fallback to default loader - else { - return switch (parameters) { - case SiriETUpdaterParameters p -> new SiriHttpLoader( - p.url(), - p.timeout(), - p.httpRequestHeaders(), - p.previewInterval() - ); - case SiriETLiteUpdaterParameters p -> new SiriLiteHttpLoader( - p.uri(), - p.timeout(), - p.httpRequestHeaders() - ); - default -> throw new IllegalArgumentException("Unexpected value: " + parameters); - }; - } - } -} diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/configure/SiriUpdaterModule.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/configure/SiriUpdaterModule.java new file mode 100644 index 00000000000..2553bd3c67e --- /dev/null +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/configure/SiriUpdaterModule.java @@ -0,0 +1,107 @@ +package org.opentripplanner.updater.siri.updater.configure; + +import java.util.function.Consumer; +import org.opentripplanner.transit.service.TimetableRepository; +import org.opentripplanner.updater.siri.SiriTimetableSnapshotSource; +import org.opentripplanner.updater.siri.updater.EstimatedTimetableSource; +import org.opentripplanner.updater.siri.updater.SiriETHttpTripUpdateSource; +import org.opentripplanner.updater.siri.updater.SiriETUpdater; +import org.opentripplanner.updater.siri.updater.SiriETUpdaterParameters; +import org.opentripplanner.updater.siri.updater.SiriFileLoader; +import org.opentripplanner.updater.siri.updater.SiriHttpLoader; +import org.opentripplanner.updater.siri.updater.SiriLoader; +import org.opentripplanner.updater.siri.updater.SiriSXUpdater; +import org.opentripplanner.updater.siri.updater.SiriSXUpdaterParameters; +import org.opentripplanner.updater.siri.updater.lite.SiriETLiteHttpTripUpdateSource; +import org.opentripplanner.updater.siri.updater.lite.SiriETLiteUpdaterParameters; +import org.opentripplanner.updater.siri.updater.lite.SiriLiteHttpLoader; +import org.opentripplanner.updater.siri.updater.lite.SiriSXLiteUpdaterParameters; +import org.opentripplanner.updater.spi.UpdateResult; +import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics; + +/** + * Dependency injection for instantiating SIRI-ET and SX updaters. + */ +public class SiriUpdaterModule { + + public static SiriETUpdater createSiriETUpdater( + SiriETUpdater.Parameters params, + SiriTimetableSnapshotSource timetableSnapshotSource + ) { + return new SiriETUpdater( + params, + timetableSnapshotSource, + createSource(params), + createMetricsConsumer(params) + ); + } + + public static SiriSXUpdater createSiriSXUpdater( + SiriSXUpdater.Parameters params, + TimetableRepository timetableRepository + ) { + return new SiriSXUpdater(params, timetableRepository, createLoader(params)); + } + + private static SiriLoader createLoader(SiriSXUpdater.Parameters params) { + return switch (params) { + case SiriSXUpdaterParameters p -> new SiriHttpLoader( + p.url(), + p.timeout(), + p.requestHeaders() + ); + case SiriSXLiteUpdaterParameters p -> new SiriLiteHttpLoader( + p.uri(), + p.timeout(), + p.requestHeaders() + ); + default -> throw new IllegalArgumentException("Unexpected value: " + params); + }; + } + + private static EstimatedTimetableSource createSource(SiriETUpdater.Parameters params) { + return switch (params) { + case SiriETUpdaterParameters p -> new SiriETHttpTripUpdateSource( + p.sourceParameters(), + createLoader(params) + ); + case SiriETLiteUpdaterParameters p -> new SiriETLiteHttpTripUpdateSource( + p.sourceParameters(), + createLoader(params) + ); + default -> throw new IllegalArgumentException("Unexpected value: " + params); + }; + } + + private static SiriLoader createLoader(SiriETUpdater.Parameters params) { + // Load real-time updates from a file. + if (SiriFileLoader.matchesUrl(params.url())) { + return new SiriFileLoader(params.url()); + } + // Fallback to default loader + else { + return switch (params) { + case SiriETUpdaterParameters p -> new SiriHttpLoader( + p.url(), + p.timeout(), + p.httpRequestHeaders(), + p.previewInterval() + ); + case SiriETLiteUpdaterParameters p -> new SiriLiteHttpLoader( + p.uri(), + p.timeout(), + p.httpRequestHeaders() + ); + default -> throw new IllegalArgumentException("Unexpected value: " + params); + }; + } + } + + private static Consumer createMetricsConsumer(SiriETUpdater.Parameters params) { + return switch (params) { + case SiriETUpdaterParameters p -> TripUpdateMetrics.streaming(p); + case SiriETLiteUpdaterParameters p -> TripUpdateMetrics.batch(p); + default -> throw new IllegalArgumentException("Unexpected value: " + params); + }; + } +} From 4c6db7fa912eebce17adcc8ae06ae184b49bd67c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 13 Dec 2024 08:04:25 +0100 Subject: [PATCH 165/269] 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 166/269] 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 167/269] 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 168/269] 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 169/269] 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 170/269] 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 171/269] 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 172/269] 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 173/269] 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 174/269] 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 175/269] 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 176/269] 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 177/269] 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 178/269] 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 179/269] 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 3825a024000a7bc576ab2f5a19b44bb1dd333822 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 17 Dec 2024 01:32:02 +0100 Subject: [PATCH 180/269] feature: Create a repository to store OSM info for other graph build modules The Repository and Service is not available during routing, only during the graph build. --- .../graph_builder/GraphBuilder.java | 8 +++- .../module/OsmBoardingLocationsModule.java | 9 +++- .../module/configure/GraphBuilderFactory.java | 7 ++- .../module/configure/GraphBuilderModules.java | 8 ++-- .../graph_builder/module/osm/OsmModule.java | 23 +++++++--- .../module/osm/OsmModuleBuilder.java | 5 +++ .../routing/graph/SerializedGraphObject.java | 7 +++ .../osminfo/OsmInfoGraphBuildRepository.java | 23 ++++++++++ .../osminfo/OsmInfoGraphBuildService.java | 22 +++++++++ .../OsmInfoGraphBuildRepositoryModule.java | 12 +++++ .../OsmInfoGraphBuildServiceModule.java | 12 +++++ .../DefaultOsmInfoGraphBuildRepository.java | 37 +++++++++++++++ .../DefaultOsmInfoGraphBuildService.java | 28 ++++++++++++ .../osminfo/model/OsmWayReferences.java | 26 +++++++++++ .../opentripplanner/standalone/OTPMain.java | 1 + .../configure/ConstructApplication.java | 15 ++++++- .../ConstructApplicationFactory.java | 18 ++++---- .../standalone/configure/LoadApplication.java | 7 ++- .../configure/LoadApplicationFactory.java | 6 +++ .../opentripplanner/ConstantsForTests.java | 22 +++++---- .../OsmBoardingLocationsModuleTest.java | 9 +++- .../islandpruning/IslandPruningUtils.java | 9 ++-- .../module/linking/LinkingTest.java | 13 ++++-- .../module/osm/OsmModuleTest.java | 45 +++++++++++++------ .../module/osm/PlatformLinkerTest.java | 12 +++-- .../module/osm/TriangleInequalityTest.java | 8 +++- .../module/osm/UnconnectedAreasTest.java | 8 +++- .../module/osm/UnroutableTest.java | 8 +++- .../routing/graph/GraphSerializationTest.java | 8 ++++ 29 files changed, 355 insertions(+), 61 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java create mode 100644 application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java create mode 100644 application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildRepositoryModule.java create mode 100644 application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildServiceModule.java create mode 100644 application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java create mode 100644 application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java create mode 100644 application/src/main/java/org/opentripplanner/service/osminfo/model/OsmWayReferences.java diff --git a/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index 0b071b64728..744a8209702 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -17,7 +17,9 @@ import org.opentripplanner.graph_builder.issue.api.DataImportIssueSummary; import org.opentripplanner.graph_builder.model.GraphBuilderModule; import org.opentripplanner.graph_builder.module.configure.DaggerGraphBuilderFactory; +import org.opentripplanner.graph_builder.module.configure.GraphBuilderFactory; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository; import org.opentripplanner.standalone.config.BuildConfig; @@ -62,6 +64,7 @@ public static GraphBuilder create( BuildConfig config, GraphBuilderDataSources dataSources, Graph graph, + OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, TimetableRepository timetableRepository, WorldEnvelopeRepository worldEnvelopeRepository, VehicleParkingRepository vehicleParkingService, @@ -78,10 +81,11 @@ public static GraphBuilder create( timetableRepository.initTimeZone(config.transitModelTimeZone); - var builder = DaggerGraphBuilderFactory - .builder() + GraphBuilderFactory.Builder builder = DaggerGraphBuilderFactory.builder(); + builder .config(config) .graph(graph) + .osmInfoGraphBuildRepository(osmInfoGraphBuildRepository) .timetableRepository(timetableRepository) .worldEnvelopeRepository(worldEnvelopeRepository) .vehicleParkingRepository(vehicleParkingService) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java index c4acabefd6c..8344b3a3273 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java @@ -13,6 +13,7 @@ import org.opentripplanner.routing.graph.index.StreetIndex; import org.opentripplanner.routing.linking.LinkingDirection; import org.opentripplanner.routing.linking.VertexLinker; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildService; import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; @@ -55,14 +56,20 @@ public class OsmBoardingLocationsModule implements GraphBuilderModule { private final Graph graph; + private final OsmInfoGraphBuildService osmInfoGraphBuildService; private final TimetableRepository timetableRepository; private final VertexFactory vertexFactory; private VertexLinker linker; @Inject - public OsmBoardingLocationsModule(Graph graph, TimetableRepository timetableRepository) { + public OsmBoardingLocationsModule( + Graph graph, + OsmInfoGraphBuildService osmInfoGraphBuildService, + TimetableRepository timetableRepository + ) { this.graph = graph; + this.osmInfoGraphBuildService = osmInfoGraphBuildService; this.timetableRepository = timetableRepository; this.vertexFactory = new VertexFactory(graph); } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java index d4d00fdc2a0..4155ecf9614 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java @@ -30,6 +30,8 @@ import org.opentripplanner.gtfs.graphbuilder.GtfsModule; import org.opentripplanner.netex.NetexModule; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; +import org.opentripplanner.service.osminfo.configure.OsmInfoGraphBuildServiceModule; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository; import org.opentripplanner.standalone.config.BuildConfig; @@ -37,7 +39,7 @@ import org.opentripplanner.transit.service.TimetableRepository; @Singleton -@Component(modules = { GraphBuilderModules.class }) +@Component(modules = { GraphBuilderModules.class, OsmInfoGraphBuildServiceModule.class }) public interface GraphBuilderFactory { //DataImportIssueStore issueStore(); GraphBuilder graphBuilder(); @@ -80,6 +82,9 @@ interface Builder { @BindsInstance Builder timetableRepository(TimetableRepository timetableRepository); + @BindsInstance + Builder osmInfoGraphBuildRepository(OsmInfoGraphBuildRepository osmInfoGraphBuildRepository); + @BindsInstance Builder worldEnvelopeRepository(WorldEnvelopeRepository worldEnvelopeRepository); diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java index 5371142d612..46c74b52a2d 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java @@ -43,13 +43,14 @@ import org.opentripplanner.osm.OsmProvider; import org.opentripplanner.routing.api.request.preference.WalkPreferences; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.standalone.config.BuildConfig; import org.opentripplanner.street.model.StreetLimitationParameters; import org.opentripplanner.transit.service.TimetableRepository; /** - * Configure all modules which is not simple enough to be injected. + * Configure all modules that are not simple enough to be injected. */ @Module public class GraphBuilderModules { @@ -60,7 +61,8 @@ static OsmModule provideOsmModule( GraphBuilderDataSources dataSources, BuildConfig config, Graph graph, - VehicleParkingRepository parkingService, + OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, + VehicleParkingRepository vehicleParkingRepository, DataImportIssueStore issueStore, StreetLimitationParameters streetLimitationParameters ) { @@ -78,7 +80,7 @@ static OsmModule provideOsmModule( } return OsmModule - .of(providers, graph, parkingService) + .of(providers, graph, osmInfoGraphBuildRepository, vehicleParkingRepository) .withEdgeNamer(config.edgeNamer) .withAreaVisibility(config.areaVisibility) .withPlatformEntriesLinking(config.platformEntriesLinking) 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 db495905041..00626d1e221 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 @@ -25,6 +25,8 @@ import org.opentripplanner.osm.wayproperty.WayProperties; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.util.ElevationUtils; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; +import org.opentripplanner.service.osminfo.model.OsmWayReferences; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.vehicleparking.model.VehicleParking; import org.opentripplanner.street.model.StreetLimitationParameters; @@ -52,7 +54,10 @@ public class OsmModule implements GraphBuilderModule { */ private final List providers; private final Graph graph; + // TODO: Use this to store edge stop references + private final OsmInfoGraphBuildRepository osmInfoGraphBuildRepository; private final VehicleParkingRepository parkingRepository; + private final DataImportIssueStore issueStore; private final OsmProcessingParameters params; private final SafetyValueNormalizer normalizer; @@ -63,36 +68,40 @@ public class OsmModule implements GraphBuilderModule { OsmModule( Collection providers, Graph graph, - VehicleParkingRepository parkingService, + OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, + VehicleParkingRepository parkingRepository, DataImportIssueStore issueStore, StreetLimitationParameters streetLimitationParameters, OsmProcessingParameters params ) { this.providers = List.copyOf(providers); this.graph = graph; + this.osmInfoGraphBuildRepository = osmInfoGraphBuildRepository; + this.parkingRepository = parkingRepository; this.issueStore = issueStore; this.params = params; this.osmdb = new OsmDatabase(issueStore); this.vertexGenerator = new VertexGenerator(osmdb, graph, params.boardingAreaRefTags()); this.normalizer = new SafetyValueNormalizer(graph, issueStore); this.streetLimitationParameters = Objects.requireNonNull(streetLimitationParameters); - this.parkingRepository = parkingService; } public static OsmModuleBuilder of( Collection providers, Graph graph, - VehicleParkingRepository service + OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, + VehicleParkingRepository vehicleParkingRepository ) { - return new OsmModuleBuilder(providers, graph, service); + return new OsmModuleBuilder(providers, graph, osmInfoGraphBuildRepository, vehicleParkingRepository); } public static OsmModuleBuilder of( OsmProvider provider, Graph graph, - VehicleParkingRepository service + OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, + VehicleParkingRepository vehicleParkingRepository ) { - return of(List.of(provider), graph, service); + return of(List.of(provider), graph, osmInfoGraphBuildRepository, vehicleParkingRepository); } @Override @@ -408,6 +417,8 @@ private void buildBasicGraph() { StreetEdge backStreet = streets.back(); normalizer.applyWayProperties(street, backStreet, wayData, way); + osmInfoGraphBuildRepository.addReferences(street, new OsmWayReferences(List.of(street.toString()))); + applyEdgesToTurnRestrictions(way, startNode, endNode, street, backStreet); startNode = endNode; osmStartNode = osmdb.getNode(startNode); diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java index 2f7f4c506c9..f7038a40c74 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java @@ -8,6 +8,7 @@ import org.opentripplanner.graph_builder.services.osm.EdgeNamer; import org.opentripplanner.osm.OsmProvider; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.street.model.StreetLimitationParameters; @@ -19,6 +20,7 @@ public class OsmModuleBuilder { private final Collection providers; private final Graph graph; private final VehicleParkingRepository parkingRepository; + private final OsmInfoGraphBuildRepository osmInfoGraphBuildRepository; private Set boardingAreaRefTags = Set.of(); private DataImportIssueStore issueStore = DataImportIssueStore.NOOP; private EdgeNamer edgeNamer = new DefaultNamer(); @@ -32,10 +34,12 @@ public class OsmModuleBuilder { OsmModuleBuilder( Collection providers, Graph graph, + OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, VehicleParkingRepository parkingRepository ) { this.providers = providers; this.graph = graph; + this.osmInfoGraphBuildRepository = osmInfoGraphBuildRepository; this.parkingRepository = parkingRepository; } @@ -88,6 +92,7 @@ public OsmModule build() { return new OsmModule( providers, graph, + osmInfoGraphBuildRepository, parkingRepository, issueStore, streetLimitationParameters, diff --git a/application/src/main/java/org/opentripplanner/routing/graph/SerializedGraphObject.java b/application/src/main/java/org/opentripplanner/routing/graph/SerializedGraphObject.java index a152b96682d..8565952e557 100644 --- a/application/src/main/java/org/opentripplanner/routing/graph/SerializedGraphObject.java +++ b/application/src/main/java/org/opentripplanner/routing/graph/SerializedGraphObject.java @@ -25,6 +25,7 @@ import org.opentripplanner.model.projectinfo.GraphFileHeader; import org.opentripplanner.model.projectinfo.OtpProjectInfo; import org.opentripplanner.routing.graph.kryosupport.KryoBuilder; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository; import org.opentripplanner.standalone.config.BuildConfig; @@ -56,6 +57,10 @@ public class SerializedGraphObject implements Serializable { private static final Logger LOG = LoggerFactory.getLogger(SerializedGraphObject.class); public final Graph graph; + + @Nullable + public final OsmInfoGraphBuildRepository osmInfoGraphBuildRepository; + public final TimetableRepository timetableRepository; public final WorldEnvelopeRepository worldEnvelopeRepository; private final Collection edges; @@ -84,6 +89,7 @@ public class SerializedGraphObject implements Serializable { public SerializedGraphObject( Graph graph, + @Nullable OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, TimetableRepository timetableRepository, WorldEnvelopeRepository worldEnvelopeRepository, VehicleParkingRepository parkingRepository, @@ -96,6 +102,7 @@ public SerializedGraphObject( ) { this.graph = graph; this.edges = graph.getEdges(); + this.osmInfoGraphBuildRepository = osmInfoGraphBuildRepository; this.timetableRepository = timetableRepository; this.worldEnvelopeRepository = worldEnvelopeRepository; this.parkingRepository = parkingRepository; diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java new file mode 100644 index 00000000000..c8a2b908893 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java @@ -0,0 +1,23 @@ +package org.opentripplanner.service.osminfo; + +import java.io.Serializable; +import java.util.Optional; +import org.opentripplanner.service.osminfo.model.OsmWayReferences; +import org.opentripplanner.street.model.edge.Edge; + +/** + * Store OSM data used during graph build, but after the OSM Graph Builder is done. + *

+ * This is a repository to support the {@link OsmInfoGraphBuildService}. + */ +public interface OsmInfoGraphBuildRepository extends Serializable { + /** + * TODO Add doc + */ + void addReferences(Edge edge, OsmWayReferences info); + + /** + * TODO Add doc + */ + Optional findReferences(Edge edge); +} diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java new file mode 100644 index 00000000000..784867ada30 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java @@ -0,0 +1,22 @@ +package org.opentripplanner.service.osminfo; + +import java.util.Optional; +import org.opentripplanner.service.osminfo.model.OsmWayReferences; +import org.opentripplanner.street.model.edge.Edge; + +/** + * The responsibility of this service is to provide information from Open Street Map, which + * is NOT in the OTP street graph. The graph build happens in phases, and some data is read in + * from the OSM files, but needed later on. For example, we might need info from OSM to link street + * edges/vertexes with transit stops/platforms. We do not want to put data in the OTP street graph + * unless it is relevant for routing. So, for information that is read by the OsmGraphBuilder, but + * needed later on, we have this service. + * + * THIS SERVICE IS ONLY AVAILABLE DURING GRAPH BUILD, NOT DURING ROUTING. * + */ +public interface OsmInfoGraphBuildService { + /** + * TODO Add doc + */ + Optional findReferences(Edge edge); +} diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildRepositoryModule.java b/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildRepositoryModule.java new file mode 100644 index 00000000000..d8b9db5608e --- /dev/null +++ b/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildRepositoryModule.java @@ -0,0 +1,12 @@ +package org.opentripplanner.service.osminfo.configure; + +import dagger.Binds; +import dagger.Module; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; + +@Module +public interface OsmInfoGraphBuildRepositoryModule { + @Binds + OsmInfoGraphBuildRepository bind(DefaultOsmInfoGraphBuildRepository repository); +} diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildServiceModule.java b/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildServiceModule.java new file mode 100644 index 00000000000..c6ac5c31ec9 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildServiceModule.java @@ -0,0 +1,12 @@ +package org.opentripplanner.service.osminfo.configure; + +import dagger.Binds; +import dagger.Module; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildService; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildService; + +@Module +public interface OsmInfoGraphBuildServiceModule { + @Binds + OsmInfoGraphBuildService bind(DefaultOsmInfoGraphBuildService service); +} diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java new file mode 100644 index 00000000000..4ba575dc8b0 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java @@ -0,0 +1,37 @@ +package org.opentripplanner.service.osminfo.internal; + +import jakarta.inject.Inject; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; +import org.opentripplanner.service.osminfo.model.OsmWayReferences; +import org.opentripplanner.street.model.edge.Edge; + +public class DefaultOsmInfoGraphBuildRepository + implements OsmInfoGraphBuildRepository, Serializable { + + private final Map references = new HashMap<>(); + + @Inject + public DefaultOsmInfoGraphBuildRepository() {} + + @Override + public void addReferences(Edge edge, OsmWayReferences info) { + Objects.requireNonNull(edge); + Objects.requireNonNull(info); + this.references.put(edge, info); + } + + @Override + public Optional findReferences(Edge edge) { + return Optional.ofNullable(references.get(edge)); + } + + @Override + public String toString() { + return "DefaultOsmInfoGraphBuildRepository{references size = " + references.size() + "}"; + } +} diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java new file mode 100644 index 00000000000..29271e17679 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java @@ -0,0 +1,28 @@ +package org.opentripplanner.service.osminfo.internal; + +import jakarta.inject.Inject; +import java.util.Optional; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildService; +import org.opentripplanner.service.osminfo.model.OsmWayReferences; +import org.opentripplanner.street.model.edge.Edge; + +public class DefaultOsmInfoGraphBuildService implements OsmInfoGraphBuildService { + + private final OsmInfoGraphBuildRepository repository; + + @Inject + public DefaultOsmInfoGraphBuildService(OsmInfoGraphBuildRepository repository) { + this.repository = repository; + } + + @Override + public Optional findReferences(Edge edge) { + return repository.findReferences(edge); + } + + @Override + public String toString() { + return "DefaultOsmInfoGraphBuildService{ repository=" + repository + '}'; + } +} diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/model/OsmWayReferences.java b/application/src/main/java/org/opentripplanner/service/osminfo/model/OsmWayReferences.java new file mode 100644 index 00000000000..6208758f153 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/service/osminfo/model/OsmWayReferences.java @@ -0,0 +1,26 @@ +package org.opentripplanner.service.osminfo.model; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + +/** + * TODO : Add java doc + */ +public class OsmWayReferences implements Serializable { + + private final List references; + + public OsmWayReferences(Collection references) { + this.references = references.stream().sorted().distinct().toList(); + } + + /** + * TODO : Add java doc + * + * returns a sorted distinct list of references + */ + public List references() { + return references; + } +} \ No newline at end of file diff --git a/application/src/main/java/org/opentripplanner/standalone/OTPMain.java b/application/src/main/java/org/opentripplanner/standalone/OTPMain.java index ade5067a981..25eea6df473 100644 --- a/application/src/main/java/org/opentripplanner/standalone/OTPMain.java +++ b/application/src/main/java/org/opentripplanner/standalone/OTPMain.java @@ -150,6 +150,7 @@ private static void startOTPServer(CommandLineParameters cli) { // with using the embedded router config. new SerializedGraphObject( app.graph(), + app.osmInfoGraphBuildRepository(), app.timetableRepository(), app.worldEnvelopeRepository(), app.vehicleParkingRepository(), 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 b4edbb36299..ebb83a045d0 100644 --- a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java +++ b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java @@ -18,6 +18,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerMapper; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.vehicleparking.VehicleParkingService; @@ -64,6 +65,11 @@ public class ConstructApplication { private final CommandLineParameters cli; private final GraphBuilderDataSources graphBuilderDataSources; + /** + * The OSM Info is injected into the graph-builder, but not the web-server; Hence not part of + * the application context. + */ + private final OsmInfoGraphBuildRepository osmInfoGraphBuildRepository; private final ConstructApplicationFactory factory; /** @@ -72,6 +78,7 @@ public class ConstructApplication { ConstructApplication( CommandLineParameters cli, Graph graph, + OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, TimetableRepository timetableRepository, WorldEnvelopeRepository worldEnvelopeRepository, ConfigModel config, @@ -84,6 +91,7 @@ public class ConstructApplication { ) { this.cli = cli; this.graphBuilderDataSources = graphBuilderDataSources; + this.osmInfoGraphBuildRepository = osmInfoGraphBuildRepository; // We create the optional GraphVisualizer here, because it would be significant more complex to // use Dagger DI to do it - passing in a parameter to enable it or not. @@ -130,7 +138,8 @@ public GraphBuilder createGraphBuilder() { buildConfig(), graphBuilderDataSources, graph(), - timetableRepository(), + osmInfoGraphBuildRepository, + factory.timetableRepository(), factory.worldEnvelopeRepository(), factory.vehicleParkingRepository(), factory.emissionsDataModel(), @@ -261,6 +270,10 @@ public DataImportIssueSummary dataImportIssueSummary() { return factory.dataImportIssueSummary(); } + public OsmInfoGraphBuildRepository osmInfoGraphBuildRepository() { + return osmInfoGraphBuildRepository; + } + public StopConsolidationRepository stopConsolidationRepository() { return factory.stopConsolidationRepository(); } diff --git a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java index d6310c0c616..3d479f0fa63 100644 --- a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java +++ b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java @@ -51,21 +51,21 @@ @Component( modules = { ConfigModule.class, - TransitModule.class, - WorldEnvelopeServiceModule.class, + ConstructApplicationModule.class, + EmissionsServiceModule.class, + GeocoderModule.class, + InteractiveLauncherModule.class, RealtimeVehicleServiceModule.class, RealtimeVehicleRepositoryModule.class, - VehicleRentalServiceModule.class, - VehicleRentalRepositoryModule.class, - VehicleParkingServiceModule.class, - ConstructApplicationModule.class, RideHailingServicesModule.class, - EmissionsServiceModule.class, + TransitModule.class, + VehicleParkingServiceModule.class, + VehicleRentalRepositoryModule.class, + VehicleRentalServiceModule.class, SorlandsbanenNorwayModule.class, StopConsolidationServiceModule.class, - InteractiveLauncherModule.class, StreetLimitationParametersServiceModule.class, - GeocoderModule.class, + WorldEnvelopeServiceModule.class, } ) public interface ConstructApplicationFactory { diff --git a/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplication.java b/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplication.java index 021af778345..ad1a7293855 100644 --- a/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplication.java +++ b/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplication.java @@ -8,6 +8,7 @@ import org.opentripplanner.graph_builder.issue.api.DataImportIssueSummary; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graph.SerializedGraphObject; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository; import org.opentripplanner.standalone.config.CommandLineParameters; @@ -20,7 +21,7 @@ * This is used to load the graph, and finally this class can create the * {@link ConstructApplication} for the next phase. *

- * By splitting the these two responsibilities into two separate phases we are sure all + * By splitting these two responsibilities into two separate phases we are sure all * components (graph and transit model) created in the load phase will be available for * creating the application using Dagger dependency injection. */ @@ -55,6 +56,7 @@ public DataSource getInputGraphDataStore() { public ConstructApplication appConstruction(SerializedGraphObject obj) { return createAppConstruction( obj.graph, + obj.osmInfoGraphBuildRepository, obj.timetableRepository, obj.worldEnvelopeRepository, obj.parkingRepository, @@ -69,6 +71,7 @@ public ConstructApplication appConstruction(SerializedGraphObject obj) { public ConstructApplication appConstruction() { return createAppConstruction( factory.emptyGraph(), + factory.emptyOsmInfoGraphBuildRepository(), factory.emptyTimetableRepository(), factory.emptyWorldEnvelopeRepository(), factory.emptyVehicleParkingRepository(), @@ -92,6 +95,7 @@ public ConfigModel config() { private ConstructApplication createAppConstruction( Graph graph, + OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, TimetableRepository timetableRepository, WorldEnvelopeRepository worldEnvelopeRepository, VehicleParkingRepository parkingRepository, @@ -103,6 +107,7 @@ private ConstructApplication createAppConstruction( return new ConstructApplication( cli, graph, + osmInfoGraphBuildRepository, timetableRepository, worldEnvelopeRepository, config(), diff --git a/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplicationFactory.java b/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplicationFactory.java index b054fac3ca5..9fdbf59bfda 100644 --- a/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplicationFactory.java +++ b/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplicationFactory.java @@ -11,6 +11,8 @@ import org.opentripplanner.ext.stopconsolidation.configure.StopConsolidationRepositoryModule; import org.opentripplanner.graph_builder.GraphBuilderDataSources; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; +import org.opentripplanner.service.osminfo.configure.OsmInfoGraphBuildRepositoryModule; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.vehicleparking.configure.VehicleParkingRepositoryModule; import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository; @@ -30,6 +32,7 @@ LoadConfigModule.class, DataStoreModule.class, GsDataSourceModule.class, + OsmInfoGraphBuildRepositoryModule.class, WorldEnvelopeRepositoryModule.class, StopConsolidationRepositoryModule.class, VehicleParkingRepositoryModule.class, @@ -43,6 +46,9 @@ public interface LoadApplicationFactory { @Singleton Graph emptyGraph(); + @Singleton + OsmInfoGraphBuildRepository emptyOsmInfoGraphBuildRepository(); + @Singleton TimetableRepository emptyTimetableRepository(); diff --git a/application/src/test/java/org/opentripplanner/ConstantsForTests.java b/application/src/test/java/org/opentripplanner/ConstantsForTests.java index e5ab48cee54..3f188ff2c89 100644 --- a/application/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/application/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -34,6 +34,7 @@ import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.linking.LinkingDirection; import org.opentripplanner.routing.linking.VertexLinker; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; @@ -137,9 +138,11 @@ public static TestOtpModel buildNewPortlandGraph(boolean withElevation) { var timetableRepository = new TimetableRepository(new SiteRepository(), deduplicator); // Add street data from OSM { - OsmProvider osmProvider = new OsmProvider(PORTLAND_CENTRAL_OSM, false); - OsmModule osmModule = OsmModule - .of(osmProvider, graph, new DefaultVehicleParkingRepository()) + var osmProvider = new OsmProvider(PORTLAND_CENTRAL_OSM, false); + var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository(); + var vehicleParkingRepository = new DefaultVehicleParkingRepository(); + var osmModule = OsmModule + .of(osmProvider, graph, osmInfoRepository, vehicleParkingRepository) .withStaticParkAndRide(true) .withStaticBikeParkAndRide(true) .build(); @@ -195,9 +198,11 @@ public static TestOtpModel buildOsmGraph(File osmFile) { var graph = new Graph(deduplicator); var timetableRepository = new TimetableRepository(siteRepository, deduplicator); // Add street data from OSM - OsmProvider osmProvider = new OsmProvider(osmFile, true); - OsmModule osmModule = OsmModule - .of(osmProvider, graph, new DefaultVehicleParkingRepository()) + var osmProvider = new OsmProvider(osmFile, true); + var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository(); + var vehicleParkingRepository = new DefaultVehicleParkingRepository(); + var osmModule = OsmModule + .of(osmProvider, graph, osmInfoRepository, vehicleParkingRepository) .build(); osmModule.buildGraph(); return new TestOtpModel(graph, timetableRepository); @@ -245,8 +250,9 @@ public static TestOtpModel buildNewMinimalNetexGraph() { var timetableRepository = new TimetableRepository(siteRepository, deduplicator); // Add street data from OSM { - OsmProvider osmProvider = new OsmProvider(OSLO_EAST_OSM, false); - OsmModule osmModule = OsmModule.of(osmProvider, graph, parkingService).build(); + var osmProvider = new OsmProvider(OSLO_EAST_OSM, false); + var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository(); + var osmModule = OsmModule.of(osmProvider, graph, osmInfoRepository, parkingService).build(); osmModule.buildGraph(); } // Add transit data from Netex diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java index c55e482e533..18c40bac898 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java @@ -14,6 +14,8 @@ import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.osm.OsmProvider; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildService; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; @@ -83,8 +85,10 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti Set.of(floatingBusVertex.getStop().getId().getId()), new NonLocalizedString("bus stop not connected to street network") ); + var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository(); + var vehicleParkingRepository = new DefaultVehicleParkingRepository(); var osmModule = OsmModule - .of(provider, graph, new DefaultVehicleParkingRepository()) + .of(provider, graph, osmInfoRepository, vehicleParkingRepository) .withBoardingAreaRefTags(Set.of("ref", "ref:IFOPT")) .withAreaVisibility(areaVisibility) .build(); @@ -107,7 +111,8 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti assertEquals(0, platformVertex.getIncoming().size()); assertEquals(0, platformVertex.getOutgoing().size()); - new OsmBoardingLocationsModule(graph, timetableRepository).buildGraph(); + var osmService = new DefaultOsmInfoGraphBuildService(osmInfoRepository); + new OsmBoardingLocationsModule(graph, osmService, timetableRepository).buildGraph(); var boardingLocations = graph.getVerticesOfType(OsmBoardingLocationVertex.class); assertEquals(5, boardingLocations.size()); // 3 nodes connected to the street network, plus one "floating" and one area centroid created by the module diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/IslandPruningUtils.java b/application/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/IslandPruningUtils.java index d71a60a972e..8e5a455095b 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/IslandPruningUtils.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/IslandPruningUtils.java @@ -5,6 +5,7 @@ import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.osm.OsmProvider; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.service.SiteRepository; @@ -24,9 +25,11 @@ static Graph buildOsmGraph( var graph = new Graph(deduplicator); var timetableRepository = new TimetableRepository(new SiteRepository(), deduplicator); // Add street data from OSM - OsmProvider osmProvider = new OsmProvider(osmFile, true); - OsmModule osmModule = OsmModule - .of(osmProvider, graph, new DefaultVehicleParkingRepository()) + var osmProvider = new OsmProvider(osmFile, true); + var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository(); + var vehicleParkingRepository = new DefaultVehicleParkingRepository(); + var osmModule = OsmModule + .of(osmProvider, graph, osmInfoRepository, vehicleParkingRepository) .withEdgeNamer(new TestNamer()) .build(); diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java index a6afa89707f..6c51235e703 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java @@ -21,6 +21,7 @@ import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.osm.OsmProvider; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model._data.StreetModelForTest; @@ -152,16 +153,20 @@ public void testStopsLinkedIdentically() { public static TestOtpModel buildGraphNoTransit() { var deduplicator = new Deduplicator(); var siteRepository = new SiteRepository(); - var gg = new Graph(deduplicator); + var graph = new Graph(deduplicator); var timetableRepository = new TimetableRepository(siteRepository, deduplicator); File file = ResourceLoader.of(LinkingTest.class).file("columbus.osm.pbf"); - OsmProvider provider = new OsmProvider(file, false); + var provider = new OsmProvider(file, false); + var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository(); + var vehicleParkingRepository = new DefaultVehicleParkingRepository(); - OsmModule osmModule = OsmModule.of(provider, gg, new DefaultVehicleParkingRepository()).build(); + var osmModule = OsmModule + .of(provider, graph, osmInfoRepository, vehicleParkingRepository) + .build(); osmModule.buildGraph(); - return new TestOtpModel(gg, timetableRepository); + return new TestOtpModel(graph, timetableRepository); } private static List outgoingStls(final TransitStopVertex tsv) { diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 833b14ade9d..6de345ddd9c 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -32,6 +32,7 @@ import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.impl.GraphPathFinder; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingService; @@ -53,30 +54,35 @@ public class OsmModuleTest { @Test public void testGraphBuilder() { var deduplicator = new Deduplicator(); - var gg = new Graph(deduplicator); + var graph = new Graph(deduplicator); File file = RESOURCE_LOADER.file("map.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); OsmModule osmModule = OsmModule - .of(provider, gg, new DefaultVehicleParkingRepository()) + .of( + provider, + graph, + new DefaultOsmInfoGraphBuildRepository(), + new DefaultVehicleParkingRepository() + ) .withAreaVisibility(true) .build(); osmModule.buildGraph(); // Kamiennogorska at south end of segment - Vertex v1 = gg.getVertex(VertexLabel.osm(280592578)); + Vertex v1 = graph.getVertex(VertexLabel.osm(280592578)); // Kamiennogorska at Mariana Smoluchowskiego - Vertex v2 = gg.getVertex(VertexLabel.osm(288969929)); + Vertex v2 = graph.getVertex(VertexLabel.osm(288969929)); // Mariana Smoluchowskiego, north end - Vertex v3 = gg.getVertex(VertexLabel.osm(280107802)); + Vertex v3 = graph.getVertex(VertexLabel.osm(280107802)); // Mariana Smoluchowskiego, south end (of segment connected to v2) - Vertex v4 = gg.getVertex(VertexLabel.osm(288970952)); + Vertex v4 = graph.getVertex(VertexLabel.osm(288970952)); assertNotNull(v1); assertNotNull(v2); @@ -117,9 +123,11 @@ public void testBuildGraphDetailed() { var gg = new Graph(deduplicator); File file = RESOURCE_LOADER.file("NYC_small.osm.pbf"); - OsmProvider provider = new OsmProvider(file, true); - OsmModule osmModule = OsmModule - .of(provider, gg, new DefaultVehicleParkingRepository()) + var provider = new OsmProvider(file, true); + var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository(); + var vehicleParkingRepository = new DefaultVehicleParkingRepository(); + var osmModule = OsmModule + .of(provider, gg, osmInfoRepository, vehicleParkingRepository) .withAreaVisibility(true) .build(); @@ -315,7 +323,14 @@ void testBarrierAtEnd() { File file = RESOURCE_LOADER.file("accessno-at-end.pbf"); OsmProvider provider = new OsmProvider(file, false); - OsmModule loader = OsmModule.of(provider, graph, new DefaultVehicleParkingRepository()).build(); + OsmModule loader = OsmModule + .of( + provider, + graph, + new DefaultOsmInfoGraphBuildRepository(), + new DefaultVehicleParkingRepository() + ) + .build(); loader.buildGraph(); Vertex start = graph.getVertex(VertexLabel.osm(1)); @@ -339,7 +354,7 @@ private BuildResult buildParkingLots() { .map(f -> new OsmProvider(f, false)) .toList(); var module = OsmModule - .of(providers, graph, service) + .of(providers, graph, new DefaultOsmInfoGraphBuildRepository(), service) .withStaticParkAndRide(true) .withStaticBikeParkAndRide(true) .build(); @@ -363,10 +378,12 @@ private void testBuildingAreas(boolean skipVisibility) { var graph = new Graph(deduplicator); File file = RESOURCE_LOADER.file("usf_area.osm.pbf"); - OsmProvider provider = new OsmProvider(file, false); + var provider = new OsmProvider(file, false); + var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository(); + var vehicleParkingRepository = new DefaultVehicleParkingRepository(); - OsmModule loader = OsmModule - .of(provider, graph, new DefaultVehicleParkingRepository()) + var loader = OsmModule + .of(provider, graph, osmInfoRepository, vehicleParkingRepository) .withAreaVisibility(!skipVisibility) .build(); diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java index f952bf90710..97ccbdd7719 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.osm.OsmProvider; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.vertex.Vertex; @@ -24,20 +25,25 @@ public void testLinkEntriesToPlatforms() { var stairsEndpointLabel = VertexLabel.osm(1028861028); var deduplicator = new Deduplicator(); - var gg = new Graph(deduplicator); + var graph = new Graph(deduplicator); File file = ResourceLoader.of(this).file("skoyen.osm.pbf"); OsmProvider provider = new OsmProvider(file, false); OsmModule osmModule = OsmModule - .of(provider, gg, new DefaultVehicleParkingRepository()) + .of( + provider, + graph, + new DefaultOsmInfoGraphBuildRepository(), + new DefaultVehicleParkingRepository() + ) .withPlatformEntriesLinking(true) .build(); osmModule.buildGraph(); - Vertex stairsEndpoint = gg.getVertex(stairsEndpointLabel); + Vertex stairsEndpoint = graph.getVertex(stairsEndpointLabel); // verify outgoing links assertTrue(stairsEndpoint.getOutgoing().stream().anyMatch(AreaEdge.class::isInstance)); diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java index ffc1f661dcc..1b8f7c9c58a 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java @@ -21,6 +21,7 @@ import org.opentripplanner.routing.api.request.request.filter.AllowAllTransitFilter; import org.opentripplanner.routing.api.request.request.filter.TransitFilter; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.vertex.Vertex; @@ -52,7 +53,12 @@ public static void onlyOnce() { File file = ResourceLoader.of(TriangleInequalityTest.class).file("NYC_small.osm.pbf"); OsmProvider provider = new OsmProvider(file, true); OsmModule osmModule = OsmModule - .of(provider, graph, new DefaultVehicleParkingRepository()) + .of( + provider, + graph, + new DefaultOsmInfoGraphBuildRepository(), + new DefaultVehicleParkingRepository() + ) .withAreaVisibility(true) .build(); osmModule.buildGraph(); diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java index 103dafa61b9..22b486d7cd6 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java @@ -13,6 +13,7 @@ import org.opentripplanner.graph_builder.module.TestStreetLinkerModule; import org.opentripplanner.osm.OsmProvider; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.VehicleParkingEdge; @@ -163,7 +164,12 @@ private Graph buildOsmGraph(String osmFileName, DataImportIssueStore issueStore) var timetableRepository = new TimetableRepository(siteRepository, deduplicator); OsmProvider provider = new OsmProvider(RESOURCE_LOADER.file(osmFileName), false); OsmModule loader = OsmModule - .of(provider, graph, new DefaultVehicleParkingRepository()) + .of( + provider, + graph, + new DefaultOsmInfoGraphBuildRepository(), + new DefaultVehicleParkingRepository() + ) .withIssueStore(issueStore) .withAreaVisibility(true) .withStaticParkAndRide(true) diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java index 138c3e67181..835c5a7fbcb 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java @@ -10,6 +10,7 @@ import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.vertex.Vertex; @@ -39,7 +40,12 @@ public void setUp() throws Exception { var osmDataFile = ResourceLoader.of(UnroutableTest.class).file("bridge_construction.osm.pbf"); OsmProvider provider = new OsmProvider(osmDataFile, true); OsmModule osmBuilder = OsmModule - .of(provider, graph, new DefaultVehicleParkingRepository()) + .of( + provider, + graph, + new DefaultOsmInfoGraphBuildRepository(), + new DefaultVehicleParkingRepository() + ) .withAreaVisibility(true) .build(); osmBuilder.buildGraph(); diff --git a/application/src/test/java/org/opentripplanner/routing/graph/GraphSerializationTest.java b/application/src/test/java/org/opentripplanner/routing/graph/GraphSerializationTest.java index 400a9eba2ba..9ccd6177cfd 100644 --- a/application/src/test/java/org/opentripplanner/routing/graph/GraphSerializationTest.java +++ b/application/src/test/java/org/opentripplanner/routing/graph/GraphSerializationTest.java @@ -23,6 +23,8 @@ import org.opentripplanner.ext.emissions.EmissionsDataModel; import org.opentripplanner.framework.geometry.HashGridSpatialIndex; import org.opentripplanner.graph_builder.issue.api.DataImportIssueSummary; +import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; +import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository; import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository; @@ -67,11 +69,13 @@ public class GraphSerializationTest { @Test public void testRoundTripSerializationForGTFSGraph() throws Exception { TestOtpModel model = ConstantsForTests.buildNewPortlandGraph(true); + var osmGraphBuildRepository = new DefaultOsmInfoGraphBuildRepository(); var weRepo = new DefaultWorldEnvelopeRepository(); var emissionsDataModel = new EmissionsDataModel(); var parkingRepository = new DefaultVehicleParkingRepository(); testRoundTrip( model.graph(), + osmGraphBuildRepository, model.timetableRepository(), weRepo, parkingRepository, @@ -85,11 +89,13 @@ public void testRoundTripSerializationForGTFSGraph() throws Exception { @Test public void testRoundTripSerializationForNetexGraph() throws Exception { TestOtpModel model = ConstantsForTests.buildNewMinimalNetexGraph(); + var osmGraphBuildRepository = new DefaultOsmInfoGraphBuildRepository(); var worldEnvelopeRepository = new DefaultWorldEnvelopeRepository(); var emissionsDataModel = new EmissionsDataModel(); var parkingRepository = new DefaultVehicleParkingRepository(); testRoundTrip( model.graph(), + osmGraphBuildRepository, model.timetableRepository(), worldEnvelopeRepository, parkingRepository, @@ -191,6 +197,7 @@ private static void assertNoDifferences(Graph g1, Graph g2) { */ private void testRoundTrip( Graph originalGraph, + OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, TimetableRepository originalTimetableRepository, WorldEnvelopeRepository worldEnvelopeRepository, VehicleParkingRepository vehicleParkingRepository, @@ -202,6 +209,7 @@ private void testRoundTrip( streetLimitationParameters.initMaxCarSpeed(40); SerializedGraphObject serializedObj = new SerializedGraphObject( originalGraph, + osmInfoGraphBuildRepository, originalTimetableRepository, worldEnvelopeRepository, vehicleParkingRepository, 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 181/269] 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 182/269] 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 183/269] 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 184/269] 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 - - + +
From 313ac9ccb2c265193d12ff036cf41b9e3f705e30 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:33:29 +0000 Subject: [PATCH 185/269] fix(deps): update debug ui dependencies (non-major) --- client/package-lock.json | 74 ++++++++++++++++++++++++---------------- client/package.json | 12 +++---- 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 52d93d9d4cf..c9feb3450ea 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -11,11 +11,11 @@ "@googlemaps/polyline-codec": "1.0.28", "@js-temporal/polyfill": "0.4.4", "bootstrap": "5.3.3", - "graphql": "16.9.0", + "graphql": "16.10.0", "graphql-request": "7.1.2", "maplibre-gl": "4.7.1", "react": "19.0.0", - "react-bootstrap": "2.10.6", + "react-bootstrap": "2.10.7", "react-dom": "19.0.0", "react-map-gl": "7.1.7" }, @@ -26,7 +26,7 @@ "@parcel/watcher": "2.5.0", "@testing-library/react": "16.1.0", "@types/react": "19.0.1", - "@types/react-dom": "19.0.1", + "@types/react-dom": "19.0.2", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.4", @@ -36,12 +36,12 @@ "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-react": "7.37.2", - "eslint-plugin-react-hooks": "5.0.0", + "eslint-plugin-react-hooks": "5.1.0", "eslint-plugin-react-refresh": "0.4.16", "jsdom": "25.0.1", - "prettier": "3.4.1", + "prettier": "3.4.2", "typescript": "5.7.2", - "vite": "6.0.2", + "vite": "6.0.3", "vitest": "2.1.8" } }, @@ -3122,9 +3122,10 @@ } }, "node_modules/@restart/ui": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.1.tgz", - "integrity": "sha512-qghR21ynHiUrpcIkKCoKYB+3rJtezY5Y7ikrwradCL+7hZHdQ2Ozc5ffxtpmpahoAGgc31gyXaSx2sXXaThmqA==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.2.tgz", + "integrity": "sha512-MWWqJqSyqUWWPBOOiRQrX57CBc/9CoYONg7sE+uag72GCAuYrHGU5c49vU5s4BUSBgiKNY6rL7TULqGDrouUaA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", "@popperjs/core": "^2.11.8", @@ -3584,6 +3585,12 @@ "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.0.1", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.1.tgz", @@ -3593,12 +3600,13 @@ } }, "node_modules/@types/react-dom": { - "version": "19.0.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.1.tgz", - "integrity": "sha512-hljHij7MpWPKF6u5vojuyfV0YA4YURsQG7KT6SzV0Zs2BXAtgdTxG6A229Ub/xiWV4w/7JL8fi6aAyjshH4meA==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", + "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", "dev": true, - "dependencies": { - "@types/react": "*" + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" } }, "node_modules/@types/react-transition-group": { @@ -5878,10 +5886,11 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz", - "integrity": "sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -6671,9 +6680,10 @@ "dev": true }, "node_modules/graphql": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", - "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -8912,10 +8922,11 @@ } }, "node_modules/prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", - "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -9043,13 +9054,15 @@ } }, "node_modules/react-bootstrap": { - "version": "2.10.6", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.6.tgz", - "integrity": "sha512-fNvKytSp0nHts1WRnRBJeBEt+I9/ZdrnhIjWOucEduRNvFRU1IXjZueDdWnBiqsTSJ7MckQJi9i/hxGolaRq+g==", + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.7.tgz", + "integrity": "sha512-w6mWb3uytB5A18S2oTZpYghcOUK30neMBBvZ/bEfA+WIF2dF4OGqjzoFVMpVXBjtyf92gkmRToHlddiMAVhQqQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.24.7", "@restart/hooks": "^0.4.9", - "@restart/ui": "^1.9.0", + "@restart/ui": "^1.9.2", + "@types/prop-types": "^15.7.12", "@types/react-transition-group": "^4.4.6", "classnames": "^2.3.2", "dom-helpers": "^5.2.1", @@ -10567,10 +10580,11 @@ } }, "node_modules/vite": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.2.tgz", - "integrity": "sha512-XdQ+VsY2tJpBsKGs0wf3U/+azx8BBpYRHFAyKm5VeEZNOJZRB63q7Sc8Iup3k0TrN3KO6QgyzFf+opSbfY1y0g==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz", + "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "^0.24.0", "postcss": "^8.4.49", diff --git a/client/package.json b/client/package.json index 92747c2fbb7..6cd715c8a58 100644 --- a/client/package.json +++ b/client/package.json @@ -20,11 +20,11 @@ "@googlemaps/polyline-codec": "1.0.28", "@js-temporal/polyfill": "0.4.4", "bootstrap": "5.3.3", - "graphql": "16.9.0", + "graphql": "16.10.0", "graphql-request": "7.1.2", "maplibre-gl": "4.7.1", "react": "19.0.0", - "react-bootstrap": "2.10.6", + "react-bootstrap": "2.10.7", "react-dom": "19.0.0", "react-map-gl": "7.1.7" }, @@ -35,7 +35,7 @@ "@parcel/watcher": "2.5.0", "@testing-library/react": "16.1.0", "@types/react": "19.0.1", - "@types/react-dom": "19.0.1", + "@types/react-dom": "19.0.2", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.4", @@ -45,12 +45,12 @@ "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-react": "7.37.2", - "eslint-plugin-react-hooks": "5.0.0", + "eslint-plugin-react-hooks": "5.1.0", "eslint-plugin-react-refresh": "0.4.16", "jsdom": "25.0.1", - "prettier": "3.4.1", + "prettier": "3.4.2", "typescript": "5.7.2", - "vite": "6.0.2", + "vite": "6.0.3", "vitest": "2.1.8" } } From fd0b1fe8ae564e70852fb75d487e67f5303a4c56 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 17 Dec 2024 22:47:28 +0100 Subject: [PATCH 186/269] Update protobuf, OSM parser and Google cloud tools --- application/pom.xml | 6 +++--- .../src/main/java/org/opentripplanner/osm/OsmParser.java | 4 ++-- .../src/main/java/org/opentripplanner/osm/OsmProvider.java | 2 +- .../updater/alert/GtfsRealtimeAlertsUpdater.java | 2 +- .../updater/trip/MqttGtfsRealtimeUpdater.java | 2 +- .../src/test/java/org/opentripplanner/GtfsTest.java | 2 +- gtfs-realtime-protobuf/pom.xml | 7 ++++++- pom.xml | 2 +- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index c3b4a6ee582..2a6e3043ba2 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -312,9 +312,9 @@ - org.openstreetmap.osmosis - osmosis-osm-binary - 0.48.3 + org.openstreetmap.pbf + osmpbf + 1.6.0 diff --git a/application/src/main/java/org/opentripplanner/osm/OsmParser.java b/application/src/main/java/org/opentripplanner/osm/OsmParser.java index 8a5f8e32448..4b443e6a505 100644 --- a/application/src/main/java/org/opentripplanner/osm/OsmParser.java +++ b/application/src/main/java/org/opentripplanner/osm/OsmParser.java @@ -1,11 +1,11 @@ package org.opentripplanner.osm; +import crosby.binary.BinaryParser; +import crosby.binary.Osmformat; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import org.openstreetmap.osmosis.osmbinary.BinaryParser; -import org.openstreetmap.osmosis.osmbinary.Osmformat; import org.opentripplanner.graph_builder.module.osm.OsmDatabase; import org.opentripplanner.osm.model.OsmMemberType; import org.opentripplanner.osm.model.OsmNode; diff --git a/application/src/main/java/org/opentripplanner/osm/OsmProvider.java b/application/src/main/java/org/opentripplanner/osm/OsmProvider.java index 597fd516b0e..91944a95b86 100644 --- a/application/src/main/java/org/opentripplanner/osm/OsmProvider.java +++ b/application/src/main/java/org/opentripplanner/osm/OsmProvider.java @@ -1,11 +1,11 @@ package org.opentripplanner.osm; +import crosby.binary.file.BlockInputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.time.ZoneId; -import org.openstreetmap.osmosis.osmbinary.file.BlockInputStream; import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.datastore.api.FileType; import org.opentripplanner.datastore.file.FileDataSource; diff --git a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java index a5be5ef4185..de6383c6016 100644 --- a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java @@ -68,7 +68,7 @@ protected void runPolling() { final FeedMessage feed = otpHttpClient.getAndMap( URI.create(url), this.headers.asMap(), - FeedMessage.PARSER::parseFrom + FeedMessage::parseFrom ); long feedTimestamp = feed.getHeader().getTimestamp(); diff --git a/application/src/main/java/org/opentripplanner/updater/trip/MqttGtfsRealtimeUpdater.java b/application/src/main/java/org/opentripplanner/updater/trip/MqttGtfsRealtimeUpdater.java index 20b49ed022f..0580cd4ea63 100644 --- a/application/src/main/java/org/opentripplanner/updater/trip/MqttGtfsRealtimeUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/trip/MqttGtfsRealtimeUpdater.java @@ -138,7 +138,7 @@ public void messageArrived(String topic, MqttMessage message) { UpdateIncrementality updateIncrementality = FULL_DATASET; try { // Decode message - GtfsRealtime.FeedMessage feedMessage = GtfsRealtime.FeedMessage.PARSER.parseFrom( + GtfsRealtime.FeedMessage feedMessage = GtfsRealtime.FeedMessage.parseFrom( message.getPayload() ); List feedEntityList = feedMessage.getEntityList(); diff --git a/application/src/test/java/org/opentripplanner/GtfsTest.java b/application/src/test/java/org/opentripplanner/GtfsTest.java index 5d548e4012c..05b7bfbf4f6 100644 --- a/application/src/test/java/org/opentripplanner/GtfsTest.java +++ b/application/src/test/java/org/opentripplanner/GtfsTest.java @@ -221,7 +221,7 @@ protected void setUp() throws Exception { try { InputStream inputStream = new FileInputStream(gtfsRealTime); - FeedMessage feedMessage = FeedMessage.PARSER.parseFrom(inputStream); + FeedMessage feedMessage = FeedMessage.parseFrom(inputStream); List feedEntityList = feedMessage.getEntityList(); List updates = new ArrayList<>(feedEntityList.size()); for (FeedEntity feedEntity : feedEntityList) { diff --git a/gtfs-realtime-protobuf/pom.xml b/gtfs-realtime-protobuf/pom.xml index e4465a4d366..d3c3305b9b2 100644 --- a/gtfs-realtime-protobuf/pom.xml +++ b/gtfs-realtime-protobuf/pom.xml @@ -11,10 +11,15 @@ gtfs-realtime-protobuf OpenTripPlanner - GTFS Realtime (protobuf) + + 4.28.3 + + com.google.protobuf protobuf-java + ${protobuf.version} @@ -46,7 +51,7 @@ - com.google.protobuf:protoc:3.22.0:exe:${os.detected.classifier} + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} diff --git a/pom.xml b/pom.xml index 27eada4007b..4108973c7ff 100644 --- a/pom.xml +++ b/pom.xml @@ -392,7 +392,7 @@ com.google.cloud libraries-bom - 26.48.0 + 26.51.0 pom import From e1e5fedd826a03f4083796ff98ed01fa71e0d05c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 17 Dec 2024 23:04:43 +0100 Subject: [PATCH 187/269] Fix formatting --- .../src/components/ItineraryList/ItineraryLegDetails.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/client/src/components/ItineraryList/ItineraryLegDetails.tsx b/client/src/components/ItineraryList/ItineraryLegDetails.tsx index 04ddc55c574..67abb055665 100644 --- a/client/src/components/ItineraryList/ItineraryLegDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryLegDetails.tsx @@ -25,11 +25,8 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean ¢{leg.generalizedCost}
- - + -{' '} +
{leg.mode}{' '} {leg.line && ( From 04d35b7c34c5ab646ee044c7bd80caa50b99355b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 00:08:13 +0100 Subject: [PATCH 188/269] Clean up code a little --- .../apis/gtfs/GtfsGraphQLIndex.java | 2 +- .../mapping/StatesToWalkStepsMapper.java | 31 +++++++------------ .../model/vertex/StationEntranceVertex.java | 26 +++++++++++----- .../model/site/StationElementBuilder.java | 3 +- .../opentripplanner/apis/gtfs/schema.graphqls | 1 - .../apis/gtfs/expectations/walk-steps.json | 17 +++++----- 6 files changed, 42 insertions(+), 38 deletions(-) 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 cd72633a886..d3f64288417 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -39,8 +39,8 @@ 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.EntranceImpl; +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; 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 956ec6d5701..97310a47453 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 @@ -30,7 +30,6 @@ import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.state.State; import org.opentripplanner.transit.model.basic.Accessibility; -import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.Entrance; /** @@ -179,8 +178,8 @@ private void processState(State backState, State forwardState) { if (edge instanceof ElevatorAlightEdge) { addStep(createElevatorWalkStep(backState, forwardState, edge)); return; - } else if (backState.getVertex() instanceof StationEntranceVertex) { - addStep(createStationEntranceWalkStep(backState, forwardState, edge)); + } else if (backState.getVertex() instanceof StationEntranceVertex stationEntranceVertex) { + addStep(createStationEntranceWalkStep(backState, forwardState, stationEntranceVertex)); return; } else if (edge instanceof PathwayEdge pwe && pwe.signpostedAs().isPresent()) { createAndSaveStep(backState, forwardState, pwe.signpostedAs().get(), FOLLOW_SIGNS, edge); @@ -525,29 +524,23 @@ private WalkStepBuilder createElevatorWalkStep(State backState, State forwardSta private WalkStepBuilder createStationEntranceWalkStep( State backState, State forwardState, - Edge edge + StationEntranceVertex vertex ) { - // don't care what came before or comes after - var step = createWalkStep(forwardState, backState); - // There is not a way to definitively determine if a user is entering or exiting the station, - // since the doors might be between or inside stations. - step.withRelativeDirection(RelativeDirection.ENTER_OR_EXIT_STATION); - - StationEntranceVertex vertex = (StationEntranceVertex) backState.getVertex(); - - FeedScopedId entranceId = new FeedScopedId("osm", vertex.getId()); - Entrance entrance = Entrance - .of(entranceId) - .withCode(vertex.getCode()) + .of(vertex.id()) + .withCode(vertex.code()) .withCoordinate(new WgsCoordinate(vertex.getCoordinate())) .withWheelchairAccessibility( - vertex.isAccessible() ? Accessibility.POSSIBLE : Accessibility.NOT_POSSIBLE + vertex.wheelchairAccessibility() ) .build(); - step.withEntrance(entrance); - return step; + // don't care what came before or comes after + return createWalkStep(forwardState, backState) + // There is not a way to definitively determine if a user is entering or exiting the station, + // since the doors might be between or inside stations. + .withRelativeDirection(RelativeDirection.ENTER_OR_EXIT_STATION) + .withEntrance(entrance); } private void createAndSaveStep( diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java index e55ac7078db..6dd528204b2 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java @@ -1,5 +1,10 @@ package org.opentripplanner.street.model.vertex; +import javax.annotation.Nullable; +import org.opentripplanner.transit.model.basic.Accessibility; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.utils.tostring.ToStringBuilder; + public class StationEntranceVertex extends OsmVertex { private final String code; @@ -11,19 +16,26 @@ public StationEntranceVertex(double x, double y, long nodeId, String code, boole this.accessible = accessible; } - public String getCode() { - return code; + public FeedScopedId id() { + return new FeedScopedId("osm", String.valueOf(nodeId)); } - public boolean isAccessible() { - return accessible; + @Nullable + public String code() { + return code; } - public String getId() { - return Long.toString(nodeId); + public Accessibility wheelchairAccessibility() { + return accessible ? Accessibility.POSSIBLE : Accessibility.NOT_POSSIBLE; } + @Override public String toString() { - return "StationEntranceVertex(" + super.toString() + ", code=" + code + ")"; + return ToStringBuilder + .of(StationEntranceVertex.class) + .addNum("nodeId", nodeId) + .addStr("code", code) + .toString(); } + } diff --git a/application/src/main/java/org/opentripplanner/transit/model/site/StationElementBuilder.java b/application/src/main/java/org/opentripplanner/transit/model/site/StationElementBuilder.java index 7a7fc0e4621..ea90231bead 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/site/StationElementBuilder.java +++ b/application/src/main/java/org/opentripplanner/transit/model/site/StationElementBuilder.java @@ -1,5 +1,6 @@ package org.opentripplanner.transit.model.site; +import javax.annotation.Nullable; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.basic.Accessibility; @@ -54,7 +55,7 @@ public String code() { return code; } - public B withCode(String code) { + public B withCode(@Nullable String code) { this.code = code; return instance(); } 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 1c4951b782c..3eac957f7bb 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3542,7 +3542,6 @@ enum RelativeDirection { CONTINUE DEPART ELEVATOR - ENTER_OR_EXIT_STATION ENTER_STATION EXIT_STATION FOLLOW_SIGNS diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json index 19f7f5cc758..be952f72303 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json @@ -11,22 +11,21 @@ "streetName": "street", "area": false, "relativeDirection": "DEPART", - "absoluteDirection" : "NORTHEAST", - "feature" : null + "absoluteDirection": "NORTHEAST", + "feature": null }, { "streetName": "elevator", "area": false, "relativeDirection": "ELEVATOR", - "absoluteDirection" : null, - "feature" : null - + "absoluteDirection": null, + "feature": null }, { - "streetName" : "entrance", - "area" : false, - "relativeDirection" : "ENTER_OR_EXIT_STATION", - "absoluteDirection" : null, + "streetName": "entrance", + "area": false, + "relativeDirection": "ENTER_OR_EXIT_STATION", + "absoluteDirection": null, "feature": { "__typename": "Entrance", "code": "A", From c4d665d21bb995678bc3e3b56a526b1cd3ce0c5f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 00:20:12 +0100 Subject: [PATCH 189/269] Reformat code and schema --- .../gtfs/generated/GraphQLDataFetchers.java | 15 +++++----- .../apis/gtfs/generated/GraphQLTypes.java | 1 - .../apis/gtfs/mapping/DirectionMapper.java | 3 +- .../mapping/StatesToWalkStepsMapper.java | 4 +-- .../model/vertex/StationEntranceVertex.java | 1 - .../opentripplanner/apis/gtfs/schema.graphqls | 28 +++++++++---------- 6 files changed, 24 insertions(+), 28 deletions(-) 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 760e890045b..d9c9ceb67e8 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 @@ -392,13 +392,6 @@ 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(); - } - /** Station entrance or exit, originating from OSM or GTFS data. */ public interface GraphQLEntrance { public DataFetcher code(); @@ -410,6 +403,13 @@ public interface GraphQLEntrance { public DataFetcher wheelchairAccessible(); } + /** 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(); @@ -1035,6 +1035,7 @@ public interface GraphQLRoutingError { public DataFetcher inputField(); } + /** A feature for a step */ public interface GraphQLStepFeature extends TypeResolver {} /** 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 fe598f88e40..a969b5223b1 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 @@ -4334,7 +4334,6 @@ public enum GraphQLRelativeDirection { CONTINUE, DEPART, ELEVATOR, - ENTER_OR_EXIT_STATION, ENTER_STATION, EXIT_STATION, FOLLOW_SIGNS, diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java index 1439cdd34c3..3f69047f94d 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java @@ -27,7 +27,7 @@ public static GraphQLRelativeDirection map(RelativeDirection relativeDirection) case HARD_LEFT -> GraphQLRelativeDirection.HARD_LEFT; case LEFT -> GraphQLRelativeDirection.LEFT; case SLIGHTLY_LEFT -> GraphQLRelativeDirection.SLIGHTLY_LEFT; - case CONTINUE -> GraphQLRelativeDirection.CONTINUE; + case CONTINUE, ENTER_OR_EXIT_STATION -> GraphQLRelativeDirection.CONTINUE; case SLIGHTLY_RIGHT -> GraphQLRelativeDirection.SLIGHTLY_RIGHT; case RIGHT -> GraphQLRelativeDirection.RIGHT; case HARD_RIGHT -> GraphQLRelativeDirection.HARD_RIGHT; @@ -38,7 +38,6 @@ public static GraphQLRelativeDirection map(RelativeDirection relativeDirection) case UTURN_RIGHT -> GraphQLRelativeDirection.UTURN_RIGHT; case ENTER_STATION -> GraphQLRelativeDirection.ENTER_STATION; case EXIT_STATION -> GraphQLRelativeDirection.EXIT_STATION; - case ENTER_OR_EXIT_STATION -> GraphQLRelativeDirection.ENTER_OR_EXIT_STATION; case FOLLOW_SIGNS -> GraphQLRelativeDirection.FOLLOW_SIGNS; }; } 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 97310a47453..9365c50509c 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 @@ -530,9 +530,7 @@ private WalkStepBuilder createStationEntranceWalkStep( .of(vertex.id()) .withCode(vertex.code()) .withCoordinate(new WgsCoordinate(vertex.getCoordinate())) - .withWheelchairAccessibility( - vertex.wheelchairAccessibility() - ) + .withWheelchairAccessibility(vertex.wheelchairAccessibility()) .build(); // don't care what came before or comes after diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java index 6dd528204b2..ef94bbb64ef 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java @@ -37,5 +37,4 @@ public String toString() { .addStr("code", code) .toString(); } - } 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 3eac957f7bb..748b69607e0 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -491,18 +491,6 @@ 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! -} - "Station entrance or exit, originating from OSM or GTFS data." type Entrance { "Short text or a number that identifies the entrance or exit for passengers. For example, `A` or `B`." @@ -515,6 +503,18 @@ type Entrance { wheelchairAccessible: WheelchairBoarding } +"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" @@ -2359,7 +2359,7 @@ type Stoptime { """ The position of the stop in the pattern. This is required to start from 0 and be consecutive along the pattern, up to n-1 for a pattern with n stops. - + The purpose of this field is to identify the position of the stop within the pattern so it can be cross-referenced between different trips on the same pattern, as stopPosition can be different between trips even within the same pattern. @@ -2518,7 +2518,7 @@ type TripOnServiceDate { 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. From bf89f4969fd39cc2b1265f35b3c79836ac6ca496 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 00:25:08 +0100 Subject: [PATCH 190/269] Fix tests --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 6 +++--- .../apis/gtfs/mapping/DirectionMapperTest.java | 1 + .../opentripplanner/apis/gtfs/expectations/walk-steps.json | 2 +- 3 files changed, 5 insertions(+), 4 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 748b69607e0..6ac56be3d1d 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -507,7 +507,7 @@ type Entrance { 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! @@ -2359,7 +2359,7 @@ type Stoptime { """ The position of the stop in the pattern. This is required to start from 0 and be consecutive along the pattern, up to n-1 for a pattern with n stops. - + The purpose of this field is to identify the position of the stop within the pattern so it can be cross-referenced between different trips on the same pattern, as stopPosition can be different between trips even within the same pattern. @@ -2518,7 +2518,7 @@ type TripOnServiceDate { 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. diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapperTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapperTest.java index 2c69f3dca46..1dcd6e210a3 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapperTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapperTest.java @@ -23,6 +23,7 @@ void absoluteDirection() { void relativeDirection() { Arrays .stream(RelativeDirection.values()) + .filter(v -> v != RelativeDirection.ENTER_OR_EXIT_STATION) .forEach(d -> { var mapped = DirectionMapper.map(d); assertEquals(d.toString(), mapped.toString()); diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json index be952f72303..0e089aac428 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json @@ -24,7 +24,7 @@ { "streetName": "entrance", "area": false, - "relativeDirection": "ENTER_OR_EXIT_STATION", + "relativeDirection": "CONTINUE", "absoluteDirection": null, "feature": { "__typename": "Entrance", From 2d012a33c3726ab0d0e8d4ff4f85ab048466e587 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Wed, 18 Dec 2024 09:00:50 +0000 Subject: [PATCH 191/269] Upgrade debug client to version 2024/12/2024-12-18T09:00 --- 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 f65d9a153ba..7ebc0fee49a 100644 --- a/application/src/client/index.html +++ b/application/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug - - + +
From 2eb0e7b45c87941ea5fb928c4929f748fa089d5b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 11:33:51 +0100 Subject: [PATCH 192/269] Add documentation --- .../model/vertex/StationEntranceVertex.java | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java index ef94bbb64ef..254c71c527d 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java @@ -5,28 +5,46 @@ import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.utils.tostring.ToStringBuilder; +/** + * A station entrance extracted from OSM and therefore not (yet) associated with the transit + * entity {@link org.opentripplanner.transit.model.site.Station}. + */ public class StationEntranceVertex extends OsmVertex { + private static final String FEED_ID = "osm"; private final String code; - private final boolean accessible; - - public StationEntranceVertex(double x, double y, long nodeId, String code, boolean accessible) { - super(x, y, nodeId); + private final boolean wheelchairAccessible; + + public StationEntranceVertex( + double lat, + double lon, + long nodeId, + String code, + boolean wheelchairAccessible + ) { + super(lat, lon, nodeId); this.code = code; - this.accessible = accessible; + this.wheelchairAccessible = wheelchairAccessible; } + /** + * The id of the entrance which may or may not be human-readable. + */ public FeedScopedId id() { - return new FeedScopedId("osm", String.valueOf(nodeId)); + return new FeedScopedId(FEED_ID, String.valueOf(nodeId)); } + /** + * Short human-readable code of the exit, like A or H3. + * If we need a proper name like "Oranienplatz" we have to add a name field. + */ @Nullable public String code() { return code; } public Accessibility wheelchairAccessibility() { - return accessible ? Accessibility.POSSIBLE : Accessibility.NOT_POSSIBLE; + return wheelchairAccessible ? Accessibility.POSSIBLE : Accessibility.NOT_POSSIBLE; } @Override From 2eedb12cdbb3e48bb61f44ca1313a85add14aa88 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Wed, 18 Dec 2024 10:53:22 +0000 Subject: [PATCH 193/269] fix test failure when building OTP in a path with special characters. https://stackoverflow.com/a/13470643 fixes #6337 --- .../org/opentripplanner/test/support/ResourceLoader.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/application/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index 5670a49fab7..e22d47b7f94 100644 --- a/application/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/application/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -8,6 +8,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.List; @@ -46,7 +47,12 @@ public static ResourceLoader of(Object object) { */ public File file(String path) { URL resource = url(path); - var file = new File(resource.getFile()); + File file; + try { + file = new File(new URI(resource.toString())); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } assertTrue( file.exists(), "File '%s' not found on file system.".formatted(file.getAbsolutePath()) From 977d8ebc943d0476f3e1723ed56f1e9ee48be39d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 12:07:09 +0100 Subject: [PATCH 194/269] Clean up --- .../ext/restapi/mapping/RelativeDirectionMapper.java | 3 +-- .../opentripplanner/ext/restapi/mapping/WalkStepMapper.java | 2 +- .../ext/restapi/model/ApiRelativeDirection.java | 1 - .../opentripplanner/apis/gtfs/datafetchers/stepImpl.java | 2 +- .../apis/transmodel/model/plan/PathGuidanceType.java | 2 +- .../module/osm/parameters/OsmExtractParameters.java | 1 - .../org/opentripplanner/model/plan/RelativeDirection.java | 6 ++++++ .../main/java/org/opentripplanner/model/plan/WalkStep.java | 2 +- 8 files changed, 11 insertions(+), 8 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/RelativeDirectionMapper.java b/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/RelativeDirectionMapper.java index 708da1fd6c3..ab9abaa4481 100644 --- a/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/RelativeDirectionMapper.java +++ b/application/src/ext/java/org/opentripplanner/ext/restapi/mapping/RelativeDirectionMapper.java @@ -14,7 +14,7 @@ public static ApiRelativeDirection mapRelativeDirection(RelativeDirection domain case HARD_LEFT -> ApiRelativeDirection.HARD_LEFT; case LEFT -> ApiRelativeDirection.LEFT; case SLIGHTLY_LEFT -> ApiRelativeDirection.SLIGHTLY_LEFT; - case CONTINUE -> ApiRelativeDirection.CONTINUE; + case CONTINUE, ENTER_OR_EXIT_STATION -> ApiRelativeDirection.CONTINUE; case SLIGHTLY_RIGHT -> ApiRelativeDirection.SLIGHTLY_RIGHT; case RIGHT -> ApiRelativeDirection.RIGHT; case HARD_RIGHT -> ApiRelativeDirection.HARD_RIGHT; @@ -25,7 +25,6 @@ public static ApiRelativeDirection mapRelativeDirection(RelativeDirection domain case UTURN_RIGHT -> ApiRelativeDirection.UTURN_RIGHT; case ENTER_STATION -> ApiRelativeDirection.ENTER_STATION; case EXIT_STATION -> ApiRelativeDirection.EXIT_STATION; - case ENTER_OR_EXIT_STATION -> ApiRelativeDirection.ENTER_OR_EXIT_STATION; case FOLLOW_SIGNS -> ApiRelativeDirection.FOLLOW_SIGNS; }; } 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 9360620d40b..49e467a89db 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 @@ -39,7 +39,7 @@ public ApiWalkStep mapWalkStep(WalkStep domain) { api.streetName = domain.getDirectionText().toString(locale); api.absoluteDirection = domain.getAbsoluteDirection().map(AbsoluteDirectionMapper::mapAbsoluteDirection).orElse(null); - api.exit = domain.getHighwayExit(); + api.exit = domain.isHighwayExit(); api.stayOn = domain.isStayOn(); api.area = domain.getArea(); api.bogusName = domain.nameIsDerived(); diff --git a/application/src/ext/java/org/opentripplanner/ext/restapi/model/ApiRelativeDirection.java b/application/src/ext/java/org/opentripplanner/ext/restapi/model/ApiRelativeDirection.java index eb624df5ea6..02a530f06de 100644 --- a/application/src/ext/java/org/opentripplanner/ext/restapi/model/ApiRelativeDirection.java +++ b/application/src/ext/java/org/opentripplanner/ext/restapi/model/ApiRelativeDirection.java @@ -21,6 +21,5 @@ public enum ApiRelativeDirection { UTURN_RIGHT, ENTER_STATION, EXIT_STATION, - ENTER_OR_EXIT_STATION, FOLLOW_SIGNS, } 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 4953d887713..7b1df1693d3 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 @@ -51,7 +51,7 @@ public DataFetcher> elevationProfile() { @Override public DataFetcher exit() { - return environment -> getSource(environment).getHighwayExit(); + return environment -> getSource(environment).isHighwayExit(); } @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 5c2fa6f3a5e..74b30c83f44 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 @@ -65,7 +65,7 @@ public static GraphQLObjectType create(GraphQLObjectType elevationStepType) { .name("exit") .description("When exiting a highway or traffic circle, the exit name/number.") .type(Scalars.GraphQLString) - .dataFetcher(environment -> ((WalkStep) environment.getSource()).getHighwayExit()) + .dataFetcher(environment -> ((WalkStep) environment.getSource()).isHighwayExit()) .build() ) .field( diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java index 37edaf687ab..a59147137f6 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/parameters/OsmExtractParameters.java @@ -49,7 +49,6 @@ public ZoneId timeZone() { return timeZone; } - @Nullable public boolean includeOsmSubwayEntrances() { return includeOsmSubwayEntrances; } diff --git a/application/src/main/java/org/opentripplanner/model/plan/RelativeDirection.java b/application/src/main/java/org/opentripplanner/model/plan/RelativeDirection.java index fbdb836ab6a..3ce16a45c11 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/RelativeDirection.java +++ b/application/src/main/java/org/opentripplanner/model/plan/RelativeDirection.java @@ -21,6 +21,12 @@ public enum RelativeDirection { UTURN_RIGHT, ENTER_STATION, EXIT_STATION, + /** + * We don't have a way to reliably tell if we are entering or exiting a station and therefore + * use this generic enum value. Please don't expose it in APIs. + *

+ * If we manage to figure it out in the future, we can remove this. + */ ENTER_OR_EXIT_STATION, FOLLOW_SIGNS; 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 eb59196b971..7edae8d7174 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -130,7 +130,7 @@ public Optional getAbsoluteDirection() { /** * When exiting a highway or traffic circle, the exit name/number. */ - public String getHighwayExit() { + public String isHighwayExit() { return exit; } From b7cc6fddb042f6f69a098208925c8d71cba7ee29 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 12:36:16 +0100 Subject: [PATCH 195/269] Remove enum mapper test for REST API --- .../ext/restapi/mapping/EnumMapperTest.java | 41 ------------------- .../ext/restapi/mapping/WalkStepMapper.java | 2 +- .../apis/gtfs/datafetchers/stepImpl.java | 11 +---- .../model/plan/PathGuidanceType.java | 4 +- .../opentripplanner/model/plan/WalkStep.java | 8 ++-- 5 files changed, 10 insertions(+), 56 deletions(-) diff --git a/application/src/ext-test/java/org/opentripplanner/ext/restapi/mapping/EnumMapperTest.java b/application/src/ext-test/java/org/opentripplanner/ext/restapi/mapping/EnumMapperTest.java index 35cc368fec4..5b03ada1c1f 100644 --- a/application/src/ext-test/java/org/opentripplanner/ext/restapi/mapping/EnumMapperTest.java +++ b/application/src/ext-test/java/org/opentripplanner/ext/restapi/mapping/EnumMapperTest.java @@ -8,39 +8,11 @@ import java.util.Map; import java.util.function.Function; import org.junit.jupiter.api.Test; -import org.opentripplanner.ext.restapi.model.ApiAbsoluteDirection; -import org.opentripplanner.ext.restapi.model.ApiRelativeDirection; import org.opentripplanner.ext.restapi.model.ApiVertexType; -import org.opentripplanner.model.plan.AbsoluteDirection; -import org.opentripplanner.model.plan.RelativeDirection; import org.opentripplanner.model.plan.VertexType; public class EnumMapperTest { - private static final String MSG = - "Assert that the API enums have the exact same values that " + - "the domain enums of the same type, and that the specialized mapper is mapping all " + - "values. If this assumtion does not hold, create a new test."; - - @Test - public void map() { - try { - verifyExactMatch( - AbsoluteDirection.class, - ApiAbsoluteDirection.class, - AbsoluteDirectionMapper::mapAbsoluteDirection - ); - verifyExactMatch( - RelativeDirection.class, - ApiRelativeDirection.class, - RelativeDirectionMapper::mapRelativeDirection - ); - } catch (RuntimeException ex) { - System.out.println(MSG); - throw ex; - } - } - @Test public void testVertexTypeMapping() { verifyExplicitMatch( @@ -75,17 +47,4 @@ private , A extends Enum> void verifyExplicitMatch( assertTrue(rest.isEmpty()); } - private , A extends Enum> void verifyExactMatch( - Class domainClass, - Class apiClass, - Function mapper - ) { - List rest = new ArrayList<>(List.of(apiClass.getEnumConstants())); - for (D it : domainClass.getEnumConstants()) { - A result = mapper.apply(it); - assertEquals(result.name(), it.name()); - rest.remove(result); - } - assertTrue(rest.isEmpty()); - } } 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 49e467a89db..c4aa11904cc 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 @@ -39,7 +39,7 @@ public ApiWalkStep mapWalkStep(WalkStep domain) { api.streetName = domain.getDirectionText().toString(locale); api.absoluteDirection = domain.getAbsoluteDirection().map(AbsoluteDirectionMapper::mapAbsoluteDirection).orElse(null); - api.exit = domain.isHighwayExit(); + api.exit = domain.highwayExit().orElse(null); api.stayOn = domain.isStayOn(); api.area = domain.getArea(); api.bogusName = domain.nameIsDerived(); 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 7b1df1693d3..409bb2abb1d 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 @@ -7,7 +7,6 @@ import org.opentripplanner.apis.gtfs.mapping.DirectionMapper; import org.opentripplanner.apis.gtfs.mapping.StreetNoteMapper; import org.opentripplanner.model.plan.ElevationProfile.Step; -import org.opentripplanner.model.plan.RelativeDirection; import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.routing.alertpatch.TransitAlert; @@ -51,18 +50,12 @@ public DataFetcher> elevationProfile() { @Override public DataFetcher exit() { - return environment -> getSource(environment).isHighwayExit(); + return environment -> getSource(environment).highwayExit().orElse(null); } @Override public DataFetcher feature() { - return environment -> { - WalkStep source = getSource(environment); - if (source.getRelativeDirection() == RelativeDirection.ENTER_OR_EXIT_STATION) { - return source.getEntrance(); - } - return null; - }; + return environment -> getSource(environment).entrance().orElse(null); } @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 74b30c83f44..86c8359c026 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 @@ -65,7 +65,9 @@ public static GraphQLObjectType create(GraphQLObjectType elevationStepType) { .name("exit") .description("When exiting a highway or traffic circle, the exit name/number.") .type(Scalars.GraphQLString) - .dataFetcher(environment -> ((WalkStep) environment.getSource()).isHighwayExit()) + .dataFetcher(environment -> + ((WalkStep) environment.getSource()).highwayExit().orElse(null) + ) .build() ) .field( 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 7edae8d7174..fea605fe910 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -130,15 +130,15 @@ public Optional getAbsoluteDirection() { /** * When exiting a highway or traffic circle, the exit name/number. */ - public String isHighwayExit() { - return exit; + public Optional highwayExit() { + return exit.describeConstable(); } /** * Get information about a subway station entrance or exit. */ - public Entrance getEntrance() { - return entrance; + public Optional entrance() { + return Optional.ofNullable(entrance); } /** From e473061d17441245d415771ee7f1c25c404a02a0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 12:44:25 +0100 Subject: [PATCH 196/269] Fix highway exits --- .../java/org/opentripplanner/model/plan/WalkStep.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 fea605fe910..7ade16de39a 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStep.java @@ -44,7 +44,7 @@ public final class WalkStep { private final double angle; private final boolean walkingBike; - private final String exit; + private final String highwayExit; private final Entrance entrance; private final ElevationProfile elevationProfile; private final boolean stayOn; @@ -57,7 +57,7 @@ public final class WalkStep { AbsoluteDirection absoluteDirection, I18NString directionText, Set streetNotes, - String exit, + String highwayExit, Entrance entrance, ElevationProfile elevationProfile, boolean nameIsDerived, @@ -78,7 +78,7 @@ public final class WalkStep { this.angle = DoubleUtils.roundTo2Decimals(angle); this.walkingBike = walkingBike; this.area = area; - this.exit = exit; + this.highwayExit = highwayExit; this.entrance = entrance; this.elevationProfile = elevationProfile; this.stayOn = stayOn; @@ -131,7 +131,7 @@ public Optional getAbsoluteDirection() { * When exiting a highway or traffic circle, the exit name/number. */ public Optional highwayExit() { - return exit.describeConstable(); + return Optional.ofNullable(highwayExit); } /** From de1e23d1264f20c9b5ce139ab22b865f121f1e6b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 18 Dec 2024 12:52:11 +0100 Subject: [PATCH 197/269] Re-enable file loader for SIRI-SX --- .../updater/configure/SiriUpdaterModule.java | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/updater/siri/updater/configure/SiriUpdaterModule.java b/application/src/main/java/org/opentripplanner/updater/siri/updater/configure/SiriUpdaterModule.java index 2553bd3c67e..f39fbbb3134 100644 --- a/application/src/main/java/org/opentripplanner/updater/siri/updater/configure/SiriUpdaterModule.java +++ b/application/src/main/java/org/opentripplanner/updater/siri/updater/configure/SiriUpdaterModule.java @@ -43,7 +43,26 @@ public static SiriSXUpdater createSiriSXUpdater( return new SiriSXUpdater(params, timetableRepository, createLoader(params)); } + private static EstimatedTimetableSource createSource(SiriETUpdater.Parameters params) { + return switch (params) { + case SiriETUpdaterParameters p -> new SiriETHttpTripUpdateSource( + p.sourceParameters(), + createLoader(params) + ); + case SiriETLiteUpdaterParameters p -> new SiriETLiteHttpTripUpdateSource( + p.sourceParameters(), + createLoader(params) + ); + default -> throw new IllegalArgumentException("Unexpected value: " + params); + }; + } + private static SiriLoader createLoader(SiriSXUpdater.Parameters params) { + // Load real-time updates from a file. + if (SiriFileLoader.matchesUrl(params.url())) { + return new SiriFileLoader(params.url()); + } + // Fallback to default loader return switch (params) { case SiriSXUpdaterParameters p -> new SiriHttpLoader( p.url(), @@ -59,20 +78,6 @@ private static SiriLoader createLoader(SiriSXUpdater.Parameters params) { }; } - private static EstimatedTimetableSource createSource(SiriETUpdater.Parameters params) { - return switch (params) { - case SiriETUpdaterParameters p -> new SiriETHttpTripUpdateSource( - p.sourceParameters(), - createLoader(params) - ); - case SiriETLiteUpdaterParameters p -> new SiriETLiteHttpTripUpdateSource( - p.sourceParameters(), - createLoader(params) - ); - default -> throw new IllegalArgumentException("Unexpected value: " + params); - }; - } - private static SiriLoader createLoader(SiriETUpdater.Parameters params) { // Load real-time updates from a file. if (SiriFileLoader.matchesUrl(params.url())) { From ed2d7f5f6e4866bfabad9b8789ba7ff094cbc36e Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 19 Dec 2024 09:59:10 +0000 Subject: [PATCH 198/269] Add changelog entry for #6311 [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 1ff69082268..a5eff7c9b2a 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -66,6 +66,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - 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) +- Make flex linking work together with boarding locations [#6311](https://github.com/opentripplanner/OpenTripPlanner/pull/6311) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From b9f41354c42b0a5e32268f839bb964eb799a8077 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 19 Dec 2024 10:14:01 +0000 Subject: [PATCH 199/269] remove unused import --- .../java/org/opentripplanner/test/support/ResourceLoader.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/application/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index e22d47b7f94..523a613913d 100644 --- a/application/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/application/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -8,7 +8,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.List; From d40d0b3acceaa3bd117cb8dc10d3f4518f04dce2 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 19 Dec 2024 10:55:34 +0000 Subject: [PATCH 200/269] use a service to store platform data --- .../apis/vectortiles/DebugStyleSpec.java | 2 - .../module/OsmBoardingLocationsModule.java | 28 +++++++------- .../graph_builder/module/osm/OsmModule.java | 37 +++++++------------ .../osminfo/OsmInfoGraphBuildRepository.java | 10 ++--- .../osminfo/OsmInfoGraphBuildService.java | 9 +++-- .../DefaultOsmInfoGraphBuildRepository.java | 16 ++++---- .../DefaultOsmInfoGraphBuildService.java | 6 +-- .../osminfo/model/OsmWayReferences.java | 26 ------------- .../service/osminfo/model/Platform.java | 8 ++++ .../street/model/edge/LinearPlatform.java | 9 ----- .../street/model/edge/LinearPlatformEdge.java | 11 ------ .../model/edge/LinearPlatformEdgeBuilder.java | 20 ---------- .../apis/vectortiles/style.json | 4 -- 13 files changed, 57 insertions(+), 129 deletions(-) delete mode 100644 application/src/main/java/org/opentripplanner/service/osminfo/model/OsmWayReferences.java create mode 100644 application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java delete mode 100644 application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatform.java delete mode 100644 application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdge.java delete mode 100644 application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdgeBuilder.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 9d55d36f301..7070f8b486e 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -20,7 +20,6 @@ import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.edge.ElevatorHopEdge; import org.opentripplanner.street.model.edge.EscalatorEdge; -import org.opentripplanner.street.model.edge.LinearPlatformEdge; import org.opentripplanner.street.model.edge.PathwayEdge; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetStationCentroidLink; @@ -81,7 +80,6 @@ public class DebugStyleSpec { private static final Class[] EDGES_TO_DISPLAY = new Class[] { StreetEdge.class, AreaEdge.class, - LinearPlatformEdge.class, EscalatorEdge.class, PathwayEdge.class, ElevatorHopEdge.class, diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java index 0664b442f8a..7e2ba334f2f 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java @@ -2,9 +2,9 @@ import jakarta.inject.Inject; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; @@ -18,12 +18,11 @@ import org.opentripplanner.routing.linking.LinkingDirection; import org.opentripplanner.routing.linking.VertexLinker; import org.opentripplanner.service.osminfo.OsmInfoGraphBuildService; +import org.opentripplanner.service.osminfo.model.Platform; import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; import org.opentripplanner.street.model.edge.Edge; -import org.opentripplanner.street.model.edge.LinearPlatform; -import org.opentripplanner.street.model.edge.LinearPlatformEdge; import org.opentripplanner.street.model.edge.NamedArea; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetEdgeBuilder; @@ -149,25 +148,24 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { // if the boarding location is a non-area way we are finding the vertex representing the // center of the way, splitting if needed - var nearbyLinearPlatformEdges = new HashMap>(); + var nearbyEdges = new HashMap>(); for (var edge : index.getEdgesForEnvelope(envelope)) { - if (edge instanceof LinearPlatformEdge platformEdge) { - var platform = platformEdge.platform; + osmInfoGraphBuildService.findPlatform(edge).ifPresent(platform -> { if (matchesReference(stop, platform.references())) { - if (!nearbyLinearPlatformEdges.containsKey(platform)) { - var list = new ArrayList(); - list.add(platformEdge); - nearbyLinearPlatformEdges.put(platform, list); + if (!nearbyEdges.containsKey(platform)) { + var list = new ArrayList(); + list.add(edge); + nearbyEdges.put(platform, list); } else { - nearbyLinearPlatformEdges.get(platform).add(platformEdge); + nearbyEdges.get(platform).add(edge); } } - } + }); } - for (var platformEdgeList : nearbyLinearPlatformEdges.entrySet()) { - LinearPlatform platform = platformEdgeList.getKey(); + for (var platformEdgeList : nearbyEdges.entrySet()) { + Platform platform = platformEdgeList.getKey(); var name = platform.name(); var label = "platform-centroid/%s".formatted(stop.getId().toString()); var centroid = platform.geometry().getCentroid(); @@ -269,7 +267,7 @@ private void linkBoardingLocationToStop( ); } - private boolean matchesReference(StationElement stop, Set references) { + private boolean matchesReference(StationElement stop, Collection references) { var stopCode = stop.getCode(); var stopId = stop.getId().getId(); 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 d25d2c83d6c..673611f367f 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 @@ -27,13 +27,11 @@ import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.util.ElevationUtils; import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; -import org.opentripplanner.service.osminfo.model.OsmWayReferences; +import org.opentripplanner.service.osminfo.model.Platform; import org.opentripplanner.service.vehicleparking.VehicleParkingRepository; import org.opentripplanner.service.vehicleparking.model.VehicleParking; import org.opentripplanner.street.model.StreetLimitationParameters; import org.opentripplanner.street.model.StreetTraversalPermission; -import org.opentripplanner.street.model.edge.LinearPlatform; -import org.opentripplanner.street.model.edge.LinearPlatformEdgeBuilder; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetEdgeBuilder; import org.opentripplanner.street.model.vertex.BarrierVertex; @@ -57,7 +55,6 @@ public class OsmModule implements GraphBuilderModule { */ private final List providers; private final Graph graph; - // TODO: Use this to store edge stop references private final OsmInfoGraphBuildRepository osmInfoGraphBuildRepository; private final VehicleParkingRepository parkingRepository; @@ -355,7 +352,7 @@ private void buildBasicGraph() { * We split segments at intersections, self-intersections, nodes with ele tags, and transit stops; * the only processing we do on other nodes is to accumulate their geometry */ - if (segmentCoordinates.size() == 0) { + if (segmentCoordinates.isEmpty()) { segmentCoordinates.add(osmStartNode.getCoordinate()); } @@ -413,8 +410,7 @@ private void buildBasicGraph() { way, i, permissions, - geometry, - platform + geometry ); params.edgeNamer().recordEdges(way, streets); @@ -423,7 +419,10 @@ private void buildBasicGraph() { StreetEdge backStreet = streets.back(); normalizer.applyWayProperties(street, backStreet, wayData, way); - osmInfoGraphBuildRepository.addReferences(street, new OsmWayReferences(List.of(street.toString()))); + platform.ifPresent(plat -> { + osmInfoGraphBuildRepository.addPlatform(street, plat); + osmInfoGraphBuildRepository.addPlatform(backStreet, plat); + }); applyEdgesToTurnRestrictions(way, startNode, endNode, street, backStreet); startNode = endNode; @@ -439,7 +438,7 @@ private void buildBasicGraph() { LOG.info(progress.completeMessage()); } - private Optional getPlatform(OsmWay way) { + private Optional getPlatform(OsmWay way) { if (way.isBoardingLocation()) { var nodeRefs = way.getNodeRefs(); var size = nodeRefs.size(); @@ -455,7 +454,7 @@ private Optional getPlatform(OsmWay way) { var references = way.getMultiTagValues(params.boardingAreaRefTags()); return Optional.of( - new LinearPlatform( + new Platform( params.edgeNamer().getNameForWay(way, "platform " + way.getId()), geometry, references @@ -523,8 +522,7 @@ private StreetEdgePair getEdgesForStreet( OsmWay way, int index, StreetTraversalPermission permissions, - LineString geometry, - Optional platform + LineString geometry ) { // No point in returning edges that can't be traversed by anyone. if (permissions.allowsNothing()) { @@ -550,8 +548,7 @@ private StreetEdgePair getEdgesForStreet( length, permissionsFront, geometry, - false, - platform + false ); } if (permissionsBack.allowsAnything()) { @@ -564,8 +561,7 @@ private StreetEdgePair getEdgesForStreet( length, permissionsBack, backGeometry, - true, - platform + true ); } if (street != null && backStreet != null) { @@ -582,19 +578,14 @@ private StreetEdge getEdgeForStreet( double length, StreetTraversalPermission permissions, LineString geometry, - boolean back, - Optional platform + boolean back ) { String label = "way " + way.getId() + " from " + index; label = label.intern(); I18NString name = params.edgeNamer().getNameForWay(way, label); float carSpeed = way.getOsmProvider().getOsmTagMapper().getCarSpeedForWay(way, back); - var seb = platform - .>map(p -> new LinearPlatformEdgeBuilder().withPlatform(p)) - .orElse(new StreetEdgeBuilder<>()); - - seb + StreetEdgeBuilder seb = new StreetEdgeBuilder<>() .withFromVertex(startEndpoint) .withToVertex(endEndpoint) .withGeometry(geometry) diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java index c8a2b908893..e30e0dd19a7 100644 --- a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java +++ b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java @@ -2,7 +2,7 @@ import java.io.Serializable; import java.util.Optional; -import org.opentripplanner.service.osminfo.model.OsmWayReferences; +import org.opentripplanner.service.osminfo.model.Platform; import org.opentripplanner.street.model.edge.Edge; /** @@ -12,12 +12,12 @@ */ public interface OsmInfoGraphBuildRepository extends Serializable { /** - * TODO Add doc + * Associate the edge with a platform */ - void addReferences(Edge edge, OsmWayReferences info); + void addPlatform(Edge edge, Platform platform); /** - * TODO Add doc + * Find the platform the edge belongs to */ - Optional findReferences(Edge edge); + Optional findPlatform(Edge edge); } diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java index 784867ada30..6a50c3c92be 100644 --- a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java +++ b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java @@ -1,7 +1,7 @@ package org.opentripplanner.service.osminfo; import java.util.Optional; -import org.opentripplanner.service.osminfo.model.OsmWayReferences; +import org.opentripplanner.service.osminfo.model.Platform; import org.opentripplanner.street.model.edge.Edge; /** @@ -16,7 +16,10 @@ */ public interface OsmInfoGraphBuildService { /** - * TODO Add doc + * Find the platform the given edge is part of. + *

+ * TODO: This service currently only stores linear platforms, but area platforms and + * node platforms should be supported as well. */ - Optional findReferences(Edge edge); + Optional findPlatform(Edge edge); } diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java index 4ba575dc8b0..6505fdd67a4 100644 --- a/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java +++ b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java @@ -7,31 +7,31 @@ import java.util.Objects; import java.util.Optional; import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; -import org.opentripplanner.service.osminfo.model.OsmWayReferences; +import org.opentripplanner.service.osminfo.model.Platform; import org.opentripplanner.street.model.edge.Edge; public class DefaultOsmInfoGraphBuildRepository implements OsmInfoGraphBuildRepository, Serializable { - private final Map references = new HashMap<>(); + private final Map platforms = new HashMap<>(); @Inject public DefaultOsmInfoGraphBuildRepository() {} @Override - public void addReferences(Edge edge, OsmWayReferences info) { + public void addPlatform(Edge edge, Platform platform) { Objects.requireNonNull(edge); - Objects.requireNonNull(info); - this.references.put(edge, info); + Objects.requireNonNull(platform); + this.platforms.put(edge, platform); } @Override - public Optional findReferences(Edge edge) { - return Optional.ofNullable(references.get(edge)); + public Optional findPlatform(Edge edge) { + return Optional.ofNullable(platforms.get(edge)); } @Override public String toString() { - return "DefaultOsmInfoGraphBuildRepository{references size = " + references.size() + "}"; + return "DefaultOsmInfoGraphBuildRepository{platforms size = " + platforms.size() + "}"; } } diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java index 29271e17679..42eb5bf364f 100644 --- a/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java +++ b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java @@ -4,7 +4,7 @@ import java.util.Optional; import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository; import org.opentripplanner.service.osminfo.OsmInfoGraphBuildService; -import org.opentripplanner.service.osminfo.model.OsmWayReferences; +import org.opentripplanner.service.osminfo.model.Platform; import org.opentripplanner.street.model.edge.Edge; public class DefaultOsmInfoGraphBuildService implements OsmInfoGraphBuildService { @@ -17,8 +17,8 @@ public DefaultOsmInfoGraphBuildService(OsmInfoGraphBuildRepository repository) { } @Override - public Optional findReferences(Edge edge) { - return repository.findReferences(edge); + public Optional findPlatform(Edge edge) { + return repository.findPlatform(edge); } @Override diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/model/OsmWayReferences.java b/application/src/main/java/org/opentripplanner/service/osminfo/model/OsmWayReferences.java deleted file mode 100644 index 6208758f153..00000000000 --- a/application/src/main/java/org/opentripplanner/service/osminfo/model/OsmWayReferences.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.opentripplanner.service.osminfo.model; - -import java.io.Serializable; -import java.util.Collection; -import java.util.List; - -/** - * TODO : Add java doc - */ -public class OsmWayReferences implements Serializable { - - private final List references; - - public OsmWayReferences(Collection references) { - this.references = references.stream().sorted().distinct().toList(); - } - - /** - * TODO : Add java doc - * - * returns a sorted distinct list of references - */ - public List references() { - return references; - } -} \ No newline at end of file diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java b/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java new file mode 100644 index 00000000000..81ff388f69e --- /dev/null +++ b/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java @@ -0,0 +1,8 @@ +package org.opentripplanner.service.osminfo.model; + +import java.util.Set; +import org.locationtech.jts.geom.LineString; +import org.opentripplanner.framework.i18n.I18NString; + +public record Platform(I18NString name, LineString geometry, Set references) { +} \ No newline at end of file diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatform.java b/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatform.java deleted file mode 100644 index 688d4418530..00000000000 --- a/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatform.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.opentripplanner.street.model.edge; - -import java.io.Serializable; -import java.util.Set; -import org.locationtech.jts.geom.LineString; -import org.opentripplanner.framework.i18n.I18NString; - -public record LinearPlatform(I18NString name, LineString geometry, Set references) - implements Serializable {} diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdge.java b/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdge.java deleted file mode 100644 index 849266166a2..00000000000 --- a/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdge.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.opentripplanner.street.model.edge; - -public class LinearPlatformEdge extends StreetEdge { - - public final LinearPlatform platform; - - protected LinearPlatformEdge(LinearPlatformEdgeBuilder builder) { - super(builder); - platform = builder.platform(); - } -} diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdgeBuilder.java b/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdgeBuilder.java deleted file mode 100644 index 7057f9a7e42..00000000000 --- a/application/src/main/java/org/opentripplanner/street/model/edge/LinearPlatformEdgeBuilder.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opentripplanner.street.model.edge; - -public class LinearPlatformEdgeBuilder extends StreetEdgeBuilder { - - private LinearPlatform platform; - - @Override - public LinearPlatformEdge buildAndConnect() { - return Edge.connectToGraph(new LinearPlatformEdge(this)); - } - - public LinearPlatform platform() { - return platform; - } - - public LinearPlatformEdgeBuilder withPlatform(LinearPlatform platform) { - this.platform = platform; - return this; - } -} 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 e513a44fcd9..66858390ab5 100644 --- a/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/application/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -244,7 +244,6 @@ "class", "StreetEdge", "AreaEdge", - "LinearPlatformEdge", "EscalatorEdge", "PathwayEdge", "ElevatorHopEdge", @@ -421,7 +420,6 @@ "class", "StreetEdge", "AreaEdge", - "LinearPlatformEdge", "EscalatorEdge", "PathwayEdge", "ElevatorHopEdge", @@ -462,7 +460,6 @@ "class", "StreetEdge", "AreaEdge", - "LinearPlatformEdge", "EscalatorEdge", "PathwayEdge", "ElevatorHopEdge", @@ -495,7 +492,6 @@ "class", "StreetEdge", "AreaEdge", - "LinearPlatformEdge", "EscalatorEdge", "PathwayEdge", "ElevatorHopEdge", From 271f30587eec18e75e0a428b615c30b7a6bc7924 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 19 Dec 2024 11:15:38 +0000 Subject: [PATCH 201/269] move Herrenberg data into the test method --- .../OsmBoardingLocationsModuleTest.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java index 18c40bac898..e0cf32eda9b 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java @@ -42,16 +42,6 @@ class OsmBoardingLocationsModuleTest { private final TimetableRepositoryForTest testModel = TimetableRepositoryForTest.of(); - File file = ResourceLoader - .of(OsmBoardingLocationsModuleTest.class) - .file("herrenberg-minimal.osm.pbf"); - RegularStop platform = testModel - .stop("de:08115:4512:4:101") - .withCoordinate(48.59328, 8.86128) - .build(); - RegularStop busStop = testModel.stop("de:08115:4512:5:C", 48.59434, 8.86452).build(); - RegularStop floatingBusStop = testModel.stop("floating-bus-stop", 48.59417, 8.86464).build(); - static Stream testCases() { return Stream.of( Arguments.of( @@ -70,6 +60,16 @@ static Stream testCases() { ) @MethodSource("testCases") void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVertices) { + File file = ResourceLoader + .of(OsmBoardingLocationsModuleTest.class) + .file("herrenberg-minimal.osm.pbf"); + RegularStop platform = testModel + .stop("de:08115:4512:4:101") + .withCoordinate(48.59328, 8.86128) + .build(); + RegularStop busStop = testModel.stop("de:08115:4512:5:C", 48.59434, 8.86452).build(); + RegularStop floatingBusStop = testModel.stop("floating-bus-stop", 48.59417, 8.86464).build(); + var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); var timetableRepository = new TimetableRepository(new SiteRepository(), deduplicator); @@ -146,13 +146,13 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti assertEquals(1, platformCentroids.size()); - var platform = platformCentroids.get(0); + var platformCentroid = platformCentroids.get(0); - assertConnections(platform, Set.of(BoardingLocationToStopLink.class, AreaEdge.class)); + assertConnections(platformCentroid, Set.of(BoardingLocationToStopLink.class, AreaEdge.class)); assertEquals( linkedVertices, - platform + platformCentroid .getOutgoingStreetEdges() .stream() .map(Edge::getToVertex) @@ -162,7 +162,7 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti assertEquals( linkedVertices, - platform + platformCentroid .getIncomingStreetEdges() .stream() .map(Edge::getFromVertex) From 5cc681285d4ff5739a0aafe9895a3914f534c977 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 19 Dec 2024 16:15:40 +0100 Subject: [PATCH 202/269] Specify version in parent pom --- gtfs-realtime-protobuf/pom.xml | 4 ---- pom.xml | 7 ++++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gtfs-realtime-protobuf/pom.xml b/gtfs-realtime-protobuf/pom.xml index d3c3305b9b2..dd3990207c9 100644 --- a/gtfs-realtime-protobuf/pom.xml +++ b/gtfs-realtime-protobuf/pom.xml @@ -11,15 +11,11 @@ gtfs-realtime-protobuf OpenTripPlanner - GTFS Realtime (protobuf) - - 4.28.3 - com.google.protobuf protobuf-java - ${protobuf.version} diff --git a/pom.xml b/pom.xml index 4108973c7ff..98cf77db29e 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,7 @@ 2.0.15 1.27 4.0.5 + 4.28.3 UTF-8 opentripplanner/OpenTripPlanner @@ -485,7 +486,11 @@ java-snapshot-testing-junit5 2.3.0 - + + com.google.protobuf + protobuf-java + ${protobuf.version} + From eaafc689606dcd07d2da138cb5813c9cda9b3453 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 19 Dec 2024 15:22:20 +0000 Subject: [PATCH 203/269] add test for linear platform --- .../module/OsmBoardingLocationsModule.java | 23 +- .../graph_builder/module/osm/OsmModule.java | 7 +- .../service/osminfo/model/Platform.java | 3 +- .../OsmBoardingLocationsModuleTest.java | 224 +++++++++++++++++- .../graph_builder/module/moorgate.osm.pbf | Bin 0 -> 165687 bytes 5 files changed, 236 insertions(+), 21 deletions(-) create mode 100644 application/src/test/resources/org/opentripplanner/graph_builder/module/moorgate.osm.pbf diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java index 7e2ba334f2f..07c39cc2d8a 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java @@ -115,7 +115,6 @@ public void buildGraph() { private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { var stop = ts.getStop(); var stopCode = stop.getCode(); - var stopId = stop.getId().getId(); Envelope envelope = new Envelope(ts.getCoordinate()); double xscale = Math.cos(ts.getCoordinate().y * Math.PI / 180); @@ -151,17 +150,19 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { var nearbyEdges = new HashMap>(); for (var edge : index.getEdgesForEnvelope(envelope)) { - osmInfoGraphBuildService.findPlatform(edge).ifPresent(platform -> { - if (matchesReference(stop, platform.references())) { - if (!nearbyEdges.containsKey(platform)) { - var list = new ArrayList(); - list.add(edge); - nearbyEdges.put(platform, list); - } else { - nearbyEdges.get(platform).add(edge); + osmInfoGraphBuildService + .findPlatform(edge) + .ifPresent(platform -> { + if (matchesReference(stop, platform.references())) { + if (!nearbyEdges.containsKey(platform)) { + var list = new ArrayList(); + list.add(edge); + nearbyEdges.put(platform, list); + } else { + nearbyEdges.get(platform).add(edge); + } } - } - }); + }); } for (var platformEdgeList : nearbyEdges.entrySet()) { 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 673611f367f..195d36b9ed1 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 @@ -92,7 +92,12 @@ public static OsmModuleBuilder of( OsmInfoGraphBuildRepository osmInfoGraphBuildRepository, VehicleParkingRepository vehicleParkingRepository ) { - return new OsmModuleBuilder(providers, graph, osmInfoGraphBuildRepository, vehicleParkingRepository); + return new OsmModuleBuilder( + providers, + graph, + osmInfoGraphBuildRepository, + vehicleParkingRepository + ); } public static OsmModuleBuilder of( diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java b/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java index 81ff388f69e..91d78385a34 100644 --- a/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java +++ b/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java @@ -4,5 +4,4 @@ import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.i18n.I18NString; -public record Platform(I18NString name, LineString geometry, Set references) { -} \ No newline at end of file +public record Platform(I18NString name, LineString geometry, Set references) {} diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java index e0cf32eda9b..a4d5e86ced8 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java @@ -2,14 +2,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; +import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +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.opentripplanner.framework.geometry.SphericalDistanceLibrary; +import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.graph_builder.module.osm.OsmModule; import org.opentripplanner.osm.OsmProvider; @@ -34,15 +41,11 @@ import org.opentripplanner.transit.service.SiteRepository; import org.opentripplanner.transit.service.TimetableRepository; -/** - * We test that the platform area at Herrenberg station (https://www.openstreetmap.org/way/27558650) - * is correctly linked to the stop even though it is not the closest edge to the stop. - */ class OsmBoardingLocationsModuleTest { private final TimetableRepositoryForTest testModel = TimetableRepositoryForTest.of(); - static Stream testCases() { + static Stream herrenbergTestCases() { return Stream.of( Arguments.of( false, @@ -55,10 +58,14 @@ static Stream testCases() { ); } + /** + * We test that the platform area at Herrenberg station (https://www.openstreetmap.org/way/27558650) + * is correctly linked to the stop even though it is not the closest edge to the stop. + */ @ParameterizedTest( name = "add boarding locations and link them to platform edges when skipVisibility={0}" ) - @MethodSource("testCases") + @MethodSource("herrenbergTestCases") void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVertices) { File file = ResourceLoader .of(OsmBoardingLocationsModuleTest.class) @@ -69,7 +76,7 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti .build(); RegularStop busStop = testModel.stop("de:08115:4512:5:C", 48.59434, 8.86452).build(); RegularStop floatingBusStop = testModel.stop("floating-bus-stop", 48.59417, 8.86464).build(); - + var deduplicator = new Deduplicator(); var graph = new Graph(deduplicator); var timetableRepository = new TimetableRepository(new SiteRepository(), deduplicator); @@ -182,6 +189,201 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti .forEach(e -> assertEquals("Platform 101;102", e.getName().toString())); } + /** + * We test that the underground platforms at Moorgate station (https://www.openstreetmap.org/way/1328222021) + * is correctly linked to the stop even though it is not the closest edge to the stop. + */ + @Test + void testLinearPlatforms() { + var deduplicator = new Deduplicator(); + var graph = new Graph(deduplicator); + var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository(); + var osmModule = OsmModule + .of( + new OsmProvider( + ResourceLoader.of(OsmBoardingLocationsModuleTest.class).file("moorgate.osm.pbf"), + false + ), + graph, + osmInfoRepository, + new DefaultVehicleParkingRepository() + ) + .withBoardingAreaRefTags(Set.of("naptan:AtcoCode")) + .build(); + osmModule.buildGraph(); + + var factory = new VertexFactory(graph); + + class TestCase { + + /** + * The linear platform to be tested + */ + public final RegularStop platform; + + /** + * The label of a vertex where the centroid should be connected to + */ + public final VertexLabel beginLabel; + + /** + * The label of the other vertex where the centroid should be connected to + */ + public final VertexLabel endLabel; + + private TransitStopVertex platformVertex = null; + + public TestCase(RegularStop platform, VertexLabel beginLabel, VertexLabel endLabel) { + this.platform = platform; + this.beginLabel = beginLabel; + this.endLabel = endLabel; + } + + /** + * Get a TransitStopVertex for the platform in the graph. It is made and added to the graph + * on the first call. + */ + TransitStopVertex getPlatformVertex() { + if (platformVertex == null) { + platformVertex = factory.transitStop(TransitStopVertex.of().withStop(platform)); + } + return platformVertex; + } + } + + var testCases = List.of( + new TestCase( + testModel + .stop("9100MRGT9") + .withName(I18NString.of("Moorgate (Platform 9)")) + .withCoordinate(51.51922107872304, -0.08767468698832413) + .withPlatformCode("9") + .build(), + VertexLabel.osm(12288669589L), + VertexLabel.osm(12288675219L) + ), + new TestCase( + testModel + .stop("9400ZZLUMGT3") + .withName(I18NString.of("Moorgate (Platform 7)")) + .withCoordinate(51.51919235051611, -0.08769925990953176) + .withPlatformCode("7") + .build(), + VertexLabel.osm(12288669575L), + VertexLabel.osm(12288675230L) + ) + ); + + for (var testCase : testCases) { + // test that the platforms are not connected + var platformVertex = testCase.getPlatformVertex(); + assertEquals(0, platformVertex.getIncoming().size()); + assertEquals(0, platformVertex.getOutgoing().size()); + + // test that the vertices to be connected by the centroid are currently connected + var fromVertex = Objects.requireNonNull(graph.getVertex(testCase.beginLabel)); + var toVertex = Objects.requireNonNull(graph.getVertex(testCase.endLabel)); + assertTrue( + getEdge(fromVertex, toVertex).isPresent(), + "malformed test: the vertices where the centroid is supposed to be located between aren't connected" + ); + assertTrue( + getEdge(toVertex, fromVertex).isPresent(), + "malformed test: the vertices where the centroid is supposed to be located between aren't connected" + ); + } + + var timetableRepository = new TimetableRepository(new SiteRepository(), deduplicator); + new OsmBoardingLocationsModule( + graph, + new DefaultOsmInfoGraphBuildService(osmInfoRepository), + timetableRepository + ) + .buildGraph(); + + var boardingLocations = graph.getVerticesOfType(OsmBoardingLocationVertex.class); + + for (var testCase : testCases) { + var platformVertex = testCase.getPlatformVertex(); + var fromVertex = Objects.requireNonNull(graph.getVertex(testCase.beginLabel)); + var toVertex = Objects.requireNonNull(graph.getVertex(testCase.endLabel)); + + var centroid = boardingLocations + .stream() + .filter(b -> b.references.contains(testCase.platform.getId().getId())) + .findFirst() + .orElseThrow(); + + // TODO: we should ideally place the centroid vertex directly on the platform by splitting + // the platform edge, but it is too difficult to touch the splitter code to use a given + // centroid vertex instead of a generated split vertex, so what we actually do is to directly + // connect the platform vertex to the split vertex + + // the actual centroid isn't used + assertEquals(0, centroid.getDegreeIn()); + assertEquals(0, centroid.getDegreeOut()); + + for (var vertex : platformVertex.getIncoming()) { + assertSplitVertex(vertex.getFromVertex(), centroid, fromVertex, toVertex); + } + + for (var vertex : platformVertex.getOutgoing()) { + assertSplitVertex(vertex.getToVertex(), centroid, fromVertex, toVertex); + } + } + } + + /** + * Assert that a split vertex is near to the given centroid, and it is possible to travel between + * the original vertices through the split vertex in a straight line + */ + private static void assertSplitVertex( + Vertex splitVertex, + OsmBoardingLocationVertex centroid, + Vertex begin, + Vertex end + ) { + var distance = SphericalDistanceLibrary.distance( + splitVertex.getCoordinate(), + centroid.getCoordinate() + ); + // FIXME: I am not sure why the calculated centroid from the original OSM geometry is about 2 m + // from the platform + assertTrue(distance < 4, "The split vertex is more than 4 m apart from the centroid"); + assertConnections(splitVertex, begin, end); + + if (splitVertex != begin && splitVertex != end) { + var forwardEdges = getEdge(begin, splitVertex) + .flatMap(first -> getEdge(splitVertex, end).map(second -> List.of(first, second))); + var backwardEdges = getEdge(end, splitVertex) + .flatMap(first -> getEdge(splitVertex, begin).map(second -> List.of(first, second))); + for (var edgeList : List.of(forwardEdges, backwardEdges)) { + edgeList.ifPresent(edges -> + assertEquals( + edges.getFirst().getOutAngle(), + edges.getLast().getInAngle(), + "The split vertex is not on a straight line between the connected vertices" + ) + ); + } + } + } + + /** + * Assert that there is a one-way path from the beginning through the given vertex to the end + * or vice versa. + */ + private static void assertConnections(Vertex vertex, Vertex beginning, Vertex end) { + if (vertex == beginning || vertex == end) { + assertTrue(beginning.isConnected(end)); + } + + assertTrue( + (getEdge(beginning, vertex).isPresent() && getEdge(vertex, end).isPresent()) || + (getEdge(end, vertex).isPresent() && getEdge(vertex, beginning).isPresent()) + ); + } + private void assertConnections( OsmBoardingLocationVertex busBoardingLocation, Set> expected @@ -192,4 +394,12 @@ private void assertConnections( assertEquals(expected, edges.stream().map(Edge::getClass).collect(Collectors.toSet())) ); } + + private static Optional getEdge(Vertex from, Vertex to) { + return from + .getOutgoingStreetEdges() + .stream() + .filter(edge -> edge.getToVertex() == to) + .findFirst(); + } } diff --git a/application/src/test/resources/org/opentripplanner/graph_builder/module/moorgate.osm.pbf b/application/src/test/resources/org/opentripplanner/graph_builder/module/moorgate.osm.pbf new file mode 100644 index 0000000000000000000000000000000000000000..ee95a0e7c5165a9fcd3de992da15a4a065ac7009 GIT binary patch literal 165687 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

SsOp}4-6F_i%>4w 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 Date: Fri, 20 Dec 2024 14:12:33 +0100 Subject: [PATCH 204/269] refactor: Extract TextVariablesSubstitution from EnvironmentVariableReplacer. This enable us to use this in places where we do not want to inject environment variables. --- .../project/EnvironmentVariableReplacer.java | 67 ++++++------ .../utils/text/TextVariablesSubstitution.java | 102 ++++++++++++++++++ .../text/TextVariablesSubstitutionTest.java | 56 ++++++++++ 3 files changed, 192 insertions(+), 33 deletions(-) create mode 100644 utils/src/main/java/org/opentripplanner/utils/text/TextVariablesSubstitution.java create mode 100644 utils/src/test/java/org/opentripplanner/utils/text/TextVariablesSubstitutionTest.java diff --git a/application/src/main/java/org/opentripplanner/standalone/config/framework/project/EnvironmentVariableReplacer.java b/application/src/main/java/org/opentripplanner/standalone/config/framework/project/EnvironmentVariableReplacer.java index 17910fa62ca..c71c1237d3f 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/framework/project/EnvironmentVariableReplacer.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/framework/project/EnvironmentVariableReplacer.java @@ -3,12 +3,12 @@ import static java.util.Map.entry; import static org.opentripplanner.model.projectinfo.OtpProjectInfo.projectInfo; -import java.util.HashMap; import java.util.Map; import java.util.function.Function; -import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.Nullable; import org.opentripplanner.framework.application.OtpAppException; +import org.opentripplanner.utils.text.TextVariablesSubstitution; /** * Replaces environment variable placeholders specified on the format ${variable} in a text with the @@ -58,46 +58,47 @@ public class EnvironmentVariableReplacer { * Search for {@link #PATTERN}s and replace each placeholder with the value of the corresponding * environment variable. * - * @param source is used only to generate human friendly error message in case the text contain a - * placeholder which can not be found. - * @throws IllegalArgumentException if a placeholder exist in the {@code text}, but the - * environment variable do not exist. + * @param source is used only to generate a human friendly error message in case the text + * contains a placeholder which cannot be found. + * @throws IllegalArgumentException if a placeholder exists in the {@code text}, but the + * environment variable does not exist. */ public static String insertEnvironmentVariables(String text, String source) { - return insertVariables(text, source, System::getenv); + return insertVariables(text, source, EnvironmentVariableReplacer::getEnvVarOrProjectInfo); } + /** + * Same as {@link #insertEnvironmentVariables(String, String)}, but the caller mus provide the + * {@code variableResolver} - environment and project info variables are not available. + */ public static String insertVariables( String text, String source, - Function getEnvVar + Function variableResolver ) { - Map substitutions = new HashMap<>(); - Matcher matcher = PATTERN.matcher(text); + return TextVariablesSubstitution.insertVariables( + text, + variableResolver, + varName -> errorVariableNameNotFound(varName, source) + ); + } - while (matcher.find()) { - String subKey = matcher.group(0); - String nameOnly = matcher.group(1); - if (!substitutions.containsKey(nameOnly)) { - String value = getEnvVar.apply(nameOnly); - if (value != null) { - substitutions.put(subKey, value); - } else if (PROJECT_INFO.containsKey(nameOnly)) { - substitutions.put(subKey, PROJECT_INFO.get(nameOnly)); - } else { - throw new OtpAppException( - "Environment variable name '" + - nameOnly + - "' in config '" + - source + - "' not found in the system environment variables." - ); - } - } + @Nullable + private static String getEnvVarOrProjectInfo(String key) { + String value = System.getenv(key); + if (value == null) { + return PROJECT_INFO.get(key); } - for (Map.Entry entry : substitutions.entrySet()) { - text = text.replace(entry.getKey(), entry.getValue()); - } - return text; + return value; + } + + private static void errorVariableNameNotFound(String variableName, String source) { + throw new OtpAppException( + "Environment variable name '" + + variableName + + "' in config '" + + source + + "' not found in the system environment variables." + ); } } diff --git a/utils/src/main/java/org/opentripplanner/utils/text/TextVariablesSubstitution.java b/utils/src/main/java/org/opentripplanner/utils/text/TextVariablesSubstitution.java new file mode 100644 index 00000000000..95226ed4bd0 --- /dev/null +++ b/utils/src/main/java/org/opentripplanner/utils/text/TextVariablesSubstitution.java @@ -0,0 +1,102 @@ +package org.opentripplanner.utils.text; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This utility class substitute variable placeholders in a given text on the format ${variable}. + * + * The pattern matching a placeholder must start with '${' and end with '}'. The variable name + * must consist of only alphanumerical characters (a-z, A-Z, 0-9), dot `.` and underscore '_'. + */ +public class TextVariablesSubstitution { + + private static final Pattern PATTERN = Pattern.compile("\\$\\{([.\\w]+)}"); + + /** + * This method uses the {@link #insertVariables(String, Function, Consumer)} to substitute + * all variable tokens in all values in the given {@code properties}. It supports nesting, but + * you must avoid cyclic references. + *

+ * Example: + *

+   *   a -> My car is a ${b} car, with an ${c} look.
+   *   b -> good old ${c}
+   *   c -> fancy
+   * 
+ * This will resolve to: + *
+   *   a -> My car is a good old fancy car, with an fancy look.
+   *   b -> good old fancy
+   *   c -> fancy
+   * 
+ */ + public static Map insertVariables( + Map properties, + Consumer errorHandler + ) { + var result = new HashMap(properties); + + for (String key : result.keySet()) { + var value = result.get(key); + var sub = insertVariables(value, result::get, errorHandler); + if (!value.equals(sub)) { + result.put(key, sub); + } + } + return result; + } + + /** + * Replace all variables({@code ${variable.name}}) in the given {@code text}. The given + * {@code variableProvider} is used to look up values to insert into the text replacing the + * variable token. + * + * @param errorHandler The error handler is called if a variable key does not exist in the + * {@code variableProvider}. + * @return the new value with all variables replaced. + */ + public static String insertVariables( + String text, + Function variableProvider, + Consumer errorHandler + ) { + return insert(text, PATTERN.matcher(text), variableProvider, errorHandler); + } + + private static String insert( + String text, + Matcher matcher, + Function variableProvider, + Consumer errorHandler + ) { + boolean matchFound = matcher.find(); + if (!matchFound) { + return text; + } + + Map substitutions = new HashMap<>(); + + while (matchFound) { + String subKey = matcher.group(0); + String nameOnly = matcher.group(1); + if (!substitutions.containsKey(nameOnly)) { + String value = variableProvider.apply(nameOnly); + if (value != null) { + substitutions.put(subKey, value); + } else { + errorHandler.accept(nameOnly); + } + } + matchFound = matcher.find(); + } + for (Map.Entry entry : substitutions.entrySet()) { + text = text.replace(entry.getKey(), entry.getValue()); + } + return insert(text, PATTERN.matcher(text), variableProvider, errorHandler); + } +} diff --git a/utils/src/test/java/org/opentripplanner/utils/text/TextVariablesSubstitutionTest.java b/utils/src/test/java/org/opentripplanner/utils/text/TextVariablesSubstitutionTest.java new file mode 100644 index 00000000000..5c1c2014cc2 --- /dev/null +++ b/utils/src/test/java/org/opentripplanner/utils/text/TextVariablesSubstitutionTest.java @@ -0,0 +1,56 @@ +package org.opentripplanner.utils.text; + +import static java.util.Map.entry; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.opentripplanner.utils.text.TextVariablesSubstitution.insertVariables; + +import java.util.Map; +import org.junit.jupiter.api.Test; + +class TextVariablesSubstitutionTest { + + @Test + void testInsertVariablesInProperties() { + Map map = Map.ofEntries( + entry("a", "A"), + entry("b", "B"), + entry("ab", "${a}${b}"), + entry("ab2", "${ab} - ${a} - ${b}") + ); + + var result = insertVariables(map, this::errorHandler); + + assertEquals("A", result.get("a")); + assertEquals("B", result.get("b")); + assertEquals("AB", result.get("ab")); + assertEquals("AB - A - B", result.get("ab2")); + } + + @Test + void testInsertVariablesInValue() { + var map = Map.ofEntries( + entry("a", "A"), + entry("b", "B"), + entry("ab", "${a}${b}"), + entry("ab2", "${ab} - ${a} - ${b}") + ); + + assertEquals( + "No substitution", + insertVariables("No substitution", map::get, this::errorHandler) + ); + assertEquals("A B", insertVariables("${a} ${b}", map::get, this::errorHandler)); + assertEquals("AB", insertVariables("${ab}", map::get, this::errorHandler)); + assertEquals("AB - A - B", insertVariables("${ab2}", map::get, this::errorHandler)); + var ex = assertThrows( + IllegalArgumentException.class, + () -> insertVariables("${c}", map::get, this::errorHandler) + ); + assertEquals("c", ex.getMessage()); + } + + private void errorHandler(String name) { + throw new IllegalArgumentException(name); + } +} From 48ccd365b6c683481a86435e8504d0b2a848e9ff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:30:17 +0000 Subject: [PATCH 205/269] fix(deps): update dependency org.entur:siri-java-model to v1.28 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 27eada4007b..f54278105ef 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 9.12.0 2.0.16 2.0.15 - 1.27 + 1.28 4.0.5 UTF-8 From 6e8ccffaac61bcbb19d52355bd9a6363b35e09d1 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Mon, 23 Dec 2024 14:16:44 +0000 Subject: [PATCH 206/269] Add changelog entry for #6303 [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 a5eff7c9b2a..a3d798051a7 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -67,6 +67,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - 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) - Make flex linking work together with boarding locations [#6311](https://github.com/opentripplanner/OpenTripPlanner/pull/6311) +- Add fallback name for corridors [#6303](https://github.com/opentripplanner/OpenTripPlanner/pull/6303) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From ac8058b23903ad14fe8a4e21e91dc170e6cb1591 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 04:18:58 +0000 Subject: [PATCH 207/269] chore(deps): update dependency mkdocs-material to v9.5.49 (#6352) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- doc/user/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/requirements.txt b/doc/user/requirements.txt index ee0eb31d65c..7adb0f88f82 100644 --- a/doc/user/requirements.txt +++ b/doc/user/requirements.txt @@ -1,4 +1,4 @@ mkdocs==1.6.1 -mkdocs-material==9.5.39 +mkdocs-material==9.5.49 mike@git+https://github.com/jimporter/mike.git@f0522f245e64687dd18384fbd86b721175711474 mkdocs-no-sitemap-plugin==0.0.1 From 7f4ee90c46416b82705fca35ce380adca8fab999 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 06:23:59 +0000 Subject: [PATCH 208/269] fix(deps): update lucene.version to v10 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 27eada4007b..bf817576cdc 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 1.14.1 5.6.0 1.5.12 - 9.12.0 + 10.1.0 2.0.16 2.0.15 1.27 From 1c8bf7b5d795c46152659fda3498c1cd1f9f280f Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 2 Jan 2025 12:23:00 +0100 Subject: [PATCH 209/269] feature: Add the ability to load custom documentation from a properties file --- .../custom-documentation-entur.properties | 27 +++ .../injectdoc/ApiDocumentationProfile.java | 27 +++ .../injectdoc/CustomDocumentation.java | 171 ++++++++++++++++++ .../injectdoc/CustomDocumentationTest.java | 76 ++++++++ 4 files changed, 301 insertions(+) create mode 100644 application/src/ext/resources/org/opentripplanner/ext/apis/transmodel/custom-documentation-entur.properties create mode 100644 application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/ApiDocumentationProfile.java create mode 100644 application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/CustomDocumentation.java create mode 100644 application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/CustomDocumentationTest.java diff --git a/application/src/ext/resources/org/opentripplanner/ext/apis/transmodel/custom-documentation-entur.properties b/application/src/ext/resources/org/opentripplanner/ext/apis/transmodel/custom-documentation-entur.properties new file mode 100644 index 00000000000..29c44b67f96 --- /dev/null +++ b/application/src/ext/resources/org/opentripplanner/ext/apis/transmodel/custom-documentation-entur.properties @@ -0,0 +1,27 @@ +# Use: +# [.].(description|deprecated)[.append] +# +# Examples +# // Replace the existing type description +# Quay.description=The place for boarding/alighting a vehicle +# +# // Append to the existing type description +# Quay.description.append=Append +# +# // Replace the existing field description +# Quay.name.description=The public name +# +# // Append to the existing field description +# Quay.name.description.append=(Source NSR) +# +# // Insert deprecated reason. Due to a bug in the Java GraphQL lib, an existing deprecated +# // reason cannot be updated. Deleting the reason from the schema, and adding it back using +# // the "default" TransmodelApiDocumentationProfile is a workaround. +# Quay.name.deprecated=This field is deprecated ... + + +TariffZone.description=A **zone** used to define a zonal fare structure in a zone-counting or \ + zone-matrix system. This includes TariffZone, as well as the specialised FareZone elements. \ + TariffZones are deprecated, please use FareZones. \ + \ + **TariffZone data will not be maintained from 1. MAY 2025 (Entur).** diff --git a/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/ApiDocumentationProfile.java b/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/ApiDocumentationProfile.java new file mode 100644 index 00000000000..71b4e06a864 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/ApiDocumentationProfile.java @@ -0,0 +1,27 @@ +package org.opentripplanner.apis.support.graphql.injectdoc; + +import org.opentripplanner.framework.doc.DocumentedEnum; + +public enum ApiDocumentationProfile implements DocumentedEnum { + DEFAULT, + ENTUR; + + private static final String TYPE_DOC = + "List of available custom documentation profiles. " + + "The default should be used in most cases. A profile may be used to deprecate part of the " + + "API in case it is not supported."; + + @Override + public String typeDescription() { + return TYPE_DOC; + } + + @Override + public String enumValueDescription() { + return switch (this) { + case DEFAULT -> "Default documentation is used."; + case ENTUR -> "Entur specific documentation. This deprecate features not supported at Entur," + + " Norway."; + }; + } +} diff --git a/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/CustomDocumentation.java b/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/CustomDocumentation.java new file mode 100644 index 00000000000..44629659b96 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/CustomDocumentation.java @@ -0,0 +1,171 @@ +package org.opentripplanner.apis.support.graphql.injectdoc; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import javax.annotation.Nullable; +import org.opentripplanner.framework.application.OtpAppException; +import org.opentripplanner.utils.text.TextVariablesSubstitution; + +/** + * Load custom documentation from a properties file and make it available to any + * consumer using the {@code type-name[.field-name]} as key for lookups. + */ +public class CustomDocumentation { + + private static final String APPEND_SUFFIX = ".append"; + private static final String DESCRIPTION_SUFFIX = ".description"; + private static final String DEPRECATED_SUFFIX = ".deprecated"; + + /** Put custom documentaion in the following sandbox package */ + private static final String DOC_PATH = "org/opentripplanner/ext/apis/transmodel/"; + private static final String FILE_NAME = "custom-documentation"; + private static final String FILE_EXTENSION = ".properties"; + + private static final CustomDocumentation EMPTY = new CustomDocumentation(Map.of()); + + private final Map textMap; + + /** + * Pacakge local to be unit-testable + */ + CustomDocumentation(Map textMap) { + this.textMap = textMap; + } + + public static CustomDocumentation of(ApiDocumentationProfile profile) { + if (profile == ApiDocumentationProfile.DEFAULT) { + return EMPTY; + } + var map = loadCustomDocumentationFromPropertiesFile(profile); + return map.isEmpty() ? EMPTY : new CustomDocumentation(map); + } + + public boolean isEmpty() { + return textMap.isEmpty(); + } + + /** + * Get documentation for a type. The given {@code typeName} is used as the key. The + * documentation text is resolved by: + *
    + *
  1. + * first looking up the given {@code key} + {@code ".description"}. If a value is found, then + * the value is returned. + *
  2. + * then {@code key} + {@code ".description.append"} is used. If a value is found the + * {@code originalDoc} + {@code value} is returned. + *
  3. + *
+ * @param typeName Use {@code TYPE_NAME} or {@code TYPE_NAME.FIELD_NAME} as key. + */ + public Optional typeDescription(String typeName, @Nullable String originalDoc) { + return text(typeName, DESCRIPTION_SUFFIX, originalDoc); + } + + /** + * Same as {@link #typeDescription(String, String)} except the given {@code typeName} and + * {@code fieldName} is used as the key. + *
+   * key := typeName + "." fieldNAme
+   * 
+ */ + public Optional fieldDescription( + String typeName, + String fieldName, + @Nullable String originalDoc + ) { + return text(key(typeName, fieldName), DESCRIPTION_SUFFIX, originalDoc); + } + + /** + * Get deprecated reason for a field (types cannot be deprecated). The key + * ({@code key = typeName + '.' + fieldName} is used to retrieve the reason from the properties + * file. The deprecated documentation text is resolved by: + *
    + *
  1. + * first looking up the given {@code key} + {@code ".deprecated"}. If a value is found, then + * the value is returned. + *
  2. + * then {@code key} + {@code ".deprecated.append"} is used. If a value is found the + * {@code originalDoc} + {@code text} is returned. + *
  3. + *
+ * Any {@code null} values are excluded from the result and if both the input {@code originalDoc} + * and the resolved value is {@code null}, then {@code empty} is returned. + */ + public Optional fieldDeprecatedReason( + String typeName, + String fieldName, + @Nullable String originalDoc + ) { + return text(key(typeName, fieldName), DEPRECATED_SUFFIX, originalDoc); + } + + /* private methods */ + + /** + * Create a key from the given {@code typeName} and {@code fieldName} + */ + private static String key(String typeName, String fieldName) { + return typeName + "." + fieldName; + } + + private Optional text(String key, String suffix, @Nullable String originalText) { + final String k = key + suffix; + return text(k).or(() -> appendText(k, originalText)); + } + + private Optional text(String key) { + return Optional.ofNullable(textMap.get(key)); + } + + private Optional appendText(String key, @Nullable String originalText) { + String value = textMap.get(key + APPEND_SUFFIX); + if (value == null) { + return Optional.empty(); + } + return originalText == null ? Optional.of(value) : Optional.of(originalText + "\n\n" + value); + } + + /* private methods */ + + private static Map loadCustomDocumentationFromPropertiesFile( + ApiDocumentationProfile profile + ) { + try { + final String resource = resourceName(profile); + var input = ClassLoader.getSystemResourceAsStream(resource); + if (input == null) { + throw new OtpAppException("Resource not found: %s", resource); + } + var props = new Properties(); + props.load(input); + Map map = new HashMap<>(); + + for (String key : props.stringPropertyNames()) { + String value = props.getProperty(key); + if (value == null) { + value = ""; + } + map.put(key, value); + } + return TextVariablesSubstitution.insertVariables( + map, + varName -> errorHandlerVariableSubstitution(varName, resource) + ); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static void errorHandlerVariableSubstitution(String name, String source) { + throw new OtpAppException("Variable substitution failed for '${%s}' in %s.", name, source); + } + + private static String resourceName(ApiDocumentationProfile profile) { + return DOC_PATH + FILE_NAME + "-" + profile.name().toLowerCase() + FILE_EXTENSION; + } +} diff --git a/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/CustomDocumentationTest.java b/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/CustomDocumentationTest.java new file mode 100644 index 00000000000..dc9356530b6 --- /dev/null +++ b/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/CustomDocumentationTest.java @@ -0,0 +1,76 @@ +package org.opentripplanner.apis.support.graphql.injectdoc; + +import static java.util.Optional.empty; +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.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class CustomDocumentationTest { + + private static final String ORIGINAL_DOC = "Original"; + + // We use a HashMap to allow inserting 'null' values + private static final Map PROPERTIES = new HashMap<>(Map.ofEntries()); + + static { + PROPERTIES.put("Type1.description", "Doc 1"); + PROPERTIES.put("Type2.description.append", "Doc 2"); + PROPERTIES.put("Type3.description", null); + PROPERTIES.put("Type.field1.description", "Doc f1"); + PROPERTIES.put("Type.field2.deprecated", "Deprecated f2"); + PROPERTIES.put("Type.field3.description.append", "Doc f3"); + PROPERTIES.put("Type.field4.deprecated.append", "Deprecated f4"); + PROPERTIES.put("Type.field5.description", null); + } + + private final CustomDocumentation subject = new CustomDocumentation(PROPERTIES); + + @Test + void testCreate() { + var defaultDoc = CustomDocumentation.of(ApiDocumentationProfile.DEFAULT); + assertTrue(defaultDoc.isEmpty()); + + var enturDoc = CustomDocumentation.of(ApiDocumentationProfile.ENTUR); + assertFalse(enturDoc.isEmpty()); + } + + @Test + void testTypeDescriptionWithUnknownKey() { + assertEquals(empty(), subject.typeDescription("", ORIGINAL_DOC)); + assertEquals(empty(), subject.typeDescription("ANY_KEY", ORIGINAL_DOC)); + assertEquals(empty(), subject.typeDescription("ANY_KEY", null)); + } + + @Test + void testTypeDescription() { + assertEquals(Optional.of("Doc 1"), subject.typeDescription("Type1", ORIGINAL_DOC)); + assertEquals( + Optional.of(ORIGINAL_DOC + "\n\nDoc 2"), + subject.typeDescription("Type2", ORIGINAL_DOC) + ); + assertEquals(Optional.empty(), subject.typeDescription("Type3", ORIGINAL_DOC)); + } + + @Test + void testFieldDescription() { + assertEquals(Optional.of("Doc f1"), subject.fieldDescription("Type", "field1", ORIGINAL_DOC)); + assertEquals( + Optional.of("Deprecated f2"), + subject.fieldDeprecatedReason("Type", "field2", ORIGINAL_DOC) + ); + assertEquals( + Optional.of("Original\n\nDoc f3"), + subject.fieldDescription("Type", "field3", ORIGINAL_DOC) + ); + assertEquals( + Optional.of("Original\n\nDeprecated f4"), + subject.fieldDeprecatedReason("Type", "field4", ORIGINAL_DOC) + ); + assertEquals(Optional.empty(), subject.fieldDeprecatedReason("Type", "field5", ORIGINAL_DOC)); + } +} From 85e124f3dfd88359fc0732b542441f7ea13403c3 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 2 Jan 2025 12:25:07 +0100 Subject: [PATCH 210/269] feature: Make a GraphQL schema visitor to inject custom documentation --- .../injectdoc/InjectCustomDocumentation.java | 173 ++++++++++++++++++ .../InjectCustomDocumentationTest.java | 134 ++++++++++++++ .../InjectCustomDocumentationTest.graphql | 52 ++++++ ...ctCustomDocumentationTest.graphql.expected | 93 ++++++++++ 4 files changed, 452 insertions(+) create mode 100644 application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentation.java create mode 100644 application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java create mode 100644 application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql create mode 100644 application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql.expected diff --git a/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentation.java b/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentation.java new file mode 100644 index 00000000000..f2793a4e6c3 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentation.java @@ -0,0 +1,173 @@ +package org.opentripplanner.apis.support.graphql.injectdoc; + +import static graphql.util.TraversalControl.CONTINUE; + +import graphql.schema.GraphQLEnumType; +import graphql.schema.GraphQLEnumValueDefinition; +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLInputObjectField; +import graphql.schema.GraphQLInputObjectType; +import graphql.schema.GraphQLInterfaceType; +import graphql.schema.GraphQLNamedSchemaElement; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLScalarType; +import graphql.schema.GraphQLSchemaElement; +import graphql.schema.GraphQLTypeVisitor; +import graphql.schema.GraphQLTypeVisitorStub; +import graphql.schema.GraphQLUnionType; +import graphql.util.TraversalControl; +import graphql.util.TraverserContext; +import java.util.Optional; +import java.util.function.BiFunction; + +/** + * This is GraphQL visitor witch inject custom documentation on types and fields. + */ +public class InjectCustomDocumentation + extends GraphQLTypeVisitorStub + implements GraphQLTypeVisitor { + + private final CustomDocumentation customDocumentation; + + public InjectCustomDocumentation(CustomDocumentation customDocumentation) { + this.customDocumentation = customDocumentation; + } + + @Override + public TraversalControl visitGraphQLScalarType( + GraphQLScalarType scalar, + TraverserContext context + ) { + return typeDoc(context, scalar, (s, doc) -> s.transform(b -> b.description(doc))); + } + + @Override + public TraversalControl visitGraphQLInterfaceType( + GraphQLInterfaceType interface_, + TraverserContext context + ) { + return typeDoc(context, interface_, (f, doc) -> f.transform(b -> b.description(doc))); + } + + @Override + public TraversalControl visitGraphQLEnumType( + GraphQLEnumType enumType, + TraverserContext context + ) { + return typeDoc(context, enumType, (f, doc) -> f.transform(b -> b.description(doc))); + } + + @Override + public TraversalControl visitGraphQLEnumValueDefinition( + GraphQLEnumValueDefinition enumValue, + TraverserContext context + ) { + return fieldDoc( + context, + enumValue, + enumValue.getDeprecationReason(), + (f, doc) -> f.transform(b -> b.description(doc)), + (f, reason) -> f.transform(b -> b.deprecationReason(reason)) + ); + } + + @Override + public TraversalControl visitGraphQLFieldDefinition( + GraphQLFieldDefinition field, + TraverserContext context + ) { + return fieldDoc( + context, + field, + field.getDeprecationReason(), + (f, doc) -> f.transform(b -> b.description(doc)), + (f, reason) -> f.transform(b -> b.deprecate(reason)) + ); + } + + @Override + public TraversalControl visitGraphQLInputObjectField( + GraphQLInputObjectField inputField, + TraverserContext context + ) { + return fieldDoc( + context, + inputField, + inputField.getDeprecationReason(), + (f, doc) -> f.transform(b -> b.description(doc)), + (f, reason) -> f.transform(b -> b.deprecate(reason)) + ); + } + + @Override + public TraversalControl visitGraphQLInputObjectType( + GraphQLInputObjectType inputType, + TraverserContext context + ) { + return typeDoc(context, inputType, (f, doc) -> f.transform(b -> b.description(doc))); + } + + @Override + public TraversalControl visitGraphQLObjectType( + GraphQLObjectType object, + TraverserContext context + ) { + return typeDoc(context, object, (f, doc) -> f.transform(b -> b.description(doc))); + } + + @Override + public TraversalControl visitGraphQLUnionType( + GraphQLUnionType union, + TraverserContext context + ) { + return typeDoc(context, union, (f, doc) -> f.transform(b -> b.description(doc))); + } + + /* private methods */ + + /** + * Set or append description on a Scalar, Object, InputType, Union, Interface or Enum. + */ + private TraversalControl typeDoc( + TraverserContext context, + T element, + BiFunction setDescription + ) { + customDocumentation + .typeDescription(element.getName(), element.getDescription()) + .map(doc -> setDescription.apply(element, doc)) + .ifPresent(f -> changeNode(context, f)); + return CONTINUE; + } + + /** + * Set or append description and deprecated reason on a field [Object, InputType, Interface, + * Union or Enum]. + */ + private TraversalControl fieldDoc( + TraverserContext context, + T field, + String originalDeprecatedReason, + BiFunction setDescription, + BiFunction setDeprecatedReason + ) { + // All fields need to be defined in a named element + if (!(context.getParentNode() instanceof GraphQLNamedSchemaElement parent)) { + throw new IllegalArgumentException("The field does not have a named parent: " + field); + } + var fieldName = field.getName(); + var typeName = parent.getName(); + + Optional f1 = customDocumentation + .fieldDescription(typeName, fieldName, field.getDescription()) + .map(doc -> setDescription.apply(field, doc)); + + Optional f2 = customDocumentation + .fieldDeprecatedReason(typeName, fieldName, originalDeprecatedReason) + .map(doc -> setDeprecatedReason.apply(f1.orElse(field), doc)); + + f2.or(() -> f1).ifPresent(f -> changeNode(context, f)); + + return CONTINUE; + } +} diff --git a/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java b/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java new file mode 100644 index 00000000000..0f326a373aa --- /dev/null +++ b/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java @@ -0,0 +1,134 @@ +package org.opentripplanner.apis.support.graphql.injectdoc; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import graphql.schema.Coercing; +import graphql.schema.GraphQLScalarType; +import graphql.schema.GraphQLSchema; +import graphql.schema.SchemaTransformer; +import graphql.schema.idl.RuntimeWiring; +import graphql.schema.idl.SchemaGenerator; +import graphql.schema.idl.SchemaParser; +import graphql.schema.idl.SchemaPrinter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * This test read in a schema file, inject documentation and convert the + * new schema to an SDL text string. The result is then compared to the + * "expected" SDL file. The input and expected files are found in the + * resources - with the same name as this test. + *

+ * Note! There is a bug in the Java GraphQL library. Existing deprecated reasons + * cannot be changed or replaced. This test adds test-cases for this, but excludes + * them from the expected result. If this is fixed in the GraphQL library, this + * test will fail, and should be updated by updating the expected result. + */ +class InjectCustomDocumentationTest { + + private GraphQLSchema schema; + private String sdl; + private String sdlExpected; + + @BeforeEach + void setUp() throws IOException { + sdl = loadSchemaResource(".graphql"); + sdlExpected = loadSchemaResource(".graphql.expected"); + + var parser = new SchemaParser(); + var generator = new SchemaGenerator(); + var typeRegistry = parser.parse(sdl); + schema = generator.makeExecutableSchema(typeRegistry, buildRuntimeWiring()); + } + + private static RuntimeWiring buildRuntimeWiring() { + return RuntimeWiring + .newRuntimeWiring() + .type("QueryType", b -> b.dataFetcher("listE", e -> List.of())) + .type("En", b -> b.enumValues(n -> n)) + .type("AB", b -> b.typeResolver(it -> null)) + .type("AC", b -> b.typeResolver(it -> null)) + .scalar( + GraphQLScalarType + .newScalar() + .name("Duration") + .coercing(new Coercing() {}) + .build() + ) + .build(); + } + + /** + * Return a map of documentation key/values. The + * value is the same as the key for easy recognition. + */ + static Map text() { + return Stream + .of( + "AB.description", + "AC.description.append", + "AType.description", + "AType.a.description", + "AType.b.deprecated", + "BType.description", + "BType.a.description", + "BType.a.deprecated", + "CType.description.append", + "CType.a.description.append", + "CType.b.deprecated.append", + "QueryType.findAB.description", + "QueryType.getAC.deprecated", + "AEnum.description", + "AEnum.E1.description", + "AEnum.E2.deprecated", + "Duration.description", + "InputType.description", + "InputType.a.description", + "InputType.b.deprecated" + ) + .collect(Collectors.toMap(e -> e, e -> e)); + } + + @Test + void test() { + Map texts = text(); + var customDocumentation = new CustomDocumentation(texts); + var visitor = new InjectCustomDocumentation(customDocumentation); + var newSchema = SchemaTransformer.transformSchema(schema, visitor); + var p = new SchemaPrinter(); + var result = p + .print(newSchema) + // Some editors like IntelliJ remove space characters at the end of a + // line, so we do the same here to avoid false positive results. + .replaceAll(" +\\n", "\n"); + + var missingValues = texts + .values() + .stream() + .sorted() + .filter(it -> !result.contains(it)) + .toList(); + + // There is a bug in the Java GraphQL API, existing deprecated + // doc is not updated or replaced. + var expected = List.of("BType.a.deprecated", "CType.b.deprecated.append"); + + assertEquals(expected, missingValues); + assertEquals(sdlExpected, result); + } + + String loadSchemaResource(String suffix) throws IOException { + var cl = getClass(); + var name = cl.getName().replace('.', '/') + suffix; + return new String( + ClassLoader.getSystemResourceAsStream(name).readAllBytes(), + StandardCharsets.UTF_8 + ); + } +} diff --git a/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql b/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql new file mode 100644 index 00000000000..599c4d3b12a --- /dev/null +++ b/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql @@ -0,0 +1,52 @@ +schema { + query: QueryType +} + +"REPLACE" +union AB = AType | BType + +"APPEND TO" +union AC = AType | BType + +# Add doc to an undocumented type +type AType { + a: Duration + b: String +} + +# Replace existing doc +"REPLACE" +type BType { + a: String @deprecated(reason: "REPLACE") +} + +# Append doc to existing documentation +"APPEND TO" +type CType { + "APPENT TO" + a: Duration + b: String @deprecated(reason: "APPEND TO") +} + +type QueryType { + # Add doc to method - args is currently not supported + findAB(args: InputType): AB + getAC: AC + listCs: CType + listEs: [AEnum] +} + +# Add doc to enums +enum AEnum { + E1 + E2 +} + +# Add doc to scalar +scalar Duration + +# Add doc to input type +input InputType { + a: String + b: String +} diff --git a/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql.expected b/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql.expected new file mode 100644 index 00000000000..c8cb7f680bf --- /dev/null +++ b/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql.expected @@ -0,0 +1,93 @@ +schema { + query: QueryType +} + +"Marks the field, argument, input field or enum value as deprecated" +directive @deprecated( + "The reason for the deprecation" + reason: String = "No longer supported" + ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION + +"Directs the executor to include this field or fragment only when the `if` argument is true" +directive @include( + "Included when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Indicates an Input Object is a OneOf Input Object." +directive @oneOf on INPUT_OBJECT + +"Directs the executor to skip this field or fragment when the `if` argument is true." +directive @skip( + "Skipped when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Exposes a URL that specifies the behaviour of this scalar." +directive @specifiedBy( + "The URL that specifies the behaviour of this scalar." + url: String! + ) on SCALAR + +"AB.description" +union AB = AType | BType + +""" +APPEND TO + +AC.description.append +""" +union AC = AType | BType + +"AType.description" +type AType { + "AType.a.description" + a: Duration + b: String @deprecated(reason : "AType.b.deprecated") +} + +"BType.description" +type BType { + "BType.a.description" + a: String @deprecated(reason : "REPLACE") +} + +""" +APPEND TO + +CType.description.append +""" +type CType { + """ + APPENT TO + + CType.a.description.append + """ + a: Duration + b: String @deprecated(reason : "APPEND TO") +} + +type QueryType { + "QueryType.findAB.description" + findAB(args: InputType): AB + getAC: AC @deprecated(reason : "QueryType.getAC.deprecated") + listCs: CType + listEs: [AEnum] +} + +"AEnum.description" +enum AEnum { + "AEnum.E1.description" + E1 + E2 @deprecated(reason : "AEnum.E2.deprecated") +} + +"Duration.description" +scalar Duration + +"InputType.description" +input InputType { + "InputType.a.description" + a: String + b: String @deprecated(reason : "InputType.b.deprecated") +} From d2fd01b2ca7c2a664f9e12996fe6ffa8cc968bbd Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 2 Jan 2025 12:33:24 +0100 Subject: [PATCH 211/269] feature: Make the API documentation profile configurable --- .../injectdoc/ApiDocumentationProfile.java | 9 +- .../config/routerconfig/ServerConfig.java | 26 ++++- doc/user/RouterConfiguration.md | 95 +++++++++++-------- 3 files changed, 83 insertions(+), 47 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/ApiDocumentationProfile.java b/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/ApiDocumentationProfile.java index 71b4e06a864..1ed63a9bd96 100644 --- a/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/ApiDocumentationProfile.java +++ b/application/src/main/java/org/opentripplanner/apis/support/graphql/injectdoc/ApiDocumentationProfile.java @@ -7,9 +7,12 @@ public enum ApiDocumentationProfile implements DocumentedEnum traceParameters; + private final ApiDocumentationProfile apiDocumentationProfile; public ServerConfig(String parameterName, NodeAdapter root) { NodeAdapter c = root @@ -42,6 +46,14 @@ public ServerConfig(String parameterName, NodeAdapter root) { ) .asDuration(Duration.ofSeconds(-1)); + this.apiDocumentationProfile = + c + .of("apiDocumentationProfile") + .since(V2_7) + .summary(ApiDocumentationProfile.DEFAULT.typeDescription()) + .description(docEnumValueList(ApiDocumentationProfile.values())) + .asEnum(ApiDocumentationProfile.DEFAULT); + this.traceParameters = c .of("traceParameters") @@ -105,6 +117,15 @@ public Duration apiProcessingTimeout() { return apiProcessingTimeout; } + @Override + public List traceParameters() { + return traceParameters; + } + + public ApiDocumentationProfile apiDocumentationProfile() { + return apiDocumentationProfile; + } + public void validate(Duration streetRoutingTimeout) { if ( !apiProcessingTimeout.isNegative() && @@ -119,9 +140,4 @@ public void validate(Duration streetRoutingTimeout) { ); } } - - @Override - public List traceParameters() { - return traceParameters; - } } diff --git a/doc/user/RouterConfiguration.md b/doc/user/RouterConfiguration.md index 7dae97fd74c..10065eab6db 100644 --- a/doc/user/RouterConfiguration.md +++ b/doc/user/RouterConfiguration.md @@ -31,45 +31,46 @@ A full list of them can be found in the [RouteRequest](RouteRequest.md). -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|-------------------------------------------------------------------------------------------|:---------------------:|-------------------------------------------------------------------------------------------------------|:----------:|---------------|:-----:| -| [configVersion](#configVersion) | `string` | Deployment version of the *router-config.json*. | *Optional* | | 2.1 | -| [flex](sandbox/Flex.md) | `object` | Configuration for flex routing. | *Optional* | | 2.1 | -| [rideHailingServices](sandbox/RideHailing.md) | `object[]` | Configuration for interfaces to external ride hailing services like Uber. | *Optional* | | 2.3 | -| [routingDefaults](RouteRequest.md) | `object` | The default parameters for the routing query. | *Optional* | | 2.0 | -| [server](#server) | `object` | Configuration for router server. | *Optional* | | 2.4 | -|    [apiProcessingTimeout](#server_apiProcessingTimeout) | `duration` | Maximum processing time for an API request | *Optional* | `"PT-1S"` | 2.4 | -|    [traceParameters](#server_traceParameters) | `object[]` | Trace OTP request using HTTP request/response parameter(s) combined with logging. | *Optional* | | 2.4 | -|          generateIdIfMissing | `boolean` | If `true` a unique value is generated if no http request header is provided, or the value is missing. | *Optional* | `false` | 2.4 | -|          httpRequestHeader | `string` | The header-key to use when fetching the trace parameter value | *Optional* | | 2.4 | -|          httpResponseHeader | `string` | The header-key to use when saving the value back into the http response | *Optional* | | 2.4 | -|          [logKey](#server_traceParameters_0_logKey) | `string` | The log event key used. | *Optional* | | 2.4 | -| timetableUpdates | `object` | Global configuration for timetable updaters. | *Optional* | | 2.2 | -|    [maxSnapshotFrequency](#timetableUpdates_maxSnapshotFrequency) | `duration` | How long a snapshot should be cached. | *Optional* | `"PT1S"` | 2.2 | -|    purgeExpiredData | `boolean` | Should expired real-time data be purged from the graph. Apply to GTFS-RT and Siri updates. | *Optional* | `true` | 2.2 | -| [transit](#transit) | `object` | Configuration for transit searches with RAPTOR. | *Optional* | | na | -|    [iterationDepartureStepInSeconds](#transit_iterationDepartureStepInSeconds) | `integer` | Step for departure times between each RangeRaptor iterations. | *Optional* | `60` | na | -|    [maxNumberOfTransfers](#transit_maxNumberOfTransfers) | `integer` | This parameter is used to allocate enough memory space for Raptor. | *Optional* | `12` | na | -|    [maxSearchWindow](#transit_maxSearchWindow) | `duration` | Upper limit of the request parameter searchWindow. | *Optional* | `"PT24H"` | 2.4 | -|    [scheduledTripBinarySearchThreshold](#transit_scheduledTripBinarySearchThreshold) | `integer` | This threshold is used to determine when to perform a binary trip schedule search. | *Optional* | `50` | na | -|    [searchThreadPoolSize](#transit_searchThreadPoolSize) | `integer` | Split a travel search in smaller jobs and run them in parallel to improve performance. | *Optional* | `0` | na | -|    [transferCacheMaxSize](#transit_transferCacheMaxSize) | `integer` | The maximum number of distinct transfers parameters to cache pre-calculated transfers for. | *Optional* | `25` | na | -|    [dynamicSearchWindow](#transit_dynamicSearchWindow) | `object` | The dynamic search window coefficients used to calculate the EDT, LAT and SW. | *Optional* | | 2.1 | -|       [maxWindow](#transit_dynamicSearchWindow_maxWindow) | `duration` | Upper limit for the search-window calculation. | *Optional* | `"PT3H"` | 2.2 | -|       [minTransitTimeCoefficient](#transit_dynamicSearchWindow_minTransitTimeCoefficient) | `double` | The coefficient to multiply with `minTransitTime`. | *Optional* | `0.5` | 2.1 | -|       [minWaitTimeCoefficient](#transit_dynamicSearchWindow_minWaitTimeCoefficient) | `double` | The coefficient to multiply with `minWaitTime`. | *Optional* | `0.5` | 2.1 | -|       [minWindow](#transit_dynamicSearchWindow_minWindow) | `duration` | The constant minimum duration for a raptor-search-window. | *Optional* | `"PT40M"` | 2.2 | -|       [stepMinutes](#transit_dynamicSearchWindow_stepMinutes) | `integer` | Used to set the steps the search-window is rounded to. | *Optional* | `10` | 2.1 | -|    [pagingSearchWindowAdjustments](#transit_pagingSearchWindowAdjustments) | `duration[]` | The provided array of durations is used to increase the search-window for the next/previous page. | *Optional* | | na | -|    [stopBoardAlightDuringTransferCost](#transit_stopBoardAlightDuringTransferCost) | `enum map of integer` | Costs for boarding and alighting during transfers at stops with a given transfer priority. | *Optional* | | 2.0 | -|    [transferCacheRequests](#transit_transferCacheRequests) | `object[]` | Routing requests to use for pre-filling the stop-to-stop transfer cache. | *Optional* | | 2.3 | -| transmodelApi | `object` | Configuration for the Transmodel GraphQL API. | *Optional* | | 2.1 | -|    [hideFeedId](#transmodelApi_hideFeedId) | `boolean` | Hide the FeedId in all API output, and add it to input. | *Optional* | `false` | na | -|    [maxNumberOfResultFields](#transmodelApi_maxNumberOfResultFields) | `integer` | The maximum number of fields in a GraphQL result | *Optional* | `1000000` | 2.6 | -|    [tracingHeaderTags](#transmodelApi_tracingHeaderTags) | `string[]` | Used to group requests when monitoring OTP. | *Optional* | | na | -| [updaters](UpdaterConfig.md) | `object[]` | Configuration for the updaters that import various types of data into OTP. | *Optional* | | 1.5 | -| [vectorTiles](sandbox/MapboxVectorTilesApi.md) | `object` | Vector tile configuration | *Optional* | | na | -| [vehicleRentalServiceDirectory](sandbox/VehicleRentalServiceDirectory.md) | `object` | Configuration for the vehicle rental service directory. | *Optional* | | 2.0 | +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|-------------------------------------------------------------------------------------------|:---------------------:|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|---------------|:-----:| +| [configVersion](#configVersion) | `string` | Deployment version of the *router-config.json*. | *Optional* | | 2.1 | +| [flex](sandbox/Flex.md) | `object` | Configuration for flex routing. | *Optional* | | 2.1 | +| [rideHailingServices](sandbox/RideHailing.md) | `object[]` | Configuration for interfaces to external ride hailing services like Uber. | *Optional* | | 2.3 | +| [routingDefaults](RouteRequest.md) | `object` | The default parameters for the routing query. | *Optional* | | 2.0 | +| [server](#server) | `object` | Configuration for router server. | *Optional* | | 2.4 | +|    [apiDocumentationProfile](#server_apiDocumentationProfile) | `enum` | List of available custom documentation profiles. A profile is used to inject custom documentation like type and field description or a deprecated reason. Currently, ONLY the Transmodel API support this feature. | *Optional* | `"default"` | 2.7 | +|    [apiProcessingTimeout](#server_apiProcessingTimeout) | `duration` | Maximum processing time for an API request | *Optional* | `"PT-1S"` | 2.4 | +|    [traceParameters](#server_traceParameters) | `object[]` | Trace OTP request using HTTP request/response parameter(s) combined with logging. | *Optional* | | 2.4 | +|          generateIdIfMissing | `boolean` | If `true` a unique value is generated if no http request header is provided, or the value is missing. | *Optional* | `false` | 2.4 | +|          httpRequestHeader | `string` | The header-key to use when fetching the trace parameter value | *Optional* | | 2.4 | +|          httpResponseHeader | `string` | The header-key to use when saving the value back into the http response | *Optional* | | 2.4 | +|          [logKey](#server_traceParameters_0_logKey) | `string` | The log event key used. | *Optional* | | 2.4 | +| timetableUpdates | `object` | Global configuration for timetable updaters. | *Optional* | | 2.2 | +|    [maxSnapshotFrequency](#timetableUpdates_maxSnapshotFrequency) | `duration` | How long a snapshot should be cached. | *Optional* | `"PT1S"` | 2.2 | +|    purgeExpiredData | `boolean` | Should expired real-time data be purged from the graph. Apply to GTFS-RT and Siri updates. | *Optional* | `true` | 2.2 | +| [transit](#transit) | `object` | Configuration for transit searches with RAPTOR. | *Optional* | | na | +|    [iterationDepartureStepInSeconds](#transit_iterationDepartureStepInSeconds) | `integer` | Step for departure times between each RangeRaptor iterations. | *Optional* | `60` | na | +|    [maxNumberOfTransfers](#transit_maxNumberOfTransfers) | `integer` | This parameter is used to allocate enough memory space for Raptor. | *Optional* | `12` | na | +|    [maxSearchWindow](#transit_maxSearchWindow) | `duration` | Upper limit of the request parameter searchWindow. | *Optional* | `"PT24H"` | 2.4 | +|    [scheduledTripBinarySearchThreshold](#transit_scheduledTripBinarySearchThreshold) | `integer` | This threshold is used to determine when to perform a binary trip schedule search. | *Optional* | `50` | na | +|    [searchThreadPoolSize](#transit_searchThreadPoolSize) | `integer` | Split a travel search in smaller jobs and run them in parallel to improve performance. | *Optional* | `0` | na | +|    [transferCacheMaxSize](#transit_transferCacheMaxSize) | `integer` | The maximum number of distinct transfers parameters to cache pre-calculated transfers for. | *Optional* | `25` | na | +|    [dynamicSearchWindow](#transit_dynamicSearchWindow) | `object` | The dynamic search window coefficients used to calculate the EDT, LAT and SW. | *Optional* | | 2.1 | +|       [maxWindow](#transit_dynamicSearchWindow_maxWindow) | `duration` | Upper limit for the search-window calculation. | *Optional* | `"PT3H"` | 2.2 | +|       [minTransitTimeCoefficient](#transit_dynamicSearchWindow_minTransitTimeCoefficient) | `double` | The coefficient to multiply with `minTransitTime`. | *Optional* | `0.5` | 2.1 | +|       [minWaitTimeCoefficient](#transit_dynamicSearchWindow_minWaitTimeCoefficient) | `double` | The coefficient to multiply with `minWaitTime`. | *Optional* | `0.5` | 2.1 | +|       [minWindow](#transit_dynamicSearchWindow_minWindow) | `duration` | The constant minimum duration for a raptor-search-window. | *Optional* | `"PT40M"` | 2.2 | +|       [stepMinutes](#transit_dynamicSearchWindow_stepMinutes) | `integer` | Used to set the steps the search-window is rounded to. | *Optional* | `10` | 2.1 | +|    [pagingSearchWindowAdjustments](#transit_pagingSearchWindowAdjustments) | `duration[]` | The provided array of durations is used to increase the search-window for the next/previous page. | *Optional* | | na | +|    [stopBoardAlightDuringTransferCost](#transit_stopBoardAlightDuringTransferCost) | `enum map of integer` | Costs for boarding and alighting during transfers at stops with a given transfer priority. | *Optional* | | 2.0 | +|    [transferCacheRequests](#transit_transferCacheRequests) | `object[]` | Routing requests to use for pre-filling the stop-to-stop transfer cache. | *Optional* | | 2.3 | +| transmodelApi | `object` | Configuration for the Transmodel GraphQL API. | *Optional* | | 2.1 | +|    [hideFeedId](#transmodelApi_hideFeedId) | `boolean` | Hide the FeedId in all API output, and add it to input. | *Optional* | `false` | na | +|    [maxNumberOfResultFields](#transmodelApi_maxNumberOfResultFields) | `integer` | The maximum number of fields in a GraphQL result | *Optional* | `1000000` | 2.6 | +|    [tracingHeaderTags](#transmodelApi_tracingHeaderTags) | `string[]` | Used to group requests when monitoring OTP. | *Optional* | | na | +| [updaters](UpdaterConfig.md) | `object[]` | Configuration for the updaters that import various types of data into OTP. | *Optional* | | 1.5 | +| [vectorTiles](sandbox/MapboxVectorTilesApi.md) | `object` | Vector tile configuration | *Optional* | | na | +| [vehicleRentalServiceDirectory](sandbox/VehicleRentalServiceDirectory.md) | `object` | Configuration for the vehicle rental service directory. | *Optional* | | 2.0 | @@ -108,6 +109,22 @@ These parameters are used to configure the router server. Many parameters are sp domain, these are set in the routing request. +

apiDocumentationProfile

+ +**Since version:** `2.7` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"default"` +**Path:** /server +**Enum values:** `default` | `entur` + +List of available custom documentation profiles. A profile is used to inject custom +documentation like type and field description or a deprecated reason. + +Currently, ONLY the Transmodel API support this feature. + + + - `default` Default documentation is used. + - `entur` Entur specific documentation. This deprecate features not supported at Entur, Norway. + +

apiProcessingTimeout

**Since version:** `2.4` ∙ **Type:** `duration` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"PT-1S"` From 90ac6d2bf29b8183707fdbd891e35eadbb6bcedb Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 2 Jan 2025 12:40:52 +0100 Subject: [PATCH 212/269] feature: Inject custom documentation in Transmodel API --- .../apis/transmodel/TransmodelAPI.java | 3 ++ .../transmodel/TransmodelGraphQLSchema.java | 31 ++++++++++++++++--- .../configure/ConstructApplication.java | 1 + .../TransmodelGraphQLSchemaTest.java | 2 ++ 4 files changed, 32 insertions(+), 5 deletions(-) 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 66377a56390..62b9b5f0a45 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; +import org.opentripplanner.apis.support.graphql.injectdoc.ApiDocumentationProfile; import org.opentripplanner.apis.transmodel.mapping.TransitIdMapper; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.standalone.api.OtpServerRequestContext; @@ -80,6 +81,7 @@ public static void setUp( TransmodelAPIParameters config, TimetableRepository timetableRepository, RouteRequest defaultRouteRequest, + ApiDocumentationProfile documentationProfile, TransitRoutingConfig transitRoutingConfig ) { if (config.hideFeedId()) { @@ -91,6 +93,7 @@ public static void setUp( TransmodelGraphQLSchema.create( defaultRouteRequest, timetableRepository.getTimeZone(), + documentationProfile, transitRoutingConfig ); } diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java b/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java index 922f9f5244b..430aa8d740f 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java @@ -27,6 +27,7 @@ import graphql.schema.GraphQLOutputType; import graphql.schema.GraphQLScalarType; import graphql.schema.GraphQLSchema; +import graphql.schema.SchemaTransformer; import java.time.LocalDate; import java.time.ZoneId; import java.util.ArrayList; @@ -42,8 +43,12 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; +import org.opentripplanner.apis.support.graphql.injectdoc.ApiDocumentationProfile; +import org.opentripplanner.apis.support.graphql.injectdoc.CustomDocumentation; +import org.opentripplanner.apis.support.graphql.injectdoc.InjectCustomDocumentation; import org.opentripplanner.apis.transmodel.mapping.PlaceMapper; import org.opentripplanner.apis.transmodel.mapping.TransitIdMapper; import org.opentripplanner.apis.transmodel.model.DefaultRouteRequestType; @@ -155,10 +160,12 @@ private TransmodelGraphQLSchema( public static GraphQLSchema create( RouteRequest defaultRequest, ZoneId timeZoneId, - TransitTuningParameters transitTuningParameters + ApiDocumentationProfile docProfile, + TransitTuningParameters transitTuning ) { - return new TransmodelGraphQLSchema(defaultRequest, timeZoneId, transitTuningParameters) - .create(); + var schema = new TransmodelGraphQLSchema(defaultRequest, timeZoneId, transitTuning).create(); + schema = decorateSchemaWithCustomDocumentation(schema, docProfile); + return schema; } @SuppressWarnings("unchecked") @@ -1620,7 +1627,7 @@ private GraphQLSchema create() { .field(DatedServiceJourneyQuery.createQuery(datedServiceJourneyType)) .build(); - return GraphQLSchema + var schema = GraphQLSchema .newSchema() .query(queryType) .additionalType(placeInterface) @@ -1628,9 +1635,23 @@ private GraphQLSchema create() { .additionalType(Relay.pageInfoType) .additionalDirective(TransmodelDirectives.TIMING_DATA) .build(); + + return schema; + } + + private static GraphQLSchema decorateSchemaWithCustomDocumentation( + GraphQLSchema schema, + ApiDocumentationProfile docProfile + ) { + var customDocumentation = CustomDocumentation.of(docProfile); + if (customDocumentation.isEmpty()) { + return schema; + } + var visitor = new InjectCustomDocumentation(customDocumentation); + return SchemaTransformer.transformSchema(schema, visitor); } - private List toIdList(List ids) { + private List toIdList(@Nullable List ids) { if (ids == null) { return Collections.emptyList(); } 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 b4edbb36299..65a1146f8f2 100644 --- a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java +++ b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java @@ -183,6 +183,7 @@ private void setupTransitRoutingServer() { routerConfig().transmodelApi(), timetableRepository(), routerConfig().routingRequestDefaults(), + routerConfig().server().apiDocumentationProfile(), routerConfig().transitTuningConfig() ); } diff --git a/application/src/test/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchemaTest.java b/application/src/test/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchemaTest.java index 4cdb0586aa7..3fc33081cda 100644 --- a/application/src/test/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchemaTest.java +++ b/application/src/test/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchemaTest.java @@ -9,6 +9,7 @@ import java.io.File; import org.junit.jupiter.api.Test; import org.opentripplanner._support.time.ZoneIds; +import org.opentripplanner.apis.support.graphql.injectdoc.ApiDocumentationProfile; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitTuningParameters; import org.opentripplanner.routing.api.request.RouteRequest; @@ -23,6 +24,7 @@ void testSchemaBuild() { var schema = TransmodelGraphQLSchema.create( new RouteRequest(), ZoneIds.OSLO, + ApiDocumentationProfile.DEFAULT, TransitTuningParameters.FOR_TEST ); assertNotNull(schema); From 0bf3c72fc2a8ff15e20b129348737cd1fef53473 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 2 Jan 2025 14:53:36 +0100 Subject: [PATCH 213/269] test: Ignore white-space when comparing text In the case used, we compare to GraphQL schemas. --- .../_support/text/TextAssertions.java | 64 +++++++++++++++++++ .../_support/text/TextAssertionsTest.java | 48 ++++++++++++++ .../InjectCustomDocumentationTest.java | 4 +- 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 application/src/test/java/org/opentripplanner/_support/text/TextAssertions.java create mode 100644 application/src/test/java/org/opentripplanner/_support/text/TextAssertionsTest.java diff --git a/application/src/test/java/org/opentripplanner/_support/text/TextAssertions.java b/application/src/test/java/org/opentripplanner/_support/text/TextAssertions.java new file mode 100644 index 00000000000..a009b76237c --- /dev/null +++ b/application/src/test/java/org/opentripplanner/_support/text/TextAssertions.java @@ -0,0 +1,64 @@ +package org.opentripplanner._support.text; + +import org.junit.jupiter.api.Assertions; + +/** + * This class contains test assert methods not supported by the standard JUnit + * framework. + */ +public final class TextAssertions { + + private static final String LINE_DELIMITERS = "(\n|\r|\r\n)"; + private static final int END_OF_TEXT = -111; + + /** + + * Assert to texts are equals line by line. Empty lines and white-space in the start and end of + * a line is ignored. + */ + public static void assertLinesEquals(String expected, String actual) { + var expLines = expected.split(LINE_DELIMITERS); + var actLines = actual.split(LINE_DELIMITERS); + + int i = -1; + int j = -1; + + while (true) { + i = next(expLines, i); + j = next(actLines, j); + + if (i == END_OF_TEXT && j == END_OF_TEXT) { + return; + } + + var exp = getLine(expLines, i); + var act = getLine(actLines, j); + + if (i == END_OF_TEXT || j == END_OF_TEXT || !exp.equals(act)) { + Assertions.fail( + "Expected%s: <%s>%n".formatted(lineText(i), exp) + + "Actual %s: <%s>%n".formatted(lineText(j), act) + ); + } + } + } + + private static String lineText(int index) { + return index < 0 ? "(@end-of-text)" : "(@line %d)".formatted(index); + } + + private static String getLine(String[] lines, int i) { + return i == END_OF_TEXT ? "" : lines[i].trim(); + } + + private static int next(String[] lines, int index) { + ++index; + while (index < lines.length) { + if (!lines[index].isBlank()) { + return index; + } + ++index; + } + return END_OF_TEXT; + } +} diff --git a/application/src/test/java/org/opentripplanner/_support/text/TextAssertionsTest.java b/application/src/test/java/org/opentripplanner/_support/text/TextAssertionsTest.java new file mode 100644 index 00000000000..b1bdb3792cb --- /dev/null +++ b/application/src/test/java/org/opentripplanner/_support/text/TextAssertionsTest.java @@ -0,0 +1,48 @@ +package org.opentripplanner._support.text; + +import static org.opentripplanner._support.text.TextAssertions.assertLinesEquals; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TextAssertionsTest { + + @Test + void testIgnoreWhiteSpace() { + // Empty text + assertLinesEquals("", "\n\n"); + + // Text with white-space inserted + assertLinesEquals( + """ + A Test + Line 2 + DOS\r\n + line-shift + """, + """ + + A Test \t + \t + + \tLine 2 + DOS\rline-shift + """ + ); + } + + @Test + void testEndOfText() { + var ex = Assertions.assertThrows( + org.opentest4j.AssertionFailedError.class, + () -> assertLinesEquals("A\n", "A\nExtra Line") + ); + Assertions.assertEquals( + """ + Expected(@end-of-text): <> + Actual (@line 1): + """, + ex.getMessage() + ); + } +} diff --git a/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java b/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java index 0f326a373aa..3e856faf413 100644 --- a/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java @@ -18,6 +18,7 @@ import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.opentripplanner._support.text.TextAssertions; /** * This test read in a schema file, inject documentation and convert the @@ -120,7 +121,8 @@ void test() { var expected = List.of("BType.a.deprecated", "CType.b.deprecated.append"); assertEquals(expected, missingValues); - assertEquals(sdlExpected, result); + + TextAssertions.assertLinesEquals(sdlExpected, result); } String loadSchemaResource(String suffix) throws IOException { From 2d4ff0c69e44bc38a8173d75ba77ac0d9c06ba16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Erik=20St=C3=B8wer?= Date: Thu, 2 Jan 2025 16:37:42 +0100 Subject: [PATCH 214/269] Updates for eslint v9 --- client/.gitignore | 4 +- client/.prettierignore | 2 +- client/eslint.config.mjs | 100 +- client/package-lock.json | 1361 +++++++++++++---- client/package.json | 10 +- .../{.prettierrc.cjs => prettier.config.mjs} | 6 +- .../ItineraryPaginationControl.tsx | 8 +- client/src/util/getApiUrl.ts | 2 +- 8 files changed, 1078 insertions(+), 415 deletions(-) rename client/{.prettierrc.cjs => prettier.config.mjs} (75%) diff --git a/client/.gitignore b/client/.gitignore index 049d26604a9..32351b4c58e 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -11,6 +11,7 @@ node_modules dist dist-ssr *.local +output # Editor directories and files .vscode/* @@ -27,4 +28,5 @@ dist-ssr src/gql/ # Vite cache folder -.vite/ \ No newline at end of file +.vite/ + diff --git a/client/.prettierignore b/client/.prettierignore index c0aafcabe5f..a96d61e932a 100644 --- a/client/.prettierignore +++ b/client/.prettierignore @@ -1,3 +1,3 @@ node_modules/ -dist/ +output/ src/gql/ diff --git a/client/eslint.config.mjs b/client/eslint.config.mjs index 3c743fdcba6..db27c811bb8 100644 --- a/client/eslint.config.mjs +++ b/client/eslint.config.mjs @@ -1,70 +1,40 @@ -import { fixupConfigRules } from "@eslint/compat"; -import reactRefresh from "eslint-plugin-react-refresh"; -import globals from "globals"; -import tsParser from "@typescript-eslint/parser"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import js from "@eslint/js"; -import { FlatCompat } from "@eslint/eslintrc"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all -}); - -export default [{ - ignores: [ - "**/node_modules", - "**/dist", - "**/.prettierrc.js", - "**/.eslintrc.cjs", - "src/gql/**/*", - ], -}, ...fixupConfigRules(compat.extends( - "eslint:recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended", - "plugin:import/recommended", - "plugin:jsx-a11y/recommended", - "plugin:@typescript-eslint/recommended", - "eslint-config-prettier", -)), { - plugins: { - "react-refresh": reactRefresh, - }, - +import js from '@eslint/js' +import globals from 'globals' +import react from 'eslint-plugin-react' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import jsxA11y from 'eslint-plugin-jsx-a11y' +import importPlugin from 'eslint-plugin-import' +import tseslint from 'typescript-eslint' +import eslintConfigPrettier from "eslint-config-prettier" + +export default tseslint.config( + { ignores: ['output', 'node_modules', 'src/gql/**/*'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], languageOptions: { - globals: { - ...globals.browser, - }, - - parser: tsParser, + ecmaVersion: 2020, + globals: globals.browser, }, - - settings: { - react: { - version: "detect", - }, - - "import/resolver": { - node: { - paths: ["src"], - extensions: [".js", ".jsx", ".ts", ".tsx"], - }, - }, + plugins: { + 'react': react, + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + 'jsx-a11y': jsxA11y, + 'import': importPlugin }, - rules: { - "react-refresh/only-export-components": ["warn", { - allowConstantExport: true, - }], - - "react/jsx-uses-react": "off", - "react/react-in-jsx-scope": "off", - "@typescript-eslint/ban-ts-comment": "off", - "import/named": "off", + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + "react/jsx-uses-react": "off", + "react/react-in-jsx-scope": "off", + '@typescript-eslint/ban-ts-comment': "off", + '@typescript-eslint/no-unused-vars': ["error", { "caughtErrors": "all", "caughtErrorsIgnorePattern": "^_" }] }, -}]; \ No newline at end of file + }, + eslintConfigPrettier, +) diff --git a/client/package-lock.json b/client/package-lock.json index 86f45f2d4fd..94329868f22 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -20,6 +20,8 @@ "react-map-gl": "7.1.7" }, "devDependencies": { + "@eslint/compat": "1.2.4", + "@eslint/js": "9.17.0", "@graphql-codegen/cli": "5.0.3", "@graphql-codegen/client-preset": "4.5.1", "@graphql-codegen/introspection": "4.0.3", @@ -27,20 +29,20 @@ "@testing-library/react": "16.1.0", "@types/react": "19.0.1", "@types/react-dom": "19.0.2", - "@typescript-eslint/eslint-plugin": "8.0.0", - "@typescript-eslint/parser": "8.0.0", "@vitejs/plugin-react": "4.3.4", "@vitest/coverage-v8": "2.1.8", - "eslint": "9.8.0", + "eslint": "9.17.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", - "eslint-plugin-react": "7.37.2", + "eslint-plugin-react": "7.37.3", "eslint-plugin-react-hooks": "5.1.0", "eslint-plugin-react-refresh": "0.4.16", + "globals": "15.14.0", "jsdom": "25.0.1", "prettier": "3.4.2", "typescript": "5.7.2", + "typescript-eslint": "8.19.0", "vite": "6.0.3", "vitest": "2.1.8" } @@ -600,6 +602,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-classes/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.25.9", "dev": true, @@ -904,6 +915,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/types": { "version": "7.26.3", "dev": true, @@ -961,13 +981,30 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/compat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.4.tgz", + "integrity": "sha512-S8ZdQj/N69YAtuqFt7653jwcvuUj131+6qGLUyDqfDg1OIoBQ66OCuXC473YQfO2AaxITTutiRQiDwoo7ZLYyg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, "node_modules/@eslint/config-array": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", - "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", "dev": true, "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.5", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -997,6 +1034,18 @@ "node": "*" } }, + "node_modules/@eslint/core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", @@ -1055,9 +1104,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", - "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1072,6 +1121,18 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@googlemaps/polyline-codec": { "version": "1.0.28", "license": "Apache-2.0" @@ -1977,6 +2038,41 @@ "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 || ^17.0.0" } }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "dev": true, @@ -1990,9 +2086,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, "engines": { "node": ">=18.18" @@ -2519,6 +2615,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "dev": true, @@ -2602,16 +2704,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0.tgz", - "integrity": "sha512-STIZdwEQRXAHvNUS6ILDf5z3u95Gc8jzywunxSNqX00OooIemaaNIA0vEgynJlycL5AjabYLLrIyHd4iazyvtg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz", + "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.0", - "@typescript-eslint/type-utils": "8.0.0", - "@typescript-eslint/utils": "8.0.0", - "@typescript-eslint/visitor-keys": "8.0.0", + "@typescript-eslint/scope-manager": "8.19.0", + "@typescript-eslint/type-utils": "8.19.0", + "@typescript-eslint/utils": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2626,12 +2728,67 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", + "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", + "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", + "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@typescript-eslint/parser": { @@ -2639,6 +2796,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0.tgz", "integrity": "sha512-pS1hdZ+vnrpDIxuFXYQpLTILglTjSYJ9MbetZctrUawogUsPdz31DIIRZ9+rab0LhYNTsk88w4fIzVheiTbWOQ==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.0.0", "@typescript-eslint/types": "8.0.0", @@ -2667,6 +2825,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0.tgz", "integrity": "sha512-V0aa9Csx/ZWWv2IPgTfY7T4agYwJyILESu/PVqFtTFz9RIS823mAze+NbnBI8xiwdX3iqeQbcTYlvB04G9wyQw==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "8.0.0", "@typescript-eslint/visitor-keys": "8.0.0" @@ -2680,13 +2839,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0.tgz", - "integrity": "sha512-mJAFP2mZLTBwAn5WI4PMakpywfWFH5nQZezUQdSKV23Pqo6o9iShQg1hP2+0hJJXP2LnZkWPphdIq4juYYwCeg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", + "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.0.0", - "@typescript-eslint/utils": "8.0.0", + "@typescript-eslint/typescript-estree": "8.19.0", + "@typescript-eslint/utils": "8.19.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2697,10 +2856,89 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", + "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", + "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", + "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/@typescript-eslint/types": { @@ -2708,6 +2946,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0.tgz", "integrity": "sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==", "dev": true, + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2721,6 +2960,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0.tgz", "integrity": "sha512-5b97WpKMX+Y43YKi4zVcCVLtK5F98dFls3Oxui8LbnmRsseKenbbDinmvxrWegKDMmlkIq/XHuyy0UGLtpCDKg==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "8.0.0", "@typescript-eslint/visitor-keys": "8.0.0", @@ -2749,6 +2989,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -2757,15 +2998,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0.tgz", - "integrity": "sha512-k/oS/A/3QeGLRvOWCg6/9rATJL5rec7/5s1YmdS0ZU6LHveJyGFwBvLhSRBv6i9xaj7etmosp+l+ViN1I9Aj/Q==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", + "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.0.0", - "@typescript-eslint/types": "8.0.0", - "@typescript-eslint/typescript-estree": "8.0.0" + "@typescript-eslint/scope-manager": "8.19.0", + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/typescript-estree": "8.19.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2775,7 +3016,105 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", + "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", + "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", + "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", + "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/@typescript-eslint/visitor-keys": { @@ -2783,6 +3122,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0.tgz", "integrity": "sha512-oN0K4nkHuOyF3PVMyETbpP5zp6wfyOvm7tWhTMfoqxSSsPmJIh6JNASuZDlODE8eE+0EB9uar+6+vxr9DBTYOA==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "8.0.0", "eslint-visitor-keys": "^3.4.3" @@ -3070,12 +3410,13 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -3167,14 +3508,15 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3199,18 +3541,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -3534,6 +3876,22 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "dev": true, @@ -3911,13 +4269,14 @@ } }, "node_modules/data-view-buffer": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3927,27 +4286,29 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -4149,11 +4510,12 @@ } }, "node_modules/dunder-proto": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", + "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" }, @@ -4200,56 +4562,60 @@ } }, "node_modules/es-abstract": { - "version": "1.23.5", + "version": "1.23.8", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.8.tgz", + "integrity": "sha512-lfab8IzDn6EpI1ibZakcgS6WsfEBiB+43cuJo+wgylx1xKXf+Sp+YR3vFuQwC/u3sxYwV8Cxe3B0DpVUu/WiJQ==", "dev": true, - "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.6", + "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", + "object.assign": "^4.1.7", + "own-keys": "^1.0.0", "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -4275,25 +4641,27 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.2.0", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", + "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", - "safe-array-concat": "^1.1.2" + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" }, "engines": { "node": ">= 0.4" @@ -4410,27 +4778,31 @@ } }, "node_modules/eslint": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", - "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.17.1", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.17.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.2", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.1.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -4440,15 +4812,11 @@ "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -4458,6 +4826,14 @@ }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -4515,8 +4891,9 @@ }, "node_modules/eslint-plugin-import": { "version": "2.31.0", + "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", @@ -4586,8 +4963,9 @@ }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", + "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", @@ -4641,27 +5019,28 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.2", + "version": "7.37.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz", + "integrity": "sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", + "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", + "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", + "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "engines": { @@ -4673,8 +5052,9 @@ }, "node_modules/eslint-plugin-react-hooks": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -4684,8 +5064,9 @@ }, "node_modules/eslint-plugin-react-refresh": { "version": "0.4.16", + "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" } @@ -5157,14 +5538,17 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.6", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -5202,18 +5586,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.5", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", + "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", + "call-bind-apply-helpers": "^1.0.1", "dunder-proto": "^1.0.0", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", "gopd": "^1.2.0", "has-symbols": "^1.1.0", - "hasown": "^2.0.2" + "hasown": "^2.0.2", + "math-intrinsics": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5222,6 +5609,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.0.tgz", + "integrity": "sha512-TtLgOcKaF1nMP2ijJnITkE4nRhbpshHhmzKiuhmSniiwWzovoqwqQ8rNuhf0mXJOqIY5iU+QkUe0CkJYrLsG9w==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "license": "MIT", @@ -5233,13 +5633,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -5342,11 +5743,15 @@ } }, "node_modules/globals": { - "version": "11.12.0", + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", "dev": true, - "license": "MIT", "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globalthis": { @@ -5747,13 +6152,14 @@ } }, "node_modules/internal-slot": { - "version": "1.0.7", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5779,12 +6185,14 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.4", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -5800,8 +6208,9 @@ }, "node_modules/is-async-function": { "version": "2.0.0", + "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" }, @@ -5814,8 +6223,9 @@ }, "node_modules/is-bigint": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, - "license": "MIT", "dependencies": { "has-bigints": "^1.0.2" }, @@ -5827,11 +6237,12 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.0", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" }, "engines": { @@ -5867,10 +6278,13 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, - "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -5881,11 +6295,13 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5910,11 +6326,12 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -5933,8 +6350,9 @@ }, "node_modules/is-generator-function": { "version": "1.0.10", + "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" }, @@ -5974,19 +6392,9 @@ }, "node_modules/is-map": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6003,11 +6411,12 @@ } }, "node_modules/is-number-object": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -6017,14 +6426,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-object": { "version": "2.0.4", "license": "MIT", @@ -6041,12 +6442,13 @@ "license": "MIT" }, "node_modules/is-regex": { - "version": "1.2.0", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "gopd": "^1.1.0", + "call-bound": "^1.0.2", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" }, @@ -6070,8 +6472,9 @@ }, "node_modules/is-set": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6080,11 +6483,12 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -6094,11 +6498,12 @@ } }, "node_modules/is-string": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -6109,13 +6514,14 @@ } }, "node_modules/is-symbol": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "has-symbols": "^1.0.3", - "safe-regex-test": "^1.0.3" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -6125,11 +6531,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, - "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -6170,8 +6577,9 @@ }, "node_modules/is-weakmap": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6180,23 +6588,28 @@ } }, "node_modules/is-weakref": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -6285,15 +6698,17 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.3", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, - "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -6773,6 +7188,15 @@ "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge2": { "version": "1.4.1", "dev": true, @@ -6993,8 +7417,9 @@ }, "node_modules/object-inspect": { "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, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7011,13 +7436,16 @@ } }, "node_modules/object.assign": { - "version": "4.1.5", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -7071,11 +7499,13 @@ } }, "node_modules/object.values": { - "version": "1.2.0", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -7149,9 +7579,26 @@ "node_modules/os-tmpdir": { "version": "1.0.2", "dev": true, - "license": "MIT", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/p-limit": { @@ -7698,18 +8145,19 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.8", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz", + "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "dunder-proto": "^1.0.0", - "es-abstract": "^1.23.5", + "dunder-proto": "^1.0.1", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", + "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", - "which-builtin-type": "^1.2.0" + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -7922,13 +8370,15 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.2", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -7957,14 +8407,31 @@ ], "license": "MIT" }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-regex-test": { - "version": "1.0.3", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -8100,14 +8567,69 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -8318,22 +8840,24 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.11", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8352,14 +8876,18 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8369,14 +8897,19 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8514,11 +9047,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, "node_modules/through": { "version": "2.3.8", "dev": true, @@ -8697,28 +9225,30 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -8728,17 +9258,18 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.3", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "reflect.getprototypeof": "^1.0.6" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -8778,6 +9309,149 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.0.tgz", + "integrity": "sha512-Ni8sUkVWYK4KAcTtPjQ/UTiRk6jcsuDhPpxULapUDi8A/l8TSBk+t1GtJA1RsCzIJg0q6+J7bf35AwQigENWRQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.19.0", + "@typescript-eslint/parser": "8.19.0", + "@typescript-eslint/utils": "8.19.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", + "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.19.0", + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/typescript-estree": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", + "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", + "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", + "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", + "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.19.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/typescript-eslint/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/typewise": { "version": "1.0.3", "license": "MIT", @@ -8815,14 +9489,18 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9450,15 +10128,16 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, - "license": "MIT", "dependencies": { "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" + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -9468,23 +10147,24 @@ } }, "node_modules/which-builtin-type": { - "version": "1.2.0", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", + "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -9495,8 +10175,9 @@ }, "node_modules/which-collection": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, - "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -9516,14 +10197,16 @@ "license": "ISC" }, "node_modules/which-typed-array": { - "version": "1.1.16", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "for-each": "^0.3.3", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { diff --git a/client/package.json b/client/package.json index 43ff5e1bc59..b16a1fe8527 100644 --- a/client/package.json +++ b/client/package.json @@ -29,6 +29,8 @@ "react-map-gl": "7.1.7" }, "devDependencies": { + "@eslint/compat": "1.2.4", + "@eslint/js": "9.17.0", "@graphql-codegen/cli": "5.0.3", "@graphql-codegen/client-preset": "4.5.1", "@graphql-codegen/introspection": "4.0.3", @@ -36,20 +38,20 @@ "@testing-library/react": "16.1.0", "@types/react": "19.0.1", "@types/react-dom": "19.0.2", - "@typescript-eslint/eslint-plugin": "8.0.0", - "@typescript-eslint/parser": "8.0.0", "@vitejs/plugin-react": "4.3.4", "@vitest/coverage-v8": "2.1.8", - "eslint": "9.8.0", + "eslint": "9.17.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", - "eslint-plugin-react": "7.37.2", + "eslint-plugin-react": "7.37.3", "eslint-plugin-react-hooks": "5.1.0", "eslint-plugin-react-refresh": "0.4.16", + "globals": "15.14.0", "jsdom": "25.0.1", "prettier": "3.4.2", "typescript": "5.7.2", + "typescript-eslint": "8.19.0", "vite": "6.0.3", "vitest": "2.1.8" } diff --git a/client/.prettierrc.cjs b/client/prettier.config.mjs similarity index 75% rename from client/.prettierrc.cjs rename to client/prettier.config.mjs index 6111ad4264d..43624202a92 100644 --- a/client/.prettierrc.cjs +++ b/client/prettier.config.mjs @@ -1,8 +1,10 @@ -module.exports = { +const config = { "trailingComma": "all", "tabWidth": 2, "semi": true, "singleQuote": true, "printWidth": 120, "bracketSpacing": true -} \ No newline at end of file +} + +export default config; \ No newline at end of file diff --git a/client/src/components/ItineraryList/ItineraryPaginationControl.tsx b/client/src/components/ItineraryList/ItineraryPaginationControl.tsx index bf74c83fbca..dc197a2451e 100644 --- a/client/src/components/ItineraryList/ItineraryPaginationControl.tsx +++ b/client/src/components/ItineraryList/ItineraryPaginationControl.tsx @@ -18,7 +18,9 @@ export function ItineraryPaginationControl({ size="sm" disabled={!previousPageCursor || loading} onClick={() => { - previousPageCursor && onPagination(previousPageCursor); + if (previousPageCursor) { + onPagination(previousPageCursor); + } }} > Previous page @@ -28,7 +30,9 @@ export function ItineraryPaginationControl({ size="sm" disabled={!nextPageCursor || loading} onClick={() => { - nextPageCursor && onPagination(nextPageCursor); + if (nextPageCursor) { + onPagination(nextPageCursor); + } }} > Next page diff --git a/client/src/util/getApiUrl.ts b/client/src/util/getApiUrl.ts index 34d25068342..87687fbe602 100644 --- a/client/src/util/getApiUrl.ts +++ b/client/src/util/getApiUrl.ts @@ -6,7 +6,7 @@ export const getApiUrl = () => { // e.g. if it is a relative path new URL(endpoint); return endpoint; - } catch (e) { + } catch (_) { return `${window.location.origin}${endpoint}`; } }; From 57898e687054eb1a4ff5adb3831adbfd2dfb7019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Erik=20St=C3=B8wer?= Date: Thu, 2 Jan 2025 16:49:09 +0100 Subject: [PATCH 215/269] Update package-lock --- client/package-lock.json | 5435 +++++++++++++++++++++++++------------- 1 file changed, 3651 insertions(+), 1784 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 94329868f22..a50d6d07f73 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -49,8 +49,9 @@ }, "node_modules/@ampproject/remapping": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -61,8 +62,9 @@ }, "node_modules/@ardatan/relay-compiler": { "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz", + "integrity": "sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.14.0", "@babel/generator": "^7.14.0", @@ -91,8 +93,9 @@ }, "node_modules/@ardatan/relay-compiler/node_modules/cliui": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -101,8 +104,9 @@ }, "node_modules/@ardatan/relay-compiler/node_modules/find-up": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -113,8 +117,9 @@ }, "node_modules/@ardatan/relay-compiler/node_modules/locate-path": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -124,8 +129,9 @@ }, "node_modules/@ardatan/relay-compiler/node_modules/p-limit": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -138,8 +144,9 @@ }, "node_modules/@ardatan/relay-compiler/node_modules/p-locate": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -149,13 +156,15 @@ }, "node_modules/@ardatan/relay-compiler/node_modules/y18n": { "version": "4.0.3", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true }, "node_modules/@ardatan/relay-compiler/node_modules/yargs": { "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, - "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -175,8 +184,9 @@ }, "node_modules/@ardatan/relay-compiler/node_modules/yargs-parser": { "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, - "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -187,8 +197,9 @@ }, "node_modules/@ardatan/sync-fetch": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz", + "integrity": "sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==", "dev": true, - "license": "MIT", "dependencies": { "node-fetch": "^2.6.1" }, @@ -198,8 +209,9 @@ }, "node_modules/@babel/code-frame": { "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -211,16 +223,18 @@ }, "node_modules/@babel/compat-data": { "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -248,8 +262,9 @@ }, "node_modules/@babel/generator": { "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/parser": "^7.26.3", "@babel/types": "^7.26.3", @@ -263,8 +278,9 @@ }, "node_modules/@babel/helper-annotate-as-pure": { "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, - "license": "MIT", "dependencies": { "@babel/types": "^7.25.9" }, @@ -274,8 +290,9 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/compat-data": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -289,8 +306,9 @@ }, "node_modules/@babel/helper-create-class-features-plugin": { "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, - "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", @@ -309,8 +327,9 @@ }, "node_modules/@babel/helper-member-expression-to-functions": { "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, - "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -321,8 +340,9 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -333,8 +353,9 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", @@ -349,8 +370,9 @@ }, "node_modules/@babel/helper-optimise-call-expression": { "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, - "license": "MIT", "dependencies": { "@babel/types": "^7.25.9" }, @@ -360,16 +382,18 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-replace-supers": { "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, - "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", @@ -384,8 +408,9 @@ }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "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/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -396,32 +421,36 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", "@babel/types": "^7.26.0" @@ -432,8 +461,9 @@ }, "node_modules/@babel/parser": { "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.26.3" }, @@ -446,8 +476,10 @@ }, "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -461,8 +493,10 @@ }, "node_modules/@babel/plugin-proposal-object-rest-spread": { "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/compat-data": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.7", @@ -479,8 +513,9 @@ }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -490,8 +525,9 @@ }, "node_modules/@babel/plugin-syntax-flow": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -504,8 +540,9 @@ }, "node_modules/@babel/plugin-syntax-import-assertions": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -518,8 +555,9 @@ }, "node_modules/@babel/plugin-syntax-jsx": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -532,8 +570,9 @@ }, "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -543,8 +582,9 @@ }, "node_modules/@babel/plugin-transform-arrow-functions": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -557,8 +597,9 @@ }, "node_modules/@babel/plugin-transform-block-scoped-functions": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -571,8 +612,9 @@ }, "node_modules/@babel/plugin-transform-block-scoping": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -585,8 +627,9 @@ }, "node_modules/@babel/plugin-transform-classes": { "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, - "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", @@ -613,8 +656,9 @@ }, "node_modules/@babel/plugin-transform-computed-properties": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" @@ -628,8 +672,9 @@ }, "node_modules/@babel/plugin-transform-destructuring": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -642,8 +687,9 @@ }, "node_modules/@babel/plugin-transform-flow-strip-types": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-syntax-flow": "^7.25.9" @@ -657,8 +703,9 @@ }, "node_modules/@babel/plugin-transform-for-of": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -672,8 +719,9 @@ }, "node_modules/@babel/plugin-transform-function-name": { "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, - "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -688,8 +736,9 @@ }, "node_modules/@babel/plugin-transform-literals": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -702,8 +751,9 @@ }, "node_modules/@babel/plugin-transform-member-expression-literals": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -716,8 +766,9 @@ }, "node_modules/@babel/plugin-transform-modules-commonjs": { "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, - "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.26.0", "@babel/helper-plugin-utils": "^7.25.9" @@ -731,8 +782,9 @@ }, "node_modules/@babel/plugin-transform-object-super": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" @@ -746,8 +798,9 @@ }, "node_modules/@babel/plugin-transform-parameters": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -760,8 +813,9 @@ }, "node_modules/@babel/plugin-transform-property-literals": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -774,8 +828,9 @@ }, "node_modules/@babel/plugin-transform-react-display-name": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -788,8 +843,9 @@ }, "node_modules/@babel/plugin-transform-react-jsx": { "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, - "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", @@ -806,8 +862,9 @@ }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -820,8 +877,9 @@ }, "node_modules/@babel/plugin-transform-react-jsx-source": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -834,8 +892,9 @@ }, "node_modules/@babel/plugin-transform-shorthand-properties": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -848,8 +907,9 @@ }, "node_modules/@babel/plugin-transform-spread": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -863,8 +923,9 @@ }, "node_modules/@babel/plugin-transform-template-literals": { "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -877,7 +938,8 @@ }, "node_modules/@babel/runtime": { "version": "7.26.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -887,8 +949,9 @@ }, "node_modules/@babel/template": { "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/parser": "^7.25.9", @@ -900,8 +963,9 @@ }, "node_modules/@babel/traverse": { "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.3", @@ -926,8 +990,9 @@ }, "node_modules/@babel/types": { "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, - "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -938,227 +1003,612 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@envelop/core": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.0.2.tgz", + "integrity": "sha512-tVL6OrMe6UjqLosiE+EH9uxh2TQC0469GwF4tE014ugRaDDKKVWwFwZe0TBMlcyHKh5MD4ZxktWo/1hqUxIuhw==", "dev": true, - "license": "MIT" + "dependencies": { + "@envelop/types": "5.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.24.0", + "node_modules/@envelop/types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@envelop/types/-/types-5.0.0.tgz", + "integrity": "sha512-IPjmgSc4KpQRlO4qbEDnBEixvtb06WDmjKfi/7fkZaryh5HuOmTtixe1EupQI5XfXO8joc3d27uUZ0QdC++euA==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", "cpu": [ - "x64" + "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ - "darwin" + "aix" ], "engines": { "node": ">=18" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, + "optional": true, + "os": [ + "android" + ], "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": ">=18" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@eslint/compat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.4.tgz", - "integrity": "sha512-S8ZdQj/N69YAtuqFt7653jwcvuUj131+6qGLUyDqfDg1OIoBQ66OCuXC473YQfO2AaxITTutiRQiDwoo7ZLYyg==", + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": "^9.10.0" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@eslint/object-schema": "^2.1.5", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "*" + "node": ">=18" } }, - "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.15" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "*" + "node": ">=18" } }, - "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], "dev": true, - "dependencies": { - "levn": "^0.4.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@googlemaps/polyline-codec": { - "version": "1.0.28", - "license": "Apache-2.0" - }, - "node_modules/@graphql-codegen/add": { - "version": "5.0.3", + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "tslib": "~2.6.0" - }, - "peerDependencies": { - "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" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@graphql-codegen/add/node_modules/tslib": { - "version": "2.6.3", - "dev": true, - "license": "0BSD" - }, - "node_modules/@graphql-codegen/cli": { - "version": "5.0.3", + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "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.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/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "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" + } + }, + "node_modules/@eslint/compat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.4.tgz", + "integrity": "sha512-S8ZdQj/N69YAtuqFt7653jwcvuUj131+6qGLUyDqfDg1OIoBQ66OCuXC473YQfO2AaxITTutiRQiDwoo7ZLYyg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@googlemaps/polyline-codec": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/@googlemaps/polyline-codec/-/polyline-codec-1.0.28.tgz", + "integrity": "sha512-m7rh8sbxlrHvebXEweBHX8r1uPtToPRYxWDD6p6k2YG8hyhBe0Wi6xRUVFpxpEseMNgF+OBotFQC5senj8K7TQ==" + }, + "node_modules/@graphql-codegen/add": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-5.0.3.tgz", + "integrity": "sha512-SxXPmramkth8XtBlAHu4H4jYcYXM/o3p01+psU+0NADQowA8jtYkK6MW5rV6T+CxkEaNZItfSmZRPgIuypcqnA==", + "dev": true, + "dependencies": { + "@graphql-codegen/plugin-helpers": "^5.0.3", + "tslib": "~2.6.0" + }, + "peerDependencies": { + "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, + "dependencies": { "@babel/generator": "^7.18.13", "@babel/template": "^7.18.10", "@babel/types": "^7.18.13", @@ -1216,8 +1666,9 @@ }, "node_modules/@graphql-codegen/client-preset": { "version": "4.5.1", + "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", @@ -1242,13 +1693,15 @@ }, "node_modules/@graphql-codegen/client-preset/node_modules/tslib": { "version": "2.6.3", - "dev": true, - "license": "0BSD" + "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", + "integrity": "sha512-IZbpkhwVqgizcjNiaVzNAzm/xbWT6YnGgeOLwVjm4KbJn3V2jchVtuzHH09G5/WkkLSk2wgbXNdwjM41JxO6Eg==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.3", "@graphql-tools/schema": "^10.0.0", @@ -1261,13 +1714,15 @@ }, "node_modules/@graphql-codegen/core/node_modules/tslib": { "version": "2.6.3", - "dev": true, - "license": "0BSD" + "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", @@ -1284,13 +1739,15 @@ }, "node_modules/@graphql-codegen/gql-tag-operations/node_modules/tslib": { "version": "2.6.3", - "dev": true, - "license": "0BSD" + "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", + "integrity": "sha512-4cHRG15Zu4MXMF4wTQmywNf4+fkDYv5lTbzraVfliDnB8rJKcaurQpRBi11KVuQUe24YTq/Cfk4uwewfNikWoA==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.3", "@graphql-codegen/visitor-plugin-common": "^5.0.0", @@ -1302,13 +1759,15 @@ }, "node_modules/@graphql-codegen/introspection/node_modules/tslib": { "version": "2.6.3", - "dev": true, - "license": "0BSD" + "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", @@ -1326,13 +1785,15 @@ }, "node_modules/@graphql-codegen/plugin-helpers/node_modules/tslib": { "version": "2.6.3", - "dev": true, - "license": "0BSD" + "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", @@ -1344,13 +1805,15 @@ }, "node_modules/@graphql-codegen/schema-ast/node_modules/tslib": { "version": "2.6.3", - "dev": true, - "license": "0BSD" + "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", @@ -1367,13 +1830,15 @@ }, "node_modules/@graphql-codegen/typed-document-node/node_modules/tslib": { "version": "2.6.3", - "dev": true, - "license": "0BSD" + "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", @@ -1390,8 +1855,9 @@ }, "node_modules/@graphql-codegen/typescript-operations": { "version": "4.4.0", + "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", @@ -1408,18 +1874,21 @@ }, "node_modules/@graphql-codegen/typescript-operations/node_modules/tslib": { "version": "2.6.3", - "dev": true, - "license": "0BSD" + "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", - "dev": true, - "license": "0BSD" + "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", @@ -1441,16 +1910,34 @@ }, "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-hive/gateway-abort-signal-any": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@graphql-hive/gateway-abort-signal-any/-/gateway-abort-signal-any-0.0.3.tgz", + "integrity": "sha512-TLYXRiK1DxkGXEdVrwbEtQ4JrsxJ4d/zXBeTzNzvuU+doTzot0wreFgrmmOq+bvqg/E6yMs1kOvBYz477gyMjA==", "dev": true, - "license": "0BSD" + "dependencies": { + "@graphql-tools/utils": "^10.7.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "graphql": "^15.0.0 || ^16.9.0 || ^17.0.0" + } }, "node_modules/@graphql-tools/apollo-engine-loader": { - "version": "8.0.7", + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.11.tgz", + "integrity": "sha512-ccM2eO2cElp5rd4hbt8ToCAc7S+FHFNpu0vMXEYeVX8q4OjA2QxkGiOgK80Iio4uYgfZZ/SViSTxxPTH5PUjoQ==", "dev": true, - "license": "MIT", "dependencies": { "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.1", "@whatwg-node/fetch": "^0.10.0", "tslib": "^2.4.0" }, @@ -1463,8 +1950,9 @@ }, "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, - "license": "MIT", "dependencies": { "@whatwg-node/node-fetch": "^0.7.1", "urlpattern-polyfill": "^10.0.0" @@ -1474,9 +1962,10 @@ } }, "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.4", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.5.tgz", + "integrity": "sha512-t7kGrt2fdfNvzy1LCAE9/OnIyMtizgFhgJmk7iLJwQsLmR7S86F8Q4aDRPbCfo7pISJP6Fx/tPdfFNjHS23WTA==", "dev": true, - "license": "MIT", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/disposablestack": "^0.0.5", @@ -1489,11 +1978,12 @@ } }, "node_modules/@graphql-tools/batch-execute": { - "version": "9.0.10", + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.11.tgz", + "integrity": "sha512-v9b618cj3hIrRGTDrOotYzpK+ZigvNcKdXK3LNBM4g/uA7pND0d4GOnuOSBQGKKN6kT/1nsz4ZpUxCoUvWPbzg==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.0", "dataloader": "^2.2.3", "tslib": "^2.8.1" }, @@ -1505,12 +1995,13 @@ } }, "node_modules/@graphql-tools/code-file-loader": { - "version": "8.1.8", + "version": "8.1.12", + "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.12.tgz", + "integrity": "sha512-4Uk7O+8WCTfN/rCz3kh25Hfj4rBnKipJ3YtOsXnLXHOBXJ15pZ+B5E8sMlyXCkpbt0aiZlhl6/yGiryBXYeP4Q==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.7", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/graphql-tag-pluck": "8.3.11", + "@graphql-tools/utils": "^10.7.1", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -1523,14 +2014,15 @@ } }, "node_modules/@graphql-tools/delegate": { - "version": "10.2.7", + "version": "10.2.9", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.2.9.tgz", + "integrity": "sha512-JlD/IdC26tyqopYvgXo48XwlDnpYPVs523dq5tg/u8kxJe3PtBmEUoE6EQ4CEMk0mB/r5ck+ZXTHt/wiOCWKhw==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/batch-execute": "^9.0.10", - "@graphql-tools/executor": "^1.3.6", + "@graphql-tools/batch-execute": "^9.0.11", + "@graphql-tools/executor": "^1.3.10", "@graphql-tools/schema": "^10.0.11", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.0", "@repeaterjs/repeater": "^3.0.6", "dataloader": "^2.2.3", "dset": "^3.1.2", @@ -1545,8 +2037,9 @@ }, "node_modules/@graphql-tools/documents": { "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, - "license": "MIT", "dependencies": { "lodash.sortby": "^4.7.0", "tslib": "^2.4.0" @@ -1559,13 +2052,15 @@ } }, "node_modules/@graphql-tools/executor": { - "version": "1.3.6", + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.3.11.tgz", + "integrity": "sha512-7Q1IwIuSgarDeaCOZ1VMZvGaWY7cD2jj+uTNn/PsenYYKqFWKH30UylEK67ZTpVMXqBJctFaxVnPP7KM0+LPWg==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.6.2", - "@graphql-typed-document-node/core": "3.2.0", + "@graphql-tools/utils": "^10.7.1", + "@graphql-typed-document-node/core": "^3.2.0", "@repeaterjs/repeater": "^3.0.4", + "@whatwg-node/disposablestack": "^0.0.5", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" }, @@ -1576,12 +2071,30 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@graphql-tools/executor-common": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-0.0.1.tgz", + "integrity": "sha512-Gan7uiQhKvAAl0UM20Oy/n5NGBBDNm+ASHvnYuD8mP+dAH0qY+2QMCHyi5py28WAlhAwr0+CAemEyzY/ZzOjdQ==", + "dev": true, + "dependencies": { + "@envelop/core": "^5.0.2", + "@graphql-tools/utils": "^10.7.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@graphql-tools/executor-graphql-ws": { - "version": "1.3.5", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-1.3.7.tgz", + "integrity": "sha512-9KUrlpil5nBgcb+XRUIxNQGI+c237LAfDBqYCdLGuYT+/oZz1b4rRIe6HuRk09vuxrbaMTzm7xHhn/iuwWW4eg==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/executor-common": "^0.0.1", + "@graphql-tools/utils": "^10.7.0", "@whatwg-node/disposablestack": "^0.0.5", "graphql-ws": "^5.14.0", "isomorphic-ws": "^5.0.0", @@ -1596,11 +2109,14 @@ } }, "node_modules/@graphql-tools/executor-http": { - "version": "1.1.14", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.2.4.tgz", + "integrity": "sha512-2WwymmIplDdzdPgs/qcqfqYfGGfpd626VejsREylTtyrBcURtyNfGw95sHOPo1O2NEXC5wJRN2o+GQBfC3Zy0g==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.6.2", + "@graphql-hive/gateway-abort-signal-any": "^0.0.3", + "@graphql-tools/executor-common": "^0.0.1", + "@graphql-tools/utils": "^10.7.0", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/disposablestack": "^0.0.5", "@whatwg-node/fetch": "^0.10.1", @@ -1618,8 +2134,9 @@ }, "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, - "license": "MIT", "dependencies": { "@whatwg-node/node-fetch": "^0.7.1", "urlpattern-polyfill": "^10.0.0" @@ -1629,9 +2146,10 @@ } }, "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.4", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.5.tgz", + "integrity": "sha512-t7kGrt2fdfNvzy1LCAE9/OnIyMtizgFhgJmk7iLJwQsLmR7S86F8Q4aDRPbCfo7pISJP6Fx/tPdfFNjHS23WTA==", "dev": true, - "license": "MIT", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/disposablestack": "^0.0.5", @@ -1644,11 +2162,12 @@ } }, "node_modules/@graphql-tools/executor-legacy-ws": { - "version": "1.1.5", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.9.tgz", + "integrity": "sha512-QwJ1QyA38NzkKBPiUr9qlNHhQW5L2ZHwcs+zWNDPIlGVHocdq8+bYjomBzBdxdVIAgTX47BwJ2awrHhO4p/gbQ==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.1", "@types/ws": "^8.0.0", "isomorphic-ws": "^5.0.0", "tslib": "^2.4.0", @@ -1662,12 +2181,13 @@ } }, "node_modules/@graphql-tools/git-loader": { - "version": "8.0.12", + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.16.tgz", + "integrity": "sha512-1glCxgRzeWE1wWLEog5fhbFafYcRT8h8pwvO5kUORXFCaBEJ/zbXLkqWhElwdI5rlIoxXWTEgOdrCimnVLF2DQ==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.7", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/graphql-tag-pluck": "8.3.11", + "@graphql-tools/utils": "^10.7.1", "is-glob": "4.0.3", "micromatch": "^4.0.8", "tslib": "^2.4.0", @@ -1681,14 +2201,15 @@ } }, "node_modules/@graphql-tools/github-loader": { - "version": "8.0.7", + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.11.tgz", + "integrity": "sha512-AzFctblS9AFwy1XYwtiQdulqqvLM87CAU4pTVfBSjDn5bhyBu4iAPfZ81gPhfIO+uBe7MKwmxBuas+/cqyxNRg==", "dev": true, - "license": "MIT", "dependencies": { "@ardatan/sync-fetch": "^0.0.1", "@graphql-tools/executor-http": "^1.1.9", - "@graphql-tools/graphql-tag-pluck": "^8.3.7", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/graphql-tag-pluck": "^8.3.11", + "@graphql-tools/utils": "^10.7.1", "@whatwg-node/fetch": "^0.10.0", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" @@ -1702,8 +2223,9 @@ }, "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, - "license": "MIT", "dependencies": { "@whatwg-node/node-fetch": "^0.7.1", "urlpattern-polyfill": "^10.0.0" @@ -1713,9 +2235,10 @@ } }, "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.4", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.5.tgz", + "integrity": "sha512-t7kGrt2fdfNvzy1LCAE9/OnIyMtizgFhgJmk7iLJwQsLmR7S86F8Q4aDRPbCfo7pISJP6Fx/tPdfFNjHS23WTA==", "dev": true, - "license": "MIT", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/disposablestack": "^0.0.5", @@ -1728,12 +2251,13 @@ } }, "node_modules/@graphql-tools/graphql-file-loader": { - "version": "8.0.6", + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.10.tgz", + "integrity": "sha512-Tu/4BbXzWBS468T/hrf+FVG+u99g0yDq53ku6fpNuoEBURnVcwSuH4ZlQnn/8Cj8FaHoZR/BvQnUuh+qjx7G0Q==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/import": "7.0.6", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/import": "7.0.10", + "@graphql-tools/utils": "^10.7.1", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -1746,16 +2270,17 @@ } }, "node_modules/@graphql-tools/graphql-tag-pluck": { - "version": "8.3.7", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.11.tgz", + "integrity": "sha512-E6nkqITS+Z5mQr7eC68XxDmYaX+FioQ4bNMqx7XxV1pyCJa6a4/c8mD8BcR3qkzQJboZXPEBRmNZhmpoJtgXpw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.22.9", "@babel/parser": "^7.16.8", "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/traverse": "^7.16.8", "@babel/types": "^7.16.8", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.1", "tslib": "^2.4.0" }, "engines": { @@ -1766,11 +2291,12 @@ } }, "node_modules/@graphql-tools/import": { - "version": "7.0.6", + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.10.tgz", + "integrity": "sha512-2aYElfSvR/6TTiSqjZ9Vx3d5dLCf4xuNJOAKW4d2n3PK2t0v2oiAu54xUoHKwks5spcYQIV1ElsaUFkt/k221w==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.1", "resolve-from": "5.0.0", "tslib": "^2.4.0" }, @@ -1782,11 +2308,12 @@ } }, "node_modules/@graphql-tools/json-file-loader": { - "version": "8.0.6", + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.10.tgz", + "integrity": "sha512-p+JzIs+Yc2e46BrIKyJ48euI2GcWmlSETM0ln0GK5nJ4JK9R1DMgyBqLcN1PchxmzX8wP1jZUyWmSugfWYW80w==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.1", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -1799,12 +2326,13 @@ } }, "node_modules/@graphql-tools/load": { - "version": "8.0.7", + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.0.11.tgz", + "integrity": "sha512-VMslHlAN3Us5ZN4y+IWzhxMzJHXPN0N+OVsApANOJqflSEz57EgUmJ2kv96WAcb3mu9+HiFh6vsa5Mwj3Ugpfg==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/schema": "^10.0.11", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/schema": "^10.0.15", + "@graphql-tools/utils": "^10.7.1", "p-limit": "3.1.0", "tslib": "^2.4.0" }, @@ -1816,11 +2344,12 @@ } }, "node_modules/@graphql-tools/merge": { - "version": "9.0.12", + "version": "9.0.16", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.16.tgz", + "integrity": "sha512-Ek2ee3e4qMsMM2pBBZpDmL7j51b3F5qYsHtckO05e8zvOWuS28yBu+VhZYOtUPr/q+lBWhL+0rvFXaUwHZEuQQ==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.1", "tslib": "^2.4.0" }, "engines": { @@ -1832,8 +2361,9 @@ }, "node_modules/@graphql-tools/optimize": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-2.0.0.tgz", + "integrity": "sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.4.0" }, @@ -1846,8 +2376,9 @@ }, "node_modules/@graphql-tools/prisma-loader": { "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, - "license": "MIT", "dependencies": { "@graphql-tools/url-loader": "^8.0.15", "@graphql-tools/utils": "^10.5.6", @@ -1875,8 +2406,9 @@ }, "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, - "license": "MIT", "dependencies": { "@whatwg-node/node-fetch": "^0.7.1", "urlpattern-polyfill": "^10.0.0" @@ -1886,9 +2418,10 @@ } }, "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.4", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.5.tgz", + "integrity": "sha512-t7kGrt2fdfNvzy1LCAE9/OnIyMtizgFhgJmk7iLJwQsLmR7S86F8Q4aDRPbCfo7pISJP6Fx/tPdfFNjHS23WTA==", "dev": true, - "license": "MIT", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/disposablestack": "^0.0.5", @@ -1902,8 +2435,9 @@ }, "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" @@ -1913,12 +2447,13 @@ } }, "node_modules/@graphql-tools/relay-operation-optimizer": { - "version": "7.0.6", + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.10.tgz", + "integrity": "sha512-Vz6FQd0J6KWM0D4Dxb+jzeKUk0OuVSWldWZJ9gaNtpxOyqwGqAR7lg/2Vp/kibIeRgD/VDz2qtTfctw3FZ+D4Q==", "dev": true, - "license": "MIT", "dependencies": { "@ardatan/relay-compiler": "12.0.0", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.1", "tslib": "^2.4.0" }, "engines": { @@ -1929,12 +2464,13 @@ } }, "node_modules/@graphql-tools/schema": { - "version": "10.0.11", + "version": "10.0.15", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.15.tgz", + "integrity": "sha512-QAD9XeC/iaVugMYWet73Vz/4wp1qmKHYPj1z/TyIW/fX41oNmNSBGNqdstMsvSG97PWLhFgbUqVCvY+1KesQKw==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/merge": "^9.0.12", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/merge": "^9.0.16", + "@graphql-tools/utils": "^10.7.1", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" }, @@ -1946,15 +2482,16 @@ } }, "node_modules/@graphql-tools/url-loader": { - "version": "8.0.18", + "version": "8.0.22", + "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.22.tgz", + "integrity": "sha512-TqFoL5w2RrIOgcbDxKGVtnom5dp24CCnyuAIkQs3vUy01DwGXHkWDaIHDNkiiEutGBeaZR7Gj7Q2Hnt9tbjRkA==", "dev": true, - "license": "MIT", "dependencies": { "@ardatan/sync-fetch": "^0.0.1", "@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/executor-legacy-ws": "^1.1.9", + "@graphql-tools/utils": "^10.7.1", "@graphql-tools/wrap": "^10.0.16", "@types/ws": "^8.0.0", "@whatwg-node/fetch": "^0.10.0", @@ -1972,8 +2509,9 @@ }, "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, - "license": "MIT", "dependencies": { "@whatwg-node/node-fetch": "^0.7.1", "urlpattern-polyfill": "^10.0.0" @@ -1983,9 +2521,10 @@ } }, "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.4", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.5.tgz", + "integrity": "sha512-t7kGrt2fdfNvzy1LCAE9/OnIyMtizgFhgJmk7iLJwQsLmR7S86F8Q4aDRPbCfo7pISJP6Fx/tPdfFNjHS23WTA==", "dev": true, - "license": "MIT", "dependencies": { "@kamilkisiela/fast-url-parser": "^1.1.4", "@whatwg-node/disposablestack": "^0.0.5", @@ -1998,9 +2537,10 @@ } }, "node_modules/@graphql-tools/utils": { - "version": "10.6.2", + "version": "10.7.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.7.1.tgz", + "integrity": "sha512-mpHAA5EddtxvnkHIBEEon5++tvL5T+j3OeOP4CAXbguAK2RBRM9DVVsoc9U68vSPLJjBRGp+b5NjlRn04g9rMA==", "dev": true, - "license": "MIT", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "cross-inspect": "1.0.1", @@ -2015,13 +2555,14 @@ } }, "node_modules/@graphql-tools/wrap": { - "version": "10.0.25", + "version": "10.0.27", + "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.27.tgz", + "integrity": "sha512-UikYBknzYgJKhzIXrzA58EO8IZ+jlX/iPmfUactK6aypc7iKCJzGD31Ha8rDI9GiHPn1F8PUAB4cTlGJ1qRh3w==", "dev": true, - "license": "MIT", "dependencies": { - "@graphql-tools/delegate": "^10.2.7", + "@graphql-tools/delegate": "^10.2.9", "@graphql-tools/schema": "^10.0.11", - "@graphql-tools/utils": "^10.6.2", + "@graphql-tools/utils": "^10.7.0", "tslib": "^2.8.1" }, "engines": { @@ -2033,7 +2574,8 @@ }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", "peerDependencies": { "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 || ^17.0.0" } @@ -2075,8 +2617,9 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -2100,8 +2643,9 @@ }, "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", @@ -2116,8 +2660,9 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { "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" }, @@ -2127,8 +2672,9 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { "version": "6.2.1", + "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" }, @@ -2138,8 +2684,9 @@ }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", + "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", @@ -2154,8 +2701,9 @@ }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", + "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" }, @@ -2168,8 +2716,9 @@ }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", + "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", @@ -2184,16 +2733,18 @@ }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -2205,29 +2756,33 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2235,7 +2790,8 @@ }, "node_modules/@js-temporal/polyfill": { "version": "0.4.4", - "license": "ISC", + "resolved": "https://registry.npmjs.org/@js-temporal/polyfill/-/polyfill-0.4.4.tgz", + "integrity": "sha512-2X6bvghJ/JAoZO52lbgyAPFj8uCflhTo2g7nkFzEQdXd/D8rEeD4HtmTEpmtGCva260fcd66YNXBOYdnmHqSOg==", "dependencies": { "jsbi": "^4.3.0", "tslib": "^2.4.1" @@ -2246,12 +2802,14 @@ }, "node_modules/@kamilkisiela/fast-url-parser": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz", + "integrity": "sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==", + "dev": true }, "node_modules/@mapbox/geojson-rewind": { "version": "0.5.2", - "license": "ISC", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", "dependencies": { "get-stream": "^6.0.1", "minimist": "^1.2.6" @@ -2262,32 +2820,39 @@ }, "node_modules/@mapbox/jsonlint-lines-primitives": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", "engines": { "node": ">= 0.6" } }, "node_modules/@mapbox/point-geometry": { "version": "0.1.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" }, "node_modules/@mapbox/tiny-sdf": { "version": "2.0.6", - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==" }, "node_modules/@mapbox/unitbezier": { "version": "0.0.1", - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==" }, "node_modules/@mapbox/vector-tile": { "version": "1.3.1", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", "dependencies": { "@mapbox/point-geometry": "~0.1.0" } }, "node_modules/@mapbox/whoots-js": { "version": "3.1.0", - "license": "ISC", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", "engines": { "node": ">=6.0.0" } @@ -2318,8 +2883,9 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -2330,16 +2896,18 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -2350,9 +2918,10 @@ }, "node_modules/@parcel/watcher": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", @@ -2382,13 +2951,54 @@ "@parcel/watcher-win32-x64": "2.5.0" } }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@parcel/watcher-darwin-x64": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -2401,10 +3011,211 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", + "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" @@ -2412,7 +3223,8 @@ }, "node_modules/@popperjs/core": { "version": "2.11.8", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -2420,7 +3232,8 @@ }, "node_modules/@react-aria/ssr": { "version": "3.9.7", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz", + "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==", "dependencies": { "@swc/helpers": "^0.5.0" }, @@ -2433,12 +3246,14 @@ }, "node_modules/@repeaterjs/repeater": { "version": "3.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", + "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==", + "dev": true }, "node_modules/@restart/hooks": { "version": "0.4.16", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", "dependencies": { "dequal": "^2.0.3" }, @@ -2448,7 +3263,8 @@ }, "node_modules/@restart/ui": { "version": "1.9.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.2.tgz", + "integrity": "sha512-MWWqJqSyqUWWPBOOiRQrX57CBc/9CoYONg7sE+uag72GCAuYrHGU5c49vU5s4BUSBgiKNY6rL7TULqGDrouUaA==", "dependencies": { "@babel/runtime": "^7.26.0", "@popperjs/core": "^2.11.8", @@ -2467,7 +3283,8 @@ }, "node_modules/@restart/ui/node_modules/@restart/hooks": { "version": "0.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.0.tgz", + "integrity": "sha512-wS+h6IusJCPjTkmOOrRZxIPICD/mtFA3PRZviutoM23/b7akyDGfZF/WS+nIFk27u7JDhPE2+0GBdZxjSqHZkg==", "dependencies": { "dequal": "^2.0.3" }, @@ -2477,39 +3294,278 @@ }, "node_modules/@restart/ui/node_modules/uncontrollable": { "version": "8.0.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", "peerDependencies": { "react": ">=16.14.0" } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.28.1", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz", + "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==", "cpu": [ - "x64" + "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ - "darwin" + "android" ] }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz", + "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@testing-library/dom": { - "version": "10.4.0", - "dev": true, - "license": "MIT", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz", + "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz", + "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz", + "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz", + "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz", + "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz", + "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz", + "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz", + "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz", + "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz", + "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz", + "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz", + "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz", + "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz", + "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz", + "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz", + "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz", + "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@swc/helpers": { + "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.8.0" + } + }, + "node_modules/@testing-library/dom": { + "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": { "@babel/code-frame": "^7.10.4", @@ -2527,8 +3583,9 @@ }, "node_modules/@testing-library/react": { "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" }, @@ -2553,14 +3610,16 @@ }, "node_modules/@types/aria-query": { "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -2571,16 +3630,18 @@ }, "node_modules/@types/babel__generator": { "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, - "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -2588,32 +3649,37 @@ }, "node_modules/@types/babel__traverse": { "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, - "license": "MIT", "dependencies": { "@babel/types": "^7.20.7" } }, "node_modules/@types/estree": { "version": "1.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true }, "node_modules/@types/geojson": { "version": "7946.0.15", - "license": "MIT" + "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", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", "dependencies": { "@types/geojson": "*" } }, "node_modules/@types/js-yaml": { "version": "4.0.9", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -2623,16 +3689,19 @@ }, "node_modules/@types/json5": { "version": "0.0.29", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true }, "node_modules/@types/mapbox__point-geometry": { "version": "0.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==" }, "node_modules/@types/mapbox__vector-tile": { "version": "1.3.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", "dependencies": { "@types/geojson": "*", "@types/mapbox__point-geometry": "*", @@ -2641,64 +3710,74 @@ }, "node_modules/@types/mapbox-gl": { "version": "3.4.1", - "license": "MIT", + "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": "22.10.1", + "version": "22.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.3.tgz", + "integrity": "sha512-DifAyw4BkrufCILvD3ucnuN8eydUfc/C1GlyrnI+LK6543w5/L3VeVgf05o3B4fqSXP1dKYLOZsKfutpxPzZrw==", "dev": true, - "license": "MIT", "dependencies": { "undici-types": "~6.20.0" } }, "node_modules/@types/pbf": { "version": "3.0.5", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" }, "node_modules/@types/prop-types": { "version": "15.7.14", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" }, "node_modules/@types/react": { "version": "19.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.1.tgz", + "integrity": "sha512-YW6614BDhqbpR5KtUYzTA+zlA7nayzJRA9ljz9CQoxthR0sDisYZLuvSMsil36t4EH/uAt8T52Xb4sVw17G+SQ==", "dependencies": { "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", + "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", "dev": true, - "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" } }, "node_modules/@types/react-transition-group": { - "version": "4.4.11", - "license": "MIT", - "dependencies": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "peerDependencies": { "@types/react": "*" } }, "node_modules/@types/supercluster": { "version": "7.1.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", "dependencies": { "@types/geojson": "*" } }, "node_modules/@types/warning": { "version": "3.0.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" }, "node_modules/@types/ws": { "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -2732,76 +3811,16 @@ "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "node_modules/@typescript-eslint/parser": { "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", + "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", "dev": true, "dependencies": { + "@typescript-eslint/scope-manager": "8.19.0", "@typescript-eslint/types": "8.19.0", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-pS1hdZ+vnrpDIxuFXYQpLTILglTjSYJ9MbetZctrUawogUsPdz31DIIRZ9+rab0LhYNTsk88w4fIzVheiTbWOQ==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/scope-manager": "8.0.0", - "@typescript-eslint/types": "8.0.0", - "@typescript-eslint/typescript-estree": "8.0.0", - "@typescript-eslint/visitor-keys": "8.0.0", + "@typescript-eslint/typescript-estree": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", "debug": "^4.3.4" }, "engines": { @@ -2812,23 +3831,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0.tgz", - "integrity": "sha512-V0aa9Csx/ZWWv2IPgTfY7T4agYwJyILESu/PVqFtTFz9RIS823mAze+NbnBI8xiwdX3iqeQbcTYlvB04G9wyQw==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", + "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "8.0.0", - "@typescript-eslint/visitor-keys": "8.0.0" + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2861,7 +3875,7 @@ "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "node_modules/@typescript-eslint/types": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", @@ -2874,7 +3888,7 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "node_modules/@typescript-eslint/typescript-estree": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", @@ -2900,36 +3914,31 @@ "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.19.0", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "balanced-match": "^1.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", @@ -2941,62 +3950,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0.tgz", - "integrity": "sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==", - "dev": true, - "peer": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0.tgz", - "integrity": "sha512-5b97WpKMX+Y43YKi4zVcCVLtK5F98dFls3Oxui8LbnmRsseKenbbDinmvxrWegKDMmlkIq/XHuyy0UGLtpCDKg==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "8.0.0", - "@typescript-eslint/visitor-keys": "8.0.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", @@ -3020,63 +3973,7 @@ "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", - "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "node_modules/@typescript-eslint/visitor-keys": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", @@ -3093,52 +3990,11 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0.tgz", - "integrity": "sha512-oN0K4nkHuOyF3PVMyETbpP5zp6wfyOvm7tWhTMfoqxSSsPmJIh6JNASuZDlODE8eE+0EB9uar+6+vxr9DBTYOA==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "8.0.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@vitejs/plugin-react": { "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx-self": "^7.25.9", @@ -3155,8 +4011,9 @@ }, "node_modules/@vitest/coverage-v8": { "version": "2.1.8", + "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", @@ -3186,8 +4043,9 @@ }, "node_modules/@vitest/expect": { "version": "2.1.8", + "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", @@ -3200,8 +4058,9 @@ }, "node_modules/@vitest/pretty-format": { "version": "2.1.8", + "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" }, @@ -3211,8 +4070,9 @@ }, "node_modules/@vitest/runner": { "version": "2.1.8", + "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" @@ -3223,8 +4083,9 @@ }, "node_modules/@vitest/snapshot": { "version": "2.1.8", + "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", @@ -3236,8 +4097,9 @@ }, "node_modules/@vitest/spy": { "version": "2.1.8", + "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" }, @@ -3247,8 +4109,9 @@ }, "node_modules/@vitest/utils": { "version": "2.1.8", + "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", @@ -3260,8 +4123,9 @@ }, "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, - "license": "MIT", "dependencies": { "tslib": "^2.6.3" }, @@ -3271,8 +4135,9 @@ }, "node_modules/@whatwg-node/fetch": { "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.6.0", "urlpattern-polyfill": "^10.0.0" @@ -3283,8 +4148,9 @@ }, "node_modules/@whatwg-node/node-fetch": { "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", @@ -3318,16 +4184,18 @@ }, "node_modules/agent-base": { "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 14" } }, "node_modules/aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, - "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -3354,8 +4222,9 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -3368,16 +4237,18 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3390,13 +4261,15 @@ }, "node_modules/argparse": { "version": "2.0.1", - "dev": true, - "license": "Python-2.0" + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/aria-query": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, - "license": "Apache-2.0", "peer": true, "dependencies": { "dequal": "^2.0.3" @@ -3404,7 +4277,8 @@ }, "node_modules/arr-union": { "version": "3.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", "engines": { "node": ">=0.10.0" } @@ -3427,8 +4301,9 @@ }, "node_modules/array-includes": { "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -3446,16 +4321,18 @@ }, "node_modules/array-union": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/array.prototype.findlast": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -3473,8 +4350,9 @@ }, "node_modules/array.prototype.findlastindex": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -3491,14 +4369,15 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.2", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3527,8 +4406,9 @@ }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", + "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", @@ -3563,46 +4443,53 @@ }, "node_modules/asap": { "version": "2.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true }, "node_modules/assertion-error": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" } }, "node_modules/assign-symbols": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", "engines": { "node": ">=0.10.0" } }, "node_modules/ast-types-flow": { "version": "0.0.8", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true }, "node_modules/astral-regex": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/asynckit": { "version": "0.4.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, "node_modules/auto-bind": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", + "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -3612,8 +4499,9 @@ }, "node_modules/available-typed-arrays": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, - "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -3626,29 +4514,33 @@ }, "node_modules/axe-core": { "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" } }, "node_modules/axobject-query": { "version": "4.1.0", + "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" } }, "node_modules/babel-plugin-syntax-trailing-function-commas": { "version": "7.0.0-beta.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", + "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==", + "dev": true }, "node_modules/babel-preset-fbjs": { "version": "3.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", + "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==", "dev": true, - "license": "MIT", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", @@ -3684,11 +4576,14 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ { @@ -3703,13 +4598,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/bl": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -3718,6 +4613,8 @@ }, "node_modules/bootstrap": { "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", "funding": [ { "type": "github", @@ -3728,23 +4625,25 @@ "url": "https://opencollective.com/bootstrap" } ], - "license": "MIT", "peerDependencies": { "@popperjs/core": "^2.11.8" } }, "node_modules/brace-expansion": { - "version": "2.0.1", + "version": "1.1.11", + "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" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -3753,7 +4652,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", + "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", "dev": true, "funding": [ { @@ -3769,11 +4670,10 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -3785,14 +4685,17 @@ }, "node_modules/bser": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } }, "node_modules/buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ { @@ -3808,7 +4711,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -3816,6 +4718,8 @@ }, "node_modules/busboy": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "dev": true, "dependencies": { "streamsearch": "^1.1.0" @@ -3826,7 +4730,8 @@ }, "node_modules/bytewise": { "version": "1.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", + "integrity": "sha512-rHuuseJ9iQ0na6UDhnrRVDh8YnWVlU6xM3VH6q/+yHDeUH2zIhUzP+2/h3LIrhLDBtTqzWpE3p3tP/boefskKQ==", "dependencies": { "bytewise-core": "^1.2.2", "typewise": "^1.0.3" @@ -3834,23 +4739,26 @@ }, "node_modules/bytewise-core": { "version": "1.2.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/bytewise-core/-/bytewise-core-1.2.3.tgz", + "integrity": "sha512-nZD//kc78OOxeYtRlVk8/zXqTB4gf/nlguL1ggWA8FuchMyOxcyHR4QPQZMUmA7czC+YnaBrPUCubqAWe50DaA==", "dependencies": { "typewise-core": "^1.2" } }, "node_modules/cac": { "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/call-bind": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -3866,8 +4774,9 @@ }, "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, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -3894,16 +4803,18 @@ }, "node_modules/callsites": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/camel-case": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", "dev": true, - "license": "MIT", "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -3911,14 +4822,17 @@ }, "node_modules/camelcase": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001687", + "version": "1.0.30001690", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", + "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", "dev": true, "funding": [ { @@ -3933,13 +4847,13 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/capital-case": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", "dev": true, - "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -3948,8 +4862,9 @@ }, "node_modules/chai": { "version": "5.1.2", + "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", @@ -3963,8 +4878,9 @@ }, "node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3978,8 +4894,9 @@ }, "node_modules/change-case": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", "dev": true, - "license": "MIT", "dependencies": { "camel-case": "^4.1.2", "capital-case": "^1.0.4", @@ -3997,8 +4914,9 @@ }, "node_modules/change-case-all": { "version": "1.0.15", + "resolved": "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.15.tgz", + "integrity": "sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ==", "dev": true, - "license": "MIT", "dependencies": { "change-case": "^4.1.2", "is-lower-case": "^2.0.2", @@ -4014,33 +4932,38 @@ }, "node_modules/chardet": { "version": "0.7.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true }, "node_modules/check-error": { "version": "2.1.1", + "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" } }, "node_modules/classnames": { "version": "2.5.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" }, "node_modules/clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cli-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -4050,8 +4973,9 @@ }, "node_modules/cli-spinners": { "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -4061,8 +4985,9 @@ }, "node_modules/cli-truncate": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, - "license": "MIT", "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" @@ -4076,16 +5001,18 @@ }, "node_modules/cli-width": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true, - "license": "ISC", "engines": { "node": ">= 10" } }, "node_modules/cliui": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -4097,8 +5024,9 @@ }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", + "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", @@ -4113,16 +5041,18 @@ }, "node_modules/clone": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8" } }, "node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4132,18 +5062,21 @@ }, "node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/colorette": { "version": "2.0.20", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true }, "node_modules/combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -4153,21 +5086,24 @@ }, "node_modules/common-tags": { "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/concat-map": { "version": "0.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/constant-case": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", "dev": true, - "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -4176,13 +5112,15 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true }, "node_modules/cosmiconfig": { "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, - "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -4205,17 +5143,19 @@ } }, "node_modules/cross-fetch": { - "version": "3.1.8", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", "dev": true, - "license": "MIT", "dependencies": { - "node-fetch": "^2.6.12" + "node-fetch": "^2.7.0" } }, "node_modules/cross-inspect": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.4.0" }, @@ -4225,8 +5165,9 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4238,8 +5179,9 @@ }, "node_modules/cssstyle": { "version": "4.1.0", + "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" }, @@ -4249,17 +5191,20 @@ }, "node_modules/csstype": { "version": "3.1.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", - "dev": true, - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true }, "node_modules/data-urls": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, - "license": "MIT", "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" @@ -4321,18 +5266,21 @@ }, "node_modules/dataloader": { "version": "2.2.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", + "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", + "dev": true }, "node_modules/debounce": { "version": "1.2.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true }, "node_modules/debug": { "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" }, @@ -4347,34 +5295,39 @@ }, "node_modules/decamelize": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/decimal.js": { "version": "10.4.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true }, "node_modules/deep-eql": { "version": "5.0.2", + "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" } }, "node_modules/deep-is": { "version": "0.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "node_modules/defaults": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, - "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -4384,8 +5337,9 @@ }, "node_modules/define-data-property": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -4400,8 +5354,9 @@ }, "node_modules/define-properties": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -4416,39 +5371,44 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/dependency-graph": { "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6.0" } }, "node_modules/dequal": { "version": "2.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "engines": { "node": ">=6" } }, "node_modules/detect-indent": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/detect-libc": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true, - "license": "Apache-2.0", "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -4458,8 +5418,9 @@ }, "node_modules/dir-glob": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -4467,15 +5428,29 @@ "node": ">=8" } }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/dom-accessibility-api": { "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/dom-helpers": { "version": "5.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" @@ -4483,8 +5458,9 @@ }, "node_modules/dot-case": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", "dev": true, - "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -4492,8 +5468,9 @@ }, "node_modules/dotenv": { "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -4503,8 +5480,9 @@ }, "node_modules/dset": { "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, - "license": "MIT", "engines": { "node": ">=4" } @@ -4525,27 +5503,32 @@ }, "node_modules/earcut": { "version": "3.0.1", - "license": "ISC" + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.1.tgz", + "integrity": "sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==" }, "node_modules/eastasianwidth": { "version": "0.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.71", - "dev": true, - "license": "ISC" + "version": "1.5.76", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz", + "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==", + "dev": true }, "node_modules/emoji-regex": { "version": "9.2.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/entities": { "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -4555,8 +5538,9 @@ }, "node_modules/error-ex": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -4626,16 +5610,18 @@ }, "node_modules/es-define-property": { "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, - "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-errors": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -4668,14 +5654,16 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "dev": true, - "license": "MIT" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true }, "node_modules/es-object-atoms": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -4684,13 +5672,15 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, - "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4698,16 +5688,18 @@ }, "node_modules/es-shim-unscopables": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, - "license": "MIT", "dependencies": { "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { "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, - "license": "MIT", "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", @@ -4721,10 +5713,11 @@ } }, "node_modules/esbuild": { - "version": "0.24.0", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -4732,44 +5725,47 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.0", - "@esbuild/android-arm": "0.24.0", - "@esbuild/android-arm64": "0.24.0", - "@esbuild/android-x64": "0.24.0", - "@esbuild/darwin-arm64": "0.24.0", - "@esbuild/darwin-x64": "0.24.0", - "@esbuild/freebsd-arm64": "0.24.0", - "@esbuild/freebsd-x64": "0.24.0", - "@esbuild/linux-arm": "0.24.0", - "@esbuild/linux-arm64": "0.24.0", - "@esbuild/linux-ia32": "0.24.0", - "@esbuild/linux-loong64": "0.24.0", - "@esbuild/linux-mips64el": "0.24.0", - "@esbuild/linux-ppc64": "0.24.0", - "@esbuild/linux-riscv64": "0.24.0", - "@esbuild/linux-s390x": "0.24.0", - "@esbuild/linux-x64": "0.24.0", - "@esbuild/netbsd-x64": "0.24.0", - "@esbuild/openbsd-arm64": "0.24.0", - "@esbuild/openbsd-x64": "0.24.0", - "@esbuild/sunos-x64": "0.24.0", - "@esbuild/win32-arm64": "0.24.0", - "@esbuild/win32-ia32": "0.24.0", - "@esbuild/win32-x64": "0.24.0" + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" } }, "node_modules/escalade": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -4838,8 +5834,9 @@ }, "node_modules/eslint-config-prettier": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -4849,8 +5846,9 @@ }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -4859,16 +5857,18 @@ }, "node_modules/eslint-import-resolver-node/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-module-utils": { "version": "2.12.0", + "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" }, @@ -4883,8 +5883,9 @@ }, "node_modules/eslint-module-utils/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -4922,45 +5923,15 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -4992,32 +5963,13 @@ }, "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { "version": "5.3.2", + "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" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-plugin-react": { "version": "7.37.3", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz", @@ -5071,41 +6023,11 @@ "eslint": ">=8.40" } }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.11", + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -5135,26 +6057,6 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", @@ -5166,17 +6068,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -5194,22 +6085,11 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esquery": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -5231,39 +6111,44 @@ }, "node_modules/estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/estree-walker": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } }, "node_modules/esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/expect-type": { "version": "1.1.0", + "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" } }, "node_modules/extend-shallow": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dependencies": { "is-extendable": "^0.1.0" }, @@ -5273,8 +6158,9 @@ }, "node_modules/external-editor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, - "license": "MIT", "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -5286,8 +6172,9 @@ }, "node_modules/extract-files": { "version": "11.0.0", + "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz", + "integrity": "sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==", "dev": true, - "license": "MIT", "engines": { "node": "^12.20 || >= 14.13" }, @@ -5297,8 +6184,9 @@ }, "node_modules/fast-decode-uri-component": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "dev": true }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -5308,8 +6196,9 @@ }, "node_modules/fast-glob": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -5323,8 +6212,9 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -5340,37 +6230,42 @@ }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fast-querystring": { "version": "1.1.2", + "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" } }, "node_modules/fastq": { - "version": "1.17.1", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fb-watchman": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" } }, "node_modules/fbjs": { "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", "dev": true, - "license": "MIT", "dependencies": { "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", @@ -5383,13 +6278,15 @@ }, "node_modules/fbjs-css-vars": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "dev": true }, "node_modules/figures": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, - "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -5402,8 +6299,9 @@ }, "node_modules/figures/node_modules/escape-string-regexp": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -5422,8 +6320,9 @@ }, "node_modules/fill-range": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5433,8 +6332,9 @@ }, "node_modules/find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -5467,16 +6367,18 @@ }, "node_modules/for-each": { "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "license": "MIT", "dependencies": { "is-callable": "^1.1.3" } }, "node_modules/foreground-child": { "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" @@ -5490,8 +6392,9 @@ }, "node_modules/foreground-child/node_modules/signal-exit": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "ISC", "engines": { "node": ">=14" }, @@ -5501,8 +6404,9 @@ }, "node_modules/form-data": { "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, - "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -5514,13 +6418,16 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "license": "MIT", + "hasInstallScript": true, "optional": true, "os": [ "darwin" @@ -5531,8 +6438,9 @@ }, "node_modules/function-bind": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5559,28 +6467,32 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/gensync": { "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/geojson-vt": { "version": "4.0.2", - "license": "ISC" + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==" }, "node_modules/get-caller-file": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -5624,7 +6536,8 @@ }, "node_modules/get-stream": { "version": "6.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "engines": { "node": ">=10" }, @@ -5651,19 +6564,23 @@ }, "node_modules/get-value": { "version": "2.0.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", "engines": { "node": ">=0.10.0" } }, "node_modules/gl-matrix": { "version": "3.4.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" }, "node_modules/glob": { "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, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -5681,8 +6598,9 @@ }, "node_modules/glob-parent": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -5690,29 +6608,10 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/global-prefix": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", + "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", "dependencies": { "ini": "^4.1.3", "kind-of": "^6.0.3", @@ -5724,14 +6623,16 @@ }, "node_modules/global-prefix/node_modules/isexe": { "version": "3.1.1", - "license": "ISC", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "engines": { "node": ">=16" } }, "node_modules/global-prefix/node_modules/which": { "version": "4.0.0", - "license": "ISC", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dependencies": { "isexe": "^3.1.1" }, @@ -5756,8 +6657,9 @@ }, "node_modules/globalthis": { "version": "1.0.4", + "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" @@ -5771,8 +6673,9 @@ }, "node_modules/globby": { "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -5790,8 +6693,9 @@ }, "node_modules/gopd": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5801,20 +6705,23 @@ }, "node_modules/graphemer": { "version": "1.4.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true }, "node_modules/graphql": { "version": "16.10.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, "node_modules/graphql-config": { "version": "5.1.3", + "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", @@ -5841,17 +6748,43 @@ } } }, + "node_modules/graphql-config/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/graphql-config/node_modules/jiti": { - "version": "2.4.1", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", "dev": true, - "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/graphql-config/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/graphql-request": { "version": "7.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-7.1.2.tgz", + "integrity": "sha512-+XE3iuC55C2di5ZUrB4pjgwe+nIQBuXVIK9J98wrVwojzDW3GMdSBZfxUk8l4j9TieIpjpggclxhNEU9ebGF8w==", "dependencies": { "@graphql-typed-document-node/core": "^3.2.0" }, @@ -5861,8 +6794,9 @@ }, "node_modules/graphql-tag": { "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.1.0" }, @@ -5875,8 +6809,9 @@ }, "node_modules/graphql-ws": { "version": "5.16.0", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz", + "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -5885,25 +6820,31 @@ } }, "node_modules/has-bigints": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/has-property-descriptors": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -5913,8 +6854,9 @@ }, "node_modules/has-proto": { "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, - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.0" }, @@ -5927,8 +6869,9 @@ }, "node_modules/has-symbols": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5938,8 +6881,9 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, - "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -5952,8 +6896,9 @@ }, "node_modules/hasown": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, - "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -5963,8 +6908,9 @@ }, "node_modules/header-case": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", "dev": true, - "license": "MIT", "dependencies": { "capital-case": "^1.0.4", "tslib": "^2.0.3" @@ -5972,8 +6918,9 @@ }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, - "license": "MIT", "dependencies": { "whatwg-encoding": "^3.1.1" }, @@ -5983,13 +6930,15 @@ }, "node_modules/html-escaper": { "version": "2.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true }, "node_modules/http-proxy-agent": { "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -6000,8 +6949,9 @@ }, "node_modules/https-proxy-agent": { "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.1.2", "debug": "4" @@ -6012,8 +6962,9 @@ }, "node_modules/iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -6023,6 +6974,8 @@ }, "node_modules/ieee754": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { "type": "github", @@ -6036,29 +6989,31 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/ignore": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/immutable": { "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.8.0" } }, "node_modules/import-fresh": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -6072,16 +7027,18 @@ }, "node_modules/import-fresh/node_modules/resolve-from": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/import-from": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", + "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=12.2" }, @@ -6091,24 +7048,28 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/inflight": { "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, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -6116,20 +7077,23 @@ }, "node_modules/inherits": { "version": "2.0.4", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "4.1.3", - "license": "ISC", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/inquirer": { "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "dev": true, - "license": "MIT", "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", @@ -6167,15 +7131,17 @@ }, "node_modules/invariant": { "version": "2.2.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dependencies": { "loose-envify": "^1.0.0" } }, "node_modules/is-absolute": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, - "license": "MIT", "dependencies": { "is-relative": "^1.0.0", "is-windows": "^1.0.1" @@ -6203,8 +7169,9 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "node_modules/is-async-function": { "version": "2.0.0", @@ -6254,8 +7221,9 @@ }, "node_modules/is-callable": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6264,9 +7232,10 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, - "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -6312,15 +7281,17 @@ }, "node_modules/is-extendable": { "version": "0.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "engines": { "node": ">=0.10.0" } }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6342,8 +7313,9 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -6365,8 +7337,9 @@ }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -6376,16 +7349,18 @@ }, "node_modules/is-interactive": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-lower-case": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz", + "integrity": "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -6404,8 +7379,9 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -6428,7 +7404,8 @@ }, "node_modules/is-plain-object": { "version": "2.0.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dependencies": { "isobject": "^3.0.1" }, @@ -6438,8 +7415,9 @@ }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true }, "node_modules/is-regex": { "version": "1.2.1", @@ -6461,8 +7439,9 @@ }, "node_modules/is-relative": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, - "license": "MIT", "dependencies": { "is-unc-path": "^1.0.0" }, @@ -6547,8 +7526,9 @@ }, "node_modules/is-unc-path": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, - "license": "MIT", "dependencies": { "unc-path-regex": "^0.1.2" }, @@ -6558,8 +7538,9 @@ }, "node_modules/is-unicode-supported": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -6569,8 +7550,9 @@ }, "node_modules/is-upper-case": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz", + "integrity": "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -6620,49 +7602,56 @@ }, "node_modules/is-windows": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/isarray": { "version": "2.0.5", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isobject": { "version": "3.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "engines": { "node": ">=0.10.0" } }, "node_modules/isomorphic-ws": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", "dev": true, - "license": "MIT", "peerDependencies": { "ws": "*" } }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-report": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -6674,8 +7663,9 @@ }, "node_modules/istanbul-lib-source-maps": { "version": "5.0.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", @@ -6687,8 +7677,9 @@ }, "node_modules/istanbul-reports": { "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -6716,8 +7707,9 @@ }, "node_modules/jackspeak": { "version": "3.4.3", + "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" }, @@ -6729,29 +7721,33 @@ } }, "node_modules/jiti": { - "version": "1.21.6", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, - "license": "MIT", "bin": { "jiti": "bin/jiti.js" } }, "node_modules/jose": { "version": "5.9.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz", + "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } }, "node_modules/js-tokens": { "version": "4.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -6761,12 +7757,14 @@ }, "node_modules/jsbi": { "version": "4.3.0", - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==" }, "node_modules/jsdom": { "version": "25.0.1", + "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", @@ -6803,9 +7801,10 @@ } }, "node_modules/jsesc": { - "version": "3.0.2", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, - "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -6821,8 +7820,9 @@ }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -6832,8 +7832,9 @@ }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "node_modules/json-stringify-pretty-compact": { "version": "4.0.0", @@ -6842,8 +7843,9 @@ }, "node_modules/json-to-pretty-yaml": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", + "integrity": "sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==", "dev": true, - "license": "Apache-2.0", "dependencies": { "remedial": "^1.0.7", "remove-trailing-spaces": "^1.0.6" @@ -6854,8 +7856,9 @@ }, "node_modules/json5": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -6865,8 +7868,9 @@ }, "node_modules/jsx-ast-utils": { "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -6879,7 +7883,8 @@ }, "node_modules/kdbush": { "version": "4.0.2", - "license": "ISC" + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==" }, "node_modules/keyv": { "version": "4.5.4", @@ -6892,20 +7897,23 @@ }, "node_modules/kind-of": { "version": "6.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "engines": { "node": ">=0.10.0" } }, "node_modules/language-subtag-registry": { "version": "0.3.23", - "dev": true, - "license": "CC0-1.0" + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true }, "node_modules/language-tags": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, - "license": "MIT", "dependencies": { "language-subtag-registry": "^0.3.20" }, @@ -6915,8 +7923,9 @@ }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -6927,13 +7936,15 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true }, "node_modules/listr2": { "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", "dev": true, - "license": "MIT", "dependencies": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", @@ -6958,8 +7969,9 @@ }, "node_modules/listr2/node_modules/wrap-ansi": { "version": "7.0.0", + "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", @@ -6974,8 +7986,9 @@ }, "node_modules/locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -6988,23 +8001,27 @@ }, "node_modules/lodash": { "version": "4.17.21", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, "node_modules/lodash.sortby": { "version": "4.7.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true }, "node_modules/log-symbols": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -7018,8 +8035,9 @@ }, "node_modules/log-update": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, - "license": "MIT", "dependencies": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", @@ -7035,8 +8053,9 @@ }, "node_modules/log-update/node_modules/slice-ansi": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -7051,7 +8070,8 @@ }, "node_modules/loose-envify": { "version": "1.4.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -7061,54 +8081,61 @@ }, "node_modules/loupe": { "version": "3.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "dev": true }, "node_modules/lower-case": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.3" } }, "node_modules/lower-case-first": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz", + "integrity": "sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.3" } }, "node_modules/lru-cache": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, "node_modules/lz-string": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, - "license": "MIT", "peer": true, "bin": { "lz-string": "bin/bin.js" } }, "node_modules/magic-string": { - "version": "0.30.14", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/magicast": { "version": "0.3.5", + "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", @@ -7117,8 +8144,9 @@ }, "node_modules/make-dir": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, - "license": "MIT", "dependencies": { "semver": "^7.5.3" }, @@ -7131,8 +8159,9 @@ }, "node_modules/make-dir/node_modules/semver": { "version": "7.6.3", + "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" }, @@ -7142,8 +8171,9 @@ }, "node_modules/map-cache": { "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -7199,16 +8229,18 @@ }, "node_modules/merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/meros": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/meros/-/meros-1.3.0.tgz", + "integrity": "sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==", "dev": true, - "license": "MIT", "engines": { "node": ">=13" }, @@ -7223,8 +8255,9 @@ }, "node_modules/micromatch": { "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -7235,16 +8268,18 @@ }, "node_modules/mime-db": { "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -7254,57 +8289,63 @@ }, "node_modules/mimic-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/minimatch": { - "version": "9.0.5", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minimist": { "version": "1.2.8", - "license": "MIT", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { "version": "7.1.2", + "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" } }, "node_modules/ms": { "version": "2.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/murmurhash-js": { "version": "1.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" }, "node_modules/mute-stream": { "version": "0.0.8", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true }, "node_modules/nanoid": { "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -7312,7 +8353,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -7322,13 +8362,15 @@ }, "node_modules/natural-compare": { "version": "1.4.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, "node_modules/no-case": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", "dev": true, - "license": "MIT", "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -7336,13 +8378,15 @@ }, "node_modules/node-addon-api": { "version": "7.1.1", - "dev": true, - "license": "MIT" + "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", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, - "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -7360,18 +8404,21 @@ }, "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", - "dev": true, - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, - "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -7379,18 +8426,21 @@ }, "node_modules/node-int64": { "version": "0.4.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true }, "node_modules/node-releases": { - "version": "2.0.18", - "dev": true, - "license": "MIT" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true }, "node_modules/normalize-path": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, - "license": "MIT", "dependencies": { "remove-trailing-separator": "^1.0.1" }, @@ -7400,17 +8450,20 @@ }, "node_modules/nullthrows": { "version": "1.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "dev": true }, "node_modules/nwsapi": { "version": "2.2.16", - "dev": true, - "license": "MIT" + "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", - "license": "MIT", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } @@ -7429,8 +8482,9 @@ }, "node_modules/object-keys": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -7457,8 +8511,9 @@ }, "node_modules/object.entries": { "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7470,8 +8525,9 @@ }, "node_modules/object.fromentries": { "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7487,8 +8543,9 @@ }, "node_modules/object.groupby": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7518,16 +8575,18 @@ }, "node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -7540,8 +8599,9 @@ }, "node_modules/optionator": { "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -7556,8 +8616,9 @@ }, "node_modules/ora": { "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, - "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -7578,8 +8639,9 @@ }, "node_modules/os-tmpdir": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -7603,8 +8665,9 @@ }, "node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -7617,8 +8680,9 @@ }, "node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -7631,8 +8695,9 @@ }, "node_modules/p-map": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, - "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -7645,21 +8710,24 @@ }, "node_modules/p-try": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "dev": true, - "license": "BlueOak-1.0.0" + "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", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", "dev": true, - "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -7667,8 +8735,9 @@ }, "node_modules/parent-module": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -7678,8 +8747,9 @@ }, "node_modules/parse-filepath": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", "dev": true, - "license": "MIT", "dependencies": { "is-absolute": "^1.0.0", "map-cache": "^0.2.0", @@ -7691,8 +8761,9 @@ }, "node_modules/parse-json": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -7708,8 +8779,9 @@ }, "node_modules/parse5": { "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "dev": true, - "license": "MIT", "dependencies": { "entities": "^4.5.0" }, @@ -7719,8 +8791,9 @@ }, "node_modules/pascal-case": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", "dev": true, - "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -7728,8 +8801,9 @@ }, "node_modules/path-case": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", "dev": true, - "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -7737,37 +8811,42 @@ }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-root": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", "dev": true, - "license": "MIT", "dependencies": { "path-root-regex": "^0.1.0" }, @@ -7777,16 +8856,18 @@ }, "node_modules/path-root-regex": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-scurry": { "version": "1.11.1", + "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" @@ -7800,33 +8881,38 @@ }, "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, "node_modules/path-type": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/pathe": { "version": "1.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true }, "node_modules/pathval": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 14.16" } }, "node_modules/pbf": { "version": "3.3.0", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", "dependencies": { "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" @@ -7837,13 +8923,15 @@ }, "node_modules/picocolors": { "version": "1.1.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -7853,14 +8941,17 @@ }, "node_modules/possible-typed-array-names": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -7876,7 +8967,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", @@ -7888,20 +8978,23 @@ }, "node_modules/potpack": { "version": "2.0.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", + "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==" }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/prettier": { "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, - "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -7914,8 +9007,9 @@ }, "node_modules/pretty-format": { "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "ansi-regex": "^5.0.1", @@ -7928,8 +9022,9 @@ }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "license": "MIT", "peer": true, "engines": { "node": ">=10" @@ -7940,15 +9035,17 @@ }, "node_modules/promise": { "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dev": true, - "license": "MIT", "dependencies": { "asap": "~2.0.3" } }, "node_modules/prop-types": { "version": "15.8.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -7957,7 +9054,8 @@ }, "node_modules/prop-types-extra": { "version": "1.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", "dependencies": { "react-is": "^16.3.2", "warning": "^4.0.0" @@ -7968,26 +9066,32 @@ }, "node_modules/prop-types-extra/node_modules/react-is": { "version": "16.13.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", - "license": "MIT" + "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, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -8002,8 +9106,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/quickselect": { "version": "3.0.0", @@ -8012,14 +9115,16 @@ }, "node_modules/react": { "version": "19.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", "engines": { "node": ">=0.10.0" } }, "node_modules/react-bootstrap": { "version": "2.10.7", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.7.tgz", + "integrity": "sha512-w6mWb3uytB5A18S2oTZpYghcOUK30neMBBvZ/bEfA+WIF2dF4OGqjzoFVMpVXBjtyf92gkmRToHlddiMAVhQqQ==", "dependencies": { "@babel/runtime": "^7.24.7", "@restart/hooks": "^0.4.9", @@ -8048,7 +9153,8 @@ }, "node_modules/react-dom": { "version": "19.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", "dependencies": { "scheduler": "^0.25.0" }, @@ -8058,17 +9164,20 @@ }, "node_modules/react-is": { "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "node_modules/react-map-gl": { "version": "7.1.7", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-7.1.7.tgz", + "integrity": "sha512-mwjc0obkBJOXCcoXQr3VoLqmqwo9vS4bXfbGsdxXzEgVCv/PM0v+1QggL7W0d/ccIy+VCjbXNlGij+PENz6VNg==", "dependencies": { "@maplibre/maplibre-gl-style-spec": "^19.2.1", "@types/mapbox-gl": ">=1.0.0" @@ -8090,7 +9199,8 @@ }, "node_modules/react-map-gl/node_modules/@maplibre/maplibre-gl-style-spec": { "version": "19.3.3", - "license": "ISC", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz", + "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", @@ -8107,19 +9217,22 @@ }, "node_modules/react-map-gl/node_modules/json-stringify-pretty-compact": { "version": "3.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", + "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==" }, "node_modules/react-refresh": { "version": "0.14.2", + "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" } }, "node_modules/react-transition-group": { "version": "4.4.5", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -8133,8 +9246,9 @@ }, "node_modules/readable-stream": { "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -8168,12 +9282,14 @@ }, "node_modules/regenerator-runtime": { "version": "0.14.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { "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, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -8189,8 +9305,9 @@ }, "node_modules/relay-runtime": { "version": "12.0.0", + "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-12.0.0.tgz", + "integrity": "sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug==", "dev": true, - "license": "MIT", "dependencies": { "@babel/runtime": "^7.0.0", "fbjs": "^3.0.0", @@ -8199,70 +9316,82 @@ }, "node_modules/remedial": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", + "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==", "dev": true, - "license": "(MIT OR Apache-2.0)", "engines": { "node": "*" } }, "node_modules/remove-trailing-separator": { "version": "1.1.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true }, "node_modules/remove-trailing-spaces": { "version": "1.0.8", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", + "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==", + "dev": true }, "node_modules/require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/require-main-filename": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "node_modules/resolve": { - "version": "1.22.8", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, - "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/resolve-from": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/resolve-protobuf-schema": { "version": "2.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", "dependencies": { "protocol-buffers-schema": "^3.3.1" } }, "node_modules/restore-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, - "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -8273,8 +9402,9 @@ }, "node_modules/reusify": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -8282,13 +9412,15 @@ }, "node_modules/rfdc": { "version": "1.4.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true }, "node_modules/rollup": { - "version": "4.28.1", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz", + "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "1.0.6" }, @@ -8300,43 +9432,47 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@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", + "@rollup/rollup-android-arm-eabi": "4.29.1", + "@rollup/rollup-android-arm64": "4.29.1", + "@rollup/rollup-darwin-arm64": "4.29.1", + "@rollup/rollup-darwin-x64": "4.29.1", + "@rollup/rollup-freebsd-arm64": "4.29.1", + "@rollup/rollup-freebsd-x64": "4.29.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.29.1", + "@rollup/rollup-linux-arm-musleabihf": "4.29.1", + "@rollup/rollup-linux-arm64-gnu": "4.29.1", + "@rollup/rollup-linux-arm64-musl": "4.29.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.29.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1", + "@rollup/rollup-linux-riscv64-gnu": "4.29.1", + "@rollup/rollup-linux-s390x-gnu": "4.29.1", + "@rollup/rollup-linux-x64-gnu": "4.29.1", + "@rollup/rollup-linux-x64-musl": "4.29.1", + "@rollup/rollup-win32-arm64-msvc": "4.29.1", + "@rollup/rollup-win32-ia32-msvc": "4.29.1", + "@rollup/rollup-win32-x64-msvc": "4.29.1", "fsevents": "~2.3.2" } }, "node_modules/rrweb-cssom": { "version": "0.7.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true }, "node_modules/run-async": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -8352,19 +9488,20 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/rw": { "version": "1.3.3", - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, "node_modules/rxjs": { "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } @@ -8390,6 +9527,8 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -8404,8 +9543,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/safe-push-apply": { "version": "1.0.0", @@ -8442,13 +9580,15 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "node_modules/saxes": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, - "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" }, @@ -8458,25 +9598,29 @@ }, "node_modules/scheduler": { "version": "0.25.0", - "license": "MIT" + "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", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/scuid/-/scuid-1.1.0.tgz", + "integrity": "sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==", + "dev": true }, "node_modules/semver": { "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/sentence-case": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", "dev": true, - "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -8485,13 +9629,15 @@ }, "node_modules/set-blocking": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true }, "node_modules/set-function-length": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -8506,8 +9652,9 @@ }, "node_modules/set-function-name": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -8520,7 +9667,8 @@ }, "node_modules/set-value": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dependencies": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -8533,13 +9681,15 @@ }, "node_modules/setimmediate": { "version": "1.0.5", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true }, "node_modules/shebang-command": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -8549,16 +9699,18 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/shell-quote": { "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8640,31 +9792,36 @@ }, "node_modules/siginfo": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true }, "node_modules/signal-exit": { "version": "3.0.7", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, "node_modules/signedsource": { "version": "1.0.0", - "dev": true, - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz", + "integrity": "sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==", + "dev": true }, "node_modules/slash": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/slice-ansi": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -8676,8 +9833,9 @@ }, "node_modules/snake-case": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", "dev": true, - "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -8685,21 +9843,24 @@ }, "node_modules/sort-asc": { "version": "0.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.2.0.tgz", + "integrity": "sha512-umMGhjPeHAI6YjABoSTrFp2zaBtXBej1a0yKkuMUyjjqu6FJsTF+JYwCswWDg+zJfk/5npWUUbd33HH/WLzpaA==", "engines": { "node": ">=0.10.0" } }, "node_modules/sort-desc": { "version": "0.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.2.0.tgz", + "integrity": "sha512-NqZqyvL4VPW+RAxxXnB8gvE1kyikh8+pR+T+CXLksVRN9eiQqkQlPwqWYU0mF9Jm7UnctShlxLyAt1CaBOTL1w==", "engines": { "node": ">=0.10.0" } }, "node_modules/sort-object": { "version": "3.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-3.0.3.tgz", + "integrity": "sha512-nK7WOY8jik6zaG9CRwZTaD5O7ETWDLZYMM12pqY8htll+7dYeqGfEUPcUBHOpSJg2vJOrvFIY2Dl5cX2ih1hAQ==", "dependencies": { "bytewise": "^1.1.0", "get-value": "^2.0.2", @@ -8714,15 +9875,17 @@ }, "node_modules/source-map-js": { "version": "1.2.1", + "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" } }, "node_modules/split-string": { "version": "3.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dependencies": { "extend-shallow": "^3.0.0" }, @@ -8732,7 +9895,8 @@ }, "node_modules/split-string/node_modules/extend-shallow": { "version": "3.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -8743,7 +9907,8 @@ }, "node_modules/split-string/node_modules/is-extendable": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dependencies": { "is-plain-object": "^2.0.4" }, @@ -8753,24 +9918,29 @@ }, "node_modules/sponge-case": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz", + "integrity": "sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.3" } }, "node_modules/stackback": { "version": "0.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true }, "node_modules/std-env": { "version": "3.8.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true }, "node_modules/streamsearch": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "dev": true, "engines": { "node": ">=10.0.0" @@ -8778,21 +9948,24 @@ }, "node_modules/string_decoder": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } }, "node_modules/string-env-interpolation": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz", + "integrity": "sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==", + "dev": true }, "node_modules/string-width": { "version": "4.2.3", + "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", @@ -8805,8 +9978,9 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", + "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", @@ -8818,18 +9992,21 @@ }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/string.prototype.includes": { "version": "2.0.1", + "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", @@ -8868,8 +10045,9 @@ }, "node_modules/string.prototype.repeat": { "version": "1.0.0", + "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" @@ -8916,8 +10094,9 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -8932,8 +10111,9 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", + "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" }, @@ -8944,8 +10124,9 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", + "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" }, @@ -8955,8 +10136,9 @@ }, "node_modules/strip-bom": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -8975,15 +10157,17 @@ }, "node_modules/supercluster": { "version": "8.0.1", - "license": "ISC", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", "dependencies": { "kdbush": "^4.0.2" } }, "node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -8993,8 +10177,9 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -9004,21 +10189,24 @@ }, "node_modules/swap-case": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz", + "integrity": "sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.3" } }, "node_modules/symbol-tree": { "version": "3.2.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true }, "node_modules/test-exclude": { "version": "7.0.1", + "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", @@ -9028,10 +10216,20 @@ "node": ">=18" } }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/test-exclude/node_modules/glob": { "version": "10.4.5", + "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", @@ -9047,25 +10245,44 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/through": { "version": "2.3.8", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true }, "node_modules/tinybench": { "version": "2.9.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true }, "node_modules/tinyexec": { - "version": "0.3.1", - "dev": true, - "license": "MIT" + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true }, "node_modules/tinypool": { "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" } @@ -9077,48 +10294,54 @@ }, "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" } }, "node_modules/tinyspy": { "version": "3.0.2", + "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" } }, "node_modules/title-case": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", + "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.3" } }, "node_modules/tldts": { - "version": "6.1.66", + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.70.tgz", + "integrity": "sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==", "dev": true, - "license": "MIT", "dependencies": { - "tldts-core": "^6.1.66" + "tldts-core": "^6.1.70" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.66", - "dev": true, - "license": "MIT" + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz", + "integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==", + "dev": true }, "node_modules/tmp": { "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, - "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" }, @@ -9128,8 +10351,9 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -9139,8 +10363,9 @@ }, "node_modules/tough-cookie": { "version": "5.0.0", + "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" }, @@ -9150,8 +10375,9 @@ }, "node_modules/tr46": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", "dev": true, - "license": "MIT", "dependencies": { "punycode": "^2.3.1" }, @@ -9173,13 +10399,15 @@ }, "node_modules/ts-log": { "version": "2.2.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.7.tgz", + "integrity": "sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg==", + "dev": true }, "node_modules/tsconfig-paths": { "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, - "license": "MIT", "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -9189,8 +10417,9 @@ }, "node_modules/tsconfig-paths/node_modules/json5": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "license": "MIT", "dependencies": { "minimist": "^1.2.0" }, @@ -9200,12 +10429,14 @@ }, "node_modules/tslib": { "version": "2.8.1", - "license": "0BSD" + "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", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -9215,8 +10446,9 @@ }, "node_modules/type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -9280,8 +10512,9 @@ }, "node_modules/typed-array-length": { "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, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -9299,8 +10532,9 @@ }, "node_modules/typescript": { "version": "5.7.2", + "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" @@ -9331,140 +10565,23 @@ "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", - "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.19.0", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/typescript-eslint/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/typewise": { "version": "1.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz", + "integrity": "sha512-aXofE06xGhaQSPzt8hlTY+/YWQhm9P0jYUp1f2XtmW/3Bk0qzXcyFWAtPoo2uTGQj1ZwbDuSyuxicq+aDo8lCQ==", "dependencies": { "typewise-core": "^1.2.0" } }, "node_modules/typewise-core": { "version": "1.2.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/typewise-core/-/typewise-core-1.2.0.tgz", + "integrity": "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg==" }, "node_modules/ua-parser-js": { - "version": "1.0.39", + "version": "1.0.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", + "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", "dev": true, "funding": [ { @@ -9480,7 +10597,6 @@ "url": "https://github.com/sponsors/faisalman" } ], - "license": "MIT", "bin": { "ua-parser-js": "script/cli.js" }, @@ -9508,15 +10624,17 @@ }, "node_modules/unc-path-regex": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/uncontrollable": { "version": "7.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", "dependencies": { "@babel/runtime": "^7.6.3", "@types/react": ">=16.9.11", @@ -9529,12 +10647,14 @@ }, "node_modules/undici-types": { "version": "6.20.0", - "dev": true, - "license": "MIT" + "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": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dependencies": { "arr-union": "^3.1.0", "get-value": "^2.0.6", @@ -9547,8 +10667,9 @@ }, "node_modules/unixify": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz", + "integrity": "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==", "dev": true, - "license": "MIT", "dependencies": { "normalize-path": "^2.1.1" }, @@ -9558,6 +10679,8 @@ }, "node_modules/update-browserslist-db": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -9573,7 +10696,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.0" @@ -9587,16 +10709,18 @@ }, "node_modules/upper-case": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.3" } }, "node_modules/upper-case-first": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -9612,26 +10736,30 @@ }, "node_modules/urlpattern-polyfill": { "version": "10.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "dev": true }, "node_modules/util-deprecate": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true }, "node_modules/value-or-promise": { "version": "1.0.12", + "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", + "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" } }, "node_modules/vite": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz", + "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.24.0", "postcss": "^8.4.49", @@ -9698,211 +10826,921 @@ } } }, - "node_modules/vite-node": { - "version": "2.1.8", + "node_modules/vite-node": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", + "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vite-node/node_modules/vite": { + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", + "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", + "dev": true, + "dependencies": { + "@vitest/expect": "2.1.8", + "@vitest/mocker": "2.1.8", + "@vitest/pretty-format": "^2.1.8", + "@vitest/runner": "2.1.8", + "@vitest/snapshot": "2.1.8", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.8", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.8", + "@vitest/ui": "2.1.8", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "node": ">=12" } }, - "node_modules/vite-node/node_modules/@esbuild/darwin-x64": { + "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-x64": { "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { "node": ">=12" } }, - "node_modules/vite-node/node_modules/esbuild": { + "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" } }, - "node_modules/vite-node/node_modules/vite": { - "version": "5.4.11", + "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "node": ">=12" } }, - "node_modules/vitest": { - "version": "2.1.8", + "node_modules/vitest/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "2.1.8", - "@vitest/mocker": "2.1.8", - "@vitest/pretty-format": "^2.1.8", - "@vitest/runner": "2.1.8", - "@vitest/snapshot": "2.1.8", - "@vitest/spy": "2.1.8", - "@vitest/utils": "2.1.8", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", - "std-env": "^3.8.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.8", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.8", - "@vitest/ui": "2.1.8", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } + "node": ">=12" } }, - "node_modules/vitest/node_modules/@esbuild/darwin-x64": { + "node_modules/vitest/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-x64": { "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ - "darwin" + "win32" ], "engines": { "node": ">=12" @@ -9910,8 +11748,9 @@ }, "node_modules/vitest/node_modules/@vitest/mocker": { "version": "2.1.8", + "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", @@ -9935,9 +11774,10 @@ }, "node_modules/vitest/node_modules/esbuild": { "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -9972,8 +11812,9 @@ }, "node_modules/vitest/node_modules/vite": { "version": "5.4.11", + "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", @@ -10030,7 +11871,8 @@ }, "node_modules/vt-pbf": { "version": "3.1.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", "dependencies": { "@mapbox/point-geometry": "0.1.0", "@mapbox/vector-tile": "^1.3.1", @@ -10039,8 +11881,9 @@ }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, - "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" }, @@ -10050,31 +11893,35 @@ }, "node_modules/warning": { "version": "4.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "dependencies": { "loose-envify": "^1.0.0" } }, "node_modules/wcwidth": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, - "license": "MIT", "dependencies": { "defaults": "^1.0.3" } }, "node_modules/webidl-conversions": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=12" } }, "node_modules/whatwg-encoding": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "dev": true, - "license": "MIT", "dependencies": { "iconv-lite": "0.6.3" }, @@ -10084,8 +11931,9 @@ }, "node_modules/whatwg-encoding/node_modules/iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -10095,16 +11943,18 @@ }, "node_modules/whatwg-mimetype": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/whatwg-url": { "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, - "license": "MIT", "dependencies": { "tr46": "^5.0.0", "webidl-conversions": "^7.0.0" @@ -10115,8 +11965,9 @@ }, "node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -10193,8 +12044,9 @@ }, "node_modules/which-module": { "version": "2.0.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true }, "node_modules/which-typed-array": { "version": "1.1.18", @@ -10218,8 +12070,9 @@ }, "node_modules/why-is-node-running": { "version": "2.3.0", + "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" @@ -10233,16 +12086,18 @@ }, "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, - "license": "MIT", "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", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -10255,8 +12110,9 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", + "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", @@ -10271,13 +12127,15 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/ws": { "version": "8.18.0", + "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" }, @@ -10296,34 +12154,39 @@ }, "node_modules/xml-name-validator": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18" } }, "node_modules/xmlchars": { "version": "2.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true }, "node_modules/y18n": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "3.1.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yaml": { - "version": "2.6.1", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true, - "license": "ISC", "bin": { "yaml": "bin.mjs" }, @@ -10333,13 +12196,15 @@ }, "node_modules/yaml-ast-parser": { "version": "0.0.43", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true }, "node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -10355,16 +12220,18 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/yocto-queue": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, From e1b3064c2a54177a4a6b04ebf36e28dcb8bdda8f Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 2 Jan 2025 15:56:47 +0000 Subject: [PATCH 216/269] Upgrade debug client to version 2025/01/2025-01-02T15:56 --- 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 7ebc0fee49a..db412af9df5 100644 --- a/application/src/client/index.html +++ b/application/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug - - + +
From f2406e7cafb0b13f09f9b7769ec31cdeb61b5cd8 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 2 Jan 2025 16:53:29 +0100 Subject: [PATCH 217/269] refactor: Cleanup test and make test run on Windows OS --- .../_support/text/TextAssertionsTest.java | 13 ++++++------ .../InjectCustomDocumentationTest.java | 21 +++++++++++-------- .../InjectCustomDocumentationTest.graphql | 2 ++ ...ctCustomDocumentationTest.graphql.expected | 2 ++ 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/application/src/test/java/org/opentripplanner/_support/text/TextAssertionsTest.java b/application/src/test/java/org/opentripplanner/_support/text/TextAssertionsTest.java index b1bdb3792cb..739b7b59c4b 100644 --- a/application/src/test/java/org/opentripplanner/_support/text/TextAssertionsTest.java +++ b/application/src/test/java/org/opentripplanner/_support/text/TextAssertionsTest.java @@ -37,12 +37,13 @@ void testEndOfText() { org.opentest4j.AssertionFailedError.class, () -> assertLinesEquals("A\n", "A\nExtra Line") ); - Assertions.assertEquals( - """ - Expected(@end-of-text): <> - Actual (@line 1): - """, - ex.getMessage() + Assertions.assertTrue( + ex.getMessage().contains("Expected(@end-of-text)"), + "<" + ex.getMessage() + "> does not contain expected line." + ); + Assertions.assertTrue( + ex.getMessage().contains("Actual (@line 1): "), + "<" + ex.getMessage() + "> does not contain actual line." ); } } diff --git a/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java b/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java index 3e856faf413..4231d7079e5 100644 --- a/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.java @@ -34,12 +34,11 @@ class InjectCustomDocumentationTest { private GraphQLSchema schema; - private String sdl; private String sdlExpected; @BeforeEach void setUp() throws IOException { - sdl = loadSchemaResource(".graphql"); + var sdl = loadSchemaResource(".graphql"); sdlExpected = loadSchemaResource(".graphql.expected"); var parser = new SchemaParser(); @@ -88,10 +87,12 @@ static Map text() { "AEnum.description", "AEnum.E1.description", "AEnum.E2.deprecated", + "AEnum.E3.deprecated", "Duration.description", "InputType.description", "InputType.a.description", - "InputType.b.deprecated" + "InputType.b.deprecated", + "InputType.c.deprecated" ) .collect(Collectors.toMap(e -> e, e -> e)); } @@ -103,11 +104,7 @@ void test() { var visitor = new InjectCustomDocumentation(customDocumentation); var newSchema = SchemaTransformer.transformSchema(schema, visitor); var p = new SchemaPrinter(); - var result = p - .print(newSchema) - // Some editors like IntelliJ remove space characters at the end of a - // line, so we do the same here to avoid false positive results. - .replaceAll(" +\\n", "\n"); + var result = p.print(newSchema); var missingValues = texts .values() @@ -118,13 +115,19 @@ void test() { // There is a bug in the Java GraphQL API, existing deprecated // doc is not updated or replaced. - var expected = List.of("BType.a.deprecated", "CType.b.deprecated.append"); + var expected = List.of( + "AEnum.E3.deprecated", + "BType.a.deprecated", + "CType.b.deprecated.append", + "InputType.c.deprecated" + ); assertEquals(expected, missingValues); TextAssertions.assertLinesEquals(sdlExpected, result); } + @SuppressWarnings("DataFlowIssue") String loadSchemaResource(String suffix) throws IOException { var cl = getClass(); var name = cl.getName().replace('.', '/') + suffix; diff --git a/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql b/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql index 599c4d3b12a..33deaa2a364 100644 --- a/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql @@ -40,6 +40,7 @@ type QueryType { enum AEnum { E1 E2 + E3 @deprecated(reason: "REPLACE") } # Add doc to scalar @@ -49,4 +50,5 @@ scalar Duration input InputType { a: String b: String + c: String @deprecated(reason: "REPLACE") } diff --git a/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql.expected b/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql.expected index c8cb7f680bf..47319e07ae0 100644 --- a/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql.expected +++ b/application/src/test/resources/org/opentripplanner/apis/support/graphql/injectdoc/InjectCustomDocumentationTest.graphql.expected @@ -80,6 +80,7 @@ enum AEnum { "AEnum.E1.description" E1 E2 @deprecated(reason : "AEnum.E2.deprecated") + E3 @deprecated(reason : "REPLACE") } "Duration.description" @@ -90,4 +91,5 @@ input InputType { "InputType.a.description" a: String b: String @deprecated(reason : "InputType.b.deprecated") + c: String @deprecated(reason : "REPLACE") } From 713143b74df15a5cb25cafe3300530bdc5f7d6ba Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 2 Jan 2025 22:58:53 +0100 Subject: [PATCH 218/269] Use three states for accessibility --- .../module/osm/ElevatorProcessor.java | 4 ++-- .../graph_builder/module/osm/VertexGenerator.java | 4 +--- .../opentripplanner/osm/model/OsmWithTags.java | 2 +- .../mapping/StatesToWalkStepsMapper.java | 1 - .../model/vertex/StationEntranceVertex.java | 8 ++++---- .../street/model/vertex/VertexFactory.java | 7 +++++-- .../osm/model/OsmWithTagsTest.java | 15 +++++++++++++++ 7 files changed, 28 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/ElevatorProcessor.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/ElevatorProcessor.java index 490d6a266b9..45ed01e4568 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/ElevatorProcessor.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/ElevatorProcessor.java @@ -95,7 +95,7 @@ public void buildElevatorEdges(Graph graph) { } int travelTime = parseDuration(node).orElse(-1); - var wheelchair = node.getWheelchairAccessibility(); + var wheelchair = node.wheelchairAccessibility(); createElevatorHopEdges( onboardVertices, @@ -138,7 +138,7 @@ public void buildElevatorEdges(Graph graph) { int travelTime = parseDuration(elevatorWay).orElse(-1); int levels = nodes.size(); - var wheelchair = elevatorWay.getWheelchairAccessibility(); + var wheelchair = elevatorWay.wheelchairAccessibility(); createElevatorHopEdges( onboardVertices, diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java index b4c3c08aa8b..8c707d005a9 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/VertexGenerator.java @@ -104,9 +104,7 @@ IntersectionVertex getVertexForOsmNode(OsmNode node, OsmWithTags way) { if (includeOsmSubwayEntrances && node.isSubwayEntrance()) { String ref = node.getTag("ref"); - - boolean accessible = node.isTag("wheelchair", "yes"); - iv = vertexFactory.stationEntrance(nid, coordinate, ref, accessible); + iv = vertexFactory.stationEntrance(nid, coordinate, ref, node.wheelchairAccessibility()); } if (iv == null) { 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 3f47d4454bd..10214460b15 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -139,7 +139,7 @@ public boolean isTagFalse(String tag) { /** * Returns the level of wheelchair access of the element. */ - public Accessibility getWheelchairAccessibility() { + public Accessibility wheelchairAccessibility() { if (isTagTrue("wheelchair")) { return Accessibility.POSSIBLE; } else if (isTagFalse("wheelchair")) { 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 9365c50509c..ae7674c7a96 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 @@ -29,7 +29,6 @@ import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.state.State; -import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.site.Entrance; /** diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java index 254c71c527d..7b9a94b0725 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/StationEntranceVertex.java @@ -13,18 +13,18 @@ public class StationEntranceVertex extends OsmVertex { private static final String FEED_ID = "osm"; private final String code; - private final boolean wheelchairAccessible; + private final Accessibility wheelchairAccessibility; public StationEntranceVertex( double lat, double lon, long nodeId, String code, - boolean wheelchairAccessible + Accessibility wheelchairAccessibility ) { super(lat, lon, nodeId); this.code = code; - this.wheelchairAccessible = wheelchairAccessible; + this.wheelchairAccessibility = wheelchairAccessibility; } /** @@ -44,7 +44,7 @@ public String code() { } public Accessibility wheelchairAccessibility() { - return wheelchairAccessible ? Accessibility.POSSIBLE : Accessibility.NOT_POSSIBLE; + return wheelchairAccessibility; } @Override diff --git a/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java b/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java index 853d66c56aa..393502ba3be 100644 --- a/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java +++ b/application/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java @@ -11,6 +11,7 @@ import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex; import org.opentripplanner.street.model.edge.StreetEdge; +import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.site.BoardingArea; import org.opentripplanner.transit.model.site.Entrance; import org.opentripplanner.transit.model.site.PathwayNode; @@ -98,9 +99,11 @@ public StationEntranceVertex stationEntrance( long nid, Coordinate coordinate, String code, - boolean accessible + Accessibility wheelchairAccessibility ) { - return addToGraph(new StationEntranceVertex(coordinate.x, coordinate.y, nid, code, accessible)); + return addToGraph( + new StationEntranceVertex(coordinate.x, coordinate.y, nid, code, wheelchairAccessibility) + ); } public OsmVertex osm( 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 84b74b8f655..597593f7333 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.osm.wayproperty.specifier.WayTestData; +import org.opentripplanner.transit.model.basic.Accessibility; public class OsmWithTagsTest { @@ -215,6 +216,20 @@ void isWheelchairAccessible() { assertTrue(osm3.isWheelchairAccessible()); } + @Test + void wheelchairAccessibility() { + var osm1 = new OsmWithTags(); + assertEquals(Accessibility.NO_INFORMATION, osm1.wheelchairAccessibility()); + + var osm2 = new OsmWithTags(); + osm2.addTag("wheelchair", "no"); + assertEquals(Accessibility.NOT_POSSIBLE, osm2.wheelchairAccessibility()); + + var osm3 = new OsmWithTags(); + osm3.addTag("wheelchair", "yes"); + assertEquals(Accessibility.POSSIBLE, osm3.wheelchairAccessibility()); + } + @Test void isRoutable() { assertFalse(WayTestData.zooPlatform().isRoutable()); From 0b6a74a840c16b02b6bd8d85393084e572ce664d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 2 Jan 2025 23:07:39 +0100 Subject: [PATCH 219/269] Update documentation --- .../opentripplanner/apis/gtfs/schema.graphqls | 16 +++++++++++++++- 1 file changed, 15 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 6ac56be3d1d..f8a51325a69 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3535,15 +3535,29 @@ enum RealtimeState { UPDATED } -"Actions to take relative to the current position when engaging a walking/driving step." +""" +A direction that is not absolute but rather fuzzy and context-dependent. +It provides the passenger with information what they should do in this step depending on where they +were in the previous one. +""" enum RelativeDirection { CIRCLE_CLOCKWISE CIRCLE_COUNTERCLOCKWISE + """ + Moving straight ahead in one of these cases + + - Passing through a crossing or intersection. + - Passing through a station entrance or exit when it is not know whether the passenger is + entering or exiting. If known then entrance information is in the `step.entity` field. + """ CONTINUE DEPART ELEVATOR + "Entering a public transport station. If known then entrance information is in the `step.entity` field." ENTER_STATION + "Exiting a public transport station. If known then entrance information is in the `step.entity` field." EXIT_STATION + "Follow the signs indicating a specific location like \"platform 1\" or \"exit B\"." FOLLOW_SIGNS HARD_LEFT HARD_RIGHT From 9a3fec8dfce0264e0b572065a797848b6c1dcd5a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 3 Jan 2025 10:35:19 +0100 Subject: [PATCH 220/269] Update code to Lucene 10 --- .../opentripplanner/ext/geocoder/LuceneIndex.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/application/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/application/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java index f742ddb131a..50452899deb 100644 --- a/application/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java +++ b/application/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java @@ -18,7 +18,7 @@ import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.PostingsFormat; -import org.apache.lucene.codecs.lucene912.Lucene912Codec; +import org.apache.lucene.codecs.lucene101.Lucene101Codec; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.StoredField; @@ -34,7 +34,7 @@ import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.suggest.document.Completion912PostingsFormat; +import org.apache.lucene.search.suggest.document.Completion101PostingsFormat; import org.apache.lucene.search.suggest.document.CompletionAnalyzer; import org.apache.lucene.search.suggest.document.ContextQuery; import org.apache.lucene.search.suggest.document.ContextSuggestField; @@ -203,8 +203,8 @@ private StopCluster toStopCluster(Document document) { static IndexWriterConfig iwcWithSuggestField(Analyzer analyzer, final Set suggestFields) { IndexWriterConfig iwc = new IndexWriterConfig(analyzer); - Codec filterCodec = new Lucene912Codec() { - final PostingsFormat postingsFormat = new Completion912PostingsFormat(); + Codec filterCodec = new Lucene101Codec() { + final PostingsFormat postingsFormat = new Completion101PostingsFormat(); @Override public PostingsFormat getPostingsFormatForField(String field) { @@ -285,7 +285,7 @@ private Stream matchingDocuments( .stream(topDocs.scoreDocs) .map(scoreDoc -> { try { - return searcher.doc(scoreDoc.doc); + return searcher.storedFields().document(scoreDoc.doc); } catch (IOException e) { throw new RuntimeException(e); } @@ -330,7 +330,7 @@ private Stream matchingDocuments( .stream(topDocs.scoreDocs) .map(scoreDoc -> { try { - return searcher.doc(scoreDoc.doc); + return searcher.storedFields().document(scoreDoc.doc); } catch (IOException e) { throw new RuntimeException(e); } From 1518e38dd6f0ca9cc4656ce38dc9ccc009fdcef6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 3 Jan 2025 10:44:48 +0100 Subject: [PATCH 221/269] Apply review feedback --- pom.xml | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 98cf77db29e..9cf7b514ce7 100644 --- a/pom.xml +++ b/pom.xml @@ -59,21 +59,23 @@ 176 + 32.1 2.53 2.18.2 + 4.0.5 3.1.9 5.11.4 - 1.14.1 - 5.6.0 1.5.12 9.12.0 - 2.0.16 + 1.14.1 2.0.15 - 1.27 - 4.0.5 + 5.6.0 4.28.3 + 1.27 + 2.0.16 + UTF-8 opentripplanner/OpenTripPlanner @@ -390,7 +392,7 @@ - + com.google.cloud libraries-bom 26.51.0 @@ -398,6 +400,18 @@ import + + com.google.protobuf + protobuf-java + ${protobuf.version} + + + + com.google.guava + guava + 33.3.1-jre + + org.slf4j @@ -420,11 +434,6 @@ trove4j 3.0.3 - - com.google.guava - guava - 33.3.1-jre - @@ -486,11 +495,7 @@ java-snapshot-testing-junit5 2.3.0 - - com.google.protobuf - protobuf-java - ${protobuf.version} - + From 509094ce927a7d0296e9ed6f2866fa457a2696be Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Fri, 3 Jan 2025 12:50:36 +0000 Subject: [PATCH 222/269] Add changelog entry for #6284 [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 a9ca5c28ca3..3dc3f018f9a 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -68,6 +68,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Add default penalty to all car API modes [#6302](https://github.com/opentripplanner/OpenTripPlanner/pull/6302) - Make flex linking work together with boarding locations [#6311](https://github.com/opentripplanner/OpenTripPlanner/pull/6311) - Add fallback name for corridors [#6303](https://github.com/opentripplanner/OpenTripPlanner/pull/6303) +- Implement SIRI Lite [#6284](https://github.com/opentripplanner/OpenTripPlanner/pull/6284) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From 249b01b9e16dadf2fc41df1e576b312795324c0b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 3 Jan 2025 11:00:34 +0100 Subject: [PATCH 223/269] Update list of GTFS Fares v2 classes --- .../org/opentripplanner/gtfs/graphbuilder/GtfsModule.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 a5fe3641e3c..fc5e5e276d7 100644 --- a/application/src/main/java/org/opentripplanner/gtfs/graphbuilder/GtfsModule.java +++ b/application/src/main/java/org/opentripplanner/gtfs/graphbuilder/GtfsModule.java @@ -14,6 +14,7 @@ import org.onebusaway.csv_entities.EntityHandler; import org.onebusaway.gtfs.impl.GtfsRelationalDaoImpl; import org.onebusaway.gtfs.model.Agency; +import org.onebusaway.gtfs.model.Area; import org.onebusaway.gtfs.model.FareAttribute; import org.onebusaway.gtfs.model.FareLegRule; import org.onebusaway.gtfs.model.FareMedium; @@ -28,6 +29,7 @@ import org.onebusaway.gtfs.model.ShapePoint; import org.onebusaway.gtfs.model.Stop; import org.onebusaway.gtfs.model.StopArea; +import org.onebusaway.gtfs.model.StopAreaElement; import org.onebusaway.gtfs.model.Trip; import org.onebusaway.gtfs.serialization.GtfsReader; import org.onebusaway.gtfs.services.GenericMutableDao; @@ -66,7 +68,9 @@ public class GtfsModule implements GraphBuilderModule { FareTransferRule.class, RiderCategory.class, FareMedium.class, - StopArea.class + StopArea.class, + StopAreaElement.class, + Area.class ); private static final Logger LOG = LoggerFactory.getLogger(GtfsModule.class); From 21f15bde2642a5019eeac847e34a8756eb1ef76d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 3 Jan 2025 13:54:23 +0100 Subject: [PATCH 224/269] Update to latest OBA --- application/pom.xml | 2 +- .../java/org/opentripplanner/gtfs/graphbuilder/GtfsModule.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index c3b4a6ee582..231675a99de 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -296,7 +296,7 @@ org.onebusaway onebusaway-gtfs - 4.3.0 + 5.0.0 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 fc5e5e276d7..3548312b79a 100644 --- a/application/src/main/java/org/opentripplanner/gtfs/graphbuilder/GtfsModule.java +++ b/application/src/main/java/org/opentripplanner/gtfs/graphbuilder/GtfsModule.java @@ -28,7 +28,6 @@ import org.onebusaway.gtfs.model.ServiceCalendarDate; import org.onebusaway.gtfs.model.ShapePoint; import org.onebusaway.gtfs.model.Stop; -import org.onebusaway.gtfs.model.StopArea; import org.onebusaway.gtfs.model.StopAreaElement; import org.onebusaway.gtfs.model.Trip; import org.onebusaway.gtfs.serialization.GtfsReader; @@ -68,7 +67,6 @@ public class GtfsModule implements GraphBuilderModule { FareTransferRule.class, RiderCategory.class, FareMedium.class, - StopArea.class, StopAreaElement.class, Area.class ); From 5b12c7f5bfdd4264d3b81322924b5a8f2e68e138 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 Jan 2025 09:33:22 +0100 Subject: [PATCH 225/269] Update docs --- .../opentripplanner/apis/gtfs/schema.graphqls | 18 +++++++++++++++--- 1 file changed, 15 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 f8a51325a69..afae31e19f0 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3548,14 +3548,26 @@ enum RelativeDirection { - Passing through a crossing or intersection. - Passing through a station entrance or exit when it is not know whether the passenger is - entering or exiting. If known then entrance information is in the `step.entity` field. + entering or exiting. If it _is_ known then `ENTER_STATION`/`EXIT_STATION` is used. + + If available, entrance information is in the `step.feature` field. """ CONTINUE DEPART ELEVATOR - "Entering a public transport station. If known then entrance information is in the `step.entity` field." + """ + Entering a public transport station. If it's not known if the passenger is entering or exiting + then `CONTINUE` is used. + + If available, entrance information is in the `step.feature` field. + """ ENTER_STATION - "Exiting a public transport station. If known then entrance information is in the `step.entity` field." + """ + Exiting a public transport station. If it's not known if the passenger is entering or exiting + then `CONTINUE` is used. + + If available then entrance information is in the `step.feature` field. + """ EXIT_STATION "Follow the signs indicating a specific location like \"platform 1\" or \"exit B\"." FOLLOW_SIGNS From 8c6fda4e0df377deadd25e6649090e518290bac9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 Jan 2025 09:46:37 +0100 Subject: [PATCH 226/269] Extract entrance from transit link --- .../model/plan/WalkStepBuilder.java | 2 +- .../mapping/StatesToWalkStepsMapper.java | 16 +++++++++++++--- .../model/edge/StreetTransitEntranceLink.java | 13 +++++++++++++ .../opentripplanner/apis/gtfs/schema.graphqls | 6 +++--- 4 files changed, 30 insertions(+), 7 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 877b40f24fd..75589718861 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/plan/WalkStepBuilder.java @@ -76,7 +76,7 @@ public WalkStepBuilder withExit(String exit) { return this; } - public WalkStepBuilder withEntrance(Entrance entrance) { + public WalkStepBuilder withEntrance(@Nullable Entrance entrance) { this.entrance = entrance; 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 ae7674c7a96..8ec6ac07e34 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 @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.framework.geometry.DirectionUtils; @@ -160,7 +161,7 @@ private void processState(State backState, State forwardState) { return; } else if (edge instanceof StreetTransitEntranceLink link) { var direction = relativeDirectionForTransitLink(link); - createAndSaveStep(backState, forwardState, link.getName(), direction, edge); + createAndSaveStep(backState, forwardState, link.getName(), direction, edge, link.entrance()); return; } @@ -181,7 +182,14 @@ private void processState(State backState, State forwardState) { addStep(createStationEntranceWalkStep(backState, forwardState, stationEntranceVertex)); return; } else if (edge instanceof PathwayEdge pwe && pwe.signpostedAs().isPresent()) { - createAndSaveStep(backState, forwardState, pwe.signpostedAs().get(), FOLLOW_SIGNS, edge); + createAndSaveStep( + backState, + forwardState, + pwe.signpostedAs().get(), + FOLLOW_SIGNS, + edge, + null + ); return; } @@ -545,7 +553,8 @@ private void createAndSaveStep( State forwardState, I18NString name, RelativeDirection direction, - Edge edge + Edge edge, + @Nullable Entrance entrance ) { addStep( createWalkStep(forwardState, backState) @@ -553,6 +562,7 @@ private void createAndSaveStep( .withNameIsDerived(false) .withDirections(lastAngle, DirectionUtils.getFirstAngle(edge.getGeometry()), false) .withRelativeDirection(direction) + .withEntrance(entrance) .addDistance(edge.getDistanceMeters()) ); diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntranceLink.java b/application/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntranceLink.java index 7145f6183e4..34ca3faeeb3 100644 --- a/application/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntranceLink.java +++ b/application/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntranceLink.java @@ -2,6 +2,7 @@ import org.opentripplanner.street.model.vertex.StreetVertex; import org.opentripplanner.street.model.vertex.TransitEntranceVertex; +import org.opentripplanner.transit.model.site.Entrance; /** * This represents the connection between a street vertex and a transit vertex belonging the street @@ -43,6 +44,18 @@ public boolean isExit() { return !isEntrance; } + /** + * Get the {@link Entrance} that this edge links to. + */ + public Entrance entrance() { + if (getToVertex() instanceof TransitEntranceVertex tev) { + return tev.getEntrance(); + } else if (getFromVertex() instanceof TransitEntranceVertex tev) { + return tev.getEntrance(); + } + throw new IllegalStateException("%s doesn't link to an entrance.".formatted(this)); + } + protected int getStreetToStopTime() { return 0; } 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 afae31e19f0..8582fc7ba6f 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3549,7 +3549,7 @@ enum RelativeDirection { - Passing through a crossing or intersection. - Passing through a station entrance or exit when it is not know whether the passenger is entering or exiting. If it _is_ known then `ENTER_STATION`/`EXIT_STATION` is used. - + If available, entrance information is in the `step.feature` field. """ CONTINUE @@ -3558,14 +3558,14 @@ enum RelativeDirection { """ Entering a public transport station. If it's not known if the passenger is entering or exiting then `CONTINUE` is used. - + If available, entrance information is in the `step.feature` field. """ ENTER_STATION """ Exiting a public transport station. If it's not known if the passenger is entering or exiting then `CONTINUE` is used. - + If available then entrance information is in the `step.feature` field. """ EXIT_STATION From bd94b1390dc720379d7394feb8824e8225e1c2b4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 Jan 2025 09:49:35 +0100 Subject: [PATCH 227/269] Add test for extracting entrance from pathway data --- .../algorithm/mapping/StatesToWalkStepsMapperTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 de9fe21718a..a2bb428a78c 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 @@ -13,6 +13,7 @@ import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.routing.services.notes.StreetNotesService; import org.opentripplanner.street.search.state.TestStateBuilder; +import org.opentripplanner.transit.model.framework.FeedScopedId; class StatesToWalkStepsMapperTest { @@ -42,6 +43,7 @@ void enterStation() { var walkSteps = buildWalkSteps(builder); assertEquals(2, walkSteps.size()); var enter = walkSteps.get(1); + assertEquals(new FeedScopedId("F", "Lichterfelde-Ost"), enter.entrance().get().getId()); assertEquals(ENTER_STATION, enter.getRelativeDirection()); } @@ -53,8 +55,9 @@ void exitStation() { .exitStation("Lichterfelde-Ost"); var walkSteps = buildWalkSteps(builder); assertEquals(3, walkSteps.size()); - var enter = walkSteps.get(2); - assertEquals(EXIT_STATION, enter.getRelativeDirection()); + var exit = walkSteps.get(2); + assertEquals(new FeedScopedId("F", "Lichterfelde-Ost"), exit.entrance().get().getId()); + assertEquals(EXIT_STATION, exit.getRelativeDirection()); } @Test From 74106b5f2fd16c6fe8024089553bb58713df2faf Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 Jan 2025 10:00:57 +0100 Subject: [PATCH 228/269] Update schema docs --- .../org/opentripplanner/apis/gtfs/schema.graphqls | 7 +++---- 1 file changed, 3 insertions(+), 4 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 8582fc7ba6f..c9d0f3d73af 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3549,8 +3549,7 @@ enum RelativeDirection { - Passing through a crossing or intersection. - Passing through a station entrance or exit when it is not know whether the passenger is entering or exiting. If it _is_ known then `ENTER_STATION`/`EXIT_STATION` is used. - - If available, entrance information is in the `step.feature` field. + More information about the entrance is in the `step.feature` field. """ CONTINUE DEPART @@ -3559,14 +3558,14 @@ enum RelativeDirection { Entering a public transport station. If it's not known if the passenger is entering or exiting then `CONTINUE` is used. - If available, entrance information is in the `step.feature` field. + More information about the entrance is in the `step.feature` field. """ ENTER_STATION """ Exiting a public transport station. If it's not known if the passenger is entering or exiting then `CONTINUE` is used. - If available then entrance information is in the `step.feature` field. + More information about the entrance is in the `step.feature` field. """ EXIT_STATION "Follow the signs indicating a specific location like \"platform 1\" or \"exit B\"." From 5d4911b45a55a034666b7edc5c305f2c133bc5b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 10:20:03 +0000 Subject: [PATCH 229/269] fix(deps): update micrometer to v1.14.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f54278105ef..fbc65c1e53a 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 2.18.2 3.1.9 5.11.4 - 1.14.1 + 1.14.2 5.6.0 1.5.12 9.12.0 From 51080a8510956dcd40cd578a4ac1320fb1de01ce Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:05:31 +0000 Subject: [PATCH 230/269] fix(deps): update dependency com.google.guava:guava to v33.4.0-jre --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fbc65c1e53a..58af808d0e1 100644 --- a/pom.xml +++ b/pom.xml @@ -422,7 +422,7 @@ com.google.guava guava - 33.3.1-jre + 33.4.0-jre From 72019c0fd0086ee8401a2fcc177313f0f1c0c5cd Mon Sep 17 00:00:00 2001 From: Eivind Morris Bakke Date: Tue, 7 Jan 2025 14:59:51 +0100 Subject: [PATCH 231/269] Add a matcher API for filters in the transit service used for regularStop lookup (#6234) * Implements a RegularStop matcher that can be used for filtering RegularStops. Also moves the envelope filtering that used to happen on the client side of the findRegularStops call into the StopModelIndex. * Updates the RegularStopRequest to latest concepts. * Adds @Nullable to feedId method on the RegularStopRequest. * Changes feedId to be called agency. * Update application/src/test/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactoryTest.java Co-authored-by: Leonard Ehrenfried * Fixes test failure. * Addresses comments in code review. * Update application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequest.java Co-authored-by: Thomas Gran * Update application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequestBuilder.java Co-authored-by: Thomas Gran * Update application/src/main/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactory.java Co-authored-by: Thomas Gran * Update application/src/main/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactory.java Co-authored-by: Thomas Gran * Update application/src/main/java/org/opentripplanner/transit/service/TransitService.java Co-authored-by: Thomas Gran * Update application/src/main/java/org/opentripplanner/transit/service/TransitService.java Co-authored-by: Thomas Gran * Addresses comments in code review. --------- Co-authored-by: Leonard Ehrenfried Co-authored-by: Thomas Gran --- .../parkAndRideApi/ParkAndRideResource.java | 2 +- .../ext/restapi/resources/IndexAPI.java | 5 +- .../layers/stops/StopsLayerBuilder.java | 2 +- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 5 +- .../transmodel/TransmodelGraphQLSchema.java | 53 ++++++++++--------- .../transmodel/model/stop/StopPlaceType.java | 3 +- .../GraphInspectorVectorTileResource.java | 2 +- .../StraightLineNearbyStopFinder.java | 2 +- .../api/OtpServerRequestContext.java | 2 +- .../transit/api/model/FilterValues.java | 2 +- .../FindRegularStopsByBoundingBoxRequest.java | 48 +++++++++++++++++ ...gularStopsByBoundingBoxRequestBuilder.java | 32 +++++++++++ .../api/request/TripOnServiceDateRequest.java | 4 +- .../TripOnServiceDateRequestBuilder.java | 4 +- .../transit/api/request/TripRequest.java | 2 +- .../api/request/TripRequestBuilder.java | 2 +- .../model/filter/expr/ExpressionBuilder.java | 5 ++ .../filter/expr/GenericUnaryMatcher.java | 33 ++++++++++++ .../transit/RegularStopMatcherFactory.java | 47 ++++++++++++++++ .../service/DefaultTransitService.java | 20 ++++++- .../transit/service/StopModelIndex.java | 11 +++- .../transit/service/TransitService.java | 21 +++++--- .../apis/transmodel/schema.graphql | 2 +- .../LegacyRouteRequestMapperTest.java | 2 +- .../routerequest/RouteRequestMapperTest.java | 2 +- .../graph/DefaultRoutingServiceTest.java | 2 +- .../filter/expr/GenericUnaryMatcherTest.java | 15 ++++++ .../RegularStopMatcherFactoryTest.java | 44 +++++++++++++++ 28 files changed, 313 insertions(+), 61 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequest.java create mode 100644 application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequestBuilder.java create mode 100644 application/src/main/java/org/opentripplanner/transit/model/filter/expr/GenericUnaryMatcher.java create mode 100644 application/src/main/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactory.java create mode 100644 application/src/test/java/org/opentripplanner/transit/model/filter/expr/GenericUnaryMatcherTest.java create mode 100644 application/src/test/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactoryTest.java diff --git a/application/src/ext/java/org/opentripplanner/ext/parkAndRideApi/ParkAndRideResource.java b/application/src/ext/java/org/opentripplanner/ext/parkAndRideApi/ParkAndRideResource.java index 747ba0617ec..611f4d46420 100644 --- a/application/src/ext/java/org/opentripplanner/ext/parkAndRideApi/ParkAndRideResource.java +++ b/application/src/ext/java/org/opentripplanner/ext/parkAndRideApi/ParkAndRideResource.java @@ -42,7 +42,7 @@ public ParkAndRideResource( // - serverContext.graphFinder(). This needs at least a comment! // - This can be replaced with a search done with the SiteRepository // - if we have a radius search there. - this.graphFinder = new DirectGraphFinder(serverContext.transitService()::findRegularStops); + this.graphFinder = new DirectGraphFinder(serverContext.transitService()::findRegularStopsByBoundingBox); } /** Envelopes are in latitude, longitude format */ diff --git a/application/src/ext/java/org/opentripplanner/ext/restapi/resources/IndexAPI.java b/application/src/ext/java/org/opentripplanner/ext/restapi/resources/IndexAPI.java index 7dbfabed156..b9c049f547e 100644 --- a/application/src/ext/java/org/opentripplanner/ext/restapi/resources/IndexAPI.java +++ b/application/src/ext/java/org/opentripplanner/ext/restapi/resources/IndexAPI.java @@ -200,7 +200,7 @@ public List getStopsInRadius( radius = Math.min(radius, MAX_STOP_SEARCH_RADIUS); - return new DirectGraphFinder(serverContext.transitService()::findRegularStops) + return new DirectGraphFinder(serverContext.transitService()::findRegularStopsByBoundingBox) .findClosestStops(new Coordinate(lon, lat), radius) .stream() .map(it -> StopMapper.mapToApiShort(it.stop, it.distance)) @@ -221,10 +221,9 @@ public List getStopsInRadius( new Coordinate(maxLon, maxLat) ); - var stops = transitService().findRegularStops(envelope); + var stops = transitService().findRegularStopsByBoundingBox(envelope); return stops .stream() - .filter(stop -> envelope.contains(stop.getCoordinate().asJtsCoordinate())) .map(StopMapper::mapToApiShort) .toList(); } diff --git a/application/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerBuilder.java b/application/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerBuilder.java index 141157f8f3e..c1e03cdefca 100644 --- a/application/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerBuilder.java +++ b/application/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerBuilder.java @@ -44,7 +44,7 @@ public StopsLayerBuilder( protected List getGeometries(Envelope query) { return transitService - .findRegularStops(query) + .findRegularStopsByBoundingBox(query) .stream() .filter(filter) .map(stop -> { 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 c56540a73f9..fa38deeab84 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 @@ -752,9 +752,8 @@ public DataFetcher> stopsByBbox() { ); Stream stopStream = getTransitService(environment) - .findRegularStops(envelope) - .stream() - .filter(stop -> envelope.contains(stop.getCoordinate().asJtsCoordinate())); + .findRegularStopsByBoundingBox(envelope) + .stream(); if (args.getGraphQLFeeds() != null) { List feedIds = args.getGraphQLFeeds(); diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java b/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java index 922f9f5244b..0561ec9de85 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java @@ -3,6 +3,7 @@ import static java.lang.Boolean.TRUE; import static java.util.Collections.emptyList; import static org.opentripplanner.apis.transmodel.mapping.SeverityMapper.getTransmodelSeverity; +import static org.opentripplanner.apis.transmodel.mapping.TransitIdMapper.mapIDToDomain; import static org.opentripplanner.apis.transmodel.mapping.TransitIdMapper.mapIDsToDomainNullSafe; import static org.opentripplanner.apis.transmodel.model.EnumTypes.FILTER_PLACE_TYPE_ENUM; import static org.opentripplanner.apis.transmodel.model.EnumTypes.MULTI_MODAL_MODE; @@ -115,6 +116,7 @@ import org.opentripplanner.routing.graphfinder.PlaceType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.transit.api.model.FilterValues; +import org.opentripplanner.transit.api.request.FindRegularStopsByBoundingBoxRequest; import org.opentripplanner.transit.api.request.TripRequest; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -439,10 +441,7 @@ private GraphQLSchema create() { .build() ) .dataFetcher(env -> - StopPlaceType.fetchStopPlaceById( - TransitIdMapper.mapIDToDomain(env.getArgument("id")), - env - ) + StopPlaceType.fetchStopPlaceById(mapIDToDomain(env.getArgument("id")), env) ) .build() ) @@ -576,7 +575,7 @@ private GraphQLSchema create() { .dataFetcher(environment -> GqlUtil .getTransitService(environment) - .getStopLocation(TransitIdMapper.mapIDToDomain(environment.getArgument("id"))) + .getStopLocation(mapIDToDomain(environment.getArgument("id"))) ) .build() ) @@ -610,7 +609,7 @@ private GraphQLSchema create() { } TransitService transitService = GqlUtil.getTransitService(environment); return ((List) environment.getArgument("ids")).stream() - .map(id -> transitService.getStopLocation(TransitIdMapper.mapIDToDomain(id))) + .map(id -> transitService.getStopLocation(mapIDToDomain(id))) .collect(Collectors.toList()); } if (environment.getArgument("name") == null) { @@ -661,7 +660,14 @@ private GraphQLSchema create() { .build() ) .argument( - GraphQLArgument.newArgument().name("authority").type(Scalars.GraphQLString).build() + GraphQLArgument + .newArgument() + .name("authority") + .deprecate( + "This is the Transmodel namespace or the GTFS feedID - avoid using this. Request a new field if necessary." + ) + .type(Scalars.GraphQLString) + .build() ) .argument( GraphQLArgument @@ -669,7 +675,7 @@ private GraphQLSchema create() { .name("filterByInUse") .description("If true only quays with at least one visiting line are included.") .type(Scalars.GraphQLBoolean) - .defaultValue(Boolean.FALSE) + .defaultValueProgrammatic(Boolean.FALSE) .build() ) .dataFetcher(environment -> { @@ -683,24 +689,19 @@ private GraphQLSchema create() { environment.getArgument("maximumLatitude") ) ); + + var authority = environment.getArgument("authority"); + var filterInUse = environment.getArgument("filterByInUse"); + + FindRegularStopsByBoundingBoxRequest findRegularStopsByBoundingBoxRequest = FindRegularStopsByBoundingBoxRequest + .of(envelope) + .withFeedId(authority) + .filterByInUse(filterInUse) + .build(); + return GqlUtil .getTransitService(environment) - .findRegularStops(envelope) - .stream() - .filter(stop -> envelope.contains(stop.getCoordinate().asJtsCoordinate())) - .filter(stop -> - environment.getArgument("authority") == null || - stop.getId().getFeedId().equalsIgnoreCase(environment.getArgument("authority")) - ) - .filter(stop -> { - boolean filterByInUse = TRUE.equals(environment.getArgument("filterByInUse")); - boolean inUse = !GqlUtil - .getTransitService(environment) - .findPatterns(stop, true) - .isEmpty(); - return !filterByInUse || inUse; - }) - .collect(Collectors.toList()); + .findRegularStopsByBoundingBox(findRegularStopsByBoundingBoxRequest); }) .build() ) @@ -1438,7 +1439,7 @@ private GraphQLSchema create() { .build() ) .dataFetcher(environment -> { - var bikeParkId = TransitIdMapper.mapIDToDomain(environment.getArgument("id")); + var bikeParkId = mapIDToDomain(environment.getArgument("id")); return GqlUtil .getVehicleParkingService(environment) .listBikeParks() @@ -1573,7 +1574,7 @@ private GraphQLSchema create() { return GqlUtil .getTransitService(environment) .getTransitAlertService() - .getAlertById(TransitIdMapper.mapIDToDomain(situationNumber)); + .getAlertById(mapIDToDomain(situationNumber)); }) .build() ) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopPlaceType.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopPlaceType.java index fa755e8c6f7..9b8fd25c68a 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopPlaceType.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopPlaceType.java @@ -546,9 +546,8 @@ public static Collection fetchStopPlaces( ); Stream stations = transitService - .findRegularStops(envelope) + .findRegularStopsByBoundingBox(envelope) .stream() - .filter(stop -> envelope.contains(stop.getCoordinate().asJtsCoordinate())) .map(StopLocation::getParentStation) .filter(Objects::nonNull) .distinct(); 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 0576e91f312..0336a57c5de 100644 --- a/application/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/application/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -183,7 +183,7 @@ private static LayerBuilder createLayerBuilder( case RegularStop -> new StopLayerBuilder<>( layerParameters, locale, - e -> context.transitService().findRegularStops(e) + e -> context.transitService().findRegularStopsByBoundingBox(e) ); case AreaStop -> new StopLayerBuilder<>( layerParameters, diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StraightLineNearbyStopFinder.java b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StraightLineNearbyStopFinder.java index 5b304d0d20c..ccdcb68446b 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StraightLineNearbyStopFinder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StraightLineNearbyStopFinder.java @@ -22,7 +22,7 @@ public StraightLineNearbyStopFinder(TransitService transitService, Duration dura // We need to accommodate straight line distance (in meters) but when streets are present we // use an earliest arrival search, which optimizes on time. Ideally we'd specify in meters, // but we don't have much of a choice here. Use the default walking speed to convert. - this.directGraphFinder = new DirectGraphFinder(transitService::findRegularStops); + this.directGraphFinder = new DirectGraphFinder(transitService::findRegularStopsByBoundingBox); } /** 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 f088a3de60e..139bac6dcc9 100644 --- a/application/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java +++ b/application/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java @@ -121,7 +121,7 @@ public interface OtpServerRequestContext { TraverseVisitor traverseVisitor(); default GraphFinder graphFinder() { - return GraphFinder.getInstance(graph(), transitService()::findRegularStops); + return GraphFinder.getInstance(graph(), transitService()::findRegularStopsByBoundingBox); } FlexParameters flexParameters(); diff --git a/application/src/main/java/org/opentripplanner/transit/api/model/FilterValues.java b/application/src/main/java/org/opentripplanner/transit/api/model/FilterValues.java index 391b2531231..63befe3add6 100644 --- a/application/src/main/java/org/opentripplanner/transit/api/model/FilterValues.java +++ b/application/src/main/java/org/opentripplanner/transit/api/model/FilterValues.java @@ -1,8 +1,8 @@ package org.opentripplanner.transit.api.model; -import com.beust.jcommander.internal.Nullable; import java.util.Collection; import java.util.NoSuchElementException; +import javax.annotation.Nullable; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.TransitService; diff --git a/application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequest.java b/application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequest.java new file mode 100644 index 00000000000..476e23d7cd8 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequest.java @@ -0,0 +1,48 @@ +package org.opentripplanner.transit.api.request; + +import javax.annotation.Nullable; +import org.locationtech.jts.geom.Envelope; +import org.opentripplanner.transit.model.site.RegularStop; + +/** + * A request for {@link RegularStop}s within a bounding box. + *

+ * This request is used to retrieve {@link RegularStop}s that are within a provided bounding box and + * match the other criteria. + */ +public class FindRegularStopsByBoundingBoxRequest { + + private final Envelope envelope; + + @Nullable + private final String feedId; + + private final boolean filterByInUse; + + FindRegularStopsByBoundingBoxRequest( + Envelope envelope, + @Nullable String feedId, + boolean filterByInUse + ) { + this.envelope = envelope; + this.feedId = feedId; + this.filterByInUse = filterByInUse; + } + + public static FindRegularStopsByBoundingBoxRequestBuilder of(Envelope envelope) { + return new FindRegularStopsByBoundingBoxRequestBuilder(envelope); + } + + public Envelope envelope() { + return envelope; + } + + @Nullable + public String feedId() { + return feedId; + } + + public boolean filterByInUse() { + return filterByInUse; + } +} diff --git a/application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequestBuilder.java b/application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequestBuilder.java new file mode 100644 index 00000000000..49d31c33db9 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/transit/api/request/FindRegularStopsByBoundingBoxRequestBuilder.java @@ -0,0 +1,32 @@ +package org.opentripplanner.transit.api.request; + +import javax.annotation.Nullable; +import org.locationtech.jts.geom.Envelope; + +public class FindRegularStopsByBoundingBoxRequestBuilder { + + private final Envelope envelope; + + @Nullable + private String feedId; + + private boolean filterByInUse = false; + + FindRegularStopsByBoundingBoxRequestBuilder(Envelope envelope) { + this.envelope = envelope; + } + + public FindRegularStopsByBoundingBoxRequestBuilder withFeedId(@Nullable String feedId) { + this.feedId = feedId; + return this; + } + + public FindRegularStopsByBoundingBoxRequestBuilder filterByInUse(boolean filterByInUse) { + this.filterByInUse = filterByInUse; + return this; + } + + public FindRegularStopsByBoundingBoxRequest build() { + return new FindRegularStopsByBoundingBoxRequest(envelope, feedId, filterByInUse); + } +} diff --git a/application/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequest.java b/application/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequest.java index c61bb8ad107..54dd1c7aea6 100644 --- a/application/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequest.java +++ b/application/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequest.java @@ -23,7 +23,7 @@ public class TripOnServiceDateRequest { private final FilterValues netexInternalPlanningCodes; private final FilterValues alterations; - protected TripOnServiceDateRequest( + TripOnServiceDateRequest( RequiredFilterValues serviceDates, FilterValues agencies, FilterValues routes, @@ -41,7 +41,7 @@ protected TripOnServiceDateRequest( this.alterations = alterations; } - public static TripOnServiceDateRequestBuilder of(RequiredFilterValues serviceDates) { + public static TripOnServiceDateRequestBuilder of(RequiredFilterValues serviceDates) { return new TripOnServiceDateRequestBuilder(serviceDates); } diff --git a/application/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequestBuilder.java b/application/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequestBuilder.java index 534557c15d8..3181819a400 100644 --- a/application/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequestBuilder.java +++ b/application/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequestBuilder.java @@ -30,9 +30,9 @@ public class TripOnServiceDateRequestBuilder { "alterations", List.of() ); - private RequiredFilterValues serviceDates; + private final RequiredFilterValues serviceDates; - protected TripOnServiceDateRequestBuilder(RequiredFilterValues serviceDates) { + TripOnServiceDateRequestBuilder(RequiredFilterValues serviceDates) { this.serviceDates = serviceDates; } diff --git a/application/src/main/java/org/opentripplanner/transit/api/request/TripRequest.java b/application/src/main/java/org/opentripplanner/transit/api/request/TripRequest.java index c73e800582b..5e05b472937 100644 --- a/application/src/main/java/org/opentripplanner/transit/api/request/TripRequest.java +++ b/application/src/main/java/org/opentripplanner/transit/api/request/TripRequest.java @@ -17,7 +17,7 @@ public class TripRequest { private final FilterValues netexInternalPlanningCodes; private final FilterValues serviceDates; - protected TripRequest( + TripRequest( FilterValues agencies, FilterValues routes, FilterValues netexInternalPlanningCodes, diff --git a/application/src/main/java/org/opentripplanner/transit/api/request/TripRequestBuilder.java b/application/src/main/java/org/opentripplanner/transit/api/request/TripRequestBuilder.java index 3a2f80a3e34..32ca31d5cd6 100644 --- a/application/src/main/java/org/opentripplanner/transit/api/request/TripRequestBuilder.java +++ b/application/src/main/java/org/opentripplanner/transit/api/request/TripRequestBuilder.java @@ -21,7 +21,7 @@ public class TripRequestBuilder { List.of() ); - protected TripRequestBuilder() {} + TripRequestBuilder() {} public TripRequestBuilder withAgencies(FilterValues agencies) { this.agencies = agencies; diff --git a/application/src/main/java/org/opentripplanner/transit/model/filter/expr/ExpressionBuilder.java b/application/src/main/java/org/opentripplanner/transit/model/filter/expr/ExpressionBuilder.java index f2910a4c8d2..87533ab1b5e 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/filter/expr/ExpressionBuilder.java +++ b/application/src/main/java/org/opentripplanner/transit/model/filter/expr/ExpressionBuilder.java @@ -22,6 +22,11 @@ public static ExpressionBuilder of() { return new ExpressionBuilder<>(); } + public ExpressionBuilder matches(Matcher matcher) { + matchers.add(matcher); + return this; + } + public ExpressionBuilder atLeastOneMatch( FilterValues filterValues, Function> matcherProvider diff --git a/application/src/main/java/org/opentripplanner/transit/model/filter/expr/GenericUnaryMatcher.java b/application/src/main/java/org/opentripplanner/transit/model/filter/expr/GenericUnaryMatcher.java new file mode 100644 index 00000000000..0ac6c5f17ee --- /dev/null +++ b/application/src/main/java/org/opentripplanner/transit/model/filter/expr/GenericUnaryMatcher.java @@ -0,0 +1,33 @@ +package org.opentripplanner.transit.model.filter.expr; + +import java.util.function.Predicate; + +/** + * A generic matcher that takes a predicate function that returns a boolean given the matched type. + *

+ * @param The type of the entity being matched. + */ +public class GenericUnaryMatcher implements Matcher { + + private final String typeName; + private final Predicate matchPredicate; + + /** + * @param typeName The typeName appears in the toString for easier debugging. + * @param matchPredicate The predicate that will be used to test the entity being matched. + */ + public GenericUnaryMatcher(String typeName, Predicate matchPredicate) { + this.typeName = typeName; + this.matchPredicate = matchPredicate; + } + + @Override + public boolean match(T entity) { + return matchPredicate.test(entity); + } + + @Override + public String toString() { + return "GenericUnaryMatcher: " + typeName; + } +} diff --git a/application/src/main/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactory.java b/application/src/main/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactory.java new file mode 100644 index 00000000000..261e7057d95 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactory.java @@ -0,0 +1,47 @@ +package org.opentripplanner.transit.model.filter.transit; + +import java.util.function.Predicate; +import org.opentripplanner.transit.api.request.FindRegularStopsByBoundingBoxRequest; +import org.opentripplanner.transit.model.filter.expr.EqualityMatcher; +import org.opentripplanner.transit.model.filter.expr.ExpressionBuilder; +import org.opentripplanner.transit.model.filter.expr.GenericUnaryMatcher; +import org.opentripplanner.transit.model.filter.expr.Matcher; +import org.opentripplanner.transit.model.site.RegularStop; + +/** + * A factory for creating matchers for {@link RegularStop} objects. + *

+ * This factory is used to create matchers for {@link RegularStop} objects based on a request. The + * resulting matcher can be used to filter a list of {@link RegularStop} objects. + */ +public class RegularStopMatcherFactory { + + /** + * Creates a matcher that filters {@link RegularStop} objects with the provided {@code request} + * and {@code inUseProvider}. The {@code inUseProvider} is used to determine if a {@link RegularStop} is + * in use. The inUseProvider is an injected function, because the check is done by the transit service + * which has access to all stops and routes. A stop is used if it has routes visiting the stop. + */ + public static Matcher of( + FindRegularStopsByBoundingBoxRequest request, + Predicate inUseProvider + ) { + ExpressionBuilder expr = ExpressionBuilder.of(); + + if (request.feedId() != null) { + expr.matches(feedId(request.feedId())); + } + if (request.filterByInUse()) { + expr.matches(inUseMatcher(inUseProvider)); + } + return expr.build(); + } + + static Matcher feedId(String feedId) { + return new EqualityMatcher<>("feedId", feedId, stop -> stop.getId().getFeedId()); + } + + static Matcher inUseMatcher(Predicate inUseProvider) { + return new GenericUnaryMatcher<>("inUse", inUseProvider); + } +} 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 cf5bb11c3b3..d27fe138ef4 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -35,11 +35,13 @@ import org.opentripplanner.routing.services.TransitAlertService; import org.opentripplanner.routing.stoptimes.ArrivalDeparture; import org.opentripplanner.routing.stoptimes.StopTimesHelper; +import org.opentripplanner.transit.api.request.FindRegularStopsByBoundingBoxRequest; import org.opentripplanner.transit.api.request.TripOnServiceDateRequest; import org.opentripplanner.transit.api.request.TripRequest; import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.filter.expr.Matcher; +import org.opentripplanner.transit.model.filter.transit.RegularStopMatcherFactory; import org.opentripplanner.transit.model.filter.transit.TripMatcherFactory; import org.opentripplanner.transit.model.filter.transit.TripOnServiceDateMatcherFactory; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; @@ -698,11 +700,27 @@ public ZonedDateTime getTransitServiceStarts() { } @Override - public Collection findRegularStops(Envelope envelope) { + public Collection findRegularStopsByBoundingBox(Envelope envelope) { OTPRequestTimeoutException.checkForTimeout(); return timetableRepository.getSiteRepository().findRegularStops(envelope); } + @Override + public Collection findRegularStopsByBoundingBox( + FindRegularStopsByBoundingBoxRequest request + ) { + OTPRequestTimeoutException.checkForTimeout(); + Collection stops = timetableRepository + .getSiteRepository() + .findRegularStops(request.envelope()); + + Matcher matcher = RegularStopMatcherFactory.of( + request, + stop -> !findPatterns(stop, true).isEmpty() + ); + return stops.stream().filter(matcher::match).toList(); + } + @Override public Collection findAreaStops(Envelope envelope) { OTPRequestTimeoutException.checkForTimeout(); diff --git a/application/src/main/java/org/opentripplanner/transit/service/StopModelIndex.java b/application/src/main/java/org/opentripplanner/transit/service/StopModelIndex.java index 13a8c0d278d..effa8f95f7e 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/StopModelIndex.java +++ b/application/src/main/java/org/opentripplanner/transit/service/StopModelIndex.java @@ -71,10 +71,17 @@ class SiteRepositoryIndex { } /** - * Find a regular stop in the spatial index + * Find a regular stop in the spatial index, where the stop is inside of the passed Envelope. + * + * @param envelope - The {@link Envelope} to search for stops in. + * @return A collection of {@link RegularStop}s that are inside of the passed envelope. */ Collection findRegularStops(Envelope envelope) { - return regularStopSpatialIndex.query(envelope); + return regularStopSpatialIndex + .query(envelope) + .stream() + .filter(stop -> envelope.contains(stop.getCoordinate().asJtsCoordinate())) + .toList(); } MultiModalStation getMultiModalStationForStation(Station station) { 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 11a628508d6..6e005b355d1 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -24,6 +24,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.services.TransitAlertService; import org.opentripplanner.routing.stoptimes.ArrivalDeparture; +import org.opentripplanner.transit.api.request.FindRegularStopsByBoundingBoxRequest; import org.opentripplanner.transit.api.request.TripOnServiceDateRequest; import org.opentripplanner.transit.api.request.TripRequest; import org.opentripplanner.transit.model.basic.Notice; @@ -271,7 +272,7 @@ List findTripTimeOnDate( boolean transitFeedCovers(Instant dateTime); - Collection findRegularStops(Envelope envelope); + Collection findRegularStopsByBoundingBox(Envelope envelope); Collection findAreaStops(Envelope envelope); @@ -287,6 +288,7 @@ List findTripTimeOnDate( * So, if more patterns of mode BUS than RAIL visit the group, the result will be [BUS,RAIL]. */ List findTransitModes(StopLocationsGroup station); + /** * For a {@link StopLocation} return its modes. *

@@ -307,18 +309,13 @@ List findTripTimeOnDate( Map getServiceCodesRunningForDate(); /** - * Returns a list of TripOnServiceDates that match the filtering defined in the request. - * - * @param request - A TripOnServiceDateRequest object with filtering defined. - * @return - A list of TripOnServiceDates + * Returns a list of {@link TripOnServiceDate}s that match the filtering defined in the request. */ List findTripsOnServiceDate(TripOnServiceDateRequest request); /** - * Returns a list of Trips that match the filtering defined in the request. + * Returns a list of {@link Trip}s that match the filtering defined in the request. * - * @param request - A TripRequest object with filtering defined. - * @return - A list of Trips */ List getTrips(TripRequest request); @@ -329,4 +326,12 @@ List findTripTimeOnDate( * @return true if the trip exists, false otherwise */ boolean containsTrip(FeedScopedId id); + + /** + * Returns a list of {@link RegularStop}s that lay within a bounding box and match the other criteria + * in the request object. + */ + Collection findRegularStopsByBoundingBox( + FindRegularStopsByBoundingBoxRequest request + ); } 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 b76f28649a4..6834d375bf1 100644 --- a/application/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql +++ b/application/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql @@ -702,7 +702,7 @@ type QueryType { quays(ids: [String], name: String): [Quay]! @timingData "Get all quays within the specified bounding box" quaysByBbox( - authority: String, + authority: String @deprecated(reason : "This is the Transmodel namespace or the GTFS feedID - avoid using this. Request a new field if necessary."), "If true only quays with at least one visiting line are included." filterByInUse: Boolean = false, maximumLatitude: Float!, diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapperTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapperTest.java index 43dd10dbdce..17193ad02c1 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapperTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapperTest.java @@ -65,7 +65,7 @@ class LegacyRouteRequestMapperTest implements PlanTestConstants { new DefaultVehicleRentalService(), new DefaultVehicleParkingService(new DefaultVehicleParkingRepository()), new DefaultRealtimeVehicleService(transitService), - GraphFinder.getInstance(graph, transitService::findRegularStops), + GraphFinder.getInstance(graph, transitService::findRegularStopsByBoundingBox), new RouteRequest() ); } diff --git a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapperTest.java b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapperTest.java index 34cb865c81a..925e5009e77 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapperTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapperTest.java @@ -75,7 +75,7 @@ class RouteRequestMapperTest { new DefaultVehicleRentalService(), new DefaultVehicleParkingService(new DefaultVehicleParkingRepository()), new DefaultRealtimeVehicleService(transitService), - GraphFinder.getInstance(graph, transitService::findRegularStops), + GraphFinder.getInstance(graph, transitService::findRegularStopsByBoundingBox), new RouteRequest() ); } diff --git a/application/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java b/application/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java index ab897e3410b..48363a98c88 100644 --- a/application/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java +++ b/application/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java @@ -115,7 +115,7 @@ public void testSpatialIndex() { SphericalDistanceLibrary.metersToLonDegrees(100, stopJ.getLat()), SphericalDistanceLibrary.metersToDegrees(100) ); - Collection stops = transitService.findRegularStops(env); + Collection stops = transitService.findRegularStopsByBoundingBox(env); assertTrue(stops.contains(stopJ)); assertTrue(stops.contains(stopL)); assertTrue(stops.contains(stopM)); diff --git a/application/src/test/java/org/opentripplanner/transit/model/filter/expr/GenericUnaryMatcherTest.java b/application/src/test/java/org/opentripplanner/transit/model/filter/expr/GenericUnaryMatcherTest.java new file mode 100644 index 00000000000..ef93df53dbd --- /dev/null +++ b/application/src/test/java/org/opentripplanner/transit/model/filter/expr/GenericUnaryMatcherTest.java @@ -0,0 +1,15 @@ +package org.opentripplanner.transit.model.filter.expr; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class GenericUnaryMatcherTest { + + @Test + void testMatches() { + var matcher = new GenericUnaryMatcher<>("int", i -> i.equals(42)); + assertTrue(matcher.match(42)); + assertFalse(matcher.match(43)); + } +} diff --git a/application/src/test/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactoryTest.java b/application/src/test/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactoryTest.java new file mode 100644 index 00000000000..5cfeaf2780f --- /dev/null +++ b/application/src/test/java/org/opentripplanner/transit/model/filter/transit/RegularStopMatcherFactoryTest.java @@ -0,0 +1,44 @@ +package org.opentripplanner.transit.model.filter.transit; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.site.RegularStop; + +class RegularStopMatcherFactoryTest { + + private static RegularStop stop1; + private static RegularStop stop2; + + @BeforeAll + static void setup() { + stop1 = + RegularStop + .of(new FeedScopedId("agency", "stopId"), new AtomicInteger()::getAndIncrement) + .build(); + + stop2 = + RegularStop + .of(new FeedScopedId("otherAgency", "otherStopId"), new AtomicInteger()::getAndIncrement) + .build(); + } + + @Test + void testFeedIds() { + var matcher = RegularStopMatcherFactory.feedId("agency"); + assertTrue(matcher.match(stop1)); + assertFalse(matcher.match(stop2)); + } + + @Test + void testInUseMatcher() { + var matcher = RegularStopMatcherFactory.inUseMatcher(stop -> + stop.getId().getFeedId().equals("agency") + ); + assertTrue(matcher.match(stop1)); + assertFalse(matcher.match(stop2)); + } +} From 1ebb855a9f3c0ed6c3eb68cc2ff418050b051389 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 7 Jan 2025 14:00:06 +0000 Subject: [PATCH 232/269] Add changelog entry for #6234 [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 3dc3f018f9a..b859f87f19a 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -69,6 +69,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Make flex linking work together with boarding locations [#6311](https://github.com/opentripplanner/OpenTripPlanner/pull/6311) - Add fallback name for corridors [#6303](https://github.com/opentripplanner/OpenTripPlanner/pull/6303) - Implement SIRI Lite [#6284](https://github.com/opentripplanner/OpenTripPlanner/pull/6284) +- Add a matcher API for filters in the transit service used for regularStop lookup [#6234](https://github.com/opentripplanner/OpenTripPlanner/pull/6234) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From 628bf95363a64a7d7d3ccd3e66a7b08d66afe794 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 7 Jan 2025 17:48:06 +0100 Subject: [PATCH 233/269] Rename 'code' to 'publicCode' --- .../apis/gtfs/datafetchers/EntranceImpl.java | 2 +- .../apis/gtfs/generated/GraphQLDataFetchers.java | 4 ++-- .../opentripplanner/apis/gtfs/generated/GraphQLTypes.java | 6 +++++- .../org/opentripplanner/apis/gtfs/schema.graphqls | 8 ++++---- .../opentripplanner/apis/gtfs/GraphQLIntegrationTest.java | 3 --- .../apis/gtfs/expectations/walk-steps.json | 2 +- .../opentripplanner/apis/gtfs/queries/walk-steps.graphql | 2 +- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java index 9891d107479..f9faa9cc4d1 100644 --- a/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java +++ b/application/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/EntranceImpl.java @@ -9,7 +9,7 @@ public class EntranceImpl implements GraphQLDataFetchers.GraphQLEntrance { @Override - public DataFetcher code() { + public DataFetcher publicCode() { return environment -> { Entrance entrance = environment.getSource(); return entrance.getCode(); 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 d9c9ceb67e8..26ace8fc66a 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 @@ -394,12 +394,12 @@ public interface GraphQLEmissions { /** Station entrance or exit, originating from OSM or GTFS data. */ public interface GraphQLEntrance { - public DataFetcher code(); - public DataFetcher entranceId(); public DataFetcher name(); + public DataFetcher publicCode(); + public DataFetcher wheelchairAccessible(); } 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 a969b5223b1..fc20625e18e 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 @@ -4327,7 +4327,11 @@ public enum GraphQLRealtimeState { UPDATED, } - /** Actions to take relative to the current position when engaging a walking/driving step. */ + /** + * A direction that is not absolute but rather fuzzy and context-dependent. + * It provides the passenger with information what they should do in this step depending on where they + * were in the previous one. + */ public enum GraphQLRelativeDirection { CIRCLE_CLOCKWISE, CIRCLE_COUNTERCLOCKWISE, 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 c9d0f3d73af..ce808e546d1 100644 --- a/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/application/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -493,12 +493,12 @@ type Emissions { "Station entrance or exit, originating from OSM or GTFS data." type Entrance { - "Short text or a number that identifies the entrance or exit for passengers. For example, `A` or `B`." - code: String "ID of the entrance in the format of `FeedId:EntranceId`. If the `FeedId` is `osm`, the entrance originates from OSM data." entranceId: String! "Name of the entrance or exit." name: String + "Short text or a number that identifies the entrance or exit for passengers. For example, `A` or `B`." + publicCode: String "Whether the entrance or exit is accessible by wheelchair" wheelchairAccessible: WheelchairBoarding } @@ -3557,14 +3557,14 @@ enum RelativeDirection { """ Entering a public transport station. If it's not known if the passenger is entering or exiting then `CONTINUE` is used. - + More information about the entrance is in the `step.feature` field. """ ENTER_STATION """ Exiting a public transport station. If it's not known if the passenger is entering or exiting then `CONTINUE` is used. - + More information about the entrance is in the `step.feature` field. """ EXIT_STATION 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 bfd2e1cc224..2f190502ccc 100644 --- a/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/application/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -66,7 +66,6 @@ import org.opentripplanner.routing.alertpatch.TimePeriod; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.api.request.RouteRequest; -import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graphfinder.GraphFinder; import org.opentripplanner.routing.graphfinder.NearbyStop; import org.opentripplanner.routing.graphfinder.PlaceAtDistance; @@ -139,8 +138,6 @@ class GraphQLIntegrationTest { .withSystem("Network-1", "https://foo.bar") .build(); - static final Graph GRAPH = new Graph(); - static final Instant ALERT_START_TIME = OffsetDateTime .parse("2023-02-15T12:03:28+01:00") .toInstant(); diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json index 0e089aac428..95adec34ea8 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json @@ -28,7 +28,7 @@ "absoluteDirection": null, "feature": { "__typename": "Entrance", - "code": "A", + "publicCode": "A", "entranceId": "osm:123", "wheelchairAccessible": "POSSIBLE" } diff --git a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql index 565e620fed3..18cb5a8d49d 100644 --- a/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql +++ b/application/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql @@ -23,7 +23,7 @@ feature { __typename ... on Entrance { - code + publicCode entranceId wheelchairAccessible } From f8b51d954dae8f06369e5133484b531736150089 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 8 Jan 2025 12:11:08 +0100 Subject: [PATCH 234/269] Fix spelling of SIRI [ci skip] --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 51245f6c3b8..0ebcb7ddd4d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -91,7 +91,7 @@ nav: - "Stop Area Relations": 'StopAreas.md' - "Street Graph Pruning": 'IslandPruning.md' - Accessibility: 'Accessibility.md' - - NeTex and Siri compatibility: 'features-explained/Netex-Siri-Compatibility.md' + - NeTEx and SIRI compatibility: 'features-explained/Netex-Siri-Compatibility.md' - "Travel Time Analysis": 'Analysis.md' - "Logging": "Logging.md" - Development: From 8771d5c94d8decc4aeeed5d648e5b626d994b1b8 Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Wed, 8 Jan 2025 17:16:19 +0000 Subject: [PATCH 235/269] apply review suggestion Co-authored-by: Leonard Ehrenfried --- .../service/osminfo/OsmInfoGraphBuildRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java index e30e0dd19a7..ac8f7276072 100644 --- a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java +++ b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java @@ -6,7 +6,7 @@ import org.opentripplanner.street.model.edge.Edge; /** - * Store OSM data used during graph build, but after the OSM Graph Builder is done. + * Store OSM data used during graph build, but discard it after it is complete. *

* This is a repository to support the {@link OsmInfoGraphBuildService}. */ From c1749286c44c5cd7f4eee82bda34ad6565c69f9e Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Wed, 8 Jan 2025 17:22:45 +0000 Subject: [PATCH 236/269] add Javadoc --- .../org/opentripplanner/routing/linking/VertexLinker.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java b/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java index a433f3882e1..2f09d618ffd 100644 --- a/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java +++ b/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java @@ -227,6 +227,11 @@ private DisposableEdgeCollection link( return tempEdges; } + /** + * Link a boarding location vertex to specific street edges. + *

+ * This is used if a platform is mapped as a linear way, where the given edges form the platform. + */ public Set linkToSpecificStreetEdgesPermanently( Vertex vertex, TraverseModeSet traverseModes, From 1bfc8332c5a71a2ba390c03e5f75c1c287f506ac Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 8 Jan 2025 17:52:37 +0100 Subject: [PATCH 237/269] Fix ScheduledTransitAlertBuilder --- .../model/plan/ScheduledTransitLeg.java | 1 + .../plan/ScheduledTransitLegBuilder.java | 9 ++++++ .../plan/ScheduledTransitLegBuilderTest.java | 30 +++++++++++++++++++ 3 files changed, 40 insertions(+) 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 55e4bccfdec..ab912eae85b 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java +++ b/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java @@ -98,6 +98,7 @@ protected ScheduledTransitLeg(ScheduledTransitLegBuilder builder) { getDistanceFromCoordinates( List.of(transitLegCoordinates.getFirst(), transitLegCoordinates.getLast()) ); + this.transitAlerts.addAll(builder.alerts()); } public ZoneId getZoneId() { diff --git a/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilder.java b/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilder.java index 3e9b5540b1c..e132137e982 100644 --- a/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilder.java +++ b/application/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilder.java @@ -3,7 +3,10 @@ import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.HashSet; +import java.util.Set; import org.opentripplanner.model.transfer.ConstrainedTransfer; +import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; @@ -23,6 +26,7 @@ public class ScheduledTransitLegBuilder> private ConstrainedTransfer transferToNextLeg; private int generalizedCost; private Float accessibilityScore; + private Set alerts = new HashSet<>(); public ScheduledTransitLegBuilder() {} @@ -40,6 +44,7 @@ public ScheduledTransitLegBuilder(ScheduledTransitLeg original) { generalizedCost = original.getGeneralizedCost(); accessibilityScore = original.accessibilityScore(); zoneId = original.getZoneId(); + alerts = original.getTransitAlerts(); } public B withTripTimes(TripTimes tripTimes) { @@ -159,6 +164,10 @@ public Float accessibilityScore() { return accessibilityScore; } + public Set alerts() { + return alerts; + } + public ScheduledTransitLeg build() { return new ScheduledTransitLeg(this); } diff --git a/application/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilderTest.java b/application/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilderTest.java index 8d3d6ed7c1e..83871257ce2 100644 --- a/application/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilderTest.java +++ b/application/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilderTest.java @@ -1,15 +1,25 @@ package org.opentripplanner.model.plan; +import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.transit.model._data.TimetableRepositoryForTest.id; import java.time.LocalDate; +import java.util.Set; import org.junit.jupiter.api.Test; import org.opentripplanner._support.time.ZoneIds; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; import org.opentripplanner.transit.model.basic.TransitMode; class ScheduledTransitLegBuilderTest { + private static final TransitAlert ALERT = TransitAlert + .of(id("alert")) + .withDescriptionText(I18NString.of("alert")) + .build(); + @Test void transferZoneId() { var pattern = TimetableRepositoryForTest.of().pattern(TransitMode.BUS).build(); @@ -28,4 +38,24 @@ void transferZoneId() { assertEquals(ZoneIds.BERLIN, withScore.getZoneId()); } + + @Test + void alerts() { + var pattern = TimetableRepositoryForTest.of().pattern(TransitMode.BUS).build(); + var leg = new ScheduledTransitLegBuilder<>() + .withZoneId(ZoneIds.BERLIN) + .withServiceDate(LocalDate.of(2023, 11, 15)) + .withTripPattern(pattern) + .withBoardStopIndexInPattern(0) + .withAlightStopIndexInPattern(1) + .build(); + + leg.addAlert(ALERT); + + var newLeg = new ScheduledTransitLegBuilder<>(leg); + + var withScore = newLeg.withAccessibilityScore(4f).build(); + + assertEquals(Set.of(ALERT), withScore.getTransitAlerts()); + } } From 7163f6a9154ede5f384c91ceec450b403f82d4e6 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 9 Jan 2025 15:21:03 +0000 Subject: [PATCH 238/269] Add changelog entry for #6262 [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 b859f87f19a..14a9f6d83ed 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -70,6 +70,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Add fallback name for corridors [#6303](https://github.com/opentripplanner/OpenTripPlanner/pull/6303) - Implement SIRI Lite [#6284](https://github.com/opentripplanner/OpenTripPlanner/pull/6284) - Add a matcher API for filters in the transit service used for regularStop lookup [#6234](https://github.com/opentripplanner/OpenTripPlanner/pull/6234) +- Make all polling updaters wait for graph update finish [#6262](https://github.com/opentripplanner/OpenTripPlanner/pull/6262) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) From 98d8fd82cf282d434efadd795dc347d1e865ea12 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 9 Jan 2025 16:33:31 +0100 Subject: [PATCH 239/269] Remove unused import --- .../model/plan/ScheduledTransitLegBuilderTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilderTest.java b/application/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilderTest.java index 83871257ce2..c8ae617fd20 100644 --- a/application/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilderTest.java +++ b/application/src/test/java/org/opentripplanner/model/plan/ScheduledTransitLegBuilderTest.java @@ -1,6 +1,5 @@ package org.opentripplanner.model.plan; -import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.opentripplanner.transit.model._data.TimetableRepositoryForTest.id; From f53245fdef475829f5a14cca1795fcce3287cb1a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 9 Jan 2025 16:35:49 +0100 Subject: [PATCH 240/269] Fix formatting --- .../updater/alert/GtfsRealtimeAlertsUpdater.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java index 604a721c57d..8deddd35b61 100644 --- a/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java +++ b/application/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java @@ -62,7 +62,7 @@ protected void runPolling() throws InterruptedException, ExecutionException { final FeedMessage feed = otpHttpClient.getAndMap( URI.create(url), this.headers.asMap(), - FeedMessage::parseFrom + FeedMessage::parseFrom ); long feedTimestamp = feed.getHeader().getTimestamp(); From 81d6c9b54b2f1d8063aa687b7ee346be0af742bf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:43:59 +0000 Subject: [PATCH 241/269] fix(deps): update jersey monorepo to v3.1.10 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7eb8dfffde8..eb494243d23 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 2.53 2.18.2 4.0.5 - 3.1.9 + 3.1.10 5.11.4 1.14.1 5.6.0 From ffa99abb60851136751d42520989e86d097917fb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 9 Jan 2025 16:52:50 +0100 Subject: [PATCH 242/269] Automerge patch version of jersey [ci skip] --- renovate.json5 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/renovate.json5 b/renovate.json5 index 557857bf54c..1394c6484b9 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -142,6 +142,15 @@ "automerge": true, "schedule": "on the 4th day of the month" }, + { + "groupName": "Low-risk dependencies (patch)", + "matchUpdateTypes": ["patch"], + "schedule": ["on the 27th day of the month"], + "reviewers": ["testower"], + "matchPackageNames": [ + "org.glassfish.jersey.{/,}**", + ] + }, { "description": "give some projects time to publish a changelog before opening the PR", "matchPackageNames": [ From edc3a7f4f920732fa3d499cb6784871291c952da Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 9 Jan 2025 17:05:28 +0100 Subject: [PATCH 243/269] Automerge protobuf only once a month [ci skip] --- renovate.json5 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/renovate.json5 b/renovate.json5 index 1394c6484b9..50134776150 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -101,7 +101,8 @@ "com.google.cloud:libraries-bom", "com.google.guava:guava", "io.micrometer:micrometer-registry-prometheus", - "io.micrometer:micrometer-registry-influx" + "io.micrometer:micrometer-registry-influx", + "com.google.protobuf:protobuf-java" ], "schedule": "on the 7th through 8th day of the month" }, From c5d1183e2a0412b48fadbf0a1b0bb7075b347465 Mon Sep 17 00:00:00 2001 From: Samuel Cedarbaum Date: Thu, 9 Jan 2025 19:46:09 -0500 Subject: [PATCH 244/269] Move TestDataFetcherDecorator to correct package path --- .../transmodel}/_support/TestDataFetcherDecorator.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename application/src/test/java/org/opentripplanner/{ext/transmodelapi => apis/transmodel}/_support/TestDataFetcherDecorator.java (100%) diff --git a/application/src/test/java/org/opentripplanner/ext/transmodelapi/_support/TestDataFetcherDecorator.java b/application/src/test/java/org/opentripplanner/apis/transmodel/_support/TestDataFetcherDecorator.java similarity index 100% rename from application/src/test/java/org/opentripplanner/ext/transmodelapi/_support/TestDataFetcherDecorator.java rename to application/src/test/java/org/opentripplanner/apis/transmodel/_support/TestDataFetcherDecorator.java From 872dbb36ee4f6fa7e1b076df7f88a1d67235f43c Mon Sep 17 00:00:00 2001 From: Samuel Cedarbaum Date: Thu, 9 Jan 2025 19:46:46 -0500 Subject: [PATCH 245/269] Add explicit cast when calling createFromDocumentedEnum --- .../org/opentripplanner/apis/transmodel/model/EnumTypes.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java index 2f8e69cc593..aab06381100 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java @@ -221,7 +221,7 @@ public class EnumTypes { public static final GraphQLEnumType OCCUPANCY_STATUS = createFromDocumentedEnum( "OccupancyStatus", - List.of( + List.>of( map("noData", OccupancyStatus.NO_DATA_AVAILABLE), map("empty", OccupancyStatus.EMPTY), map("manySeatsAvailable", OccupancyStatus.MANY_SEATS_AVAILABLE), From e23144bf9b611019f95cb1b4b2fa1f26df906b34 Mon Sep 17 00:00:00 2001 From: Samuel Cedarbaum Date: Thu, 9 Jan 2025 19:56:28 -0500 Subject: [PATCH 246/269] Add type annotation to OccupancyStatus instead of explicit cast --- .../org/opentripplanner/apis/transmodel/model/EnumTypes.java | 2 +- .../transit/model/timetable/OccupancyStatus.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java b/application/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java index aab06381100..2f8e69cc593 100644 --- a/application/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java @@ -221,7 +221,7 @@ public class EnumTypes { public static final GraphQLEnumType OCCUPANCY_STATUS = createFromDocumentedEnum( "OccupancyStatus", - List.>of( + List.of( map("noData", OccupancyStatus.NO_DATA_AVAILABLE), map("empty", OccupancyStatus.EMPTY), map("manySeatsAvailable", OccupancyStatus.MANY_SEATS_AVAILABLE), diff --git a/application/src/main/java/org/opentripplanner/transit/model/timetable/OccupancyStatus.java b/application/src/main/java/org/opentripplanner/transit/model/timetable/OccupancyStatus.java index 9c32ca39394..fe760747ef4 100644 --- a/application/src/main/java/org/opentripplanner/transit/model/timetable/OccupancyStatus.java +++ b/application/src/main/java/org/opentripplanner/transit/model/timetable/OccupancyStatus.java @@ -9,7 +9,7 @@ *

* Descriptions are copied from the GTFS-RT specification with additions of SIRI nordic profile documentation. */ -public enum OccupancyStatus implements DocumentedEnum { +public enum OccupancyStatus implements DocumentedEnum { NO_DATA_AVAILABLE, EMPTY, MANY_SEATS_AVAILABLE, From 6a540d18e0d720a55c32b64c0575caf44016d7f1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 10 Jan 2025 09:15:30 +0100 Subject: [PATCH 247/269] Remove testower from reviewers of jersey [ci skip] --- renovate.json5 | 1 - 1 file changed, 1 deletion(-) diff --git a/renovate.json5 b/renovate.json5 index 50134776150..100a91d959a 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -147,7 +147,6 @@ "groupName": "Low-risk dependencies (patch)", "matchUpdateTypes": ["patch"], "schedule": ["on the 27th day of the month"], - "reviewers": ["testower"], "matchPackageNames": [ "org.glassfish.jersey.{/,}**", ] From 1eb0556da113a86f560b3bdccdf8f9e8dc47ba8d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:53:23 +0000 Subject: [PATCH 248/269] chore(deps): update dependency jsdom to v26 --- client/package-lock.json | 183 ++++++++++++++++++++++++++++++++++----- client/package.json | 2 +- 2 files changed, 164 insertions(+), 21 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index a50d6d07f73..258494649cc 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -39,7 +39,7 @@ "eslint-plugin-react-hooks": "5.1.0", "eslint-plugin-react-refresh": "0.4.16", "globals": "15.14.0", - "jsdom": "25.0.1", + "jsdom": "26.0.0", "prettier": "3.4.2", "typescript": "5.7.2", "typescript-eslint": "8.19.0", @@ -207,6 +207,30 @@ "node": ">=14" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.2.tgz", + "integrity": "sha512-RtWv9jFN2/bLExuZgFFZ0I3pWWeezAHGgrmjqGGWclATl1aDe3yhCUaI0Ilkp6OCk9zX7+FjvDasEX8Q9Rxc5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.1", + "@csstools/css-color-parser": "^3.0.7", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^11.0.2" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -1007,6 +1031,121 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz", + "integrity": "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.1.tgz", + "integrity": "sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.7.tgz", + "integrity": "sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.1", + "@csstools/css-calc": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@envelop/core": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.0.2.tgz", @@ -5178,12 +5317,14 @@ } }, "node_modules/cssstyle": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", - "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.2.1.tgz", + "integrity": "sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==", "dev": true, + "license": "MIT", "dependencies": { - "rrweb-cssom": "^0.7.1" + "@asamuzakjp/css-color": "^2.8.2", + "rrweb-cssom": "^0.8.0" }, "engines": { "node": ">=18" @@ -7761,22 +7902,23 @@ "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==" }, "node_modules/jsdom": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", - "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", + "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", "dev": true, + "license": "MIT", "dependencies": { - "cssstyle": "^4.1.0", + "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.4.3", - "form-data": "^4.0.0", + "form-data": "^4.0.1", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", + "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.12", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.0.0", @@ -7784,7 +7926,7 @@ "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0", + "whatwg-url": "^14.1.0", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, @@ -7792,7 +7934,7 @@ "node": ">=18" }, "peerDependencies": { - "canvas": "^2.11.2" + "canvas": "^3.0.0" }, "peerDependenciesMeta": { "canvas": { @@ -9455,10 +9597,11 @@ } }, "node_modules/rrweb-cssom": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", - "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", - "dev": true + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" }, "node_modules/run-async": { "version": "2.4.1", diff --git a/client/package.json b/client/package.json index b16a1fe8527..d81511d93c9 100644 --- a/client/package.json +++ b/client/package.json @@ -48,7 +48,7 @@ "eslint-plugin-react-hooks": "5.1.0", "eslint-plugin-react-refresh": "0.4.16", "globals": "15.14.0", - "jsdom": "25.0.1", + "jsdom": "26.0.0", "prettier": "3.4.2", "typescript": "5.7.2", "typescript-eslint": "8.19.0", From 4248ffe1644ae50d1970d26310b5593acf7fe508 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 10 Jan 2025 15:38:12 +0100 Subject: [PATCH 249/269] Fix mapping in Transmodel API --- .../mapping/RelativeDirectionMapper.java | 33 +++++++++++++++++++ .../model/plan/PathGuidanceType.java | 5 ++- .../apis/transmodel/model/EnumTypesTest.java | 21 ++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 application/src/main/java/org/opentripplanner/apis/transmodel/mapping/RelativeDirectionMapper.java diff --git a/application/src/main/java/org/opentripplanner/apis/transmodel/mapping/RelativeDirectionMapper.java b/application/src/main/java/org/opentripplanner/apis/transmodel/mapping/RelativeDirectionMapper.java new file mode 100644 index 00000000000..3228cb914df --- /dev/null +++ b/application/src/main/java/org/opentripplanner/apis/transmodel/mapping/RelativeDirectionMapper.java @@ -0,0 +1,33 @@ +package org.opentripplanner.apis.transmodel.mapping; + +import org.opentripplanner.model.plan.RelativeDirection; + +/** + * This mapper makes sure that only those values are returned which have a mapping in the Transmodel API, + * as we don't really want to return all of them. + */ +public class RelativeDirectionMapper { + + public static RelativeDirection map(RelativeDirection relativeDirection) { + return switch (relativeDirection) { + case DEPART, + SLIGHTLY_LEFT, + HARD_LEFT, + LEFT, + CONTINUE, + SLIGHTLY_RIGHT, + RIGHT, + HARD_RIGHT, + CIRCLE_CLOCKWISE, + CIRCLE_COUNTERCLOCKWISE, + ELEVATOR, + UTURN_LEFT, + UTURN_RIGHT -> relativeDirection; + // for these the Transmodel API doesn't have a mapping. should it? + case ENTER_STATION, + EXIT_STATION, + ENTER_OR_EXIT_STATION, + FOLLOW_SIGNS -> RelativeDirection.CONTINUE; + }; + } +} 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 86c8359c026..8840e8fedb8 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 @@ -5,6 +5,7 @@ import graphql.schema.GraphQLList; import graphql.schema.GraphQLNonNull; import graphql.schema.GraphQLObjectType; +import org.opentripplanner.apis.transmodel.mapping.RelativeDirectionMapper; import org.opentripplanner.apis.transmodel.model.EnumTypes; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.plan.WalkStep; @@ -31,7 +32,9 @@ public static GraphQLObjectType create(GraphQLObjectType elevationStepType) { .name("relativeDirection") .description("The relative direction of this step.") .type(EnumTypes.RELATIVE_DIRECTION) - .dataFetcher(environment -> ((WalkStep) environment.getSource()).getRelativeDirection()) + .dataFetcher(environment -> + RelativeDirectionMapper.map(((WalkStep) environment.getSource()).getRelativeDirection()) + ) .build() ) .field( diff --git a/application/src/test/java/org/opentripplanner/apis/transmodel/model/EnumTypesTest.java b/application/src/test/java/org/opentripplanner/apis/transmodel/model/EnumTypesTest.java index 3d834fced58..9090cd1bdc5 100644 --- a/application/src/test/java/org/opentripplanner/apis/transmodel/model/EnumTypesTest.java +++ b/application/src/test/java/org/opentripplanner/apis/transmodel/model/EnumTypesTest.java @@ -1,14 +1,23 @@ package org.opentripplanner.apis.transmodel.model; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.opentripplanner.apis.transmodel.model.EnumTypes.RELATIVE_DIRECTION; import static org.opentripplanner.apis.transmodel.model.EnumTypes.ROUTING_ERROR_CODE; import static org.opentripplanner.apis.transmodel.model.EnumTypes.map; +import graphql.GraphQLContext; import java.util.EnumSet; import java.util.List; +import java.util.Locale; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.opentripplanner.apis.transmodel.mapping.RelativeDirectionMapper; import org.opentripplanner.framework.doc.DocumentedEnum; +import org.opentripplanner.model.plan.RelativeDirection; import org.opentripplanner.routing.api.response.RoutingErrorCode; class EnumTypesTest { @@ -75,6 +84,18 @@ void testMap() { assertEquals("DocumentedEnumMapping[apiName=iH, internal=Hi]", mapping.toString()); } + @ParameterizedTest + @EnumSource(RelativeDirection.class) + void serializeRelativeDirection(RelativeDirection direction) { + var value = RELATIVE_DIRECTION.serialize( + RelativeDirectionMapper.map(direction), + GraphQLContext.getDefault(), + Locale.ENGLISH + ); + assertInstanceOf(String.class, value); + assertNotNull(value); + } + @Test void assertAllRoutingErrorCodesAreMapped() { var expected = EnumSet.allOf(RoutingErrorCode.class); From 77e29087dea17be0312171364cd1cd732c17e607 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 22:14:36 +0000 Subject: [PATCH 250/269] fix(deps): update debug ui dependencies (non-major) --- client/package-lock.json | 217 ++++++++++++++++++++++----------------- client/package.json | 16 +-- 2 files changed, 129 insertions(+), 104 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 258494649cc..bb145274bd4 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -17,21 +17,21 @@ "react": "19.0.0", "react-bootstrap": "2.10.7", "react-dom": "19.0.0", - "react-map-gl": "7.1.7" + "react-map-gl": "7.1.8" }, "devDependencies": { - "@eslint/compat": "1.2.4", - "@eslint/js": "9.17.0", + "@eslint/compat": "1.2.5", + "@eslint/js": "9.18.0", "@graphql-codegen/cli": "5.0.3", "@graphql-codegen/client-preset": "4.5.1", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.5.0", "@testing-library/react": "16.1.0", - "@types/react": "19.0.1", + "@types/react": "19.0.4", "@types/react-dom": "19.0.2", "@vitejs/plugin-react": "4.3.4", "@vitest/coverage-v8": "2.1.8", - "eslint": "9.17.0", + "eslint": "9.18.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", @@ -41,9 +41,9 @@ "globals": "15.14.0", "jsdom": "26.0.0", "prettier": "3.4.2", - "typescript": "5.7.2", - "typescript-eslint": "8.19.0", - "vite": "6.0.3", + "typescript": "5.7.3", + "typescript-eslint": "8.19.1", + "vite": "6.0.7", "vitest": "2.1.8" } }, @@ -1611,10 +1611,11 @@ } }, "node_modules/@eslint/compat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.4.tgz", - "integrity": "sha512-S8ZdQj/N69YAtuqFt7653jwcvuUj131+6qGLUyDqfDg1OIoBQ66OCuXC473YQfO2AaxITTutiRQiDwoo7ZLYyg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.5.tgz", + "integrity": "sha512-5iuG/StT+7OfvhoBHPlmxkPA9om6aDUFgmD4+mWKAGsYt4vCe8rypneG03AuseyRHBmcCLXQtIH5S26tIoggLg==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1642,10 +1643,11 @@ } }, "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -1689,10 +1691,11 @@ } }, "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", + "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -1707,11 +1710,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, + "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { @@ -3875,9 +3880,10 @@ "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" }, "node_modules/@types/react": { - "version": "19.0.1", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.1.tgz", - "integrity": "sha512-YW6614BDhqbpR5KtUYzTA+zlA7nayzJRA9ljz9CQoxthR0sDisYZLuvSMsil36t4EH/uAt8T52Xb4sVw17G+SQ==", + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.4.tgz", + "integrity": "sha512-3O4QisJDYr1uTUMZHA2YswiQZRq+Pd8D+GdVFYikTutYsTz+QZgWkAPnP7rx9txoI6EXKcPiluMqWPFV3tT9Wg==", + "license": "MIT", "dependencies": { "csstype": "^3.0.2" } @@ -3922,20 +3928,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz", - "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz", + "integrity": "sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/type-utils": "8.19.0", - "@typescript-eslint/utils": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.19.1", + "@typescript-eslint/type-utils": "8.19.1", + "@typescript-eslint/utils": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3951,15 +3958,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.1.tgz", + "integrity": "sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.19.1", + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/typescript-estree": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1", "debug": "^4.3.4" }, "engines": { @@ -3975,13 +3983,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.1.tgz", + "integrity": "sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3992,15 +4001,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", - "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.1.tgz", + "integrity": "sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/utils": "8.19.0", + "@typescript-eslint/typescript-estree": "8.19.1", + "@typescript-eslint/utils": "8.19.1", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4015,10 +4025,11 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.1.tgz", + "integrity": "sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -4028,19 +4039,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", - "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.1.tgz", + "integrity": "sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4058,6 +4070,7 @@ "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" } @@ -4067,6 +4080,7 @@ "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" }, @@ -4082,6 +4096,7 @@ "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" }, @@ -4090,15 +4105,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", - "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.1.tgz", + "integrity": "sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0" + "@typescript-eslint/scope-manager": "8.19.1", + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/typescript-estree": "8.19.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4113,12 +4129,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.1.tgz", + "integrity": "sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/types": "8.19.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -5915,18 +5932,19 @@ } }, "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", + "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", + "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/js": "9.18.0", + "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", @@ -9317,9 +9335,10 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "node_modules/react-map-gl": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-7.1.7.tgz", - "integrity": "sha512-mwjc0obkBJOXCcoXQr3VoLqmqwo9vS4bXfbGsdxXzEgVCv/PM0v+1QggL7W0d/ccIy+VCjbXNlGij+PENz6VNg==", + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-7.1.8.tgz", + "integrity": "sha512-zwF16XMOdOKH4py5ehS1bgQIChqW8UN3b1bXps+JnADbYLSbOoUPQ3tNw0EZ2OTBWArR5aaQlhlEqg4lE47T8A==", + "license": "MIT", "dependencies": { "@maplibre/maplibre-gl-style-spec": "^19.2.1", "@types/mapbox-gl": ">=1.0.0" @@ -9343,6 +9362,7 @@ "version": "19.3.3", "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz", "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==", + "license": "ISC", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", @@ -9360,7 +9380,8 @@ "node_modules/react-map-gl/node_modules/json-stringify-pretty-compact": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", - "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==" + "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==", + "license": "MIT" }, "node_modules/react-refresh": { "version": "0.14.2", @@ -10529,15 +10550,16 @@ } }, "node_modules/ts-api-utils": { - "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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-log": { @@ -10674,10 +10696,11 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10687,14 +10710,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.0.tgz", - "integrity": "sha512-Ni8sUkVWYK4KAcTtPjQ/UTiRk6jcsuDhPpxULapUDi8A/l8TSBk+t1GtJA1RsCzIJg0q6+J7bf35AwQigENWRQ==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.1.tgz", + "integrity": "sha512-LKPUQpdEMVOeKluHi8md7rwLcoXHhwvWp3x+sJkMuq3gGm9yaYJtPo8sRZSblMFJ5pcOGCAak/scKf1mvZDlQw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.19.0", - "@typescript-eslint/parser": "8.19.0", - "@typescript-eslint/utils": "8.19.0" + "@typescript-eslint/eslint-plugin": "8.19.1", + "@typescript-eslint/parser": "8.19.1", + "@typescript-eslint/utils": "8.19.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10899,12 +10923,13 @@ } }, "node_modules/vite": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz", - "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", + "integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.24.0", + "esbuild": "^0.24.2", "postcss": "^8.4.49", "rollup": "^4.23.0" }, diff --git a/client/package.json b/client/package.json index d81511d93c9..78180de6b6a 100644 --- a/client/package.json +++ b/client/package.json @@ -26,21 +26,21 @@ "react": "19.0.0", "react-bootstrap": "2.10.7", "react-dom": "19.0.0", - "react-map-gl": "7.1.7" + "react-map-gl": "7.1.8" }, "devDependencies": { - "@eslint/compat": "1.2.4", - "@eslint/js": "9.17.0", + "@eslint/compat": "1.2.5", + "@eslint/js": "9.18.0", "@graphql-codegen/cli": "5.0.3", "@graphql-codegen/client-preset": "4.5.1", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.5.0", "@testing-library/react": "16.1.0", - "@types/react": "19.0.1", + "@types/react": "19.0.4", "@types/react-dom": "19.0.2", "@vitejs/plugin-react": "4.3.4", "@vitest/coverage-v8": "2.1.8", - "eslint": "9.17.0", + "eslint": "9.18.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", @@ -50,9 +50,9 @@ "globals": "15.14.0", "jsdom": "26.0.0", "prettier": "3.4.2", - "typescript": "5.7.2", - "typescript-eslint": "8.19.0", - "vite": "6.0.3", + "typescript": "5.7.3", + "typescript-eslint": "8.19.1", + "vite": "6.0.7", "vitest": "2.1.8" } } From 71bb5fa9583f837c06899f5a378dba0c05cda38e Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Sat, 11 Jan 2025 19:24:18 +0000 Subject: [PATCH 251/269] Upgrade debug client to version 2025/01/2025-01-11T19:23 --- 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 db412af9df5..d3dd8544a53 100644 --- a/application/src/client/index.html +++ b/application/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug - - + +

From 3fcd54f98c69c89475fcc9569e1dd40fe1c0c5ab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:18:25 +0000 Subject: [PATCH 252/269] fix(deps): update google.dagger.version to v2.54 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eb494243d23..f77d753472d 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ 32.1 - 2.53 + 2.54 2.18.2 4.0.5 3.1.10 From 521c87aeb3f2781d5358bb0d90e3e089ff419985 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 10 Jan 2025 18:17:43 +0100 Subject: [PATCH 253/269] Update GraphiQL, add headers to UI --- application/src/client/graphiql/index.html | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/application/src/client/graphiql/index.html b/application/src/client/graphiql/index.html index 0e8d4afd1ea..e3a4a1a0f8f 100644 --- a/application/src/client/graphiql/index.html +++ b/application/src/client/graphiql/index.html @@ -30,13 +30,14 @@ copy them directly into your environment, or perhaps include them in your favored resource bundler. --> - + OTP GraphQL Explorer
Loading...
- + +