From 3ca1a0c7914ccf2e5aa77b47af5a0ce1eae3ee24 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 23 May 2024 15:20:27 +0200 Subject: [PATCH 001/192] Implement query for filtering by service days --- .../apis/gtfs/GraphQLScalars.java | 5 ++ .../apis/gtfs/GtfsGraphQLIndex.java | 8 +-- .../apis/gtfs/datafetchers/RouteImpl.java | 45 ++++++++++++++++- .../apis/gtfs/generated/GraphQLTypes.java | 49 +++++++++++++++++++ .../apis/gtfs/generated/graphql-codegen.yml | 1 + .../apis/transmodel/support/GqlUtil.java | 4 +- .../graphql/scalar}/DateScalarFactory.java | 20 +++++--- .../opentripplanner/apis/gtfs/schema.graphqls | 12 ++++- .../graphql/scalar/DateScalarFactoryTest.java | 23 +++++++++ .../gtfs/expectations/routes-extended.json | 42 ++++++++++------ .../apis/gtfs/queries/routes-extended.graphql | 3 ++ 11 files changed, 180 insertions(+), 32 deletions(-) rename src/main/java/org/opentripplanner/{apis/transmodel/model/scalars => framework/graphql/scalar}/DateScalarFactory.java (72%) create mode 100644 src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java index 5ab24c89543..a3034cad273 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java @@ -15,6 +15,7 @@ import java.time.format.DateTimeFormatter; import javax.annotation.Nonnull; import org.locationtech.jts.geom.Geometry; +import org.opentripplanner.framework.graphql.scalar.DateScalarFactory; import org.opentripplanner.framework.graphql.scalar.DurationScalarFactory; import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.framework.model.Grams; @@ -111,6 +112,10 @@ public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralExce ) .build(); + public static final GraphQLScalarType LOCAL_DATE_SCALAR = DateScalarFactory.createDateScalar( + "LocalDate" + ); + public static final GraphQLScalarType GEOJSON_SCALAR = GraphQLScalarType .newScalar() .name("GeoJson") diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index 53d53b9bc4b..20a37d6b940 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -22,8 +22,6 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.opentripplanner.apis.gtfs.datafetchers.AgencyImpl; @@ -84,7 +82,6 @@ import org.opentripplanner.apis.gtfs.model.StopPosition; import org.opentripplanner.ext.actuator.MicrometerGraphQLInstrumentation; import org.opentripplanner.framework.application.OTPFeature; -import org.opentripplanner.framework.concurrent.OtpRequestThreadFactory; import org.opentripplanner.framework.graphql.GraphQLResponseSerializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,10 +92,6 @@ class GtfsGraphQLIndex { private static final GraphQLSchema indexSchema = buildSchema(); - static final ExecutorService threadPool = Executors.newCachedThreadPool( - OtpRequestThreadFactory.of("gtfs-api-%d") - ); - protected static GraphQLSchema buildSchema() { try { URL url = Objects.requireNonNull(GtfsGraphQLIndex.class.getResource("schema.graphqls")); @@ -112,6 +105,7 @@ protected static GraphQLSchema buildSchema() { .scalar(GraphQLScalars.GRAPHQL_ID_SCALAR) .scalar(GraphQLScalars.GRAMS_SCALAR) .scalar(GraphQLScalars.OFFSET_DATETIME_SCALAR) + .scalar(GraphQLScalars.LOCAL_DATE_SCALAR) .scalar(ExtendedScalars.GraphQLLong) .type("Node", type -> type.typeResolver(new NodeTypeResolver())) .type("PlaceInterface", type -> type.typeResolver(new PlaceInterfaceTypeResolver())) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index 9f9b3c60b31..4b21728b8b4 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -3,6 +3,7 @@ import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -174,10 +175,32 @@ public DataFetcher mode() { @Override public DataFetcher> patterns() { - return environment -> - getTransitService(environment).getPatternsForRoute(getSource(environment)); + return environment -> { + final TransitService transitService = getTransitService(environment); + var patterns = transitService.getPatternsForRoute(getSource(environment)); + + var args = new GraphQLTypes.GraphQLRoutePatternsArgs(environment.getArguments()); + + var serviceDays = args.getGraphQLServiceDays(); + if ( + serviceDays != null && + (serviceDays.getGraphQLStart() != null || serviceDays.getGraphQLEnd() != null) + ) { + var start = args.getGraphQLServiceDays().getGraphQLStart(); + return patterns + .stream() + .filter(pattern -> + hasServicesOnDate(patterns, transitService, start) + ) + .toList(); + } else { + return patterns; + } + }; } + + @Override public DataFetcher shortName() { return environment -> getSource(environment).getShortName(); @@ -241,4 +264,22 @@ private TransitService getTransitService(DataFetchingEnvironment environment) { private Route getSource(DataFetchingEnvironment environment) { return environment.getSource(); } + + private static boolean hasServicesOnDate(Collection patterns, TransitService transitService, LocalDate start) { + return patterns + .stream() + .anyMatch(p -> + p + .scheduledTripsAsStream() + .anyMatch(trip -> { + var dates = transitService + .getCalendarService() + .getServiceDatesForServiceId(trip.getServiceId()); + + return dates + .stream() + .anyMatch(date -> date.isEqual(start) || date.isAfter(start)); + }) + ); + } } 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 ecc038fec18..de625f15c7b 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -2751,6 +2751,26 @@ public void setGraphQLLanguage(String language) { } } + public static class GraphQLRoutePatternsArgs { + + private GraphQLServiceDayFilterInput serviceDays; + + public GraphQLRoutePatternsArgs(Map args) { + if (args != null) { + this.serviceDays = + new GraphQLServiceDayFilterInput((Map) args.get("serviceDays")); + } + } + + public GraphQLServiceDayFilterInput getGraphQLServiceDays() { + return this.serviceDays; + } + + public void setGraphQLServiceDays(GraphQLServiceDayFilterInput serviceDays) { + this.serviceDays = serviceDays; + } + } + /** Entities that are relevant for routes that can contain alerts */ public enum GraphQLRouteAlertType { AGENCY, @@ -2772,6 +2792,35 @@ public enum GraphQLRoutingErrorCode { WALKING_BETTER_THAN_TRANSIT, } + public static class GraphQLServiceDayFilterInput { + + private java.time.LocalDate end; + private java.time.LocalDate start; + + public GraphQLServiceDayFilterInput(Map args) { + if (args != null) { + this.end = (java.time.LocalDate) args.get("end"); + this.start = (java.time.LocalDate) args.get("start"); + } + } + + public java.time.LocalDate getGraphQLEnd() { + return this.end; + } + + public java.time.LocalDate getGraphQLStart() { + return this.start; + } + + public void setGraphQLEnd(java.time.LocalDate end) { + this.end = end; + } + + public void setGraphQLStart(java.time.LocalDate start) { + this.start = start; + } + } + public static class GraphQLStopAlertsArgs { 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 29c2bff0257..9309e60224f 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 @@ -28,6 +28,7 @@ config: Grams: org.opentripplanner.framework.model.Grams OffsetDateTime: java.time.OffsetDateTime Duration: java.time.Duration + LocalDate: java.time.LocalDate mappers: AbsoluteDirection: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAbsoluteDirection#GraphQLAbsoluteDirection Agency: org.opentripplanner.transit.model.organization.Agency#Agency diff --git a/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java b/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java index 1f0722eb991..05a8eaa45fa 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java @@ -15,12 +15,12 @@ import java.util.Locale; import org.opentripplanner.apis.transmodel.TransmodelRequestContext; import org.opentripplanner.apis.transmodel.mapping.TransitIdMapper; -import org.opentripplanner.apis.transmodel.model.scalars.DateScalarFactory; import org.opentripplanner.apis.transmodel.model.scalars.DateTimeScalarFactory; import org.opentripplanner.apis.transmodel.model.scalars.DoubleFunctionFactory; import org.opentripplanner.apis.transmodel.model.scalars.LocalTimeScalarFactory; import org.opentripplanner.apis.transmodel.model.scalars.TimeScalarFactory; import org.opentripplanner.framework.graphql.GraphQLUtils; +import org.opentripplanner.framework.graphql.scalar.DateScalarFactory; import org.opentripplanner.framework.graphql.scalar.DurationScalarFactory; import org.opentripplanner.routing.graphfinder.GraphFinder; import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; @@ -45,7 +45,7 @@ public class GqlUtil { public GqlUtil(ZoneId timeZone) { this.dateTimeScalar = DateTimeScalarFactory.createMillisecondsSinceEpochAsDateTimeStringScalar(timeZone); - this.dateScalar = DateScalarFactory.createDateScalar(); + this.dateScalar = DateScalarFactory.createDateScalar("Date"); this.doubleFunctionScalar = DoubleFunctionFactory.createDoubleFunctionScalar(); this.localTimeScalar = LocalTimeScalarFactory.createLocalTimeScalar(); this.timeScalar = TimeScalarFactory.createSecondsSinceMidnightAsTimeObject(); diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DateScalarFactory.java b/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java similarity index 72% rename from src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DateScalarFactory.java rename to src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java index 6d45018ed2a..c19add1e0cb 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DateScalarFactory.java +++ b/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java @@ -1,4 +1,4 @@ -package org.opentripplanner.apis.transmodel.model.scalars; +package org.opentripplanner.framework.graphql.scalar; import graphql.language.StringValue; import graphql.schema.Coercing; @@ -8,6 +8,7 @@ import graphql.schema.GraphQLScalarType; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; public class DateScalarFactory { @@ -15,14 +16,21 @@ public class DateScalarFactory { private static final String DOCUMENTATION = "Local date using the ISO 8601 format: `YYYY-MM-DD`. Example: `2020-05-17`."; - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() + .optionalStart() + .append(DateTimeFormatter.ofPattern("yyyyMMdd")) + .optionalEnd() + .optionalStart() + .append(DateTimeFormatter.ISO_LOCAL_DATE) + .optionalStart() + .toFormatter(); private DateScalarFactory() {} - public static GraphQLScalarType createDateScalar() { + public static GraphQLScalarType createDateScalar(String scalarName) { return GraphQLScalarType .newScalar() - .name("Date") + .name(scalarName) .description(DOCUMENTATION) .coercing( new Coercing() { @@ -33,7 +41,7 @@ public String serialize(Object input) throws CoercingSerializeException { } throw new CoercingSerializeException( - "Only LocalDate is supported to serialize but found " + input + "Only %s is supported to serialize but found %s".formatted(scalarName, input) ); } @@ -43,7 +51,7 @@ public LocalDate parseValue(Object input) throws CoercingParseValueException { return LocalDate.from(FORMATTER.parse((String) input)); } catch (DateTimeParseException e) { throw new CoercingParseValueException( - "Expected type 'Date' but was '" + input + "'." + "Expected type '%s' but was '%s'.".formatted(scalarName, input) ); } } diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 393d5f014db..44426296c29 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2542,6 +2542,13 @@ enum Qualifier { HAIL } +scalar LocalDate + +input ServiceDayFilterInput { + start: LocalDate + end: LocalDate +} + type QueryType { """Fetches an object given its ID""" node( @@ -3421,7 +3428,10 @@ type Route implements Node { bikesAllowed: BikesAllowed """List of patterns which operate on this route""" - patterns: [Pattern] + patterns( + "Filter patterns by the service day they operate on." + serviceDays: ServiceDayFilterInput + ): [Pattern] """List of stops on this route""" stops: [Stop] diff --git a/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java new file mode 100644 index 00000000000..afc4a41f408 --- /dev/null +++ b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java @@ -0,0 +1,23 @@ +package org.opentripplanner.framework.graphql.scalar; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import graphql.schema.GraphQLScalarType; +import java.time.LocalDate; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class DateScalarFactoryTest { + + private static final GraphQLScalarType SCALAR = DateScalarFactory.createDateScalar("Date"); + + @ParameterizedTest + @ValueSource(strings = { "2024-05-23", "20240523" }) + void parse(String input) { + var result = SCALAR.getCoercing().parseValue(input); + assertInstanceOf(LocalDate.class, result); + var date = (LocalDate) result; + assertEquals(LocalDate.of(2024, 5, 23), date); + } +} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/routes-extended.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/routes-extended.json index 8739eab0045..8856972ce4e 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/routes-extended.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/routes-extended.json @@ -11,7 +11,8 @@ }, "mode" : "CARPOOL", "sortOrder" : 12, - "bikesAllowed" : "ALLOWED" + "bikesAllowed" : "ALLOWED", + "patterns" : [ ] }, { "longName" : "Long name for SUBWAY", @@ -23,7 +24,8 @@ }, "mode" : "SUBWAY", "sortOrder" : 2, - "bikesAllowed" : "NO_INFORMATION" + "bikesAllowed" : "NO_INFORMATION", + "patterns" : [ ] }, { "longName" : "Long name for BUS", @@ -35,7 +37,8 @@ }, "mode" : "BUS", "sortOrder" : 3, - "bikesAllowed" : "ALLOWED" + "bikesAllowed" : "ALLOWED", + "patterns" : [ ] }, { "longName" : "Long name for FERRY", @@ -47,7 +50,8 @@ }, "mode" : "FERRY", "sortOrder" : 5, - "bikesAllowed" : "NO_INFORMATION" + "bikesAllowed" : "NO_INFORMATION", + "patterns" : [ ] }, { "longName" : "Long name for COACH", @@ -59,7 +63,8 @@ }, "mode" : "COACH", "sortOrder" : 1, - "bikesAllowed" : "NOT_ALLOWED" + "bikesAllowed" : "NOT_ALLOWED", + "patterns" : [ ] }, { "longName" : "Long name for TRAM", @@ -71,7 +76,8 @@ }, "mode" : "TRAM", "sortOrder" : 4, - "bikesAllowed" : "NOT_ALLOWED" + "bikesAllowed" : "NOT_ALLOWED", + "patterns" : [ ] }, { "longName" : "Long name for CABLE_CAR", @@ -83,7 +89,8 @@ }, "mode" : "CABLE_CAR", "sortOrder" : 7, - "bikesAllowed" : "NOT_ALLOWED" + "bikesAllowed" : "NOT_ALLOWED", + "patterns" : [ ] }, { "longName" : "Long name for FUNICULAR", @@ -95,7 +102,8 @@ }, "mode" : "FUNICULAR", "sortOrder" : 9, - "bikesAllowed" : "ALLOWED" + "bikesAllowed" : "ALLOWED", + "patterns" : [ ] }, { "longName" : "Long name for RAIL", @@ -107,7 +115,8 @@ }, "mode" : "RAIL", "sortOrder" : null, - "bikesAllowed" : "ALLOWED" + "bikesAllowed" : "ALLOWED", + "patterns" : [ ] }, { "longName" : "Long name for MONORAIL", @@ -119,7 +128,8 @@ }, "mode" : "MONORAIL", "sortOrder" : 11, - "bikesAllowed" : "NO_INFORMATION" + "bikesAllowed" : "NO_INFORMATION", + "patterns" : [ ] }, { "longName" : "Long name for GONDOLA", @@ -131,7 +141,8 @@ }, "mode" : "GONDOLA", "sortOrder" : 8, - "bikesAllowed" : "NO_INFORMATION" + "bikesAllowed" : "NO_INFORMATION", + "patterns" : [ ] }, { "longName" : "Long name for TROLLEYBUS", @@ -143,7 +154,8 @@ }, "mode" : "TROLLEYBUS", "sortOrder" : 10, - "bikesAllowed" : "NOT_ALLOWED" + "bikesAllowed" : "NOT_ALLOWED", + "patterns" : [ ] }, { "longName" : "Long name for AIRPLANE", @@ -155,7 +167,8 @@ }, "mode" : "AIRPLANE", "sortOrder" : 6, - "bikesAllowed" : "ALLOWED" + "bikesAllowed" : "ALLOWED", + "patterns" : [ ] }, { "longName" : "Long name for TAXI", @@ -167,7 +180,8 @@ }, "mode" : "TAXI", "sortOrder" : 13, - "bikesAllowed" : "NOT_ALLOWED" + "bikesAllowed" : "NOT_ALLOWED", + "patterns" : [ ] } ] } diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql index 50d89980442..5da7db77286 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql @@ -10,5 +10,8 @@ mode sortOrder bikesAllowed + patterns(serviceDays: {start: "2024-05-23" end: "2024-05-30"}) { + name + } } } \ No newline at end of file From c3cb2d663b0fecca30ab94408956bfea5defb060 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 24 May 2024 13:18:40 +0200 Subject: [PATCH 002/192] Move filtering logic into class --- .../apis/gtfs/PatternByServiceDaysFilter.java | 78 +++++++++++++++++++ .../apis/gtfs/datafetchers/QueryTypeImpl.java | 26 +++---- .../apis/gtfs/datafetchers/RouteImpl.java | 37 +-------- .../apis/gtfs/generated/GraphQLTypes.java | 15 ++++ .../opentripplanner/apis/gtfs/schema.graphqls | 2 + 5 files changed, 112 insertions(+), 46 deletions(-) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java new file mode 100644 index 00000000000..ee42964ae03 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java @@ -0,0 +1,78 @@ +package org.opentripplanner.apis.gtfs; + +import java.time.LocalDate; +import java.util.Collection; +import java.util.Objects; +import java.util.stream.Stream; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDayFilterInput; +import org.opentripplanner.transit.model.network.Route; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.service.TransitService; + +public class PatternByServiceDaysFilter { + + private final TransitService transitService; + private final LocalDate startInclusive; + private final LocalDate endInclusive; + + PatternByServiceDaysFilter( + TransitService transitService, + LocalDate startInclusive, + LocalDate endInclusive + ) { + Objects.requireNonNull(transitService); + this.transitService = transitService; + // optional + this.startInclusive = startInclusive; + this.endInclusive = endInclusive; + } + + public PatternByServiceDaysFilter( + TransitService transitService, + GraphQLServiceDayFilterInput graphQLServiceDays + ) { + this(transitService, graphQLServiceDays.getGraphQLStart(), graphQLServiceDays.getGraphQLEnd()); + } + + public Collection filterPatterns(Collection tripPatterns) { + return tripPatterns.stream().filter(this::hasServicesOnDate).toList(); + } + + public Stream filterRoutes(Stream routeStream) { + return routeStream.filter(r -> { + var patterns = transitService.getPatternsForRoute(r); + return !this.filterPatterns(patterns).isEmpty(); + }); + } + + private boolean hasServicesOnDate(TripPattern pattern) { + return pattern + .scheduledTripsAsStream() + .anyMatch(trip -> { + var dates = transitService + .getCalendarService() + .getServiceDatesForServiceId(trip.getServiceId()); + + var matchesStartInclusive = dates + .stream() + .anyMatch(date -> + date == null || date.isEqual(startInclusive) || date.isAfter(startInclusive) + ); + + var matchesEndInclusive = dates + .stream() + .anyMatch(date -> + date == null || date.isEqual(endInclusive) || date.isBefore(endInclusive) + ); + + return matchesStartInclusive && matchesEndInclusive; + }); + } + + public static boolean hasServiceDayFilter(GraphQLServiceDayFilterInput serviceDays) { + return ( + serviceDays != null && + (serviceDays.getGraphQLStart() != null || serviceDays.getGraphQLEnd() != 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 a4f5cb00e2f..38bf604171c 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -13,20 +13,14 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.DataFetchingEnvironmentImpl; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.GraphQLUtils; +import org.opentripplanner.apis.gtfs.PatternByServiceDaysFilter; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLQueryTypeStopsByRadiusArgs; @@ -46,11 +40,7 @@ import org.opentripplanner.routing.core.FareType; import org.opentripplanner.routing.error.RoutingValidationException; import org.opentripplanner.routing.fares.FareService; -import org.opentripplanner.routing.graphfinder.GraphFinder; -import org.opentripplanner.routing.graphfinder.NearbyStop; -import org.opentripplanner.routing.graphfinder.PatternAtStop; -import org.opentripplanner.routing.graphfinder.PlaceAtDistance; -import org.opentripplanner.routing.graphfinder.PlaceType; +import org.opentripplanner.routing.graphfinder.*; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.service.vehiclerental.VehicleRentalService; @@ -604,6 +594,16 @@ public DataFetcher> routes() { GraphQLUtils.startsWith(route.getLongName(), name, environment.getLocale()) ); } + + if ( + PatternByServiceDaysFilter.hasServiceDayFilter(args.getGraphQLLimitByPatternServiceDays()) + ) { + var filter = new PatternByServiceDaysFilter( + transitService, + args.getGraphQLLimitByPatternServiceDays() + ); + routeStream = filter.filterRoutes(routeStream); + } return routeStream.toList(); }; } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index 4b21728b8b4..17b3cc56c87 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -3,13 +3,13 @@ import graphql.relay.Relay; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; -import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.GraphQLUtils; +import org.opentripplanner.apis.gtfs.PatternByServiceDaysFilter; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; @@ -181,26 +181,15 @@ public DataFetcher> patterns() { var args = new GraphQLTypes.GraphQLRoutePatternsArgs(environment.getArguments()); - var serviceDays = args.getGraphQLServiceDays(); - if ( - serviceDays != null && - (serviceDays.getGraphQLStart() != null || serviceDays.getGraphQLEnd() != null) - ) { - var start = args.getGraphQLServiceDays().getGraphQLStart(); - return patterns - .stream() - .filter(pattern -> - hasServicesOnDate(patterns, transitService, start) - ) - .toList(); + if (PatternByServiceDaysFilter.hasServiceDayFilter(args.getGraphQLServiceDays())) { + var filter = new PatternByServiceDaysFilter(transitService, args.getGraphQLServiceDays()); + return filter.filterPatterns(patterns); } else { return patterns; } }; } - - @Override public DataFetcher shortName() { return environment -> getSource(environment).getShortName(); @@ -264,22 +253,4 @@ private TransitService getTransitService(DataFetchingEnvironment environment) { private Route getSource(DataFetchingEnvironment environment) { return environment.getSource(); } - - private static boolean hasServicesOnDate(Collection patterns, TransitService transitService, LocalDate start) { - return patterns - .stream() - .anyMatch(p -> - p - .scheduledTripsAsStream() - .anyMatch(trip -> { - var dates = transitService - .getCalendarService() - .getServiceDatesForServiceId(trip.getServiceId()); - - return dates - .stream() - .anyMatch(date -> date.isEqual(start) || date.isAfter(start)); - }) - ); - } } 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 de625f15c7b..fcd64389f59 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -2266,6 +2266,7 @@ public static class GraphQLQueryTypeRoutesArgs { private List feeds; private List ids; + private GraphQLServiceDayFilterInput limitByPatternServiceDays; private String name; private List transportModes; @@ -2273,6 +2274,10 @@ public GraphQLQueryTypeRoutesArgs(Map args) { if (args != null) { this.feeds = (List) args.get("feeds"); this.ids = (List) args.get("ids"); + this.limitByPatternServiceDays = + new GraphQLServiceDayFilterInput( + (Map) args.get("limitByPatternServiceDays") + ); this.name = (String) args.get("name"); if (args.get("transportModes") != null) { this.transportModes = @@ -2292,6 +2297,10 @@ public List getGraphQLIds() { return this.ids; } + public GraphQLServiceDayFilterInput getGraphQLLimitByPatternServiceDays() { + return this.limitByPatternServiceDays; + } + public String getGraphQLName() { return this.name; } @@ -2308,6 +2317,12 @@ public void setGraphQLIds(List ids) { this.ids = ids; } + public void setGraphQLLimitByPatternServiceDays( + GraphQLServiceDayFilterInput limitByPatternServiceDays + ) { + this.limitByPatternServiceDays = limitByPatternServiceDays; + } + public void setGraphQLName(String name) { this.name = name; } diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 44426296c29..c4ed7ff40d3 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2707,6 +2707,8 @@ type QueryType { """Only include routes, which use one of these modes""" transportModes: [Mode] + + limitByPatternServiceDays: ServiceDayFilterInput ): [Route] """ From 775a77618804adf2be6097d91d16a193a9437401 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 24 May 2024 14:37:45 +0200 Subject: [PATCH 003/192] Add test for range handling --- .../apis/gtfs/PatternByServiceDaysFilter.java | 10 +++- .../gtfs/PatternByServiceDaysFilterTest.java | 51 +++++++++++++++++++ .../graphql/scalar/DateScalarFactoryTest.java | 8 +++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java index ee42964ae03..b953699656a 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java @@ -4,6 +4,7 @@ import java.util.Collection; import java.util.Objects; import java.util.stream.Stream; +import javax.annotation.Nullable; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDayFilterInput; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; @@ -17,14 +18,19 @@ public class PatternByServiceDaysFilter { PatternByServiceDaysFilter( TransitService transitService, - LocalDate startInclusive, - LocalDate endInclusive + @Nullable LocalDate startInclusive, + @Nullable LocalDate endInclusive ) { Objects.requireNonNull(transitService); this.transitService = transitService; + // optional this.startInclusive = startInclusive; this.endInclusive = endInclusive; + + if (startInclusive != null && endInclusive != null && startInclusive.isAfter(endInclusive)) { + throw new IllegalArgumentException("start must be before end"); + } } public PatternByServiceDaysFilter( diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java new file mode 100644 index 00000000000..8c480119722 --- /dev/null +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java @@ -0,0 +1,51 @@ +package org.opentripplanner.apis.gtfs; + +import static java.time.LocalDate.parse; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.transit.service.TransitService; + +class PatternByServiceDaysFilterTest { + + private static final TransitService EMPTY_SERVICE = new DefaultTransitService(new TransitModel()); + + static List invalidRange() { + return List.of( + Arguments.of(parse("2024-05-02"), parse("2024-05-01")), + Arguments.of(parse("2024-05-03"), parse("2024-05-01")) + ); + } + + @ParameterizedTest + @MethodSource("invalidRange") + void invalidRange(LocalDate start, LocalDate end) { + assertThrows( + IllegalArgumentException.class, + () -> new PatternByServiceDaysFilter(EMPTY_SERVICE, start, end) + ); + } + + static List validRange() { + return List.of( + Arguments.of(parse("2024-05-02"), parse("2024-05-02")), + Arguments.of(parse("2024-05-02"), parse("2024-05-03")), + Arguments.of(null, parse("2024-05-03")), + Arguments.of(parse("2024-05-03"), null), + Arguments.of(null, null) + ); + } + + @ParameterizedTest + @MethodSource("validRange") + void validRange(LocalDate start, LocalDate end) { + assertDoesNotThrow(() -> new PatternByServiceDaysFilter(EMPTY_SERVICE, start, end)); + } +} diff --git a/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java index afc4a41f408..afc86cdf9ba 100644 --- a/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java +++ b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java @@ -2,7 +2,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import graphql.schema.CoercingParseValueException; import graphql.schema.GraphQLScalarType; import java.time.LocalDate; import org.junit.jupiter.params.ParameterizedTest; @@ -20,4 +22,10 @@ void parse(String input) { var date = (LocalDate) result; assertEquals(LocalDate.of(2024, 5, 23), date); } + + @ParameterizedTest + @ValueSource(strings = { "2024-05", "2024", "2024-99-04", "202405-23" }) + void failParsing(String input) { + assertThrows(CoercingParseValueException.class, () -> SCALAR.getCoercing().parseValue(input)); + } } From 7e66dfd009b394878837ac578b7a7b30db63ce98 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 24 May 2024 15:47:44 +0200 Subject: [PATCH 004/192] Add tests --- .../apis/gtfs/PatternByServiceDaysFilter.java | 15 +-- .../gtfs/PatternByServiceDaysFilterTest.java | 94 +++++++++++++++++++ 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java index b953699656a..a53b7842257 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java @@ -59,19 +59,14 @@ private boolean hasServicesOnDate(TripPattern pattern) { .getCalendarService() .getServiceDatesForServiceId(trip.getServiceId()); - var matchesStartInclusive = dates + return dates .stream() .anyMatch(date -> - date == null || date.isEqual(startInclusive) || date.isAfter(startInclusive) + ( + startInclusive == null || date.isEqual(startInclusive) || date.isAfter(startInclusive) + ) && + (endInclusive == null || date.isEqual(endInclusive) || date.isBefore(endInclusive)) ); - - var matchesEndInclusive = dates - .stream() - .anyMatch(date -> - date == null || date.isEqual(endInclusive) || date.isBefore(endInclusive) - ); - - return matchesStartInclusive && matchesEndInclusive; }); } diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java index 8c480119722..76b4a3bb526 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java @@ -2,20 +2,57 @@ import static java.time.LocalDate.parse; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.time.LocalDate; +import java.util.Collection; import java.util.List; +import java.util.Set; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.model.calendar.CalendarService; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.Route; +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.timetable.ScheduledTripTimes; +import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.transit.service.TransitService; class PatternByServiceDaysFilterTest { private static final TransitService EMPTY_SERVICE = new DefaultTransitService(new TransitModel()); + private static final Route ROUTE_1 = TransitModelForTest.route("1").build(); + private static final Trip TRIP = TransitModelForTest.trip("t1").withRoute(ROUTE_1).build(); + private static final TransitModelForTest MODEL = new TransitModelForTest(StopModel.of()); + private static final RegularStop STOP_1 = MODEL.stop("1").build(); + private static final StopPattern STOP_PATTERN = TransitModelForTest.stopPattern(STOP_1, STOP_1); + private static final TripPattern PATTERN_1 = pattern(); + private static final List NOT_REMOVED = List.of(PATTERN_1); + private static final List REMOVED = List.of(); + + private static TripPattern pattern() { + var pattern = TransitModelForTest + .tripPattern("1", ROUTE_1) + .withStopPattern(STOP_PATTERN) + .build(); + + var tt = ScheduledTripTimes + .of() + .withTrip(TRIP) + .withArrivalTimes("10:00 10:05") + .withDepartureTimes("10:00 10:05") + .build(); + pattern.add(tt); + return pattern; + } static List invalidRange() { return List.of( @@ -48,4 +85,61 @@ static List validRange() { void validRange(LocalDate start, LocalDate end) { assertDoesNotThrow(() -> new PatternByServiceDaysFilter(EMPTY_SERVICE, start, end)); } + + static List patternRanges() { + return List.of( + Arguments.of(null, null, NOT_REMOVED), + Arguments.of(null, parse("2024-05-03"), NOT_REMOVED), + Arguments.of(null, parse("2024-05-01"), NOT_REMOVED), + Arguments.of(parse("2024-05-03"), null, NOT_REMOVED), + Arguments.of(parse("2024-05-02"), parse("2024-05-02"), REMOVED), + Arguments.of(parse("2024-05-02"), parse("2024-05-03"), REMOVED), + Arguments.of(parse("2025-01-01"), null, REMOVED), + Arguments.of(parse("2025-01-01"), parse("2025-01-02"), REMOVED), + Arguments.of(null, parse("2023-12-31"), REMOVED), + Arguments.of(parse("2023-12-31"), parse("2024-04-30"), REMOVED) + ); + } + + @ParameterizedTest + @MethodSource("patternRanges") + void filterPatterns(LocalDate start, LocalDate end, List expectedPatterns) { + var model = new TransitModel(); + var service = mockService(model); + + var filter = new PatternByServiceDaysFilter(service, start, end); + + var result = filter.filterPatterns(NOT_REMOVED); + + assertEquals(expectedPatterns, result); + } + + private static TransitService mockService(TransitModel model) { + return new DefaultTransitService(model) { + @Override + public Collection getPatternsForRoute(Route route) { + return Set.of(PATTERN_1); + } + + @Override + public CalendarService getCalendarService() { + return new CalendarService() { + @Override + public Set getServiceIds() { + return Set.of(); + } + + @Override + public Set getServiceDatesForServiceId(FeedScopedId serviceId) { + return Set.of(parse("2024-05-01"), parse("2024-06-01")); + } + + @Override + public Set getServiceIdsOnDate(LocalDate date) { + return Set.of(); + } + }; + } + }; + } } From cbe912dd1fcc9361840105983c04880ef1ebba87 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 24 May 2024 16:45:56 +0200 Subject: [PATCH 005/192] Flesh out tests --- .../apis/gtfs/PatternByServiceDaysFilter.java | 12 +++-- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 17 +++++-- .../gtfs/PatternByServiceDaysFilterTest.java | 46 ++++++++++++++----- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java index a53b7842257..6d6a5e3d8e1 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java @@ -44,11 +44,13 @@ public Collection filterPatterns(Collection tripPatter return tripPatterns.stream().filter(this::hasServicesOnDate).toList(); } - public Stream filterRoutes(Stream routeStream) { - return routeStream.filter(r -> { - var patterns = transitService.getPatternsForRoute(r); - return !this.filterPatterns(patterns).isEmpty(); - }); + public Collection filterRoutes(Stream routeStream) { + return routeStream + .filter(r -> { + var patterns = transitService.getPatternsForRoute(r); + return !this.filterPatterns(patterns).isEmpty(); + }) + .toList(); } private boolean hasServicesOnDate(TripPattern pattern) { 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 38bf604171c..d5f99860e5d 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,14 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.DataFetchingEnvironmentImpl; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; import org.locationtech.jts.geom.Coordinate; @@ -40,7 +47,11 @@ import org.opentripplanner.routing.core.FareType; import org.opentripplanner.routing.error.RoutingValidationException; import org.opentripplanner.routing.fares.FareService; -import org.opentripplanner.routing.graphfinder.*; +import org.opentripplanner.routing.graphfinder.GraphFinder; +import org.opentripplanner.routing.graphfinder.NearbyStop; +import org.opentripplanner.routing.graphfinder.PatternAtStop; +import org.opentripplanner.routing.graphfinder.PlaceAtDistance; +import org.opentripplanner.routing.graphfinder.PlaceType; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.service.vehiclerental.VehicleRentalService; @@ -602,7 +613,7 @@ public DataFetcher> routes() { transitService, args.getGraphQLLimitByPatternServiceDays() ); - routeStream = filter.filterRoutes(routeStream); + routeStream = filter.filterRoutes(routeStream).stream(); } return routeStream.toList(); }; diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java index 76b4a3bb526..32f6a78b2ee 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.opentripplanner.apis.gtfs.PatternByServiceDaysFilterTest.FilterExpectation.NOT_REMOVED; +import static org.opentripplanner.apis.gtfs.PatternByServiceDaysFilterTest.FilterExpectation.REMOVED; import java.time.LocalDate; import java.util.Collection; @@ -35,8 +37,11 @@ class PatternByServiceDaysFilterTest { private static final RegularStop STOP_1 = MODEL.stop("1").build(); private static final StopPattern STOP_PATTERN = TransitModelForTest.stopPattern(STOP_1, STOP_1); private static final TripPattern PATTERN_1 = pattern(); - private static final List NOT_REMOVED = List.of(PATTERN_1); - private static final List REMOVED = List.of(); + + enum FilterExpectation { + REMOVED, + NOT_REMOVED, + } private static TripPattern pattern() { var pattern = TransitModelForTest @@ -86,7 +91,7 @@ void validRange(LocalDate start, LocalDate end) { assertDoesNotThrow(() -> new PatternByServiceDaysFilter(EMPTY_SERVICE, start, end)); } - static List patternRanges() { + static List ranges() { return List.of( Arguments.of(null, null, NOT_REMOVED), Arguments.of(null, parse("2024-05-03"), NOT_REMOVED), @@ -102,20 +107,39 @@ static List patternRanges() { } @ParameterizedTest - @MethodSource("patternRanges") - void filterPatterns(LocalDate start, LocalDate end, List expectedPatterns) { - var model = new TransitModel(); - var service = mockService(model); + @MethodSource("ranges") + void filterPatterns(LocalDate start, LocalDate end, FilterExpectation expectation) { + var service = mockService(); + var filter = new PatternByServiceDaysFilter(service, start, end); + + var filterInput = List.of(PATTERN_1); + var filterOutput = filter.filterPatterns(filterInput); + if (expectation == NOT_REMOVED) { + assertEquals(filterOutput, filterInput); + } else { + assertEquals(List.of(), filterOutput); + } + } + + @ParameterizedTest + @MethodSource("ranges") + void filterRoutes(LocalDate start, LocalDate end, FilterExpectation expectation) { + var service = mockService(); var filter = new PatternByServiceDaysFilter(service, start, end); - var result = filter.filterPatterns(NOT_REMOVED); + var filterInput = List.of(ROUTE_1); + var filterOutput = filter.filterRoutes(filterInput.stream()); - assertEquals(expectedPatterns, result); + if (expectation == NOT_REMOVED) { + assertEquals(filterOutput, filterInput); + } else { + assertEquals(List.of(), filterOutput); + } } - private static TransitService mockService(TransitModel model) { - return new DefaultTransitService(model) { + private static TransitService mockService() { + return new DefaultTransitService(new TransitModel()) { @Override public Collection getPatternsForRoute(Route route) { return Set.of(PATTERN_1); From d6b568fe6210f67c9fa37df9ffcf383e0938bb8b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 24 May 2024 17:22:22 +0200 Subject: [PATCH 006/192] Rename service day to service date --- ....java => PatternByServiceDatesFilter.java} | 33 +++++++++++++---- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 8 ++--- .../apis/gtfs/datafetchers/RouteImpl.java | 6 ++-- .../apis/gtfs/generated/GraphQLTypes.java | 36 +++++++++---------- .../opentripplanner/apis/gtfs/schema.graphqls | 24 ++++++++++--- ...a => PatternByServiceDatesFilterTest.java} | 14 ++++---- 6 files changed, 77 insertions(+), 44 deletions(-) rename src/main/java/org/opentripplanner/apis/gtfs/{PatternByServiceDaysFilter.java => PatternByServiceDatesFilter.java} (65%) rename src/test/java/org/opentripplanner/apis/gtfs/{PatternByServiceDaysFilterTest.java => PatternByServiceDatesFilterTest.java} (90%) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java similarity index 65% rename from src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java rename to src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index 6d6a5e3d8e1..313c44f2647 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -5,18 +5,30 @@ import java.util.Objects; import java.util.stream.Stream; import javax.annotation.Nullable; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDayFilterInput; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDateFilterInput; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.service.TransitService; -public class PatternByServiceDaysFilter { +/** + * Encapsulates the logic to filter patterns by the service dates that they operate on. It also + * has a method to filter routes by checking if their patterns operate on the required days + */ +public class PatternByServiceDatesFilter { private final TransitService transitService; private final LocalDate startInclusive; private final LocalDate endInclusive; - PatternByServiceDaysFilter( + /** + * + * @param transitService + * @param startInclusive The inclusive start date to check the patterns for. If null then no start + * date is defined and this will therefore match all dates. + * @param endInclusive The inclusive end date to check the patterns for. If null then no end date + * is defined this will therefore match all dates. + */ + PatternByServiceDatesFilter( TransitService transitService, @Nullable LocalDate startInclusive, @Nullable LocalDate endInclusive @@ -33,17 +45,24 @@ public class PatternByServiceDaysFilter { } } - public PatternByServiceDaysFilter( + public PatternByServiceDatesFilter( TransitService transitService, - GraphQLServiceDayFilterInput graphQLServiceDays + GraphQLServiceDateFilterInput filterInput ) { - this(transitService, graphQLServiceDays.getGraphQLStart(), graphQLServiceDays.getGraphQLEnd()); + this(transitService, filterInput.getGraphQLStart(), filterInput.getGraphQLEnd()); } + /** + * Filter the patterns by the service dates that it operates on. + */ public Collection filterPatterns(Collection tripPatterns) { return tripPatterns.stream().filter(this::hasServicesOnDate).toList(); } + /** + * Filter the routes by listing all their patterns' service dates and checking if they + * operate on the specified dates. + */ public Collection filterRoutes(Stream routeStream) { return routeStream .filter(r -> { @@ -72,7 +91,7 @@ private boolean hasServicesOnDate(TripPattern pattern) { }); } - public static boolean hasServiceDayFilter(GraphQLServiceDayFilterInput serviceDays) { + public static boolean hasServiceDayFilter(GraphQLServiceDateFilterInput serviceDays) { return ( serviceDays != null && (serviceDays.getGraphQLStart() != null || serviceDays.getGraphQLEnd() != 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 d5f99860e5d..d5033af7da7 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -27,7 +27,7 @@ import org.locationtech.jts.geom.Envelope; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.GraphQLUtils; -import org.opentripplanner.apis.gtfs.PatternByServiceDaysFilter; +import org.opentripplanner.apis.gtfs.PatternByServiceDatesFilter; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLQueryTypeStopsByRadiusArgs; @@ -607,11 +607,11 @@ public DataFetcher> routes() { } if ( - PatternByServiceDaysFilter.hasServiceDayFilter(args.getGraphQLLimitByPatternServiceDays()) + PatternByServiceDatesFilter.hasServiceDayFilter(args.getGraphQLLimitByPatternServiceDates()) ) { - var filter = new PatternByServiceDaysFilter( + var filter = new PatternByServiceDatesFilter( transitService, - args.getGraphQLLimitByPatternServiceDays() + args.getGraphQLLimitByPatternServiceDates() ); routeStream = filter.filterRoutes(routeStream).stream(); } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index 17b3cc56c87..d9b76ab72ef 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -9,7 +9,7 @@ import java.util.stream.Collectors; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.GraphQLUtils; -import org.opentripplanner.apis.gtfs.PatternByServiceDaysFilter; +import org.opentripplanner.apis.gtfs.PatternByServiceDatesFilter; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; @@ -181,8 +181,8 @@ public DataFetcher> patterns() { var args = new GraphQLTypes.GraphQLRoutePatternsArgs(environment.getArguments()); - if (PatternByServiceDaysFilter.hasServiceDayFilter(args.getGraphQLServiceDays())) { - var filter = new PatternByServiceDaysFilter(transitService, args.getGraphQLServiceDays()); + if (PatternByServiceDatesFilter.hasServiceDayFilter(args.getGraphQLServiceDates())) { + var filter = new PatternByServiceDatesFilter(transitService, args.getGraphQLServiceDates()); return filter.filterPatterns(patterns); } else { return patterns; 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 fcd64389f59..04da89080c3 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -2266,7 +2266,7 @@ public static class GraphQLQueryTypeRoutesArgs { private List feeds; private List ids; - private GraphQLServiceDayFilterInput limitByPatternServiceDays; + private GraphQLServiceDateFilterInput limitByPatternServiceDates; private String name; private List transportModes; @@ -2274,9 +2274,9 @@ public GraphQLQueryTypeRoutesArgs(Map args) { if (args != null) { this.feeds = (List) args.get("feeds"); this.ids = (List) args.get("ids"); - this.limitByPatternServiceDays = - new GraphQLServiceDayFilterInput( - (Map) args.get("limitByPatternServiceDays") + this.limitByPatternServiceDates = + new GraphQLServiceDateFilterInput( + (Map) args.get("limitByPatternServiceDates") ); this.name = (String) args.get("name"); if (args.get("transportModes") != null) { @@ -2297,8 +2297,8 @@ public List getGraphQLIds() { return this.ids; } - public GraphQLServiceDayFilterInput getGraphQLLimitByPatternServiceDays() { - return this.limitByPatternServiceDays; + public GraphQLServiceDateFilterInput getGraphQLLimitByPatternServiceDates() { + return this.limitByPatternServiceDates; } public String getGraphQLName() { @@ -2317,10 +2317,10 @@ public void setGraphQLIds(List ids) { this.ids = ids; } - public void setGraphQLLimitByPatternServiceDays( - GraphQLServiceDayFilterInput limitByPatternServiceDays + public void setGraphQLLimitByPatternServiceDates( + GraphQLServiceDateFilterInput limitByPatternServiceDates ) { - this.limitByPatternServiceDays = limitByPatternServiceDays; + this.limitByPatternServiceDates = limitByPatternServiceDates; } public void setGraphQLName(String name) { @@ -2768,21 +2768,21 @@ public void setGraphQLLanguage(String language) { public static class GraphQLRoutePatternsArgs { - private GraphQLServiceDayFilterInput serviceDays; + private GraphQLServiceDateFilterInput serviceDates; public GraphQLRoutePatternsArgs(Map args) { if (args != null) { - this.serviceDays = - new GraphQLServiceDayFilterInput((Map) args.get("serviceDays")); + this.serviceDates = + new GraphQLServiceDateFilterInput((Map) args.get("serviceDates")); } } - public GraphQLServiceDayFilterInput getGraphQLServiceDays() { - return this.serviceDays; + public GraphQLServiceDateFilterInput getGraphQLServiceDates() { + return this.serviceDates; } - public void setGraphQLServiceDays(GraphQLServiceDayFilterInput serviceDays) { - this.serviceDays = serviceDays; + public void setGraphQLServiceDates(GraphQLServiceDateFilterInput serviceDates) { + this.serviceDates = serviceDates; } } @@ -2807,12 +2807,12 @@ public enum GraphQLRoutingErrorCode { WALKING_BETTER_THAN_TRANSIT, } - public static class GraphQLServiceDayFilterInput { + public static class GraphQLServiceDateFilterInput { private java.time.LocalDate end; private java.time.LocalDate start; - public GraphQLServiceDayFilterInput(Map args) { + public GraphQLServiceDateFilterInput(Map args) { if (args != null) { this.end = (java.time.LocalDate) args.get("end"); this.start = (java.time.LocalDate) args.get("start"); diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index c4ed7ff40d3..c495b821965 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -1663,6 +1663,11 @@ type RideHailingEstimate { productName: String } +""" +An ISO-8601-formatted local date, i.e. `2024-05-24` for the 24th of May, 2024. +""" +scalar LocalDate + """ An ISO-8601-formatted datetime with offset, i.e. `2023-06-13T14:30+03:00` for 2:30pm on June 13th 2023 at Helsinki's offset from UTC at that time. @@ -2542,10 +2547,18 @@ enum Qualifier { HAIL } -scalar LocalDate -input ServiceDayFilterInput { +"""Filters entity by their service dates.""" +input ServiceDateFilterInput { + """ + Inclusive start date of the filter. If `null` this means that no `start` filter is applied and all + dates that are before or on `end` are selected. + """ start: LocalDate + """ + Inclusive end date of the filter. If `null` this means that no end filter is applied and all + entities that are after or on `start` are selected. + """ end: LocalDate } @@ -2708,7 +2721,8 @@ type QueryType { """Only include routes, which use one of these modes""" transportModes: [Mode] - limitByPatternServiceDays: ServiceDayFilterInput + """Only include routes whose pattern operates on at least one service date specified by this filter.""" + limitByPatternServiceDates: ServiceDateFilterInput ): [Route] """ @@ -3431,8 +3445,8 @@ type Route implements Node { """List of patterns which operate on this route""" patterns( - "Filter patterns by the service day they operate on." - serviceDays: ServiceDayFilterInput + "Filter patterns by the service dates they operate on." + serviceDates: ServiceDateFilterInput ): [Pattern] """List of stops on this route""" diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java similarity index 90% rename from src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java rename to src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index 32f6a78b2ee..2ef9b22f335 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDaysFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -4,8 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.opentripplanner.apis.gtfs.PatternByServiceDaysFilterTest.FilterExpectation.NOT_REMOVED; -import static org.opentripplanner.apis.gtfs.PatternByServiceDaysFilterTest.FilterExpectation.REMOVED; +import static org.opentripplanner.apis.gtfs.PatternByServiceDatesFilterTest.FilterExpectation.NOT_REMOVED; +import static org.opentripplanner.apis.gtfs.PatternByServiceDatesFilterTest.FilterExpectation.REMOVED; import java.time.LocalDate; import java.util.Collection; @@ -28,7 +28,7 @@ import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.transit.service.TransitService; -class PatternByServiceDaysFilterTest { +class PatternByServiceDatesFilterTest { private static final TransitService EMPTY_SERVICE = new DefaultTransitService(new TransitModel()); private static final Route ROUTE_1 = TransitModelForTest.route("1").build(); @@ -71,7 +71,7 @@ static List invalidRange() { void invalidRange(LocalDate start, LocalDate end) { assertThrows( IllegalArgumentException.class, - () -> new PatternByServiceDaysFilter(EMPTY_SERVICE, start, end) + () -> new PatternByServiceDatesFilter(EMPTY_SERVICE, start, end) ); } @@ -88,7 +88,7 @@ static List validRange() { @ParameterizedTest @MethodSource("validRange") void validRange(LocalDate start, LocalDate end) { - assertDoesNotThrow(() -> new PatternByServiceDaysFilter(EMPTY_SERVICE, start, end)); + assertDoesNotThrow(() -> new PatternByServiceDatesFilter(EMPTY_SERVICE, start, end)); } static List ranges() { @@ -110,7 +110,7 @@ static List ranges() { @MethodSource("ranges") void filterPatterns(LocalDate start, LocalDate end, FilterExpectation expectation) { var service = mockService(); - var filter = new PatternByServiceDaysFilter(service, start, end); + var filter = new PatternByServiceDatesFilter(service, start, end); var filterInput = List.of(PATTERN_1); var filterOutput = filter.filterPatterns(filterInput); @@ -126,7 +126,7 @@ void filterPatterns(LocalDate start, LocalDate end, FilterExpectation expectatio @MethodSource("ranges") void filterRoutes(LocalDate start, LocalDate end, FilterExpectation expectation) { var service = mockService(); - var filter = new PatternByServiceDaysFilter(service, start, end); + var filter = new PatternByServiceDatesFilter(service, start, end); var filterInput = List.of(ROUTE_1); var filterOutput = filter.filterRoutes(filterInput.stream()); From 3f0d492ebc7081f33540f7e487bd4826b9ea7d9c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 24 May 2024 17:38:08 +0200 Subject: [PATCH 007/192] Add example for LocalDate --- magidoc.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/magidoc.mjs b/magidoc.mjs index 4fea5e4e127..73f1a4d8343 100644 --- a/magidoc.mjs +++ b/magidoc.mjs @@ -36,7 +36,8 @@ To learn how to deactivate it, read the queryGenerationFactories: { 'Polyline': '<>', 'GeoJson': '<>', - 'OffsetDateTime': '2024-02-05T18:04:23+01:00' + 'OffsetDateTime': '2024-02-05T18:04:23+01:00', + 'LocalDate': '2024-05-24', }, } }, From 28b078d0ed817067c790f124a2b9e3b76e8786b0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 24 May 2024 17:39:16 +0200 Subject: [PATCH 008/192] Update test --- .../opentripplanner/apis/gtfs/queries/routes-extended.graphql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql index 5da7db77286..0251245d183 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql @@ -10,7 +10,7 @@ mode sortOrder bikesAllowed - patterns(serviceDays: {start: "2024-05-23" end: "2024-05-30"}) { + patterns(serviceDates: {start: "2024-05-23" end: "2024-05-30"}) { name } } From e6971b4d786e3c94b02e52aca1512c81079ac887 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 27 May 2024 13:02:49 +0200 Subject: [PATCH 009/192] Add more test cases --- .../gtfs/PatternByServiceDatesFilter.java | 2 +- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 4 ++- .../apis/gtfs/datafetchers/RouteImpl.java | 2 +- .../gtfs/PatternByServiceDatesFilterTest.java | 30 +++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index 313c44f2647..c7a0314fbb5 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -91,7 +91,7 @@ private boolean hasServicesOnDate(TripPattern pattern) { }); } - public static boolean hasServiceDayFilter(GraphQLServiceDateFilterInput serviceDays) { + public static boolean hasServiceDateFilter(GraphQLServiceDateFilterInput serviceDays) { return ( serviceDays != null && (serviceDays.getGraphQLStart() != null || serviceDays.getGraphQLEnd() != 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 d5033af7da7..0936d98cf74 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -607,7 +607,9 @@ public DataFetcher> routes() { } if ( - PatternByServiceDatesFilter.hasServiceDayFilter(args.getGraphQLLimitByPatternServiceDates()) + PatternByServiceDatesFilter.hasServiceDateFilter( + args.getGraphQLLimitByPatternServiceDates() + ) ) { var filter = new PatternByServiceDatesFilter( transitService, diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index d9b76ab72ef..ac4a485706b 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -181,7 +181,7 @@ public DataFetcher> patterns() { var args = new GraphQLTypes.GraphQLRoutePatternsArgs(environment.getArguments()); - if (PatternByServiceDatesFilter.hasServiceDayFilter(args.getGraphQLServiceDates())) { + if (PatternByServiceDatesFilter.hasServiceDateFilter(args.getGraphQLServiceDates())) { var filter = new PatternByServiceDatesFilter(transitService, args.getGraphQLServiceDates()); return filter.filterPatterns(patterns); } else { diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index 2ef9b22f335..09582d67d3c 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -3,17 +3,22 @@ import static java.time.LocalDate.parse; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.apis.gtfs.PatternByServiceDatesFilterTest.FilterExpectation.NOT_REMOVED; import static org.opentripplanner.apis.gtfs.PatternByServiceDatesFilterTest.FilterExpectation.REMOVED; import java.time.LocalDate; +import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Set; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDateFilterInput; import org.opentripplanner.model.calendar.CalendarService; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -37,6 +42,7 @@ class PatternByServiceDatesFilterTest { private static final RegularStop STOP_1 = MODEL.stop("1").build(); private static final StopPattern STOP_PATTERN = TransitModelForTest.stopPattern(STOP_1, STOP_1); private static final TripPattern PATTERN_1 = pattern(); + private static final LocalDate DATE = LocalDate.parse("2024-05-27"); enum FilterExpectation { REMOVED, @@ -166,4 +172,28 @@ public Set getServiceIdsOnDate(LocalDate date) { } }; } + + public static List noFilterCases() { + var list = new ArrayList(); + list.add(null); + list.add(new GraphQLServiceDateFilterInput(Map.of())); + return list; + } + + @ParameterizedTest + @MethodSource("noFilterCases") + void hasNoServiceDateFilter(GraphQLServiceDateFilterInput input) { + assertFalse(PatternByServiceDatesFilter.hasServiceDateFilter(input)); + } + + public static List> hasFilterCases() { + return List.of(Map.of("start", DATE), Map.of("end", DATE), Map.of("start", DATE, "end", DATE)); + } + + @ParameterizedTest + @MethodSource("hasFilterCases") + void hasServiceDateFilter(Map params) { + var input = new GraphQLServiceDateFilterInput(params); + assertTrue(PatternByServiceDatesFilter.hasServiceDateFilter(input)); + } } From 244da3f314334ee22803325053765208f863d356 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 27 May 2024 14:36:36 +0200 Subject: [PATCH 010/192] Update test case for edge case --- .../apis/gtfs/PatternByServiceDatesFilterTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index 09582d67d3c..7e0cae144ed 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -103,6 +103,7 @@ static List ranges() { Arguments.of(null, parse("2024-05-03"), NOT_REMOVED), Arguments.of(null, parse("2024-05-01"), NOT_REMOVED), Arguments.of(parse("2024-05-03"), null, NOT_REMOVED), + Arguments.of(parse("2024-05-01"), null, NOT_REMOVED), Arguments.of(parse("2024-05-02"), parse("2024-05-02"), REMOVED), Arguments.of(parse("2024-05-02"), parse("2024-05-03"), REMOVED), Arguments.of(parse("2025-01-01"), null, REMOVED), From 994d0b5f36bf2ea51a0ec0ac28792975a1388fc3 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 4 Jun 2024 15:26:01 +0200 Subject: [PATCH 011/192] Be strict about accepting RFC3339 --- .../framework/graphql/scalar/DateScalarFactory.java | 10 +--------- .../graphql/scalar/DateScalarFactoryTest.java | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java b/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java index c19add1e0cb..9b22357f38a 100644 --- a/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java +++ b/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java @@ -8,7 +8,6 @@ import graphql.schema.GraphQLScalarType; import java.time.LocalDate; import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; public class DateScalarFactory { @@ -16,14 +15,7 @@ public class DateScalarFactory { private static final String DOCUMENTATION = "Local date using the ISO 8601 format: `YYYY-MM-DD`. Example: `2020-05-17`."; - private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() - .optionalStart() - .append(DateTimeFormatter.ofPattern("yyyyMMdd")) - .optionalEnd() - .optionalStart() - .append(DateTimeFormatter.ISO_LOCAL_DATE) - .optionalStart() - .toFormatter(); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; private DateScalarFactory() {} diff --git a/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java index afc86cdf9ba..d7a3023ab2b 100644 --- a/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java +++ b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java @@ -15,7 +15,7 @@ class DateScalarFactoryTest { private static final GraphQLScalarType SCALAR = DateScalarFactory.createDateScalar("Date"); @ParameterizedTest - @ValueSource(strings = { "2024-05-23", "20240523" }) + @ValueSource(strings = { "2024-05-23" }) void parse(String input) { var result = SCALAR.getCoercing().parseValue(input); assertInstanceOf(LocalDate.class, result); @@ -24,7 +24,7 @@ void parse(String input) { } @ParameterizedTest - @ValueSource(strings = { "2024-05", "2024", "2024-99-04", "202405-23" }) + @ValueSource(strings = { "2024-05", "2024", "2024-99-04", "202405-23", "20240523" }) void failParsing(String input) { assertThrows(CoercingParseValueException.class, () -> SCALAR.getCoercing().parseValue(input)); } From 782964a500117b0d96ac614e7f6306bee779416e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 4 Jun 2024 16:06:45 +0200 Subject: [PATCH 012/192] Update documenation about service date --- .../opentripplanner/apis/gtfs/schema.graphqls | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index c495b821965..b21d7c863a4 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -2548,7 +2548,13 @@ enum Qualifier { } -"""Filters entity by their service dates.""" +""" +Filters entity by their service dates. + +**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. +""" input ServiceDateFilterInput { """ Inclusive start date of the filter. If `null` this means that no `start` filter is applied and all @@ -2721,7 +2727,13 @@ type QueryType { """Only include routes, which use one of these modes""" transportModes: [Mode] - """Only include routes whose pattern operates on at least one service date specified by this filter.""" + """ + Only include routes whose pattern operates on at least one service date specified by this filter. + + **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. + """ limitByPatternServiceDates: ServiceDateFilterInput ): [Route] @@ -3445,7 +3457,13 @@ type Route implements Node { """List of patterns which operate on this route""" patterns( - "Filter patterns by the service dates they operate on." + """ + Filter patterns by the service dates they operate on. + + **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. + """ serviceDates: ServiceDateFilterInput ): [Pattern] From 1734b8a61334f4bcf3de140be163fd3f93910296 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 10 Jun 2024 14:21:39 +0200 Subject: [PATCH 013/192] Clarify which date formats are allowed --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index b21d7c863a4..3d1d87a5233 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -1665,8 +1665,10 @@ type RideHailingEstimate { """ An ISO-8601-formatted local date, i.e. `2024-05-24` for the 24th of May, 2024. + +ISO-8601 allows many different date formats, however only the most common one - `yyyy-MM-dd` - is accepted. """ -scalar LocalDate +scalar LocalDate @specifiedBy(url: "https://www.iso.org/standard/70907.html") """ An ISO-8601-formatted datetime with offset, i.e. `2023-06-13T14:30+03:00` for 2:30pm on June 13th 2023 at Helsinki's offset from UTC at that time. From 9cfc1d7b8f2bd8f3a36504e70a02465705376cd3 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 10 Jun 2024 14:30:30 +0200 Subject: [PATCH 014/192] Allow overriding of description --- .../opentripplanner/apis/gtfs/GraphQLScalars.java | 3 ++- .../apis/transmodel/support/GqlUtil.java | 2 +- .../graphql/scalar/DateScalarFactory.java | 14 +++++++++++--- .../graphql/scalar/DateScalarFactoryTest.java | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java index a3034cad273..a04e24e391b 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java @@ -113,7 +113,8 @@ public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralExce .build(); public static final GraphQLScalarType LOCAL_DATE_SCALAR = DateScalarFactory.createDateScalar( - "LocalDate" + "LocalDate", + null ); public static final GraphQLScalarType GEOJSON_SCALAR = GraphQLScalarType diff --git a/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java b/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java index 05a8eaa45fa..e94253cf33f 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java @@ -45,7 +45,7 @@ public class GqlUtil { public GqlUtil(ZoneId timeZone) { this.dateTimeScalar = DateTimeScalarFactory.createMillisecondsSinceEpochAsDateTimeStringScalar(timeZone); - this.dateScalar = DateScalarFactory.createDateScalar("Date"); + this.dateScalar = DateScalarFactory.createDateScalar("Date", DateScalarFactory.DESCRIPTION); this.doubleFunctionScalar = DoubleFunctionFactory.createDoubleFunctionScalar(); this.localTimeScalar = LocalTimeScalarFactory.createLocalTimeScalar(); this.timeScalar = TimeScalarFactory.createSecondsSinceMidnightAsTimeObject(); diff --git a/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java b/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java index 9b22357f38a..590dea013a3 100644 --- a/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java +++ b/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java @@ -9,21 +9,29 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import javax.annotation.Nullable; public class DateScalarFactory { - private static final String DOCUMENTATION = + public static final String DESCRIPTION = "Local date using the ISO 8601 format: `YYYY-MM-DD`. Example: `2020-05-17`."; private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; private DateScalarFactory() {} - public static GraphQLScalarType createDateScalar(String scalarName) { + /** + * @param description Nullable description that allows caller to pass in null which leads to the + * description from schema.graphqls to be used. + */ + public static GraphQLScalarType createDateScalar( + String scalarName, + @Nullable String description + ) { return GraphQLScalarType .newScalar() .name(scalarName) - .description(DOCUMENTATION) + .description(description) .coercing( new Coercing() { @Override diff --git a/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java index d7a3023ab2b..02e4b37d7d6 100644 --- a/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java +++ b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java @@ -12,7 +12,7 @@ class DateScalarFactoryTest { - private static final GraphQLScalarType SCALAR = DateScalarFactory.createDateScalar("Date"); + private static final GraphQLScalarType SCALAR = DateScalarFactory.createDateScalar("Date", null); @ParameterizedTest @ValueSource(strings = { "2024-05-23" }) From 591405c5b9f8646ee88fc532889028582ea91ec8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 10 Jun 2024 14:43:47 +0200 Subject: [PATCH 015/192] Instantiate real service instead of mocking it --- .../gtfs/PatternByServiceDatesFilterTest.java | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index 7e0cae144ed..adf7213a5ac 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.apis.gtfs.PatternByServiceDatesFilterTest.FilterExpectation.NOT_REMOVED; import static org.opentripplanner.apis.gtfs.PatternByServiceDatesFilterTest.FilterExpectation.REMOVED; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import java.time.LocalDate; import java.util.ArrayList; @@ -20,6 +21,8 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDateFilterInput; import org.opentripplanner.model.calendar.CalendarService; +import org.opentripplanner.model.calendar.CalendarServiceData; +import org.opentripplanner.model.calendar.impl.CalendarServiceImpl; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; @@ -37,7 +40,12 @@ class PatternByServiceDatesFilterTest { private static final TransitService EMPTY_SERVICE = new DefaultTransitService(new TransitModel()); private static final Route ROUTE_1 = TransitModelForTest.route("1").build(); - private static final Trip TRIP = TransitModelForTest.trip("t1").withRoute(ROUTE_1).build(); + private static final FeedScopedId SERVICE_ID = id("service"); + private static final Trip TRIP = TransitModelForTest + .trip("t1") + .withRoute(ROUTE_1) + .withServiceId(SERVICE_ID) + .build(); private static final TransitModelForTest MODEL = new TransitModelForTest(StopModel.of()); private static final RegularStop STOP_1 = MODEL.stop("1").build(); private static final StopPattern STOP_PATTERN = TransitModelForTest.stopPattern(STOP_1, STOP_1); @@ -146,6 +154,9 @@ void filterRoutes(LocalDate start, LocalDate end, FilterExpectation expectation) } private static TransitService mockService() { + var data = new CalendarServiceData(); + data.putServiceDatesForServiceId(SERVICE_ID, List.of(parse("2024-05-01"), parse("2024-06-01"))); + var service = new CalendarServiceImpl(data); return new DefaultTransitService(new TransitModel()) { @Override public Collection getPatternsForRoute(Route route) { @@ -154,22 +165,7 @@ public Collection getPatternsForRoute(Route route) { @Override public CalendarService getCalendarService() { - return new CalendarService() { - @Override - public Set getServiceIds() { - return Set.of(); - } - - @Override - public Set getServiceDatesForServiceId(FeedScopedId serviceId) { - return Set.of(parse("2024-05-01"), parse("2024-06-01")); - } - - @Override - public Set getServiceIdsOnDate(LocalDate date) { - return Set.of(); - } - }; + return service; } }; } From 5d29a448b96537f3d74da4f67be57832b60737f2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 10 Jun 2024 14:58:04 +0200 Subject: [PATCH 016/192] Rename argument provider methods --- .../apis/gtfs/PatternByServiceDatesFilterTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index adf7213a5ac..4e5779d41f6 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -73,7 +73,7 @@ private static TripPattern pattern() { return pattern; } - static List invalidRange() { + static List invalidRangeCases() { return List.of( Arguments.of(parse("2024-05-02"), parse("2024-05-01")), Arguments.of(parse("2024-05-03"), parse("2024-05-01")) @@ -81,7 +81,7 @@ static List invalidRange() { } @ParameterizedTest - @MethodSource("invalidRange") + @MethodSource("invalidRangeCases") void invalidRange(LocalDate start, LocalDate end) { assertThrows( IllegalArgumentException.class, @@ -89,7 +89,7 @@ void invalidRange(LocalDate start, LocalDate end) { ); } - static List validRange() { + static List validRangeCases() { return List.of( Arguments.of(parse("2024-05-02"), parse("2024-05-02")), Arguments.of(parse("2024-05-02"), parse("2024-05-03")), @@ -100,7 +100,7 @@ static List validRange() { } @ParameterizedTest - @MethodSource("validRange") + @MethodSource("validRangeCases") void validRange(LocalDate start, LocalDate end) { assertDoesNotThrow(() -> new PatternByServiceDatesFilter(EMPTY_SERVICE, start, end)); } From b618e716e073516f93beee42f4ee8916557815a3 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 11 Jun 2024 15:24:43 +0200 Subject: [PATCH 017/192] Pass in functions rather than the whole transit service --- .../gtfs/PatternByServiceDatesFilter.java | 33 ++++++++------ .../apis/gtfs/datafetchers/QueryTypeImpl.java | 4 +- .../apis/gtfs/datafetchers/RouteImpl.java | 2 +- .../gtfs/PatternByServiceDatesFilterTest.java | 43 ++++++------------- 4 files changed, 35 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index c7a0314fbb5..ee9b206bc09 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -3,11 +3,13 @@ import java.time.LocalDate; import java.util.Collection; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Stream; import javax.annotation.Nullable; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDateFilterInput; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.service.TransitService; /** @@ -16,26 +18,26 @@ */ public class PatternByServiceDatesFilter { - private final TransitService transitService; private final LocalDate startInclusive; private final LocalDate endInclusive; + private final Function> getPatternsForRoute; + private final Function> getServiceDatesForTrip; /** * - * @param transitService * @param startInclusive The inclusive start date to check the patterns for. If null then no start * date is defined and this will therefore match all dates. * @param endInclusive The inclusive end date to check the patterns for. If null then no end date * is defined this will therefore match all dates. */ PatternByServiceDatesFilter( - TransitService transitService, @Nullable LocalDate startInclusive, - @Nullable LocalDate endInclusive + @Nullable LocalDate endInclusive, + Function> getPatternsForRoute, + Function> getServiceDatesForTrip ) { - Objects.requireNonNull(transitService); - this.transitService = transitService; - + this.getPatternsForRoute = Objects.requireNonNull(getPatternsForRoute); + this.getServiceDatesForTrip = Objects.requireNonNull(getServiceDatesForTrip); // optional this.startInclusive = startInclusive; this.endInclusive = endInclusive; @@ -46,10 +48,15 @@ public class PatternByServiceDatesFilter { } public PatternByServiceDatesFilter( - TransitService transitService, - GraphQLServiceDateFilterInput filterInput + GraphQLServiceDateFilterInput filterInput, + TransitService transitService ) { - this(transitService, filterInput.getGraphQLStart(), filterInput.getGraphQLEnd()); + this( + filterInput.getGraphQLStart(), + filterInput.getGraphQLEnd(), + transitService::getPatternsForRoute, + trip -> transitService.getCalendarService().getServiceDatesForServiceId(trip.getServiceId()) + ); } /** @@ -66,7 +73,7 @@ public Collection filterPatterns(Collection tripPatter public Collection filterRoutes(Stream routeStream) { return routeStream .filter(r -> { - var patterns = transitService.getPatternsForRoute(r); + var patterns = getPatternsForRoute.apply(r); return !this.filterPatterns(patterns).isEmpty(); }) .toList(); @@ -76,9 +83,7 @@ private boolean hasServicesOnDate(TripPattern pattern) { return pattern .scheduledTripsAsStream() .anyMatch(trip -> { - var dates = transitService - .getCalendarService() - .getServiceDatesForServiceId(trip.getServiceId()); + var dates = getServiceDatesForTrip.apply(trip); return dates .stream() 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 0936d98cf74..6aafa780bf7 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -612,8 +612,8 @@ public DataFetcher> routes() { ) ) { var filter = new PatternByServiceDatesFilter( - transitService, - args.getGraphQLLimitByPatternServiceDates() + args.getGraphQLLimitByPatternServiceDates(), + transitService ); routeStream = filter.filterRoutes(routeStream).stream(); } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index ac4a485706b..596936dfdb5 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -182,7 +182,7 @@ public DataFetcher> patterns() { var args = new GraphQLTypes.GraphQLRoutePatternsArgs(environment.getArguments()); if (PatternByServiceDatesFilter.hasServiceDateFilter(args.getGraphQLServiceDates())) { - var filter = new PatternByServiceDatesFilter(transitService, args.getGraphQLServiceDates()); + var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService); return filter.filterPatterns(patterns); } else { return patterns; diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index 4e5779d41f6..be67f3c90eb 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -12,17 +12,12 @@ import java.time.LocalDate; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Set; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDateFilterInput; -import org.opentripplanner.model.calendar.CalendarService; -import org.opentripplanner.model.calendar.CalendarServiceData; -import org.opentripplanner.model.calendar.impl.CalendarServiceImpl; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; @@ -31,14 +26,10 @@ import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.timetable.ScheduledTripTimes; import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; -import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.transit.service.TransitService; class PatternByServiceDatesFilterTest { - private static final TransitService EMPTY_SERVICE = new DefaultTransitService(new TransitModel()); private static final Route ROUTE_1 = TransitModelForTest.route("1").build(); private static final FeedScopedId SERVICE_ID = id("service"); private static final Trip TRIP = TransitModelForTest @@ -85,7 +76,7 @@ static List invalidRangeCases() { void invalidRange(LocalDate start, LocalDate end) { assertThrows( IllegalArgumentException.class, - () -> new PatternByServiceDatesFilter(EMPTY_SERVICE, start, end) + () -> new PatternByServiceDatesFilter(start, end, r -> List.of(), d -> List.of()) ); } @@ -102,7 +93,9 @@ static List validRangeCases() { @ParameterizedTest @MethodSource("validRangeCases") void validRange(LocalDate start, LocalDate end) { - assertDoesNotThrow(() -> new PatternByServiceDatesFilter(EMPTY_SERVICE, start, end)); + assertDoesNotThrow(() -> + new PatternByServiceDatesFilter(start, end, r -> List.of(), d -> List.of()) + ); } static List ranges() { @@ -124,8 +117,7 @@ static List ranges() { @ParameterizedTest @MethodSource("ranges") void filterPatterns(LocalDate start, LocalDate end, FilterExpectation expectation) { - var service = mockService(); - var filter = new PatternByServiceDatesFilter(service, start, end); + var filter = mockFilter(start, end); var filterInput = List.of(PATTERN_1); var filterOutput = filter.filterPatterns(filterInput); @@ -140,8 +132,7 @@ void filterPatterns(LocalDate start, LocalDate end, FilterExpectation expectatio @ParameterizedTest @MethodSource("ranges") void filterRoutes(LocalDate start, LocalDate end, FilterExpectation expectation) { - var service = mockService(); - var filter = new PatternByServiceDatesFilter(service, start, end); + var filter = mockFilter(start, end); var filterInput = List.of(ROUTE_1); var filterOutput = filter.filterRoutes(filterInput.stream()); @@ -153,21 +144,13 @@ void filterRoutes(LocalDate start, LocalDate end, FilterExpectation expectation) } } - private static TransitService mockService() { - var data = new CalendarServiceData(); - data.putServiceDatesForServiceId(SERVICE_ID, List.of(parse("2024-05-01"), parse("2024-06-01"))); - var service = new CalendarServiceImpl(data); - return new DefaultTransitService(new TransitModel()) { - @Override - public Collection getPatternsForRoute(Route route) { - return Set.of(PATTERN_1); - } - - @Override - public CalendarService getCalendarService() { - return service; - } - }; + private static PatternByServiceDatesFilter mockFilter(LocalDate start, LocalDate end) { + return new PatternByServiceDatesFilter( + start, + end, + route -> List.of(PATTERN_1), + trip -> List.of(parse("2024-05-01"), parse("2024-06-01")) + ); } public static List noFilterCases() { From 4ad42e77ac33da5de3d85adc04a7c391af24757b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 11 Jun 2024 15:32:57 +0200 Subject: [PATCH 018/192] Resolve merge conflicts --- .../apis/gtfs/generated/GraphQLTypes.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index 3c187ca3bbe..fee9eb96702 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -3448,6 +3448,7 @@ public static class GraphQLQueryTypeRoutesArgs { private List feeds; private List ids; + private GraphQLServiceDateFilterInput limitByPatternServiceDates; private String name; private List transportModes; @@ -3455,6 +3456,10 @@ public GraphQLQueryTypeRoutesArgs(Map args) { if (args != null) { this.feeds = (List) args.get("feeds"); this.ids = (List) args.get("ids"); + this.limitByPatternServiceDates = + new GraphQLServiceDateFilterInput( + (Map) args.get("limitByPatternServiceDates") + ); this.name = (String) args.get("name"); if (args.get("transportModes") != null) { this.transportModes = @@ -3474,6 +3479,10 @@ public List getGraphQLIds() { return this.ids; } + public GraphQLServiceDateFilterInput getGraphQLLimitByPatternServiceDates() { + return this.limitByPatternServiceDates; + } + public String getGraphQLName() { return this.name; } @@ -3490,6 +3499,12 @@ public void setGraphQLIds(List ids) { this.ids = ids; } + public void setGraphQLLimitByPatternServiceDates( + GraphQLServiceDateFilterInput limitByPatternServiceDates + ) { + this.limitByPatternServiceDates = limitByPatternServiceDates; + } + public void setGraphQLName(String name) { this.name = name; } @@ -3933,6 +3948,26 @@ public void setGraphQLLanguage(String language) { } } + public static class GraphQLRoutePatternsArgs { + + private GraphQLServiceDateFilterInput serviceDates; + + public GraphQLRoutePatternsArgs(Map args) { + if (args != null) { + this.serviceDates = + new GraphQLServiceDateFilterInput((Map) args.get("serviceDates")); + } + } + + public GraphQLServiceDateFilterInput getGraphQLServiceDates() { + return this.serviceDates; + } + + public void setGraphQLServiceDates(GraphQLServiceDateFilterInput serviceDates) { + this.serviceDates = serviceDates; + } + } + /** Entities that are relevant for routes that can contain alerts */ public enum GraphQLRouteAlertType { AGENCY, @@ -4094,6 +4129,35 @@ public void setGraphQLDestinationScooterPolicy( } } + public static class GraphQLServiceDateFilterInput { + + private java.time.LocalDate end; + private java.time.LocalDate start; + + public GraphQLServiceDateFilterInput(Map args) { + if (args != null) { + this.end = (java.time.LocalDate) args.get("end"); + this.start = (java.time.LocalDate) args.get("start"); + } + } + + public java.time.LocalDate getGraphQLEnd() { + return this.end; + } + + public java.time.LocalDate getGraphQLStart() { + return this.start; + } + + public void setGraphQLEnd(java.time.LocalDate end) { + this.end = end; + } + + public void setGraphQLStart(java.time.LocalDate start) { + this.start = start; + } + } + public static class GraphQLStopAlertsArgs { private List types; From 3c3bd18df7264042cb5059c2845b98013a714ecb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 11 Jun 2024 15:45:47 +0200 Subject: [PATCH 019/192] Throw exception if both start/end are null --- .../apis/gtfs/PatternByServiceDatesFilter.java | 6 +++++- .../apis/gtfs/PatternByServiceDatesFilterTest.java | 5 ++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index ee9b206bc09..0867e6ead54 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -42,7 +42,11 @@ public class PatternByServiceDatesFilter { this.startInclusive = startInclusive; this.endInclusive = endInclusive; - if (startInclusive != null && endInclusive != null && startInclusive.isAfter(endInclusive)) { + if (startInclusive == null && endInclusive == null) { + throw new IllegalArgumentException("startInclusive and endInclusive cannot be both null"); + } else if ( + startInclusive != null && endInclusive != null && startInclusive.isAfter(endInclusive) + ) { throw new IllegalArgumentException("start must be before end"); } } diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index be67f3c90eb..b9ad1c4973a 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -66,6 +66,7 @@ private static TripPattern pattern() { static List invalidRangeCases() { return List.of( + Arguments.of(null, null), Arguments.of(parse("2024-05-02"), parse("2024-05-01")), Arguments.of(parse("2024-05-03"), parse("2024-05-01")) ); @@ -85,8 +86,7 @@ static List validRangeCases() { Arguments.of(parse("2024-05-02"), parse("2024-05-02")), Arguments.of(parse("2024-05-02"), parse("2024-05-03")), Arguments.of(null, parse("2024-05-03")), - Arguments.of(parse("2024-05-03"), null), - Arguments.of(null, null) + Arguments.of(parse("2024-05-03"), null) ); } @@ -100,7 +100,6 @@ void validRange(LocalDate start, LocalDate end) { static List ranges() { return List.of( - Arguments.of(null, null, NOT_REMOVED), Arguments.of(null, parse("2024-05-03"), NOT_REMOVED), Arguments.of(null, parse("2024-05-01"), NOT_REMOVED), Arguments.of(parse("2024-05-03"), null, NOT_REMOVED), From 0a8d03cf8cbc2b1a3a185a9b94a002f2812403d4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 11 Jun 2024 15:54:10 +0200 Subject: [PATCH 020/192] Rename and deprecate --- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 11 ++------ .../apis/gtfs/generated/GraphQLTypes.java | 27 +++++++++---------- .../opentripplanner/apis/gtfs/schema.graphqls | 4 +-- 3 files changed, 16 insertions(+), 26 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 6713f790149..7a644da4be8 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -611,15 +611,8 @@ public DataFetcher> routes() { ); } - if ( - PatternByServiceDatesFilter.hasServiceDateFilter( - args.getGraphQLLimitByPatternServiceDates() - ) - ) { - var filter = new PatternByServiceDatesFilter( - args.getGraphQLLimitByPatternServiceDates(), - transitService - ); + if (PatternByServiceDatesFilter.hasServiceDateFilter(args.getGraphQLServiceDates())) { + var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService); routeStream = filter.filterRoutes(routeStream).stream(); } return routeStream.toList(); 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 fee9eb96702..9f6a237f7d9 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; @@ -3448,19 +3449,17 @@ public static class GraphQLQueryTypeRoutesArgs { private List feeds; private List ids; - private GraphQLServiceDateFilterInput limitByPatternServiceDates; private String name; + private GraphQLServiceDateFilterInput serviceDates; private List transportModes; public GraphQLQueryTypeRoutesArgs(Map args) { if (args != null) { this.feeds = (List) args.get("feeds"); this.ids = (List) args.get("ids"); - this.limitByPatternServiceDates = - new GraphQLServiceDateFilterInput( - (Map) args.get("limitByPatternServiceDates") - ); this.name = (String) args.get("name"); + this.serviceDates = + new GraphQLServiceDateFilterInput((Map) args.get("serviceDates")); if (args.get("transportModes") != null) { this.transportModes = ((List) args.get("transportModes")).stream() @@ -3479,14 +3478,14 @@ public List getGraphQLIds() { return this.ids; } - public GraphQLServiceDateFilterInput getGraphQLLimitByPatternServiceDates() { - return this.limitByPatternServiceDates; - } - public String getGraphQLName() { return this.name; } + public GraphQLServiceDateFilterInput getGraphQLServiceDates() { + return this.serviceDates; + } + public List getGraphQLTransportModes() { return this.transportModes; } @@ -3499,16 +3498,14 @@ public void setGraphQLIds(List ids) { this.ids = ids; } - public void setGraphQLLimitByPatternServiceDates( - GraphQLServiceDateFilterInput limitByPatternServiceDates - ) { - this.limitByPatternServiceDates = limitByPatternServiceDates; - } - public void setGraphQLName(String name) { this.name = name; } + public void setGraphQLServiceDates(GraphQLServiceDateFilterInput serviceDates) { + this.serviceDates = serviceDates; + } + public void setGraphQLTransportModes(List transportModes) { this.transportModes = transportModes; } diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 64ea183fadc..8ca0ac189cb 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3606,7 +3606,7 @@ type QueryType { """Get all routes""" routes( """Only return routes with these ids""" - ids: [String] + ids: [String] @deprecated(reason: "Since it is hard to reason about the ID filter being combined with others in this resolver, it will be moved to a separate one.") """Only return routes with these feedIds""" feeds: [String] @@ -3624,7 +3624,7 @@ type QueryType { 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. """ - limitByPatternServiceDates: ServiceDateFilterInput + serviceDates: ServiceDateFilterInput ): [Route] """ From baa1a75e2fea4f4b7977151d1e34545f3abed165 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 10 Jun 2024 17:51:58 +0200 Subject: [PATCH 021/192] Create geocoder with Dagger --- .../ext/geocoder/GeocoderResource.java | 17 +---------------- .../ext/geocoder/LuceneIndex.java | 2 ++ .../org/opentripplanner/apis/APIEndpoints.java | 2 -- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java b/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java index f5d1f950632..39a6177ef68 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java @@ -3,7 +3,6 @@ import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Context; @@ -28,21 +27,7 @@ public class GeocoderResource { private final OtpServerRequestContext serverContext; public GeocoderResource(@Context OtpServerRequestContext requestContext) { - serverContext = requestContext; - } - - /** - * This class is only here for backwards-compatibility. It will be removed in the future. - */ - @Path("/routers/{ignoreRouterId}/geocode") - public static class GeocoderResourceOldPath extends GeocoderResource { - - public GeocoderResourceOldPath( - @Context OtpServerRequestContext serverContext, - @PathParam("ignoreRouterId") String ignore - ) { - super(serverContext); - } + luceneIndex = Objects.requireNonNull(requestContext.lucenceIndex()); } /** diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java index e0ea8ba36b9..90b17f849de 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java @@ -69,6 +69,8 @@ public LuceneIndex(TransitService transitService) { this.transitService = transitService; this.stopClusterMapper = new StopClusterMapper(transitService); + LOG.info("Creating geocoder lucene index"); + this.analyzer = new PerFieldAnalyzerWrapper( new StandardAnalyzer(), diff --git a/src/main/java/org/opentripplanner/apis/APIEndpoints.java b/src/main/java/org/opentripplanner/apis/APIEndpoints.java index fe8db5b3911..b6ad08ea4d9 100644 --- a/src/main/java/org/opentripplanner/apis/APIEndpoints.java +++ b/src/main/java/org/opentripplanner/apis/APIEndpoints.java @@ -61,8 +61,6 @@ private APIEndpoints() { addIfEnabled(SandboxAPIMapboxVectorTilesApi, VectorTilesResource.class); addIfEnabled(SandboxAPIParkAndRideApi, ParkAndRideResource.class); addIfEnabled(SandboxAPIGeocoder, GeocoderResource.class); - // scheduled to be removed and only here for backwards compatibility - addIfEnabled(SandboxAPIGeocoder, GeocoderResource.GeocoderResourceOldPath.class); // scheduled to be removed addIfEnabled(APIBikeRental, BikeRental.class); From f554b8f645c17fe109bd9d7a98a9f1ce2e1ea8ad Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 10 Jun 2024 16:08:53 +0200 Subject: [PATCH 022/192] Add lucene index to OTP server context --- .../ext/geocoder/GeocoderResource.java | 14 +++++++------- .../ext/geocoder/LuceneIndex.java | 13 ------------- .../opentripplanner/routing/graph/Graph.java | 10 ---------- .../standalone/api/OtpServerRequestContext.java | 4 ++++ .../configure/ConstructApplication.java | 2 -- .../configure/ConstructApplicationModule.java | 7 +++++-- .../server/DefaultServerRequestContext.java | 17 ++++++++++++++--- .../org/opentripplanner/TestServerContext.java | 2 +- .../mapping/TripRequestMapperTest.java | 4 +--- .../transit/speed_test/SpeedTest.java | 2 +- 10 files changed, 33 insertions(+), 42 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java b/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java index 39a6177ef68..26aabb6bc29 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java @@ -24,10 +24,10 @@ @Produces(MediaType.APPLICATION_JSON) public class GeocoderResource { - private final OtpServerRequestContext serverContext; + private final LuceneIndex luceneIndex; public GeocoderResource(@Context OtpServerRequestContext requestContext) { - luceneIndex = Objects.requireNonNull(requestContext.lucenceIndex()); + luceneIndex = requestContext.lucenceIndex(); } /** @@ -56,7 +56,7 @@ public Response textSearch( @GET @Path("stopClusters") public Response stopClusters(@QueryParam("query") String query) { - var clusters = LuceneIndex.forServer(serverContext).queryStopClusters(query).toList(); + var clusters = luceneIndex.queryStopClusters(query).toList(); return Response.status(Response.Status.OK).entity(clusters).build(); } @@ -81,8 +81,8 @@ private List query( } private Collection queryStopLocations(String query, boolean autocomplete) { - return LuceneIndex - .forServer(serverContext) + return + luceneIndex .queryStopLocations(query, autocomplete) .map(sl -> new SearchResult( @@ -96,8 +96,8 @@ private Collection queryStopLocations(String query, boolean autoco } private Collection queryStations(String query, boolean autocomplete) { - return LuceneIndex - .forServer(serverContext) + return + luceneIndex .queryStopLocationGroups(query, autocomplete) .map(sc -> new SearchResult( diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java index 90b17f849de..f7702ad760c 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java @@ -42,7 +42,6 @@ import org.apache.lucene.store.ByteBuffersDirectory; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.i18n.I18NString; -import org.opentripplanner.standalone.api.OtpServerRequestContext; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.site.StopLocationsGroup; @@ -146,18 +145,6 @@ public LuceneIndex(TransitService transitService) { } } - public static synchronized LuceneIndex forServer(OtpServerRequestContext serverContext) { - var graph = serverContext.graph(); - var existingIndex = graph.getLuceneIndex(); - if (existingIndex != null) { - return existingIndex; - } - - var newIndex = new LuceneIndex(serverContext.transitService()); - graph.setLuceneIndex(newIndex); - return newIndex; - } - public Stream queryStopLocations(String query, boolean autocomplete) { return matchingDocuments(StopLocation.class, query, autocomplete) .map(document -> transitService.getStopLocation(FeedScopedId.parse(document.get(ID)))); diff --git a/src/main/java/org/opentripplanner/routing/graph/Graph.java b/src/main/java/org/opentripplanner/routing/graph/Graph.java index 4b6c9938317..926d524bad2 100644 --- a/src/main/java/org/opentripplanner/routing/graph/Graph.java +++ b/src/main/java/org/opentripplanner/routing/graph/Graph.java @@ -16,7 +16,6 @@ import javax.annotation.Nullable; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.ext.dataoverlay.configuration.DataOverlayParameterBindings; -import org.opentripplanner.ext.geocoder.LuceneIndex; import org.opentripplanner.framework.geometry.CompactElevationProfile; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.model.calendar.openinghours.OpeningHoursCalendarService; @@ -136,7 +135,6 @@ public class Graph implements Serializable { * creating the data overlay context when routing. */ public DataOverlayParameterBindings dataOverlayParameterBindings; - private LuceneIndex luceneIndex; @Inject public Graph( @@ -384,14 +382,6 @@ public void setFareService(FareService fareService) { this.fareService = fareService; } - public LuceneIndex getLuceneIndex() { - return luceneIndex; - } - - public void setLuceneIndex(LuceneIndex luceneIndex) { - this.luceneIndex = luceneIndex; - } - private void indexIfNotIndexed(StopModel stopModel) { if (streetIndex == null) { index(stopModel); diff --git a/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java b/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java index 6552d82770f..3a577611617 100644 --- a/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java +++ b/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java @@ -8,6 +8,7 @@ import org.opentripplanner.ext.dataoverlay.routing.DataOverlayContext; import org.opentripplanner.ext.emissions.EmissionsService; import org.opentripplanner.ext.flex.FlexParameters; +import org.opentripplanner.ext.geocoder.LuceneIndex; import org.opentripplanner.ext.ridehailing.RideHailingService; import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; import org.opentripplanner.framework.application.OTPFeature; @@ -136,4 +137,7 @@ default DataOverlayContext dataOverlayContext(RouteRequest request) { ) ); } + + @Nullable + LuceneIndex lucenceIndex(); } diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java index b1bc6888753..db4a4af7fbd 100644 --- a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java +++ b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java @@ -5,7 +5,6 @@ import org.opentripplanner.apis.transmodel.TransmodelAPI; import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.ext.emissions.EmissionsDataModel; -import org.opentripplanner.ext.geocoder.LuceneIndex; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; import org.opentripplanner.framework.application.LogMDCSupport; import org.opentripplanner.framework.application.OTPFeature; @@ -181,7 +180,6 @@ private void setupTransitRoutingServer() { if (OTPFeature.SandboxAPIGeocoder.isOn()) { LOG.info("Creating debug client geocoder lucene index"); - LuceneIndex.forServer(createServerContext()); } } diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java index eb244ce726c..6c830054c49 100644 --- a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java +++ b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java @@ -7,6 +7,7 @@ import javax.annotation.Nullable; import org.opentripplanner.astar.spi.TraverseVisitor; import org.opentripplanner.ext.emissions.EmissionsService; +import org.opentripplanner.ext.geocoder.LuceneIndex; import org.opentripplanner.ext.interactivelauncher.api.LauncherRequestDecorator; import org.opentripplanner.ext.ridehailing.RideHailingService; import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; @@ -40,7 +41,8 @@ OtpServerRequestContext providesServerContext( StreetLimitationParametersService streetLimitationParametersService, @Nullable TraverseVisitor traverseVisitor, EmissionsService emissionsService, - LauncherRequestDecorator launcherRequestDecorator + LauncherRequestDecorator launcherRequestDecorator, + @Nullable LuceneIndex luceneIndex ) { var defaultRequest = launcherRequestDecorator.intercept(routerConfig.routingRequestDefaults()); @@ -60,7 +62,8 @@ OtpServerRequestContext providesServerContext( rideHailingServices, stopConsolidationService, streetLimitationParametersService, - traverseVisitor + traverseVisitor, + luceneIndex ); } diff --git a/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java b/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java index 7a4ccea9247..6b822506fd9 100644 --- a/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java +++ b/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java @@ -7,6 +7,7 @@ import org.opentripplanner.astar.spi.TraverseVisitor; import org.opentripplanner.ext.emissions.EmissionsService; import org.opentripplanner.ext.flex.FlexParameters; +import org.opentripplanner.ext.geocoder.LuceneIndex; import org.opentripplanner.ext.ridehailing.RideHailingService; import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; import org.opentripplanner.inspector.raster.TileRendererManager; @@ -49,6 +50,7 @@ public class DefaultServerRequestContext implements OtpServerRequestContext { private final EmissionsService emissionsService; private final StopConsolidationService stopConsolidationService; private final StreetLimitationParametersService streetLimitationParametersService; + private final LuceneIndex luceneIndex; /** * Make sure all mutable components are copied/cloned before calling this constructor. @@ -70,7 +72,8 @@ private DefaultServerRequestContext( StopConsolidationService stopConsolidationService, StreetLimitationParametersService streetLimitationParametersService, FlexParameters flexParameters, - TraverseVisitor traverseVisitor + TraverseVisitor traverseVisitor, + @Nullable LuceneIndex luceneIndex ) { this.graph = graph; this.transitService = transitService; @@ -89,6 +92,7 @@ private DefaultServerRequestContext( this.emissionsService = emissionsService; this.stopConsolidationService = stopConsolidationService; this.streetLimitationParametersService = streetLimitationParametersService; + this.luceneIndex = luceneIndex; } /** @@ -110,7 +114,8 @@ public static DefaultServerRequestContext create( List rideHailingServices, @Nullable StopConsolidationService stopConsolidationService, StreetLimitationParametersService streetLimitationParametersService, - @Nullable TraverseVisitor traverseVisitor + @Nullable TraverseVisitor traverseVisitor, + @Nullable LuceneIndex luceneIndex ) { return new DefaultServerRequestContext( graph, @@ -129,7 +134,7 @@ public static DefaultServerRequestContext create( stopConsolidationService, streetLimitationParametersService, flexParameters, - traverseVisitor + traverseVisitor, luceneIndex ); } @@ -235,6 +240,12 @@ public VectorTileConfig vectorTileConfig() { return vectorTileConfig; } + @Nullable + @Override + public LuceneIndex lucenceIndex() { + return luceneIndex; + } + @Override public EmissionsService emissionsService() { return emissionsService; diff --git a/src/test/java/org/opentripplanner/TestServerContext.java b/src/test/java/org/opentripplanner/TestServerContext.java index 2f4ded1121d..fe265f30fdc 100644 --- a/src/test/java/org/opentripplanner/TestServerContext.java +++ b/src/test/java/org/opentripplanner/TestServerContext.java @@ -55,7 +55,7 @@ public static OtpServerRequestContext createServerContext( List.of(), null, createStreetLimitationParametersService(), - null + null, null ); creatTransitLayerForRaptor(transitModel, routerConfig.transitTuningConfig()); return context; diff --git a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java index de6b48c5ef4..d18b94e8a20 100644 --- a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java @@ -15,12 +15,10 @@ import io.micrometer.core.instrument.Metrics; import java.time.Duration; import java.time.LocalDate; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; @@ -146,7 +144,7 @@ void setup() { List.of(), null, new DefaultStreetLimitationParametersService(new StreetLimitationParameters()), - null + null, null ), null, transitService diff --git a/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java b/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java index e045f49c01d..41fb0311eb5 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java @@ -121,7 +121,7 @@ public SpeedTest( List.of(), null, TestServerContext.createStreetLimitationParametersService(), - null + null, null ); // Creating transitLayerForRaptor should be integrated into the TransitModel, but for now // we do it manually here From 9e21b04911d9761f70e080da0bc2ffc5e5e694b1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 10 Jun 2024 18:14:32 +0200 Subject: [PATCH 023/192] Wire up Geocoder with Dagger --- .../ext/geocoder/GeocoderResource.java | 6 ++-- .../ext/geocoder/LuceneIndex.java | 4 +++ .../geocoder/configure/GeocoderModule.java | 28 +++++++++++++++++++ .../ConstructApplicationFactory.java | 6 ++++ .../server/DefaultServerRequestContext.java | 3 +- .../opentripplanner/TestServerContext.java | 3 +- .../mapping/TripRequestMapperTest.java | 3 +- .../transit/speed_test/SpeedTest.java | 3 +- 8 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java b/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java index 26aabb6bc29..c9cd199c557 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java @@ -81,8 +81,7 @@ private List query( } private Collection queryStopLocations(String query, boolean autocomplete) { - return - luceneIndex + return luceneIndex .queryStopLocations(query, autocomplete) .map(sl -> new SearchResult( @@ -96,8 +95,7 @@ private Collection queryStopLocations(String query, boolean autoco } private Collection queryStations(String query, boolean autocomplete) { - return - luceneIndex + return luceneIndex .queryStopLocationGroups(query, autocomplete) .map(sc -> new SearchResult( diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java index f7702ad760c..e4de7b4e5c4 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java @@ -46,9 +46,13 @@ import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.site.StopLocationsGroup; import org.opentripplanner.transit.service.TransitService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LuceneIndex implements Serializable { + private static final Logger LOG = LoggerFactory.getLogger(LuceneIndex.class); + private static final String TYPE = "type"; private static final String ID = "id"; private static final String SECONDARY_IDS = "secondary_ids"; diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java b/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java new file mode 100644 index 00000000000..07f14103aad --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java @@ -0,0 +1,28 @@ +package org.opentripplanner.ext.geocoder.configure; + +import dagger.Module; +import dagger.Provides; +import jakarta.inject.Singleton; +import javax.annotation.Nullable; +import org.opentripplanner.ext.geocoder.LuceneIndex; +import org.opentripplanner.framework.application.OTPFeature; +import org.opentripplanner.transit.service.TransitService; + +/** + * This module converts the ride hailing configurations into ride hailing services to be used by the + * application context. + */ +@Module +public class GeocoderModule { + + @Provides + @Singleton + @Nullable + LuceneIndex luceneIndex(TransitService service) { + if (OTPFeature.SandboxAPIGeocoder.isOn()) { + return new LuceneIndex(service); + } else { + return null; + } + } +} diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java index fe95fe0447d..b307776ef52 100644 --- a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java +++ b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java @@ -6,6 +6,8 @@ import javax.annotation.Nullable; import org.opentripplanner.ext.emissions.EmissionsDataModel; import org.opentripplanner.ext.emissions.EmissionsServiceModule; +import org.opentripplanner.ext.geocoder.LuceneIndex; +import org.opentripplanner.ext.geocoder.configure.GeocoderModule; import org.opentripplanner.ext.interactivelauncher.configuration.InteractiveLauncherModule; import org.opentripplanner.ext.ridehailing.configure.RideHailingServicesModule; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; @@ -56,6 +58,7 @@ StopConsolidationServiceModule.class, InteractiveLauncherModule.class, StreetLimitationParametersServiceModule.class, + GeocoderModule.class, } ) public interface ConstructApplicationFactory { @@ -87,6 +90,9 @@ public interface ConstructApplicationFactory { StreetLimitationParameters streetLimitationParameters(); + @Nullable + LuceneIndex luceneIndex(); + @Component.Builder interface Builder { @BindsInstance diff --git a/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java b/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java index 6b822506fd9..0e81193d787 100644 --- a/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java +++ b/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java @@ -134,7 +134,8 @@ public static DefaultServerRequestContext create( stopConsolidationService, streetLimitationParametersService, flexParameters, - traverseVisitor, luceneIndex + traverseVisitor, + luceneIndex ); } diff --git a/src/test/java/org/opentripplanner/TestServerContext.java b/src/test/java/org/opentripplanner/TestServerContext.java index fe265f30fdc..90dca6ff840 100644 --- a/src/test/java/org/opentripplanner/TestServerContext.java +++ b/src/test/java/org/opentripplanner/TestServerContext.java @@ -55,7 +55,8 @@ public static OtpServerRequestContext createServerContext( List.of(), null, createStreetLimitationParametersService(), - null, null + null, + null ); creatTransitLayerForRaptor(transitModel, routerConfig.transitTuningConfig()); return context; diff --git a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java index d18b94e8a20..14d608626b3 100644 --- a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java @@ -144,7 +144,8 @@ void setup() { List.of(), null, new DefaultStreetLimitationParametersService(new StreetLimitationParameters()), - null, null + null, + null ), null, transitService diff --git a/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java b/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java index 41fb0311eb5..85a33281f81 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java @@ -121,7 +121,8 @@ public SpeedTest( List.of(), null, TestServerContext.createStreetLimitationParametersService(), - null, null + null, + null ); // Creating transitLayerForRaptor should be integrated into the TransitModel, but for now // we do it manually here From 5a7b2efd3d4adb5348fada0d69818a202734bdce Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 12 Jun 2024 15:14:25 +0200 Subject: [PATCH 024/192] Add consolidated stops to geocoder --- .../ext/geocoder/LuceneIndexTest.java | 10 ++- .../ext/geocoder/LuceneIndex.java | 9 ++- .../ext/geocoder/StopClusterMapper.java | 66 +++++++++++++++++-- .../geocoder/configure/GeocoderModule.java | 11 ++-- .../StopConsolidationService.java | 2 + .../DefaultStopConsolidationService.java | 5 ++ .../configure/ConstructApplication.java | 3 +- 7 files changed, 92 insertions(+), 14 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/geocoder/LuceneIndexTest.java b/src/ext-test/java/org/opentripplanner/ext/geocoder/LuceneIndexTest.java index cee6cf3a2d8..3e6c2b15195 100644 --- a/src/ext-test/java/org/opentripplanner/ext/geocoder/LuceneIndexTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/geocoder/LuceneIndexTest.java @@ -18,6 +18,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.opentripplanner.ext.stopconsolidation.internal.DefaultStopConsolidationRepository; +import org.opentripplanner.ext.stopconsolidation.internal.DefaultStopConsolidationService; import org.opentripplanner.model.FeedInfo; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.basic.TransitMode; @@ -160,8 +162,12 @@ public FeedInfo getFeedInfo(String feedId) { ); } }; - index = new LuceneIndex(transitService); - mapper = new StopClusterMapper(transitService); + var stopConsolidationService = new DefaultStopConsolidationService( + new DefaultStopConsolidationRepository(), + transitModel + ); + index = new LuceneIndex(transitService, stopConsolidationService); + mapper = new StopClusterMapper(transitService, stopConsolidationService); } @Test diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java index e4de7b4e5c4..1c01c1c36ff 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java @@ -11,6 +11,7 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Stream; +import javax.annotation.Nullable; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.en.EnglishAnalyzer; import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper; @@ -40,6 +41,7 @@ import org.apache.lucene.search.suggest.document.FuzzyCompletionQuery; import org.apache.lucene.search.suggest.document.SuggestIndexSearcher; import org.apache.lucene.store.ByteBuffersDirectory; +import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -68,9 +70,12 @@ public class LuceneIndex implements Serializable { private final SuggestIndexSearcher searcher; private final StopClusterMapper stopClusterMapper; - public LuceneIndex(TransitService transitService) { + public LuceneIndex( + TransitService transitService, + @Nullable StopConsolidationService stopConsolidationService + ) { this.transitService = transitService; - this.stopClusterMapper = new StopClusterMapper(transitService); + this.stopClusterMapper = new StopClusterMapper(transitService, stopConsolidationService); LOG.info("Creating geocoder lucene index"); diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java b/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java index d9f388ea0e8..4253041a88a 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java @@ -3,6 +3,7 @@ import static org.opentripplanner.ext.geocoder.StopCluster.LocationType.STATION; import static org.opentripplanner.ext.geocoder.StopCluster.LocationType.STOP; +import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Iterables; import java.util.Collection; import java.util.List; @@ -10,6 +11,8 @@ import java.util.Optional; import java.util.stream.Collectors; import javax.annotation.Nullable; +import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; +import org.opentripplanner.ext.stopconsolidation.model.StopReplacement; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; @@ -28,9 +31,14 @@ class StopClusterMapper { private final TransitService transitService; + private final StopConsolidationService stopConsolidationService; - StopClusterMapper(TransitService transitService) { + StopClusterMapper( + TransitService transitService, + StopConsolidationService stopConsolidationService + ) { this.transitService = transitService; + this.stopConsolidationService = stopConsolidationService; } /** @@ -45,16 +53,67 @@ Iterable generateStopClusters( Collection stopLocations, Collection stopLocationsGroups ) { + var stopClusters = buildStopClusters(stopLocations); + var stationClusters = buildStationClusters(stopLocationsGroups); + var consolidatedStopClusters = buildConsolidatedStopClusters(); + + return Iterables.concat(stopClusters, stationClusters, consolidatedStopClusters); + } + + private Iterable buildConsolidatedStopClusters() { + var multiMap = stopConsolidationService + .replacements() + .stream() + .collect( + ImmutableListMultimap.toImmutableListMultimap( + StopReplacement::primary, + StopReplacement::secondary + ) + ); + return multiMap + .keySet() + .stream() + .map(primary -> { + var secondaryIds = multiMap.get(primary); + var secondaries = secondaryIds.stream().map(transitService::getStopLocation).toList(); + var codes = ListUtils.combine( + ListUtils.ofNullable(primary.getCode()), + getCodes(secondaries) + ); + var names = ListUtils.combine( + ListUtils.ofNullable(primary.getName()), + getNames(secondaries) + ); + + return new LuceneStopCluster( + primary.getId().toString(), + secondaryIds.stream().map(id -> id.toString()).toList(), + names, + codes, + toCoordinate(primary.getCoordinate()) + ); + }) + .toList(); + } + + private static List buildStationClusters( + Collection stopLocationsGroups + ) { + return stopLocationsGroups.stream().map(StopClusterMapper::map).toList(); + } + + private List buildStopClusters(Collection stopLocations) { List stops = stopLocations .stream() // remove stop locations without a parent station .filter(sl -> sl.getParentStation() == null) + .filter(sl -> !stopConsolidationService.isPartOfConsolidatedStop(sl)) // stops without a name (for example flex areas) are useless for searching, so we remove them, too .filter(sl -> sl.getName() != null) .toList(); // if they are very close to each other and have the same name, only one is chosen (at random) - var deduplicatedStops = stops + return stops .stream() .collect( Collectors.groupingBy(sl -> @@ -66,9 +125,6 @@ Iterable generateStopClusters( .map(group -> map(group).orElse(null)) .filter(Objects::nonNull) .toList(); - var stations = stopLocationsGroups.stream().map(StopClusterMapper::map).toList(); - - return Iterables.concat(deduplicatedStops, stations); } private static LuceneStopCluster map(StopLocationsGroup g) { diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java b/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java index 07f14103aad..17949e5cb8a 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java @@ -5,12 +5,12 @@ import jakarta.inject.Singleton; import javax.annotation.Nullable; import org.opentripplanner.ext.geocoder.LuceneIndex; +import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.transit.service.TransitService; /** - * This module converts the ride hailing configurations into ride hailing services to be used by the - * application context. + * This module builds the lucene geocoder based on the whether the feature flag is */ @Module public class GeocoderModule { @@ -18,9 +18,12 @@ public class GeocoderModule { @Provides @Singleton @Nullable - LuceneIndex luceneIndex(TransitService service) { + LuceneIndex luceneIndex( + TransitService service, + @Nullable StopConsolidationService stopConsolidationService + ) { if (OTPFeature.SandboxAPIGeocoder.isOn()) { - return new LuceneIndex(service); + return new LuceneIndex(service, stopConsolidationService); } else { return null; } diff --git a/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationService.java b/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationService.java index 11ad4be69ff..68efe8744cc 100644 --- a/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationService.java +++ b/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationService.java @@ -43,4 +43,6 @@ public interface StopConsolidationService { * For a given stop id return the primary stop if it is part of a consolidated stop group. */ Optional primaryStop(FeedScopedId id); + + boolean isPartOfConsolidatedStop(StopLocation sl); } diff --git a/src/ext/java/org/opentripplanner/ext/stopconsolidation/internal/DefaultStopConsolidationService.java b/src/ext/java/org/opentripplanner/ext/stopconsolidation/internal/DefaultStopConsolidationService.java index 216489512f5..9f31e366be5 100644 --- a/src/ext/java/org/opentripplanner/ext/stopconsolidation/internal/DefaultStopConsolidationService.java +++ b/src/ext/java/org/opentripplanner/ext/stopconsolidation/internal/DefaultStopConsolidationService.java @@ -67,6 +67,11 @@ public boolean isSecondaryStop(StopLocation stop) { return repo.groups().stream().anyMatch(r -> r.secondaries().contains(stop.getId())); } + @Override + public boolean isPartOfConsolidatedStop(StopLocation sl) { + return isSecondaryStop(sl) || isPrimaryStop(sl); + } + @Override public boolean isActive() { return !repo.groups().isEmpty(); diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java index db4a4af7fbd..f2c279e7609 100644 --- a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java +++ b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java @@ -179,7 +179,8 @@ private void setupTransitRoutingServer() { } if (OTPFeature.SandboxAPIGeocoder.isOn()) { - LOG.info("Creating debug client geocoder lucene index"); + LOG.info("Initializing geocoder"); + this.factory.luceneIndex(); } } From d826aeb1e3b7feb824905173267e6f24952947c4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 12 Jun 2024 16:07:06 +0200 Subject: [PATCH 025/192] Filter nullable codes --- .../org/opentripplanner/ext/geocoder/StopClusterMapper.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java b/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java index 4253041a88a..003225f9781 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java @@ -75,7 +75,11 @@ private Iterable buildConsolidatedStopClusters() { .stream() .map(primary -> { var secondaryIds = multiMap.get(primary); - var secondaries = secondaryIds.stream().map(transitService::getStopLocation).toList(); + var secondaries = secondaryIds + .stream() + .map(transitService::getStopLocation) + .filter(Objects::nonNull) + .toList(); var codes = ListUtils.combine( ListUtils.ofNullable(primary.getCode()), getCodes(secondaries) From ba12693a795de12237166fdeb4098c53c910a97e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 12 Jun 2024 16:18:48 +0200 Subject: [PATCH 026/192] Remove logging --- src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java | 2 -- .../org/opentripplanner/ext/geocoder/StopClusterMapper.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java index 1c01c1c36ff..f5b4a6e59e9 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java @@ -77,8 +77,6 @@ public LuceneIndex( this.transitService = transitService; this.stopClusterMapper = new StopClusterMapper(transitService, stopConsolidationService); - LOG.info("Creating geocoder lucene index"); - this.analyzer = new PerFieldAnalyzerWrapper( new StandardAnalyzer(), diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java b/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java index 003225f9781..98a617b809f 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/StopClusterMapper.java @@ -35,7 +35,7 @@ class StopClusterMapper { StopClusterMapper( TransitService transitService, - StopConsolidationService stopConsolidationService + @Nullable StopConsolidationService stopConsolidationService ) { this.transitService = transitService; this.stopConsolidationService = stopConsolidationService; From 8258a1e03af4675554cc2ecfc2e52e99dd04c7ad Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 13 Jun 2024 13:21:39 +0200 Subject: [PATCH 027/192] Add Google's truth test assertion library, add test --- pom.xml | 6 ++ .../ext/geocoder/StopClusterMapperTest.java | 57 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/ext-test/java/org/opentripplanner/ext/geocoder/StopClusterMapperTest.java diff --git a/pom.xml b/pom.xml index 6f202b592f8..266bbf6ab61 100644 --- a/pom.xml +++ b/pom.xml @@ -690,6 +690,12 @@ ${junit.version} test + + com.google.truth + truth + 1.4.2 + test + com.tngtech.archunit archunit diff --git a/src/ext-test/java/org/opentripplanner/ext/geocoder/StopClusterMapperTest.java b/src/ext-test/java/org/opentripplanner/ext/geocoder/StopClusterMapperTest.java new file mode 100644 index 00000000000..578f7d4118f --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/geocoder/StopClusterMapperTest.java @@ -0,0 +1,57 @@ +package org.opentripplanner.ext.geocoder; + +import static com.google.common.truth.Truth.assertThat; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner.ext.stopconsolidation.internal.DefaultStopConsolidationRepository; +import org.opentripplanner.ext.stopconsolidation.internal.DefaultStopConsolidationService; +import org.opentripplanner.ext.stopconsolidation.model.ConsolidatedStopGroup; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.StopModel; +import org.opentripplanner.transit.service.TransitModel; + +class StopClusterMapperTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final RegularStop STOP_A = TEST_MODEL.stop("A").build(); + private static final RegularStop STOP_B = TEST_MODEL.stop("B").build(); + private static final RegularStop STOP_C = TEST_MODEL.stop("C").build(); + private static final List STOPS = List.of(STOP_A, STOP_B, STOP_C); + private static final StopModel STOP_MODEL = TEST_MODEL + .stopModelBuilder() + .withRegularStops(STOPS) + .build(); + private static final TransitModel TRANSIT_MODEL = new TransitModel( + STOP_MODEL, + new Deduplicator() + ); + private static final List LOCATIONS = STOPS + .stream() + .map(StopLocation.class::cast) + .toList(); + + @Test + void clusterConsolidatedStops() { + var repo = new DefaultStopConsolidationRepository(); + repo.addGroups(List.of(new ConsolidatedStopGroup(STOP_A.getId(), List.of(STOP_B.getId())))); + + var service = new DefaultStopConsolidationService(repo, TRANSIT_MODEL); + var mapper = new StopClusterMapper(new DefaultTransitService(TRANSIT_MODEL), service); + + var clusters = mapper.generateStopClusters(LOCATIONS, List.of()); + + var expected = new LuceneStopCluster( + STOP_A.getId().toString(), + List.of(STOP_B.getId().toString()), + List.of(STOP_A.getName(), STOP_B.getName()), + List.of(STOP_A.getCode(), STOP_B.getCode()), + new StopCluster.Coordinate(STOP_A.getLat(), STOP_A.getLon()) + ); + assertThat(clusters).contains(expected); + } +} From d5ea5af9eb804e72c53f871b1091f0f215cf21b3 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 11:20:36 +0200 Subject: [PATCH 028/192] Extract two separate methods for Transmodel and GTFS scalar versions --- .../apis/gtfs/GraphQLScalars.java | 5 +-- .../apis/transmodel/support/GqlUtil.java | 2 +- .../graphql/scalar/DateScalarFactory.java | 17 ++++++-- .../graphql/scalar/DateScalarFactoryTest.java | 39 +++++++++++++++---- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java index e7b0b491f0d..8ec3172db52 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java @@ -236,10 +236,7 @@ private static Optional validateCost(int cost) { ) .build(); - public static final GraphQLScalarType LOCAL_DATE_SCALAR = DateScalarFactory.createDateScalar( - "LocalDate", - null - ); + public static final GraphQLScalarType LOCAL_DATE_SCALAR = DateScalarFactory.createGtfsDateScalar(); public static final GraphQLScalarType GEOJSON_SCALAR = GraphQLScalarType .newScalar() diff --git a/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java b/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java index e94253cf33f..16083085500 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/support/GqlUtil.java @@ -45,7 +45,7 @@ public class GqlUtil { public GqlUtil(ZoneId timeZone) { this.dateTimeScalar = DateTimeScalarFactory.createMillisecondsSinceEpochAsDateTimeStringScalar(timeZone); - this.dateScalar = DateScalarFactory.createDateScalar("Date", DateScalarFactory.DESCRIPTION); + this.dateScalar = DateScalarFactory.createTransmodelDateScalar(); this.doubleFunctionScalar = DoubleFunctionFactory.createDoubleFunctionScalar(); this.localTimeScalar = LocalTimeScalarFactory.createLocalTimeScalar(); this.timeScalar = TimeScalarFactory.createSecondsSinceMidnightAsTimeObject(); diff --git a/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java b/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java index 590dea013a3..931a98fc5d9 100644 --- a/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java +++ b/src/main/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactory.java @@ -13,18 +13,29 @@ public class DateScalarFactory { - public static final String DESCRIPTION = + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; + public static final String TRANSMODEL_DESCRIPTION = "Local date using the ISO 8601 format: `YYYY-MM-DD`. Example: `2020-05-17`."; - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; + private static final String TRANSMODEL_NAME = "Date"; + private static final String GTFS_NAME = "LocalDate"; private DateScalarFactory() {} + public static GraphQLScalarType createTransmodelDateScalar() { + return createDateScalar(TRANSMODEL_NAME, TRANSMODEL_DESCRIPTION); + } + + public static GraphQLScalarType createGtfsDateScalar() { + // description comes from schema.graphqls + return createDateScalar(GTFS_NAME, null); + } + /** * @param description Nullable description that allows caller to pass in null which leads to the * description from schema.graphqls to be used. */ - public static GraphQLScalarType createDateScalar( + private static GraphQLScalarType createDateScalar( String scalarName, @Nullable String description ) { diff --git a/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java index 02e4b37d7d6..db95f0ba8ce 100644 --- a/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java +++ b/src/test/java/org/opentripplanner/framework/graphql/scalar/DateScalarFactoryTest.java @@ -7,25 +7,48 @@ import graphql.schema.CoercingParseValueException; import graphql.schema.GraphQLScalarType; import java.time.LocalDate; +import java.util.List; +import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class DateScalarFactoryTest { - private static final GraphQLScalarType SCALAR = DateScalarFactory.createDateScalar("Date", null); + private static final GraphQLScalarType GTFS_SCALAR = DateScalarFactory.createGtfsDateScalar(); + private static final GraphQLScalarType TRANSMODEL_SCALAR = DateScalarFactory.createTransmodelDateScalar(); + private static final List INVALID_DATES = List.of( + "2024-05", + "2024", + "2024-99-04", + "202405-23", + "20240523" + ); + + static Stream succesfulCases() { + return Stream.of(GTFS_SCALAR, TRANSMODEL_SCALAR).map(s -> Arguments.of(s, "2024-05-23")); + } @ParameterizedTest - @ValueSource(strings = { "2024-05-23" }) - void parse(String input) { - var result = SCALAR.getCoercing().parseValue(input); + @MethodSource("succesfulCases") + void parse(GraphQLScalarType scalar, String input) { + var result = scalar.getCoercing().parseValue(input); assertInstanceOf(LocalDate.class, result); var date = (LocalDate) result; assertEquals(LocalDate.of(2024, 5, 23), date); } + static Stream invalidCases() { + return INVALID_DATES + .stream() + .flatMap(date -> + Stream.of(Arguments.of(GTFS_SCALAR, date), Arguments.of(TRANSMODEL_SCALAR, date)) + ); + } + @ParameterizedTest - @ValueSource(strings = { "2024-05", "2024", "2024-99-04", "202405-23", "20240523" }) - void failParsing(String input) { - assertThrows(CoercingParseValueException.class, () -> SCALAR.getCoercing().parseValue(input)); + @MethodSource("invalidCases") + void failParsing(GraphQLScalarType scalar, String input) { + assertThrows(CoercingParseValueException.class, () -> scalar.getCoercing().parseValue(input)); } } From 54b2fe248a34d909cb77a7e2450d0258455727d5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 11:32:42 +0200 Subject: [PATCH 029/192] Refer to API documentation --- .../apis/gtfs/PatternByServiceDatesFilter.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index 0867e6ead54..88dacb22afd 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -24,11 +24,9 @@ public class PatternByServiceDatesFilter { private final Function> getServiceDatesForTrip; /** - * - * @param startInclusive The inclusive start date to check the patterns for. If null then no start - * date is defined and this will therefore match all dates. - * @param endInclusive The inclusive end date to check the patterns for. If null then no end date - * is defined this will therefore match all dates. + * This method is not private to enable unit testing. + *

+ * See the API documentation for a discussion of {@code startInclusive} and {@code endInclusive}. */ PatternByServiceDatesFilter( @Nullable LocalDate startInclusive, @@ -38,7 +36,7 @@ public class PatternByServiceDatesFilter { ) { this.getPatternsForRoute = Objects.requireNonNull(getPatternsForRoute); this.getServiceDatesForTrip = Objects.requireNonNull(getServiceDatesForTrip); - // optional + // optional, but one must be defined this.startInclusive = startInclusive; this.endInclusive = endInclusive; From 665ce7ac129049849f5fed1ef05312fe8b874089 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 12:07:58 +0200 Subject: [PATCH 030/192] Move small helper method into separate class --- .../apis/gtfs/GraphQLUtils.java | 14 ++++++++ .../gtfs/PatternByServiceDatesFilter.java | 7 ---- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 2 +- .../apis/gtfs/datafetchers/RouteImpl.java | 2 +- .../gtfs/PatternByServiceDatesFilterTest.java | 30 ---------------- .../framework/graphql/GraphQLUtilsTest.java | 34 +++++++++++++++++++ 6 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java index 3fb339daa32..9aa1235879c 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 org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFilterPlaceType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFormFactor; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; @@ -109,4 +110,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); } + + /** + * Checks if a service date filter input has at least one filter set. If both start and end are + * null then no filtering is necessary and this method returns null. + */ + public static boolean hasServiceDateFilter( + GraphQLTypes.GraphQLServiceDateFilterInput serviceDays + ) { + return ( + serviceDays != null && + (serviceDays.getGraphQLStart() != null || serviceDays.getGraphQLEnd() != null) + ); + } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index 88dacb22afd..41e2b812420 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -97,11 +97,4 @@ private boolean hasServicesOnDate(TripPattern pattern) { ); }); } - - public static boolean hasServiceDateFilter(GraphQLServiceDateFilterInput serviceDays) { - return ( - serviceDays != null && - (serviceDays.getGraphQLStart() != null || serviceDays.getGraphQLEnd() != 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 7a644da4be8..c83669a386b 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -611,7 +611,7 @@ public DataFetcher> routes() { ); } - if (PatternByServiceDatesFilter.hasServiceDateFilter(args.getGraphQLServiceDates())) { + if (GraphQLUtils.hasServiceDateFilter(args.getGraphQLServiceDates())) { var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService); routeStream = filter.filterRoutes(routeStream).stream(); } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index 596936dfdb5..5eeb662a6d1 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -181,7 +181,7 @@ public DataFetcher> patterns() { var args = new GraphQLTypes.GraphQLRoutePatternsArgs(environment.getArguments()); - if (PatternByServiceDatesFilter.hasServiceDateFilter(args.getGraphQLServiceDates())) { + if (GraphQLUtils.hasServiceDateFilter(args.getGraphQLServiceDates())) { var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService); return filter.filterPatterns(patterns); } else { diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index b9ad1c4973a..1893516ab58 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -3,21 +3,16 @@ import static java.time.LocalDate.parse; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.apis.gtfs.PatternByServiceDatesFilterTest.FilterExpectation.NOT_REMOVED; import static org.opentripplanner.apis.gtfs.PatternByServiceDatesFilterTest.FilterExpectation.REMOVED; import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; -import java.util.Map; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDateFilterInput; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; @@ -41,7 +36,6 @@ class PatternByServiceDatesFilterTest { private static final RegularStop STOP_1 = MODEL.stop("1").build(); private static final StopPattern STOP_PATTERN = TransitModelForTest.stopPattern(STOP_1, STOP_1); private static final TripPattern PATTERN_1 = pattern(); - private static final LocalDate DATE = LocalDate.parse("2024-05-27"); enum FilterExpectation { REMOVED, @@ -151,28 +145,4 @@ private static PatternByServiceDatesFilter mockFilter(LocalDate start, LocalDate trip -> List.of(parse("2024-05-01"), parse("2024-06-01")) ); } - - public static List noFilterCases() { - var list = new ArrayList(); - list.add(null); - list.add(new GraphQLServiceDateFilterInput(Map.of())); - return list; - } - - @ParameterizedTest - @MethodSource("noFilterCases") - void hasNoServiceDateFilter(GraphQLServiceDateFilterInput input) { - assertFalse(PatternByServiceDatesFilter.hasServiceDateFilter(input)); - } - - public static List> hasFilterCases() { - return List.of(Map.of("start", DATE), Map.of("end", DATE), Map.of("start", DATE, "end", DATE)); - } - - @ParameterizedTest - @MethodSource("hasFilterCases") - void hasServiceDateFilter(Map params) { - var input = new GraphQLServiceDateFilterInput(params); - assertTrue(PatternByServiceDatesFilter.hasServiceDateFilter(input)); - } } diff --git a/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java b/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java index b72cb6e5a0d..8119b9256c8 100644 --- a/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java +++ b/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java @@ -3,19 +3,29 @@ import static graphql.execution.ExecutionContextBuilder.newExecutionContextBuilder; import static java.util.Map.entry; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import graphql.ExecutionInput; import graphql.execution.ExecutionContext; import graphql.execution.ExecutionId; import graphql.schema.DataFetchingEnvironmentImpl; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Map; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.framework.i18n.TranslatedString; class GraphQLUtilsTest { + private static final LocalDate DATE = LocalDate.parse("2024-05-27"); + static final ExecutionContext executionContext; static { @@ -122,4 +132,28 @@ void testGetLocaleWithLocalContextLocale() { assertEquals(frenchLocale, locale); } + + public static List noFilterCases() { + var list = new ArrayList(); + list.add(null); + list.add(new GraphQLTypes.GraphQLServiceDateFilterInput(Map.of())); + return list; + } + + @ParameterizedTest + @MethodSource("noFilterCases") + void hasNoServiceDateFilter(GraphQLTypes.GraphQLServiceDateFilterInput input) { + assertFalse(org.opentripplanner.apis.gtfs.GraphQLUtils.hasServiceDateFilter(input)); + } + + public static List> hasFilterCases() { + return List.of(Map.of("start", DATE), Map.of("end", DATE), Map.of("start", DATE, "end", DATE)); + } + + @ParameterizedTest + @MethodSource("hasFilterCases") + void hasServiceDateFilter(Map params) { + var input = new GraphQLTypes.GraphQLServiceDateFilterInput(params); + assertTrue(org.opentripplanner.apis.gtfs.GraphQLUtils.hasServiceDateFilter(input)); + } } From 49ac4b7acf477733f7556a0c03b825535d89eacf Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 13:27:05 +0200 Subject: [PATCH 031/192] Move invalid trip id test --- .../trip/TimetableSnapshotSourceTest.java | 36 ----------------- .../rejection/InvalidTripIdTest.java | 40 +++++++++++++++++++ 2 files changed, 40 insertions(+), 36 deletions(-) create mode 100644 src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java index 1dad90b416b..ef45c442f0a 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java @@ -22,7 +22,6 @@ import java.time.Duration; import java.time.LocalDate; import java.util.List; -import java.util.stream.Stream; import javax.annotation.Nonnull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -109,41 +108,6 @@ public void testGetSnapshotWithMaxSnapshotFrequencyCleared() { assertNotSame(snapshot, newSnapshot); } - /** - * This test just asserts that invalid trip ids don't throw an exception and are ignored instead - */ - @Test - public void invalidTripId() { - var updater = new TimetableSnapshotSource( - TimetableSnapshotSourceParameters.DEFAULT, - transitModel - ); - - Stream - .of("", null) - .forEach(id -> { - var tripDescriptorBuilder = TripDescriptor.newBuilder(); - tripDescriptorBuilder.setTripId(""); - tripDescriptorBuilder.setScheduleRelationship( - TripDescriptor.ScheduleRelationship.SCHEDULED - ); - var tripUpdateBuilder = TripUpdate.newBuilder(); - - tripUpdateBuilder.setTrip(tripDescriptorBuilder); - var tripUpdate = tripUpdateBuilder.build(); - - var result = updater.applyTripUpdates( - TRIP_MATCHER_NOOP, - REQUIRED_NO_DATA, - DIFFERENTIAL, - List.of(tripUpdate), - feedId - ); - - assertEquals(0, result.successful()); - }); - } - @Test public void testHandleModifiedTrip() { // GIVEN diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java new file mode 100644 index 00000000000..58f9fb2ff55 --- /dev/null +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java @@ -0,0 +1,40 @@ +package org.opentripplanner.updater.trip.moduletests.rejection; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.transit.realtime.GtfsRealtime; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.updater.trip.RealtimeTestEnvironment; + +public class InvalidTripIdTest { + + static Stream invalidCases() { + return Stream.of(null, "", " "); + } + + /** + * This test just asserts that invalid trip ids don't throw an exception and are ignored instead + */ + @ParameterizedTest(name = "tripId=\"{0}\"") + @MethodSource("invalidCases") + void invalidTripId(String tripId) { + var env = RealtimeTestEnvironment.gtfs(); + var tripDescriptorBuilder = GtfsRealtime.TripDescriptor.newBuilder(); + if (tripId != null) { + tripDescriptorBuilder.setTripId(tripId); + } + tripDescriptorBuilder.setScheduleRelationship( + GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED + ); + var tripUpdateBuilder = GtfsRealtime.TripUpdate.newBuilder(); + + tripUpdateBuilder.setTrip(tripDescriptorBuilder); + var tripUpdate = tripUpdateBuilder.build(); + + var result = env.applyTripUpdate(tripUpdate); + + assertEquals(0, result.successful()); + } +} From 3f79d0e357b3c8b0ff444d35efbcbcd5a4fdefc7 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 13:31:03 +0200 Subject: [PATCH 032/192] Move assertions into separate class --- .../siri/SiriTimetableSnapshotSourceTest.java | 18 +----------------- .../test/support/UpdateResultAssertions.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 17 deletions(-) create mode 100644 src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java index a069f5c6656..73f6b92ffe7 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java @@ -1,24 +1,12 @@ package org.opentripplanner.ext.siri; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.test.support.UpdateResultAssertions.assertFailure; -import java.util.List; -import java.util.Set; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.opentripplanner.model.PickDrop; import org.opentripplanner.transit.model.timetable.RealTimeState; -import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripOnServiceDate; -import org.opentripplanner.transit.model.timetable.TripTimes; -import org.opentripplanner.transit.model.timetable.TripTimesFactory; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.StopModel; -import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.transit.service.TransitService; -import org.opentripplanner.updater.TimetableSnapshotSourceParameters; import org.opentripplanner.updater.spi.UpdateError; -import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; class SiriTimetableSnapshotSourceTest { @@ -402,10 +390,6 @@ void testExtraUnknownStop() { assertFailure(UpdateError.UpdateErrorType.INVALID_STOP_SEQUENCE, result); } - private void assertFailure(UpdateError.UpdateErrorType expectedError, UpdateResult result) { - assertEquals(Set.of(expectedError), result.failures().keySet()); - } - private static SiriEtBuilder updatedJourneyBuilder(RealtimeTestEnvironment env) { return new SiriEtBuilder(env.getDateTimeHelper()) .withEstimatedCalls(builder -> diff --git a/src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java b/src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java new file mode 100644 index 00000000000..e41660b0443 --- /dev/null +++ b/src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java @@ -0,0 +1,13 @@ +package org.opentripplanner.test.support; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Set; +import org.opentripplanner.updater.spi.UpdateError; +import org.opentripplanner.updater.spi.UpdateResult; + +public class UpdateResultAssertions { + public static void assertFailure(UpdateError.UpdateErrorType expectedError, UpdateResult result) { + assertEquals(Set.of(expectedError), result.failures().keySet()); + } +} From c442d17a6392e51eb06b1d5c9283910234327595 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 14:06:47 +0200 Subject: [PATCH 033/192] Move skipped tests --- .../test/support/UpdateResultAssertions.java | 13 ++ .../updater/trip/RealtimeTestEnvironment.java | 8 +- .../trip/TimetableSnapshotSourceTest.java | 215 ------------------ .../trip/moduletests/delay/DelayedTest.java | 71 +++++- .../trip/moduletests/delay/SkippedTest.java | 128 ++++++++++- .../rejection/InvalidInputTest.java | 5 +- 6 files changed, 213 insertions(+), 227 deletions(-) diff --git a/src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java b/src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java index e41660b0443..c6be0406ae3 100644 --- a/src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java +++ b/src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java @@ -1,13 +1,26 @@ package org.opentripplanner.test.support; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Set; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.spi.UpdateResult; public class UpdateResultAssertions { + public static void assertFailure(UpdateError.UpdateErrorType expectedError, UpdateResult result) { assertEquals(Set.of(expectedError), result.failures().keySet()); } + + public static UpdateResult assertSuccess(UpdateResult updateResult) { + var errorCodes = updateResult.failures().keySet(); + assertEquals( + Set.of(), + errorCodes, + "Update result should have no error codes but had %s".formatted(errorCodes) + ); + assertTrue(updateResult.successful() > 0); + return updateResult; + } } diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index c2fb31e2dfb..fdd726eb624 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -15,6 +15,7 @@ import org.opentripplanner.ext.siri.EntityResolver; import org.opentripplanner.ext.siri.SiriFuzzyTripMatcher; import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource; +import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TimetableSnapshot; @@ -300,7 +301,12 @@ private UpdateResult applyEstimatedTimetable( } private Trip createTrip(String id, Route route, List stops) { - var trip = Trip.of(id(id)).withRoute(route).withServiceId(SERVICE_ID).build(); + var trip = Trip + .of(id(id)) + .withRoute(route) + .withHeadsign(I18NString.of("Headsign of %s".formatted(id))) + .withServiceId(SERVICE_ID) + .build(); var tripOnServiceDate = TripOnServiceDate .of(trip.getId()) diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java index ef45c442f0a..9cf69821aff 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java @@ -2,7 +2,6 @@ import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.ADDED; import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.CANCELED; -import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; import static com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate.ScheduleRelationship.SKIPPED; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -29,7 +28,6 @@ import org.opentripplanner.ConstantsForTests; import org.opentripplanner.TestOtpModel; import org.opentripplanner._support.time.ZoneIds; -import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.Timetable; @@ -310,219 +308,6 @@ public void testHandleModifiedTrip() { } } - @Nested - class Scheduled { - - @Test - public void scheduled() { - // GIVEN - - String scheduledTripId = "1.1"; - - var builder = new TripUpdateBuilder( - scheduledTripId, - SERVICE_DATE, - SCHEDULED, - transitModel.getTimeZone() - ) - .addDelayedStopTime(1, 0) - .addDelayedStopTime(2, 60, 80) - .addDelayedStopTime(3, 90, 90); - - var tripUpdate = builder.build(); - - var updater = defaultUpdater(); - - // WHEN - updater.applyTripUpdates( - TRIP_MATCHER_NOOP, - REQUIRED_NO_DATA, - DIFFERENTIAL, - List.of(tripUpdate), - feedId - ); - - // THEN - final TimetableSnapshot snapshot = updater.getTimetableSnapshot(); - - final FeedScopedId tripId = new FeedScopedId(feedId, scheduledTripId); - final Trip trip = transitModel.getTransitModelIndex().getTripForId().get(tripId); - final TripPattern originalTripPattern = transitModel - .getTransitModelIndex() - .getPatternForTrip() - .get(trip); - - final Timetable originalTimetableForToday = snapshot.resolve( - originalTripPattern, - SERVICE_DATE - ); - final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); - - assertNotSame(originalTimetableForToday, originalTimetableScheduled); - - final int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); - assertTrue( - originalTripIndexScheduled > -1, - "Original trip should be found in scheduled time table" - ); - final TripTimes originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( - originalTripIndexScheduled - ); - assertFalse( - originalTripTimesScheduled.isCanceledOrDeleted(), - "Original trip times should not be canceled in scheduled time table" - ); - assertEquals(RealTimeState.SCHEDULED, originalTripTimesScheduled.getRealTimeState()); - - final int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); - assertTrue( - originalTripIndexForToday > -1, - "Original trip should be found in time table for service date" - ); - final TripTimes originalTripTimesForToday = originalTimetableForToday.getTripTimes( - originalTripIndexForToday - ); - assertEquals(RealTimeState.UPDATED, originalTripTimesForToday.getRealTimeState()); - assertEquals(0, originalTripTimesForToday.getArrivalDelay(0)); - assertEquals(0, originalTripTimesForToday.getDepartureDelay(0)); - assertEquals(60, originalTripTimesForToday.getArrivalDelay(1)); - assertEquals(80, originalTripTimesForToday.getDepartureDelay(1)); - assertEquals(90, originalTripTimesForToday.getArrivalDelay(2)); - assertEquals(90, originalTripTimesForToday.getDepartureDelay(2)); - } - - @Test - public void scheduledTripWithSkippedAndNoData() { - // GIVEN - - String scheduledTripId = "1.1"; - - var builder = new TripUpdateBuilder( - scheduledTripId, - SERVICE_DATE, - SCHEDULED, - transitModel.getTimeZone() - ) - .addNoDataStop(1) - .addSkippedStop(2) - .addNoDataStop(3); - - var tripUpdate = builder.build(); - - var updater = defaultUpdater(); - - // WHEN - updater.applyTripUpdates( - TRIP_MATCHER_NOOP, - REQUIRED_NO_DATA, - DIFFERENTIAL, - List.of(tripUpdate), - feedId - ); - - // THEN - final TimetableSnapshot snapshot = updater.getTimetableSnapshot(); - - // Original trip pattern - { - final FeedScopedId tripId = new FeedScopedId(feedId, scheduledTripId); - final Trip trip = transitModel.getTransitModelIndex().getTripForId().get(tripId); - final TripPattern originalTripPattern = transitModel - .getTransitModelIndex() - .getPatternForTrip() - .get(trip); - - final Timetable originalTimetableForToday = snapshot.resolve( - originalTripPattern, - SERVICE_DATE - ); - final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); - - assertNotSame(originalTimetableForToday, originalTimetableScheduled); - - final int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); - assertTrue( - originalTripIndexScheduled > -1, - "Original trip should be found in scheduled time table" - ); - final TripTimes originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( - originalTripIndexScheduled - ); - assertFalse( - originalTripTimesScheduled.isCanceledOrDeleted(), - "Original trip times should not be canceled in scheduled time table" - ); - assertEquals(RealTimeState.SCHEDULED, originalTripTimesScheduled.getRealTimeState()); - - final int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); - assertTrue( - originalTripIndexForToday > -1, - "Original trip should be found in time table for service date" - ); - final TripTimes originalTripTimesForToday = originalTimetableForToday.getTripTimes( - originalTripIndexForToday - ); - assertTrue( - originalTripTimesForToday.isDeleted(), - "Original trip times should be deleted in time table for service date" - ); - // original trip should be deleted - assertEquals(RealTimeState.DELETED, originalTripTimesForToday.getRealTimeState()); - } - - // New trip pattern - { - final TripPattern newTripPattern = snapshot.getRealtimeAddedTripPattern( - new FeedScopedId(feedId, scheduledTripId), - SERVICE_DATE - ); - assertNotNull(newTripPattern, "New trip pattern should be found"); - - final Timetable newTimetableForToday = snapshot.resolve(newTripPattern, SERVICE_DATE); - final Timetable newTimetableScheduled = snapshot.resolve(newTripPattern, null); - - assertNotSame(newTimetableForToday, newTimetableScheduled); - - assertTrue(newTripPattern.canBoard(0)); - assertFalse(newTripPattern.canBoard(1)); - assertTrue(newTripPattern.canBoard(2)); - - assertEquals(new NonLocalizedString("foo"), newTripPattern.getTripHeadsign()); - assertEquals( - newTripPattern.getOriginalTripPattern().getTripHeadsign(), - newTripPattern.getTripHeadsign() - ); - - final int newTimetableForTodayModifiedTripIndex = newTimetableForToday.getTripIndex( - scheduledTripId - ); - assertTrue( - newTimetableForTodayModifiedTripIndex > -1, - "New trip should be found in time table for service date" - ); - - var newTripTimes = newTimetableForToday.getTripTimes(newTimetableForTodayModifiedTripIndex); - assertEquals(RealTimeState.UPDATED, newTripTimes.getRealTimeState()); - - assertEquals( - -1, - newTimetableScheduled.getTripIndex(scheduledTripId), - "New trip should not be found in scheduled time table" - ); - - assertEquals(0, newTripTimes.getArrivalDelay(0)); - assertEquals(0, newTripTimes.getDepartureDelay(0)); - assertEquals(0, newTripTimes.getArrivalDelay(1)); - assertEquals(0, newTripTimes.getDepartureDelay(1)); - assertEquals(0, newTripTimes.getArrivalDelay(2)); - assertEquals(0, newTripTimes.getDepartureDelay(2)); - assertTrue(newTripTimes.isNoDataStop(0)); - assertTrue(newTripTimes.isCancelledStop(1)); - assertTrue(newTripTimes.isNoDataStop(2)); - } - } - } - @Nested class Added { diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java index 4cfe1e5500d..5752e4941be 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java @@ -2,26 +2,32 @@ import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.test.support.UpdateResultAssertions.assertSuccess; +import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import org.junit.jupiter.api.Test; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TimetableSnapshot; +import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; /** * Delays should be applied to the first trip but should leave the second trip untouched. */ -public class DelayedTest { +class DelayedTest { private static final int DELAY = 1; private static final int STOP_SEQUENCE = 1; @Test - public void delayed() { + void singleStopDelay() { var env = RealtimeTestEnvironment.gtfs(); var tripUpdate = new TripUpdateBuilder( @@ -85,4 +91,65 @@ public void delayed() { assertEquals(RealTimeState.SCHEDULED, realtime.getTripTimes(tripIndex).getRealTimeState()); } } + + /** + * Tests delays to multiple stop times, where arrival and departure do not have the same delay. + */ + @Test + void complexDelay() { + var env = RealtimeTestEnvironment.gtfs(); + + String tripId = env.trip2.getId().getId(); + + var builder = new TripUpdateBuilder(tripId, SERVICE_DATE, SCHEDULED, env.timeZone) + .addDelayedStopTime(0, 0) + .addDelayedStopTime(1, 60, 80) + .addDelayedStopTime(2, 90, 90); + + var tripUpdate = builder.build(); + + assertSuccess(env.applyTripUpdate(tripUpdate)); + + // THEN + final TimetableSnapshot snapshot = env.getTimetableSnapshot(); + + final TripPattern originalTripPattern = env.transitModel + .getTransitModelIndex() + .getPatternForTrip() + .get(env.trip2); + + final Timetable originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); + final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); + + assertNotSame(originalTimetableForToday, originalTimetableScheduled); + + final int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); + assertTrue( + originalTripIndexScheduled > -1, + "Original trip should be found in scheduled time table" + ); + final TripTimes originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( + originalTripIndexScheduled + ); + assertFalse( + originalTripTimesScheduled.isCanceledOrDeleted(), + "Original trip times should not be canceled in scheduled time table" + ); + assertEquals(RealTimeState.SCHEDULED, originalTripTimesScheduled.getRealTimeState()); + + final int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); + assertTrue( + originalTripIndexForToday > -1, + "Original trip should be found in time table for service date" + ); + + assertEquals( + "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", + env.getScheduledTimetable(env.trip2.getId()) + ); + assertEquals( + "UPDATED | A1 0:01 0:01:01 | B1 0:02:10 0:02:31 | C1 0:02:50 0:02:51", + env.getRealtimeTimetable(env.trip2) + ); + } } diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index 9afb7e76261..4424d47495b 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -3,12 +3,16 @@ import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; 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.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.test.support.UpdateResultAssertions.assertSuccess; +import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import static org.opentripplanner.updater.trip.UpdateIncrementality.DIFFERENTIAL; import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -40,9 +44,8 @@ public void scheduledTripWithSkippedAndScheduled() { .addDelayedStopTime(2, 90) .build(); - var result = env.applyTripUpdate(tripUpdate); + assertSuccess(env.applyTripUpdate(tripUpdate)); - assertEquals(1, result.successful()); final TimetableSnapshot snapshot = env.getTimetableSnapshot(); @@ -144,9 +147,7 @@ public void scheduledTripWithPreviouslySkipped() { .addDelayedStopTime(2, 90) .build(); - var result = env.applyTripUpdate(tripUpdate, DIFFERENTIAL); - - assertEquals(1, result.successful()); + var result = assertSuccess(env.applyTripUpdate(tripUpdate, DIFFERENTIAL)); // Create update to the same trip but now the skipped stop is no longer skipped var scheduledBuilder = new TripUpdateBuilder( @@ -162,7 +163,7 @@ public void scheduledTripWithPreviouslySkipped() { tripUpdate = scheduledBuilder.build(); // apply the update with the previously skipped stop now scheduled - result = env.applyTripUpdate(tripUpdate, DIFFERENTIAL); + result = assertSuccess(env.applyTripUpdate(tripUpdate, DIFFERENTIAL)); assertEquals(1, result.successful()); // Check that the there is no longer a realtime added trip pattern for the trip and that the @@ -222,4 +223,119 @@ public void scheduledTripWithPreviouslySkipped() { assertEquals(90, originalTripTimesForToday.getDepartureDelay(2)); } } + + /** + * Tests a mixture of SKIPPED and NO_DATA. + */ + @Test + public void skippedNoData() { + var env = RealtimeTestEnvironment.gtfs(); + + final FeedScopedId tripId = env.trip2.getId(); + var builder = new TripUpdateBuilder(tripId.getId(), SERVICE_DATE, SCHEDULED, env.timeZone) + .addNoDataStop(0) + .addSkippedStop(1) + .addNoDataStop(2); + + var tripUpdate = builder.build(); + + assertSuccess(env.applyTripUpdate(tripUpdate)); + + // THEN + var snapshot = env.getTimetableSnapshot(); + + // Original trip pattern + { + final TripPattern originalTripPattern = env.transitModel + .getTransitModelIndex() + .getPatternForTrip() + .get(env.trip2); + + final Timetable originalTimetableForToday = snapshot.resolve( + originalTripPattern, + SERVICE_DATE + ); + final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); + + assertNotSame(originalTimetableForToday, originalTimetableScheduled); + + final int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); + assertTrue( + originalTripIndexScheduled > -1, + "Original trip should be found in scheduled time table" + ); + final TripTimes originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( + originalTripIndexScheduled + ); + assertFalse( + originalTripTimesScheduled.isCanceledOrDeleted(), + "Original trip times should not be canceled in scheduled time table" + ); + assertEquals(RealTimeState.SCHEDULED, originalTripTimesScheduled.getRealTimeState()); + + final int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); + assertTrue( + originalTripIndexForToday > -1, + "Original trip should be found in time table for service date" + ); + final TripTimes originalTripTimesForToday = originalTimetableForToday.getTripTimes( + originalTripIndexForToday + ); + assertTrue( + originalTripTimesForToday.isDeleted(), + "Original trip times should be deleted in time table for service date" + ); + // original trip should be deleted + assertEquals(RealTimeState.DELETED, originalTripTimesForToday.getRealTimeState()); + } + + // New trip pattern + { + final TripPattern newTripPattern = snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE); + assertNotNull(newTripPattern, "New trip pattern should be found"); + + final Timetable newTimetableForToday = snapshot.resolve(newTripPattern, SERVICE_DATE); + final Timetable newTimetableScheduled = snapshot.resolve(newTripPattern, null); + + assertNotSame(newTimetableForToday, newTimetableScheduled); + + assertTrue(newTripPattern.canBoard(0)); + assertFalse(newTripPattern.canBoard(1)); + assertTrue(newTripPattern.canBoard(2)); + + assertEquals( + new NonLocalizedString("Headsign of TestTrip2"), + newTripPattern.getTripHeadsign() + ); + assertEquals( + newTripPattern.getOriginalTripPattern().getTripHeadsign(), + newTripPattern.getTripHeadsign() + ); + + final int newTimetableForTodayModifiedTripIndex = newTimetableForToday.getTripIndex(tripId); + assertTrue( + newTimetableForTodayModifiedTripIndex > -1, + "New trip should be found in time table for service date" + ); + + var newTripTimes = newTimetableForToday.getTripTimes(newTimetableForTodayModifiedTripIndex); + assertEquals(RealTimeState.UPDATED, newTripTimes.getRealTimeState()); + + assertEquals( + -1, + newTimetableScheduled.getTripIndex(tripId), + "New trip should not be found in scheduled time table" + ); + + assertEquals(0, newTripTimes.getArrivalDelay(0)); + assertEquals(0, newTripTimes.getDepartureDelay(0)); + assertEquals(0, newTripTimes.getArrivalDelay(1)); + assertEquals(0, newTripTimes.getDepartureDelay(1)); + assertEquals(0, newTripTimes.getArrivalDelay(2)); + assertEquals(0, newTripTimes.getDepartureDelay(2)); + assertTrue(newTripTimes.isNoDataStop(0)); + assertTrue(newTripTimes.isCancelledStop(1)); + assertTrue(newTripTimes.isNoDataStop(2)); + } + } } diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java index 00831333943..d0b3ad5dc31 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java @@ -3,12 +3,12 @@ import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.test.support.UpdateResultAssertions.assertFailure; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NO_SERVICE_ON_DATE; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import java.time.LocalDate; import java.util.List; -import java.util.Set; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; @@ -40,7 +40,6 @@ public void invalidTripDate(LocalDate date) { var snapshot = env.getTimetableSnapshot(); assertTrue(snapshot.isEmpty()); assertEquals(1, result.failed()); - var errors = result.failures().keySet(); - assertEquals(Set.of(NO_SERVICE_ON_DATE), errors); + assertFailure(NO_SERVICE_ON_DATE, result); } } From 7cb284b43b936ecd40ed37931e82c4c697fe185b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 14:30:35 +0200 Subject: [PATCH 034/192] Move ADDED tests --- .../updater/trip/RealtimeTestEnvironment.java | 9 +- .../trip/TimetableSnapshotSourceTest.java | 217 ------------------ .../trip/moduletests/addition/AddedTest.java | 207 +++++++++++++++++ .../trip/moduletests/delay/SkippedTest.java | 1 - .../rejection/InvalidInputTest.java | 2 - 5 files changed, 213 insertions(+), 223 deletions(-) create mode 100644 src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index fdd726eb624..98f339a4cc7 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -56,16 +56,19 @@ public final class RealtimeTestEnvironment { ); public static final LocalDate SERVICE_DATE = LocalDate.of(2024, 5, 8); public static final FeedScopedId SERVICE_ID = TransitModelForTest.id("CAL_1"); + public static final String STOP_A1_ID = "A1"; + public static final String STOP_B1_ID = "B1"; + public static final String STOP_C1_ID = "C1"; private final TransitModelForTest testModel = TransitModelForTest.of(); public final ZoneId timeZone = ZoneId.of(TransitModelForTest.TIME_ZONE_ID); public final Station stationA = testModel.station("A").build(); public final Station stationB = testModel.station("B").build(); public final Station stationC = testModel.station("C").build(); public final Station stationD = testModel.station("D").build(); - public final RegularStop stopA1 = testModel.stop("A1").withParentStation(stationA).build(); - public final RegularStop stopB1 = testModel.stop("B1").withParentStation(stationB).build(); + public final RegularStop stopA1 = testModel.stop(STOP_A1_ID).withParentStation(stationA).build(); + public final RegularStop stopB1 = testModel.stop(STOP_B1_ID).withParentStation(stationB).build(); public final RegularStop stopB2 = testModel.stop("B2").withParentStation(stationB).build(); - public final RegularStop stopC1 = testModel.stop("C1").withParentStation(stationC).build(); + public final RegularStop stopC1 = testModel.stop(STOP_C1_ID).withParentStation(stationC).build(); public final RegularStop stopD1 = testModel.stop("D1").withParentStation(stationD).build(); public final StopModel stopModel = testModel .stopModelBuilder() diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java index 9cf69821aff..7ae7b7386f3 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java @@ -1,6 +1,5 @@ package org.opentripplanner.updater.trip; -import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.ADDED; import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.CANCELED; import static com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate.ScheduleRelationship.SKIPPED; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -17,22 +16,18 @@ import com.google.transit.realtime.GtfsRealtime.TripUpdate; import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeEvent; import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate; -import de.mfdz.MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType; import java.time.Duration; import java.time.LocalDate; import java.util.List; import javax.annotation.Nonnull; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.opentripplanner.ConstantsForTests; import org.opentripplanner.TestOtpModel; import org.opentripplanner._support.time.ZoneIds; import org.opentripplanner.framework.time.ServiceDateUtils; -import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TimetableSnapshot; -import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.RealTimeState; @@ -41,7 +36,6 @@ import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; import org.opentripplanner.updater.TimetableSnapshotSourceParameters; -import org.opentripplanner.updater.spi.UpdateSuccess.WarningType; public class TimetableSnapshotSourceTest { @@ -308,217 +302,6 @@ public void testHandleModifiedTrip() { } } - @Nested - class Added { - - final String addedTripId = "added_trip"; - - @Test - public void addedTrip() { - var builder = new TripUpdateBuilder( - addedTripId, - SERVICE_DATE, - ADDED, - transitModel.getTimeZone() - ); - - builder.addStopTime("A", 30).addStopTime("C", 40).addStopTime("E", 55); - - var tripUpdate = builder.build(); - - var updater = defaultUpdater(); - - // WHEN - updater.applyTripUpdates( - TRIP_MATCHER_NOOP, - REQUIRED_NO_DATA, - DIFFERENTIAL, - List.of(tripUpdate), - feedId - ); - - // THEN - assertAddedTrip(SERVICE_DATE, this.addedTripId, updater); - } - - private TripPattern assertAddedTrip( - LocalDate serviceDate, - String tripId, - TimetableSnapshotSource updater - ) { - var stopA = transitModel.getStopModel().getRegularStop(new FeedScopedId(feedId, "A")); - // Get the trip pattern of the added trip which goes through stopA - var snapshot = updater.getTimetableSnapshot(); - var patternsAtA = snapshot.getPatternsForStop(stopA); - - assertNotNull(patternsAtA, "Added trip pattern should be found"); - assertEquals(1, patternsAtA.size()); - var tripPattern = patternsAtA.stream().findFirst().get(); - - final Timetable forToday = snapshot.resolve(tripPattern, serviceDate); - final Timetable schedule = snapshot.resolve(tripPattern, null); - - assertNotSame(forToday, schedule); - - final int forTodayAddedTripIndex = forToday.getTripIndex(tripId); - assertTrue( - forTodayAddedTripIndex > -1, - "Added trip should be found in time table for service date" - ); - assertEquals( - RealTimeState.ADDED, - forToday.getTripTimes(forTodayAddedTripIndex).getRealTimeState() - ); - - final int scheduleTripIndex = schedule.getTripIndex(tripId); - assertEquals(-1, scheduleTripIndex, "Added trip should not be found in scheduled time table"); - return tripPattern; - } - - @Test - public void addedTripWithNewRoute() { - // GIVEN - - final var builder = new TripUpdateBuilder( - addedTripId, - SERVICE_DATE, - ADDED, - transitModel.getTimeZone() - ); - // add extension to set route name, url, mode - builder.addTripExtension(); - - builder - .addStopTime("A", 30, DropOffPickupType.PHONE_AGENCY) - .addStopTime("C", 40, DropOffPickupType.COORDINATE_WITH_DRIVER) - .addStopTime("E", 55, DropOffPickupType.NONE); - - var tripUpdate = builder.build(); - - var updater = defaultUpdater(); - - // WHEN - var result = updater.applyTripUpdates( - TRIP_MATCHER_NOOP, - REQUIRED_NO_DATA, - DIFFERENTIAL, - List.of(tripUpdate), - feedId - ); - - // THEN - - assertTrue(result.warnings().isEmpty()); - - var pattern = assertAddedTrip(SERVICE_DATE, addedTripId, updater); - - var route = pattern.getRoute(); - assertEquals(TripUpdateBuilder.ROUTE_URL, route.getUrl()); - assertEquals(TripUpdateBuilder.ROUTE_NAME, route.getName()); - assertEquals(TransitMode.RAIL, route.getMode()); - - var fromTransitModel = transitModel.getTransitModelIndex().getRouteForId(route.getId()); - assertEquals(fromTransitModel, route); - - assertEquals(PickDrop.CALL_AGENCY, pattern.getBoardType(0)); - assertEquals(PickDrop.CALL_AGENCY, pattern.getAlightType(0)); - - assertEquals(PickDrop.COORDINATE_WITH_DRIVER, pattern.getBoardType(1)); - assertEquals(PickDrop.COORDINATE_WITH_DRIVER, pattern.getAlightType(1)); - } - - @Test - public void addedWithUnknownStop() { - // GIVEN - final var builder = new TripUpdateBuilder( - addedTripId, - SERVICE_DATE, - ADDED, - transitModel.getTimeZone() - ); - // add extension to set route name, url, mode - builder.addTripExtension(); - - builder - .addStopTime("A", 30, DropOffPickupType.PHONE_AGENCY) - .addStopTime("UNKNOWN_STOP_ID", 40, DropOffPickupType.COORDINATE_WITH_DRIVER) - .addStopTime("E", 55, DropOffPickupType.NONE); - - var tripUpdate = builder.build(); - - var updater = defaultUpdater(); - - // WHEN - var result = updater.applyTripUpdates( - TRIP_MATCHER_NOOP, - REQUIRED_NO_DATA, - DIFFERENTIAL, - List.of(tripUpdate), - feedId - ); - - // THEN - - assertFalse(result.warnings().isEmpty()); - - assertEquals(List.of(WarningType.UNKNOWN_STOPS_REMOVED_FROM_ADDED_TRIP), result.warnings()); - - var pattern = assertAddedTrip(SERVICE_DATE, addedTripId, updater); - - assertEquals(2, pattern.getStops().size()); - } - - @Test - public void repeatedlyAddedTripWithNewRoute() { - // GIVEN - - final var builder = new TripUpdateBuilder( - addedTripId, - SERVICE_DATE, - ADDED, - transitModel.getTimeZone() - ); - // add extension to set route name, url, mode - builder.addTripExtension(); - - builder - .addStopTime("A", 30, DropOffPickupType.PHONE_AGENCY) - .addStopTime("C", 40, DropOffPickupType.COORDINATE_WITH_DRIVER) - .addStopTime("E", 55, DropOffPickupType.NONE); - - var tripUpdate = builder.build(); - - var updater = defaultUpdater(); - - // WHEN - updater.applyTripUpdates( - TRIP_MATCHER_NOOP, - REQUIRED_NO_DATA, - DIFFERENTIAL, - List.of(tripUpdate), - feedId - ); - var pattern = assertAddedTrip(SERVICE_DATE, addedTripId, updater); - var firstRoute = pattern.getRoute(); - - // apply the update a second time to check that no new route instance is created but the old one is reused - updater.applyTripUpdates( - TRIP_MATCHER_NOOP, - REQUIRED_NO_DATA, - DIFFERENTIAL, - List.of(tripUpdate), - feedId - ); - var secondPattern = assertAddedTrip(SERVICE_DATE, addedTripId, updater); - var secondRoute = secondPattern.getRoute(); - - // THEN - - assertSame(firstRoute, secondRoute); - assertNotNull(transitModel.getTransitModelIndex().getRouteForId(firstRoute.getId())); - } - } - @Nonnull private TimetableSnapshotSource defaultUpdater() { return new TimetableSnapshotSource( diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java new file mode 100644 index 00000000000..2cce94117b3 --- /dev/null +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -0,0 +1,207 @@ +package org.opentripplanner.updater.trip.moduletests.addition; + +import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.ADDED; +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.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.test.support.UpdateResultAssertions.assertSuccess; +import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; +import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_A1_ID; +import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_B1_ID; +import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_C1_ID; + +import de.mfdz.MfdzRealtimeExtensions; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner.model.PickDrop; +import org.opentripplanner.model.Timetable; +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.updater.spi.UpdateSuccess; +import org.opentripplanner.updater.trip.RealtimeTestEnvironment; +import org.opentripplanner.updater.trip.TripUpdateBuilder; + +public class AddedTest { + + final String ADDED_TRIP_ID = "added_trip"; + + @Test + public void addedTrip() { + var env = RealtimeTestEnvironment.gtfs(); + + var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone) + .addStopTime(STOP_A1_ID, 30) + .addStopTime(STOP_B1_ID, 40) + .addStopTime(STOP_C1_ID, 55) + .build(); + + assertSuccess(env.applyTripUpdate(tripUpdate)); + assertAddedTrip(this.ADDED_TRIP_ID, env); + } + + + @Test + public void addedTripWithNewRoute() { + var env = RealtimeTestEnvironment.gtfs(); + final var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); + // add extension to set route name, url, mode + builder.addTripExtension(); + + builder + .addStopTime( + STOP_A1_ID, + 30, + MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.PHONE_AGENCY + ) + .addStopTime( + STOP_B1_ID, + 40, + MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.COORDINATE_WITH_DRIVER + ) + .addStopTime( + STOP_B1_ID, + 55, + MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.NONE + ); + + var tripUpdate = builder.build(); + + // WHEN + var result = assertSuccess(env.applyTripUpdate(tripUpdate)); + + // THEN + + assertTrue(result.warnings().isEmpty()); + + var pattern = assertAddedTrip(ADDED_TRIP_ID, env); + + var route = pattern.getRoute(); + assertEquals(TripUpdateBuilder.ROUTE_URL, route.getUrl()); + assertEquals(TripUpdateBuilder.ROUTE_NAME, route.getName()); + assertEquals(TransitMode.RAIL, route.getMode()); + + var fromTransitModel = env.transitModel.getTransitModelIndex().getRouteForId(route.getId()); + assertEquals(fromTransitModel, route); + + assertEquals(PickDrop.CALL_AGENCY, pattern.getBoardType(0)); + assertEquals(PickDrop.CALL_AGENCY, pattern.getAlightType(0)); + + assertEquals(PickDrop.COORDINATE_WITH_DRIVER, pattern.getBoardType(1)); + assertEquals(PickDrop.COORDINATE_WITH_DRIVER, pattern.getAlightType(1)); + } + + @Test + public void addedWithUnknownStop() { + var env = RealtimeTestEnvironment.gtfs(); + final var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); + // add extension to set route name, url, mode + builder.addTripExtension(); + + builder + .addStopTime( + STOP_A1_ID, + 30, + MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.PHONE_AGENCY + ) + .addStopTime( + "UNKNOWN_STOP_ID", + 40, + MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.COORDINATE_WITH_DRIVER + ) + .addStopTime( + STOP_C1_ID, + 55, + MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.NONE + ); + + var tripUpdate = builder.build(); + + var result = assertSuccess(env.applyTripUpdate(tripUpdate)); + + assertFalse(result.warnings().isEmpty()); + + assertEquals( + List.of(UpdateSuccess.WarningType.UNKNOWN_STOPS_REMOVED_FROM_ADDED_TRIP), + result.warnings() + ); + + var pattern = assertAddedTrip(ADDED_TRIP_ID, env); + + assertEquals(2, pattern.getStops().size()); + } + + @Test + public void repeatedlyAddedTripWithNewRoute() { + var env = RealtimeTestEnvironment.gtfs(); + final var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); + // add extension to set route name, url, mode + builder.addTripExtension(); + + builder + .addStopTime( + STOP_A1_ID, + 30, + MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.PHONE_AGENCY + ) + .addStopTime( + STOP_B1_ID, + 40, + MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.COORDINATE_WITH_DRIVER + ) + .addStopTime( + STOP_C1_ID, + 55, + MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.NONE + ); + + var tripUpdate = builder.build(); + + assertSuccess(env.applyTripUpdate(tripUpdate)); + var pattern = assertAddedTrip(ADDED_TRIP_ID, env); + var firstRoute = pattern.getRoute(); + + // apply the update a second time to check that no new route instance is created but the old one is reused + env.applyTripUpdate(tripUpdate); + var secondPattern = assertAddedTrip(ADDED_TRIP_ID, env); + var secondRoute = secondPattern.getRoute(); + + // THEN + + assertSame(firstRoute, secondRoute); + assertNotNull(env.transitModel.getTransitModelIndex().getRouteForId(firstRoute.getId())); + } + private TripPattern assertAddedTrip(String tripId, RealtimeTestEnvironment env) { + var snapshot = env.getTimetableSnapshot(); + var stopA = env.transitModel.getStopModel().getRegularStop(env.stopA1.getId()); + // Get the trip pattern of the added trip which goes through stopA + var patternsAtA = env.getTimetableSnapshot().getPatternsForStop(stopA); + + assertNotNull(patternsAtA, "Added trip pattern should be found"); + assertEquals(1, patternsAtA.size()); + var tripPattern = patternsAtA.stream().findFirst().get(); + + final Timetable forToday = snapshot.resolve(tripPattern, SERVICE_DATE); + final Timetable schedule = snapshot.resolve(tripPattern, null); + + assertNotSame(forToday, schedule); + + final int forTodayAddedTripIndex = forToday.getTripIndex(tripId); + assertTrue( + forTodayAddedTripIndex > -1, + "Added trip should be found in time table for service date" + ); + assertEquals( + RealTimeState.ADDED, + forToday.getTripTimes(forTodayAddedTripIndex).getRealTimeState() + ); + + final int scheduleTripIndex = schedule.getTripIndex(tripId); + assertEquals(-1, scheduleTripIndex, "Added trip should not be found in scheduled time table"); + return tripPattern; + } + +} diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index 4424d47495b..fb3ae2fd3bb 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -46,7 +46,6 @@ public void scheduledTripWithSkippedAndScheduled() { assertSuccess(env.applyTripUpdate(tripUpdate)); - final TimetableSnapshot snapshot = env.getTimetableSnapshot(); // Original trip pattern diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java index d0b3ad5dc31..8084f3e64d3 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java @@ -1,7 +1,6 @@ package org.opentripplanner.updater.trip.moduletests.rejection; import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.test.support.UpdateResultAssertions.assertFailure; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NO_SERVICE_ON_DATE; @@ -39,7 +38,6 @@ public void invalidTripDate(LocalDate date) { var snapshot = env.getTimetableSnapshot(); assertTrue(snapshot.isEmpty()); - assertEquals(1, result.failed()); assertFailure(NO_SERVICE_ON_DATE, result); } } From 50701c175474168cfad3fb8ff838a014ac61e267 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 14:39:43 +0200 Subject: [PATCH 035/192] Move ADDED tests --- .../trip/moduletests/addition/AddedTest.java | 3 +- .../trip/moduletests/delay/SkippedTest.java | 50 +++++++------------ 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 2cce94117b3..4d3da180f49 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -43,7 +43,6 @@ public void addedTrip() { assertAddedTrip(this.ADDED_TRIP_ID, env); } - @Test public void addedTripWithNewRoute() { var env = RealtimeTestEnvironment.gtfs(); @@ -174,6 +173,7 @@ public void repeatedlyAddedTripWithNewRoute() { assertSame(firstRoute, secondRoute); assertNotNull(env.transitModel.getTransitModelIndex().getRouteForId(firstRoute.getId())); } + private TripPattern assertAddedTrip(String tripId, RealtimeTestEnvironment env) { var snapshot = env.getTimetableSnapshot(); var stopA = env.transitModel.getStopModel().getRegularStop(env.stopA1.getId()); @@ -203,5 +203,4 @@ private TripPattern assertAddedTrip(String tripId, RealtimeTestEnvironment env) assertEquals(-1, scheduleTripIndex, "Added trip should not be found in scheduled time table"); return tripPattern; } - } diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index fb3ae2fd3bb..20ca8d20b30 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -33,12 +33,7 @@ public void scheduledTripWithSkippedAndScheduled() { var env = RealtimeTestEnvironment.gtfs(); String scheduledTripId = env.trip2.getId().getId(); - var tripUpdate = new TripUpdateBuilder( - scheduledTripId, - RealtimeTestEnvironment.SERVICE_DATE, - SCHEDULED, - env.timeZone - ) + var tripUpdate = new TripUpdateBuilder(scheduledTripId, SERVICE_DATE, SCHEDULED, env.timeZone) .addDelayedStopTime(0, 0) .addSkippedStop(1) .addDelayedStopTime(2, 90) @@ -59,7 +54,7 @@ public void scheduledTripWithSkippedAndScheduled() { final Timetable originalTimetableForToday = snapshot.resolve( originalTripPattern, - RealtimeTestEnvironment.SERVICE_DATE + SERVICE_DATE ); final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); @@ -81,13 +76,10 @@ public void scheduledTripWithSkippedAndScheduled() { { final TripPattern newTripPattern = snapshot.getRealtimeAddedTripPattern( env.trip2.getId(), - RealtimeTestEnvironment.SERVICE_DATE + SERVICE_DATE ); - final Timetable newTimetableForToday = snapshot.resolve( - newTripPattern, - RealtimeTestEnvironment.SERVICE_DATE - ); + final Timetable newTimetableForToday = snapshot.resolve(newTripPattern, SERVICE_DATE); final Timetable newTimetableScheduled = snapshot.resolve(newTripPattern, null); assertNotSame(newTimetableForToday, newTimetableScheduled); @@ -135,12 +127,7 @@ public void scheduledTripWithPreviouslySkipped() { var env = RealtimeTestEnvironment.gtfs(); var tripId = env.trip2.getId(); - var tripUpdate = new TripUpdateBuilder( - tripId.getId(), - RealtimeTestEnvironment.SERVICE_DATE, - SCHEDULED, - env.timeZone - ) + var tripUpdate = new TripUpdateBuilder(tripId.getId(), SERVICE_DATE, SCHEDULED, env.timeZone) .addDelayedStopTime(0, 0) .addSkippedStop(1) .addDelayedStopTime(2, 90) @@ -151,7 +138,7 @@ public void scheduledTripWithPreviouslySkipped() { // Create update to the same trip but now the skipped stop is no longer skipped var scheduledBuilder = new TripUpdateBuilder( tripId.getId(), - RealtimeTestEnvironment.SERVICE_DATE, + SERVICE_DATE, SCHEDULED, env.timeZone ) @@ -170,10 +157,7 @@ public void scheduledTripWithPreviouslySkipped() { var snapshot = env.getTimetableSnapshot(); { - final TripPattern newTripPattern = snapshot.getRealtimeAddedTripPattern( - env.trip2.getId(), - RealtimeTestEnvironment.SERVICE_DATE - ); + final TripPattern newTripPattern = snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE); assertNull(newTripPattern); final Trip trip = env.transitModel.getTransitModelIndex().getTripForId().get(tripId); @@ -183,7 +167,7 @@ public void scheduledTripWithPreviouslySkipped() { .get(trip); final Timetable originalTimetableForToday = snapshot.resolve( originalTripPattern, - RealtimeTestEnvironment.SERVICE_DATE + SERVICE_DATE ); final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); @@ -213,13 +197,15 @@ public void scheduledTripWithPreviouslySkipped() { final TripTimes originalTripTimesForToday = originalTimetableForToday.getTripTimes( originalTripIndexForToday ); - assertEquals(RealTimeState.UPDATED, originalTripTimesForToday.getRealTimeState()); - assertEquals(0, originalTripTimesForToday.getArrivalDelay(0)); - assertEquals(0, originalTripTimesForToday.getDepartureDelay(0)); - assertEquals(50, originalTripTimesForToday.getArrivalDelay(1)); - assertEquals(50, originalTripTimesForToday.getDepartureDelay(1)); - assertEquals(90, originalTripTimesForToday.getArrivalDelay(2)); - assertEquals(90, originalTripTimesForToday.getDepartureDelay(2)); + + assertEquals( + "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", + env.getScheduledTimetable(tripId) + ); + assertEquals( + "UPDATED | A1 0:01 0:01:01 | B1 0:02 0:02:01 | C1 0:02:50 0:02:51", + env.getRealtimeTimetable(tripId, SERVICE_DATE) + ); } } @@ -317,7 +303,7 @@ public void skippedNoData() { "New trip should be found in time table for service date" ); - var newTripTimes = newTimetableForToday.getTripTimes(newTimetableForTodayModifiedTripIndex); + var newTripTimes = newTimetableForToday.getTripTimes(tripId); assertEquals(RealTimeState.UPDATED, newTripTimes.getRealTimeState()); assertEquals( From d85c67a5a741d82e17a597b4ffd15787d6f98724 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 14:53:50 +0200 Subject: [PATCH 036/192] Refactor handling of schedule relationships --- .../updater/trip/TimetableSnapshotSource.java | 50 ++++++------------- .../trip/moduletests/delay/SkippedTest.java | 3 -- 2 files changed, 16 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index cc39d82369b..ba4ffe211b4 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -1,5 +1,6 @@ package org.opentripplanner.updater.trip; +import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.INVALID_ARRIVAL_TIME; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.INVALID_DEPARTURE_TIME; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NOT_IMPLEMENTED_DUPLICATED; @@ -19,6 +20,7 @@ import com.google.common.collect.Multimaps; import com.google.transit.realtime.GtfsRealtime; import com.google.transit.realtime.GtfsRealtime.TripDescriptor; +import com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship; import com.google.transit.realtime.GtfsRealtime.TripUpdate; import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate; import de.mfdz.MfdzRealtimeExtensions; @@ -150,7 +152,7 @@ public UpdateResult applyTripUpdates( return UpdateResult.empty(); } - Map failuresByRelationship = new HashMap<>(); + Map failuresByRelationship = new HashMap<>(); List> results = new ArrayList<>(); snapshotManager.withLock(() -> { @@ -199,11 +201,12 @@ public UpdateResult applyTripUpdates( serviceDate = localDateNow.get(); } // Determine what kind of trip update this is - final TripDescriptor.ScheduleRelationship tripScheduleRelationship = determineTripScheduleRelationship( - tripDescriptor + var scheduleRelationship = Objects.requireNonNullElse( + tripDescriptor.getScheduleRelationship(), + SCHEDULED ); if (updateIncrementality == DIFFERENTIAL) { - purgePatternModifications(tripScheduleRelationship, tripId, serviceDate); + purgePatternModifications(scheduleRelationship, tripId, serviceDate); } uIndex += 1; @@ -213,7 +216,7 @@ public UpdateResult applyTripUpdates( Result result; try { result = - switch (tripScheduleRelationship) { + switch (scheduleRelationship) { case SCHEDULED -> handleScheduledTrip( tripUpdate, tripId, @@ -255,11 +258,11 @@ public UpdateResult applyTripUpdates( if (result.isFailure()) { debug(tripId, "Failed to apply TripUpdate."); LOG.trace(" Contents: {}", tripUpdate); - if (failuresByRelationship.containsKey(tripScheduleRelationship)) { - var c = failuresByRelationship.get(tripScheduleRelationship); - failuresByRelationship.put(tripScheduleRelationship, ++c); + if (failuresByRelationship.containsKey(scheduleRelationship)) { + var c = failuresByRelationship.get(scheduleRelationship); + failuresByRelationship.put(scheduleRelationship, ++c); } else { - failuresByRelationship.put(tripScheduleRelationship, 1); + failuresByRelationship.put(scheduleRelationship, 1); } } } @@ -282,7 +285,7 @@ public UpdateResult applyTripUpdates( * added trip pattern. */ private void purgePatternModifications( - TripDescriptor.ScheduleRelationship tripScheduleRelationship, + ScheduleRelationship tripScheduleRelationship, FeedScopedId tripId, LocalDate serviceDate ) { @@ -290,8 +293,8 @@ private void purgePatternModifications( if ( !isPreviouslyAddedTrip(tripId, pattern, serviceDate) || ( - tripScheduleRelationship != TripDescriptor.ScheduleRelationship.CANCELED && - tripScheduleRelationship != TripDescriptor.ScheduleRelationship.DELETED + tripScheduleRelationship != ScheduleRelationship.CANCELED && + tripScheduleRelationship != ScheduleRelationship.DELETED ) ) { // Remove previous realtime updates for this trip. This is necessary to avoid previous @@ -327,7 +330,7 @@ public TimetableSnapshot getTimetableSnapshot() { private static void logUpdateResult( String feedId, - Map failuresByRelationship, + Map failuresByRelationship, UpdateResult updateResult ) { ResultLogger.logUpdateResult(feedId, "gtfs-rt-trip-updates", updateResult); @@ -345,27 +348,6 @@ private static void logUpdateResult( }); } - /** - * Determine how the trip update should be handled. - * - * @param tripDescriptor trip descriptor - * @return TripDescriptor.ScheduleRelationship indicating how the trip update should be handled - */ - private TripDescriptor.ScheduleRelationship determineTripScheduleRelationship( - final TripDescriptor tripDescriptor - ) { - // Assume default value - TripDescriptor.ScheduleRelationship tripScheduleRelationship = - TripDescriptor.ScheduleRelationship.SCHEDULED; - - // If trip update contains schedule relationship, use it - if (tripDescriptor.hasScheduleRelationship()) { - tripScheduleRelationship = tripDescriptor.getScheduleRelationship(); - } - - return tripScheduleRelationship; - } - private Result handleScheduledTrip( TripUpdate tripUpdate, FeedScopedId tripId, diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index 20ca8d20b30..78770e6f6b7 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -194,9 +194,6 @@ public void scheduledTripWithPreviouslySkipped() { originalTripIndexForToday > -1, "Original trip should be found in time table for service date" ); - final TripTimes originalTripTimesForToday = originalTimetableForToday.getTripTimes( - originalTripIndexForToday - ); assertEquals( "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", From 58005ac84917998876a0203a7f02d110908ca17b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 15:04:49 +0200 Subject: [PATCH 037/192] Simplify access --- .../updater/trip/moduletests/addition/AddedTest.java | 10 +++++----- .../cancellation/CancellationDeletionTest.java | 4 ++-- .../updater/trip/moduletests/delay/SkippedTest.java | 6 +++--- .../trip/moduletests/rejection/InvalidInputTest.java | 4 ++-- .../trip/moduletests/rejection/InvalidTripIdTest.java | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 4d3da180f49..ff03362cbdd 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -25,12 +25,12 @@ import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; -public class AddedTest { +class AddedTest { final String ADDED_TRIP_ID = "added_trip"; @Test - public void addedTrip() { + void addedTrip() { var env = RealtimeTestEnvironment.gtfs(); var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone) @@ -44,7 +44,7 @@ public void addedTrip() { } @Test - public void addedTripWithNewRoute() { + void addedTripWithNewRoute() { var env = RealtimeTestEnvironment.gtfs(); final var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); // add extension to set route name, url, mode @@ -94,7 +94,7 @@ public void addedTripWithNewRoute() { } @Test - public void addedWithUnknownStop() { + void addedWithUnknownStop() { var env = RealtimeTestEnvironment.gtfs(); final var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); // add extension to set route name, url, mode @@ -134,7 +134,7 @@ public void addedWithUnknownStop() { } @Test - public void repeatedlyAddedTripWithNewRoute() { + void repeatedlyAddedTripWithNewRoute() { var env = RealtimeTestEnvironment.gtfs(); final var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); // add extension to set route name, url, mode diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java index b2c31e2254e..b0bb999f350 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java @@ -32,7 +32,7 @@ static List cases() { @ParameterizedTest @MethodSource("cases") - public void cancelledTrip(ScheduleRelationship relationship, RealTimeState state) { + void cancelledTrip(ScheduleRelationship relationship, RealTimeState state) { var env = RealtimeTestEnvironment.gtfs(); var pattern1 = env.getPatternForTrip(env.trip1); @@ -73,7 +73,7 @@ public void cancelledTrip(ScheduleRelationship relationship, RealTimeState state */ @ParameterizedTest @MethodSource("cases") - public void cancelingAddedTrip(ScheduleRelationship relationship, RealTimeState state) { + void cancelingAddedTrip(ScheduleRelationship relationship, RealTimeState state) { var env = RealtimeTestEnvironment.gtfs(); var addedTripId = "added-trip"; // First add ADDED trip diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index 78770e6f6b7..4d91eefd390 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -29,7 +29,7 @@ public class SkippedTest { @Test - public void scheduledTripWithSkippedAndScheduled() { + void scheduledTripWithSkippedAndScheduled() { var env = RealtimeTestEnvironment.gtfs(); String scheduledTripId = env.trip2.getId().getId(); @@ -123,7 +123,7 @@ public void scheduledTripWithSkippedAndScheduled() { * the new stop-skipping trip pattern should also be removed. */ @Test - public void scheduledTripWithPreviouslySkipped() { + void scheduledTripWithPreviouslySkipped() { var env = RealtimeTestEnvironment.gtfs(); var tripId = env.trip2.getId(); @@ -210,7 +210,7 @@ public void scheduledTripWithPreviouslySkipped() { * Tests a mixture of SKIPPED and NO_DATA. */ @Test - public void skippedNoData() { + void skippedNoData() { var env = RealtimeTestEnvironment.gtfs(); final FeedScopedId tripId = env.trip2.getId(); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java index 8084f3e64d3..4d28c60eeb5 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java @@ -17,7 +17,7 @@ * A trip with start date that is outside the service period shouldn't throw an exception and is * ignored instead. */ -public class InvalidInputTest { +class InvalidInputTest { public static List cases() { return List.of(SERVICE_DATE.minusYears(10), SERVICE_DATE.plusYears(10)); @@ -25,7 +25,7 @@ public static List cases() { @ParameterizedTest @MethodSource("cases") - public void invalidTripDate(LocalDate date) { + void invalidTripDate(LocalDate date) { var env = RealtimeTestEnvironment.gtfs(); var update = new TripUpdateBuilder(env.trip1.getId().getId(), date, SCHEDULED, env.timeZone) diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java index 58f9fb2ff55..8d7f7629575 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; -public class InvalidTripIdTest { +class InvalidTripIdTest { static Stream invalidCases() { return Stream.of(null, "", " "); From 3e346d438192d19143dd7da0e9ffbb522c2d7e3d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 15:38:33 +0200 Subject: [PATCH 038/192] Remove unused method --- .../opentripplanner/transit/model/network/TripPattern.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java b/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java index f9734c1cb1a..deab7ee79f4 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java +++ b/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java @@ -334,10 +334,6 @@ public boolean isBoardAndAlightAt(int stopIndex, PickDrop value) { // TODO: These should probably be deprecated. That would require grabbing the scheduled timetable, // and would avoid mistakes where real-time updates are accidentally not taken into account. - public boolean stopPatternIsEqual(TripPattern other) { - return stopPattern.equals(other.stopPattern); - } - public Trip getTrip(int tripIndex) { return scheduledTimetable.getTripTimes(tripIndex).getTrip(); } From 6412eeabe0f94aae12407f7514965021374d00a6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 17 Jun 2024 15:43:48 +0200 Subject: [PATCH 039/192] Remove method that is only used in test --- .../opentripplanner/transit/model/network/TripPattern.java | 7 ------- .../gtfs/interlining/InterlineProcessorTest.java | 5 ++--- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java b/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java index deab7ee79f4..57c71d06113 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java +++ b/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java @@ -331,13 +331,6 @@ public boolean isBoardAndAlightAt(int stopIndex, PickDrop value) { /* METHODS THAT DELEGATE TO THE SCHEDULED TIMETABLE */ - // TODO: These should probably be deprecated. That would require grabbing the scheduled timetable, - // and would avoid mistakes where real-time updates are accidentally not taken into account. - - public Trip getTrip(int tripIndex) { - return scheduledTimetable.getTripTimes(tripIndex).getTrip(); - } - // TODO OTP2 this method modifies the state, it will be refactored in a subsequent step /** * Add the given tripTimes to this pattern's scheduled timetable, recording the corresponding trip diff --git a/src/test/java/org/opentripplanner/gtfs/interlining/InterlineProcessorTest.java b/src/test/java/org/opentripplanner/gtfs/interlining/InterlineProcessorTest.java index 67ca61f0403..a1aa4f9d753 100644 --- a/src/test/java/org/opentripplanner/gtfs/interlining/InterlineProcessorTest.java +++ b/src/test/java/org/opentripplanner/gtfs/interlining/InterlineProcessorTest.java @@ -125,9 +125,8 @@ void testInterline( void staySeatedNotAllowed() { var transferService = new DefaultTransferService(); - var fromTrip = patterns.get(0).getTrip(0); - var toTrip = patterns.get(1).getTrip(0); - + var fromTrip = patterns.get(0).getScheduledTimetable().getTripTimes().get(0).getTrip(); + var toTrip = patterns.get(1).getScheduledTimetable().getTripTimes().get(0).getTrip(); var notAllowed = new StaySeatedNotAllowed(fromTrip, toTrip); var calendarService = new CalendarServiceData(); From 4d0dc4c7004794496384305376ffa6ae38ac67a4 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Mon, 17 Jun 2024 17:11:58 +0200 Subject: [PATCH 040/192] Add SingeCriteriaComparator to itinerary filter We will use this later to improve group-by-filter --- .../system/SingeCriteriaComparator.java | 81 +++++++++ .../model/plan/TestItineraryBuilder.java | 42 +++-- .../system/SingeCriteriaComparatorTest.java | 162 ++++++++++++++++++ 3 files changed, 270 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java create mode 100644 src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java new file mode 100644 index 00000000000..8f757cee5a1 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java @@ -0,0 +1,81 @@ +package org.opentripplanner.routing.algorithm.filterchain.filters.system; + +import java.util.Comparator; +import java.util.function.ToIntFunction; +import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; + +/** + * Comparator used to compare a SINGE criteria for dominance. The difference between this and the + * {@link org.opentripplanner.raptor.util.paretoset.ParetoComparator} is that: + *

    + *
  1. This applies to one criteria, not multiple.
  2. + *
  3. This interface apply to itineraries; It is not generic.
  4. + *
+ * A set of instances of this interface can be used to create a pareto-set. See + * {@link org.opentripplanner.raptor.util.paretoset.ParetoSet} and + * {@link org.opentripplanner.raptor.util.paretoset.ParetoComparator}. + *

+ * This interface extends {@link Comparator} so elements can be sorted as well. Not all criteria + * can be sorted, if so the {@link #strictOrder()} should return false (this is the default). + */ +@FunctionalInterface +public interface SingeCriteriaComparator extends Comparator { + /** + * The left criteria dominates the right criteria. Note! The right criteria my dominate + * the left criteria if there is no {@link #strictOrder()}. If left and right are equals, then + * there is no dominance. + */ + boolean leftDominanceExist(Itinerary left, Itinerary right); + + /** + * The compare function can be used to order elements based on the criteria for this instance. + * Note! This method should not be used if there is no {@link #strictOrder()}. + */ + @Override + default int compare(Itinerary left, Itinerary right) { + throw new IllegalStateException( + "This criteria can not be used to sort elements, there is no deterministic defined order." + ); + } + + /** + * Return true if the criteria can be deterministically sorted. + */ + default boolean strictOrder() { + return false; + } + + static SingeCriteriaComparator compareNumTransfers() { + return compareLessThan(Itinerary::getNumberOfTransfers); + } + + static SingeCriteriaComparator compareGeneralizedCost() { + return compareLessThan(Itinerary::getGeneralizedCost); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + static SingeCriteriaComparator compareTransitPriorityGroups() { + return (left, right) -> TransitGroupPriority32n.dominate(left.getGeneralizedCost2().get(), right.getGeneralizedCost2().get()); + } + + + static SingeCriteriaComparator compareLessThan(final ToIntFunction op) { + return new SingeCriteriaComparator() { + @Override + public boolean leftDominanceExist(Itinerary left, Itinerary right) { + return op.applyAsInt(left) < op.applyAsInt(right); + } + + @Override + public int compare(Itinerary left, Itinerary right) { + return op.applyAsInt(left) - op.applyAsInt(right); + } + + @Override + public boolean strictOrder() { + return true; + } + }; + } +} diff --git a/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java b/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java index 28be5b3a7e2..ddff2e76fca 100644 --- a/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java +++ b/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java @@ -55,6 +55,8 @@ */ public class TestItineraryBuilder implements PlanTestConstants { + private static final int NOT_SET = -1_999_999; + public static final LocalDate SERVICE_DAY = LocalDate.of(2020, Month.FEBRUARY, 2); public static final Route BUS_ROUTE = route("1").withMode(TransitMode.BUS).build(); public static final Route RAIL_ROUTE = route("2").withMode(TransitMode.RAIL).build(); @@ -69,7 +71,8 @@ public class TestItineraryBuilder implements PlanTestConstants { private final List legs = new ArrayList<>(); private Place lastPlace; private int lastEndTime; - private int cost = 0; + private int c1 = 0; + private int c2 = NOT_SET; private TestItineraryBuilder(Place origin, int startTime) { this.lastPlace = origin; @@ -241,7 +244,7 @@ public TestItineraryBuilder flex(int start, int end, Place to) { FlexibleTransitLeg leg = new FlexibleTransitLeg(edge, newTime(start), newTime(end), legCost); legs.add(leg); - cost += legCost; + c1 += legCost; // Setup for adding another leg lastEndTime = end; @@ -330,17 +333,6 @@ public TestItineraryBuilder faresV2Rail( ); } - public Itinerary egress(int walkDuration) { - walk(walkDuration, null); - return build(); - } - - public Itinerary build() { - Itinerary itinerary = new Itinerary(legs); - itinerary.setGeneralizedCost(cost); - return itinerary; - } - public TestItineraryBuilder frequencyBus(int tripId, int startTime, int endTime, Place to) { return transit( RAIL_ROUTE, @@ -401,6 +393,26 @@ public TestItineraryBuilder carHail(int duration, Place to) { return this; } + public TestItineraryBuilder withGeneralizedCost2(int c2) { + this.c2 = c2; + return this; + } + + public Itinerary egress(int walkDuration) { + walk(walkDuration, null); + return build(); + } + + public Itinerary build() { + Itinerary itinerary = new Itinerary(legs); + itinerary.setGeneralizedCost(c1); + if(c2 != NOT_SET) { + itinerary.setGeneralizedCost2(c2); + } + return itinerary; + } + + /* private methods */ /** Create a dummy trip */ @@ -506,7 +518,7 @@ public TestItineraryBuilder transit( leg.setDistanceMeters(speed(leg.getMode()) * (end - start)); legs.add(leg); - cost += legCost; + c1 += legCost; // Setup for adding another leg lastEndTime = end; @@ -536,7 +548,7 @@ private StreetLeg streetLeg( .build(); legs.add(leg); - cost += legCost; + c1 += legCost; // Setup for adding another leg lastEndTime = endTime; diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java new file mode 100644 index 00000000000..d94d21673c4 --- /dev/null +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java @@ -0,0 +1,162 @@ +package org.opentripplanner.routing.algorithm.filterchain.filters.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.collection.CompositeComparator; +import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.model.plan.Place; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; +import org.opentripplanner.transit.model._data.TransitModelForTest; + +class SingeCriteriaComparatorTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + + private static final Place A = TEST_MODEL.place("A", 10, 11); + private static final Place B = TEST_MODEL.place("B", 10, 13); + private static final Place C = TEST_MODEL.place("C", 10, 14); + private static final Place D = TEST_MODEL.place("D", 10, 15); + + private static final int START = 1000; + private static final int TX_AT = 1500; + private static final int END_LOW = 2000; + + // [Tx, Cost] => [0, 1240] + private static final Itinerary zeroTransferLowCost = newItinerary(A) + .bus(1, START, END_LOW, B) + .walk(60, C) + .build(); + // [Tx, Cost] => [0, 1360] + private static final Itinerary zeroTransferHighCost = newItinerary(A) + .bus(1, START, END_LOW, B) + .walk(120, C) + .build(); + // [Tx, Cost] => [1, 1240] + private static final Itinerary oneTransferLowCost = newItinerary(A) + .bus(1, START, TX_AT, B) + .bus(2, TX_AT, END_LOW, C) + .build(); + + @BeforeAll + static void setUp() { + assertEquals(0, zeroTransferLowCost.getNumberOfTransfers()); + assertEquals(0, zeroTransferHighCost.getNumberOfTransfers()); + assertEquals(1, oneTransferLowCost.getNumberOfTransfers()); + + int expectedCost = zeroTransferLowCost.getGeneralizedCost(); + assertTrue(expectedCost < zeroTransferHighCost.getGeneralizedCost()); + assertEquals(expectedCost, oneTransferLowCost.getGeneralizedCost()); + } + + @Test + void compare() { + var l = new ArrayList(); + l.add(zeroTransferHighCost); + l.add(zeroTransferLowCost); + l.add(oneTransferLowCost); + + l.sort( + new CompositeComparator<>( + SingeCriteriaComparator.compareGeneralizedCost(), + SingeCriteriaComparator.compareNumTransfers() + ) + ); + + assertEquals(List.of(zeroTransferLowCost, oneTransferLowCost, zeroTransferHighCost), l); + } + + @Test + void compareThrowsExceptionIfNotStrictOrder() { + assertThrows( + IllegalStateException.class, + () -> + SingeCriteriaComparator + .compareTransitPriorityGroups() + .compare(zeroTransferLowCost, zeroTransferHighCost) + ); + } + + @Test + void strictOrder() { + assertTrue(SingeCriteriaComparator.compareNumTransfers().strictOrder()); + assertTrue(SingeCriteriaComparator.compareGeneralizedCost().strictOrder()); + assertFalse(SingeCriteriaComparator.compareTransitPriorityGroups().strictOrder()); + } + + @Test + void compareNumTransfers() { + var subject = SingeCriteriaComparator.compareNumTransfers(); + + // leftDominanceExist + assertFalse(subject.leftDominanceExist(zeroTransferHighCost, zeroTransferLowCost)); + Assertions.assertTrue(subject.leftDominanceExist(zeroTransferLowCost, oneTransferLowCost)); + assertFalse(subject.leftDominanceExist(oneTransferLowCost, zeroTransferLowCost)); + + // strict order expected + assertTrue(subject.strictOrder()); + + // Compare + assertEquals(0, subject.compare(zeroTransferHighCost, zeroTransferLowCost)); + assertEquals(-1, subject.compare(zeroTransferLowCost, oneTransferLowCost)); + assertEquals(1, subject.compare(oneTransferLowCost, zeroTransferLowCost)); + } + + @Test + void compareGeneralizedCost() { + var subject = SingeCriteriaComparator.compareGeneralizedCost(); + + System.out.println(zeroTransferLowCost.getGeneralizedCost()); + System.out.println(zeroTransferHighCost.getGeneralizedCost()); + System.out.println(oneTransferLowCost.getGeneralizedCost()); + + // leftDominanceExist + assertFalse(subject.leftDominanceExist(zeroTransferHighCost, zeroTransferLowCost)); + assertTrue(subject.leftDominanceExist(zeroTransferLowCost, zeroTransferHighCost)); + assertFalse(subject.leftDominanceExist(zeroTransferLowCost, oneTransferLowCost)); + + // strict order expected + assertTrue(subject.strictOrder()); + + // Compare + assertTrue(0 < subject.compare(zeroTransferHighCost, zeroTransferLowCost)); + assertTrue(0 > subject.compare(zeroTransferLowCost, zeroTransferHighCost)); + assertEquals(0, subject.compare(zeroTransferLowCost, oneTransferLowCost)); + } + + @Test + void compareTransitPriorityGroups() { + var group1 = newItinerary(A) + .bus(1, START, END_LOW, C) + .withGeneralizedCost2(1) + .build(); + var group2 = newItinerary(A) + .bus(1, START, END_LOW, C) + .withGeneralizedCost2(2) + .build(); + var group1And2 = newItinerary(A) + .bus(1, START, END_LOW, C) + .withGeneralizedCost2(TransitGroupPriority32n.mergeInGroupId(1, 2)) + .build(); + + var subject = SingeCriteriaComparator.compareTransitPriorityGroups(); + + assertTrue(subject.leftDominanceExist(group1, group2)); + assertTrue(subject.leftDominanceExist(group2, group1)); + assertTrue(subject.leftDominanceExist(group1, group1And2)); + assertTrue(subject.leftDominanceExist(group2, group1And2)); + assertFalse(subject.leftDominanceExist(group1And2, group1)); + assertFalse(subject.leftDominanceExist(group1And2, group1)); + + // Cannot be ordered => compare will fail + assertFalse(subject.strictOrder()); + } +} From f549d053c39a6dbb84fa94b50f74b851fa842d65 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 18 Jun 2024 12:34:54 +0200 Subject: [PATCH 041/192] Mention new filter engine --- .../opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index 41e2b812420..f43bdf7decc 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -15,6 +15,9 @@ /** * Encapsulates the logic to filter patterns by the service dates that they operate on. It also * has a method to filter routes by checking if their patterns operate on the required days + *

+ * Once a more complete filtering engine is in place in the core data model, this code should be + * there rather than a separate class in the API package. */ public class PatternByServiceDatesFilter { From 6844979453abbeaf213e4c6063574a19ae6b420a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 18 Jun 2024 12:43:26 +0200 Subject: [PATCH 042/192] Return error code for invalid trip IDs --- .../updater/trip/TimetableSnapshotSource.java | 2 ++ .../trip/moduletests/rejection/InvalidTripIdTest.java | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index ba4ffe211b4..c452d5f58f8 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -3,6 +3,7 @@ import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.INVALID_ARRIVAL_TIME; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.INVALID_DEPARTURE_TIME; +import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.INVALID_INPUT_STRUCTURE; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NOT_IMPLEMENTED_DUPLICATED; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NOT_IMPLEMENTED_UNSCHEDULED; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NO_SERVICE_ON_DATE; @@ -178,6 +179,7 @@ public UpdateResult applyTripUpdates( if (!tripDescriptor.hasTripId() || tripDescriptor.getTripId().isBlank()) { debug(feedId, "", "No trip id found for gtfs-rt trip update: \n{}", tripUpdate); + results.add(Result.failure(UpdateError.noTripId(INVALID_INPUT_STRUCTURE))); continue; } diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java index 8d7f7629575..6cacaeb6ca9 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java @@ -1,6 +1,7 @@ package org.opentripplanner.updater.trip.moduletests.rejection; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.test.support.UpdateResultAssertions.assertFailure; +import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.INVALID_INPUT_STRUCTURE; import com.google.transit.realtime.GtfsRealtime; import java.util.stream.Stream; @@ -35,6 +36,6 @@ void invalidTripId(String tripId) { var result = env.applyTripUpdate(tripUpdate); - assertEquals(0, result.successful()); + assertFailure(INVALID_INPUT_STRUCTURE, result); } } From 17858f56899971370b9200f16dc35ab619052862 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 18 Jun 2024 13:24:50 +0200 Subject: [PATCH 043/192] Make module tests a lot more compact --- .../trip/moduletests/addition/AddedTest.java | 56 ++------ .../CancellationDeletionTest.java | 15 +-- .../trip/moduletests/delay/DelayedTest.java | 77 ++++------- .../trip/moduletests/delay/SkippedTest.java | 123 +++++++----------- .../rejection/InvalidInputTest.java | 2 - 5 files changed, 93 insertions(+), 180 deletions(-) diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index ff03362cbdd..6a7d076923f 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -13,7 +13,7 @@ import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_B1_ID; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_C1_ID; -import de.mfdz.MfdzRealtimeExtensions; +import de.mfdz.MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType; import java.util.List; import org.junit.jupiter.api.Test; import org.opentripplanner.model.PickDrop; @@ -51,21 +51,9 @@ void addedTripWithNewRoute() { builder.addTripExtension(); builder - .addStopTime( - STOP_A1_ID, - 30, - MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.PHONE_AGENCY - ) - .addStopTime( - STOP_B1_ID, - 40, - MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.COORDINATE_WITH_DRIVER - ) - .addStopTime( - STOP_B1_ID, - 55, - MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.NONE - ); + .addStopTime(STOP_A1_ID, 30, DropOffPickupType.PHONE_AGENCY) + .addStopTime(STOP_B1_ID, 40, DropOffPickupType.COORDINATE_WITH_DRIVER) + .addStopTime(STOP_B1_ID, 55, DropOffPickupType.NONE); var tripUpdate = builder.build(); @@ -101,21 +89,9 @@ void addedWithUnknownStop() { builder.addTripExtension(); builder - .addStopTime( - STOP_A1_ID, - 30, - MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.PHONE_AGENCY - ) - .addStopTime( - "UNKNOWN_STOP_ID", - 40, - MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.COORDINATE_WITH_DRIVER - ) - .addStopTime( - STOP_C1_ID, - 55, - MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.NONE - ); + .addStopTime(STOP_A1_ID, 30, DropOffPickupType.PHONE_AGENCY) + .addStopTime("UNKNOWN_STOP_ID", 40, DropOffPickupType.COORDINATE_WITH_DRIVER) + .addStopTime(STOP_C1_ID, 55, DropOffPickupType.NONE); var tripUpdate = builder.build(); @@ -141,21 +117,9 @@ void repeatedlyAddedTripWithNewRoute() { builder.addTripExtension(); builder - .addStopTime( - STOP_A1_ID, - 30, - MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.PHONE_AGENCY - ) - .addStopTime( - STOP_B1_ID, - 40, - MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.COORDINATE_WITH_DRIVER - ) - .addStopTime( - STOP_C1_ID, - 55, - MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType.NONE - ); + .addStopTime(STOP_A1_ID, 30, DropOffPickupType.PHONE_AGENCY) + .addStopTime(STOP_B1_ID, 40, DropOffPickupType.COORDINATE_WITH_DRIVER) + .addStopTime(STOP_C1_ID, 55, DropOffPickupType.NONE); var tripUpdate = builder.build(); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java index b0bb999f350..41e42bf00db 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.test.support.UpdateResultAssertions.assertSuccess; import static org.opentripplanner.updater.trip.UpdateIncrementality.DIFFERENTIAL; import com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship; @@ -45,11 +46,9 @@ void cancelledTrip(ScheduleRelationship relationship, RealTimeState state) { env.timeZone ) .build(); - var result = env.applyTripUpdate(update); + assertSuccess(env.applyTripUpdate(update)); - assertEquals(1, result.successful()); - - final TimetableSnapshot snapshot = env.getTimetableSnapshot(); + var snapshot = env.getTimetableSnapshot(); final Timetable forToday = snapshot.resolve(pattern1, RealtimeTestEnvironment.SERVICE_DATE); final Timetable schedule = snapshot.resolve(pattern1, null); assertNotSame(forToday, schedule); @@ -88,9 +87,7 @@ void cancelingAddedTrip(ScheduleRelationship relationship, RealTimeState state) .addStopTime(env.stopC1.getId().getId(), 55) .build(); - var result = env.applyTripUpdate(update, DIFFERENTIAL); - - assertEquals(1, result.successful()); + assertSuccess(env.applyTripUpdate(update, DIFFERENTIAL)); // Cancel or delete the added trip update = @@ -101,9 +98,7 @@ void cancelingAddedTrip(ScheduleRelationship relationship, RealTimeState state) env.timeZone ) .build(); - result = env.applyTripUpdate(update, DIFFERENTIAL); - - assertEquals(1, result.successful()); + assertSuccess(env.applyTripUpdate(update, DIFFERENTIAL)); final TimetableSnapshot snapshot = env.getTimetableSnapshot(); // Get the trip pattern of the added trip which goes through stopA diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java index 5752e4941be..7440a189846 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.test.support.UpdateResultAssertions.assertSuccess; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; @@ -43,53 +42,34 @@ void singleStopDelay() { assertEquals(1, result.successful()); - // trip1 should be modified - { - var pattern1 = env.getPatternForTrip(env.trip1); - final int trip1Index = pattern1.getScheduledTimetable().getTripIndex(env.trip1.getId()); - - final TimetableSnapshot snapshot = env.getTimetableSnapshot(); - final Timetable trip1Realtime = snapshot.resolve( - pattern1, - RealtimeTestEnvironment.SERVICE_DATE - ); - final Timetable trip1Scheduled = snapshot.resolve(pattern1, null); - - assertNotSame(trip1Realtime, trip1Scheduled); - assertNotSame( - trip1Realtime.getTripTimes(trip1Index), - trip1Scheduled.getTripTimes(trip1Index) - ); - assertEquals(1, trip1Realtime.getTripTimes(trip1Index).getArrivalDelay(STOP_SEQUENCE)); - assertEquals(1, trip1Realtime.getTripTimes(trip1Index).getDepartureDelay(STOP_SEQUENCE)); - - assertEquals( - RealTimeState.SCHEDULED, - trip1Scheduled.getTripTimes(trip1Index).getRealTimeState() - ); - assertEquals( - RealTimeState.UPDATED, - trip1Realtime.getTripTimes(trip1Index).getRealTimeState() - ); - } - - // trip2 should keep the scheduled information - { - var pattern = env.getPatternForTrip(env.trip2); - final int tripIndex = pattern.getScheduledTimetable().getTripIndex(env.trip2.getId()); - - final TimetableSnapshot snapshot = env.getTimetableSnapshot(); - final Timetable realtime = snapshot.resolve(pattern, RealtimeTestEnvironment.SERVICE_DATE); - final Timetable scheduled = snapshot.resolve(pattern, null); - - assertSame(realtime, scheduled); - assertSame(realtime.getTripTimes(tripIndex), scheduled.getTripTimes(tripIndex)); - assertEquals(0, realtime.getTripTimes(tripIndex).getArrivalDelay(STOP_SEQUENCE)); - assertEquals(0, realtime.getTripTimes(tripIndex).getDepartureDelay(STOP_SEQUENCE)); - - assertEquals(RealTimeState.SCHEDULED, scheduled.getTripTimes(tripIndex).getRealTimeState()); - assertEquals(RealTimeState.SCHEDULED, realtime.getTripTimes(tripIndex).getRealTimeState()); - } + var pattern1 = env.getPatternForTrip(env.trip1); + int trip1Index = pattern1.getScheduledTimetable().getTripIndex(env.trip1.getId()); + + final TimetableSnapshot snapshot = env.getTimetableSnapshot(); + final Timetable trip1Realtime = snapshot.resolve( + pattern1, + RealtimeTestEnvironment.SERVICE_DATE + ); + final Timetable trip1Scheduled = snapshot.resolve(pattern1, null); + + assertNotSame(trip1Realtime, trip1Scheduled); + assertNotSame(trip1Realtime.getTripTimes(trip1Index), trip1Scheduled.getTripTimes(trip1Index)); + assertEquals(DELAY, trip1Realtime.getTripTimes(trip1Index).getArrivalDelay(STOP_SEQUENCE)); + assertEquals(DELAY, trip1Realtime.getTripTimes(trip1Index).getDepartureDelay(STOP_SEQUENCE)); + + assertEquals( + RealTimeState.SCHEDULED, + trip1Scheduled.getTripTimes(trip1Index).getRealTimeState() + ); + + assertEquals( + "SCHEDULED | A1 0:00:10 0:00:11 | B1 0:00:20 0:00:21", + env.getScheduledTimetable(env.trip1.getId()) + ); + assertEquals( + "UPDATED | A1 [ND] 0:00:10 0:00:11 | B1 0:00:21 0:00:22", + env.getRealtimeTimetable(env.trip1.getId().getId()) + ); } /** @@ -110,7 +90,6 @@ void complexDelay() { assertSuccess(env.applyTripUpdate(tripUpdate)); - // THEN final TimetableSnapshot snapshot = env.getTimetableSnapshot(); final TripPattern originalTripPattern = env.transitModel diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index 4d91eefd390..c70dd380602 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -13,13 +13,10 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.framework.i18n.NonLocalizedString; -import org.opentripplanner.model.Timetable; -import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.model.timetable.TripTimesStringBuilder; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; @@ -41,27 +38,24 @@ void scheduledTripWithSkippedAndScheduled() { assertSuccess(env.applyTripUpdate(tripUpdate)); - final TimetableSnapshot snapshot = env.getTimetableSnapshot(); + var snapshot = env.getTimetableSnapshot(); // Original trip pattern { - final FeedScopedId tripId = env.trip2.getId(); - final Trip trip = env.transitModel.getTransitModelIndex().getTripForId().get(tripId); - final TripPattern originalTripPattern = env.transitModel + var tripId = env.trip2.getId(); + var trip = env.transitModel.getTransitModelIndex().getTripForId().get(tripId); + var originalTripPattern = env.transitModel .getTransitModelIndex() .getPatternForTrip() .get(trip); - final Timetable originalTimetableForToday = snapshot.resolve( - originalTripPattern, - SERVICE_DATE - ); - final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); + var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); + var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); assertNotSame(originalTimetableForToday, originalTimetableScheduled); - final int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); - final TripTimes originalTripTimesForToday = originalTimetableForToday.getTripTimes( + int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); + var originalTripTimesForToday = originalTimetableForToday.getTripTimes( originalTripIndexForToday ); assertTrue( @@ -74,13 +68,10 @@ void scheduledTripWithSkippedAndScheduled() { // New trip pattern { - final TripPattern newTripPattern = snapshot.getRealtimeAddedTripPattern( - env.trip2.getId(), - SERVICE_DATE - ); + var newTripPattern = snapshot.getRealtimeAddedTripPattern(env.trip2.getId(), SERVICE_DATE); - final Timetable newTimetableForToday = snapshot.resolve(newTripPattern, SERVICE_DATE); - final Timetable newTimetableScheduled = snapshot.resolve(newTripPattern, null); + var newTimetableForToday = snapshot.resolve(newTripPattern, SERVICE_DATE); + var newTimetableScheduled = snapshot.resolve(newTripPattern, null); assertNotSame(newTimetableForToday, newTimetableScheduled); @@ -88,7 +79,7 @@ void scheduledTripWithSkippedAndScheduled() { assertFalse(newTripPattern.canBoard(1)); assertTrue(newTripPattern.canBoard(2)); - final int newTimetableForTodayModifiedTripIndex = newTimetableForToday.getTripIndex( + int newTimetableForTodayModifiedTripIndex = newTimetableForToday.getTripIndex( scheduledTripId ); @@ -101,15 +92,10 @@ void scheduledTripWithSkippedAndScheduled() { "New trip should not be found in scheduled time table" ); - assertEquals(0, newTripTimes.getArrivalDelay(0)); - assertEquals(0, newTripTimes.getDepartureDelay(0)); - assertEquals(42, newTripTimes.getArrivalDelay(1)); - assertEquals(47, newTripTimes.getDepartureDelay(1)); - assertEquals(90, newTripTimes.getArrivalDelay(2)); - assertEquals(90, newTripTimes.getDepartureDelay(2)); - assertFalse(newTripTimes.isCancelledStop(0)); - assertTrue(newTripTimes.isCancelledStop(1)); - assertFalse(newTripTimes.isNoDataStop(2)); + assertEquals( + "UPDATED | A1 0:01 0:01:01 | B1 [C] 0:01:52 0:01:58 | C1 0:02:50 0:02:51", + env.getRealtimeTimetable(scheduledTripId) + ); } } @@ -133,7 +119,7 @@ void scheduledTripWithPreviouslySkipped() { .addDelayedStopTime(2, 90) .build(); - var result = assertSuccess(env.applyTripUpdate(tripUpdate, DIFFERENTIAL)); + assertSuccess(env.applyTripUpdate(tripUpdate, DIFFERENTIAL)); // Create update to the same trip but now the skipped stop is no longer skipped var scheduledBuilder = new TripUpdateBuilder( @@ -149,38 +135,34 @@ void scheduledTripWithPreviouslySkipped() { tripUpdate = scheduledBuilder.build(); // apply the update with the previously skipped stop now scheduled - result = assertSuccess(env.applyTripUpdate(tripUpdate, DIFFERENTIAL)); + assertSuccess(env.applyTripUpdate(tripUpdate, DIFFERENTIAL)); - assertEquals(1, result.successful()); // Check that the there is no longer a realtime added trip pattern for the trip and that the // stoptime updates have gone through var snapshot = env.getTimetableSnapshot(); { - final TripPattern newTripPattern = snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE); + var newTripPattern = snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE); assertNull(newTripPattern); final Trip trip = env.transitModel.getTransitModelIndex().getTripForId().get(tripId); - final TripPattern originalTripPattern = env.transitModel + var originalTripPattern = env.transitModel .getTransitModelIndex() .getPatternForTrip() .get(trip); - final Timetable originalTimetableForToday = snapshot.resolve( - originalTripPattern, - SERVICE_DATE - ); + var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); - final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); + var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); assertNotSame(originalTimetableForToday, originalTimetableScheduled); - final int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); + int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); assertTrue( originalTripIndexScheduled > -1, "Original trip should be found in scheduled time table" ); - final TripTimes originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( + var originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( originalTripIndexScheduled ); assertFalse( @@ -188,7 +170,7 @@ void scheduledTripWithPreviouslySkipped() { "Original trip times should not be canceled in scheduled time table" ); assertEquals(RealTimeState.SCHEDULED, originalTripTimesScheduled.getRealTimeState()); - final int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); + int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); assertTrue( originalTripIndexForToday > -1, @@ -214,12 +196,12 @@ void skippedNoData() { var env = RealtimeTestEnvironment.gtfs(); final FeedScopedId tripId = env.trip2.getId(); - var builder = new TripUpdateBuilder(tripId.getId(), SERVICE_DATE, SCHEDULED, env.timeZone) + + var tripUpdate = new TripUpdateBuilder(tripId.getId(), SERVICE_DATE, SCHEDULED, env.timeZone) .addNoDataStop(0) .addSkippedStop(1) - .addNoDataStop(2); - - var tripUpdate = builder.build(); + .addNoDataStop(2) + .build(); assertSuccess(env.applyTripUpdate(tripUpdate)); @@ -228,39 +210,40 @@ void skippedNoData() { // Original trip pattern { - final TripPattern originalTripPattern = env.transitModel + var originalTripPattern = env.transitModel .getTransitModelIndex() .getPatternForTrip() .get(env.trip2); - final Timetable originalTimetableForToday = snapshot.resolve( - originalTripPattern, - SERVICE_DATE - ); - final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); + var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); + var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); assertNotSame(originalTimetableForToday, originalTimetableScheduled); - final int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); + int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); assertTrue( originalTripIndexScheduled > -1, "Original trip should be found in scheduled time table" ); - final TripTimes originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( + var originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( originalTripIndexScheduled ); assertFalse( originalTripTimesScheduled.isCanceledOrDeleted(), "Original trip times should not be canceled in scheduled time table" ); - assertEquals(RealTimeState.SCHEDULED, originalTripTimesScheduled.getRealTimeState()); - final int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); + assertEquals( + "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", + TripTimesStringBuilder.encodeTripTimes(originalTripTimesScheduled, originalTripPattern) + ); + + int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); assertTrue( originalTripIndexForToday > -1, "Original trip should be found in time table for service date" ); - final TripTimes originalTripTimesForToday = originalTimetableForToday.getTripTimes( + var originalTripTimesForToday = originalTimetableForToday.getTripTimes( originalTripIndexForToday ); assertTrue( @@ -273,11 +256,11 @@ void skippedNoData() { // New trip pattern { - final TripPattern newTripPattern = snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE); + var newTripPattern = snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE); assertNotNull(newTripPattern, "New trip pattern should be found"); - final Timetable newTimetableForToday = snapshot.resolve(newTripPattern, SERVICE_DATE); - final Timetable newTimetableScheduled = snapshot.resolve(newTripPattern, null); + var newTimetableForToday = snapshot.resolve(newTripPattern, SERVICE_DATE); + var newTimetableScheduled = snapshot.resolve(newTripPattern, null); assertNotSame(newTimetableForToday, newTimetableScheduled); @@ -294,14 +277,13 @@ void skippedNoData() { newTripPattern.getTripHeadsign() ); - final int newTimetableForTodayModifiedTripIndex = newTimetableForToday.getTripIndex(tripId); + int newTimetableForTodayModifiedTripIndex = newTimetableForToday.getTripIndex(tripId); assertTrue( newTimetableForTodayModifiedTripIndex > -1, "New trip should be found in time table for service date" ); var newTripTimes = newTimetableForToday.getTripTimes(tripId); - assertEquals(RealTimeState.UPDATED, newTripTimes.getRealTimeState()); assertEquals( -1, @@ -309,15 +291,10 @@ void skippedNoData() { "New trip should not be found in scheduled time table" ); - assertEquals(0, newTripTimes.getArrivalDelay(0)); - assertEquals(0, newTripTimes.getDepartureDelay(0)); - assertEquals(0, newTripTimes.getArrivalDelay(1)); - assertEquals(0, newTripTimes.getDepartureDelay(1)); - assertEquals(0, newTripTimes.getArrivalDelay(2)); - assertEquals(0, newTripTimes.getDepartureDelay(2)); - assertTrue(newTripTimes.isNoDataStop(0)); - assertTrue(newTripTimes.isCancelledStop(1)); - assertTrue(newTripTimes.isNoDataStop(2)); + assertEquals( + "UPDATED | A1 [ND] 0:01 0:01:01 | B1 [C] 0:01:10 0:01:11 | C1 [ND] 0:01:20 0:01:21", + TripTimesStringBuilder.encodeTripTimes(newTripTimes, newTripPattern) + ); } } } diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java index 4d28c60eeb5..4815d7180c4 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java @@ -29,9 +29,7 @@ void invalidTripDate(LocalDate date) { var env = RealtimeTestEnvironment.gtfs(); var update = new TripUpdateBuilder(env.trip1.getId().getId(), date, SCHEDULED, env.timeZone) - .addDelayedStopTime(1, 0) .addDelayedStopTime(2, 60, 80) - .addDelayedStopTime(3, 90, 90) .build(); var result = env.applyTripUpdate(update); From ee4887ecf0a77253c1b0d56941530c91845ea95f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 18 Jun 2024 15:45:33 +0200 Subject: [PATCH 044/192] Switch to endExclusive --- .../apis/gtfs/PatternByServiceDatesFilter.java | 16 ++++++++-------- .../opentripplanner/apis/gtfs/schema.graphqls | 11 +++++++---- .../gtfs/PatternByServiceDatesFilterTest.java | 4 +++- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index f43bdf7decc..9184bd9d728 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -22,18 +22,18 @@ public class PatternByServiceDatesFilter { private final LocalDate startInclusive; - private final LocalDate endInclusive; + private final LocalDate endExclusive; private final Function> getPatternsForRoute; private final Function> getServiceDatesForTrip; /** * This method is not private to enable unit testing. *

- * See the API documentation for a discussion of {@code startInclusive} and {@code endInclusive}. + * See the API documentation for a discussion of {@code startInclusive} and {@code endExclusive}. */ PatternByServiceDatesFilter( @Nullable LocalDate startInclusive, - @Nullable LocalDate endInclusive, + @Nullable LocalDate endExclusive, Function> getPatternsForRoute, Function> getServiceDatesForTrip ) { @@ -41,12 +41,12 @@ public class PatternByServiceDatesFilter { this.getServiceDatesForTrip = Objects.requireNonNull(getServiceDatesForTrip); // optional, but one must be defined this.startInclusive = startInclusive; - this.endInclusive = endInclusive; + this.endExclusive = endExclusive; - if (startInclusive == null && endInclusive == null) { - throw new IllegalArgumentException("startInclusive and endInclusive cannot be both null"); + if (startInclusive == null && endExclusive == null) { + throw new IllegalArgumentException("startInclusive and endExclusive cannot be both null"); } else if ( - startInclusive != null && endInclusive != null && startInclusive.isAfter(endInclusive) + startInclusive != null && endExclusive != null && startInclusive.isAfter(endExclusive) ) { throw new IllegalArgumentException("start must be before end"); } @@ -96,7 +96,7 @@ private boolean hasServicesOnDate(TripPattern pattern) { ( startInclusive == null || date.isEqual(startInclusive) || date.isAfter(startInclusive) ) && - (endInclusive == null || date.isEqual(endInclusive) || date.isBefore(endInclusive)) + (endExclusive == null || date.isBefore(endExclusive)) ); }); } diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 8ca0ac189cb..cbedf2a369f 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3447,13 +3447,16 @@ on Sunday morning at 1am to 3am, might have the previous Saturday's service date """ input ServiceDateFilterInput { """ - Inclusive start date of the filter. If `null` this means that no `start` filter is applied and all - dates that are before or on `end` are selected. + **Inclusive** start date of the filter. If `null` this means that no `start` filter is applied and all + dates that are before `end` are selected. """ start: LocalDate """ - Inclusive end date of the filter. If `null` this means that no end filter is applied and all - entities that are after or on `start` are selected. + **Exclusive** end date of the filter. This means that if you want a time window from Sunday to + Sunday, `end` must be on Monday. + + If `null` this means that no end filter is applied and all entities that are after or on `start` + are selected. """ end: LocalDate } diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index 1893516ab58..01743dec89d 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -95,11 +95,13 @@ void validRange(LocalDate start, LocalDate end) { static List ranges() { return List.of( Arguments.of(null, parse("2024-05-03"), NOT_REMOVED), - Arguments.of(null, parse("2024-05-01"), NOT_REMOVED), Arguments.of(parse("2024-05-03"), null, NOT_REMOVED), Arguments.of(parse("2024-05-01"), null, NOT_REMOVED), + Arguments.of(null, parse("2024-04-30"), REMOVED), + Arguments.of(null, parse("2024-05-01"), REMOVED), Arguments.of(parse("2024-05-02"), parse("2024-05-02"), REMOVED), Arguments.of(parse("2024-05-02"), parse("2024-05-03"), REMOVED), + Arguments.of(parse("2024-05-02"), parse("2024-06-01"), REMOVED), Arguments.of(parse("2025-01-01"), null, REMOVED), Arguments.of(parse("2025-01-01"), parse("2025-01-02"), REMOVED), Arguments.of(null, parse("2023-12-31"), REMOVED), From e9fc8d65aa5e73e88c7abd3bd35c24081949ce17 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Wed, 19 Jun 2024 00:45:40 +0200 Subject: [PATCH 045/192] Add McMinimumNumberItineraryFilter - a multi-criteria version of the MinNumIttnFilter --- .../system/SingeCriteriaComparator.java | 25 +-- .../filters/system/mcmin/Group.java | 56 +++++ .../filters/system/mcmin/Item.java | 47 +++++ .../mcmin/McMinimumNumberItineraryFilter.java | 102 ++++++++++ .../filters/system/mcmin/State.java | 190 +++++++++++++++++ .../model/plan/TestItineraryBuilder.java | 14 +- .../system/SingeCriteriaComparatorTest.java | 52 +---- .../filters/system/mcmin/ItemTest.java | 56 +++++ .../McMinimumNumberItineraryFilterTest.java | 192 ++++++++++++++++++ 9 files changed, 662 insertions(+), 72 deletions(-) create mode 100644 src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Group.java create mode 100644 src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Item.java create mode 100644 src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilter.java create mode 100644 src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/State.java create mode 100644 src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/ItemTest.java create mode 100644 src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilterTest.java diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java index 8f757cee5a1..aa876d5bcdc 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java @@ -20,7 +20,7 @@ * can be sorted, if so the {@link #strictOrder()} should return false (this is the default). */ @FunctionalInterface -public interface SingeCriteriaComparator extends Comparator { +public interface SingeCriteriaComparator { /** * The left criteria dominates the right criteria. Note! The right criteria my dominate * the left criteria if there is no {@link #strictOrder()}. If left and right are equals, then @@ -28,17 +28,6 @@ public interface SingeCriteriaComparator extends Comparator { */ boolean leftDominanceExist(Itinerary left, Itinerary right); - /** - * The compare function can be used to order elements based on the criteria for this instance. - * Note! This method should not be used if there is no {@link #strictOrder()}. - */ - @Override - default int compare(Itinerary left, Itinerary right) { - throw new IllegalStateException( - "This criteria can not be used to sort elements, there is no deterministic defined order." - ); - } - /** * Return true if the criteria can be deterministically sorted. */ @@ -56,10 +45,13 @@ static SingeCriteriaComparator compareGeneralizedCost() { @SuppressWarnings("OptionalGetWithoutIsPresent") static SingeCriteriaComparator compareTransitPriorityGroups() { - return (left, right) -> TransitGroupPriority32n.dominate(left.getGeneralizedCost2().get(), right.getGeneralizedCost2().get()); + return (left, right) -> + TransitGroupPriority32n.dominate( + left.getGeneralizedCost2().get(), + right.getGeneralizedCost2().get() + ); } - static SingeCriteriaComparator compareLessThan(final ToIntFunction op) { return new SingeCriteriaComparator() { @Override @@ -67,11 +59,6 @@ public boolean leftDominanceExist(Itinerary left, Itinerary right) { return op.applyAsInt(left) < op.applyAsInt(right); } - @Override - public int compare(Itinerary left, Itinerary right) { - return op.applyAsInt(left) - op.applyAsInt(right); - } - @Override public boolean strictOrder() { return true; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Group.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Group.java new file mode 100644 index 00000000000..0f231517ae3 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Group.java @@ -0,0 +1,56 @@ +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * The purpose of a group is to maintain a list of items, all optimal for a single + * criteria/comparator. After the group is created, then the criteria is no longer needed, so we do + * not keep a reference to the original criteria. + */ +class Group implements Iterable { + + private final List items = new ArrayList<>(); + + public Group(Item firstItem) { + add(firstItem); + } + + Item first() { + return items.getFirst(); + } + + boolean isEmpty() { + return items.isEmpty(); + } + + + boolean isSingleItemGroup() { + return items.size() == 1; + } + + void add(Item item) { + item.incGroupCount(); + items.add(item); + } + + void removeAllItems() { + items.forEach(Item::decGroupCount); + items.clear(); + } + + void addNewDominantItem(Item item) { + removeAllItems(); + add(item); + } + + boolean contains(Item item) { + return this.items.contains(item); + } + + @Override + public Iterator iterator() { + return items.iterator(); + } +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Item.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Item.java new file mode 100644 index 00000000000..58898f2ee63 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Item.java @@ -0,0 +1,47 @@ +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; + +import org.opentripplanner.model.plan.Itinerary; + +/** + * An item is a decorated itinerary. The extra information added is the index in the input list + * (sort order) and a groupCount. The sort order is used to break ties, while the group-count is + * used to select the itinerary witch exist in the highest number of groups. The group dynamically + * updates the group-count; The count is incremented when an item is added to a group, and + * decremented when the group is removed from the State. + */ +class Item { + + private final Itinerary item; + private final int index; + private int groupCount = 0; + + Item(Itinerary item, int index) { + this.item = item; + this.index = index; + } + + /** + * An item is better than another if the groupCount is higher, and in case of a tie, if the sort + * index is lower. + */ + public boolean betterThan(Item o) { + return groupCount != o.groupCount ? groupCount > o.groupCount : index < o.index; + } + + Itinerary item() { + return item; + } + + void incGroupCount() { + ++this.groupCount; + } + + void decGroupCount() { + --this.groupCount; + } + + @Override + public String toString() { + return "Item #%d {count:%d, %s}".formatted(index, groupCount, item.toStr()); + } +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilter.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilter.java new file mode 100644 index 00000000000..b1e7b857c3b --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilter.java @@ -0,0 +1,102 @@ +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; + +import java.util.List; +import java.util.function.Predicate; +import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingeCriteriaComparator; +import org.opentripplanner.routing.algorithm.filterchain.framework.spi.RemoveItineraryFlagger; + +/** + * This filter is used to reduce a set of itineraries down to the specified limit, if possible. + * The filter is guaranteed to keep at least the given {@code minNumItineraries} and also + * the best itinerary for each criterion. The criterion is defined using the list of + * {@code comparators}. + *

+ * The main usage of this filter is to combine it with a grouping filter and for each group + * make sure there is at least {@code minNumItineraries} and that the best itinerary with respect + * to each criterion is kept. So, if the grouping is based on time and riding common trips, then + * this filter will use the reminding criterion (transfers, generalized-cost, + * [transit-group-priority]) to filter the grouped set of itineraries. + *

+ * IMPLEMENTATION DETAILS + *

+ * This is not a trivial problem. In most cases, the best itinerary for a given criteria is unique, + * but there might be ties - same number of transfers, same cost, and/or different priority groups. + * In case of a tie, we will look if an itinerary is "best-in-group" for more than one criterion, + * if so we pick the one witch is best in the highest number of groups. Again, if there is a tie + * (best in the same number of groups), then we fall back to the given itinerary sorting order. + *

+ * This filter will use the order of the input itineraries to break ties. So, make sure to call the + * appropriate sort function before this filter is invoked. + *

+ * Note! For a criteria like num-of-transfers or generalized-cost, there is only one set of "best" + * itineraries, and usually there are only one or a few itineraries. In case there is more than one, + * picking just one is fine. But, for transit-group-priority there might be more than on set of + * itineraries. For each set, we need to pick one itinerary for the final result. Each of these + * sets may or may not have more than one itinerary. + *

+ * Let's discuss an example: + *

+ *   minNumItineraries = 4
+ *   comparators = [ generalized-cost, min-num-transfers, transit-group-priority ]
+ *   itineraries: [
+ *    #0 : [ 1000, 2, (a) ]
+ *    #1 : [ 1000, 3, (a,b) ]
+ *    #2 : [ 1000, 3, (b) ]
+ *    #3 : [ 1200, 1, (a,b) ]
+ *    #4 : [ 1200, 1, (a) ]
+ *    #5 : [ 1300, 2, (c) ]
+ *    #6 : [ 1300, 3, (c) ]
+ *   ]
+ * 
+ * The best itineraries by generalized-cost are (#0, #1, #2). The best itineraries by + * min-num-transfers are (#3, #4). The best itineraries by transit-group-priority are + * (a:(#0, #4), b:(#2), c:(#5, #6)). + *

+ * So we need to pick one from each group (#0, #1, #2), (#3, #4), (#0, #4), (#2), and (#5, #6). + * Since #2 is a single, we pick it first. Itinerary #2 is also one of the best + * generalized-cost itineraries - so we are done with generalized-cost itineraries as well. The two + * groups left are (#3, #4), (#0, #4), and (#5, #6). #4 exists in 2 groups, so we pick it next. Now + * we are left with (#5, #6). To break the tie, we look at the sort-order. We pick + * itinerary #5. Result: #2, #4, and #5. + *

+ * The `minNumItineraries` limit is not met, so we need to pick another itinerary, we use the + * sort-order again and add itinerary #0. The result returned is: [#0, #2, #4, #5] + */ +public class McMinimumNumberItineraryFilter implements RemoveItineraryFlagger { + + private final String name; + private final int minNumItineraries; + private final List comparators; + + public McMinimumNumberItineraryFilter( + String name, + int minNumItineraries, + List comparators + ) { + this.name = name; + this.minNumItineraries = minNumItineraries; + this.comparators = comparators; + } + + @Override + public String name() { + return name; + } + + @Override + public List flagForRemoval(List itineraries) { + if (itineraries.size() <= minNumItineraries) { + return List.of(); + } + var state = new State(itineraries, comparators); + state.findAllSingleItemGroupsAndAddTheItemToTheResult(); + state.findTheBestItemsUntilAllGroupsAreRepresentedInTheResult(); + state.fillUpTheResultWithMinimumNumberOfItineraries(minNumItineraries); + + // We now have the itineraries we want, but we must invert this and return the + // list of itineraries to drop - keeping the original order + var ok = state.getResult(); + return itineraries.stream().filter(Predicate.not(ok::contains)).toList(); + } +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/State.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/State.java new file mode 100644 index 00000000000..acce6a963f4 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/State.java @@ -0,0 +1,190 @@ +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.annotation.Nullable; +import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingeCriteriaComparator; + +/** + * Keep a list of items, groups and the result in progress. This is just a class for + * simple bookkeeping for the state of the filter. + */ +class State { + private final List items; + private final List groups; + private final List result = new ArrayList<>(); + + /** + * Initialize the state by wrapping each itinerary in an item (with index) and create groups for + * each criterion with the best itineraries (can be more than one with, for example, the same + * cost). There should be at least one itinerary from each group surviving the filtering process. + * The same itinerary can exist in multiple groups. + */ + State(List itineraries, List comparators) { + this.items = createListOfItems(itineraries); + this.groups = createGroups(items, comparators); + } + + List getResult() { + return result.stream().map(Item::item).toList(); + } + + /** + * Find and add all groups with a single item in them and add them to the result + */ + void findAllSingleItemGroupsAndAddTheItemToTheResult() { + var item = findItemInFirstSingleItemGroup(groups); + while (item != null) { + addToResult(item); + item = findItemInFirstSingleItemGroup(groups); + } + } + + /** + * Find the items with the highest group count and the lowest index. Theoretically, there might be + * a smaller set of itineraries that TOGETHER represent all groups than what we achieve here, but + * it is fare more complicated to compute - so this is probably good enough. + */ + void findTheBestItemsUntilAllGroupsAreRepresentedInTheResult() { + while (!groups.isEmpty()) { + addToResult(findBestItem(groups)); + } + } + + /** + * Fill up with itineraries until the minimum number of itineraries is reached + */ + void fillUpTheResultWithMinimumNumberOfItineraries(int minNumItineraries) { + int end = Math.min(items.size(), minNumItineraries); + for (int i = 0; result.size() < end; ++i) { + var it = items.get(i); + if (!result.contains(it)) { + result.add(it); + } + } + } + + private void addToResult(Item item) { + result.add(item); + removeGroupsWitchContainsItem(item); + } + + /** + * If an itinerary is accepted into the final result, then all groups that contain that itinerary + * can be removed. In addition, the item groupCount should be decremented if a group is dropped. + * This makes sure that the groups represented in the final result do not count when selecting the + * next item. + */ + private void removeGroupsWitchContainsItem(Item item) { + for (Group group : groups) { + if (group.contains(item)) { + group.removeAllItems(); + } + } + groups.removeIf(Group::isEmpty); + } + + + /** + * The best item is the one witch exists in most groups, and in case of a tie, the sort order/ + * itinerary index is used. + */ + private static Item findBestItem(List groups) { + var candidate = groups.getFirst().first(); + for (Group group : groups) { + for (Item item : group) { + if (item.betterThan(candidate)) { + candidate = item; + } + } + } + return candidate; + } + + /** + * Search through all groups and return all items witch comes from groups with only one item. + */ + @Nullable + private static Item findItemInFirstSingleItemGroup(List groups) { + return groups.stream().filter(Group::isSingleItemGroup).findFirst().map(Group::first).orElse(null); + } + + private static ArrayList createListOfItems(List itineraries) { + var items = new ArrayList(); + for (int i = 0; i < itineraries.size(); i++) { + items.add(new Item(itineraries.get(i), i)); + } + return items; + } + + private static List createGroups(Collection items, List comparators) { + List groups = new ArrayList<>(); + for (SingeCriteriaComparator comparator : comparators) { + if (comparator.strictOrder()) { + groups.add(createOrderedGroup(items, comparator)); + } else { + groups.addAll(createUnorderedGroups(items, comparator)); + } + } + return groups; + } + + /** + * In a strict ordered group only one optimal value exist for the criteria defined by the given + * {@code comparator}. All items that have this value should be included in the group created. + */ + private static Group createOrderedGroup(Collection items, SingeCriteriaComparator comparator) { + Group group = null; + for (Item item : items) { + if (group == null) { + group = new Group(item); + continue; + } + var current = group.first(); + if (comparator.leftDominanceExist(item.item(), current.item())) { + group.addNewDominantItem(item); + } else if (!comparator.leftDominanceExist(current.item(), item.item())) { + group.add(item); + } + } + return group; + } + + /** + * For a none strict ordered criteria, multiple optimal values exist. The criterion is defined by + * the given {@code comparator}. This method will create a group for each optimal value found in + * the given set of items. + * + * @see #createOrderedGroup(Collection, SingeCriteriaComparator) + */ + private static Collection createUnorderedGroups( + Collection items, + SingeCriteriaComparator comparator + ) { + List result = new ArrayList<>(); + + for (Item item : items) { + int groupCount = result.size(); + for (Group group : result) { + var groupItem = group.first().item(); + if (comparator.leftDominanceExist(groupItem, item.item())) { + if (comparator.leftDominanceExist(item.item(), groupItem)) { + // Mutual dominance => the item belong in another group + --groupCount; + } + } else { + if (comparator.leftDominanceExist(item.item(), groupItem)) { + group.removeAllItems(); + } + group.add(item); + } + } + if (groupCount == 0) { + result.add(new Group(item)); + } + } + return result; + } +} diff --git a/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java b/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java index ddff2e76fca..edaafabd753 100644 --- a/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java +++ b/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java @@ -394,7 +394,7 @@ public TestItineraryBuilder carHail(int duration, Place to) { } public TestItineraryBuilder withGeneralizedCost2(int c2) { - this.c2 = c2; + this.c2 = c2; return this; } @@ -403,16 +403,24 @@ public Itinerary egress(int walkDuration) { return build(); } + /** + * Override any value set for c1. The given value will be assigned to the itinerary + * independent of any values set on the legs. + */ + public Itinerary build(int c1) { + this.c1 = c1; + return build(); + } + public Itinerary build() { Itinerary itinerary = new Itinerary(legs); itinerary.setGeneralizedCost(c1); - if(c2 != NOT_SET) { + if (c2 != NOT_SET) { itinerary.setGeneralizedCost2(c2); } return itinerary; } - /* private methods */ /** Create a dummy trip */ diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java index d94d21673c4..5435a8864c7 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java @@ -2,16 +2,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; -import java.util.ArrayList; -import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.opentripplanner.framework.collection.CompositeComparator; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Place; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; @@ -57,34 +53,6 @@ static void setUp() { assertEquals(expectedCost, oneTransferLowCost.getGeneralizedCost()); } - @Test - void compare() { - var l = new ArrayList(); - l.add(zeroTransferHighCost); - l.add(zeroTransferLowCost); - l.add(oneTransferLowCost); - - l.sort( - new CompositeComparator<>( - SingeCriteriaComparator.compareGeneralizedCost(), - SingeCriteriaComparator.compareNumTransfers() - ) - ); - - assertEquals(List.of(zeroTransferLowCost, oneTransferLowCost, zeroTransferHighCost), l); - } - - @Test - void compareThrowsExceptionIfNotStrictOrder() { - assertThrows( - IllegalStateException.class, - () -> - SingeCriteriaComparator - .compareTransitPriorityGroups() - .compare(zeroTransferLowCost, zeroTransferHighCost) - ); - } - @Test void strictOrder() { assertTrue(SingeCriteriaComparator.compareNumTransfers().strictOrder()); @@ -103,11 +71,6 @@ void compareNumTransfers() { // strict order expected assertTrue(subject.strictOrder()); - - // Compare - assertEquals(0, subject.compare(zeroTransferHighCost, zeroTransferLowCost)); - assertEquals(-1, subject.compare(zeroTransferLowCost, oneTransferLowCost)); - assertEquals(1, subject.compare(oneTransferLowCost, zeroTransferLowCost)); } @Test @@ -125,23 +88,12 @@ void compareGeneralizedCost() { // strict order expected assertTrue(subject.strictOrder()); - - // Compare - assertTrue(0 < subject.compare(zeroTransferHighCost, zeroTransferLowCost)); - assertTrue(0 > subject.compare(zeroTransferLowCost, zeroTransferHighCost)); - assertEquals(0, subject.compare(zeroTransferLowCost, oneTransferLowCost)); } @Test void compareTransitPriorityGroups() { - var group1 = newItinerary(A) - .bus(1, START, END_LOW, C) - .withGeneralizedCost2(1) - .build(); - var group2 = newItinerary(A) - .bus(1, START, END_LOW, C) - .withGeneralizedCost2(2) - .build(); + var group1 = newItinerary(A).bus(1, START, END_LOW, C).withGeneralizedCost2(1).build(); + var group2 = newItinerary(A).bus(1, START, END_LOW, C).withGeneralizedCost2(2).build(); var group1And2 = newItinerary(A) .bus(1, START, END_LOW, C) .withGeneralizedCost2(TransitGroupPriority32n.mergeInGroupId(1, 2)) diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/ItemTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/ItemTest.java new file mode 100644 index 00000000000..fbb2f5dea57 --- /dev/null +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/ItemTest.java @@ -0,0 +1,56 @@ +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.model.plan.Place; +import org.opentripplanner.transit.model._data.TransitModelForTest; + +class ItemTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final Place A = TEST_MODEL.place("A", 10, 11); + private static final Place B = TEST_MODEL.place("B", 10, 11); + private static final Itinerary ITINERARY = newItinerary(A).bus(1, 1, 2, B).build(); + + @Test + void betterThan() { + var i1 = new Item(ITINERARY, 3); + var i2 = new Item(ITINERARY, 7); + + // i1 is better than i2 because the index is lower + assertTrue(i1.betterThan(i2)); + assertFalse(i2.betterThan(i1)); + + // Incrementing both does not change anything + i1.incGroupCount(); + i2.incGroupCount(); + assertTrue(i1.betterThan(i2)); + assertFalse(i2.betterThan(i1)); + + // Incrementing i2 make it better + i2.incGroupCount(); + assertFalse(i1.betterThan(i2)); + assertTrue(i2.betterThan(i1)); + } + + @Test + void item() { + assertSame(ITINERARY, new Item(ITINERARY, 7).item()); + } + + @Test + void testToString() { + Item item = new Item(ITINERARY, 7); + assertEquals("Item #7 {count:0, A ~ BUS 1 0:00:01 0:00:02 ~ B [C₁121]}", item.toString()); + item.incGroupCount(); + assertEquals("Item #7 {count:1, A ~ BUS 1 0:00:01 0:00:02 ~ B [C₁121]}", item.toString()); + item.decGroupCount(); + assertEquals("Item #7 {count:0, A ~ BUS 1 0:00:01 0:00:02 ~ B [C₁121]}", item.toString()); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilterTest.java new file mode 100644 index 00000000000..8d3d03cb857 --- /dev/null +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilterTest.java @@ -0,0 +1,192 @@ +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; + +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.model.plan.Place; +import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingeCriteriaComparator; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; +import org.opentripplanner.transit.model._data.TransitModelForTest; + +class McMinimumNumberItineraryFilterTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + + private static final Place A = TEST_MODEL.place("A", 10, 11); + private static final Place B = TEST_MODEL.place("B", 10, 13); + private static final Place C = TEST_MODEL.place("C", 10, 14); + private static final Place D = TEST_MODEL.place("D", 10, 15); + private static final Place E = TEST_MODEL.place("E", 10, 15); + private static final Place[] PLACES = { A, B, C, D, E }; + + private static final int START = 3600 * 10; + private static final int GROUP_A = TransitGroupPriority32n.groupId(1); + private static final int GROUP_B = TransitGroupPriority32n.groupId(2); + private static final int GROUP_C = TransitGroupPriority32n.groupId(3); + private static final int GROUP_AB = TransitGroupPriority32n.mergeInGroupId(GROUP_A, GROUP_B); + private static final int GROUP_BC = TransitGroupPriority32n.mergeInGroupId(GROUP_B, GROUP_C); + private static final int GROUP_ABC = TransitGroupPriority32n.mergeInGroupId(GROUP_AB, GROUP_C); + + private static final boolean EXP_KEEP = true; + private static final boolean EXP_DROP = false; + + private static final int COST_LOW = 1000; + private static final int COST_MED = 1200; + private static final int COST_HIGH = 1500; + + private static final int TX_0 = 0; + private static final int TX_1 = 1; + private static final int TX_2 = 2; + + private final McMinimumNumberItineraryFilter subject = new McMinimumNumberItineraryFilter( + "test", + 2, + List.of( + SingeCriteriaComparator.compareGeneralizedCost(), + SingeCriteriaComparator.compareNumTransfers(), + SingeCriteriaComparator.compareTransitPriorityGroups() + ) + ); + + static TestRow row( + boolean expected, + int c1, + int nTransfers, + int transitGroups, + String description + ) { + return new TestRow(expected, c1, nTransfers, transitGroups); + } + + static List> filterTestCases() { + return List.of( + List.of(/* Should not fail for an empty list of itineraries*/), + List.of( + // Test minNumItinerariesLimit = 2 + row(EXP_KEEP, COST_LOW, TX_1, GROUP_A, "Best in everything"), + row(EXP_KEEP, COST_HIGH, TX_2, GROUP_AB, "Worse, kept because minNumItinerariesLimit is 2") + ), + List.of( + // Test minNumItinerariesLimit, first is added + row(EXP_KEEP, COST_HIGH, TX_2, GROUP_ABC, "Worst, kept because of minNumItinerariesLimit"), + row(EXP_KEEP, COST_LOW, TX_0, GROUP_A, "Best in everything"), + row(EXP_DROP, COST_HIGH, TX_1, GROUP_AB, "Dropped because not better than #2.") + ), + List.of( + // The minNumItinerariesLimit is met, so no extra itinerary(#0) is added + row(EXP_DROP, COST_HIGH, TX_2, GROUP_AB, "First element is dropped"), + row(EXP_KEEP, COST_LOW, TX_1, GROUP_B, "Best cost and group B"), + row(EXP_KEEP, COST_MED, TX_0, GROUP_A, "Best nTransfers and group A") + ), + List.of( + row(EXP_KEEP, COST_LOW, TX_2, GROUP_A, "Best: c1 and group A"), + row(EXP_DROP, COST_LOW, TX_1, GROUP_AB, "Best compromise: c1, Tx, and group AB"), + row(EXP_KEEP, COST_LOW, TX_2, GROUP_C, "Best: c1 and group C"), + row(EXP_KEEP, COST_MED, TX_0, GROUP_BC, "Best: num-of-transfers") + ), + /** + * This is the example explained in JavaDoc {@link McMinimumNumberItineraryFilter} + */ + List.of( + row(EXP_DROP, COST_LOW, TX_1, GROUP_A, ""), + row(EXP_DROP, COST_LOW, TX_2, GROUP_AB, ""), + row(EXP_KEEP, COST_LOW, TX_2, GROUP_B, "Kept -> Only one in group B"), + row(EXP_DROP, COST_MED, TX_0, GROUP_AB, ""), + row(EXP_KEEP, COST_MED, TX_0, GROUP_A, "Kept -> Best transfer and group A"), + row(EXP_KEEP, COST_HIGH, TX_1, GROUP_C, "Kept -> Best group C, tie with #6"), + row(EXP_DROP, COST_HIGH, TX_2, GROUP_C, "") + ) + ); + } + + @ParameterizedTest + @MethodSource("filterTestCases") + void filterTest(List rows) { + var input = rows.stream().map(TestRow::create).toList(); + var expected = rows.stream().filter(TestRow::expected).map(TestRow::create).toList(); + + var result = subject.removeMatchesForTest(input); + + assertEquals(toStr(expected), toStr(result)); + } + + @Test + void testName() { + assertEquals("test", subject.name()); + } + + /** + * Make sure the test setup is correct - this does not test anything in src/main + */ + @Test + void testGroupsToString() { + assertEquals("A", groupsToString(GROUP_A)); + assertEquals("B", groupsToString(GROUP_B)); + assertEquals("C", groupsToString(GROUP_C)); + assertEquals("AB", groupsToString(GROUP_AB)); + assertEquals("BC", groupsToString(GROUP_BC)); + assertEquals("ABC", groupsToString(GROUP_ABC)); + } + + private static String groupsToString(int groups) { + var buf = new StringBuilder(); + char ch = 'A'; + // Check for 5 groups - the test does not use so many, but it does not matter + for (int i = 0; i < 5; ++i) { + int mask = 1 << i; + if ((groups & mask) != 0) { + buf.append(ch); + } + ch = (char) (ch + 1); + } + return buf.toString(); + } + + private static String toStr(List list) { + return list + .stream() + .map(i -> + "[ %d %d %s ]".formatted( + i.getGeneralizedCost(), + i.getNumberOfTransfers(), + groupsToString(i.getGeneralizedCost2().orElse(-1)) + ) + ) + .collect(Collectors.joining(", ")); + } + + record TestRow(boolean expected, int c1, int nTransfers, int transitGroupIds) { + Itinerary create() { + int start = START; + var builder = newItinerary(A); + + if (nTransfers < 0) { + builder.drive(start, ++start, E); + } else { + builder.bus(1, ++start, ++start, PLACES[1]); + for (int i = 0; i < nTransfers; i++) { + builder.bus(1, ++start, ++start, PLACES[i + 2]); + } + builder.withGeneralizedCost2(transitGroupIds); + } + return builder.build(c1); + } + + @Override + public String toString() { + // The red-x is a unicode character(U+274C) and should be visible in most IDEs. + return "%s %d %d %s".formatted( + expected ? "" : "❌", + c1, + nTransfers, + groupsToString(transitGroupIds) + ); + } + } +} From 02cb8f752a32a580daad6e7409b361761528c53f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 18 Jun 2024 14:02:32 +0200 Subject: [PATCH 046/192] Increase code reuse in SkippedTest --- .../trip/moduletests/addition/AddedTest.java | 5 +- .../CancellationDeletionTest.java | 12 +- .../trip/moduletests/delay/DelayedTest.java | 26 +- .../trip/moduletests/delay/SkippedTest.java | 303 +++++++----------- .../rejection/InvalidTripIdTest.java | 9 +- 5 files changed, 128 insertions(+), 227 deletions(-) diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 6a7d076923f..8a2d6e96407 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -17,7 +17,6 @@ import java.util.List; import org.junit.jupiter.api.Test; import org.opentripplanner.model.PickDrop; -import org.opentripplanner.model.Timetable; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.RealTimeState; @@ -148,8 +147,8 @@ private TripPattern assertAddedTrip(String tripId, RealtimeTestEnvironment env) assertEquals(1, patternsAtA.size()); var tripPattern = patternsAtA.stream().findFirst().get(); - final Timetable forToday = snapshot.resolve(tripPattern, SERVICE_DATE); - final Timetable schedule = snapshot.resolve(tripPattern, null); + var forToday = snapshot.resolve(tripPattern, SERVICE_DATE); + var schedule = snapshot.resolve(tripPattern, null); assertNotSame(forToday, schedule); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java index 41e42bf00db..3b527b64e5d 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java @@ -12,8 +12,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.model.Timetable; -import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; @@ -49,8 +47,8 @@ void cancelledTrip(ScheduleRelationship relationship, RealTimeState state) { assertSuccess(env.applyTripUpdate(update)); var snapshot = env.getTimetableSnapshot(); - final Timetable forToday = snapshot.resolve(pattern1, RealtimeTestEnvironment.SERVICE_DATE); - final Timetable schedule = snapshot.resolve(pattern1, null); + var forToday = snapshot.resolve(pattern1, RealtimeTestEnvironment.SERVICE_DATE); + var schedule = snapshot.resolve(pattern1, null); assertNotSame(forToday, schedule); assertNotSame(forToday.getTripTimes(tripIndex1), schedule.getTripTimes(tripIndex1)); @@ -100,15 +98,15 @@ void cancelingAddedTrip(ScheduleRelationship relationship, RealTimeState state) .build(); assertSuccess(env.applyTripUpdate(update, DIFFERENTIAL)); - final TimetableSnapshot snapshot = env.getTimetableSnapshot(); + var snapshot = env.getTimetableSnapshot(); // Get the trip pattern of the added trip which goes through stopA var patternsAtA = snapshot.getPatternsForStop(env.stopA1); assertNotNull(patternsAtA, "Added trip pattern should be found"); var tripPattern = patternsAtA.stream().findFirst().get(); - final Timetable forToday = snapshot.resolve(tripPattern, RealtimeTestEnvironment.SERVICE_DATE); - final Timetable schedule = snapshot.resolve(tripPattern, null); + var forToday = snapshot.resolve(tripPattern, RealtimeTestEnvironment.SERVICE_DATE); + var schedule = snapshot.resolve(tripPattern, null); assertNotSame(forToday, schedule); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java index 7440a189846..d9282e1b155 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java @@ -9,8 +9,6 @@ import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import org.junit.jupiter.api.Test; -import org.opentripplanner.model.Timetable; -import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.TripTimes; @@ -45,12 +43,9 @@ void singleStopDelay() { var pattern1 = env.getPatternForTrip(env.trip1); int trip1Index = pattern1.getScheduledTimetable().getTripIndex(env.trip1.getId()); - final TimetableSnapshot snapshot = env.getTimetableSnapshot(); - final Timetable trip1Realtime = snapshot.resolve( - pattern1, - RealtimeTestEnvironment.SERVICE_DATE - ); - final Timetable trip1Scheduled = snapshot.resolve(pattern1, null); + var snapshot = env.getTimetableSnapshot(); + var trip1Realtime = snapshot.resolve(pattern1, RealtimeTestEnvironment.SERVICE_DATE); + var trip1Scheduled = snapshot.resolve(pattern1, null); assertNotSame(trip1Realtime, trip1Scheduled); assertNotSame(trip1Realtime.getTripTimes(trip1Index), trip1Scheduled.getTripTimes(trip1Index)); @@ -79,26 +74,25 @@ void singleStopDelay() { void complexDelay() { var env = RealtimeTestEnvironment.gtfs(); - String tripId = env.trip2.getId().getId(); + var tripId = env.trip2.getId().getId(); - var builder = new TripUpdateBuilder(tripId, SERVICE_DATE, SCHEDULED, env.timeZone) + var tripUpdate = new TripUpdateBuilder(tripId, SERVICE_DATE, SCHEDULED, env.timeZone) .addDelayedStopTime(0, 0) .addDelayedStopTime(1, 60, 80) - .addDelayedStopTime(2, 90, 90); - - var tripUpdate = builder.build(); + .addDelayedStopTime(2, 90, 90) + .build(); assertSuccess(env.applyTripUpdate(tripUpdate)); - final TimetableSnapshot snapshot = env.getTimetableSnapshot(); + var snapshot = env.getTimetableSnapshot(); final TripPattern originalTripPattern = env.transitModel .getTransitModelIndex() .getPatternForTrip() .get(env.trip2); - final Timetable originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); - final Timetable originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); + var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); + var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); assertNotSame(originalTimetableForToday, originalTimetableScheduled); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index c70dd380602..349e472036d 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -3,7 +3,6 @@ import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; 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.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -12,10 +11,8 @@ import static org.opentripplanner.updater.trip.UpdateIncrementality.DIFFERENTIAL; import org.junit.jupiter.api.Test; -import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.timetable.RealTimeState; -import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripTimesStringBuilder; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; @@ -38,65 +35,14 @@ void scheduledTripWithSkippedAndScheduled() { assertSuccess(env.applyTripUpdate(tripUpdate)); - var snapshot = env.getTimetableSnapshot(); + assertOriginalTripPatternIsDeleted(env, env.trip2.getId()); + + assertNewTripTimesIsUpdated(env, env.trip2.getId()); - // Original trip pattern - { - var tripId = env.trip2.getId(); - var trip = env.transitModel.getTransitModelIndex().getTripForId().get(tripId); - var originalTripPattern = env.transitModel - .getTransitModelIndex() - .getPatternForTrip() - .get(trip); - - var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); - var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); - - assertNotSame(originalTimetableForToday, originalTimetableScheduled); - - int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); - var originalTripTimesForToday = originalTimetableForToday.getTripTimes( - originalTripIndexForToday - ); - assertTrue( - originalTripTimesForToday.isDeleted(), - "Original trip times should be deleted in time table for service date" - ); - // original trip should be canceled - assertEquals(RealTimeState.DELETED, originalTripTimesForToday.getRealTimeState()); - } - - // New trip pattern - { - var newTripPattern = snapshot.getRealtimeAddedTripPattern(env.trip2.getId(), SERVICE_DATE); - - var newTimetableForToday = snapshot.resolve(newTripPattern, SERVICE_DATE); - var newTimetableScheduled = snapshot.resolve(newTripPattern, null); - - assertNotSame(newTimetableForToday, newTimetableScheduled); - - assertTrue(newTripPattern.canBoard(0)); - assertFalse(newTripPattern.canBoard(1)); - assertTrue(newTripPattern.canBoard(2)); - - int newTimetableForTodayModifiedTripIndex = newTimetableForToday.getTripIndex( - scheduledTripId - ); - - var newTripTimes = newTimetableForToday.getTripTimes(newTimetableForTodayModifiedTripIndex); - assertEquals(RealTimeState.UPDATED, newTripTimes.getRealTimeState()); - - assertEquals( - -1, - newTimetableScheduled.getTripIndex(scheduledTripId), - "New trip should not be found in scheduled time table" - ); - - assertEquals( - "UPDATED | A1 0:01 0:01:01 | B1 [C] 0:01:52 0:01:58 | C1 0:02:50 0:02:51", - env.getRealtimeTimetable(scheduledTripId) - ); - } + assertEquals( + "UPDATED | A1 0:01 0:01:01 | B1 [C] 0:01:52 0:01:58 | C1 0:02:50 0:02:51", + env.getRealtimeTimetable(scheduledTripId) + ); } /** @@ -141,51 +87,18 @@ void scheduledTripWithPreviouslySkipped() { // stoptime updates have gone through var snapshot = env.getTimetableSnapshot(); - { - var newTripPattern = snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE); - assertNull(newTripPattern); - final Trip trip = env.transitModel.getTransitModelIndex().getTripForId().get(tripId); - - var originalTripPattern = env.transitModel - .getTransitModelIndex() - .getPatternForTrip() - .get(trip); - var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); - - var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); - - assertNotSame(originalTimetableForToday, originalTimetableScheduled); - - int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); - - assertTrue( - originalTripIndexScheduled > -1, - "Original trip should be found in scheduled time table" - ); - var originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( - originalTripIndexScheduled - ); - assertFalse( - originalTripTimesScheduled.isCanceledOrDeleted(), - "Original trip times should not be canceled in scheduled time table" - ); - assertEquals(RealTimeState.SCHEDULED, originalTripTimesScheduled.getRealTimeState()); - int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); - - assertTrue( - originalTripIndexForToday > -1, - "Original trip should be found in time table for service date" - ); - - assertEquals( - "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", - env.getScheduledTimetable(tripId) - ); - assertEquals( - "UPDATED | A1 0:01 0:01:01 | B1 0:02 0:02:01 | C1 0:02:50 0:02:51", - env.getRealtimeTimetable(tripId, SERVICE_DATE) - ); - } + assertNull(snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE)); + + assertNewTripTimesIsUpdated(env, tripId); + + assertEquals( + "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", + env.getScheduledTimetable(tripId) + ); + assertEquals( + "UPDATED | A1 0:01 0:01:01 | B1 0:02 0:02:01 | C1 0:02:50 0:02:51", + env.getRealtimeTimetable(tripId, SERVICE_DATE) + ); } /** @@ -205,96 +118,96 @@ void skippedNoData() { assertSuccess(env.applyTripUpdate(tripUpdate)); - // THEN + assertOriginalTripPatternIsDeleted(env, tripId); + + assertNewTripTimesIsUpdated(env, tripId); + + assertEquals( + "UPDATED | A1 [ND] 0:01 0:01:01 | B1 [C] 0:01:10 0:01:11 | C1 [ND] 0:01:20 0:01:21", + env.getRealtimeTimetable(env.trip2) + ); + } + + private static void assertOriginalTripPatternIsDeleted( + RealtimeTestEnvironment env, + FeedScopedId tripId + ) { + var trip = env.transitModel.getTransitModelIndex().getTripForId().get(tripId); + var originalTripPattern = env.transitModel.getTransitModelIndex().getPatternForTrip().get(trip); var snapshot = env.getTimetableSnapshot(); + var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); + var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); + + assertNotSame(originalTimetableForToday, originalTimetableScheduled); + + int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); + assertTrue( + originalTripIndexScheduled > -1, + "Original trip should be found in scheduled time table" + ); + var originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( + originalTripIndexScheduled + ); + assertFalse( + originalTripTimesScheduled.isCanceledOrDeleted(), + "Original trip times should not be canceled in scheduled time table" + ); + + assertEquals( + "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", + TripTimesStringBuilder.encodeTripTimes(originalTripTimesScheduled, originalTripPattern) + ); + + int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); + assertTrue( + originalTripIndexForToday > -1, + "Original trip should be found in time table for service date" + ); + var originalTripTimesForToday = originalTimetableForToday.getTripTimes( + originalTripIndexForToday + ); + assertTrue( + originalTripTimesForToday.isDeleted(), + "Original trip times should be deleted in time table for service date" + ); + // original trip should be deleted + assertEquals(RealTimeState.DELETED, originalTripTimesForToday.getRealTimeState()); + } - // Original trip pattern - { - var originalTripPattern = env.transitModel - .getTransitModelIndex() - .getPatternForTrip() - .get(env.trip2); - - var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); - var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); - - assertNotSame(originalTimetableForToday, originalTimetableScheduled); - - int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); - assertTrue( - originalTripIndexScheduled > -1, - "Original trip should be found in scheduled time table" - ); - var originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( - originalTripIndexScheduled - ); - assertFalse( - originalTripTimesScheduled.isCanceledOrDeleted(), - "Original trip times should not be canceled in scheduled time table" - ); - - assertEquals( - "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", - TripTimesStringBuilder.encodeTripTimes(originalTripTimesScheduled, originalTripPattern) - ); - - int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); - assertTrue( - originalTripIndexForToday > -1, - "Original trip should be found in time table for service date" - ); - var originalTripTimesForToday = originalTimetableForToday.getTripTimes( - originalTripIndexForToday - ); - assertTrue( - originalTripTimesForToday.isDeleted(), - "Original trip times should be deleted in time table for service date" - ); - // original trip should be deleted - assertEquals(RealTimeState.DELETED, originalTripTimesForToday.getRealTimeState()); - } - - // New trip pattern - { - var newTripPattern = snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE); - assertNotNull(newTripPattern, "New trip pattern should be found"); - - var newTimetableForToday = snapshot.resolve(newTripPattern, SERVICE_DATE); - var newTimetableScheduled = snapshot.resolve(newTripPattern, null); - - assertNotSame(newTimetableForToday, newTimetableScheduled); - - assertTrue(newTripPattern.canBoard(0)); - assertFalse(newTripPattern.canBoard(1)); - assertTrue(newTripPattern.canBoard(2)); - - assertEquals( - new NonLocalizedString("Headsign of TestTrip2"), - newTripPattern.getTripHeadsign() - ); - assertEquals( - newTripPattern.getOriginalTripPattern().getTripHeadsign(), - newTripPattern.getTripHeadsign() - ); - - int newTimetableForTodayModifiedTripIndex = newTimetableForToday.getTripIndex(tripId); - assertTrue( - newTimetableForTodayModifiedTripIndex > -1, - "New trip should be found in time table for service date" - ); - - var newTripTimes = newTimetableForToday.getTripTimes(tripId); - - assertEquals( - -1, - newTimetableScheduled.getTripIndex(tripId), - "New trip should not be found in scheduled time table" - ); - - assertEquals( - "UPDATED | A1 [ND] 0:01 0:01:01 | B1 [C] 0:01:10 0:01:11 | C1 [ND] 0:01:20 0:01:21", - TripTimesStringBuilder.encodeTripTimes(newTripTimes, newTripPattern) - ); - } + private static void assertNewTripTimesIsUpdated( + RealtimeTestEnvironment env, + FeedScopedId tripId + ) { + var originalTripPattern = env.transitModel + .getTransitModelIndex() + .getPatternForTrip() + .get(env.trip2); + var snapshot = env.getTimetableSnapshot(); + var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); + + var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); + + assertNotSame(originalTimetableForToday, originalTimetableScheduled); + + int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); + + assertTrue( + originalTripIndexScheduled > -1, + "Original trip should be found in scheduled time table" + ); + var originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( + originalTripIndexScheduled + ); + assertFalse( + originalTripTimesScheduled.isCanceledOrDeleted(), + "Original trip times should not be canceled in scheduled time table" + ); + assertEquals(RealTimeState.SCHEDULED, originalTripTimesScheduled.getRealTimeState()); + int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); + + assertTrue( + originalTripIndexForToday > -1, + "Original trip should be found in time table for service date" + ); } } diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java index 6cacaeb6ca9..e6e7a4737f7 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java @@ -1,5 +1,6 @@ package org.opentripplanner.updater.trip.moduletests.rejection; +import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; import static org.opentripplanner.test.support.UpdateResultAssertions.assertFailure; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.INVALID_INPUT_STRUCTURE; @@ -26,16 +27,12 @@ void invalidTripId(String tripId) { if (tripId != null) { tripDescriptorBuilder.setTripId(tripId); } - tripDescriptorBuilder.setScheduleRelationship( - GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED - ); + tripDescriptorBuilder.setScheduleRelationship(SCHEDULED); var tripUpdateBuilder = GtfsRealtime.TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); var tripUpdate = tripUpdateBuilder.build(); - var result = env.applyTripUpdate(tripUpdate); - - assertFailure(INVALID_INPUT_STRUCTURE, result); + assertFailure(INVALID_INPUT_STRUCTURE, env.applyTripUpdate(tripUpdate)); } } From 21e745f4651d9f0c5b5b9112d0b209904fb4b9ab Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 19 Jun 2024 09:44:31 +0200 Subject: [PATCH 047/192] Simplify AddedTest --- .../trip/moduletests/addition/AddedTest.java | 46 ++++++------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 8a2d6e96407..1e75f558510 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -2,7 +2,6 @@ import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.ADDED; 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.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; @@ -45,22 +44,15 @@ void addedTrip() { @Test void addedTripWithNewRoute() { var env = RealtimeTestEnvironment.gtfs(); - final var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); - // add extension to set route name, url, mode - builder.addTripExtension(); - - builder + var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone) + .addTripExtension() .addStopTime(STOP_A1_ID, 30, DropOffPickupType.PHONE_AGENCY) .addStopTime(STOP_B1_ID, 40, DropOffPickupType.COORDINATE_WITH_DRIVER) - .addStopTime(STOP_B1_ID, 55, DropOffPickupType.NONE); - - var tripUpdate = builder.build(); + .addStopTime(STOP_B1_ID, 55, DropOffPickupType.NONE) + .build(); - // WHEN var result = assertSuccess(env.applyTripUpdate(tripUpdate)); - // THEN - assertTrue(result.warnings().isEmpty()); var pattern = assertAddedTrip(ADDED_TRIP_ID, env); @@ -83,21 +75,16 @@ void addedTripWithNewRoute() { @Test void addedWithUnknownStop() { var env = RealtimeTestEnvironment.gtfs(); - final var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); - // add extension to set route name, url, mode - builder.addTripExtension(); - - builder + var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone) + // add extension to set route name, url, mode + .addTripExtension() .addStopTime(STOP_A1_ID, 30, DropOffPickupType.PHONE_AGENCY) .addStopTime("UNKNOWN_STOP_ID", 40, DropOffPickupType.COORDINATE_WITH_DRIVER) - .addStopTime(STOP_C1_ID, 55, DropOffPickupType.NONE); - - var tripUpdate = builder.build(); + .addStopTime(STOP_C1_ID, 55, DropOffPickupType.NONE) + .build(); var result = assertSuccess(env.applyTripUpdate(tripUpdate)); - assertFalse(result.warnings().isEmpty()); - assertEquals( List.of(UpdateSuccess.WarningType.UNKNOWN_STOPS_REMOVED_FROM_ADDED_TRIP), result.warnings() @@ -111,16 +98,13 @@ void addedWithUnknownStop() { @Test void repeatedlyAddedTripWithNewRoute() { var env = RealtimeTestEnvironment.gtfs(); - final var builder = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone); - // add extension to set route name, url, mode - builder.addTripExtension(); - - builder + var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone) + // add extension to set route name, url, mode + .addTripExtension() .addStopTime(STOP_A1_ID, 30, DropOffPickupType.PHONE_AGENCY) .addStopTime(STOP_B1_ID, 40, DropOffPickupType.COORDINATE_WITH_DRIVER) - .addStopTime(STOP_C1_ID, 55, DropOffPickupType.NONE); - - var tripUpdate = builder.build(); + .addStopTime(STOP_C1_ID, 55, DropOffPickupType.NONE) + .build(); assertSuccess(env.applyTripUpdate(tripUpdate)); var pattern = assertAddedTrip(ADDED_TRIP_ID, env); @@ -131,8 +115,6 @@ void repeatedlyAddedTripWithNewRoute() { var secondPattern = assertAddedTrip(ADDED_TRIP_ID, env); var secondRoute = secondPattern.getRoute(); - // THEN - assertSame(firstRoute, secondRoute); assertNotNull(env.transitModel.getTransitModelIndex().getRouteForId(firstRoute.getId())); } From 6755cbc868b09e25f3395658b0d6bf566c543a3f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 19 Jun 2024 10:18:12 +0200 Subject: [PATCH 048/192] Update Javadoc --- .../opentripplanner/ext/geocoder/configure/GeocoderModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java b/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java index 17949e5cb8a..b67f81385b8 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java @@ -10,7 +10,7 @@ import org.opentripplanner.transit.service.TransitService; /** - * This module builds the lucene geocoder based on the whether the feature flag is + * This module builds the Lucene geocoder based on whether the feature flag is on or off. */ @Module public class GeocoderModule { From 4bbe43e8c708b45bde67fb641b19ac6db1001afb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 19 Jun 2024 10:20:17 +0200 Subject: [PATCH 049/192] Re-add backwards-compatibility --- .../ext/geocoder/GeocoderResource.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java b/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java index c9cd199c557..304829843ae 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/GeocoderResource.java @@ -3,6 +3,7 @@ import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Context; @@ -30,6 +31,20 @@ public GeocoderResource(@Context OtpServerRequestContext requestContext) { luceneIndex = requestContext.lucenceIndex(); } + /** + * This class is only here for backwards-compatibility. It will be removed in the future. + */ + @Path("/routers/{ignoreRouterId}/geocode") + public static class GeocoderResourceOldPath extends GeocoderResource { + + public GeocoderResourceOldPath( + @Context OtpServerRequestContext serverContext, + @PathParam("ignoreRouterId") String ignore + ) { + super(serverContext); + } + } + /** * Geocode using data using the OTP graph for stops, clusters and street names * From 960f2439a51fb7596fa0f3699158e2e10a9e3fc4 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Wed, 19 Jun 2024 13:55:34 +0200 Subject: [PATCH 050/192] Add OTPFeature MultiCriteriaGroupMaxFilter - turn on/off This is default off, witch make OTP work as before. We will remove the old behavior and this OTPFeature if the new multi-criteria filter works as expected. --- docs/Configuration.md | 61 ++++++++++--------- .../framework/application/OTPFeature.java | 8 +++ 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 05611e23628..71048f08f82 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -219,36 +219,37 @@ Here is a list of all features which can be toggled on/off and their default val -| Feature | Description | Enabled by default | Sandbox | -|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------:|:-------:| -| `APIBikeRental` | Enable the bike rental endpoint. | ✓️ | | -| `APIServerInfo` | Enable the server info endpoint. | ✓️ | | -| `APIUpdaterStatus` | Enable endpoint for graph updaters status. | ✓️ | | -| `ConsiderPatternsForDirectTransfers` | Enable limiting transfers so that there is only a single transfer to each pattern. | ✓️ | | -| `DebugUi` | Enable the debug GraphQL client and web UI and located at the root of the web server as well as the debug map tiles it uses. Be aware that the map tiles are not a stable API and can change without notice. Use the [vector tiles feature if](sandbox/MapboxVectorTilesApi.md) you want a stable map tiles API. | ✓️ | | -| `FloatingBike` | Enable floating bike routing. | ✓️ | | -| `GtfsGraphQlApi` | Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md). | ✓️ | | -| `GtfsGraphQlApiRentalStationFuzzyMatching` | Does vehicleRentalStation query also allow ids that are not feed scoped. | | | -| `MinimumTransferTimeIsDefinitive` | If the minimum transfer time is a lower bound (default) or the definitive time for the transfer. Set this to `true` if you want to set a transfer time lower than what OTP derives from OSM data. | | | -| `OptimizeTransfers` | OTP will inspect all itineraries found and optimize where (which stops) the transfer will happen. Waiting time, priority and guaranteed transfers are taken into account. | ✓️ | | -| `ParallelRouting` | Enable performing parts of the trip planning in parallel. | | | -| `TransferConstraints` | Enforce transfers to happen according to the _transfers.txt_ (GTFS) and Interchanges (NeTEx). Turning this _off_ will increase the routing performance a little. | ✓️ | | -| `TransmodelGraphQlApi` | Enable the [Transmodel (NeTEx) GraphQL API](apis/TransmodelApi.md). | ✓️ | ✓️ | -| `ActuatorAPI` | Endpoint for actuators (service health status). | | ✓️ | -| `AsyncGraphQLFetchers` | Whether the @async annotation in the GraphQL schema should lead to the fetch being executed asynchronously. This allows batch or alias queries to run in parallel at the cost of consuming extra threads. | | | -| `Co2Emissions` | Enable the emissions sandbox module. | | ✓️ | -| `DataOverlay` | Enable usage of data overlay when calculating costs for the street network. | | ✓️ | -| `FaresV2` | Enable import of GTFS-Fares v2 data. | | ✓️ | -| `FlexRouting` | Enable FLEX routing. | | ✓️ | -| `GoogleCloudStorage` | Enable Google Cloud Storage integration. | | ✓️ | -| `LegacyRestApi` | Enable legacy REST API. This API will be removed in the future. | ✓️ | ✓️ | -| `RealtimeResolver` | When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with real-time data | | ✓️ | -| `ReportApi` | Enable the report API. | | ✓️ | -| `RestAPIPassInDefaultConfigAsJson` | Enable a default RouteRequest to be passed in as JSON on the REST API - FOR DEBUGGING ONLY! | | | -| `SandboxAPIGeocoder` | Enable the Geocoder API. | | ✓️ | -| `SandboxAPIMapboxVectorTilesApi` | Enable Mapbox vector tiles API. | | ✓️ | -| `SandboxAPIParkAndRideApi` | Enable park-and-ride endpoint. | | ✓️ | -| `TransferAnalyzer` | Analyze transfers during graph build. | | ✓️ | +| Feature | Description | Enabled by default | Sandbox | +|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------:|:-------:| +| `APIBikeRental` | Enable the bike rental endpoint. | ✓️ | | +| `APIServerInfo` | Enable the server info endpoint. | ✓️ | | +| `APIUpdaterStatus` | Enable endpoint for graph updaters status. | ✓️ | | +| `ConsiderPatternsForDirectTransfers` | Enable limiting transfers so that there is only a single transfer to each pattern. | ✓️ | | +| `DebugUi` | Enable the debug GraphQL client and web UI and located at the root of the web server as well as the debug map tiles it uses. Be aware that the map tiles are not a stable API and can change without notice. Use the [vector tiles feature if](sandbox/MapboxVectorTilesApi.md) you want a stable map tiles API. | ✓️ | | +| `FloatingBike` | Enable floating bike routing. | ✓️ | | +| `GtfsGraphQlApi` | Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md). | ✓️ | | +| `GtfsGraphQlApiRentalStationFuzzyMatching` | Does vehicleRentalStation query also allow ids that are not feed scoped. | | | +| `MinimumTransferTimeIsDefinitive` | If the minimum transfer time is a lower bound (default) or the definitive time for the transfer. Set this to `true` if you want to set a transfer time lower than what OTP derives from OSM data. | | | +| `OptimizeTransfers` | OTP will inspect all itineraries found and optimize where (which stops) the transfer will happen. Waiting time, priority and guaranteed transfers are taken into account. | ✓️ | | +| `ParallelRouting` | Enable performing parts of the trip planning in parallel. | | | +| `TransferConstraints` | Enforce transfers to happen according to the _transfers.txt_ (GTFS) and Interchanges (NeTEx). Turning this _off_ will increase the routing performance a little. | ✓️ | | +| `TransmodelGraphQlApi` | Enable the [Transmodel (NeTEx) GraphQL API](apis/TransmodelApi.md). | ✓️ | ✓️ | +| `ActuatorAPI` | Endpoint for actuators (service health status). | | ✓️ | +| `AsyncGraphQLFetchers` | Whether the @async annotation in the GraphQL schema should lead to the fetch being executed asynchronously. This allows batch or alias queries to run in parallel at the cost of consuming extra threads. | | | +| `Co2Emissions` | Enable the emissions sandbox module. | | ✓️ | +| `DataOverlay` | Enable usage of data overlay when calculating costs for the street network. | | ✓️ | +| `FaresV2` | Enable import of GTFS-Fares v2 data. | | ✓️ | +| `FlexRouting` | Enable FLEX routing. | | ✓️ | +| `GoogleCloudStorage` | Enable Google Cloud Storage integration. | | ✓️ | +| `LegacyRestApi` | Enable legacy REST API. This API will be removed in the future. | ✓️ | ✓️ | +| `MultiCriteriaGroupMaxFilter` | Keep the best itinerary with respect to each criteria used in the transit-routing search. For example the itinerary with the lowest cost, fewest transfers, and each unique transit-group (transit-group-priority) is kept, even if the max-limit is exceeded. This is turned off by default for now, until this feature is well tested. | | | +| `RealtimeResolver` | When routing with ignoreRealtimeUpdates=true, add an extra step which populates results with real-time data | | ✓️ | +| `ReportApi` | Enable the report API. | | ✓️ | +| `RestAPIPassInDefaultConfigAsJson` | Enable a default RouteRequest to be passed in as JSON on the REST API - FOR DEBUGGING ONLY! | | | +| `SandboxAPIGeocoder` | Enable the Geocoder API. | | ✓️ | +| `SandboxAPIMapboxVectorTilesApi` | Enable Mapbox vector tiles API. | | ✓️ | +| `SandboxAPIParkAndRideApi` | Enable park-and-ride endpoint. | | ✓️ | +| `TransferAnalyzer` | Analyze transfers during graph build. | | ✓️ | diff --git a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java index 29489de19f2..accbf4041f2 100644 --- a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java +++ b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java @@ -90,6 +90,14 @@ public enum OTPFeature { FlexRouting(false, true, "Enable FLEX routing."), GoogleCloudStorage(false, true, "Enable Google Cloud Storage integration."), LegacyRestApi(true, true, "Enable legacy REST API. This API will be removed in the future."), + MultiCriteriaGroupMaxFilter( + false, + false, + "Keep the best itinerary with respect to each criteria used in the transit-routing search. " + + "For example the itinerary with the lowest cost, fewest transfers, and each unique transit-group " + + "(transit-group-priority) is kept, even if the max-limit is exceeded. This is turned off by default " + + "for now, until this feature is well tested." + ), RealtimeResolver( false, true, From 78c4a17669b3eb12dcf3f75f4c098dc436887587 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Wed, 19 Jun 2024 14:00:06 +0200 Subject: [PATCH 051/192] refactor: rename package mcmin to mcmax --- .../system/{mcmin => mcmax}/Group.java | 3 +-- .../filters/system/{mcmin => mcmax}/Item.java | 2 +- .../McMinimumNumberItineraryFilter.java | 2 +- .../system/{mcmin => mcmax}/State.java | 21 ++++++++++++++----- .../system/{mcmin => mcmax}/ItemTest.java | 2 +- .../McMinimumNumberItineraryFilterTest.java | 20 +++++++++--------- 6 files changed, 30 insertions(+), 20 deletions(-) rename src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/{mcmin => mcmax}/Group.java (98%) rename src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/{mcmin => mcmax}/Item.java (98%) rename src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/{mcmin => mcmax}/McMinimumNumberItineraryFilter.java (99%) rename src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/{mcmin => mcmax}/State.java (93%) rename src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/{mcmin => mcmax}/ItemTest.java (99%) rename src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/{mcmin => mcmax}/McMinimumNumberItineraryFilterTest.java (95%) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Group.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/Group.java similarity index 98% rename from src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Group.java rename to src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/Group.java index 0f231517ae3..7bfdad83e8f 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Group.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/Group.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmax; import java.util.ArrayList; import java.util.Iterator; @@ -25,7 +25,6 @@ boolean isEmpty() { return items.isEmpty(); } - boolean isSingleItemGroup() { return items.size() == 1; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Item.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/Item.java similarity index 98% rename from src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Item.java rename to src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/Item.java index 58898f2ee63..36c3d662493 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/Item.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/Item.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmax; import org.opentripplanner.model.plan.Itinerary; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilter.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilter.java similarity index 99% rename from src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilter.java rename to src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilter.java index b1e7b857c3b..da8d6fa1c66 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilter.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmax; import java.util.List; import java.util.function.Predicate; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/State.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java similarity index 93% rename from src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/State.java rename to src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java index acce6a963f4..d616c505446 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/State.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmax; import java.util.ArrayList; import java.util.Collection; @@ -12,6 +12,7 @@ * simple bookkeeping for the state of the filter. */ class State { + private final List items; private final List groups; private final List result = new ArrayList<>(); @@ -86,7 +87,6 @@ private void removeGroupsWitchContainsItem(Item item) { groups.removeIf(Group::isEmpty); } - /** * The best item is the one witch exists in most groups, and in case of a tie, the sort order/ * itinerary index is used. @@ -108,7 +108,12 @@ private static Item findBestItem(List groups) { */ @Nullable private static Item findItemInFirstSingleItemGroup(List groups) { - return groups.stream().filter(Group::isSingleItemGroup).findFirst().map(Group::first).orElse(null); + return groups + .stream() + .filter(Group::isSingleItemGroup) + .findFirst() + .map(Group::first) + .orElse(null); } private static ArrayList createListOfItems(List itineraries) { @@ -119,7 +124,10 @@ private static ArrayList createListOfItems(List itineraries) { return items; } - private static List createGroups(Collection items, List comparators) { + private static List createGroups( + Collection items, + List comparators + ) { List groups = new ArrayList<>(); for (SingeCriteriaComparator comparator : comparators) { if (comparator.strictOrder()) { @@ -135,7 +143,10 @@ private static List createGroups(Collection items, List items, SingeCriteriaComparator comparator) { + private static Group createOrderedGroup( + Collection items, + SingeCriteriaComparator comparator + ) { Group group = null; for (Item item : items) { if (group == null) { diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/ItemTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/ItemTest.java similarity index 99% rename from src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/ItemTest.java rename to src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/ItemTest.java index fbb2f5dea57..c8cd04212e5 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/ItemTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/ItemTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmax; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilterTest.java similarity index 95% rename from src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilterTest.java rename to src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilterTest.java index 8d3d03cb857..e094e53cd03 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmin/McMinimumNumberItineraryFilterTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilterTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmin; +package org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmax; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; @@ -153,10 +153,10 @@ private static String toStr(List list) { .stream() .map(i -> "[ %d %d %s ]".formatted( - i.getGeneralizedCost(), - i.getNumberOfTransfers(), - groupsToString(i.getGeneralizedCost2().orElse(-1)) - ) + i.getGeneralizedCost(), + i.getNumberOfTransfers(), + groupsToString(i.getGeneralizedCost2().orElse(-1)) + ) ) .collect(Collectors.joining(", ")); } @@ -182,11 +182,11 @@ Itinerary create() { public String toString() { // The red-x is a unicode character(U+274C) and should be visible in most IDEs. return "%s %d %d %s".formatted( - expected ? "" : "❌", - c1, - nTransfers, - groupsToString(transitGroupIds) - ); + expected ? "" : "❌", + c1, + nTransfers, + groupsToString(transitGroupIds) + ); } } } From 04574fa9f9a2fa28cc1a310687d3e77a43e765b5 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Wed, 19 Jun 2024 14:12:20 +0200 Subject: [PATCH 052/192] refactor: rename McMinimumNumberItineraryFilter to McMaxLimitFilter --- ...eraryFilter.java => McMaxLimitFilter.java} | 25 +++++++++++-------- ...terTest.java => McMaxLimitFilterTest.java} | 6 ++--- 2 files changed, 17 insertions(+), 14 deletions(-) rename src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/{McMinimumNumberItineraryFilter.java => McMaxLimitFilter.java} (80%) rename src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/{McMinimumNumberItineraryFilterTest.java => McMaxLimitFilterTest.java} (96%) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilter.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java similarity index 80% rename from src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilter.java rename to src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java index da8d6fa1c66..180ef979085 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java @@ -8,15 +8,15 @@ /** * This filter is used to reduce a set of itineraries down to the specified limit, if possible. - * The filter is guaranteed to keep at least the given {@code minNumItineraries} and also - * the best itinerary for each criterion. The criterion is defined using the list of - * {@code comparators}. + * The filter is guaranteed to keep at least the given {@code minNumItineraries} and/or the best + * itinerary for each criterion. The criterion is defined using the list of {@code comparators}. *

- * The main usage of this filter is to combine it with a grouping filter and for each group + * The main usage of this filter is to combine it with a transit grouping filter and for each group * make sure there is at least {@code minNumItineraries} and that the best itinerary with respect * to each criterion is kept. So, if the grouping is based on time and riding common trips, then * this filter will use the reminding criterion (transfers, generalized-cost, - * [transit-group-priority]) to filter the grouped set of itineraries. + * [transit-group-priority]) to filter the grouped set of itineraries. DO NOT INCLUDE CRITERIA + * USED TO GROUP THE ITINERARIES, ONLY THE REMINDING CRITERION USED IN THE RAPTOR SEARCH. *

* IMPLEMENTATION DETAILS *

@@ -31,11 +31,14 @@ *

* Note! For a criteria like num-of-transfers or generalized-cost, there is only one set of "best" * itineraries, and usually there are only one or a few itineraries. In case there is more than one, - * picking just one is fine. But, for transit-group-priority there might be more than on set of - * itineraries. For each set, we need to pick one itinerary for the final result. Each of these - * sets may or may not have more than one itinerary. + * picking just one is fine. But, for transit-group-priority there might be more than one optimal + * set of itineraries. For each set, we need to pick one itinerary for the final result. Each of + * these sets may or may not have more than one itinerary. If you group by agency, then there will + * be at least one itinerary for each agency present in the result (simplified, an itinerary may + * consist of legs with different agencies). The transit-group-priority pareto-function used by + * Raptor is reused, so we do not need to worry about the logic here. *

- * Let's discuss an example: + * Let's discuss an example (this example also exists as a unit-test case): *

  *   minNumItineraries = 4
  *   comparators = [ generalized-cost, min-num-transfers, transit-group-priority ]
@@ -63,13 +66,13 @@
  * The `minNumItineraries` limit is not met, so we need to pick another itinerary, we use the
  * sort-order again and add itinerary #0. The result returned is: [#0, #2, #4, #5]
  */
-public class McMinimumNumberItineraryFilter implements RemoveItineraryFlagger {
+public class McMaxLimitFilter implements RemoveItineraryFlagger {
 
   private final String name;
   private final int minNumItineraries;
   private final List comparators;
 
-  public McMinimumNumberItineraryFilter(
+  public McMaxLimitFilter(
     String name,
     int minNumItineraries,
     List comparators
diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java
similarity index 96%
rename from src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilterTest.java
rename to src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java
index e094e53cd03..14d0fe62337 100644
--- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMinimumNumberItineraryFilterTest.java
+++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java
@@ -14,7 +14,7 @@
 import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n;
 import org.opentripplanner.transit.model._data.TransitModelForTest;
 
-class McMinimumNumberItineraryFilterTest {
+class McMaxLimitFilterTest {
 
   private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of();
 
@@ -44,7 +44,7 @@ class McMinimumNumberItineraryFilterTest {
   private static final int TX_1 = 1;
   private static final int TX_2 = 2;
 
-  private final McMinimumNumberItineraryFilter subject = new McMinimumNumberItineraryFilter(
+  private final McMaxLimitFilter subject = new McMaxLimitFilter(
     "test",
     2,
     List.of(
@@ -91,7 +91,7 @@ static List> filterTestCases() {
         row(EXP_KEEP, COST_MED, TX_0, GROUP_BC, "Best: num-of-transfers")
       ),
       /**
-       * This is the example explained in JavaDoc {@link McMinimumNumberItineraryFilter}
+       * This is the example explained in JavaDoc {@link McMaxLimitFilter}
        */
       List.of(
         row(EXP_DROP, COST_LOW, TX_1, GROUP_A, ""),

From ef6ff0edbcba690bae9ce935f7fde75c3f88248e Mon Sep 17 00:00:00 2001
From: Thomas Gran 
Date: Wed, 19 Jun 2024 14:17:31 +0200
Subject: [PATCH 053/192] feature: Integrate McMaxLimitFilter in
 ItineraryListFilterChain

---
 .../ItineraryListFilterChainBuilder.java      | 36 ++++++++++++++++---
 .../system/SingeCriteriaComparator.java       |  2 +-
 .../RouteRequestToFilterChainMapper.java      |  4 +++
 .../system/SingeCriteriaComparatorTest.java   |  4 +--
 .../system/mcmax/McMaxLimitFilterTest.java    |  2 +-
 5 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java
index e8b8ed43c1c..84f3030b09f 100644
--- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java
+++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java
@@ -12,6 +12,7 @@
 import java.util.function.Function;
 import javax.annotation.Nullable;
 import org.opentripplanner.ext.accessibilityscore.DecorateWithAccessibilityScore;
+import org.opentripplanner.framework.application.OTPFeature;
 import org.opentripplanner.framework.collection.ListSection;
 import org.opentripplanner.framework.lang.Sandbox;
 import org.opentripplanner.model.plan.Itinerary;
@@ -27,6 +28,8 @@
 import org.opentripplanner.routing.algorithm.filterchain.filters.system.NumItinerariesFilter;
 import org.opentripplanner.routing.algorithm.filterchain.filters.system.OutsideSearchWindowFilter;
 import org.opentripplanner.routing.algorithm.filterchain.filters.system.PagingFilter;
+import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingeCriteriaComparator;
+import org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmax.McMaxLimitFilter;
 import org.opentripplanner.routing.algorithm.filterchain.filters.transit.DecorateTransitAlert;
 import org.opentripplanner.routing.algorithm.filterchain.filters.transit.KeepItinerariesWithFewestTransfers;
 import org.opentripplanner.routing.algorithm.filterchain.filters.transit.RemoveItinerariesWithShortStreetLeg;
@@ -64,7 +67,6 @@ public class ItineraryListFilterChainBuilder {
   private static final int NOT_SET = -1;
   private final SortOrder sortOrder;
   private final List groupBySimilarity = new ArrayList<>();
-
   private ItineraryFilterDebugProfile debug = ItineraryFilterDebugProfile.OFF;
   private int maxNumberOfItineraries = NOT_SET;
   private ListSection maxNumberOfItinerariesCropSection = ListSection.TAIL;
@@ -86,6 +88,7 @@ public class ItineraryListFilterChainBuilder {
   private double minBikeParkingDistance;
   private boolean removeTransitIfWalkingIsBetter = true;
   private ItinerarySortKey itineraryPageCut;
+  private boolean transitGroupPriorityUsed = false;
 
   /**
    * Sandbox filters which decorate the itineraries with extra information.
@@ -292,6 +295,15 @@ public ItineraryListFilterChainBuilder withPagingDeduplicationFilter(
     return this;
   }
 
+  /**
+   * Adjust filters to include multi-criteria parameter c2 and treat it as the
+   * transit-group.
+   */
+  public ItineraryListFilterChainBuilder withTransitGroupPriority() {
+    this.transitGroupPriorityUsed = true;
+    return this;
+  }
+
   /**
    * If set, walk-all-the-way itineraries are removed. This happens AFTER e.g. the group-by and
    * remove-transit-with-higher-cost-than-best-on-street-only filter. This make sure that poor
@@ -531,7 +543,7 @@ private ItineraryListFilter buildGroupBySameRoutesAndStopsFilter() {
       GroupBySameRoutesAndStops::new,
       List.of(
         new SortingFilter(SortOrderComparator.comparator(sortOrder)),
-        new RemoveFilter(new MaxLimit(GroupBySameRoutesAndStops.TAG, 1))
+        new RemoveFilter(createMaxLimitFilter(GroupBySameRoutesAndStops.TAG, 1))
       )
     );
   }
@@ -574,7 +586,7 @@ private List buildGroupByTripIdAndDistanceFilters() {
             GroupByAllSameStations::new,
             List.of(
               new SortingFilter(generalizedCostComparator()),
-              new RemoveFilter(new MaxLimit(innerGroupName, 1))
+              new RemoveFilter(createMaxLimitFilter(innerGroupName, 1))
             )
           )
         );
@@ -587,7 +599,7 @@ private List buildGroupByTripIdAndDistanceFilters() {
       }
 
       addSort(nested, generalizedCostComparator());
-      addRemoveFilter(nested, new MaxLimit(tag, group.maxNumOfItinerariesPerGroup));
+      addRemoveFilter(nested, createMaxLimitFilter(tag, group.maxNumOfItinerariesPerGroup));
 
       nested.add(new KeepItinerariesWithFewestTransfers(sysTags));
 
@@ -620,4 +632,20 @@ private static void addDecorateFilter(
   ) {
     filters.add(new DecorateFilter(decorator));
   }
+
+  private RemoveItineraryFlagger createMaxLimitFilter(String filterName, int maxLimit) {
+    if (OTPFeature.MultiCriteriaGroupMaxFilter.isOn()) {
+      List comparators = new ArrayList<>();
+      comparators.add(SingeCriteriaComparator.compareGeneralizedCost());
+      comparators.add(SingeCriteriaComparator.compareNumTransfers());
+      if (transitGroupPriorityUsed) {
+        comparators.add(SingeCriteriaComparator.compareTransitGroupsPriority());
+      }
+      return new McMaxLimitFilter(filterName, maxLimit, comparators);
+    }
+    // Default is to just use a "hard" max limit
+    else {
+      return new MaxLimit(filterName, maxLimit);
+    }
+  }
 }
diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java
index aa876d5bcdc..528542d40df 100644
--- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java
+++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java
@@ -44,7 +44,7 @@ static SingeCriteriaComparator compareGeneralizedCost() {
   }
 
   @SuppressWarnings("OptionalGetWithoutIsPresent")
-  static SingeCriteriaComparator compareTransitPriorityGroups() {
+  static SingeCriteriaComparator compareTransitGroupsPriority() {
     return (left, right) ->
       TransitGroupPriority32n.dominate(
         left.getGeneralizedCost2().get(),
diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java
index 651d94b4eac..c1fab68f999 100644
--- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java
+++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RouteRequestToFilterChainMapper.java
@@ -94,6 +94,10 @@ public static ItineraryListFilterChain createFilterChain(
       .withRemoveTransitIfWalkingIsBetter(true)
       .withDebugEnabled(params.debug());
 
+    if (!request.preferences().transit().relaxTransitGroupPriority().isNormal()) {
+      builder.withTransitGroupPriority();
+    }
+
     var fareService = context.graph().getFareService();
     if (fareService != null) {
       builder.withFareDecorator(new DecorateWithFare(fareService));
diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java
index 5435a8864c7..8f4b2b4ddeb 100644
--- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java
+++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java
@@ -57,7 +57,7 @@ static void setUp() {
   void strictOrder() {
     assertTrue(SingeCriteriaComparator.compareNumTransfers().strictOrder());
     assertTrue(SingeCriteriaComparator.compareGeneralizedCost().strictOrder());
-    assertFalse(SingeCriteriaComparator.compareTransitPriorityGroups().strictOrder());
+    assertFalse(SingeCriteriaComparator.compareTransitGroupsPriority().strictOrder());
   }
 
   @Test
@@ -99,7 +99,7 @@ void compareTransitPriorityGroups() {
       .withGeneralizedCost2(TransitGroupPriority32n.mergeInGroupId(1, 2))
       .build();
 
-    var subject = SingeCriteriaComparator.compareTransitPriorityGroups();
+    var subject = SingeCriteriaComparator.compareTransitGroupsPriority();
 
     assertTrue(subject.leftDominanceExist(group1, group2));
     assertTrue(subject.leftDominanceExist(group2, group1));
diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java
index 14d0fe62337..3c35b0172f8 100644
--- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java
+++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java
@@ -50,7 +50,7 @@ class McMaxLimitFilterTest {
     List.of(
       SingeCriteriaComparator.compareGeneralizedCost(),
       SingeCriteriaComparator.compareNumTransfers(),
-      SingeCriteriaComparator.compareTransitPriorityGroups()
+      SingeCriteriaComparator.compareTransitGroupsPriority()
     )
   );
 

From a714a532e6ef0404e001fbd9519127a8a2935126 Mon Sep 17 00:00:00 2001
From: Leonard Ehrenfried 
Date: Thu, 20 Jun 2024 13:18:21 +0200
Subject: [PATCH 054/192] Inject transit model instead of transit service

---
 .../opentripplanner/ext/geocoder/LuceneIndex.java | 15 ++++++++++-----
 .../ext/geocoder/configure/GeocoderModule.java    |  6 +++---
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java
index f5b4a6e59e9..6283a779088 100644
--- a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java
+++ b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java
@@ -47,14 +47,12 @@
 import org.opentripplanner.transit.model.framework.FeedScopedId;
 import org.opentripplanner.transit.model.site.StopLocation;
 import org.opentripplanner.transit.model.site.StopLocationsGroup;
+import org.opentripplanner.transit.service.DefaultTransitService;
+import org.opentripplanner.transit.service.TransitModel;
 import org.opentripplanner.transit.service.TransitService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class LuceneIndex implements Serializable {
 
-  private static final Logger LOG = LoggerFactory.getLogger(LuceneIndex.class);
-
   private static final String TYPE = "type";
   private static final String ID = "id";
   private static final String SECONDARY_IDS = "secondary_ids";
@@ -70,7 +68,14 @@ public class LuceneIndex implements Serializable {
   private final SuggestIndexSearcher searcher;
   private final StopClusterMapper stopClusterMapper;
 
-  public LuceneIndex(
+  public LuceneIndex(TransitModel transitModel, StopConsolidationService stopConsolidationService) {
+    this(new DefaultTransitService(transitModel), stopConsolidationService);
+  }
+
+  /**
+   * This method is only visible for testing.
+   */
+  LuceneIndex(
     TransitService transitService,
     @Nullable StopConsolidationService stopConsolidationService
   ) {
diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java b/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java
index b67f81385b8..9eaf6ade8e5 100644
--- a/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java
+++ b/src/ext/java/org/opentripplanner/ext/geocoder/configure/GeocoderModule.java
@@ -7,7 +7,7 @@
 import org.opentripplanner.ext.geocoder.LuceneIndex;
 import org.opentripplanner.ext.stopconsolidation.StopConsolidationService;
 import org.opentripplanner.framework.application.OTPFeature;
-import org.opentripplanner.transit.service.TransitService;
+import org.opentripplanner.transit.service.TransitModel;
 
 /**
  * This module builds the Lucene geocoder based on whether the feature flag is on or off.
@@ -19,11 +19,11 @@ public class GeocoderModule {
   @Singleton
   @Nullable
   LuceneIndex luceneIndex(
-    TransitService service,
+    TransitModel transitModel,
     @Nullable StopConsolidationService stopConsolidationService
   ) {
     if (OTPFeature.SandboxAPIGeocoder.isOn()) {
-      return new LuceneIndex(service, stopConsolidationService);
+      return new LuceneIndex(transitModel, stopConsolidationService);
     } else {
       return null;
     }

From 29a750daf8a04d472783603690a30b296ff5ffa4 Mon Sep 17 00:00:00 2001
From: Leonard Ehrenfried 
Date: Thu, 20 Jun 2024 22:41:18 +0200
Subject: [PATCH 055/192] Add comment

---
 .../standalone/configure/ConstructApplication.java               | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java
index f2c279e7609..7259acb00c2 100644
--- a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java
+++ b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java
@@ -180,6 +180,7 @@ private void setupTransitRoutingServer() {
 
     if (OTPFeature.SandboxAPIGeocoder.isOn()) {
       LOG.info("Initializing geocoder");
+      // eagerly initialize the geocoder
       this.factory.luceneIndex();
     }
   }

From 9b17f9777cb21c395ce3ff890db49d8938c26c43 Mon Sep 17 00:00:00 2001
From: Leonard Ehrenfried 
Date: Thu, 20 Jun 2024 22:42:54 +0200
Subject: [PATCH 056/192] Remove trailing and leading whitespace

---
 src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java
index 6283a779088..8f11ff252e7 100644
--- a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java
+++ b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java
@@ -253,6 +253,7 @@ private Stream matchingDocuments(
     String searchTerms,
     boolean autocomplete
   ) {
+    searchTerms = searchTerms.strip();
     try {
       if (autocomplete) {
         var completionQuery = new FuzzyCompletionQuery(

From f95fd60bbd42ca15a4123fb3132fbd6f2a0620ae Mon Sep 17 00:00:00 2001
From: Leonard Ehrenfried 
Date: Fri, 21 Jun 2024 14:50:20 +0200
Subject: [PATCH 057/192] Add comment about unusal instantiation of
 DefaultTransitService

---
 .../java/org/opentripplanner/ext/geocoder/LuceneIndex.java   | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java
index 8f11ff252e7..fe7bef8ad13 100644
--- a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java
+++ b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java
@@ -68,6 +68,11 @@ public class LuceneIndex implements Serializable {
   private final SuggestIndexSearcher searcher;
   private final StopClusterMapper stopClusterMapper;
 
+  /**
+   * Since the {@link TransitService} is request scoped, we don't inject it into this class.
+   * However, we do need some methods in the service and that's why we instantiate it manually in this
+   * constructor.
+   */
   public LuceneIndex(TransitModel transitModel, StopConsolidationService stopConsolidationService) {
     this(new DefaultTransitService(transitModel), stopConsolidationService);
   }

From 6915d08a11625b87e7ebf681464a7743a59881d6 Mon Sep 17 00:00:00 2001
From: Thomas Gran 
Date: Thu, 27 Jun 2024 14:54:11 +0200
Subject: [PATCH 058/192] Apply suggestions from code review

Co-authored-by: Leonard Ehrenfried 
---
 .../filterchain/filters/system/SingeCriteriaComparator.java | 6 +++---
 .../filterchain/filters/system/mcmax/McMaxLimitFilter.java  | 6 +++---
 .../algorithm/filterchain/filters/system/mcmax/State.java   | 4 ++--
 .../filters/system/SingeCriteriaComparatorTest.java         | 4 ++--
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java
index 528542d40df..55fb040caf8 100644
--- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java
+++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java
@@ -6,11 +6,11 @@
 import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n;
 
 /**
- * Comparator used to compare a SINGE criteria for dominance. The difference between this and the
+ * Comparator used to compare a SINGLE criteria for dominance. The difference between this and the
  * {@link org.opentripplanner.raptor.util.paretoset.ParetoComparator} is that:
  * 
    *
  1. This applies to one criteria, not multiple.
  2. - *
  3. This interface apply to itineraries; It is not generic.
  4. + *
  5. This interface applies to itineraries; It is not generic.
  6. *
* A set of instances of this interface can be used to create a pareto-set. See * {@link org.opentripplanner.raptor.util.paretoset.ParetoSet} and @@ -22,7 +22,7 @@ @FunctionalInterface public interface SingeCriteriaComparator { /** - * The left criteria dominates the right criteria. Note! The right criteria my dominate + * The left criteria dominates the right criteria. Note! The right criteria may dominate * the left criteria if there is no {@link #strictOrder()}. If left and right are equals, then * there is no dominance. */ diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java index 180ef979085..687d5d0c17c 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java @@ -16,20 +16,20 @@ * to each criterion is kept. So, if the grouping is based on time and riding common trips, then * this filter will use the reminding criterion (transfers, generalized-cost, * [transit-group-priority]) to filter the grouped set of itineraries. DO NOT INCLUDE CRITERIA - * USED TO GROUP THE ITINERARIES, ONLY THE REMINDING CRITERION USED IN THE RAPTOR SEARCH. + * USED TO GROUP THE ITINERARIES, ONLY THE REMAINING CRITERION USED IN THE RAPTOR SEARCH. *

* IMPLEMENTATION DETAILS *

* This is not a trivial problem. In most cases, the best itinerary for a given criteria is unique, * but there might be ties - same number of transfers, same cost, and/or different priority groups. * In case of a tie, we will look if an itinerary is "best-in-group" for more than one criterion, - * if so we pick the one witch is best in the highest number of groups. Again, if there is a tie + * if so we pick the one which is best in the highest number of groups. Again, if there is a tie * (best in the same number of groups), then we fall back to the given itinerary sorting order. *

* This filter will use the order of the input itineraries to break ties. So, make sure to call the * appropriate sort function before this filter is invoked. *

- * Note! For a criteria like num-of-transfers or generalized-cost, there is only one set of "best" + * Note! For criteria like num-of-transfers or generalized-cost, there is only one set of "best" * itineraries, and usually there are only one or a few itineraries. In case there is more than one, * picking just one is fine. But, for transit-group-priority there might be more than one optimal * set of itineraries. For each set, we need to pick one itinerary for the final result. Each of diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java index d616c505446..06224ccfcab 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java @@ -46,7 +46,7 @@ void findAllSingleItemGroupsAndAddTheItemToTheResult() { /** * Find the items with the highest group count and the lowest index. Theoretically, there might be * a smaller set of itineraries that TOGETHER represent all groups than what we achieve here, but - * it is fare more complicated to compute - so this is probably good enough. + * it is far more complicated to compute - so this is probably good enough. */ void findTheBestItemsUntilAllGroupsAreRepresentedInTheResult() { while (!groups.isEmpty()) { @@ -88,7 +88,7 @@ private void removeGroupsWitchContainsItem(Item item) { } /** - * The best item is the one witch exists in most groups, and in case of a tie, the sort order/ + * The best item is the one which exists in most groups, and in case of a tie, the sort order/ * itinerary index is used. */ private static Item findBestItem(List groups) { diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java index 8f4b2b4ddeb..addf6dcd1d0 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java @@ -13,7 +13,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; import org.opentripplanner.transit.model._data.TransitModelForTest; -class SingeCriteriaComparatorTest { +class SingleCriteriaComparatorTest { private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); @@ -66,7 +66,7 @@ void compareNumTransfers() { // leftDominanceExist assertFalse(subject.leftDominanceExist(zeroTransferHighCost, zeroTransferLowCost)); - Assertions.assertTrue(subject.leftDominanceExist(zeroTransferLowCost, oneTransferLowCost)); + assertTrue(subject.leftDominanceExist(zeroTransferLowCost, oneTransferLowCost)); assertFalse(subject.leftDominanceExist(oneTransferLowCost, zeroTransferLowCost)); // strict order expected From c1dc4b324890abd0c4f5010e48de072abc7c461a Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 27 Jun 2024 14:57:49 +0200 Subject: [PATCH 059/192] refactor: Fix spelling SingleCriteriaComparator --- .../ItineraryListFilterChainBuilder.java | 10 +++++----- ...mparator.java => SingleCriteriaComparator.java} | 12 ++++++------ .../filters/system/mcmax/McMaxLimitFilter.java | 6 +++--- .../filterchain/filters/system/mcmax/State.java | 14 +++++++------- ...Test.java => SingleCriteriaComparatorTest.java} | 13 ++++++------- .../filters/system/mcmax/McMaxLimitFilterTest.java | 8 ++++---- 6 files changed, 31 insertions(+), 32 deletions(-) rename src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/{SingeCriteriaComparator.java => SingleCriteriaComparator.java} (85%) rename src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/{SingeCriteriaComparatorTest.java => SingleCriteriaComparatorTest.java} (89%) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java index 84f3030b09f..071814a7abf 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/ItineraryListFilterChainBuilder.java @@ -28,7 +28,7 @@ import org.opentripplanner.routing.algorithm.filterchain.filters.system.NumItinerariesFilter; import org.opentripplanner.routing.algorithm.filterchain.filters.system.OutsideSearchWindowFilter; import org.opentripplanner.routing.algorithm.filterchain.filters.system.PagingFilter; -import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingeCriteriaComparator; +import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingleCriteriaComparator; import org.opentripplanner.routing.algorithm.filterchain.filters.system.mcmax.McMaxLimitFilter; import org.opentripplanner.routing.algorithm.filterchain.filters.transit.DecorateTransitAlert; import org.opentripplanner.routing.algorithm.filterchain.filters.transit.KeepItinerariesWithFewestTransfers; @@ -635,11 +635,11 @@ private static void addDecorateFilter( private RemoveItineraryFlagger createMaxLimitFilter(String filterName, int maxLimit) { if (OTPFeature.MultiCriteriaGroupMaxFilter.isOn()) { - List comparators = new ArrayList<>(); - comparators.add(SingeCriteriaComparator.compareGeneralizedCost()); - comparators.add(SingeCriteriaComparator.compareNumTransfers()); + List comparators = new ArrayList<>(); + comparators.add(SingleCriteriaComparator.compareGeneralizedCost()); + comparators.add(SingleCriteriaComparator.compareNumTransfers()); if (transitGroupPriorityUsed) { - comparators.add(SingeCriteriaComparator.compareTransitGroupsPriority()); + comparators.add(SingleCriteriaComparator.compareTransitGroupsPriority()); } return new McMaxLimitFilter(filterName, maxLimit, comparators); } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparator.java similarity index 85% rename from src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java rename to src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparator.java index 55fb040caf8..ee814213157 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparator.java @@ -20,7 +20,7 @@ * can be sorted, if so the {@link #strictOrder()} should return false (this is the default). */ @FunctionalInterface -public interface SingeCriteriaComparator { +public interface SingleCriteriaComparator { /** * The left criteria dominates the right criteria. Note! The right criteria may dominate * the left criteria if there is no {@link #strictOrder()}. If left and right are equals, then @@ -35,16 +35,16 @@ default boolean strictOrder() { return false; } - static SingeCriteriaComparator compareNumTransfers() { + static SingleCriteriaComparator compareNumTransfers() { return compareLessThan(Itinerary::getNumberOfTransfers); } - static SingeCriteriaComparator compareGeneralizedCost() { + static SingleCriteriaComparator compareGeneralizedCost() { return compareLessThan(Itinerary::getGeneralizedCost); } @SuppressWarnings("OptionalGetWithoutIsPresent") - static SingeCriteriaComparator compareTransitGroupsPriority() { + static SingleCriteriaComparator compareTransitGroupsPriority() { return (left, right) -> TransitGroupPriority32n.dominate( left.getGeneralizedCost2().get(), @@ -52,8 +52,8 @@ static SingeCriteriaComparator compareTransitGroupsPriority() { ); } - static SingeCriteriaComparator compareLessThan(final ToIntFunction op) { - return new SingeCriteriaComparator() { + static SingleCriteriaComparator compareLessThan(final ToIntFunction op) { + return new SingleCriteriaComparator() { @Override public boolean leftDominanceExist(Itinerary left, Itinerary right) { return op.applyAsInt(left) < op.applyAsInt(right); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java index 687d5d0c17c..9ee35e0c656 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java @@ -3,7 +3,7 @@ import java.util.List; import java.util.function.Predicate; import org.opentripplanner.model.plan.Itinerary; -import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingeCriteriaComparator; +import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingleCriteriaComparator; import org.opentripplanner.routing.algorithm.filterchain.framework.spi.RemoveItineraryFlagger; /** @@ -70,12 +70,12 @@ public class McMaxLimitFilter implements RemoveItineraryFlagger { private final String name; private final int minNumItineraries; - private final List comparators; + private final List comparators; public McMaxLimitFilter( String name, int minNumItineraries, - List comparators + List comparators ) { this.name = name; this.minNumItineraries = minNumItineraries; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java index 06224ccfcab..93b8b1097c9 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/State.java @@ -5,7 +5,7 @@ import java.util.List; import javax.annotation.Nullable; import org.opentripplanner.model.plan.Itinerary; -import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingeCriteriaComparator; +import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingleCriteriaComparator; /** * Keep a list of items, groups and the result in progress. This is just a class for @@ -23,7 +23,7 @@ class State { * cost). There should be at least one itinerary from each group surviving the filtering process. * The same itinerary can exist in multiple groups. */ - State(List itineraries, List comparators) { + State(List itineraries, List comparators) { this.items = createListOfItems(itineraries); this.groups = createGroups(items, comparators); } @@ -126,10 +126,10 @@ private static ArrayList createListOfItems(List itineraries) { private static List createGroups( Collection items, - List comparators + List comparators ) { List groups = new ArrayList<>(); - for (SingeCriteriaComparator comparator : comparators) { + for (SingleCriteriaComparator comparator : comparators) { if (comparator.strictOrder()) { groups.add(createOrderedGroup(items, comparator)); } else { @@ -145,7 +145,7 @@ private static List createGroups( */ private static Group createOrderedGroup( Collection items, - SingeCriteriaComparator comparator + SingleCriteriaComparator comparator ) { Group group = null; for (Item item : items) { @@ -168,11 +168,11 @@ private static Group createOrderedGroup( * the given {@code comparator}. This method will create a group for each optimal value found in * the given set of items. * - * @see #createOrderedGroup(Collection, SingeCriteriaComparator) + * @see #createOrderedGroup(Collection, SingleCriteriaComparator) */ private static Collection createUnorderedGroups( Collection items, - SingeCriteriaComparator comparator + SingleCriteriaComparator comparator ) { List result = new ArrayList<>(); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparatorTest.java similarity index 89% rename from src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java rename to src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparatorTest.java index addf6dcd1d0..2609fc931b5 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingeCriteriaComparatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparatorTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.opentripplanner.model.plan.Itinerary; @@ -55,14 +54,14 @@ static void setUp() { @Test void strictOrder() { - assertTrue(SingeCriteriaComparator.compareNumTransfers().strictOrder()); - assertTrue(SingeCriteriaComparator.compareGeneralizedCost().strictOrder()); - assertFalse(SingeCriteriaComparator.compareTransitGroupsPriority().strictOrder()); + assertTrue(SingleCriteriaComparator.compareNumTransfers().strictOrder()); + assertTrue(SingleCriteriaComparator.compareGeneralizedCost().strictOrder()); + assertFalse(SingleCriteriaComparator.compareTransitGroupsPriority().strictOrder()); } @Test void compareNumTransfers() { - var subject = SingeCriteriaComparator.compareNumTransfers(); + var subject = SingleCriteriaComparator.compareNumTransfers(); // leftDominanceExist assertFalse(subject.leftDominanceExist(zeroTransferHighCost, zeroTransferLowCost)); @@ -75,7 +74,7 @@ void compareNumTransfers() { @Test void compareGeneralizedCost() { - var subject = SingeCriteriaComparator.compareGeneralizedCost(); + var subject = SingleCriteriaComparator.compareGeneralizedCost(); System.out.println(zeroTransferLowCost.getGeneralizedCost()); System.out.println(zeroTransferHighCost.getGeneralizedCost()); @@ -99,7 +98,7 @@ void compareTransitPriorityGroups() { .withGeneralizedCost2(TransitGroupPriority32n.mergeInGroupId(1, 2)) .build(); - var subject = SingeCriteriaComparator.compareTransitGroupsPriority(); + var subject = SingleCriteriaComparator.compareTransitGroupsPriority(); assertTrue(subject.leftDominanceExist(group1, group2)); assertTrue(subject.leftDominanceExist(group2, group1)); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java index 3c35b0172f8..ca7a1bb5336 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilterTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Place; -import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingeCriteriaComparator; +import org.opentripplanner.routing.algorithm.filterchain.filters.system.SingleCriteriaComparator; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; import org.opentripplanner.transit.model._data.TransitModelForTest; @@ -48,9 +48,9 @@ class McMaxLimitFilterTest { "test", 2, List.of( - SingeCriteriaComparator.compareGeneralizedCost(), - SingeCriteriaComparator.compareNumTransfers(), - SingeCriteriaComparator.compareTransitGroupsPriority() + SingleCriteriaComparator.compareGeneralizedCost(), + SingleCriteriaComparator.compareNumTransfers(), + SingleCriteriaComparator.compareTransitGroupsPriority() ) ); From dd4bf1bb11ebd7df3940d3ee07100c1205b2809e Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 27 Jun 2024 14:59:07 +0200 Subject: [PATCH 060/192] Apply suggestions from code review Co-authored-by: Leonard Ehrenfried --- .../filterchain/filters/system/mcmax/McMaxLimitFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java index 9ee35e0c656..c0b07e7400a 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/mcmax/McMaxLimitFilter.java @@ -14,7 +14,7 @@ * The main usage of this filter is to combine it with a transit grouping filter and for each group * make sure there is at least {@code minNumItineraries} and that the best itinerary with respect * to each criterion is kept. So, if the grouping is based on time and riding common trips, then - * this filter will use the reminding criterion (transfers, generalized-cost, + * this filter will use the remaining criterion (transfers, generalized-cost, * [transit-group-priority]) to filter the grouped set of itineraries. DO NOT INCLUDE CRITERIA * USED TO GROUP THE ITINERARIES, ONLY THE REMAINING CRITERION USED IN THE RAPTOR SEARCH. *

From 9b4b7fe884f05ec965e31aaef7124a1358e2f6d8 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 27 Jun 2024 15:03:13 +0200 Subject: [PATCH 061/192] refactor: Remove System.out --- .../opentripplanner/datastore/file/ZipFileDataSourceTest.java | 1 - .../datastore/file/ZipStreamDataSourceDecoratorTest.java | 1 - .../filters/system/SingleCriteriaComparatorTest.java | 4 ---- 3 files changed, 6 deletions(-) diff --git a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java index da46fc430b6..b5b8f797eb3 100644 --- a/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java +++ b/src/test/java/org/opentripplanner/datastore/file/ZipFileDataSourceTest.java @@ -74,7 +74,6 @@ public void testIO() throws IOException { Collection content = subject.content(); Collection names = content.stream().map(DataSource::name).toList(); - //System.out.println(names); assertTrue( names.containsAll(List.of("agency.txt", "stops.txt", "trips.txt")), names.toString() diff --git a/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java b/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java index 09479d85478..21d4bc6bed0 100644 --- a/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java +++ b/src/test/java/org/opentripplanner/datastore/file/ZipStreamDataSourceDecoratorTest.java @@ -100,7 +100,6 @@ void testIO() throws IOException { Collection content = subject.content(); Collection names = content.stream().map(DataSource::name).toList(); - System.out.println(names); assertTrue(names.containsAll(EXPECTED_ZIP_ENTRIES), names.toString()); DataSource entry = subject.entry("agency.txt"); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparatorTest.java index 2609fc931b5..ebcf441370f 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/system/SingleCriteriaComparatorTest.java @@ -76,10 +76,6 @@ void compareNumTransfers() { void compareGeneralizedCost() { var subject = SingleCriteriaComparator.compareGeneralizedCost(); - System.out.println(zeroTransferLowCost.getGeneralizedCost()); - System.out.println(zeroTransferHighCost.getGeneralizedCost()); - System.out.println(oneTransferLowCost.getGeneralizedCost()); - // leftDominanceExist assertFalse(subject.leftDominanceExist(zeroTransferHighCost, zeroTransferLowCost)); assertTrue(subject.leftDominanceExist(zeroTransferLowCost, zeroTransferHighCost)); From 13d55c22999ff71795065e754adff9efec21efd8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 1 Jul 2024 13:55:51 +0200 Subject: [PATCH 062/192] Rename ServiceDateFilterInput to LocalDateRangeInput --- .../apis/gtfs/GraphQLUtils.java | 8 +- .../gtfs/PatternByServiceDatesFilter.java | 4 +- .../apis/gtfs/generated/GraphQLTypes.java | 75 +++++++++---------- .../opentripplanner/apis/gtfs/schema.graphqls | 12 +-- .../framework/graphql/GraphQLUtilsTest.java | 10 +-- 5 files changed, 51 insertions(+), 58 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java index 9aa1235879c..1fe09b60787 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java @@ -115,12 +115,10 @@ public static boolean startsWith(I18NString str, String name, Locale locale) { * Checks if a service date filter input has at least one filter set. If both start and end are * null then no filtering is necessary and this method returns null. */ - public static boolean hasServiceDateFilter( - GraphQLTypes.GraphQLServiceDateFilterInput serviceDays - ) { + public static boolean hasServiceDateFilter(GraphQLTypes.GraphQLLocalDateRangeInput dateRange) { return ( - serviceDays != null && - (serviceDays.getGraphQLStart() != null || serviceDays.getGraphQLEnd() != null) + dateRange != null && + (dateRange.getGraphQLStart() != null || dateRange.getGraphQLEnd() != null) ); } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index 9184bd9d728..2aef232abc1 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -6,7 +6,7 @@ import java.util.function.Function; import java.util.stream.Stream; import javax.annotation.Nullable; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLServiceDateFilterInput; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.Trip; @@ -53,7 +53,7 @@ public class PatternByServiceDatesFilter { } public PatternByServiceDatesFilter( - GraphQLServiceDateFilterInput filterInput, + GraphQLTypes.GraphQLLocalDateRangeInput filterInput, TransitService transitService ) { this( 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 9f6a237f7d9..01c40b5b239 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/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; @@ -1269,6 +1268,35 @@ public void setGraphQLOriginModesWithParentStation( } } + public static class GraphQLLocalDateRangeInput { + + private java.time.LocalDate end; + private java.time.LocalDate start; + + public GraphQLLocalDateRangeInput(Map args) { + if (args != null) { + this.end = (java.time.LocalDate) args.get("end"); + this.start = (java.time.LocalDate) args.get("start"); + } + } + + public java.time.LocalDate getGraphQLEnd() { + return this.end; + } + + public java.time.LocalDate getGraphQLStart() { + return this.start; + } + + public void setGraphQLEnd(java.time.LocalDate end) { + this.end = end; + } + + public void setGraphQLStart(java.time.LocalDate start) { + this.start = start; + } + } + /** Identifies whether this stop represents a stop or station. */ public enum GraphQLLocationType { ENTRANCE, @@ -3450,7 +3478,7 @@ public static class GraphQLQueryTypeRoutesArgs { private List feeds; private List ids; private String name; - private GraphQLServiceDateFilterInput serviceDates; + private GraphQLLocalDateRangeInput serviceDates; private List transportModes; public GraphQLQueryTypeRoutesArgs(Map args) { @@ -3459,7 +3487,7 @@ public GraphQLQueryTypeRoutesArgs(Map args) { this.ids = (List) args.get("ids"); this.name = (String) args.get("name"); this.serviceDates = - new GraphQLServiceDateFilterInput((Map) args.get("serviceDates")); + new GraphQLLocalDateRangeInput((Map) args.get("serviceDates")); if (args.get("transportModes") != null) { this.transportModes = ((List) args.get("transportModes")).stream() @@ -3482,7 +3510,7 @@ public String getGraphQLName() { return this.name; } - public GraphQLServiceDateFilterInput getGraphQLServiceDates() { + public GraphQLLocalDateRangeInput getGraphQLServiceDates() { return this.serviceDates; } @@ -3502,7 +3530,7 @@ public void setGraphQLName(String name) { this.name = name; } - public void setGraphQLServiceDates(GraphQLServiceDateFilterInput serviceDates) { + public void setGraphQLServiceDates(GraphQLLocalDateRangeInput serviceDates) { this.serviceDates = serviceDates; } @@ -3947,20 +3975,20 @@ public void setGraphQLLanguage(String language) { public static class GraphQLRoutePatternsArgs { - private GraphQLServiceDateFilterInput serviceDates; + private GraphQLLocalDateRangeInput serviceDates; public GraphQLRoutePatternsArgs(Map args) { if (args != null) { this.serviceDates = - new GraphQLServiceDateFilterInput((Map) args.get("serviceDates")); + new GraphQLLocalDateRangeInput((Map) args.get("serviceDates")); } } - public GraphQLServiceDateFilterInput getGraphQLServiceDates() { + public GraphQLLocalDateRangeInput getGraphQLServiceDates() { return this.serviceDates; } - public void setGraphQLServiceDates(GraphQLServiceDateFilterInput serviceDates) { + public void setGraphQLServiceDates(GraphQLLocalDateRangeInput serviceDates) { this.serviceDates = serviceDates; } } @@ -4126,35 +4154,6 @@ public void setGraphQLDestinationScooterPolicy( } } - public static class GraphQLServiceDateFilterInput { - - private java.time.LocalDate end; - private java.time.LocalDate start; - - public GraphQLServiceDateFilterInput(Map args) { - if (args != null) { - this.end = (java.time.LocalDate) args.get("end"); - this.start = (java.time.LocalDate) args.get("start"); - } - } - - public java.time.LocalDate getGraphQLEnd() { - return this.end; - } - - public java.time.LocalDate getGraphQLStart() { - return this.start; - } - - public void setGraphQLEnd(java.time.LocalDate end) { - this.end = end; - } - - public void setGraphQLStart(java.time.LocalDate start) { - this.start = start; - } - } - public static class GraphQLStopAlertsArgs { private List types; diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index cbedf2a369f..26ee6bdf4db 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -3439,13 +3439,9 @@ enum Qualifier { """ -Filters entity by their service dates. - -**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. +Filters an entity by a date range. """ -input ServiceDateFilterInput { +input LocalDateRangeInput { """ **Inclusive** start date of the filter. If `null` this means that no `start` filter is applied and all dates that are before `end` are selected. @@ -3627,7 +3623,7 @@ type QueryType { 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. """ - serviceDates: ServiceDateFilterInput + serviceDates: LocalDateRangeInput ): [Route] """ @@ -4525,7 +4521,7 @@ type Route implements Node { 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. """ - serviceDates: ServiceDateFilterInput + serviceDates: LocalDateRangeInput ): [Pattern] """List of stops on this route""" diff --git a/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java b/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java index 8119b9256c8..42330652410 100644 --- a/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java +++ b/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java @@ -133,16 +133,16 @@ void testGetLocaleWithLocalContextLocale() { assertEquals(frenchLocale, locale); } - public static List noFilterCases() { - var list = new ArrayList(); + public static List noFilterCases() { + var list = new ArrayList(); list.add(null); - list.add(new GraphQLTypes.GraphQLServiceDateFilterInput(Map.of())); + list.add(new GraphQLTypes.GraphQLLocalDateRangeInput(Map.of())); return list; } @ParameterizedTest @MethodSource("noFilterCases") - void hasNoServiceDateFilter(GraphQLTypes.GraphQLServiceDateFilterInput input) { + void hasNoServiceDateFilter(GraphQLTypes.GraphQLLocalDateRangeInput input) { assertFalse(org.opentripplanner.apis.gtfs.GraphQLUtils.hasServiceDateFilter(input)); } @@ -153,7 +153,7 @@ public static List> hasFilterCases() { @ParameterizedTest @MethodSource("hasFilterCases") void hasServiceDateFilter(Map params) { - var input = new GraphQLTypes.GraphQLServiceDateFilterInput(params); + var input = new GraphQLTypes.GraphQLLocalDateRangeInput(params); assertTrue(org.opentripplanner.apis.gtfs.GraphQLUtils.hasServiceDateFilter(input)); } } From 0edc190f22bb03c9d97044a2c8147ed691195b37 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 1 Jul 2024 17:36:45 +0200 Subject: [PATCH 063/192] Move logic into mapper, extract separate class for date range --- .../apis/gtfs/GraphQLUtils.java | 12 ------ .../gtfs/PatternByServiceDatesFilter.java | 28 +++++-------- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 3 +- .../apis/gtfs/datafetchers/RouteImpl.java | 3 +- .../gtfs/mapping/LocalDateRangeMapper.java | 18 ++++++++ .../apis/gtfs/model/LocalDateRange.java | 23 +++++++++++ .../gtfs/PatternByServiceDatesFilterTest.java | 17 ++++++-- .../mapping/LocalDateRangeMapperTest.java | 41 +++++++++++++++++++ .../apis/gtfs/model/LocalDateRangeTest.java | 22 ++++++++++ .../framework/graphql/GraphQLUtilsTest.java | 34 --------------- 10 files changed, 132 insertions(+), 69 deletions(-) create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapper.java create mode 100644 src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java create mode 100644 src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java create mode 100644 src/test/java/org/opentripplanner/apis/gtfs/model/LocalDateRangeTest.java diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java index 1fe09b60787..3fb339daa32 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLUtils.java @@ -2,7 +2,6 @@ import java.time.Instant; import java.util.Locale; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFilterPlaceType; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLFormFactor; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; @@ -110,15 +109,4 @@ 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); } - - /** - * Checks if a service date filter input has at least one filter set. If both start and end are - * null then no filtering is necessary and this method returns null. - */ - public static boolean hasServiceDateFilter(GraphQLTypes.GraphQLLocalDateRangeInput dateRange) { - return ( - dateRange != null && - (dateRange.getGraphQLStart() != null || dateRange.getGraphQLEnd() != null) - ); - } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index 2aef232abc1..82d67cae7f3 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -5,8 +5,8 @@ import java.util.Objects; import java.util.function.Function; import java.util.stream.Stream; -import javax.annotation.Nullable; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.model.LocalDateRange; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.Trip; @@ -21,33 +21,26 @@ */ public class PatternByServiceDatesFilter { - private final LocalDate startInclusive; - private final LocalDate endExclusive; private final Function> getPatternsForRoute; private final Function> getServiceDatesForTrip; + private final LocalDateRange range; /** * This method is not private to enable unit testing. *

- * See the API documentation for a discussion of {@code startInclusive} and {@code endExclusive}. */ PatternByServiceDatesFilter( - @Nullable LocalDate startInclusive, - @Nullable LocalDate endExclusive, + LocalDateRange range, Function> getPatternsForRoute, Function> getServiceDatesForTrip ) { this.getPatternsForRoute = Objects.requireNonNull(getPatternsForRoute); this.getServiceDatesForTrip = Objects.requireNonNull(getServiceDatesForTrip); - // optional, but one must be defined - this.startInclusive = startInclusive; - this.endExclusive = endExclusive; + this.range = range; - if (startInclusive == null && endExclusive == null) { + if (range.unlimited()) { throw new IllegalArgumentException("startInclusive and endExclusive cannot be both null"); - } else if ( - startInclusive != null && endExclusive != null && startInclusive.isAfter(endExclusive) - ) { + } else if (range.startBeforeEnd()) { throw new IllegalArgumentException("start must be before end"); } } @@ -57,8 +50,7 @@ public PatternByServiceDatesFilter( TransitService transitService ) { this( - filterInput.getGraphQLStart(), - filterInput.getGraphQLEnd(), + new LocalDateRange(filterInput.getGraphQLStart(), filterInput.getGraphQLEnd()), transitService::getPatternsForRoute, trip -> transitService.getCalendarService().getServiceDatesForServiceId(trip.getServiceId()) ); @@ -94,9 +86,11 @@ private boolean hasServicesOnDate(TripPattern pattern) { .stream() .anyMatch(date -> ( - startInclusive == null || date.isEqual(startInclusive) || date.isAfter(startInclusive) + range.startInclusive() == null || + date.isEqual(range.startInclusive()) || + date.isAfter(range.startInclusive()) ) && - (endExclusive == null || date.isBefore(endExclusive)) + (range.endExclusive() == null || date.isBefore(range.endExclusive())) ); }); } 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 c83669a386b..b22310d1da3 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -31,6 +31,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLQueryTypeStopsByRadiusArgs; +import org.opentripplanner.apis.gtfs.mapping.LocalDateRangeMapper; import org.opentripplanner.apis.gtfs.mapping.routerequest.LegacyRouteRequestMapper; import org.opentripplanner.apis.gtfs.mapping.routerequest.RouteRequestMapper; import org.opentripplanner.ext.fares.impl.DefaultFareService; @@ -611,7 +612,7 @@ public DataFetcher> routes() { ); } - if (GraphQLUtils.hasServiceDateFilter(args.getGraphQLServiceDates())) { + if (LocalDateRangeMapper.hasServiceDateFilter(args.getGraphQLServiceDates())) { var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService); routeStream = filter.filterRoutes(routeStream).stream(); } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index 5eeb662a6d1..921f85ff813 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -15,6 +15,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; import org.opentripplanner.apis.gtfs.mapping.BikesAllowedMapper; +import org.opentripplanner.apis.gtfs.mapping.LocalDateRangeMapper; import org.opentripplanner.routing.alertpatch.EntitySelector; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.services.TransitAlertService; @@ -181,7 +182,7 @@ public DataFetcher> patterns() { var args = new GraphQLTypes.GraphQLRoutePatternsArgs(environment.getArguments()); - if (GraphQLUtils.hasServiceDateFilter(args.getGraphQLServiceDates())) { + if (LocalDateRangeMapper.hasServiceDateFilter(args.getGraphQLServiceDates())) { var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService); return filter.filterPatterns(patterns); } else { diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapper.java new file mode 100644 index 00000000000..56064ea37dc --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapper.java @@ -0,0 +1,18 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.model.LocalDateRange; + +public class LocalDateRangeMapper { + + /** + * Checks if a service date filter input has at least one filter set. If both start and end are + * null then no filtering is necessary and this method returns null. + */ + public static boolean hasServiceDateFilter(GraphQLTypes.GraphQLLocalDateRangeInput dateRange) { + return ( + dateRange != null && + !new LocalDateRange(dateRange.getGraphQLStart(), dateRange.getGraphQLEnd()).unlimited() + ); + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java b/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java new file mode 100644 index 00000000000..83e0c93b5bd --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java @@ -0,0 +1,23 @@ +package org.opentripplanner.apis.gtfs.model; + +import java.time.LocalDate; +import javax.annotation.Nullable; + +/** + * See the API documentation for a discussion of {@code startInclusive} and {@code endExclusive}. + */ +public record LocalDateRange(@Nullable LocalDate startInclusive, @Nullable LocalDate endExclusive) { + /** + * Does it actually define a limit or is the range unlimited? + */ + public boolean unlimited() { + return startInclusive == null && endExclusive == null; + } + + /** + * Is the start date before the end ( + */ + public boolean startBeforeEnd() { + return startInclusive != null && endExclusive != null && startInclusive.isAfter(endExclusive); + } +} diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index 01743dec89d..57f399ad821 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -13,6 +13,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.apis.gtfs.model.LocalDateRange; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; @@ -71,7 +72,12 @@ static List invalidRangeCases() { void invalidRange(LocalDate start, LocalDate end) { assertThrows( IllegalArgumentException.class, - () -> new PatternByServiceDatesFilter(start, end, r -> List.of(), d -> List.of()) + () -> + new PatternByServiceDatesFilter( + new LocalDateRange(start, end), + r -> List.of(), + d -> List.of() + ) ); } @@ -88,7 +94,11 @@ static List validRangeCases() { @MethodSource("validRangeCases") void validRange(LocalDate start, LocalDate end) { assertDoesNotThrow(() -> - new PatternByServiceDatesFilter(start, end, r -> List.of(), d -> List.of()) + new PatternByServiceDatesFilter( + new LocalDateRange(start, end), + r -> List.of(), + d -> List.of() + ) ); } @@ -141,8 +151,7 @@ void filterRoutes(LocalDate start, LocalDate end, FilterExpectation expectation) private static PatternByServiceDatesFilter mockFilter(LocalDate start, LocalDate end) { return new PatternByServiceDatesFilter( - start, - end, + new LocalDateRange(start, end), route -> List.of(PATTERN_1), trip -> List.of(parse("2024-05-01"), parse("2024-06-01")) ); diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java new file mode 100644 index 00000000000..eba38c0a180 --- /dev/null +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java @@ -0,0 +1,41 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; + +class LocalDateRangeMapperTest { + + private static final LocalDate DATE = LocalDate.parse("2024-05-27"); + + public static List noFilterCases() { + var list = new ArrayList(); + list.add(null); + list.add(new GraphQLTypes.GraphQLLocalDateRangeInput(Map.of())); + return list; + } + + @ParameterizedTest + @MethodSource("noFilterCases") + void hasNoServiceDateFilter(GraphQLTypes.GraphQLLocalDateRangeInput input) { + assertFalse(LocalDateRangeMapper.hasServiceDateFilter(input)); + } + + public static List> hasFilterCases() { + return List.of(Map.of("start", DATE), Map.of("end", DATE), Map.of("start", DATE, "end", DATE)); + } + + @ParameterizedTest + @MethodSource("hasFilterCases") + void hasServiceDateFilter(Map params) { + var input = new GraphQLTypes.GraphQLLocalDateRangeInput(params); + assertTrue(LocalDateRangeMapper.hasServiceDateFilter(input)); + } +} diff --git a/src/test/java/org/opentripplanner/apis/gtfs/model/LocalDateRangeTest.java b/src/test/java/org/opentripplanner/apis/gtfs/model/LocalDateRangeTest.java new file mode 100644 index 00000000000..e675a4c8772 --- /dev/null +++ b/src/test/java/org/opentripplanner/apis/gtfs/model/LocalDateRangeTest.java @@ -0,0 +1,22 @@ +package org.opentripplanner.apis.gtfs.model; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.time.LocalDate; +import org.junit.jupiter.api.Test; + +class LocalDateRangeTest { + + private static final LocalDate DATE = LocalDate.parse("2024-06-01"); + + @Test + void unlimited() { + assertFalse(new LocalDateRange(DATE, DATE).unlimited()); + } + + @Test + void limited() { + assertFalse(new LocalDateRange(DATE, null).unlimited()); + assertFalse(new LocalDateRange(null, DATE).unlimited()); + } +} diff --git a/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java b/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java index 42330652410..b72cb6e5a0d 100644 --- a/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java +++ b/src/test/java/org/opentripplanner/framework/graphql/GraphQLUtilsTest.java @@ -3,29 +3,19 @@ import static graphql.execution.ExecutionContextBuilder.newExecutionContextBuilder; import static java.util.Map.entry; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import graphql.ExecutionInput; import graphql.execution.ExecutionContext; import graphql.execution.ExecutionId; import graphql.schema.DataFetchingEnvironmentImpl; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; import java.util.Locale; import java.util.Map; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.framework.i18n.TranslatedString; class GraphQLUtilsTest { - private static final LocalDate DATE = LocalDate.parse("2024-05-27"); - static final ExecutionContext executionContext; static { @@ -132,28 +122,4 @@ void testGetLocaleWithLocalContextLocale() { assertEquals(frenchLocale, locale); } - - public static List noFilterCases() { - var list = new ArrayList(); - list.add(null); - list.add(new GraphQLTypes.GraphQLLocalDateRangeInput(Map.of())); - return list; - } - - @ParameterizedTest - @MethodSource("noFilterCases") - void hasNoServiceDateFilter(GraphQLTypes.GraphQLLocalDateRangeInput input) { - assertFalse(org.opentripplanner.apis.gtfs.GraphQLUtils.hasServiceDateFilter(input)); - } - - public static List> hasFilterCases() { - return List.of(Map.of("start", DATE), Map.of("end", DATE), Map.of("start", DATE, "end", DATE)); - } - - @ParameterizedTest - @MethodSource("hasFilterCases") - void hasServiceDateFilter(Map params) { - var input = new GraphQLTypes.GraphQLLocalDateRangeInput(params); - assertTrue(org.opentripplanner.apis.gtfs.GraphQLUtils.hasServiceDateFilter(input)); - } } From 85d513a1ed29e3bba87acd11f26657b20bbeef83 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 1 Jul 2024 17:59:18 +0200 Subject: [PATCH 064/192] Move assertion class to another package --- .../ext/siri/SiriTimetableSnapshotSourceTest.java | 2 +- .../{test/support => updater/spi}/UpdateResultAssertions.java | 4 +--- .../updater/trip/moduletests/addition/AddedTest.java | 2 +- .../moduletests/cancellation/CancellationDeletionTest.java | 2 +- .../updater/trip/moduletests/delay/DelayedTest.java | 2 +- .../updater/trip/moduletests/delay/SkippedTest.java | 2 +- .../updater/trip/moduletests/rejection/InvalidInputTest.java | 2 +- .../updater/trip/moduletests/rejection/InvalidTripIdTest.java | 2 +- 8 files changed, 8 insertions(+), 10 deletions(-) rename src/test/java/org/opentripplanner/{test/support => updater/spi}/UpdateResultAssertions.java (82%) diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java index 73f6b92ffe7..3c5d3acf1cb 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java @@ -1,7 +1,7 @@ package org.opentripplanner.ext.siri; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.test.support.UpdateResultAssertions.assertFailure; +import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertFailure; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java b/src/test/java/org/opentripplanner/updater/spi/UpdateResultAssertions.java similarity index 82% rename from src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java rename to src/test/java/org/opentripplanner/updater/spi/UpdateResultAssertions.java index c6be0406ae3..13806cde4c3 100644 --- a/src/test/java/org/opentripplanner/test/support/UpdateResultAssertions.java +++ b/src/test/java/org/opentripplanner/updater/spi/UpdateResultAssertions.java @@ -1,11 +1,9 @@ -package org.opentripplanner.test.support; +package org.opentripplanner.updater.spi; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Set; -import org.opentripplanner.updater.spi.UpdateError; -import org.opentripplanner.updater.spi.UpdateResult; public class UpdateResultAssertions { diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 1e75f558510..7f5d976e737 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -6,7 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.test.support.UpdateResultAssertions.assertSuccess; +import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_A1_ID; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_B1_ID; diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java index 3b527b64e5d..c85225b7828 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.test.support.UpdateResultAssertions.assertSuccess; +import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; import static org.opentripplanner.updater.trip.UpdateIncrementality.DIFFERENTIAL; import com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship; diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java index d9282e1b155..53805939e40 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java @@ -5,7 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.test.support.UpdateResultAssertions.assertSuccess; +import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index 349e472036d..c17130fbda6 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -6,7 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.test.support.UpdateResultAssertions.assertSuccess; +import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import static org.opentripplanner.updater.trip.UpdateIncrementality.DIFFERENTIAL; diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java index 4815d7180c4..da362451753 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java @@ -2,8 +2,8 @@ import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.opentripplanner.test.support.UpdateResultAssertions.assertFailure; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NO_SERVICE_ON_DATE; +import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertFailure; import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import java.time.LocalDate; diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java index e6e7a4737f7..83c2547dbc7 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java @@ -1,8 +1,8 @@ package org.opentripplanner.updater.trip.moduletests.rejection; import static com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED; -import static org.opentripplanner.test.support.UpdateResultAssertions.assertFailure; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.INVALID_INPUT_STRUCTURE; +import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertFailure; import com.google.transit.realtime.GtfsRealtime; import java.util.stream.Stream; From 8c3364e9830682b8ab8dab1bf321fe57933cb90e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 1 Jul 2024 18:09:04 +0200 Subject: [PATCH 065/192] Rename Stop to StopCall --- .../updater/trip/RealtimeTestEnvironment.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index e9aa783833f..57aba0b19bb 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -114,12 +114,20 @@ private RealtimeTestEnvironment(SourceType sourceType) { Route route1 = TransitModelForTest.route(route1Id).build(); trip1 = - createTrip("TestTrip1", route1, List.of(new Stop(stopA1, 10, 11), new Stop(stopB1, 20, 21))); + createTrip( + "TestTrip1", + route1, + List.of(new StopCall(stopA1, 10, 11), new StopCall(stopB1, 20, 21)) + ); trip2 = createTrip( "TestTrip2", route1, - List.of(new Stop(stopA1, 60, 61), new Stop(stopB1, 70, 71), new Stop(stopC1, 80, 81)) + List.of( + new StopCall(stopA1, 60, 61), + new StopCall(stopB1, 70, 71), + new StopCall(stopC1, 80, 81) + ) ); CalendarServiceData calendarServiceData = new CalendarServiceData(); @@ -295,7 +303,7 @@ private UpdateResult applyEstimatedTimetable( return getEstimatedTimetableHandler(fuzzyMatching).applyUpdate(updates, DIFFERENTIAL); } - private Trip createTrip(String id, Route route, List stops) { + private Trip createTrip(String id, Route route, List stops) { var trip = Trip .of(id(id)) .withRoute(route) @@ -323,7 +331,7 @@ private Trip createTrip(String id, Route route, List stops) { final TripPattern pattern = TransitModelForTest .tripPattern(id + "Pattern", route) - .withStopPattern(TransitModelForTest.stopPattern(stops.stream().map(Stop::stop).toList())) + .withStopPattern(TransitModelForTest.stopPattern(stops.stream().map(StopCall::stop).toList())) .build(); pattern.add(tripTimes); @@ -348,5 +356,5 @@ private StopTime createStopTime( return st; } - protected record Stop(RegularStop stop, int arrivalTime, int departureTime) {} + protected record StopCall(RegularStop stop, int arrivalTime, int departureTime) {} } From 1301e96ac7224728a7be8ff36cfdaeaa2cb3b57d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 2 Jul 2024 11:41:03 +0200 Subject: [PATCH 066/192] Make assertion return void --- .../updater/spi/UpdateResultAssertions.java | 3 +-- .../updater/trip/moduletests/addition/AddedTest.java | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/opentripplanner/updater/spi/UpdateResultAssertions.java b/src/test/java/org/opentripplanner/updater/spi/UpdateResultAssertions.java index 13806cde4c3..38468744886 100644 --- a/src/test/java/org/opentripplanner/updater/spi/UpdateResultAssertions.java +++ b/src/test/java/org/opentripplanner/updater/spi/UpdateResultAssertions.java @@ -11,7 +11,7 @@ public static void assertFailure(UpdateError.UpdateErrorType expectedError, Upda assertEquals(Set.of(expectedError), result.failures().keySet()); } - public static UpdateResult assertSuccess(UpdateResult updateResult) { + public static void assertSuccess(UpdateResult updateResult) { var errorCodes = updateResult.failures().keySet(); assertEquals( Set.of(), @@ -19,6 +19,5 @@ public static UpdateResult assertSuccess(UpdateResult updateResult) { "Update result should have no error codes but had %s".formatted(errorCodes) ); assertTrue(updateResult.successful() > 0); - return updateResult; } } diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 7f5d976e737..8d3984cc546 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -51,8 +51,8 @@ void addedTripWithNewRoute() { .addStopTime(STOP_B1_ID, 55, DropOffPickupType.NONE) .build(); - var result = assertSuccess(env.applyTripUpdate(tripUpdate)); - + var result = env.applyTripUpdate(tripUpdate); + assertSuccess(result); assertTrue(result.warnings().isEmpty()); var pattern = assertAddedTrip(ADDED_TRIP_ID, env); @@ -83,7 +83,8 @@ void addedWithUnknownStop() { .addStopTime(STOP_C1_ID, 55, DropOffPickupType.NONE) .build(); - var result = assertSuccess(env.applyTripUpdate(tripUpdate)); + var result = env.applyTripUpdate(tripUpdate); + assertSuccess(result); assertEquals( List.of(UpdateSuccess.WarningType.UNKNOWN_STOPS_REMOVED_FROM_ADDED_TRIP), From a83a9e820481df8bda3f37e799aa30626aa971f6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 2 Jul 2024 16:20:33 +0200 Subject: [PATCH 067/192] Update src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java Co-authored-by: Joel Lappalainen --- .../opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index 82d67cae7f3..e7dfa1bcf07 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -14,7 +14,7 @@ /** * Encapsulates the logic to filter patterns by the service dates that they operate on. It also - * has a method to filter routes by checking if their patterns operate on the required days + * has a method to filter routes by checking if their patterns operate on the required days. *

* Once a more complete filtering engine is in place in the core data model, this code should be * there rather than a separate class in the API package. From b88a35e69b1b576ac2930fd511f1a6298dc2dfb0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 2 Jul 2024 16:21:10 +0200 Subject: [PATCH 068/192] Update error message --- .../opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index e7dfa1bcf07..1f2ef45dfd5 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -39,7 +39,7 @@ public class PatternByServiceDatesFilter { this.range = range; if (range.unlimited()) { - throw new IllegalArgumentException("startInclusive and endExclusive cannot be both null"); + throw new IllegalArgumentException("start and end cannot be both null"); } else if (range.startBeforeEnd()) { throw new IllegalArgumentException("start must be before end"); } From 3d138afb0404b84a694128d4e9470071f910bcf8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 3 Jul 2024 17:29:55 +0200 Subject: [PATCH 069/192] Make record private --- .../opentripplanner/updater/trip/RealtimeTestEnvironment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index 57aba0b19bb..34923fed9e4 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -356,5 +356,5 @@ private StopTime createStopTime( return st; } - protected record StopCall(RegularStop stop, int arrivalTime, int departureTime) {} + private record StopCall(RegularStop stop, int arrivalTime, int departureTime) {} } From 2e22b5e28b14d8cfe510a289ab812b26a3e13488 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 3 Jul 2024 17:37:56 +0200 Subject: [PATCH 070/192] Clean up after merge --- .../opentripplanner/apis/gtfs/schema.graphqls | 110 ++++++++++++------ .../apis/gtfs/queries/routes-extended.graphql | 30 ++--- 2 files changed, 91 insertions(+), 49 deletions(-) diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index eb3c631ec3c..0fcb76a211a 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -469,18 +469,18 @@ type FareProductUse { **Illustration** ```yaml itinerary: - leg1: - fareProducts: - id: "AAA" // id of a FareProductUse instance - product: - id: "day-pass" // product id - name: "Day Pass" - leg2: - fareProducts: - id: "AAA" // identical to leg1. the passenger needs to buy ONE pass, not two. - product: - id: "day-pass" // product id - name: "Day Pass" + leg1: + fareProducts: + id: "AAA" // id of a FareProductUse instance + product: + id: "day-pass" // product id + name: "Day Pass" + leg2: + fareProducts: + id: "AAA" // identical to leg1. the passenger needs to buy ONE pass, not two. + product: + id: "day-pass" // product id + name: "Day Pass" ``` **It is the responsibility of the API consumers to display the day pass as a product for the @@ -494,18 +494,18 @@ type FareProductUse { **Illustration** ```yaml itinerary: - leg1: - fareProducts: - id: "AAA" // id of a FareProductUse instance, not product id - product: - id: "single-ticket" // product id - name: "Single Ticket" - leg2: - fareProducts: - id: "BBB" // different to leg1. the passenger needs to buy two single tickets. - product: - id: "single-ticket" // product id - name: "Single Ticket" + leg1: + fareProducts: + id: "AAA" // id of a FareProductUse instance, not product id + product: + id: "single-ticket" // product id + name: "Single Ticket" + leg2: + fareProducts: + id: "BBB" // different to leg1. the passenger needs to buy two single tickets. + product: + id: "single-ticket" // product id + name: "Single Ticket" ``` """ id: String! @@ -588,8 +588,8 @@ type Itinerary { How many transfers are part of this itinerary. Notes: - - Interlined/stay-seated transfers do not increase this count. - - Transferring from a flex to a fixed schedule trip and vice versa increases this count. + - Interlined/stay-seated transfers do not increase this count. + - Transferring from a flex to a fixed schedule trip and vice versa increases this count. """ numberOfTransfers: Int! "Time when the user leaves from the origin." @@ -1618,9 +1618,17 @@ type QueryType { "Only return routes with these feedIds" feeds: [String], "Only return routes with these ids" - ids: [String], + ids: [String] @deprecated(reason : "Since it is hard to reason about the ID filter being combined with others in this resolver, it will be moved to a separate one."), "Query routes by this name" name: String, + """ + Only include routes whose pattern operates on at least one service date specified by this filter. + + **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. + """ + serviceDates: LocalDateRangeInput, "Only include routes, which use one of these modes" transportModes: [Mode] ): [Route] @@ -1843,7 +1851,16 @@ type Route implements Node { "Transport mode of this route, e.g. `BUS`" mode: TransitMode "List of patterns which operate on this route" - patterns: [Pattern] + patterns( + """ + Filter patterns by the service dates they operate on. + + **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. + """ + serviceDates: LocalDateRangeInput + ): [Pattern] "Short name of the route, usually a line number, e.g. 550" shortName: String """ @@ -1889,8 +1906,8 @@ type RouteType { """ GTFS Route type. For the list of possible values, see: - https://developers.google.com/transit/gtfs/reference/#routestxt and - https://developers.google.com/transit/gtfs/reference/extended-route-types + https://developers.google.com/transit/gtfs/reference/#routestxt and + https://developers.google.com/transit/gtfs/reference/extended-route-types """ routeType: Int! """ @@ -3496,6 +3513,13 @@ scalar GeoJson @specifiedBy(url : "https://www.rfcreader.com/#rfc7946") scalar Grams +""" +An ISO-8601-formatted local date, i.e. `2024-05-24` for the 24th of May, 2024. + +ISO-8601 allows many different date formats, however only the most common one - `yyyy-MM-dd` - is accepted. +""" +scalar LocalDate @specifiedBy(url : "https://www.iso.org/standard/70907.html") + "A IETF BCP 47 language tag" scalar Locale @specifiedBy(url : "https://www.rfcreader.com/#rfc5646") @@ -3615,6 +3639,7 @@ input BicycleWalkPreferencesInput { "Costs related to walking a bicycle." cost: BicycleWalkPreferencesCostInput """ + " How long it takes to hop on or off a bicycle when switching to walking the bicycle or when getting on the bicycle again. However, this is not applied when getting on a rented bicycle for the first time or off the bicycle when returning the bicycle. @@ -3836,6 +3861,23 @@ input InputUnpreferred { useUnpreferredRoutesPenalty: Int @deprecated(reason : "Use unpreferredCost instead") } +"Filters an entity by a date range." +input LocalDateRangeInput { + """ + **Exclusive** end date of the filter. This means that if you want a time window from Sunday to + Sunday, `end` must be on Monday. + + If `null` this means that no end filter is applied and all entities that are after or on `start` + are selected. + """ + end: LocalDate + """ + **Inclusive** start date of the filter. If `null` this means that no `start` filter is applied and all + dates that are before `end` are selected. + """ + start: LocalDate +} + """ The filter definition to include or exclude parking facilities used during routing. @@ -3843,11 +3885,11 @@ Logically, the filter algorithm work as follows: - The starting point is the set of all facilities, lets call it `A`. - Then all `select` filters are applied to `A`, potentially reducing the number of facilities used. - Let's call the result of this `B`. - An empty `select` will lead to `A` being equal to `B`. +Let's call the result of this `B`. +An empty `select` will lead to `A` being equal to `B`. - Lastly, the `not` filters are applied to `B`, reducing the set further. - Lets call this final set `C`. - An empty `not` will lead to `B` being equal to `C`. +Lets call this final set `C`. +An empty `not` will lead to `B` being equal to `C`. - The remaining parking facilities in `C` are used for routing. """ input ParkingFilter { diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql index 0251245d183..7f5c68961aa 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/routes-extended.graphql @@ -1,17 +1,17 @@ { - routes { - longName - shortName - gtfsId - agency { - gtfsId - name - } - mode - sortOrder - bikesAllowed - patterns(serviceDates: {start: "2024-05-23" end: "2024-05-30"}) { - name - } + routes { + longName + shortName + gtfsId + agency { + gtfsId + name } -} \ No newline at end of file + mode + sortOrder + bikesAllowed + patterns(serviceDates: { start: "2024-05-23", end: "2024-05-30" }) { + name + } + } +} From 67c32797197bfcaeb940aa330d3a2922eaa15f83 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 3 Jul 2024 17:45:14 +0200 Subject: [PATCH 071/192] Fix indentation --- .../opentripplanner/apis/gtfs/schema.graphqls | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 0fcb76a211a..1a43a763ea9 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -469,18 +469,18 @@ type FareProductUse { **Illustration** ```yaml itinerary: - leg1: - fareProducts: - id: "AAA" // id of a FareProductUse instance - product: - id: "day-pass" // product id - name: "Day Pass" - leg2: - fareProducts: - id: "AAA" // identical to leg1. the passenger needs to buy ONE pass, not two. - product: - id: "day-pass" // product id - name: "Day Pass" + leg1: + fareProducts: + id: "AAA" // id of a FareProductUse instance + product: + id: "day-pass" // product id + name: "Day Pass" + leg2: + fareProducts: + id: "AAA" // identical to leg1. the passenger needs to buy ONE pass, not two. + product: + id: "day-pass" // product id + name: "Day Pass" ``` **It is the responsibility of the API consumers to display the day pass as a product for the @@ -494,18 +494,18 @@ type FareProductUse { **Illustration** ```yaml itinerary: - leg1: - fareProducts: - id: "AAA" // id of a FareProductUse instance, not product id - product: - id: "single-ticket" // product id - name: "Single Ticket" - leg2: - fareProducts: - id: "BBB" // different to leg1. the passenger needs to buy two single tickets. - product: - id: "single-ticket" // product id - name: "Single Ticket" + leg1: + fareProducts: + id: "AAA" // id of a FareProductUse instance, not product id + product: + id: "single-ticket" // product id + name: "Single Ticket" + leg2: + fareProducts: + id: "BBB" // different to leg1. the passenger needs to buy two single tickets. + product: + id: "single-ticket" // product id + name: "Single Ticket" ``` """ id: String! @@ -588,8 +588,8 @@ type Itinerary { How many transfers are part of this itinerary. Notes: - - Interlined/stay-seated transfers do not increase this count. - - Transferring from a flex to a fixed schedule trip and vice versa increases this count. + - Interlined/stay-seated transfers do not increase this count. + - Transferring from a flex to a fixed schedule trip and vice versa increases this count. """ numberOfTransfers: Int! "Time when the user leaves from the origin." @@ -1906,8 +1906,8 @@ type RouteType { """ GTFS Route type. For the list of possible values, see: - https://developers.google.com/transit/gtfs/reference/#routestxt and - https://developers.google.com/transit/gtfs/reference/extended-route-types + https://developers.google.com/transit/gtfs/reference/#routestxt and + https://developers.google.com/transit/gtfs/reference/extended-route-types """ routeType: Int! """ @@ -3639,7 +3639,6 @@ input BicycleWalkPreferencesInput { "Costs related to walking a bicycle." cost: BicycleWalkPreferencesCostInput """ - " How long it takes to hop on or off a bicycle when switching to walking the bicycle or when getting on the bicycle again. However, this is not applied when getting on a rented bicycle for the first time or off the bicycle when returning the bicycle. @@ -3885,11 +3884,11 @@ Logically, the filter algorithm work as follows: - The starting point is the set of all facilities, lets call it `A`. - Then all `select` filters are applied to `A`, potentially reducing the number of facilities used. -Let's call the result of this `B`. -An empty `select` will lead to `A` being equal to `B`. + Let's call the result of this `B`. + An empty `select` will lead to `A` being equal to `B`. - Lastly, the `not` filters are applied to `B`, reducing the set further. -Lets call this final set `C`. -An empty `not` will lead to `B` being equal to `C`. + Lets call this final set `C`. + An empty `not` will lead to `B` being equal to `C`. - The remaining parking facilities in `C` are used for routing. """ input ParkingFilter { From cf87f1650ed8c208834d02d2cfa36be85fd47af7 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Fri, 28 Jun 2024 17:20:43 +0200 Subject: [PATCH 072/192] refactor: Move transit-group-priority to the domain model --- .../model/TransitGroupPriorityReport.java | 2 +- .../cost/DefaultTransitGroupCalculator.java | 23 ++++++++ .../transit/mappers/RaptorRequestMapper.java | 4 +- .../RaptorRoutingRequestTransitData.java | 1 + ...aptorRoutingRequestTransitDataCreator.java | 1 + .../grouppriority}/BinarySetOperator.java | 2 +- .../PriorityGroupConfigurator.java | 3 +- .../grouppriority}/PriorityGroupMatcher.java | 6 +- .../TransitGroupPriority32n.java | 26 +-------- .../DefaultTransitGroupCalculatorTest.java | 32 +++++++++++ .../TransitGroupPriority32nTest.java | 57 ++++++++++--------- ...rRoutingRequestTransitDataCreatorTest.java | 1 + .../PriorityGroupConfiguratorTest.java | 3 +- .../PriorityGroupMatcherTest.java | 3 +- 14 files changed, 101 insertions(+), 63 deletions(-) create mode 100644 src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculator.java rename src/main/java/org/opentripplanner/{routing/algorithm/raptoradapter/transit/request => transit/model/network/grouppriority}/BinarySetOperator.java (79%) rename src/main/java/org/opentripplanner/{routing/algorithm/raptoradapter/transit/request => transit/model/network/grouppriority}/PriorityGroupConfigurator.java (96%) rename src/main/java/org/opentripplanner/{routing/algorithm/raptoradapter/transit/request => transit/model/network/grouppriority}/PriorityGroupMatcher.java (95%) rename src/main/java/org/opentripplanner/{routing/algorithm/raptoradapter/transit/cost => transit/model/network}/grouppriority/TransitGroupPriority32n.java (67%) create mode 100644 src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java rename src/test/java/org/opentripplanner/{routing/algorithm/raptoradapter/transit/request => transit/model/network/grouppriority}/PriorityGroupConfiguratorTest.java (96%) rename src/test/java/org/opentripplanner/{routing/algorithm/raptoradapter/transit/request => transit/model/network/grouppriority}/PriorityGroupMatcherTest.java (96%) diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java index 635469cb3a2..8e3dd0dfb17 100644 --- a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java +++ b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java @@ -4,9 +4,9 @@ import java.util.TreeMap; import java.util.TreeSet; import java.util.stream.Collectors; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.PriorityGroupConfigurator; import org.opentripplanner.routing.api.request.request.TransitRequest; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.network.grouppriority.PriorityGroupConfigurator; /** * This class is used to report all transit-groups used for transit-group-priority. The report is diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculator.java new file mode 100644 index 00000000000..c16a7ea9e7b --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculator.java @@ -0,0 +1,23 @@ +package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; + +import org.opentripplanner.raptor.api.model.DominanceFunction; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriority32n; + +public class DefaultTransitGroupCalculator implements RaptorTransitGroupCalculator { + + @Override + public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { + return TransitGroupPriority32n.mergeInGroupId(currentGroupIds, boardingGroupId); + } + + @Override + public DominanceFunction dominanceFunction() { + return TransitGroupPriority32n::dominate; + } + + @Override + public String toString() { + return "DefaultTransitGroupCalculator{Using TGP32n}"; + } +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java index 879536fdcd0..f6851b5e918 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java @@ -20,8 +20,8 @@ import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.rangeraptor.SystemErrDebugLogger; import org.opentripplanner.routing.algorithm.raptoradapter.router.performance.PerformanceTimersForRaptor; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultTransitGroupCalculator; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; import org.opentripplanner.routing.api.request.DebugEventType; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; @@ -119,7 +119,7 @@ private RaptorRequest doMap() { mcBuilder.withPassThroughPoints(mapPassThroughPoints()); r.relaxGeneralizedCostAtDestination().ifPresent(mcBuilder::withRelaxCostAtDestination); } else if (!pt.relaxTransitGroupPriority().isNormal()) { - mcBuilder.withTransitPriorityCalculator(TransitGroupPriority32n.priorityCalculator()); + mcBuilder.withTransitPriorityCalculator(new DefaultTransitGroupCalculator()); mcBuilder.withRelaxC1(mapRelaxCost(pt.relaxTransitGroupPriority())); } }); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java index 8c310206a01..909b1c0a6b3 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java @@ -30,6 +30,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.GeneralizedCostParametersMapper; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.transit.model.network.RoutingTripPattern; +import org.opentripplanner.transit.model.network.grouppriority.PriorityGroupConfigurator; /** * This is the data provider for the Range Raptor search engine. It uses data from the TransitLayer, diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java index f987e4f7a21..e2233e1e9df 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java @@ -19,6 +19,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripPatternForDate; import org.opentripplanner.transit.model.network.RoutingTripPattern; +import org.opentripplanner.transit.model.network.grouppriority.PriorityGroupConfigurator; import org.opentripplanner.transit.model.timetable.TripTimes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/BinarySetOperator.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/BinarySetOperator.java similarity index 79% rename from src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/BinarySetOperator.java rename to src/main/java/org/opentripplanner/transit/model/network/grouppriority/BinarySetOperator.java index 35e5b8c0918..4d8ce2c8fd7 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/BinarySetOperator.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/BinarySetOperator.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; +package org.opentripplanner.transit.model.network.grouppriority; /** * Used to concatenate matches with either the logical "AND" or "OR" operator. diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfigurator.java similarity index 96% rename from src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java rename to src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfigurator.java index 6ef82786b99..dc25bc498d0 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfigurator.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; +package org.opentripplanner.transit.model.network.grouppriority; import gnu.trove.impl.Constants; import gnu.trove.map.TObjectIntMap; @@ -8,7 +8,6 @@ import java.util.List; import java.util.stream.Stream; import org.opentripplanner.framework.lang.ArrayUtils; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.TripPattern; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcher.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java similarity index 95% rename from src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcher.java rename to src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java index c017f2862ab..b4c37c6735b 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcher.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java @@ -1,7 +1,7 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; +package org.opentripplanner.transit.model.network.grouppriority; -import static org.opentripplanner.routing.algorithm.raptoradapter.transit.request.BinarySetOperator.AND; -import static org.opentripplanner.routing.algorithm.raptoradapter.transit.request.BinarySetOperator.OR; +import static org.opentripplanner.transit.model.network.grouppriority.BinarySetOperator.AND; +import static org.opentripplanner.transit.model.network.grouppriority.BinarySetOperator.OR; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32n.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java similarity index 67% rename from src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32n.java rename to src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java index feb3f6f7b3a..1bdff703e07 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32n.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java @@ -1,7 +1,4 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority; - -import org.opentripplanner.raptor.api.model.DominanceFunction; -import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; +package org.opentripplanner.transit.model.network.grouppriority; /** * This is a "BitSet" implementation for groupId. It can store upto 32 groups, @@ -13,25 +10,6 @@ public class TransitGroupPriority32n { private static final int MIN_SEQ_NO = 0; private static final int MAX_SEQ_NO = 32; - public static RaptorTransitGroupCalculator priorityCalculator() { - return new RaptorTransitGroupCalculator() { - @Override - public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { - return mergeInGroupId(currentGroupIds, boardingGroupId); - } - - @Override - public DominanceFunction dominanceFunction() { - return TransitGroupPriority32n::dominate; - } - - @Override - public String toString() { - return "TransitGroupPriority32nCalculator{}"; - } - }; - } - /** * Left dominate right, if right contains a group which does not exist in left. Left * do NOT dominate right if they are equals or left is a super set of right. @@ -47,7 +25,7 @@ public String toString() { /** * Use this method to map from a continuous group index [0..32) to the groupId used - * during routing. The ID is implementation specific and optimized for performance. + * during routing. The ID is implementation-specific and optimized for performance. */ public static int groupId(final int priorityGroupIndex) { assertValidGroupSeqNo(priorityGroupIndex); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java new file mode 100644 index 00000000000..9e24bf573c4 --- /dev/null +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java @@ -0,0 +1,32 @@ +package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; + +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 org.junit.jupiter.api.Test; + +class DefaultTransitGroupCalculatorTest { + + private final DefaultTransitGroupCalculator subject = new DefaultTransitGroupCalculator(); + + @Test + void mergeGroupIds() { + // Smoke test, should not fail + subject.mergeGroupIds(1, 2); + } + + @Test + void dominanceFunction() { + // This is assuming 1 & 2 represent different transit-groups - this just a smoke test to + // see that the delegation works as expected. The 'leftDominateRight' is unit-tested elsewhere. + assertTrue(subject.dominanceFunction().leftDominateRight(1, 2)); + assertTrue(subject.dominanceFunction().leftDominateRight(2, 1)); + assertFalse(subject.dominanceFunction().leftDominateRight(1, 1)); + } + + @Test + void testToString() { + assertEquals("DefaultTransitGroupCalculator{Using TGP32n}", subject.toString()); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java index 2713a190dbf..3fe242c984b 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java @@ -4,9 +4,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriority32n.dominate; +import static org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriority32n.groupId; +import static org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriority32n.mergeInGroupId; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; class TransitGroupPriority32nTest { @@ -16,49 +18,48 @@ class TransitGroupPriority32nTest { private static final int GROUP_INDEX_30 = 30; private static final int GROUP_INDEX_31 = 31; - private static final int GROUP_0 = TransitGroupPriority32n.groupId(GROUP_INDEX_0); - private static final int GROUP_1 = TransitGroupPriority32n.groupId(GROUP_INDEX_1); - private static final int GROUP_2 = TransitGroupPriority32n.groupId(GROUP_INDEX_2); - private static final int GROUP_30 = TransitGroupPriority32n.groupId(GROUP_INDEX_30); - private static final int GROUP_31 = TransitGroupPriority32n.groupId(GROUP_INDEX_31); - private static final RaptorTransitGroupCalculator subjct = TransitGroupPriority32n.priorityCalculator(); + private static final int GROUP_0 = groupId(GROUP_INDEX_0); + private static final int GROUP_1 = groupId(GROUP_INDEX_1); + private static final int GROUP_2 = groupId(GROUP_INDEX_2); + private static final int GROUP_30 = groupId(GROUP_INDEX_30); + private static final int GROUP_31 = groupId(GROUP_INDEX_31); @Test - void groupId() { - assertEqualsHex(0x00_00_00_00, TransitGroupPriority32n.groupId(0)); - assertEqualsHex(0x00_00_00_01, TransitGroupPriority32n.groupId(1)); - assertEqualsHex(0x00_00_00_02, TransitGroupPriority32n.groupId(2)); - assertEqualsHex(0x00_00_00_04, TransitGroupPriority32n.groupId(3)); - assertEqualsHex(0x40_00_00_00, TransitGroupPriority32n.groupId(31)); - assertEqualsHex(0x80_00_00_00, TransitGroupPriority32n.groupId(32)); + void testGroupId() { + assertEqualsHex(0x00_00_00_00, groupId(0)); + assertEqualsHex(0x00_00_00_01, groupId(1)); + assertEqualsHex(0x00_00_00_02, groupId(2)); + assertEqualsHex(0x00_00_00_04, groupId(3)); + assertEqualsHex(0x40_00_00_00, groupId(31)); + assertEqualsHex(0x80_00_00_00, groupId(32)); - assertThrows(IllegalArgumentException.class, () -> TransitGroupPriority32n.groupId(-1)); - assertThrows(IllegalArgumentException.class, () -> TransitGroupPriority32n.groupId(33)); + assertThrows(IllegalArgumentException.class, () -> groupId(-1)); + assertThrows(IllegalArgumentException.class, () -> groupId(33)); } @Test void mergeTransitGroupPriorityIds() { - assertEqualsHex(GROUP_0, subjct.mergeGroupIds(GROUP_0, GROUP_0)); - assertEqualsHex(GROUP_1, subjct.mergeGroupIds(GROUP_1, GROUP_1)); - assertEqualsHex(GROUP_0 | GROUP_1, subjct.mergeGroupIds(GROUP_0, GROUP_1)); - assertEqualsHex(GROUP_30 | GROUP_31, subjct.mergeGroupIds(GROUP_30, GROUP_31)); + assertEqualsHex(GROUP_0, mergeInGroupId(GROUP_0, GROUP_0)); + assertEqualsHex(GROUP_1, mergeInGroupId(GROUP_1, GROUP_1)); + assertEqualsHex(GROUP_0 | GROUP_1, mergeInGroupId(GROUP_0, GROUP_1)); + assertEqualsHex(GROUP_30 | GROUP_31, mergeInGroupId(GROUP_30, GROUP_31)); assertEqualsHex( GROUP_0 | GROUP_1 | GROUP_2 | GROUP_30 | GROUP_31, - subjct.mergeGroupIds(GROUP_0 | GROUP_1 | GROUP_2 | GROUP_30, GROUP_31) + mergeInGroupId(GROUP_0 | GROUP_1 | GROUP_2 | GROUP_30, GROUP_31) ); } @Test void dominanceFunction() { - assertFalse(subjct.dominanceFunction().leftDominateRight(GROUP_0, GROUP_0)); - assertFalse(subjct.dominanceFunction().leftDominateRight(GROUP_31, GROUP_31)); - assertFalse(subjct.dominanceFunction().leftDominateRight(GROUP_1 | GROUP_2, GROUP_1 | GROUP_2)); + assertFalse(dominate(GROUP_0, GROUP_0)); + assertFalse(dominate(GROUP_31, GROUP_31)); + assertFalse(dominate(GROUP_1 | GROUP_2, GROUP_1 | GROUP_2)); - assertTrue(subjct.dominanceFunction().leftDominateRight(GROUP_0, GROUP_1)); - assertFalse(subjct.dominanceFunction().leftDominateRight(GROUP_1, GROUP_0)); + assertTrue(dominate(GROUP_0, GROUP_1)); + assertFalse(dominate(GROUP_1, GROUP_0)); - assertTrue(subjct.dominanceFunction().leftDominateRight(GROUP_1, GROUP_1 | GROUP_2)); - assertFalse(subjct.dominanceFunction().leftDominateRight(GROUP_1 | GROUP_2, GROUP_1)); + assertTrue(dominate(GROUP_1, GROUP_1 | GROUP_2)); + assertFalse(dominate(GROUP_1 | GROUP_2, GROUP_1)); } static void assertEqualsHex(int expected, int actual) { diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java index ed71b3de400..861420d73eb 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java @@ -19,6 +19,7 @@ import org.opentripplanner.transit.model.network.RoutingTripPattern; import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.network.grouppriority.PriorityGroupConfigurator; import org.opentripplanner.transit.model.timetable.ScheduledTripTimes; import org.opentripplanner.transit.model.timetable.TripTimes; diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfiguratorTest.java similarity index 96% rename from src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java rename to src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfiguratorTest.java index 7f974927c1b..14e18968160 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfiguratorTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; +package org.opentripplanner.transit.model.network.grouppriority; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TestTransitCaseData.STOP_A; @@ -7,6 +7,7 @@ import java.util.List; import org.junit.jupiter.api.Test; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TestRouteData; import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.network.TripPattern; diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcherTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java similarity index 96% rename from src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcherTest.java rename to src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java index 91d0142f9ef..030ac65ecf4 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcherTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; +package org.opentripplanner.transit.model.network.grouppriority; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -6,6 +6,7 @@ import java.util.List; import org.junit.jupiter.api.Test; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.TestRouteData; import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.FeedScopedId; From 916a9a931e8d99e53b52f2e90956181735d86c93 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 00:06:07 +0200 Subject: [PATCH 073/192] refactor: Rename to RaptorTransitGroupPriorityCalculator --- .../raptor/api/request/MultiCriteriaRequest.java | 10 +++++----- ....java => RaptorTransitGroupPriorityCalculator.java} | 2 +- .../multicriteria/configure/McRangeRaptorConfig.java | 4 ++-- .../ride/c2/TransitGroupPriorityRideFactory.java | 6 +++--- ...java => DefaultTransitGroupPriorityCalculator.java} | 4 ++-- .../transit/mappers/RaptorRequestMapper.java | 4 ++-- .../raptor/moduletests/K01_TransitPriorityTest.java | 4 ++-- .../K02_TransitPriorityDestinationTest.java | 4 ++-- .../support/TestGroupPriorityCalculator.java | 6 +++--- .../cost/DefaultTransitGroupCalculatorTest.java | 2 +- 10 files changed, 23 insertions(+), 23 deletions(-) rename src/main/java/org/opentripplanner/raptor/api/request/{RaptorTransitGroupCalculator.java => RaptorTransitGroupPriorityCalculator.java} (93%) rename src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/{DefaultTransitGroupCalculator.java => DefaultTransitGroupPriorityCalculator.java} (84%) diff --git a/src/main/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequest.java b/src/main/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequest.java index 368b4660922..683c9807af5 100644 --- a/src/main/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequest.java +++ b/src/main/java/org/opentripplanner/raptor/api/request/MultiCriteriaRequest.java @@ -18,7 +18,7 @@ public class MultiCriteriaRequest { private final RelaxFunction relaxC1; @Nullable - private final RaptorTransitGroupCalculator transitPriorityCalculator; + private final RaptorTransitGroupPriorityCalculator transitPriorityCalculator; private final List passThroughPoints; @@ -63,7 +63,7 @@ public RelaxFunction relaxC1() { return relaxC1; } - public Optional transitPriorityCalculator() { + public Optional transitPriorityCalculator() { return Optional.ofNullable(transitPriorityCalculator); } @@ -140,7 +140,7 @@ public static class Builder { private final MultiCriteriaRequest original; private RelaxFunction relaxC1; - private RaptorTransitGroupCalculator transitPriorityCalculator; + private RaptorTransitGroupPriorityCalculator transitPriorityCalculator; private List passThroughPoints; private Double relaxCostAtDestination; @@ -163,11 +163,11 @@ public Builder withRelaxC1(RelaxFunction relaxC1) { } @Nullable - public RaptorTransitGroupCalculator transitPriorityCalculator() { + public RaptorTransitGroupPriorityCalculator transitPriorityCalculator() { return transitPriorityCalculator; } - public Builder withTransitPriorityCalculator(RaptorTransitGroupCalculator value) { + public Builder withTransitPriorityCalculator(RaptorTransitGroupPriorityCalculator value) { transitPriorityCalculator = value; return this; } diff --git a/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupCalculator.java b/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupPriorityCalculator.java similarity index 93% rename from src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupCalculator.java rename to src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupPriorityCalculator.java index b5f0598415e..06c10b51daf 100644 --- a/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupCalculator.java +++ b/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupPriorityCalculator.java @@ -2,7 +2,7 @@ import org.opentripplanner.raptor.api.model.DominanceFunction; -public interface RaptorTransitGroupCalculator { +public interface RaptorTransitGroupPriorityCalculator { /** * Merge in the transit group id with an existing set. Note! Both the set * and the group id type is {@code int}. diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java index 8eef90950dd..3673e78ee47 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/configure/McRangeRaptorConfig.java @@ -6,7 +6,7 @@ import org.opentripplanner.raptor.api.model.DominanceFunction; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.api.request.MultiCriteriaRequest; -import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; import org.opentripplanner.raptor.rangeraptor.context.SearchContext; import org.opentripplanner.raptor.rangeraptor.internalapi.Heuristics; import org.opentripplanner.raptor.rangeraptor.internalapi.ParetoSetCost; @@ -201,7 +201,7 @@ private DominanceFunction dominanceFunctionC2() { return null; } - private RaptorTransitGroupCalculator getTransitGroupPriorityCalculator() { + private RaptorTransitGroupPriorityCalculator getTransitGroupPriorityCalculator() { return mcRequest().transitPriorityCalculator().orElseThrow(); } diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java index 5d65c40d021..79ae1558836 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java @@ -2,7 +2,7 @@ import org.opentripplanner.raptor.api.model.RaptorTripPattern; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; -import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; import org.opentripplanner.raptor.rangeraptor.multicriteria.arrivals.McStopArrival; import org.opentripplanner.raptor.rangeraptor.multicriteria.ride.PatternRide; import org.opentripplanner.raptor.rangeraptor.multicriteria.ride.PatternRideFactory; @@ -15,10 +15,10 @@ public class TransitGroupPriorityRideFactory implements PatternRideFactory> { private int currentPatternGroupPriority; - private final RaptorTransitGroupCalculator transitGroupPriorityCalculator; + private final RaptorTransitGroupPriorityCalculator transitGroupPriorityCalculator; public TransitGroupPriorityRideFactory( - RaptorTransitGroupCalculator transitGroupPriorityCalculator + RaptorTransitGroupPriorityCalculator transitGroupPriorityCalculator ) { this.transitGroupPriorityCalculator = transitGroupPriorityCalculator; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupPriorityCalculator.java similarity index 84% rename from src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculator.java rename to src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupPriorityCalculator.java index c16a7ea9e7b..6045a9aa1de 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupPriorityCalculator.java @@ -1,10 +1,10 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; import org.opentripplanner.raptor.api.model.DominanceFunction; -import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriority32n; -public class DefaultTransitGroupCalculator implements RaptorTransitGroupCalculator { +public class DefaultTransitGroupPriorityCalculator implements RaptorTransitGroupPriorityCalculator { @Override public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java index f6851b5e918..0123d85c7ad 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java @@ -20,7 +20,7 @@ import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.rangeraptor.SystemErrDebugLogger; import org.opentripplanner.routing.algorithm.raptoradapter.router.performance.PerformanceTimersForRaptor; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultTransitGroupCalculator; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultTransitGroupPriorityCalculator; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.routing.api.request.DebugEventType; import org.opentripplanner.routing.api.request.RouteRequest; @@ -119,7 +119,7 @@ private RaptorRequest doMap() { mcBuilder.withPassThroughPoints(mapPassThroughPoints()); r.relaxGeneralizedCostAtDestination().ifPresent(mcBuilder::withRelaxCostAtDestination); } else if (!pt.relaxTransitGroupPriority().isNormal()) { - mcBuilder.withTransitPriorityCalculator(new DefaultTransitGroupCalculator()); + mcBuilder.withTransitPriorityCalculator(new DefaultTransitGroupPriorityCalculator()); mcBuilder.withRelaxC1(mapRelaxCost(pt.relaxTransitGroupPriority())); } }); diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java b/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java index 00b1d528e7e..f03eb5335a0 100644 --- a/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java +++ b/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java @@ -22,7 +22,7 @@ import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.request.RaptorProfile; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; -import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; @@ -34,7 +34,7 @@ */ public class K01_TransitPriorityTest { - private static final RaptorTransitGroupCalculator PRIORITY_GROUP_CALCULATOR = + private static final RaptorTransitGroupPriorityCalculator PRIORITY_GROUP_CALCULATOR = TestGroupPriorityCalculator.PRIORITY_CALCULATOR; private static final int C1_SLACK_90s = RaptorCostConverter.toRaptorCost(90); diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java b/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java index c6ab8e337ea..fb21128728c 100644 --- a/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java +++ b/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java @@ -25,7 +25,7 @@ import org.opentripplanner.raptor._data.transit.TestTripSchedule; import org.opentripplanner.raptor.api.request.RaptorProfile; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; -import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; @@ -37,7 +37,7 @@ */ public class K02_TransitPriorityDestinationTest { - private static final RaptorTransitGroupCalculator PRIORITY_GROUP_CALCULATOR = + private static final RaptorTransitGroupPriorityCalculator PRIORITY_GROUP_CALCULATOR = TestGroupPriorityCalculator.PRIORITY_CALCULATOR; private static final int C1_SLACK_90s = RaptorCostConverter.toRaptorCost(90); diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java b/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java index cdbe82f18a6..3234dc126fb 100644 --- a/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java +++ b/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java @@ -5,11 +5,11 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.raptor.api.model.DominanceFunction; -import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; -public class TestGroupPriorityCalculator implements RaptorTransitGroupCalculator { +public class TestGroupPriorityCalculator implements RaptorTransitGroupPriorityCalculator { - public static final RaptorTransitGroupCalculator PRIORITY_CALCULATOR = new TestGroupPriorityCalculator(); + public static final RaptorTransitGroupPriorityCalculator PRIORITY_CALCULATOR = new TestGroupPriorityCalculator(); public static final int GROUP_A = 0x01; public static final int GROUP_B = 0x02; diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java index 9e24bf573c4..7c523abf741 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java @@ -8,7 +8,7 @@ class DefaultTransitGroupCalculatorTest { - private final DefaultTransitGroupCalculator subject = new DefaultTransitGroupCalculator(); + private final DefaultTransitGroupPriorityCalculator subject = new DefaultTransitGroupPriorityCalculator(); @Test void mergeGroupIds() { From 3627e5014c02e27ca77c3e9295f7ee9758442846 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 00:01:58 +0200 Subject: [PATCH 074/192] refactor: Move DefaultTransitGroupCalculator to grouppriority package --- .../raptoradapter/transit/mappers/RaptorRequestMapper.java | 2 +- .../grouppriority}/DefaultTransitGroupPriorityCalculator.java | 3 +-- .../grouppriority}/DefaultTransitGroupCalculatorTest.java | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) rename src/main/java/org/opentripplanner/{routing/algorithm/raptoradapter/transit/cost => transit/model/network/grouppriority}/DefaultTransitGroupPriorityCalculator.java (79%) rename src/test/java/org/opentripplanner/{routing/algorithm/raptoradapter/transit/cost => transit/model/network/grouppriority}/DefaultTransitGroupCalculatorTest.java (93%) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java index 0123d85c7ad..948b132e408 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java @@ -20,11 +20,11 @@ import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.rangeraptor.SystemErrDebugLogger; import org.opentripplanner.routing.algorithm.raptoradapter.router.performance.PerformanceTimersForRaptor; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.DefaultTransitGroupPriorityCalculator; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.routing.api.request.DebugEventType; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; +import org.opentripplanner.transit.model.network.grouppriority.DefaultTransitGroupPriorityCalculator; import org.opentripplanner.transit.model.site.StopLocation; public class RaptorRequestMapper { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupPriorityCalculator.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculator.java similarity index 79% rename from src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupPriorityCalculator.java rename to src/main/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculator.java index 6045a9aa1de..d09d206adb0 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupPriorityCalculator.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculator.java @@ -1,8 +1,7 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; +package org.opentripplanner.transit.model.network.grouppriority; import org.opentripplanner.raptor.api.model.DominanceFunction; import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; -import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriority32n; public class DefaultTransitGroupPriorityCalculator implements RaptorTransitGroupPriorityCalculator { diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupCalculatorTest.java similarity index 93% rename from src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java rename to src/test/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupCalculatorTest.java index 7c523abf741..131bce04707 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultTransitGroupCalculatorTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupCalculatorTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; +package org.opentripplanner.transit.model.network.grouppriority; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; From dea9911773b06a26ec04b818c712097eec70836f Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 00:16:32 +0200 Subject: [PATCH 075/192] refactor: Move TransitGroupPriority32nTest to grouppriority package --- .../network}/grouppriority/TransitGroupPriority32nTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/org/opentripplanner/{routing/algorithm/raptoradapter/transit/cost => transit/model/network}/grouppriority/TransitGroupPriority32nTest.java (96%) diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32nTest.java similarity index 96% rename from src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java rename to src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32nTest.java index 3fe242c984b..9f681dc16c6 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32nTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority; +package org.opentripplanner.transit.model.network.grouppriority; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; From 82b21681c658c8e141e480386f84be49bc6684be Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 00:28:24 +0200 Subject: [PATCH 076/192] refactor: Rename to TransitGroupPriorityService from PriorityGroupConfigurator --- .../model/TransitGroupPriorityReport.java | 6 +++--- .../request/RaptorRoutingRequestTransitData.java | 8 ++++---- .../RaptorRoutingRequestTransitDataCreator.java | 10 +++++----- ...rator.java => TransitGroupPriorityService.java} | 14 +++++++------- ...RaptorRoutingRequestTransitDataCreatorTest.java | 4 ++-- ...t.java => TransitGroupPriorityServiceTest.java} | 8 ++++---- 6 files changed, 25 insertions(+), 25 deletions(-) rename src/main/java/org/opentripplanner/transit/model/network/grouppriority/{PriorityGroupConfigurator.java => TransitGroupPriorityService.java} (93%) rename src/test/java/org/opentripplanner/transit/model/network/grouppriority/{PriorityGroupConfiguratorTest.java => TransitGroupPriorityServiceTest.java} (94%) diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java index 8e3dd0dfb17..67f7350ee14 100644 --- a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java +++ b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java @@ -6,7 +6,7 @@ import java.util.stream.Collectors; import org.opentripplanner.routing.api.request.request.TransitRequest; import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.model.network.grouppriority.PriorityGroupConfigurator; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; /** * This class is used to report all transit-groups used for transit-group-priority. The report is @@ -17,14 +17,14 @@ public class TransitGroupPriorityReport { public static String build(Collection patterns, TransitRequest request) { - var c = PriorityGroupConfigurator.of( + var service = TransitGroupPriorityService.of( request.priorityGroupsByAgency(), request.priorityGroupsGlobal() ); var map = new TreeMap(); for (var it : patterns) { - int groupId = c.lookupTransitGroupPriorityId(it); + int groupId = service.lookupTransitGroupPriorityId(it); var de = map.computeIfAbsent(groupId, DebugEntity::new); de.add( it.getRoute().getAgency().getId().toString(), diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java index 909b1c0a6b3..83a29ee88d7 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java @@ -30,7 +30,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.GeneralizedCostParametersMapper; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.transit.model.network.RoutingTripPattern; -import org.opentripplanner.transit.model.network.grouppriority.PriorityGroupConfigurator; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; /** * This is the data provider for the Range Raptor search engine. It uses data from the TransitLayer, @@ -244,12 +244,12 @@ public RaptorConstrainedBoardingSearch transferConstraintsReverseS return new ConstrainedBoardingSearch(false, toStopTransfers, fromStopTransfers); } - private PriorityGroupConfigurator createTransitGroupPriorityConfigurator(RouteRequest request) { + private TransitGroupPriorityService createTransitGroupPriorityConfigurator(RouteRequest request) { if (request.preferences().transit().relaxTransitGroupPriority().isNormal()) { - return PriorityGroupConfigurator.empty(); + return TransitGroupPriorityService.empty(); } var transitRequest = request.journey().transit(); - return PriorityGroupConfigurator.of( + return TransitGroupPriorityService.of( transitRequest.priorityGroupsByAgency(), transitRequest.priorityGroupsGlobal() ); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java index e2233e1e9df..815bf839e31 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java @@ -19,7 +19,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripPatternForDate; import org.opentripplanner.transit.model.network.RoutingTripPattern; -import org.opentripplanner.transit.model.network.grouppriority.PriorityGroupConfigurator; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; import org.opentripplanner.transit.model.timetable.TripTimes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -94,7 +94,7 @@ static List merge( ZonedDateTime transitSearchTimeZero, List patternForDateList, TransitDataProviderFilter filter, - PriorityGroupConfigurator priorityGroupConfigurator + TransitGroupPriorityService transitGroupPriorityService ) { // Group TripPatternForDate objects by TripPattern. // This is done in a loop to increase performance. @@ -148,7 +148,7 @@ static List merge( tripPattern.getAlightingPossible(), BoardAlight.ALIGHT ), - priorityGroupConfigurator.lookupTransitGroupPriorityId(tripPattern.getPattern()) + transitGroupPriorityService.lookupTransitGroupPriorityId(tripPattern.getPattern()) ) ); } @@ -160,7 +160,7 @@ List createTripPatterns( int additionalPastSearchDays, int additionalFutureSearchDays, TransitDataProviderFilter filter, - PriorityGroupConfigurator priorityGroupConfigurator + TransitGroupPriorityService transitGroupPriorityService ) { List tripPatternForDates = getTripPatternsForDateRange( additionalPastSearchDays, @@ -168,7 +168,7 @@ List createTripPatterns( filter ); - return merge(transitSearchTimeZero, tripPatternForDates, filter, priorityGroupConfigurator); + return merge(transitSearchTimeZero, tripPatternForDates, filter, transitGroupPriorityService); } private static List filterActiveTripPatterns( diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfigurator.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java similarity index 93% rename from src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfigurator.java rename to src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java index dc25bc498d0..9967d6ed8eb 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfigurator.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java @@ -20,7 +20,7 @@ *

* THIS CLASS IS NOT THREAD-SAFE. */ -public class PriorityGroupConfigurator { +public class TransitGroupPriorityService { /** * There are two ways we can treat the base (local-traffic) transit priority group: @@ -53,7 +53,7 @@ public class PriorityGroupConfigurator { private final List agencyMatchersIds; private final List globalMatchersIds; - private PriorityGroupConfigurator() { + private TransitGroupPriorityService() { this.enabled = false; this.agencyMatchers = null; this.globalMatchers = null; @@ -61,7 +61,7 @@ private PriorityGroupConfigurator() { this.globalMatchersIds = List.of(); } - private PriorityGroupConfigurator( + private TransitGroupPriorityService( Collection byAgency, Collection global ) { @@ -74,18 +74,18 @@ private PriorityGroupConfigurator( this.agencyMatchersIds = Arrays.stream(agencyMatchers).map(MatcherAgencyAndIds::new).toList(); } - public static PriorityGroupConfigurator empty() { - return new PriorityGroupConfigurator(); + public static TransitGroupPriorityService empty() { + return new TransitGroupPriorityService(); } - public static PriorityGroupConfigurator of( + public static TransitGroupPriorityService of( Collection byAgency, Collection global ) { if (Stream.of(byAgency, global).allMatch(Collection::isEmpty)) { return empty(); } - return new PriorityGroupConfigurator(byAgency, global); + return new TransitGroupPriorityService(byAgency, global); } /** diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java index 861420d73eb..c66caa05985 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java @@ -19,7 +19,7 @@ import org.opentripplanner.transit.model.network.RoutingTripPattern; import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.model.network.grouppriority.PriorityGroupConfigurator; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; import org.opentripplanner.transit.model.timetable.ScheduledTripTimes; import org.opentripplanner.transit.model.timetable.TripTimes; @@ -66,7 +66,7 @@ public void testMergeTripPatterns() { startOfTime, tripPatternsForDates, new TestTransitDataProviderFilter(), - PriorityGroupConfigurator.empty() + TransitGroupPriorityService.empty() ); // Get the results diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfiguratorTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java similarity index 94% rename from src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfiguratorTest.java rename to src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java index 14e18968160..b81bc4ed15f 100644 --- a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupConfiguratorTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java @@ -13,7 +13,7 @@ import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.RegularStop; -class PriorityGroupConfiguratorTest { +class TransitGroupPriorityServiceTest { private static final String AGENCY_A1 = "A1"; private static final String AGENCY_A2 = "A2"; @@ -69,7 +69,7 @@ class PriorityGroupConfiguratorTest { @Test void emptyConfigurationShouldReturnGroupZero() { - var subject = PriorityGroupConfigurator.of(List.of(), List.of()); + var subject = TransitGroupPriorityService.of(List.of(), List.of()); assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(railR1)); assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(busB2)); assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(null)); @@ -83,7 +83,7 @@ void lookupTransitGroupIdByAgency() { .build(); // Add matcher `byAgency` for bus and real - var subject = PriorityGroupConfigurator.of(List.of(select), List.of()); + var subject = TransitGroupPriorityService.of(List.of(select), List.of()); // Agency groups are indexed (group-id set) at request time assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(null)); @@ -97,7 +97,7 @@ void lookupTransitGroupIdByAgency() { @Test void lookupTransitPriorityGroupIdByGlobalMode() { // Global groups are indexed (group-id set) at construction time - var subject = PriorityGroupConfigurator.of( + var subject = TransitGroupPriorityService.of( List.of(), List.of( TransitGroupSelect.of().addModes(List.of(TransitMode.BUS)).build(), From 376a05fbea056963279dcef101aadc475196a6eb Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 00:38:26 +0200 Subject: [PATCH 077/192] refactor: Extract Matcher interface in group-priority --- .../model/network/grouppriority/Matcher.java | 11 ++++ .../grouppriority/PriorityGroupMatcher.java | 50 ++++++++----------- .../TransitGroupPriorityService.java | 10 ++-- .../PriorityGroupMatcherTest.java | 4 +- 4 files changed, 40 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matcher.java diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matcher.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matcher.java new file mode 100644 index 00000000000..d92ac9476a6 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matcher.java @@ -0,0 +1,11 @@ +package org.opentripplanner.transit.model.network.grouppriority; + +import org.opentripplanner.transit.model.network.TripPattern; + +interface Matcher { + boolean match(TripPattern pattern); + + default boolean isEmpty() { + return false; + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java index b4c37c6735b..50782093e5f 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java @@ -28,25 +28,25 @@ * a `CompositeMatcher`. So, a new matcher is only created if the field in the * select is present. */ -public abstract class PriorityGroupMatcher { +public final class PriorityGroupMatcher { - private static final PriorityGroupMatcher NOOP = new PriorityGroupMatcher() { + private static final Matcher NOOP = new Matcher() { @Override - boolean match(TripPattern pattern) { + public boolean match(TripPattern pattern) { return false; } @Override - boolean isEmpty() { + public boolean isEmpty() { return true; } }; - public static PriorityGroupMatcher of(TransitGroupSelect select) { + public static Matcher of(TransitGroupSelect select) { if (select.isEmpty()) { return NOOP; } - List list = new ArrayList<>(); + List list = new ArrayList<>(); if (!select.modes().isEmpty()) { list.add(new ModeMatcher(select.modes())); @@ -65,12 +65,12 @@ public static PriorityGroupMatcher of(TransitGroupSelect select) { return andOf(list); } - static PriorityGroupMatcher[] of(Collection selectors) { + static Matcher[] of(Collection selectors) { return selectors .stream() .map(PriorityGroupMatcher::of) - .filter(Predicate.not(PriorityGroupMatcher::isEmpty)) - .toArray(PriorityGroupMatcher[]::new); + .filter(Predicate.not(Matcher::isEmpty)) + .toArray(Matcher[]::new); } private static String arrayToString(BinarySetOperator op, T[] values) { @@ -81,9 +81,9 @@ private static String colToString(BinarySetOperator op, Collection values return values.stream().map(Objects::toString).collect(Collectors.joining(" " + op + " ")); } - private static PriorityGroupMatcher andOf(List list) { + private static Matcher andOf(List list) { // Remove empty/noop matchers - list = list.stream().filter(Predicate.not(PriorityGroupMatcher::isEmpty)).toList(); + list = list.stream().filter(Predicate.not(Matcher::isEmpty)).toList(); if (list.isEmpty()) { return NOOP; @@ -94,13 +94,7 @@ private static PriorityGroupMatcher andOf(List list) { return new AndMatcher(list); } - abstract boolean match(TripPattern pattern); - - boolean isEmpty() { - return false; - } - - private static final class ModeMatcher extends PriorityGroupMatcher { + private static final class ModeMatcher implements Matcher { private final Set modes; @@ -109,7 +103,7 @@ public ModeMatcher(List modes) { } @Override - boolean match(TripPattern pattern) { + public boolean match(TripPattern pattern) { return modes.contains(pattern.getMode()); } @@ -119,7 +113,7 @@ public String toString() { } } - private static final class RegExpMatcher extends PriorityGroupMatcher { + private static final class RegExpMatcher implements Matcher { private final String typeName; private final Pattern[] subModeRegexp; @@ -136,7 +130,7 @@ public RegExpMatcher( } @Override - boolean match(TripPattern pattern) { + public boolean match(TripPattern pattern) { var value = toValue.apply(pattern); for (Pattern p : subModeRegexp) { if (p.matcher(value).matches()) { @@ -152,7 +146,7 @@ public String toString() { } } - private static final class IdMatcher extends PriorityGroupMatcher { + private static final class IdMatcher implements Matcher { private final String typeName; private final Set ids; @@ -169,7 +163,7 @@ public IdMatcher( } @Override - boolean match(TripPattern pattern) { + public boolean match(TripPattern pattern) { return ids.contains(idProvider.apply(pattern)); } @@ -183,16 +177,16 @@ public String toString() { * Takes a list of matchers and provide a single interface. All matchers in the list must match * for the composite matcher to return a match. */ - private static final class AndMatcher extends PriorityGroupMatcher { + private static final class AndMatcher implements Matcher { - private final PriorityGroupMatcher[] matchers; + private final Matcher[] matchers; - public AndMatcher(List matchers) { - this.matchers = matchers.toArray(PriorityGroupMatcher[]::new); + public AndMatcher(List matchers) { + this.matchers = matchers.toArray(Matcher[]::new); } @Override - boolean match(TripPattern pattern) { + public boolean match(TripPattern pattern) { for (var m : matchers) { if (!m.match(pattern)) { return false; diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java index 9967d6ed8eb..4e9165944eb 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java @@ -46,8 +46,8 @@ public class TransitGroupPriorityService { private final int baseGroupId = TransitGroupPriority32n.groupId(GROUP_INDEX_COUNTER_START); private int groupIndexCounter = GROUP_INDEX_COUNTER_START; private final boolean enabled; - private final PriorityGroupMatcher[] agencyMatchers; - private final PriorityGroupMatcher[] globalMatchers; + private final Matcher[] agencyMatchers; + private final Matcher[] globalMatchers; // Index matchers and ids private final List agencyMatchersIds; @@ -129,11 +129,11 @@ private int nextGroupId() { } /** Pair of matcher and groupId. Used only inside this class. */ - record MatcherAndId(PriorityGroupMatcher matcher, int groupId) {} + record MatcherAndId(Matcher matcher, int groupId) {} /** Matcher with map of ids by agency. */ - record MatcherAgencyAndIds(PriorityGroupMatcher matcher, TObjectIntMap ids) { - MatcherAgencyAndIds(PriorityGroupMatcher matcher) { + record MatcherAgencyAndIds(Matcher matcher, TObjectIntMap ids) { + MatcherAgencyAndIds(Matcher matcher) { this( matcher, new TObjectIntHashMap<>(Constants.DEFAULT_CAPACITY, Constants.DEFAULT_LOAD_FACTOR, -1) diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java index 030ac65ecf4..18941f8232c 100644 --- a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java @@ -58,7 +58,7 @@ void testAgencyIds() { assertEquals("AgencyId(F:A1)", m1.toString()); assertEquals("AgencyId(F:A1 | F:ANY)", m2.toString()); - for (PriorityGroupMatcher m : matchers) { + for (Matcher m : matchers) { assertFalse(m.isEmpty()); assertTrue(m.match(rail1)); assertTrue(m.match(ferry)); @@ -79,7 +79,7 @@ void routeIds() { assertEquals("RouteId(F:R1)", m1.toString()); assertEquals("RouteId(F:R1 | F:ANY)", m2.toString()); - for (PriorityGroupMatcher m : matchers) { + for (Matcher m : matchers) { assertFalse(m.isEmpty()); assertTrue(m.match(rail1)); assertFalse(m.match(ferry)); From 6ca1e8b60c0b9b38561bcbf3143843224a1d23b5 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 00:40:29 +0200 Subject: [PATCH 078/192] refactor: Rename local class to Matchers from PriorityGroupMatcher --- ...riorityGroupMatcher.java => Matchers.java} | 4 +-- .../TransitGroupPriorityService.java | 4 +-- ...roupMatcherTest.java => MatchersTest.java} | 26 +++++++------------ 3 files changed, 13 insertions(+), 21 deletions(-) rename src/main/java/org/opentripplanner/transit/model/network/grouppriority/{PriorityGroupMatcher.java => Matchers.java} (98%) rename src/test/java/org/opentripplanner/transit/model/network/grouppriority/{PriorityGroupMatcherTest.java => MatchersTest.java} (84%) diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java similarity index 98% rename from src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java rename to src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java index 50782093e5f..a82b34ac8ee 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcher.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java @@ -28,7 +28,7 @@ * a `CompositeMatcher`. So, a new matcher is only created if the field in the * select is present. */ -public final class PriorityGroupMatcher { +public final class Matchers { private static final Matcher NOOP = new Matcher() { @Override @@ -68,7 +68,7 @@ public static Matcher of(TransitGroupSelect select) { static Matcher[] of(Collection selectors) { return selectors .stream() - .map(PriorityGroupMatcher::of) + .map(Matchers::of) .filter(Predicate.not(Matcher::isEmpty)) .toArray(Matcher[]::new); } diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java index 4e9165944eb..220d6a2cc03 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java @@ -65,8 +65,8 @@ private TransitGroupPriorityService( Collection byAgency, Collection global ) { - this.agencyMatchers = PriorityGroupMatcher.of(byAgency); - this.globalMatchers = PriorityGroupMatcher.of(global); + this.agencyMatchers = Matchers.of(byAgency); + this.globalMatchers = Matchers.of(global); this.enabled = Stream.of(agencyMatchers, globalMatchers).anyMatch(ArrayUtils::hasContent); this.globalMatchersIds = Arrays.stream(globalMatchers).map(m -> new MatcherAndId(m, nextGroupId())).toList(); diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java similarity index 84% rename from src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java rename to src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java index 18941f8232c..15b26313f89 100644 --- a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/PriorityGroupMatcherTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java @@ -12,7 +12,7 @@ import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.TripPattern; -class PriorityGroupMatcherTest { +class MatchersTest { private final TestRouteData r1 = TestRouteData .rail("R1") @@ -35,7 +35,7 @@ class PriorityGroupMatcherTest { @Test void testMode() { - var m = PriorityGroupMatcher.of( + var m = Matchers.of( TransitGroupSelect.of().addModes(List.of(TransitMode.BUS, TransitMode.TRAM)).build() ); assertEquals("Mode(BUS | TRAM)", m.toString()); @@ -47,12 +47,8 @@ void testMode() { @Test void testAgencyIds() { - var m1 = PriorityGroupMatcher.of( - TransitGroupSelect.of().addAgencyIds(List.of(r1agencyId)).build() - ); - var m2 = PriorityGroupMatcher.of( - TransitGroupSelect.of().addAgencyIds(List.of(r1agencyId, anyId)).build() - ); + var m1 = Matchers.of(TransitGroupSelect.of().addAgencyIds(List.of(r1agencyId)).build()); + var m2 = Matchers.of(TransitGroupSelect.of().addAgencyIds(List.of(r1agencyId, anyId)).build()); var matchers = List.of(m1, m2); assertEquals("AgencyId(F:A1)", m1.toString()); @@ -68,12 +64,8 @@ void testAgencyIds() { @Test void routeIds() { - var m1 = PriorityGroupMatcher.of( - TransitGroupSelect.of().addRouteIds(List.of(r1routeId)).build() - ); - var m2 = PriorityGroupMatcher.of( - TransitGroupSelect.of().addRouteIds(List.of(r1routeId, anyId)).build() - ); + var m1 = Matchers.of(TransitGroupSelect.of().addRouteIds(List.of(r1routeId)).build()); + var m2 = Matchers.of(TransitGroupSelect.of().addRouteIds(List.of(r1routeId, anyId)).build()); var matchers = List.of(m1, m2); assertEquals("RouteId(F:R1)", m1.toString()); @@ -89,7 +81,7 @@ void routeIds() { @Test void testSubMode() { - var subject = PriorityGroupMatcher.of( + var subject = Matchers.of( TransitGroupSelect.of().addSubModeRegexp(List.of(".*local.*")).build() ); @@ -103,7 +95,7 @@ void testSubMode() { @Test void testAnd() { - var subject = PriorityGroupMatcher.of( + var subject = Matchers.of( TransitGroupSelect .of() .addSubModeRegexp(List.of("express")) @@ -125,7 +117,7 @@ void testAnd() { @Test void testToString() { - var subject = PriorityGroupMatcher.of( + var subject = Matchers.of( TransitGroupSelect .of() .addModes(List.of(TransitMode.BUS, TransitMode.TRAM)) From af2bff225dd27e33a33f78eb68a1cb827c7b1a30 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 00:44:19 +0200 Subject: [PATCH 079/192] refactor: Cleanup access modifiers in group-priority --- .../model/network/grouppriority/Matchers.java | 10 +++++----- .../grouppriority/TransitGroupPriority32n.java | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java index a82b34ac8ee..90fcae9c77a 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java @@ -28,7 +28,7 @@ * a `CompositeMatcher`. So, a new matcher is only created if the field in the * select is present. */ -public final class Matchers { +final class Matchers { private static final Matcher NOOP = new Matcher() { @Override @@ -42,7 +42,7 @@ public boolean isEmpty() { } }; - public static Matcher of(TransitGroupSelect select) { + static Matcher of(TransitGroupSelect select) { if (select.isEmpty()) { return NOOP; } @@ -119,7 +119,7 @@ private static final class RegExpMatcher implements Matcher { private final Pattern[] subModeRegexp; private final Function toValue; - public RegExpMatcher( + RegExpMatcher( String typeName, List subModeRegexp, Function toValue @@ -152,7 +152,7 @@ private static final class IdMatcher implements Matcher { private final Set ids; private final Function idProvider; - public IdMatcher( + IdMatcher( String typeName, List ids, Function idProvider @@ -181,7 +181,7 @@ private static final class AndMatcher implements Matcher { private final Matcher[] matchers; - public AndMatcher(List matchers) { + AndMatcher(List matchers) { this.matchers = matchers.toArray(Matcher[]::new); } diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java index 1bdff703e07..0243f64a206 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java @@ -4,7 +4,7 @@ * This is a "BitSet" implementation for groupId. It can store upto 32 groups, * a set with few elements does NOT dominate a set with more elements. */ -public class TransitGroupPriority32n { +class TransitGroupPriority32n { private static final int GROUP_ZERO = 0; private static final int MIN_SEQ_NO = 0; @@ -14,20 +14,15 @@ public class TransitGroupPriority32n { * Left dominate right, if right contains a group which does not exist in left. Left * do NOT dominate right if they are equals or left is a super set of right. */ - public static boolean dominate(int left, int right) { + static boolean dominate(int left, int right) { return ((left ^ right) & right) != 0; } - @Override - public String toString() { - return "TransitGroupPriority32n{}"; - } - /** * Use this method to map from a continuous group index [0..32) to the groupId used * during routing. The ID is implementation-specific and optimized for performance. */ - public static int groupId(final int priorityGroupIndex) { + static int groupId(final int priorityGroupIndex) { assertValidGroupSeqNo(priorityGroupIndex); return priorityGroupIndex == MIN_SEQ_NO ? GROUP_ZERO : 0x01 << (priorityGroupIndex - 1); } @@ -35,10 +30,15 @@ public static int groupId(final int priorityGroupIndex) { /** * Merge a groupId into a set of groupIds. */ - public static int mergeInGroupId(final int currentSetOfGroupIds, final int newGroupId) { + static int mergeInGroupId(final int currentSetOfGroupIds, final int newGroupId) { return currentSetOfGroupIds | newGroupId; } + @Override + public String toString() { + return "TransitGroupPriority32n{}"; + } + private static void assertValidGroupSeqNo(int priorityGroupIndex) { if (priorityGroupIndex < MIN_SEQ_NO) { throw new IllegalArgumentException( From 7f9e18652779fc8f00dcbf585b4e268772a3a106 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 00:54:04 +0200 Subject: [PATCH 080/192] refactor: Rename DefaultTransitGroupPriorityCalculator --- .../DefaultTransitGroupPriorityCalculator.java | 6 +++++- ....java => DefaultTransitGroupPriorityCalculatorTest.java} | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) rename src/test/java/org/opentripplanner/transit/model/network/grouppriority/{DefaultTransitGroupCalculatorTest.java => DefaultTransitGroupPriorityCalculatorTest.java} (95%) diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculator.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculator.java index d09d206adb0..df5d2abef8a 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculator.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculator.java @@ -3,7 +3,11 @@ import org.opentripplanner.raptor.api.model.DominanceFunction; import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; -public class DefaultTransitGroupPriorityCalculator implements RaptorTransitGroupPriorityCalculator { +/** + * Implement {@link RaptorTransitGroupPriorityCalculator}. + */ +public final class DefaultTransitGroupPriorityCalculator + implements RaptorTransitGroupPriorityCalculator { @Override public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupCalculatorTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculatorTest.java similarity index 95% rename from src/test/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupCalculatorTest.java rename to src/test/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculatorTest.java index 131bce04707..745442e3e7b 100644 --- a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupCalculatorTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/DefaultTransitGroupPriorityCalculatorTest.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test; -class DefaultTransitGroupCalculatorTest { +class DefaultTransitGroupPriorityCalculatorTest { private final DefaultTransitGroupPriorityCalculator subject = new DefaultTransitGroupPriorityCalculator(); From d683b22901fe037a3f3b4dd5a9b305e2133b23ac Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 01:20:56 +0200 Subject: [PATCH 081/192] feature: Prepare to support group-priority for trips --- .../model/TransitGroupPriorityReport.java | 2 +- .../RaptorRoutingRequestTransitData.java | 1 + .../network/grouppriority/EntityAdapter.java | 16 ++++ .../model/network/grouppriority/Matcher.java | 4 +- .../model/network/grouppriority/Matchers.java | 54 ++++++------ .../TransitGroupPriority32n.java | 9 +- .../TransitGroupPriorityService.java | 85 ++++++++++++------- .../grouppriority/TripPatternAdapter.java | 34 ++++++++ .../network/grouppriority/MatchersTest.java | 11 ++- .../TransitGroupPriorityServiceTest.java | 13 +-- 10 files changed, 151 insertions(+), 78 deletions(-) create mode 100644 src/main/java/org/opentripplanner/transit/model/network/grouppriority/EntityAdapter.java create mode 100644 src/main/java/org/opentripplanner/transit/model/network/grouppriority/TripPatternAdapter.java diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java index 67f7350ee14..66ad29fad56 100644 --- a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java +++ b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java @@ -17,7 +17,7 @@ public class TransitGroupPriorityReport { public static String build(Collection patterns, TransitRequest request) { - var service = TransitGroupPriorityService.of( + var service = new TransitGroupPriorityService( request.priorityGroupsByAgency(), request.priorityGroupsGlobal() ); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java index 83a29ee88d7..fa0676dcf94 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java @@ -250,6 +250,7 @@ private TransitGroupPriorityService createTransitGroupPriorityConfigurator(Route } var transitRequest = request.journey().transit(); return TransitGroupPriorityService.of( + request.preferences().transit().relaxTransitGroupPriority(), transitRequest.priorityGroupsByAgency(), transitRequest.priorityGroupsGlobal() ); diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/EntityAdapter.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/EntityAdapter.java new file mode 100644 index 00000000000..c6802562283 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/EntityAdapter.java @@ -0,0 +1,16 @@ +package org.opentripplanner.transit.model.network.grouppriority; + +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.framework.FeedScopedId; + +/** + * This is the keys used to group transit trips and trip-patterns. This is used to calculate a + * unique groupId based on the request config. We use the adapter pattern to be able to generate + * the groupId based on different input types (TripPattern and Trip). + */ +interface EntityAdapter { + TransitMode mode(); + String subMode(); + FeedScopedId agencyId(); + FeedScopedId routeId(); +} diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matcher.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matcher.java index d92ac9476a6..bb5b4075364 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matcher.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matcher.java @@ -1,9 +1,7 @@ package org.opentripplanner.transit.model.network.grouppriority; -import org.opentripplanner.transit.model.network.TripPattern; - interface Matcher { - boolean match(TripPattern pattern); + boolean match(EntityAdapter entity); default boolean isEmpty() { return false; diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java index 90fcae9c77a..6a22ea2172f 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java @@ -18,7 +18,6 @@ import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.model.network.TripPattern; /** * This class turns a {@link TransitGroupSelect} into a matcher. @@ -32,7 +31,7 @@ final class Matchers { private static final Matcher NOOP = new Matcher() { @Override - public boolean match(TripPattern pattern) { + public boolean match(EntityAdapter entity) { return false; } @@ -42,7 +41,7 @@ public boolean isEmpty() { } }; - static Matcher of(TransitGroupSelect select) { + public static Matcher of(TransitGroupSelect select) { if (select.isEmpty()) { return NOOP; } @@ -52,19 +51,18 @@ static Matcher of(TransitGroupSelect select) { list.add(new ModeMatcher(select.modes())); } if (!select.subModeRegexp().isEmpty()) { - list.add( - new RegExpMatcher("SubMode", select.subModeRegexp(), p -> p.getNetexSubmode().name()) - ); + list.add(new RegExpMatcher("SubMode", select.subModeRegexp(), EntityAdapter::subMode)); } if (!select.agencyIds().isEmpty()) { - list.add(new IdMatcher("Agency", select.agencyIds(), p -> p.getRoute().getAgency().getId())); + list.add(new IdMatcher("Agency", select.agencyIds(), EntityAdapter::agencyId)); } if (!select.routeIds().isEmpty()) { - list.add(new IdMatcher("Route", select.routeIds(), p -> p.getRoute().getId())); + list.add(new IdMatcher("Route", select.routeIds(), EntityAdapter::routeId)); } return andOf(list); } + @SuppressWarnings("unchecked") static Matcher[] of(Collection selectors) { return selectors .stream() @@ -103,8 +101,8 @@ public ModeMatcher(List modes) { } @Override - public boolean match(TripPattern pattern) { - return modes.contains(pattern.getMode()); + public boolean match(EntityAdapter entity) { + return modes.contains(entity.mode()); } @Override @@ -116,23 +114,23 @@ public String toString() { private static final class RegExpMatcher implements Matcher { private final String typeName; - private final Pattern[] subModeRegexp; - private final Function toValue; + private final Pattern[] patterns; + private final Function toValue; - RegExpMatcher( + public RegExpMatcher( String typeName, - List subModeRegexp, - Function toValue + List regexps, + Function toValue ) { this.typeName = typeName; - this.subModeRegexp = subModeRegexp.stream().map(Pattern::compile).toArray(Pattern[]::new); + this.patterns = regexps.stream().map(Pattern::compile).toArray(Pattern[]::new); this.toValue = toValue; } @Override - public boolean match(TripPattern pattern) { - var value = toValue.apply(pattern); - for (Pattern p : subModeRegexp) { + public boolean match(EntityAdapter entity) { + var value = toValue.apply(entity); + for (Pattern p : patterns) { if (p.matcher(value).matches()) { return true; } @@ -142,7 +140,7 @@ public boolean match(TripPattern pattern) { @Override public String toString() { - return typeName + "Regexp(" + arrayToString(OR, subModeRegexp) + ')'; + return typeName + "Regexp(" + arrayToString(OR, patterns) + ')'; } } @@ -150,12 +148,12 @@ private static final class IdMatcher implements Matcher { private final String typeName; private final Set ids; - private final Function idProvider; + private final Function idProvider; - IdMatcher( + public IdMatcher( String typeName, List ids, - Function idProvider + Function idProvider ) { this.typeName = typeName; this.ids = new HashSet<>(ids); @@ -163,8 +161,8 @@ private static final class IdMatcher implements Matcher { } @Override - public boolean match(TripPattern pattern) { - return ids.contains(idProvider.apply(pattern)); + public boolean match(EntityAdapter entity) { + return ids.contains(idProvider.apply(entity)); } @Override @@ -181,14 +179,14 @@ private static final class AndMatcher implements Matcher { private final Matcher[] matchers; - AndMatcher(List matchers) { + public AndMatcher(List matchers) { this.matchers = matchers.toArray(Matcher[]::new); } @Override - public boolean match(TripPattern pattern) { + public boolean match(EntityAdapter entity) { for (var m : matchers) { - if (!m.match(pattern)) { + if (!m.match(entity)) { return false; } } diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java index 0243f64a206..32423070e09 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriority32n.java @@ -1,7 +1,7 @@ package org.opentripplanner.transit.model.network.grouppriority; /** - * This is a "BitSet" implementation for groupId. It can store upto 32 groups, + * This is a "BitSet" implementation for groupId. It can store up to 31 groups, * a set with few elements does NOT dominate a set with more elements. */ class TransitGroupPriority32n { @@ -11,8 +11,11 @@ class TransitGroupPriority32n { private static final int MAX_SEQ_NO = 32; /** - * Left dominate right, if right contains a group which does not exist in left. Left - * do NOT dominate right if they are equals or left is a super set of right. + * Left dominates right: + * - if right contains a group which does not exist in the left. + * Left do NOT dominate right: + * - if they are equals or + * - left is a superset of right. */ static boolean dominate(int left, int right) { return ((left ^ right) & right) != 0; diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java index 220d6a2cc03..dab40533dba 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java @@ -8,38 +8,35 @@ import java.util.List; import java.util.stream.Stream; import org.opentripplanner.framework.lang.ArrayUtils; +import org.opentripplanner.routing.api.request.framework.CostLinearFunction; import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.TripPattern; /** - * This class dynamically builds an index of transit-group-ids from the - * provided {@link TransitGroupSelect}s while serving the caller with - * group-ids for each requested pattern. It is made for optimal - * performance, since it is used in request scope. + * This class dynamically builds an index of transit-group-ids from the provided + * {@link TransitGroupSelect}s while serving the caller with group-ids for each requested + * trip/pattern. It is made for optimal performance, since it is used in request scope. *

* THIS CLASS IS NOT THREAD-SAFE. */ public class TransitGroupPriorityService { /** + * IMPLEMENTATION DETAILS + * * There are two ways we can treat the base (local-traffic) transit priority group: *

    - *
  1. We can assign group id 1 (one) to the base group and it will be treated as any other group. - *
  2. We can assign group id 0 (zero) to the base and it will not be added to the set of groups - * a given path has. + *
  3. + * We can assign group id 1 (one) to the base group and it will be treated as any other group. + *
  4. + *
  5. + * We can assign group id 0 (zero) to the base and it will not be added to the set of groups + * a given path has. + *
  6. *
- * When we compare paths we compare sets of group ids. A set is dominating another set if it is + * When we compare paths, we compare sets of group ids. A set is dominating another set if it is * a smaller subset or different from the other set. - *

- * Example - base-group-id = 0 (zero) - *

- * Let B be the base and G be concrete group. Then: (B) dominates (G), (G) dominates (B), (B) - * dominates (BG), but (G) does not dominate (BG). In other words, paths with only agency - * X (group G) is not given an advantage in the routing over paths with a combination of agency - * X (group G) and local traffic (group B). - *

- * TODO: Experiment with base-group-id=0 and make it configurable. */ private static final int GROUP_INDEX_COUNTER_START = 1; @@ -61,7 +58,7 @@ private TransitGroupPriorityService() { this.globalMatchersIds = List.of(); } - private TransitGroupPriorityService( + public TransitGroupPriorityService( Collection byAgency, Collection global ) { @@ -79,13 +76,25 @@ public static TransitGroupPriorityService empty() { } public static TransitGroupPriorityService of( - Collection byAgency, - Collection global + CostLinearFunction relaxTransitGroupPriority, + List groupByAgency, + List groupGlobal ) { - if (Stream.of(byAgency, global).allMatch(Collection::isEmpty)) { - return empty(); + if (relaxTransitGroupPriority.isNormal()) { + return TransitGroupPriorityService.empty(); + } else if (Stream.of(groupByAgency, groupGlobal).allMatch(Collection::isEmpty)) { + return TransitGroupPriorityService.empty(); + } else { + return new TransitGroupPriorityService(groupByAgency, groupGlobal); } - return new TransitGroupPriorityService(byAgency, global); + } + + /** + * Return true is the feature is configured and the request a {@code relaxTransitGroupPriority} + * function. + */ + public boolean isEnabled() { + return enabled; } /** @@ -94,13 +103,23 @@ public static TransitGroupPriorityService of( * @throws IllegalArgumentException if more than 32 group-ids are requested. */ public int lookupTransitGroupPriorityId(TripPattern tripPattern) { - if (!enabled || tripPattern == null) { + return tripPattern == null + ? baseGroupId + : lookupTransitGroupPriorityId(new TripPatternAdapter(tripPattern)); + } + + /** + * Fetch/lookup the transit-group-id for the given entity. + *

+ * @throws IllegalArgumentException if more than 32 group-ids are requested. + */ + private int lookupTransitGroupPriorityId(EntityAdapter entity) { + if (!enabled) { return baseGroupId; } - for (var it : agencyMatchersIds) { - if (it.matcher().match(tripPattern)) { - var agencyId = tripPattern.getRoute().getAgency().getId(); + if (it.matcher().match(entity)) { + var agencyId = entity.agencyId(); int groupId = it.ids().get(agencyId); if (groupId < 0) { @@ -112,7 +131,7 @@ public int lookupTransitGroupPriorityId(TripPattern tripPattern) { } for (var it : globalMatchersIds) { - if (it.matcher.match(tripPattern)) { + if (it.matcher.match(entity)) { return it.groupId(); } } @@ -120,6 +139,10 @@ public int lookupTransitGroupPriorityId(TripPattern tripPattern) { return baseGroupId; } + /** + * This is the group-id assigned to all transit trips/patterns witch does not match a + * specific group. + */ public int baseGroupId() { return baseGroupId; } @@ -129,10 +152,10 @@ private int nextGroupId() { } /** Pair of matcher and groupId. Used only inside this class. */ - record MatcherAndId(Matcher matcher, int groupId) {} + private record MatcherAndId(Matcher matcher, int groupId) {} - /** Matcher with map of ids by agency. */ - record MatcherAgencyAndIds(Matcher matcher, TObjectIntMap ids) { + /** Matcher with a map of ids by agency. */ + private record MatcherAgencyAndIds(Matcher matcher, TObjectIntMap ids) { MatcherAgencyAndIds(Matcher matcher) { this( matcher, diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TripPatternAdapter.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TripPatternAdapter.java new file mode 100644 index 00000000000..223f163f535 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TripPatternAdapter.java @@ -0,0 +1,34 @@ +package org.opentripplanner.transit.model.network.grouppriority; + +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.TripPattern; + +class TripPatternAdapter implements EntityAdapter { + + private final TripPattern tripPattern; + + public TripPatternAdapter(TripPattern tripPattern) { + this.tripPattern = tripPattern; + } + + @Override + public TransitMode mode() { + return tripPattern.getMode(); + } + + @Override + public String subMode() { + return tripPattern.getNetexSubmode().name(); + } + + @Override + public FeedScopedId agencyId() { + return tripPattern.getRoute().getAgency().getId(); + } + + @Override + public FeedScopedId routeId() { + return tripPattern.getRoute().getId(); + } +} diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java index 15b26313f89..36901214d5b 100644 --- a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java @@ -10,7 +10,6 @@ import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.model.network.TripPattern; class MatchersTest { @@ -26,11 +25,11 @@ class MatchersTest { .withSubmode("localFerry") .build(); - private final TripPattern rail1 = r1.getTripPattern(); - private final TripPattern bus = b1.getTripPattern(); - private final TripPattern ferry = f1.getTripPattern(); - private final FeedScopedId r1agencyId = rail1.getRoute().getAgency().getId(); - private final FeedScopedId r1routeId = rail1.getRoute().getId(); + private final EntityAdapter rail1 = new TripPatternAdapter(r1.getTripPattern()); + private final EntityAdapter bus = new TripPatternAdapter(b1.getTripPattern()); + private final EntityAdapter ferry = new TripPatternAdapter(f1.getTripPattern()); + private final FeedScopedId r1agencyId = rail1.agencyId(); + private final FeedScopedId r1routeId = rail1.routeId(); private final FeedScopedId anyId = new FeedScopedId("F", "ANY"); @Test diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java index b81bc4ed15f..4ef17d7ce04 100644 --- a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java @@ -66,13 +66,14 @@ class TransitGroupPriorityServiceTest { private final TripPattern railR3 = routeR3.getTripPattern(); private final TripPattern ferryF3 = routeF3.getTripPattern(); private final TripPattern busB3 = routeB3.getTripPattern(); + private final TripPattern nullValue = null; @Test void emptyConfigurationShouldReturnGroupZero() { - var subject = TransitGroupPriorityService.of(List.of(), List.of()); + var subject = new TransitGroupPriorityService(List.of(), List.of()); assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(railR1)); assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(busB2)); - assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(null)); + assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(nullValue)); } @Test @@ -83,10 +84,10 @@ void lookupTransitGroupIdByAgency() { .build(); // Add matcher `byAgency` for bus and real - var subject = TransitGroupPriorityService.of(List.of(select), List.of()); + var subject = new TransitGroupPriorityService(List.of(select), List.of()); // Agency groups are indexed (group-id set) at request time - assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(null)); + assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullValue)); assertEquals(EXP_GROUP_1, subject.lookupTransitGroupPriorityId(busB2)); assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR3)); assertEquals(EXP_GROUP_3, subject.lookupTransitGroupPriorityId(railR1)); @@ -97,7 +98,7 @@ void lookupTransitGroupIdByAgency() { @Test void lookupTransitPriorityGroupIdByGlobalMode() { // Global groups are indexed (group-id set) at construction time - var subject = TransitGroupPriorityService.of( + var subject = new TransitGroupPriorityService( List.of(), List.of( TransitGroupSelect.of().addModes(List.of(TransitMode.BUS)).build(), @@ -105,7 +106,7 @@ void lookupTransitPriorityGroupIdByGlobalMode() { ) ); - assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(null)); + assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullValue)); assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR1)); assertEquals(EXP_GROUP_1, subject.lookupTransitGroupPriorityId(busB2)); assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR3)); From cea94e49a76c89f91b850045945e2c0c143a93f7 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 01:37:17 +0200 Subject: [PATCH 082/192] refactor: Create TransitGroupPriorityService in RoutingWorker and pass down This is preparing for using the service to decorate street only itineraries. --- .../flex/trip/ScheduledDeviatedTripTest.java | 6 ++++-- .../routing/algorithm/RoutingWorker.java | 9 +++++++++ .../raptoradapter/router/TransitRouter.java | 7 +++++++ .../RaptorRoutingRequestTransitData.java | 19 ++++--------------- ...rRoutingRequestTransitDataCreatorTest.java | 3 ++- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripTest.java index 3971d03ac6d..6466d7bf6d6 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripTest.java @@ -41,14 +41,15 @@ import org.opentripplanner.street.search.request.StreetSearchRequest; import org.opentripplanner.street.search.state.State; import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; import org.opentripplanner.transit.model.site.AreaStop; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitModel; /** * This tests that the feed for the Cobb County Flex service is processed correctly. This service - * contains both flex zones but also scheduled stops. Inside the zone passengers can get on or off - * anywhere so there it works more like a taxi. + * contains both flex zones but also scheduled stops. Inside the zone, passengers can get on or off + * anywhere, so there it works more like a taxi. *

* Read about the details at: https://www.cobbcounty.org/transportation/cobblinc/routes-and-schedules/flex */ @@ -212,6 +213,7 @@ private static List getItineraries( var result = TransitRouter.route( request, serverContext, + TransitGroupPriorityService.empty(), transitStartOfTime, additionalSearchDays, new DebugTimingAggregator() diff --git a/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java b/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java index 06ceaeebeb2..a6218e1720a 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java @@ -36,6 +36,7 @@ import org.opentripplanner.routing.framework.DebugTimingAggregator; import org.opentripplanner.service.paging.PagingService; import org.opentripplanner.standalone.api.OtpServerRequestContext; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,6 +65,7 @@ public class RoutingWorker { */ private final ZonedDateTime transitSearchTimeZero; private final AdditionalSearchDays additionalSearchDays; + private final TransitGroupPriorityService transitGroupPriorityService; private SearchParams raptorSearchParamsUsed = null; private PageCursorInput pageCursorInput = null; @@ -79,6 +81,12 @@ public RoutingWorker(OtpServerRequestContext serverContext, RouteRequest request this.transitSearchTimeZero = ServiceDateUtils.asStartOfService(request.dateTime(), zoneId); this.additionalSearchDays = createAdditionalSearchDays(serverContext.raptorTuningParameters(), zoneId, request); + this.transitGroupPriorityService = + TransitGroupPriorityService.of( + request.preferences().transit().relaxTransitGroupPriority(), + request.journey().transit().priorityGroupsByAgency(), + request.journey().transit().priorityGroupsGlobal() + ); } public RoutingResponse route() { @@ -258,6 +266,7 @@ private Void routeTransit(List itineraries, Collection var transitResults = TransitRouter.route( request, serverContext, + transitGroupPriorityService, transitSearchTimeZero, additionalSearchDays, debugTimingAggregator diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java index 06f4ff1cf45..37bb7270902 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java @@ -41,6 +41,7 @@ import org.opentripplanner.routing.framework.DebugTimingAggregator; import org.opentripplanner.standalone.api.OtpServerRequestContext; import org.opentripplanner.street.search.TemporaryVerticesContainer; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; public class TransitRouter { @@ -48,6 +49,7 @@ public class TransitRouter { private final RouteRequest request; private final OtpServerRequestContext serverContext; + private final TransitGroupPriorityService transitGroupPriorityService; private final DebugTimingAggregator debugTimingAggregator; private final ZonedDateTime transitSearchTimeZero; private final AdditionalSearchDays additionalSearchDays; @@ -56,12 +58,14 @@ public class TransitRouter { private TransitRouter( RouteRequest request, OtpServerRequestContext serverContext, + TransitGroupPriorityService transitGroupPriorityService, ZonedDateTime transitSearchTimeZero, AdditionalSearchDays additionalSearchDays, DebugTimingAggregator debugTimingAggregator ) { this.request = request; this.serverContext = serverContext; + this.transitGroupPriorityService = transitGroupPriorityService; this.transitSearchTimeZero = transitSearchTimeZero; this.additionalSearchDays = additionalSearchDays; this.debugTimingAggregator = debugTimingAggregator; @@ -71,6 +75,7 @@ private TransitRouter( public static TransitRouterResult route( RouteRequest request, OtpServerRequestContext serverContext, + TransitGroupPriorityService priorityGroupConfigurator, ZonedDateTime transitSearchTimeZero, AdditionalSearchDays additionalSearchDays, DebugTimingAggregator debugTimingAggregator @@ -78,6 +83,7 @@ public static TransitRouterResult route( TransitRouter transitRouter = new TransitRouter( request, serverContext, + priorityGroupConfigurator, transitSearchTimeZero, additionalSearchDays, debugTimingAggregator @@ -309,6 +315,7 @@ private RaptorRoutingRequestTransitData createRequestTransitDataProvider( ) { return new RaptorRoutingRequestTransitData( transitLayer, + transitGroupPriorityService, transitSearchTimeZero, additionalSearchDays.additionalSearchDaysInPast(), additionalSearchDays.additionalSearchDaysInFuture(), diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java index fa0676dcf94..66e29b392d4 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java @@ -34,7 +34,7 @@ /** * This is the data provider for the Range Raptor search engine. It uses data from the TransitLayer, - * but filters it by dates and modes per request. Transfers durations are pre-calculated per request + * but filters it by dates and modes per request. Transfer durations are pre-calculated per request * based on walk speed. */ public class RaptorRoutingRequestTransitData implements RaptorTransitDataProvider { @@ -72,6 +72,7 @@ public class RaptorRoutingRequestTransitData implements RaptorTransitDataProvide public RaptorRoutingRequestTransitData( TransitLayer transitLayer, + TransitGroupPriorityService transitGroupPriorityService, ZonedDateTime transitSearchTimeZero, int additionalPastSearchDays, int additionalFutureSearchDays, @@ -83,7 +84,7 @@ public RaptorRoutingRequestTransitData( this.transitSearchTimeZero = transitSearchTimeZero; // Delegate to the creator to construct the needed data structures. The code is messy so - // it is nice to NOT have it in the class. It isolate this code to only be available at + // it is nice to NOT have it in the class. It isolates this code to only be available at // the time of construction var transitDataCreator = new RaptorRoutingRequestTransitDataCreator( transitLayer, @@ -93,7 +94,7 @@ public RaptorRoutingRequestTransitData( additionalPastSearchDays, additionalFutureSearchDays, filter, - createTransitGroupPriorityConfigurator(request) + transitGroupPriorityService ); this.patternIndex = transitDataCreator.createPatternIndex(tripPatterns); this.activeTripPatternsPerStop = transitDataCreator.createTripPatternsPerStop(tripPatterns); @@ -243,16 +244,4 @@ public RaptorConstrainedBoardingSearch transferConstraintsReverseS } return new ConstrainedBoardingSearch(false, toStopTransfers, fromStopTransfers); } - - private TransitGroupPriorityService createTransitGroupPriorityConfigurator(RouteRequest request) { - if (request.preferences().transit().relaxTransitGroupPriority().isNormal()) { - return TransitGroupPriorityService.empty(); - } - var transitRequest = request.journey().transit(); - return TransitGroupPriorityService.of( - request.preferences().transit().relaxTransitGroupPriority(), - transitRequest.priorityGroupsByAgency(), - transitRequest.priorityGroupsGlobal() - ); - } } diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java index c66caa05985..ea815a2f47f 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreatorTest.java @@ -61,7 +61,8 @@ public void testMergeTripPatterns() { tripPatternsForDates.add(new TripPatternForDate(tripPattern1, tripTimes, List.of(), third)); tripPatternsForDates.add(new TripPatternForDate(tripPattern3, tripTimes, List.of(), third)); - // Patterns containing trip schedules for all 3 days. Trip schedules for later days are offset in time when requested. + // Patterns containing trip schedules for all 3 days. Trip schedules for later days are offset + // in time when requested. List combinedTripPatterns = RaptorRoutingRequestTransitDataCreator.merge( startOfTime, tripPatternsForDates, From 90213eff461b7b70dc6a8f6c8ee153731ef6b2f0 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 01:39:31 +0200 Subject: [PATCH 083/192] refactor: Cleanup on Itinerary and Leg --- .../opentripplanner/model/plan/Itinerary.java | 203 +++++++++--------- .../org/opentripplanner/model/plan/Leg.java | 1 + 2 files changed, 100 insertions(+), 104 deletions(-) diff --git a/src/main/java/org/opentripplanner/model/plan/Itinerary.java b/src/main/java/org/opentripplanner/model/plan/Itinerary.java index cb14227e83d..dee80addd91 100644 --- a/src/main/java/org/opentripplanner/model/plan/Itinerary.java +++ b/src/main/java/org/opentripplanner/model/plan/Itinerary.java @@ -177,11 +177,6 @@ public Leg lastLeg() { return getLegs().get(getLegs().size() - 1); } - /** Get the first transit leg if one exist */ - public Optional firstTransitLeg() { - return getLegs().stream().filter(TransitLeg.class::isInstance).findFirst(); - } - /** * An itinerary can be flagged for removal with a system notice. *

@@ -225,105 +220,6 @@ public Itinerary withTimeShiftToStartAt(ZonedDateTime afterTime) { return newItin; } - /** @see #equals(Object) */ - @Override - public final int hashCode() { - return super.hashCode(); - } - - /** - * Return {@code true} it the other object is the same object using the {@link - * Object#equals(Object)}. An itinerary is a temporary object and the equals method should not be - * used for comparision of 2 instances, only to check that to objects are the same instance. - */ - @Override - public final boolean equals(Object o) { - return super.equals(o); - } - - /** - * Used to convert a list of itineraries to a SHORT human-readable string. - * - * @see #toStr() - *

- * It is great for comparing lists of itineraries in a test: {@code - * assertEquals(toStr(List.of(it1)), toStr(result))}. - */ - public static String toStr(List list) { - return list.stream().map(Itinerary::toStr).collect(Collectors.joining(", ")); - } - - @Override - public String toString() { - return ToStringBuilder - .of(Itinerary.class) - .addStr("from", firstLeg().getFrom().toStringShort()) - .addStr("to", lastLeg().getTo().toStringShort()) - .addTime("start", firstLeg().getStartTime()) - .addTime("end", lastLeg().getEndTime()) - .addNum("nTransfers", numberOfTransfers) - .addDuration("duration", duration) - .addDuration("nonTransitTime", nonTransitDuration) - .addDuration("transitTime", transitDuration) - .addDuration("waitingTime", waitingDuration) - .addNum("generalizedCost", generalizedCost, UNKNOWN) - .addNum("generalizedCost2", generalizedCost2) - .addNum("waitTimeOptimizedCost", waitTimeOptimizedCost, UNKNOWN) - .addNum("transferPriorityCost", transferPriorityCost, UNKNOWN) - .addNum("nonTransitDistance", nonTransitDistanceMeters, "m") - .addBool("tooSloped", tooSloped) - .addNum("elevationLost", elevationLost, 0.0) - .addNum("elevationGained", elevationGained, 0.0) - .addCol("legs", legs) - .addObj("fare", fare) - .addObj("emissionsPerPerson", emissionsPerPerson) - .toString(); - } - - /** - * Used to convert an itinerary to a SHORT human readable string - including just a few of the - * most important fields. It is much shorter and easier to read then the {@link - * Itinerary#toString()}. - *

- * It is great for comparing to itineraries in a test: {@code assertEquals(toStr(it1), - * toStr(it2))}. - *

- * Example: {@code A ~ Walk 2m ~ B ~ BUS 55 12:04 12:14 ~ C [cost: 1066]} - *

- * Reads: Start at A, walk 2 minutes to stop B, take bus 55, board at 12:04 and alight at 12:14 - * ... - */ - public String toStr() { - // No translater needed, stop indexes are never passed to the builder - PathStringBuilder buf = new PathStringBuilder(null); - buf.stop(firstLeg().getFrom().name.toString()); - - for (Leg leg : legs) { - if (leg.isWalkingLeg()) { - buf.walk((int) leg.getDuration().toSeconds()); - } else if (leg instanceof TransitLeg transitLeg) { - buf.transit( - transitLeg.getMode().name(), - transitLeg.getTrip().logName(), - transitLeg.getStartTime(), - transitLeg.getEndTime() - ); - } else if (leg instanceof StreetLeg streetLeg) { - buf.street(streetLeg.getMode().name(), leg.getStartTime(), leg.getEndTime()); - } - buf.stop(leg.getTo().name.toString()); - } - - // The generalizedCost2 is printed as is, it is a special cost and the scale depends on the - // use-case. - buf.summary( - RaptorCostConverter.toRaptorCost(generalizedCost), - getGeneralizedCost2().orElse(RaptorConstants.NOT_SET) - ); - - return buf.toString(); - } - /** Total duration of the itinerary in seconds */ public Duration getDuration() { return duration; @@ -698,6 +594,105 @@ public Duration walkDuration() { return walkDuration; } + /** @see #equals(Object) */ + @Override + public final int hashCode() { + return super.hashCode(); + } + + /** + * Return {@code true} it the other object is the same object using the {@link + * Object#equals(Object)}. An itinerary is a temporary object and the equals method should not be + * used for comparision of 2 instances, only to check that to objects are the same instance. + */ + @Override + public final boolean equals(Object o) { + return super.equals(o); + } + + @Override + public String toString() { + return ToStringBuilder + .of(Itinerary.class) + .addStr("from", firstLeg().getFrom().toStringShort()) + .addStr("to", lastLeg().getTo().toStringShort()) + .addTime("start", firstLeg().getStartTime()) + .addTime("end", lastLeg().getEndTime()) + .addNum("nTransfers", numberOfTransfers) + .addDuration("duration", duration) + .addDuration("nonTransitTime", nonTransitDuration) + .addDuration("transitTime", transitDuration) + .addDuration("waitingTime", waitingDuration) + .addNum("generalizedCost", generalizedCost, UNKNOWN) + .addNum("generalizedCost2", generalizedCost2) + .addNum("waitTimeOptimizedCost", waitTimeOptimizedCost, UNKNOWN) + .addNum("transferPriorityCost", transferPriorityCost, UNKNOWN) + .addNum("nonTransitDistance", nonTransitDistanceMeters, "m") + .addBool("tooSloped", tooSloped) + .addNum("elevationLost", elevationLost, 0.0) + .addNum("elevationGained", elevationGained, 0.0) + .addCol("legs", legs) + .addObj("fare", fare) + .addObj("emissionsPerPerson", emissionsPerPerson) + .toString(); + } + + /** + * Used to convert a list of itineraries to a SHORT human-readable string. + * + * @see #toStr() + *

+ * It is great for comparing lists of itineraries in a test: {@code + * assertEquals(toStr(List.of(it1)), toStr(result))}. + */ + public static String toStr(List list) { + return list.stream().map(Itinerary::toStr).collect(Collectors.joining(", ")); + } + + /** + * Used to convert an itinerary to a SHORT human readable string - including just a few of the + * most important fields. It is much shorter and easier to read then the {@link + * Itinerary#toString()}. + *

+ * It is great for comparing to itineraries in a test: {@code assertEquals(toStr(it1), + * toStr(it2))}. + *

+ * Example: {@code A ~ Walk 2m ~ B ~ BUS 55 12:04 12:14 ~ C [cost: 1066]} + *

+ * Reads: Start at A, walk 2 minutes to stop B, take bus 55, board at 12:04 and alight at 12:14 + * ... + */ + public String toStr() { + // No translater needed, stop indexes are never passed to the builder + PathStringBuilder buf = new PathStringBuilder(null); + buf.stop(firstLeg().getFrom().name.toString()); + + for (Leg leg : legs) { + if (leg.isWalkingLeg()) { + buf.walk((int) leg.getDuration().toSeconds()); + } else if (leg instanceof TransitLeg transitLeg) { + buf.transit( + transitLeg.getMode().name(), + transitLeg.getTrip().logName(), + transitLeg.getStartTime(), + transitLeg.getEndTime() + ); + } else if (leg instanceof StreetLeg streetLeg) { + buf.street(streetLeg.getMode().name(), leg.getStartTime(), leg.getEndTime()); + } + buf.stop(leg.getTo().name.toString()); + } + + // The generalizedCost2 is printed as is, it is a special cost and the scale depends on the + // use-case. + buf.summary( + RaptorCostConverter.toRaptorCost(generalizedCost), + getGeneralizedCost2().orElse(RaptorConstants.NOT_SET) + ); + + return buf.toString(); + } + private static int penaltyCost(TimeAndCost penalty) { return penalty.cost().toSeconds(); } diff --git a/src/main/java/org/opentripplanner/model/plan/Leg.java b/src/main/java/org/opentripplanner/model/plan/Leg.java index 1ee72761d66..2a0b6726560 100644 --- a/src/main/java/org/opentripplanner/model/plan/Leg.java +++ b/src/main/java/org/opentripplanner/model/plan/Leg.java @@ -184,6 +184,7 @@ default Route getRoute() { /** * For transit legs, the trip. For non-transit legs, null. */ + @Nullable default Trip getTrip() { return null; } From cefcc05f3fbeb2ed4e27bd9b77a9a398a0743688 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 01:44:11 +0200 Subject: [PATCH 084/192] feature: Add c2 for street itineraries if transit-group-priority is used --- ...ransitGroupPriorityItineraryDecorator.java | 49 +++++++++++++++++++ .../routing/algorithm/RoutingWorker.java | 4 ++ .../TransitGroupPriorityService.java | 10 ++++ .../network/grouppriority/TripAdapter.java | 34 +++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 src/main/java/org/opentripplanner/model/plan/grouppriority/TransitGroupPriorityItineraryDecorator.java create mode 100644 src/main/java/org/opentripplanner/transit/model/network/grouppriority/TripAdapter.java diff --git a/src/main/java/org/opentripplanner/model/plan/grouppriority/TransitGroupPriorityItineraryDecorator.java b/src/main/java/org/opentripplanner/model/plan/grouppriority/TransitGroupPriorityItineraryDecorator.java new file mode 100644 index 00000000000..80b7b39bd0c --- /dev/null +++ b/src/main/java/org/opentripplanner/model/plan/grouppriority/TransitGroupPriorityItineraryDecorator.java @@ -0,0 +1,49 @@ +package org.opentripplanner.model.plan.grouppriority; + +import java.util.Collection; +import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.model.plan.Leg; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator; +import org.opentripplanner.transit.model.network.grouppriority.DefaultTransitGroupPriorityCalculator; +import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService; + +/** + * This class will set the {@link Itinerary#getGeneralizedCost2()} value if the feature is + * enabled and no such value is set. The AStar router does not produce itineraries with this, + * so we decorate itineraries with this here to make sure the `c2` is set correct and can be + * used in the itinerary-filter-chain. + */ +public class TransitGroupPriorityItineraryDecorator { + + private final TransitGroupPriorityService priorityGroupConfigurator; + private final RaptorTransitGroupPriorityCalculator transitGroupCalculator; + + public TransitGroupPriorityItineraryDecorator( + TransitGroupPriorityService priorityGroupConfigurator + ) { + this.priorityGroupConfigurator = priorityGroupConfigurator; + this.transitGroupCalculator = new DefaultTransitGroupPriorityCalculator(); + } + + public void decorate(Collection itineraries) { + if (!priorityGroupConfigurator.isEnabled()) { + return; + } + for (Itinerary it : itineraries) { + decorate(it); + } + } + + public void decorate(Itinerary itinerary) { + if (itinerary.getGeneralizedCost2().isEmpty() && priorityGroupConfigurator.isEnabled()) { + int c2 = priorityGroupConfigurator.baseGroupId(); + for (Leg leg : itinerary.getLegs()) { + if (leg.getTrip() != null) { + int newGroupId = priorityGroupConfigurator.lookupTransitGroupPriorityId(leg.getTrip()); + c2 = transitGroupCalculator.mergeGroupIds(c2, newGroupId); + } + } + itinerary.setGeneralizedCost2(c2); + } + } +} diff --git a/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java b/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java index a6218e1720a..7ac3dd1caf6 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/RoutingWorker.java @@ -16,6 +16,7 @@ import org.opentripplanner.framework.application.OTPRequestTimeoutException; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.plan.Itinerary; +import org.opentripplanner.model.plan.grouppriority.TransitGroupPriorityItineraryDecorator; import org.opentripplanner.model.plan.paging.cursor.PageCursorInput; import org.opentripplanner.raptor.api.request.RaptorTuningParameters; import org.opentripplanner.raptor.api.request.SearchParams; @@ -130,6 +131,9 @@ public RoutingResponse route() { routeTransit(itineraries, routingErrors); } + // Set C2 value for Street and FLEX if transit-group-priority is used + new TransitGroupPriorityItineraryDecorator(transitGroupPriorityService).decorate(itineraries); + debugTimingAggregator.finishedRouting(); // Filter itineraries diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java index dab40533dba..048b2279a88 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityService.java @@ -12,6 +12,7 @@ import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.Trip; /** * This class dynamically builds an index of transit-group-ids from the provided @@ -108,6 +109,15 @@ public int lookupTransitGroupPriorityId(TripPattern tripPattern) { : lookupTransitGroupPriorityId(new TripPatternAdapter(tripPattern)); } + /** + * Fetch/lookup the transit-group-id for the given trip. + *

+ * @throws IllegalArgumentException if more than 32 group-ids are requested. + */ + public int lookupTransitGroupPriorityId(Trip trip) { + return trip == null ? baseGroupId : lookupTransitGroupPriorityId(new TripAdapter(trip)); + } + /** * Fetch/lookup the transit-group-id for the given entity. *

diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TripAdapter.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TripAdapter.java new file mode 100644 index 00000000000..7ff1d158e62 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/TripAdapter.java @@ -0,0 +1,34 @@ +package org.opentripplanner.transit.model.network.grouppriority; + +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.timetable.Trip; + +class TripAdapter implements EntityAdapter { + + private final Trip trip; + + public TripAdapter(Trip trip) { + this.trip = trip; + } + + @Override + public TransitMode mode() { + return trip.getMode(); + } + + @Override + public String subMode() { + return trip.getNetexSubMode().name(); + } + + @Override + public FeedScopedId agencyId() { + return trip.getRoute().getAgency().getId(); + } + + @Override + public FeedScopedId routeId() { + return trip.getRoute().getId(); + } +} From de7c6989662343fd777a7c67a5ac754f2101d0dc Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jul 2024 00:01:58 +0200 Subject: [PATCH 085/192] test: Add missing tests to o.o.transit.model.network.grouppriority --- .../model/network/grouppriority/Matchers.java | 32 ++++++++++------- .../network/grouppriority/MatchersTest.java | 8 +++++ .../TransitGroupPriorityServiceTest.java | 21 +++++++++--- .../grouppriority/TripAdapterTest.java | 34 +++++++++++++++++++ 4 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 src/test/java/org/opentripplanner/transit/model/network/grouppriority/TripAdapterTest.java diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java index 6a22ea2172f..7e1e7e6853a 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/Matchers.java @@ -29,19 +29,9 @@ */ final class Matchers { - private static final Matcher NOOP = new Matcher() { - @Override - public boolean match(EntityAdapter entity) { - return false; - } + private static final Matcher NOOP = new EmptyMatcher(); - @Override - public boolean isEmpty() { - return true; - } - }; - - public static Matcher of(TransitGroupSelect select) { + static Matcher of(TransitGroupSelect select) { if (select.isEmpty()) { return NOOP; } @@ -92,6 +82,24 @@ private static Matcher andOf(List list) { return new AndMatcher(list); } + private static final class EmptyMatcher implements Matcher { + + @Override + public boolean match(EntityAdapter entity) { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public String toString() { + return "Empty"; + } + } + private static final class ModeMatcher implements Matcher { private final Set modes; diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java index 36901214d5b..cdda3755d37 100644 --- a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/MatchersTest.java @@ -32,6 +32,14 @@ class MatchersTest { private final FeedScopedId r1routeId = rail1.routeId(); private final FeedScopedId anyId = new FeedScopedId("F", "ANY"); + @Test + void testEmptySelect() { + var m = Matchers.of(TransitGroupSelect.of().build()); + assertEquals("Empty", m.toString()); + assertTrue(m.isEmpty()); + assertFalse(m.match(bus)); + } + @Test void testMode() { var m = Matchers.of( diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java index 4ef17d7ce04..993eb14c424 100644 --- a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java @@ -12,6 +12,7 @@ import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.timetable.Trip; class TransitGroupPriorityServiceTest { @@ -66,14 +67,15 @@ class TransitGroupPriorityServiceTest { private final TripPattern railR3 = routeR3.getTripPattern(); private final TripPattern ferryF3 = routeF3.getTripPattern(); private final TripPattern busB3 = routeB3.getTripPattern(); - private final TripPattern nullValue = null; + private final TripPattern nullTripPattern = null; + private final Trip nullTrip = null; @Test void emptyConfigurationShouldReturnGroupZero() { - var subject = new TransitGroupPriorityService(List.of(), List.of()); + var subject = TransitGroupPriorityService.empty(); assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(railR1)); assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(busB2)); - assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(nullValue)); + assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(nullTripPattern)); } @Test @@ -87,12 +89,17 @@ void lookupTransitGroupIdByAgency() { var subject = new TransitGroupPriorityService(List.of(select), List.of()); // Agency groups are indexed (group-id set) at request time - assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullValue)); + assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullTripPattern)); assertEquals(EXP_GROUP_1, subject.lookupTransitGroupPriorityId(busB2)); assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR3)); assertEquals(EXP_GROUP_3, subject.lookupTransitGroupPriorityId(railR1)); assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(busB3)); assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(ferryF3)); + + // Verify we get the same result with using the trip, not trip-pattern + assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullTrip)); + assertEquals(EXP_GROUP_1, subject.lookupTransitGroupPriorityId(busB2.getTrip(0))); + assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR3.getTrip(0))); } @Test @@ -106,12 +113,16 @@ void lookupTransitPriorityGroupIdByGlobalMode() { ) ); - assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullValue)); + assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullTripPattern)); assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR1)); assertEquals(EXP_GROUP_1, subject.lookupTransitGroupPriorityId(busB2)); assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR3)); assertEquals(EXP_GROUP_1, subject.lookupTransitGroupPriorityId(busB3)); assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(ferryF3)); + + // Verify we get the same result with using the trip, not trip-pattern + assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullTrip)); + assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR1.getTrip(0))); } private static TestRouteData route( diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TripAdapterTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TripAdapterTest.java new file mode 100644 index 00000000000..c1aeeea1a59 --- /dev/null +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TripAdapterTest.java @@ -0,0 +1,34 @@ +package org.opentripplanner.transit.model.network.grouppriority; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.timetable.Trip; + +class TripAdapterTest { + + private final Trip trip = TransitModelForTest.trip("Trip").build(); + + private final TripAdapter subject = new TripAdapter(trip); + + @Test + void mode() { + assertEquals(trip.getMode(), subject.mode()); + } + + @Test + void subMode() { + assertEquals(trip.getNetexSubMode().name(), subject.subMode()); + } + + @Test + void agencyId() { + assertEquals(trip.getRoute().getAgency().getId(), subject.agencyId()); + } + + @Test + void routeId() { + assertEquals(trip.getRoute().getId(), subject.routeId()); + } +} From 361ff9130c759254e98a2091c80dd312795c68d2 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jul 2024 16:28:27 +0200 Subject: [PATCH 086/192] Move helper code into utils --- .../apis/gtfs/datafetchers/QueryTypeImpl.java | 4 ++-- .../opentripplanner/apis/gtfs/datafetchers/RouteImpl.java | 4 ++-- .../time/LocalDateRangeUtil.java} | 4 ++-- .../apis/gtfs/mapping/LocalDateRangeMapperTest.java | 5 +++-- 4 files changed, 9 insertions(+), 8 deletions(-) rename src/main/java/org/opentripplanner/apis/gtfs/{mapping/LocalDateRangeMapper.java => support/time/LocalDateRangeUtil.java} (86%) 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 b22310d1da3..d0963361d11 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -31,9 +31,9 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLQueryTypeStopsByRadiusArgs; -import org.opentripplanner.apis.gtfs.mapping.LocalDateRangeMapper; import org.opentripplanner.apis.gtfs.mapping.routerequest.LegacyRouteRequestMapper; import org.opentripplanner.apis.gtfs.mapping.routerequest.RouteRequestMapper; +import org.opentripplanner.apis.gtfs.support.time.LocalDateRangeUtil; import org.opentripplanner.ext.fares.impl.DefaultFareService; import org.opentripplanner.ext.fares.impl.GtfsFaresService; import org.opentripplanner.ext.fares.model.FareRuleSet; @@ -612,7 +612,7 @@ public DataFetcher> routes() { ); } - if (LocalDateRangeMapper.hasServiceDateFilter(args.getGraphQLServiceDates())) { + if (LocalDateRangeUtil.hasServiceDateFilter(args.getGraphQLServiceDates())) { var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService); routeStream = filter.filterRoutes(routeStream).stream(); } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java index 921f85ff813..a3f557951f0 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/RouteImpl.java @@ -15,7 +15,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; import org.opentripplanner.apis.gtfs.mapping.BikesAllowedMapper; -import org.opentripplanner.apis.gtfs.mapping.LocalDateRangeMapper; +import org.opentripplanner.apis.gtfs.support.time.LocalDateRangeUtil; import org.opentripplanner.routing.alertpatch.EntitySelector; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.services.TransitAlertService; @@ -182,7 +182,7 @@ public DataFetcher> patterns() { var args = new GraphQLTypes.GraphQLRoutePatternsArgs(environment.getArguments()); - if (LocalDateRangeMapper.hasServiceDateFilter(args.getGraphQLServiceDates())) { + if (LocalDateRangeUtil.hasServiceDateFilter(args.getGraphQLServiceDates())) { var filter = new PatternByServiceDatesFilter(args.getGraphQLServiceDates(), transitService); return filter.filterPatterns(patterns); } else { diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/support/time/LocalDateRangeUtil.java similarity index 86% rename from src/main/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapper.java rename to src/main/java/org/opentripplanner/apis/gtfs/support/time/LocalDateRangeUtil.java index 56064ea37dc..b4545f10658 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/support/time/LocalDateRangeUtil.java @@ -1,9 +1,9 @@ -package org.opentripplanner.apis.gtfs.mapping; +package org.opentripplanner.apis.gtfs.support.time; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.model.LocalDateRange; -public class LocalDateRangeMapper { +public class LocalDateRangeUtil { /** * Checks if a service date filter input has at least one filter set. If both start and end are diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java index eba38c0a180..81b3b283151 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.support.time.LocalDateRangeUtil; class LocalDateRangeMapperTest { @@ -25,7 +26,7 @@ public static List noFilterCases() { @ParameterizedTest @MethodSource("noFilterCases") void hasNoServiceDateFilter(GraphQLTypes.GraphQLLocalDateRangeInput input) { - assertFalse(LocalDateRangeMapper.hasServiceDateFilter(input)); + assertFalse(LocalDateRangeUtil.hasServiceDateFilter(input)); } public static List> hasFilterCases() { @@ -36,6 +37,6 @@ public static List> hasFilterCases() { @MethodSource("hasFilterCases") void hasServiceDateFilter(Map params) { var input = new GraphQLTypes.GraphQLLocalDateRangeInput(params); - assertTrue(LocalDateRangeMapper.hasServiceDateFilter(input)); + assertTrue(LocalDateRangeUtil.hasServiceDateFilter(input)); } } From 364d33e1f45f1ab065b2c5efb30e2ab6e10d05ac Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Fri, 5 Jul 2024 17:04:39 +0200 Subject: [PATCH 087/192] Update src/main/java/org/opentripplanner/transit/model/network/grouppriority/EntityAdapter.java Co-authored-by: Leonard Ehrenfried --- .../transit/model/network/grouppriority/EntityAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/EntityAdapter.java b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/EntityAdapter.java index c6802562283..760da3d87ca 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/grouppriority/EntityAdapter.java +++ b/src/main/java/org/opentripplanner/transit/model/network/grouppriority/EntityAdapter.java @@ -4,7 +4,7 @@ import org.opentripplanner.transit.model.framework.FeedScopedId; /** - * This is the keys used to group transit trips and trip-patterns. This is used to calculate a + * These are the keys used to group transit trips and trip-patterns. This is used to calculate a * unique groupId based on the request config. We use the adapter pattern to be able to generate * the groupId based on different input types (TripPattern and Trip). */ From b6de8304340acb952d88a46cd9589f569d3c7db9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Jul 2024 22:46:04 +0200 Subject: [PATCH 088/192] Rename method --- .../apis/gtfs/PatternByServiceDatesFilterTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java index 57f399ad821..f01bac12006 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilterTest.java @@ -122,7 +122,7 @@ static List ranges() { @ParameterizedTest @MethodSource("ranges") void filterPatterns(LocalDate start, LocalDate end, FilterExpectation expectation) { - var filter = mockFilter(start, end); + var filter = defaultFilter(start, end); var filterInput = List.of(PATTERN_1); var filterOutput = filter.filterPatterns(filterInput); @@ -137,7 +137,7 @@ void filterPatterns(LocalDate start, LocalDate end, FilterExpectation expectatio @ParameterizedTest @MethodSource("ranges") void filterRoutes(LocalDate start, LocalDate end, FilterExpectation expectation) { - var filter = mockFilter(start, end); + var filter = defaultFilter(start, end); var filterInput = List.of(ROUTE_1); var filterOutput = filter.filterRoutes(filterInput.stream()); @@ -149,7 +149,7 @@ void filterRoutes(LocalDate start, LocalDate end, FilterExpectation expectation) } } - private static PatternByServiceDatesFilter mockFilter(LocalDate start, LocalDate end) { + private static PatternByServiceDatesFilter defaultFilter(LocalDate start, LocalDate end) { return new PatternByServiceDatesFilter( new LocalDateRange(start, end), route -> List.of(PATTERN_1), From 614434fa0b772aea3378b1ee9e2c1ae53f611bec Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Jul 2024 22:54:34 +0200 Subject: [PATCH 089/192] Move code inside date range in order to remove feature envy --- .../apis/gtfs/PatternByServiceDatesFilter.java | 11 +---------- .../apis/gtfs/model/LocalDateRange.java | 14 +++++++++++++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java index 1f2ef45dfd5..8eecfe6273b 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/PatternByServiceDatesFilter.java @@ -82,16 +82,7 @@ private boolean hasServicesOnDate(TripPattern pattern) { .anyMatch(trip -> { var dates = getServiceDatesForTrip.apply(trip); - return dates - .stream() - .anyMatch(date -> - ( - range.startInclusive() == null || - date.isEqual(range.startInclusive()) || - date.isAfter(range.startInclusive()) - ) && - (range.endExclusive() == null || date.isBefore(range.endExclusive())) - ); + return dates.stream().anyMatch(range::contains); }); } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java b/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java index 83e0c93b5bd..d466534e65c 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java @@ -15,9 +15,21 @@ public boolean unlimited() { } /** - * Is the start date before the end ( + * Is the start date before the end? */ public boolean startBeforeEnd() { return startInclusive != null && endExclusive != null && startInclusive.isAfter(endExclusive); } + + /** + * Is the given LocalDate instance inside of this date range? + */ + public boolean contains(LocalDate date) { + return ( + ( + startInclusive() == null || date.isEqual(startInclusive()) || date.isAfter(startInclusive()) + ) && + (endExclusive() == null || date.isBefore(endExclusive())) + ); + } } From 4b55d2a2da5c25a6494a795f1251a6858be32acd Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Mon, 8 Jul 2024 11:19:51 +0200 Subject: [PATCH 090/192] Remove reference to TimetableSnapshot in TransitModelIndex --- .../service/DefaultTransitService.java | 35 ++++++++++++------- .../transit/service/TransitModel.java | 1 + .../transit/service/TransitModelIndex.java | 19 ---------- .../transit/service/TransitService.java | 20 +++++++++++ .../service/DefaultTransitServiceTest.java | 21 +++++++++++ 5 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index ca88b7d3130..9edb9d5647a 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -9,6 +9,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -16,6 +17,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.annotation.Nullable; import org.locationtech.jts.geom.Envelope; import org.opentripplanner.ext.flex.FlexIndex; import org.opentripplanner.framework.application.OTPRequestTimeoutException; @@ -398,15 +400,23 @@ public List stopTimesForPatternAtStop( /** * Returns all the patterns for a specific stop. If includeRealtimeUpdates is set, new patterns * added by realtime updates are added to the collection. + * A set is used here because trip patterns + * that were updated by realtime data is both part of the TransitModelIndex and the TimetableSnapshot */ @Override public Collection getPatternsForStop( StopLocation stop, boolean includeRealtimeUpdates ) { - return transitModel - .getTransitModelIndex() - .getPatternsForStop(stop, includeRealtimeUpdates ? lazyGetTimeTableSnapShot() : null); + Set tripPatterns = new HashSet<>(getPatternsForStop(stop)); + + if (includeRealtimeUpdates) { + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + tripPatterns.addAll(currentSnapshot.getPatternsForStop(stop)); + } + } + return tripPatterns; } @Override @@ -434,28 +444,28 @@ public GroupOfRoutes getGroupOfRoutesForId(FeedScopedId id) { @Override public Timetable getTimetableForTripPattern(TripPattern tripPattern, LocalDate serviceDate) { OTPRequestTimeoutException.checkForTimeout(); - TimetableSnapshot timetableSnapshot = lazyGetTimeTableSnapShot(); - return timetableSnapshot != null - ? timetableSnapshot.resolve(tripPattern, serviceDate) + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + return currentSnapshot != null + ? currentSnapshot.resolve(tripPattern, serviceDate) : tripPattern.getScheduledTimetable(); } @Override public TripPattern getRealtimeAddedTripPattern(FeedScopedId tripId, LocalDate serviceDate) { - TimetableSnapshot timetableSnapshot = lazyGetTimeTableSnapShot(); - if (timetableSnapshot == null) { + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot == null) { return null; } - return timetableSnapshot.getRealtimeAddedTripPattern(tripId, serviceDate); + return currentSnapshot.getRealtimeAddedTripPattern(tripId, serviceDate); } @Override public boolean hasRealtimeAddedTripPatterns() { - TimetableSnapshot timetableSnapshot = lazyGetTimeTableSnapShot(); - if (timetableSnapshot == null) { + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot == null) { return false; } - return timetableSnapshot.hasRealtimeAddedTripPatterns(); + return currentSnapshot.hasRealtimeAddedTripPatterns(); } /** @@ -463,6 +473,7 @@ public boolean hasRealtimeAddedTripPatterns() { * * @return The same TimetableSnapshot is returned throughout the lifecycle of this object. */ + @Nullable private TimetableSnapshot lazyGetTimeTableSnapShot() { if (this.timetableSnapshot == null) { timetableSnapshot = transitModel.getTimetableSnapshot(); diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModel.java b/src/main/java/org/opentripplanner/transit/service/TransitModel.java index d5211d5dd2e..8fb8da5e391 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModel.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModel.java @@ -169,6 +169,7 @@ public void index() { } } + @Nullable public TimetableSnapshot getTimetableSnapshot() { return timetableSnapshotProvider == null ? null diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java b/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java index 84759ffae72..468fc72942e 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java @@ -15,7 +15,6 @@ import org.opentripplanner.ext.flex.FlexIndex; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.framework.application.OTPFeature; -import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.model.calendar.CalendarService; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.GroupOfRoutes; @@ -154,24 +153,6 @@ public Collection getTripsForStop(StopLocation stop) { .collect(Collectors.toList()); } - /** - * Returns all the patterns for a specific stop. If timetableSnapshot is included, new patterns - * added by realtime updates are added to the collection. A set is used here because trip patterns - * that were updated by realtime data is both part of the TransitModelIndex and the TimetableSnapshot. - */ - public Collection getPatternsForStop( - StopLocation stop, - TimetableSnapshot timetableSnapshot - ) { - Set tripPatterns = new HashSet<>(getPatternsForStop(stop)); - - if (timetableSnapshot != null) { - tripPatterns.addAll(timetableSnapshot.getPatternsForStop(stop)); - } - - return tripPatterns; - } - /** * Get a list of all operators spanning across all feeds. */ diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index 83b65c44d12..5cedfd91f9c 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -93,8 +93,16 @@ public interface TransitService { Set getRoutesForStop(StopLocation stop); + /** + * Return all the scheduled trip patterns for a specific stop + * (not taking into account real-time updates). + */ Collection getPatternsForStop(StopLocation stop); + /** + * Returns all the patterns for a specific stop. If includeRealtimeUpdates is set, new patterns + * added by realtime updates are added to the collection. + */ Collection getPatternsForStop(StopLocation stop, boolean includeRealtimeUpdates); Collection getTripsForStop(StopLocation stop); @@ -127,8 +135,16 @@ public interface TransitService { Collection getAllRoutes(); + /** + * Return the scheduled trip pattern for a given trip (not taking into account real-time updates) + */ TripPattern getPatternForTrip(Trip trip); + /** + * Return the trip pattern for a given trip on a service date. The real-time updated version + * is returned if it exists, otherwise the scheduled trip pattern is returned. + * + */ TripPattern getPatternForTrip(Trip trip, LocalDate serviceDate); Collection getPatternsForRoute(Route route); @@ -167,6 +183,10 @@ List stopTimesForPatternAtStop( GroupOfRoutes getGroupOfRoutesForId(FeedScopedId id); + /** + * Return the timetable for a given trip pattern and date, taking into account real-time updates. + * If no real-times update are applied, fall back to scheduled data. + */ Timetable getTimetableForTripPattern(TripPattern tripPattern, LocalDate serviceDate); TripPattern getRealtimeAddedTripPattern(FeedScopedId tripId, LocalDate serviceDate); diff --git a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index f04fe782a0a..0cf72dd4748 100644 --- a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -6,10 +6,13 @@ import static org.opentripplanner.transit.model.basic.TransitMode.RAIL; import static org.opentripplanner.transit.model.basic.TransitMode.TRAM; +import com.google.common.collect.ImmutableSetMultimap; import java.util.Collection; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.network.TripPattern; @@ -46,6 +49,12 @@ static void setup() { transitModel.addTripPattern(RAIL_PATTERN.getId(), RAIL_PATTERN); transitModel.index(); + transitModel.initTimetableSnapshotProvider(() -> { + TimetableSnapshot timetableSnapshot = new TimetableSnapshot(); + timetableSnapshot.setPatternsForStop(ImmutableSetMultimap.of(STOP_B, BUS_PATTERN)); + return timetableSnapshot; + }); + service = new DefaultTransitService(transitModel) { @Override @@ -76,4 +85,16 @@ void stationModes() { var modes = service.getModesOfStopLocationsGroup(STATION); assertEquals(List.of(RAIL, FERRY, TRAM), modes); } + + @Test + void getPatternForStopsWithoutRealTime() { + Collection patternsForStop = service.getPatternsForStop(STOP_B, false); + assertEquals(Set.of(FERRY_PATTERN, RAIL_PATTERN), patternsForStop); + } + + @Test + void getPatternForStopsWithRealTime() { + Collection patternsForStop = service.getPatternsForStop(STOP_B, true); + assertEquals(Set.of(FERRY_PATTERN, RAIL_PATTERN, BUS_PATTERN), patternsForStop); + } } From b617f95f7e3a525fd7299eff3dd433fbe28a934e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 8 Jul 2024 11:18:50 +0200 Subject: [PATCH 091/192] Rework test --- .../apis/gtfs/model/LocalDateRangeTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/opentripplanner/apis/gtfs/model/LocalDateRangeTest.java b/src/test/java/org/opentripplanner/apis/gtfs/model/LocalDateRangeTest.java index e675a4c8772..5d079f47459 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/model/LocalDateRangeTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/model/LocalDateRangeTest.java @@ -1,6 +1,7 @@ package org.opentripplanner.apis.gtfs.model; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.LocalDate; import org.junit.jupiter.api.Test; @@ -10,13 +11,14 @@ class LocalDateRangeTest { private static final LocalDate DATE = LocalDate.parse("2024-06-01"); @Test - void unlimited() { + void limited() { assertFalse(new LocalDateRange(DATE, DATE).unlimited()); + assertFalse(new LocalDateRange(DATE, null).unlimited()); + assertFalse(new LocalDateRange(null, DATE).unlimited()); } @Test - void limited() { - assertFalse(new LocalDateRange(DATE, null).unlimited()); - assertFalse(new LocalDateRange(null, DATE).unlimited()); + void unlimited() { + assertTrue(new LocalDateRange(null, null).unlimited()); } } From dbdc80bf95aabdd3468a0df6fe6f2bd56b3456bd Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 3 Jul 2024 16:06:10 +0200 Subject: [PATCH 092/192] Parse parking lots from NeTEx --- .../netex/index/NetexEntityIndex.java | 14 +++- .../api/NetexEntityIndexReadOnlyView.java | 4 + .../netex/loader/parser/NetexParser.java | 6 +- .../netex/loader/parser/SiteFrameParser.java | 12 ++- .../netex/mapping/NetexMapper.java | 8 ++ .../netex/mapping/VehicleParkingMapper.java | 78 +++++++++++++++++++ .../vehicle_parking/VehicleParking.java | 1 + 7 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java diff --git a/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java b/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java index 0ca734d5b6a..9841765f3a3 100644 --- a/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java +++ b/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java @@ -1,6 +1,9 @@ package org.opentripplanner.netex.index; +import com.google.common.collect.ImmutableSet; import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import org.opentripplanner.netex.index.api.NetexEntityIndexReadOnlyView; import org.opentripplanner.netex.index.api.ReadOnlyHierarchicalMap; import org.opentripplanner.netex.index.api.ReadOnlyHierarchicalMapById; @@ -28,6 +31,7 @@ import org.rutebanken.netex.model.OperatingDay; import org.rutebanken.netex.model.OperatingPeriod_VersionStructure; import org.rutebanken.netex.model.Operator; +import org.rutebanken.netex.model.Parking; import org.rutebanken.netex.model.Quay; import org.rutebanken.netex.model.Route; import org.rutebanken.netex.model.ServiceJourney; @@ -97,8 +101,9 @@ public class NetexEntityIndex { public final HierarchicalVersionMapById stopPlaceById; public final HierarchicalVersionMapById tariffZonesById; public final HierarchicalMapById brandingById; + public final Set parkings; - // Relations between entities - The Netex XML sometimes rely on the the + // Relations between entities - The Netex XML sometimes relies on the // nested structure of the XML document, rater than explicit references. // Since we throw away the document we need to keep track of these. @@ -142,6 +147,7 @@ public NetexEntityIndex() { this.tariffZonesById = new HierarchicalVersionMapById<>(); this.brandingById = new HierarchicalMapById<>(); this.timeZone = new HierarchicalElement<>(); + this.parkings = new HashSet<>(0); } /** @@ -184,6 +190,7 @@ public NetexEntityIndex(NetexEntityIndex parent) { this.tariffZonesById = new HierarchicalVersionMapById<>(parent.tariffZonesById); this.brandingById = new HierarchicalMapById<>(parent.brandingById); this.timeZone = new HierarchicalElement<>(parent.timeZone); + this.parkings = new HashSet<>(parent.parkings); } /** @@ -353,6 +360,11 @@ public ReadOnlyHierarchicalVersionMapById getStopPlaceById() { return stopPlaceById; } + @Override + public ImmutableSet getParkings() { + return ImmutableSet.copyOf(parkings); + } + @Override public ReadOnlyHierarchicalVersionMapById getTariffZonesById() { return tariffZonesById; diff --git a/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java b/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java index 3c7bc98b36a..9229f40c7f8 100644 --- a/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java +++ b/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java @@ -1,5 +1,6 @@ package org.opentripplanner.netex.index.api; +import com.google.common.collect.ImmutableSet; import java.util.Collection; import org.rutebanken.netex.model.Authority; import org.rutebanken.netex.model.Branding; @@ -19,6 +20,7 @@ import org.rutebanken.netex.model.OperatingDay; import org.rutebanken.netex.model.OperatingPeriod_VersionStructure; import org.rutebanken.netex.model.Operator; +import org.rutebanken.netex.model.Parking; import org.rutebanken.netex.model.Quay; import org.rutebanken.netex.model.Route; import org.rutebanken.netex.model.ServiceJourney; @@ -80,6 +82,8 @@ public interface NetexEntityIndexReadOnlyView { ReadOnlyHierarchicalVersionMapById getStopPlaceById(); + ImmutableSet getParkings(); + ReadOnlyHierarchicalVersionMapById getTariffZonesById(); ReadOnlyHierarchicalMapById getBrandingById(); diff --git a/src/main/java/org/opentripplanner/netex/loader/parser/NetexParser.java b/src/main/java/org/opentripplanner/netex/loader/parser/NetexParser.java index 54b0043e072..3c24562ef6f 100644 --- a/src/main/java/org/opentripplanner/netex/loader/parser/NetexParser.java +++ b/src/main/java/org/opentripplanner/netex/loader/parser/NetexParser.java @@ -16,7 +16,7 @@ abstract class NetexParser { /** - * Currently a lot of elements on a frame is skipped. If any of these elements are pressent we + * Currently a lot of elements on a frame is skipped. If any of these elements are present we * print a warning for elements that might be relevant for OTP and an info message for none * relevant elements. */ @@ -39,10 +39,10 @@ static void verifyCommonUnusedPropertiesIsNotSet(Logger log, VersionFrame_Versio /** * Log a warning for Netex elements which is not mapped. There might be something wrong with the * data or there might be something wrong with the Netex data import(ignoring these elements). The - * element should be relevant to OTP. OTP do not support Netex 100%, but elements in Nordic + * element should be relevant to OTP. OTP does not support NeTEx 100%, but elements in the Nordic * profile, see https://enturas.atlassian.net/wiki/spaces/PUBLIC/overview should be supported. *

- * If you get this warning and think the element should be mapped, please feel free to report an + * If you see this warning and think the element should be mapped, please feel free to report an * issue on GitHub. */ static void warnOnMissingMapping(Logger log, Object rel) { diff --git a/src/main/java/org/opentripplanner/netex/loader/parser/SiteFrameParser.java b/src/main/java/org/opentripplanner/netex/loader/parser/SiteFrameParser.java index 8cbe0c8aee6..7651bbb39d9 100644 --- a/src/main/java/org/opentripplanner/netex/loader/parser/SiteFrameParser.java +++ b/src/main/java/org/opentripplanner/netex/loader/parser/SiteFrameParser.java @@ -9,6 +9,7 @@ import org.opentripplanner.netex.support.JAXBUtils; import org.rutebanken.netex.model.FlexibleStopPlace; import org.rutebanken.netex.model.GroupOfStopPlaces; +import org.rutebanken.netex.model.Parking; import org.rutebanken.netex.model.Quay; import org.rutebanken.netex.model.Quays_RelStructure; import org.rutebanken.netex.model.Site_VersionFrameStructure; @@ -35,6 +36,7 @@ class SiteFrameParser extends NetexParser { private final Collection tariffZones = new ArrayList<>(); private final Collection quays = new ArrayList<>(); + private final Collection parkings = new ArrayList<>(0); @Override public void parse(Site_VersionFrameStructure frame) { @@ -51,6 +53,10 @@ public void parse(Site_VersionFrameStructure frame) { if (frame.getTariffZones() != null) { parseTariffZones(frame.getTariffZones().getTariffZone()); } + + if (frame.getParkings() != null) { + parseParkings(frame.getParkings().getParking()); + } // Keep list sorted alphabetically warnOnMissingMapping(LOG, frame.getAccesses()); warnOnMissingMapping(LOG, frame.getAddresses()); @@ -59,7 +65,6 @@ public void parse(Site_VersionFrameStructure frame) { warnOnMissingMapping(LOG, frame.getCheckConstraintDelays()); warnOnMissingMapping(LOG, frame.getCheckConstraintThroughputs()); warnOnMissingMapping(LOG, frame.getNavigationPaths()); - warnOnMissingMapping(LOG, frame.getParkings()); warnOnMissingMapping(LOG, frame.getPathJunctions()); warnOnMissingMapping(LOG, frame.getPathLinks()); warnOnMissingMapping(LOG, frame.getPointsOfInterest()); @@ -79,6 +84,7 @@ void setResultOnIndex(NetexEntityIndex netexIndex) { netexIndex.stopPlaceById.addAll(stopPlaces); netexIndex.tariffZonesById.addAll(tariffZones); netexIndex.quayById.addAll(quays); + netexIndex.parkings.addAll(parkings); } private void parseFlexibleStopPlaces(Collection flexibleStopPlacesList) { @@ -89,6 +95,10 @@ private void parseGroupsOfStopPlaces(Collection groupsOfStopP groupsOfStopPlaces.addAll(groupsOfStopPlacesList); } + private void parseParkings(List parking) { + parkings.addAll(parking); + } + private void parseStopPlaces(List> stopPlaceList) { for (JAXBElement jaxBStopPlace : stopPlaceList) { StopPlace stopPlace = (StopPlace) jaxBStopPlace.getValue(); diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index c3c9ad2d0ae..85ab33fabc5 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -200,6 +200,8 @@ public void mapNetexToOtp(NetexEntityIndexReadOnlyView netexIndex) { mapNoticeAssignments(); addEntriesToGroupMapperForPostProcessingLater(); + + mapParkings(); } /* PRIVATE METHODS */ @@ -511,6 +513,12 @@ private void mapNoticeAssignments() { } } + private void mapParkings() { + var mapper = new VehicleParkingMapper(idFactory); + var parkingLots = mapper.map(currentNetexIndex.getParkings()); + System.out.print(parkingLots); + } + private void addEntriesToGroupMapperForPostProcessingLater() { if (level != 0) { groupMapper.addInterchange( diff --git a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java new file mode 100644 index 00000000000..11003ea2561 --- /dev/null +++ b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java @@ -0,0 +1,78 @@ +package org.opentripplanner.netex.mapping; + +import static org.rutebanken.netex.model.ParkingVehicleEnumeration.CYCLE; +import static org.rutebanken.netex.model.ParkingVehicleEnumeration.E_CYCLE; +import static org.rutebanken.netex.model.ParkingVehicleEnumeration.PEDAL_CYCLE; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; +import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.rutebanken.netex.model.Parking; +import org.rutebanken.netex.model.ParkingVehicleEnumeration; + +/** + * Maps from NeTEx Parking to an internal {@link VehicleParking}. + */ +class VehicleParkingMapper { + + private final FeedScopedIdFactory idFactory; + + private static final Set BICYCLE_TYPES = Set.of( + PEDAL_CYCLE, + E_CYCLE, + CYCLE + ); + + public VehicleParkingMapper(FeedScopedIdFactory idFactory) { + this.idFactory = idFactory; + } + + Collection map(Collection parkings) { + return parkings.stream().map(this::map).collect(Collectors.toUnmodifiableSet()); + } + + VehicleParking map(Parking parking) { + return VehicleParking + .builder() + .id(idFactory.createId(parking.getId())) + .name(NonLocalizedString.ofNullable(parking.getName().getValue())) + .coordinate(WgsCoordinateMapper.mapToDomain(parking.getCentroid())) + .capacity(mapCapacity(parking)) + .entrance(mapEntrance(parking)) + .build(); + } + + private VehicleParking.VehicleParkingEntranceCreator mapEntrance(Parking parking) { + return builder -> + builder + .entranceId(idFactory.createId(parking.getId() + "/entrance")) + .coordinate(WgsCoordinateMapper.mapToDomain(parking.getCentroid())) + .walkAccessible(true) + .carAccessible(true); + } + + private static VehicleParkingSpaces mapCapacity(Parking parking) { + var builder = VehicleParkingSpaces.builder(); + + // we assume that if we have bicycle in vehicle types it's a bicycle parking lot + // it's not possible in NeTEx to split the spaces between the types + var hasBikes = hasBikes(parking); + int capacity = parking.getTotalCapacity().intValue(); + + if (hasBikes) { + builder.bicycleSpaces(capacity); + } else { + builder.carSpaces(capacity); + } + + return builder.carSpaces(capacity).build(); + } + + private static boolean hasBikes(Parking parking) { + return parking.getParkingVehicleTypes().stream().anyMatch(BICYCLE_TYPES::contains); + } +} diff --git a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java index 6ebd34e1287..d5dfc4ce8c1 100644 --- a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java +++ b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java @@ -308,6 +308,7 @@ public boolean equals(Object o) { public String toString() { return ToStringBuilder .of(VehicleParking.class) + .addStr("id", id.toString()) .addStr("name", name.toString()) .addObj("coordinate", coordinate) .toString(); From 8550febea5b78272777faa34f1e4a67039d2650e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 3 Jul 2024 17:29:05 +0200 Subject: [PATCH 093/192] Flesh out parsing, add test --- .../VehicleParkingGroupsLayerTest.java | 4 +- .../VehicleParkingsLayerTest.java | 4 +- .../opentripplanner/netex/NetexBundle.java | 7 +- .../netex/configure/NetexConfigure.java | 7 +- .../netex/mapping/NetexMapper.java | 8 +- .../netex/mapping/VehicleParkingMapper.java | 11 +-- .../vehicle_parking/VehicleParkingSpaces.java | 11 +++ .../opentripplanner/ConstantsForTests.java | 7 +- .../mapping/VehicleParkingMapperTest.java | 86 +++++++++++++++++++ 9 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java index 60ba9100f43..1beca037457 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java @@ -125,8 +125,8 @@ public void vehicleParkingGroupGeometryTest() { assertEquals("[POINT (1.1 1.9)]", geometries.toString()); assertEquals( - "VehicleParkingAndGroup[vehicleParkingGroup=VehicleParkingGroup{name: 'groupName', coordinate: (1.9, 1.1)}, vehicleParking=[VehicleParking{name: 'name', coordinate: (2.0, 1.0)}]]", - geometries.get(0).getUserData().toString() + "VehicleParkingAndGroup[vehicleParkingGroup=VehicleParkingGroup{name: 'groupName', coordinate: (1.9, 1.1)}, vehicleParking=[VehicleParking{id: 'F:id', name: 'name', coordinate: (2.0, 1.0)}]]", + geometries.getFirst().getUserData().toString() ); } diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingsLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingsLayerTest.java index fdb723b3dc7..14e96e2aa28 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingsLayerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingsLayerTest.java @@ -117,8 +117,8 @@ public void vehicleParkingGeometryTest() { assertEquals("[POINT (1 2)]", geometries.toString()); assertEquals( - "VehicleParking{name: 'default name', coordinate: (2.0, 1.0)}", - geometries.get(0).getUserData().toString() + "VehicleParking{id: 'F:id', name: 'default name', coordinate: (2.0, 1.0)}", + geometries.getFirst().getUserData().toString() ); } diff --git a/src/main/java/org/opentripplanner/netex/NetexBundle.java b/src/main/java/org/opentripplanner/netex/NetexBundle.java index 8d6f098de89..c0da007bb10 100644 --- a/src/main/java/org/opentripplanner/netex/NetexBundle.java +++ b/src/main/java/org/opentripplanner/netex/NetexBundle.java @@ -17,6 +17,7 @@ import org.opentripplanner.netex.loader.parser.NetexDocumentParser; import org.opentripplanner.netex.mapping.NetexMapper; import org.opentripplanner.netex.validation.Validator; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.transit.model.framework.Deduplicator; import org.rutebanken.netex.model.PublicationDeliveryStructure; import org.slf4j.Logger; @@ -46,6 +47,7 @@ public class NetexBundle implements Closeable { private final double maxStopToShapeSnapDistance; private final boolean noTransfersOnIsolatedStops; private final boolean ignoreFareFrame; + private final VehicleParkingService parkingService; /** The NeTEx entities loaded from the input files and passed on to the mapper. */ private NetexEntityIndex index = new NetexEntityIndex(); /** Report errors to issue store */ @@ -59,6 +61,7 @@ public NetexBundle( CompositeDataSource source, NetexDataSourceHierarchy hierarchy, OtpTransitServiceBuilder transitBuilder, + VehicleParkingService parkingService, Set ferryIdsNotAllowedForBicycle, double maxStopToShapeSnapDistance, boolean noTransfersOnIsolatedStops, @@ -68,6 +71,7 @@ public NetexBundle( this.source = source; this.hierarchy = hierarchy; this.transitBuilder = transitBuilder; + this.parkingService = parkingService; this.ferryIdsNotAllowedForBicycle = ferryIdsNotAllowedForBicycle; this.maxStopToShapeSnapDistance = maxStopToShapeSnapDistance; this.noTransfersOnIsolatedStops = noTransfersOnIsolatedStops; @@ -93,7 +97,8 @@ public OtpTransitServiceBuilder loadBundle( issueStore, ferryIdsNotAllowedForBicycle, maxStopToShapeSnapDistance, - noTransfersOnIsolatedStops + noTransfersOnIsolatedStops, + parkingService ); // Load data diff --git a/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java b/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java index 464c03f28e1..7224d243d77 100644 --- a/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java +++ b/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java @@ -11,6 +11,7 @@ import org.opentripplanner.netex.config.NetexFeedParameters; import org.opentripplanner.netex.loader.NetexDataSourceHierarchy; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.standalone.config.BuildConfig; import org.opentripplanner.transit.service.TransitModel; @@ -46,7 +47,7 @@ public NetexModule createNetexModule( transitModel.getStopModel(), issueStore ); - netexBundles.add(netexBundle(transitServiceBuilder, it)); + netexBundles.add(netexBundle(transitServiceBuilder, it, graph.getVehicleParkingService())); } return new NetexModule( @@ -62,7 +63,8 @@ public NetexModule createNetexModule( /** public to enable testing */ public NetexBundle netexBundle( OtpTransitServiceBuilder transitServiceBuilder, - ConfiguredDataSource configuredDataSource + ConfiguredDataSource configuredDataSource, + VehicleParkingService parkingService ) { var source = (CompositeDataSource) configuredDataSource.dataSource(); var config = configuredDataSource.config(); @@ -72,6 +74,7 @@ public NetexBundle netexBundle( source, hierarchy(source, config), transitServiceBuilder, + parkingService, config.ferryIdsNotAllowedForBicycle(), buildParams.maxStopToShapeSnapDistance, config.noTransfersOnIsolatedStops(), diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index 85ab33fabc5..d99a315a39b 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -19,6 +19,7 @@ import org.opentripplanner.netex.mapping.calendar.CalendarServiceBuilder; import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; import org.opentripplanner.netex.mapping.support.NetexMapperIndexes; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.Deduplicator; @@ -68,6 +69,7 @@ public class NetexMapper { private final Set ferryIdsNotAllowedForBicycle; private final double maxStopToShapeSnapDistance; private final boolean noTransfersOnIsolatedStops; + private final VehicleParkingService parkingService; /** Map entries that cross reference entities within a group/operator, for example Interchanges. */ private GroupNetexMapper groupMapper; @@ -94,7 +96,8 @@ public NetexMapper( DataImportIssueStore issueStore, Set ferryIdsNotAllowedForBicycle, double maxStopToShapeSnapDistance, - boolean noTransfersOnIsolatedStops + boolean noTransfersOnIsolatedStops, + VehicleParkingService parkingService ) { this.transitBuilder = transitBuilder; this.deduplicator = deduplicator; @@ -103,6 +106,7 @@ public NetexMapper( this.ferryIdsNotAllowedForBicycle = ferryIdsNotAllowedForBicycle; this.noTransfersOnIsolatedStops = noTransfersOnIsolatedStops; this.maxStopToShapeSnapDistance = maxStopToShapeSnapDistance; + this.parkingService = parkingService; this.calendarServiceBuilder = new CalendarServiceBuilder(idFactory); this.tripCalendarBuilder = new TripCalendarBuilder(this.calendarServiceBuilder, issueStore); } @@ -516,7 +520,7 @@ private void mapNoticeAssignments() { private void mapParkings() { var mapper = new VehicleParkingMapper(idFactory); var parkingLots = mapper.map(currentNetexIndex.getParkings()); - System.out.print(parkingLots); + parkingService.updateVehicleParking(parkingLots, List.of()); } private void addEntriesToGroupMapperForPostProcessingLater() { diff --git a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java index 11003ea2561..a6ee863596e 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java @@ -42,6 +42,8 @@ VehicleParking map(Parking parking) { .name(NonLocalizedString.ofNullable(parking.getName().getValue())) .coordinate(WgsCoordinateMapper.mapToDomain(parking.getCentroid())) .capacity(mapCapacity(parking)) + .bicyclePlaces(hasBikes(parking)) + .carPlaces(!hasBikes(parking)) .entrance(mapEntrance(parking)) .build(); } @@ -57,13 +59,12 @@ private VehicleParking.VehicleParkingEntranceCreator mapEntrance(Parking parking private static VehicleParkingSpaces mapCapacity(Parking parking) { var builder = VehicleParkingSpaces.builder(); - - // we assume that if we have bicycle in vehicle types it's a bicycle parking lot - // it's not possible in NeTEx to split the spaces between the types - var hasBikes = hasBikes(parking); int capacity = parking.getTotalCapacity().intValue(); - if (hasBikes) { + // we assume that if we have bicycle in vehicle types it's a bicycle parking lot + // it's not possible in NeTEx to split the spaces between the types, so if you want that + // you have to define two parking lots with the same coordinates + if (hasBikes(parking)) { builder.bicycleSpaces(capacity); } else { builder.carSpaces(capacity); diff --git a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingSpaces.java b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingSpaces.java index de25fc75521..8b21cc21f2c 100644 --- a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingSpaces.java +++ b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingSpaces.java @@ -2,6 +2,7 @@ import java.io.Serializable; import java.util.Objects; +import org.opentripplanner.framework.tostring.ToStringBuilder; /** * The number of spaces by type. {@code null} if unknown. @@ -70,6 +71,16 @@ public boolean equals(Object o) { ); } + @Override + public String toString() { + return ToStringBuilder + .of(VehicleParkingSpaces.class) + .addNum("carSpaces", carSpaces) + .addNum("wheelchairAccessibleCarSpaces", wheelchairAccessibleCarSpaces) + .addNum("bicycleSpaces", bicycleSpaces) + .toString(); + } + public static class VehicleParkingSpacesBuilder { private Integer bicycleSpaces; diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index e74c66e527b..b002d7a3c41 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -31,6 +31,7 @@ import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.linking.LinkingDirection; import org.opentripplanner.routing.linking.VertexLinker; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; @@ -102,7 +103,8 @@ public static NetexBundle createMinimalNetexNordicBundle() { var configuredDataSource = new ConfiguredDataSource<>(dataSource, buildConfig.netexDefaults); var transitService = new OtpTransitServiceBuilder(new StopModel(), DataImportIssueStore.NOOP); - return new NetexConfigure(buildConfig).netexBundle(transitService, configuredDataSource); + return new NetexConfigure(buildConfig) + .netexBundle(transitService, configuredDataSource, new VehicleParkingService()); } public static NetexBundle createMinimalNetexEpipBundle() { @@ -114,7 +116,8 @@ public static NetexBundle createMinimalNetexEpipBundle() { var configuredDataSource = new ConfiguredDataSource<>(dataSource, buildConfig.netexDefaults); var transitService = new OtpTransitServiceBuilder(new StopModel(), DataImportIssueStore.NOOP); - return new NetexConfigure(buildConfig).netexBundle(transitService, configuredDataSource); + return new NetexConfigure(buildConfig) + .netexBundle(transitService, configuredDataSource, new VehicleParkingService()); } /** diff --git a/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java new file mode 100644 index 00000000000..336281fe53d --- /dev/null +++ b/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java @@ -0,0 +1,86 @@ +package org.opentripplanner.netex.mapping; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.rutebanken.netex.model.ParkingVehicleEnumeration.AGRICULTURAL_VEHICLE; +import static org.rutebanken.netex.model.ParkingVehicleEnumeration.CAR; +import static org.rutebanken.netex.model.ParkingVehicleEnumeration.CAR_WITH_CARAVAN; +import static org.rutebanken.netex.model.ParkingVehicleEnumeration.CYCLE; +import static org.rutebanken.netex.model.ParkingVehicleEnumeration.PEDAL_CYCLE; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.framework.geometry.WgsCoordinate; +import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.rutebanken.netex.model.LocationStructure; +import org.rutebanken.netex.model.MultilingualString; +import org.rutebanken.netex.model.Parking; +import org.rutebanken.netex.model.ParkingVehicleEnumeration; +import org.rutebanken.netex.model.SimplePoint_VersionStructure; + +class VehicleParkingMapperTest { + + private static final VehicleParkingMapper MAPPER = new VehicleParkingMapper( + new FeedScopedIdFactory("parking") + ); + + public static List> carCases() { + return List.of(Set.of(), Set.of(CAR, AGRICULTURAL_VEHICLE)); + } + + @ParameterizedTest + @MethodSource("carCases") + void mapCarLot(Set vehicleTypes) { + var vp = MAPPER.map(parking(vehicleTypes)); + assertEquals("A name", vp.getName().toString()); + assertCommonProperties(vp); + assertTrue(vp.hasAnyCarPlaces()); + assertEquals(VehicleParkingSpaces.builder().carSpaces(10).build(), vp.getCapacity()); + } + + public static List> bicycleCases() { + return List.of(Set.of(CYCLE), Set.of(PEDAL_CYCLE, CAR, CAR_WITH_CARAVAN)); + } + + @ParameterizedTest + @MethodSource("bicycleCases") + void mapBicycleLot(Set vehicleTypes) { + var vp = MAPPER.map(parking(vehicleTypes)); + assertEquals("A name", vp.getName().toString()); + assertCommonProperties(vp); + assertTrue(vp.hasBicyclePlaces()); + assertEquals(VehicleParkingSpaces.builder().bicycleSpaces(10).build(), vp.getCapacity()); + } + + private static void assertCommonProperties(VehicleParking vp) { + assertEquals(new WgsCoordinate(10, 20), vp.getCoordinate()); + assertEquals( + "[VehicleParkingEntrance{entranceId: parking:LOT1/entrance, coordinate: (10.0, 20.0), carAccessible: true, walkAccessible: true}]", + vp.getEntrances().toString() + ); + } + + private static Parking parking(Set vehicleTypes) { + var name = new MultilingualString(); + name.setValue("A name"); + var point = new SimplePoint_VersionStructure(); + var loc = new LocationStructure(); + loc.setLatitude(new BigDecimal(10)); + loc.setLongitude(new BigDecimal(20)); + point.setLocation(loc); + + var parking = new Parking(); + parking.setId("LOT1"); + parking.setName(name); + parking.setCentroid(point); + parking.setTotalCapacity(BigInteger.TEN); + parking.getParkingVehicleTypes().addAll(vehicleTypes); + return parking; + } +} From e95b6755b9efb011713368d8dcd55faf74bf6fc6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 3 Jul 2024 17:54:23 +0200 Subject: [PATCH 094/192] Fix building of capacity --- .../org/opentripplanner/netex/mapping/VehicleParkingMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java index a6ee863596e..15ebea466c0 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java @@ -70,7 +70,7 @@ private static VehicleParkingSpaces mapCapacity(Parking parking) { builder.carSpaces(capacity); } - return builder.carSpaces(capacity).build(); + return builder.build(); } private static boolean hasBikes(Parking parking) { From b5d492afd92c6ceee0801b553fefaa3340ae9e1c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 3 Jul 2024 22:53:31 +0200 Subject: [PATCH 095/192] Change wiring of vehicle parking --- .../org/opentripplanner/netex/NetexBundle.java | 16 ++++++++++------ .../org/opentripplanner/netex/NetexModule.java | 7 +++++++ .../netex/configure/NetexConfigure.java | 7 ++----- .../netex/mapping/NetexMapper.java | 18 ++++++------------ .../mapping/VehicleParkingMapperTest.java | 3 +-- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/NetexBundle.java b/src/main/java/org/opentripplanner/netex/NetexBundle.java index c0da007bb10..7d80f431309 100644 --- a/src/main/java/org/opentripplanner/netex/NetexBundle.java +++ b/src/main/java/org/opentripplanner/netex/NetexBundle.java @@ -3,6 +3,7 @@ import jakarta.xml.bind.JAXBException; import java.io.Closeable; import java.io.IOException; +import java.util.Collection; import java.util.List; import java.util.Set; import org.opentripplanner.datastore.api.CompositeDataSource; @@ -17,7 +18,7 @@ import org.opentripplanner.netex.loader.parser.NetexDocumentParser; import org.opentripplanner.netex.mapping.NetexMapper; import org.opentripplanner.netex.validation.Validator; -import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.transit.model.framework.Deduplicator; import org.rutebanken.netex.model.PublicationDeliveryStructure; import org.slf4j.Logger; @@ -47,7 +48,6 @@ public class NetexBundle implements Closeable { private final double maxStopToShapeSnapDistance; private final boolean noTransfersOnIsolatedStops; private final boolean ignoreFareFrame; - private final VehicleParkingService parkingService; /** The NeTEx entities loaded from the input files and passed on to the mapper. */ private NetexEntityIndex index = new NetexEntityIndex(); /** Report errors to issue store */ @@ -61,7 +61,6 @@ public NetexBundle( CompositeDataSource source, NetexDataSourceHierarchy hierarchy, OtpTransitServiceBuilder transitBuilder, - VehicleParkingService parkingService, Set ferryIdsNotAllowedForBicycle, double maxStopToShapeSnapDistance, boolean noTransfersOnIsolatedStops, @@ -71,7 +70,6 @@ public NetexBundle( this.source = source; this.hierarchy = hierarchy; this.transitBuilder = transitBuilder; - this.parkingService = parkingService; this.ferryIdsNotAllowedForBicycle = ferryIdsNotAllowedForBicycle; this.maxStopToShapeSnapDistance = maxStopToShapeSnapDistance; this.noTransfersOnIsolatedStops = noTransfersOnIsolatedStops; @@ -97,8 +95,7 @@ public OtpTransitServiceBuilder loadBundle( issueStore, ferryIdsNotAllowedForBicycle, maxStopToShapeSnapDistance, - noTransfersOnIsolatedStops, - parkingService + noTransfersOnIsolatedStops ); // Load data @@ -120,6 +117,13 @@ public void close() throws IOException { source.close(); } + /** + * Return the list of parking lots contained in the netex bundle. + */ + public Collection vehicleParkings() { + return mapper.mapVehicleParkings(); + } + /** Load all files entries in the bundle */ private void loadFileEntries() { // Load global shared files diff --git a/src/main/java/org/opentripplanner/netex/NetexModule.java b/src/main/java/org/opentripplanner/netex/NetexModule.java index b9a05d25b10..8c5c0826670 100644 --- a/src/main/java/org/opentripplanner/netex/NetexModule.java +++ b/src/main/java/org/opentripplanner/netex/NetexModule.java @@ -13,6 +13,7 @@ import org.opentripplanner.model.calendar.ServiceDateInterval; import org.opentripplanner.model.impl.OtpTransitServiceBuilder; import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingHelper; import org.opentripplanner.standalone.config.BuildConfig; import org.opentripplanner.transit.service.TransitModel; @@ -100,6 +101,12 @@ public void buildGraph() { ); transitModel.validateTimeZones(); + + var lots = netexBundle.vehicleParkings(); + graph.getVehicleParkingService().updateVehicleParking(lots, List.of()); + var linker = new VehicleParkingHelper(graph); + lots.forEach(linker::linkVehicleParkingToGraph); + } transitModel.updateCalendarServiceData(hasActiveTransit, calendarServiceData, issueStore); diff --git a/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java b/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java index 7224d243d77..464c03f28e1 100644 --- a/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java +++ b/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java @@ -11,7 +11,6 @@ import org.opentripplanner.netex.config.NetexFeedParameters; import org.opentripplanner.netex.loader.NetexDataSourceHierarchy; import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.standalone.config.BuildConfig; import org.opentripplanner.transit.service.TransitModel; @@ -47,7 +46,7 @@ public NetexModule createNetexModule( transitModel.getStopModel(), issueStore ); - netexBundles.add(netexBundle(transitServiceBuilder, it, graph.getVehicleParkingService())); + netexBundles.add(netexBundle(transitServiceBuilder, it)); } return new NetexModule( @@ -63,8 +62,7 @@ public NetexModule createNetexModule( /** public to enable testing */ public NetexBundle netexBundle( OtpTransitServiceBuilder transitServiceBuilder, - ConfiguredDataSource configuredDataSource, - VehicleParkingService parkingService + ConfiguredDataSource configuredDataSource ) { var source = (CompositeDataSource) configuredDataSource.dataSource(); var config = configuredDataSource.config(); @@ -74,7 +72,6 @@ public NetexBundle netexBundle( source, hierarchy(source, config), transitServiceBuilder, - parkingService, config.ferryIdsNotAllowedForBicycle(), buildParams.maxStopToShapeSnapDistance, config.noTransfersOnIsolatedStops(), diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index d99a315a39b..e6a0d724dd4 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -19,7 +19,7 @@ import org.opentripplanner.netex.mapping.calendar.CalendarServiceBuilder; import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; import org.opentripplanner.netex.mapping.support.NetexMapperIndexes; -import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.Deduplicator; @@ -69,7 +69,6 @@ public class NetexMapper { private final Set ferryIdsNotAllowedForBicycle; private final double maxStopToShapeSnapDistance; private final boolean noTransfersOnIsolatedStops; - private final VehicleParkingService parkingService; /** Map entries that cross reference entities within a group/operator, for example Interchanges. */ private GroupNetexMapper groupMapper; @@ -96,8 +95,7 @@ public NetexMapper( DataImportIssueStore issueStore, Set ferryIdsNotAllowedForBicycle, double maxStopToShapeSnapDistance, - boolean noTransfersOnIsolatedStops, - VehicleParkingService parkingService + boolean noTransfersOnIsolatedStops ) { this.transitBuilder = transitBuilder; this.deduplicator = deduplicator; @@ -106,7 +104,6 @@ public NetexMapper( this.ferryIdsNotAllowedForBicycle = ferryIdsNotAllowedForBicycle; this.noTransfersOnIsolatedStops = noTransfersOnIsolatedStops; this.maxStopToShapeSnapDistance = maxStopToShapeSnapDistance; - this.parkingService = parkingService; this.calendarServiceBuilder = new CalendarServiceBuilder(idFactory); this.tripCalendarBuilder = new TripCalendarBuilder(this.calendarServiceBuilder, issueStore); } @@ -204,8 +201,11 @@ public void mapNetexToOtp(NetexEntityIndexReadOnlyView netexIndex) { mapNoticeAssignments(); addEntriesToGroupMapperForPostProcessingLater(); + } - mapParkings(); + public Collection mapVehicleParkings() { + var mapper = new VehicleParkingMapper(idFactory); + return mapper.map(currentNetexIndex.getParkings()); } /* PRIVATE METHODS */ @@ -517,12 +517,6 @@ private void mapNoticeAssignments() { } } - private void mapParkings() { - var mapper = new VehicleParkingMapper(idFactory); - var parkingLots = mapper.map(currentNetexIndex.getParkings()); - parkingService.updateVehicleParking(parkingLots, List.of()); - } - private void addEntriesToGroupMapperForPostProcessingLater() { if (level != 0) { groupMapper.addInterchange( diff --git a/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java index 336281fe53d..98dbf661839 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java @@ -38,7 +38,6 @@ public static List> carCases() { @MethodSource("carCases") void mapCarLot(Set vehicleTypes) { var vp = MAPPER.map(parking(vehicleTypes)); - assertEquals("A name", vp.getName().toString()); assertCommonProperties(vp); assertTrue(vp.hasAnyCarPlaces()); assertEquals(VehicleParkingSpaces.builder().carSpaces(10).build(), vp.getCapacity()); @@ -52,13 +51,13 @@ public static List> bicycleCases() { @MethodSource("bicycleCases") void mapBicycleLot(Set vehicleTypes) { var vp = MAPPER.map(parking(vehicleTypes)); - assertEquals("A name", vp.getName().toString()); assertCommonProperties(vp); assertTrue(vp.hasBicyclePlaces()); assertEquals(VehicleParkingSpaces.builder().bicycleSpaces(10).build(), vp.getCapacity()); } private static void assertCommonProperties(VehicleParking vp) { + assertEquals("A name", vp.getName().toString()); assertEquals(new WgsCoordinate(10, 20), vp.getCoordinate()); assertEquals( "[VehicleParkingEntrance{entranceId: parking:LOT1/entrance, coordinate: (10.0, 20.0), carAccessible: true, walkAccessible: true}]", From 3133496c81fbbe7ae8af2fc6c324d8688e4a04e1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jul 2024 00:02:15 +0200 Subject: [PATCH 096/192] Improve building of the links --- client/src/components/MapView/MapView.tsx | 2 +- client/src/util/getColorForMode.ts | 4 ++-- .../apis/vectortiles/DebugStyleSpec.java | 23 +++++++++++++++---- .../apis/vectortiles/model/StyleBuilder.java | 23 ++++++++++++++++--- .../graph_builder/GraphBuilder.java | 2 +- .../module/StreetLinkerModule.java | 22 +----------------- .../vector/vertex/VertexPropertyMapper.java | 6 +++++ .../netex/mapping/VehicleParkingMapper.java | 2 +- .../opentripplanner/routing/graph/Graph.java | 6 ----- .../vehicle_parking/VehicleParkingHelper.java | 3 +-- .../model/edge/StreetVehicleParkingLink.java | 7 +++--- .../vertex/VehicleParkingEntranceVertex.java | 16 +++++++++++++ 12 files changed, 71 insertions(+), 45 deletions(-) diff --git a/client/src/components/MapView/MapView.tsx b/client/src/components/MapView/MapView.tsx index 36d98be79b6..9c3761bb530 100644 --- a/client/src/components/MapView/MapView.tsx +++ b/client/src/components/MapView/MapView.tsx @@ -75,7 +75,7 @@ export function MapView({ }} // it's unfortunate that you have to list these layers here. // maybe there is a way around it: https://github.com/visgl/react-map-gl/discussions/2343 - interactiveLayerIds={['regular-stop', 'area-stop', 'group-stop', 'vertex', 'edge', 'link']} + interactiveLayerIds={['regular-stop', 'area-stop', 'group-stop', 'parking-vertex', 'vertex', 'edge', 'link']} onClick={showFeaturePropPopup} // put lat/long in URL and pan to it on page reload hash={true} diff --git a/client/src/util/getColorForMode.ts b/client/src/util/getColorForMode.ts index 79af525e826..ca7f9fd3474 100644 --- a/client/src/util/getColorForMode.ts +++ b/client/src/util/getColorForMode.ts @@ -1,10 +1,10 @@ import { Mode } from '../gql/graphql.ts'; export const getColorForMode = function (mode: Mode) { - if (mode === Mode.Foot) return '#444'; + if (mode === Mode.Foot) return '#7e7e7e'; if (mode === Mode.Bicycle) return '#5076D9'; if (mode === Mode.Scooter) return '#253664'; - if (mode === Mode.Car) return '#444'; + if (mode === Mode.Car) return '#191616'; if (mode === Mode.Rail) return '#86BF8B'; if (mode === Mode.Coach) return '#25642A'; if (mode === Mode.Metro) return '#D9B250'; diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 7741a7a58cb..21cdfee9ef7 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -23,6 +23,7 @@ import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.TemporaryFreeEdge; import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge; +import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; /** * A Mapbox/Mapblibre style specification for rendering debug information about transit and @@ -38,7 +39,8 @@ public class DebugStyleSpec { "© OpenStreetMap Contributors" ); private static final String MAGENTA = "#f21d52"; - private static final String GREEN = "#22DD9E"; + private static final String BRIGHT_GREEN = "#22DD9E"; + private static final String DARK_GREEN = "#136b04"; private static final String PURPLE = "#BC55F2"; private static final String BLACK = "#140d0e"; private static final int MAX_ZOOM = 23; @@ -101,7 +103,7 @@ static StyleSpec build( .ofId("link") .typeLine() .vectorSourceLayer(edges) - .lineColor(GREEN) + .lineColor(BRIGHT_GREEN) .edgeFilter( StreetTransitStopLink.class, StreetTransitEntranceLink.class, @@ -125,11 +127,24 @@ static StyleSpec build( .minZoom(15) .maxZoom(MAX_ZOOM) .intiallyHidden(), + StyleBuilder + .ofId("parking-vertex") + .typeCircle() + .vectorSourceLayer(vertices) + .vertexFilter(VehicleParkingEntranceVertex.class) + .circleStroke(BLACK, CIRCLE_STROKE) + .circleRadius( + new ZoomDependentNumber(1, List.of(new ZoomStop(13, 1.4f), new ZoomStop(MAX_ZOOM, 10))) + ) + .circleColor(DARK_GREEN) + .minZoom(13) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), StyleBuilder .ofId("area-stop") .typeFill() .vectorSourceLayer(areaStops) - .fillColor(GREEN) + .fillColor(BRIGHT_GREEN) .fillOpacity(0.5f) .fillOutlineColor(BLACK) .minZoom(6) @@ -138,7 +153,7 @@ static StyleSpec build( .ofId("group-stop") .typeFill() .vectorSourceLayer(groupStops) - .fillColor(GREEN) + .fillColor(BRIGHT_GREEN) .fillOpacity(0.5f) .fillOutlineColor(BLACK) .minZoom(6) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index 28c1e792fd1..312eaa9e727 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -12,6 +12,7 @@ import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.street.model.edge.Edge; +import org.opentripplanner.street.model.vertex.Vertex; /** * Builds a Maplibre/Mapbox vector tile @@ -37,6 +38,8 @@ public StyleBuilder vectorSourceLayer(VectorSourceLayer source) { return sourceLayer(source.vectorLayer()); } + + public enum LayerType { Circle, Line, @@ -192,9 +195,17 @@ public StyleBuilder intiallyHidden() { */ @SafeVarargs public final StyleBuilder edgeFilter(Class... classToFilter) { - var clazzes = Arrays.stream(classToFilter).map(Class::getSimpleName).toList(); - filter = ListUtils.combine(List.of("in", "class"), clazzes); - return this; + return filterClasses(classToFilter); + } + + + + /** + * Only apply the style to the given vertices. + */ + @SafeVarargs + public final StyleBuilder vertexFilter(Class... classToFilter) { + return filterClasses(classToFilter); } public JsonNode toJson() { @@ -216,6 +227,12 @@ public JsonNode toJson() { return OBJECT_MAPPER.valueToTree(copy); } + private StyleBuilder filterClasses(Class... classToFilter) { + var clazzes = Arrays.stream(classToFilter).map(Class::getSimpleName).toList(); + filter = ListUtils.combine(List.of("in", "class"), clazzes); + return this; + } + private String validateColor(String color) { if (!color.startsWith("#")) { throw new IllegalArgumentException("Colors must start with '#'"); diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index c036c113e26..3c1408089a4 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -127,7 +127,7 @@ public static GraphBuilder create( graphBuilder.addModule(factory.osmBoardingLocationsModule()); } - // This module is outside the hasGTFS conditional block because it also links things like bike rental + // This module is outside the hasGTFS conditional block because it also links things like parking // which need to be handled even when there's no transit. graphBuilder.addModule(factory.streetLinkerModule()); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java index 37334161b0a..48e6e484a0c 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -258,16 +258,12 @@ private void linkTransitEntrances(Graph graph) { } private void linkVehicleParks(Graph graph, DataImportIssueStore issueStore) { - if (graph.hasLinkedBikeParks) { - LOG.info("Already linked vehicle parks to graph..."); - return; - } LOG.info("Linking vehicle parks to graph..."); List vehicleParkingToRemove = new ArrayList<>(); for (VehicleParkingEntranceVertex vehicleParkingEntranceVertex : graph.getVerticesOfType( VehicleParkingEntranceVertex.class )) { - if (vehicleParkingEntranceHasLinks(vehicleParkingEntranceVertex)) { + if (vehicleParkingEntranceVertex.isLinkedToGraph()) { continue; } @@ -296,22 +292,6 @@ private void linkVehicleParks(Graph graph, DataImportIssueStore issueStore) { var vehicleParkingService = graph.getVehicleParkingService(); vehicleParkingService.updateVehicleParking(List.of(), vehicleParkingToRemove); } - graph.hasLinkedBikeParks = true; - } - - private boolean vehicleParkingEntranceHasLinks( - VehicleParkingEntranceVertex vehicleParkingEntranceVertex - ) { - return !( - vehicleParkingEntranceVertex - .getIncoming() - .stream() - .allMatch(VehicleParkingEdge.class::isInstance) && - vehicleParkingEntranceVertex - .getOutgoing() - .stream() - .allMatch(VehicleParkingEdge.class::isInstance) - ); } /** diff --git a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java index a493269cc3b..3bf6a9d8706 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java @@ -9,6 +9,7 @@ import org.opentripplanner.inspector.vector.KeyValue; import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex; import org.opentripplanner.street.model.vertex.BarrierVertex; +import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.model.vertex.Vertex; public class VertexPropertyMapper extends PropertyMapper { @@ -23,6 +24,11 @@ protected Collection map(Vertex input) { switch (input) { case BarrierVertex v -> List.of(kv("permission", v.getBarrierPermissions().toString())); case VehicleRentalPlaceVertex v -> List.of(kv("rentalId", v.getStation().getId())); + case VehicleParkingEntranceVertex v -> List.of( + kv("rentalId", v.getVehicleParking().getId()), + kv("walkAccessible", Boolean.toString(v.isWalkAccessible())), + kv("carAccessible", Boolean.toString(v.isCarAccessible())) + ); default -> List.of(); }; return ListUtils.combine(baseProps, properties); diff --git a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java index 15ebea466c0..ffb545d5dd9 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java @@ -27,7 +27,7 @@ class VehicleParkingMapper { CYCLE ); - public VehicleParkingMapper(FeedScopedIdFactory idFactory) { + VehicleParkingMapper(FeedScopedIdFactory idFactory) { this.idFactory = idFactory; } diff --git a/src/main/java/org/opentripplanner/routing/graph/Graph.java b/src/main/java/org/opentripplanner/routing/graph/Graph.java index 4b6c9938317..6a6d962cf36 100644 --- a/src/main/java/org/opentripplanner/routing/graph/Graph.java +++ b/src/main/java/org/opentripplanner/routing/graph/Graph.java @@ -90,12 +90,6 @@ public class Graph implements Serializable { /** True if OSM data was loaded into this Graph. */ public boolean hasStreets = false; - /** - * Have bike parks already been linked to the graph. As the linking happens twice if a base graph - * is used, we store information on whether bike park linking should be skipped. - */ - - public boolean hasLinkedBikeParks = false; /** * The difference in meters between the WGS84 ellipsoid height and geoid height at the graph's * center diff --git a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelper.java b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelper.java index 507ce9329ed..257f2805ca2 100644 --- a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelper.java +++ b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelper.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.VehicleParkingEdge; @@ -30,7 +29,7 @@ public List createVehicleParkingVertices( .getEntrances() .stream() .map(vertexFactory::vehicleParkingEntrance) - .collect(Collectors.toList()); + .toList(); } public static void linkVehicleParkingEntrances( diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java b/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java index e682a7bfac1..098e91b1b63 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java @@ -2,6 +2,7 @@ import javax.annotation.Nonnull; import org.locationtech.jts.geom.LineString; +import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.routing.api.request.preference.VehicleParkingPreferences; @@ -95,11 +96,9 @@ public I18NString getName() { return vehicleParkingEntranceVertex.getName(); } + @Override public LineString getGeometry() { - return null; + return GeometryUtils.makeLineString(fromv.getCoordinate(), tov.getCoordinate()); } - public double getDistanceMeters() { - return 0; - } } diff --git a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java index a3ab0aa17b0..d43f29ad2bc 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java @@ -1,10 +1,12 @@ package org.opentripplanner.street.model.vertex; +import java.util.Collection; import java.util.Objects; import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingEntrance; +import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.VehicleParkingEdge; @@ -49,4 +51,18 @@ public boolean isCarAccessible() { public boolean isWalkAccessible() { return parkingEntrance.isWalkAccessible(); } + + public boolean isLinkedToGraph( + ) { + return !( + hasLinks(getIncoming()) && + hasLinks(getOutgoing()) + ); + } + + private boolean hasLinks(Collection incoming) { + return incoming + .stream() + .allMatch(VehicleParkingEdge.class::isInstance); + } } From 96b900489c0a5c58cebb68d64802ada8cdfa7293 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jul 2024 00:04:59 +0200 Subject: [PATCH 097/192] Swap colour order --- client/src/util/getColorForMode.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/util/getColorForMode.ts b/client/src/util/getColorForMode.ts index ca7f9fd3474..cb1ad8b6981 100644 --- a/client/src/util/getColorForMode.ts +++ b/client/src/util/getColorForMode.ts @@ -1,10 +1,10 @@ import { Mode } from '../gql/graphql.ts'; export const getColorForMode = function (mode: Mode) { - if (mode === Mode.Foot) return '#7e7e7e'; + if (mode === Mode.Foot) return '#191616'; if (mode === Mode.Bicycle) return '#5076D9'; if (mode === Mode.Scooter) return '#253664'; - if (mode === Mode.Car) return '#191616'; + if (mode === Mode.Car) return '#7e7e7e'; if (mode === Mode.Rail) return '#86BF8B'; if (mode === Mode.Coach) return '#25642A'; if (mode === Mode.Metro) return '#D9B250'; From 90ddcec9e9dd7598bcfffce746dba5be2f8b5fdd Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jul 2024 08:02:56 +0200 Subject: [PATCH 098/192] Format code --- .../apis/vectortiles/model/StyleBuilder.java | 4 -- .../vector/vertex/VertexPropertyMapper.java | 2 +- .../opentripplanner/netex/NetexModule.java | 1 - .../model/edge/StreetVehicleParkingLink.java | 1 - .../vertex/VehicleParkingEntranceVertex.java | 12 ++--- .../opentripplanner/ConstantsForTests.java | 5 +- .../apis/vectortiles/style.json | 46 +++++++++++++++++++ 7 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index 312eaa9e727..d842b5e6687 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -38,8 +38,6 @@ public StyleBuilder vectorSourceLayer(VectorSourceLayer source) { return sourceLayer(source.vectorLayer()); } - - public enum LayerType { Circle, Line, @@ -198,8 +196,6 @@ public final StyleBuilder edgeFilter(Class... classToFilter) { return filterClasses(classToFilter); } - - /** * Only apply the style to the given vertices. */ diff --git a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java index 3bf6a9d8706..83b4a675fdb 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java @@ -28,7 +28,7 @@ protected Collection map(Vertex input) { kv("rentalId", v.getVehicleParking().getId()), kv("walkAccessible", Boolean.toString(v.isWalkAccessible())), kv("carAccessible", Boolean.toString(v.isCarAccessible())) - ); + ); default -> List.of(); }; return ListUtils.combine(baseProps, properties); diff --git a/src/main/java/org/opentripplanner/netex/NetexModule.java b/src/main/java/org/opentripplanner/netex/NetexModule.java index 8c5c0826670..c0390e9793d 100644 --- a/src/main/java/org/opentripplanner/netex/NetexModule.java +++ b/src/main/java/org/opentripplanner/netex/NetexModule.java @@ -106,7 +106,6 @@ public void buildGraph() { graph.getVehicleParkingService().updateVehicleParking(lots, List.of()); var linker = new VehicleParkingHelper(graph); lots.forEach(linker::linkVehicleParkingToGraph); - } transitModel.updateCalendarServiceData(hasActiveTransit, calendarServiceData, issueStore); diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java b/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java index 098e91b1b63..2ad9d0f39c4 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java @@ -100,5 +100,4 @@ public I18NString getName() { public LineString getGeometry() { return GeometryUtils.makeLineString(fromv.getCoordinate(), tov.getCoordinate()); } - } diff --git a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java index d43f29ad2bc..91512c98c46 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java @@ -52,17 +52,11 @@ public boolean isWalkAccessible() { return parkingEntrance.isWalkAccessible(); } - public boolean isLinkedToGraph( - ) { - return !( - hasLinks(getIncoming()) && - hasLinks(getOutgoing()) - ); + public boolean isLinkedToGraph() { + return !(hasLinks(getIncoming()) && hasLinks(getOutgoing())); } private boolean hasLinks(Collection incoming) { - return incoming - .stream() - .allMatch(VehicleParkingEdge.class::isInstance); + return incoming.stream().allMatch(VehicleParkingEdge.class::isInstance); } } diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index b002d7a3c41..76826b8c247 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -31,7 +31,6 @@ import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.linking.LinkingDirection; import org.opentripplanner.routing.linking.VertexLinker; -import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; @@ -104,7 +103,7 @@ public static NetexBundle createMinimalNetexNordicBundle() { var transitService = new OtpTransitServiceBuilder(new StopModel(), DataImportIssueStore.NOOP); return new NetexConfigure(buildConfig) - .netexBundle(transitService, configuredDataSource, new VehicleParkingService()); + .netexBundle(transitService, configuredDataSource); } public static NetexBundle createMinimalNetexEpipBundle() { @@ -117,7 +116,7 @@ public static NetexBundle createMinimalNetexEpipBundle() { var transitService = new OtpTransitServiceBuilder(new StopModel(), DataImportIssueStore.NOOP); return new NetexConfigure(buildConfig) - .netexBundle(transitService, configuredDataSource, new VehicleParkingService()); + .netexBundle(transitService, configuredDataSource); } /** diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 6f981b7f67d..5a2ed9572e2 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -191,6 +191,52 @@ "visibility" : "none" } }, + { + "id" : "parking-vertex", + "type" : "circle", + "source" : "vectorSource", + "source-layer" : "vertices", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "circle-stroke-color" : "#140d0e", + "circle-stroke-width" : { + "base" : 1.0, + "stops" : [ + [ + 15, + 0.2 + ], + [ + 23, + 3.0 + ] + ] + }, + "circle-radius" : { + "base" : 1.0, + "stops" : [ + [ + 13, + 1.4 + ], + [ + 23, + 10.0 + ] + ] + }, + "circle-color" : "#136b04" + }, + "filter" : [ + "in", + "class", + "VehicleParkingEntranceVertex" + ], + "layout" : { + "visibility" : "none" + } + }, { "id" : "area-stop", "type" : "fill", From 39f13f3a75b08809bae948074bb55e99c0834125 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jul 2024 08:07:49 +0200 Subject: [PATCH 099/192] Update snapshot tests --- src/test/java/org/opentripplanner/ConstantsForTests.java | 6 ++---- .../algorithm/mapping/__snapshots__/CarSnapshotTest.snap | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/opentripplanner/ConstantsForTests.java b/src/test/java/org/opentripplanner/ConstantsForTests.java index 76826b8c247..e74c66e527b 100644 --- a/src/test/java/org/opentripplanner/ConstantsForTests.java +++ b/src/test/java/org/opentripplanner/ConstantsForTests.java @@ -102,8 +102,7 @@ public static NetexBundle createMinimalNetexNordicBundle() { var configuredDataSource = new ConfiguredDataSource<>(dataSource, buildConfig.netexDefaults); var transitService = new OtpTransitServiceBuilder(new StopModel(), DataImportIssueStore.NOOP); - return new NetexConfigure(buildConfig) - .netexBundle(transitService, configuredDataSource); + return new NetexConfigure(buildConfig).netexBundle(transitService, configuredDataSource); } public static NetexBundle createMinimalNetexEpipBundle() { @@ -115,8 +114,7 @@ public static NetexBundle createMinimalNetexEpipBundle() { var configuredDataSource = new ConfiguredDataSource<>(dataSource, buildConfig.netexDefaults); var transitService = new OtpTransitServiceBuilder(new StopModel(), DataImportIssueStore.NOOP); - return new NetexConfigure(buildConfig) - .netexBundle(transitService, configuredDataSource); + return new NetexConfigure(buildConfig).netexBundle(transitService, configuredDataSource); } /** diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarSnapshotTest.snap b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarSnapshotTest.snap index 8f527b15bf7..5d83d85cd7a 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarSnapshotTest.snap +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/CarSnapshotTest.snap @@ -28,8 +28,8 @@ org.opentripplanner.routing.algorithm.mapping.CarSnapshotTest.directCarPark=[ "generalizedCost" : 61, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 5, - "points" : "ya|tGv~{kV??nCEB|Dn@@" + "length" : 6, + "points" : "ya|tGv~{kV??nCEB|Dn@@??" }, "mode" : "CAR", "pathway" : false, @@ -147,8 +147,8 @@ org.opentripplanner.routing.algorithm.mapping.CarSnapshotTest.directCarPark=[ "generalizedCost" : 285, "interlineWithPreviousLeg" : false, "legGeometry" : { - "length" : 5, - "points" : "gz{tGrd|kVn@@CgElCEB?" + "length" : 6, + "points" : "gz{tGrd|kV??n@@CgElCEB?" }, "mode" : "WALK", "pathway" : false, From 2dd6589e169b957227965f9e53211a6987a4f235 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jul 2024 11:10:22 +0200 Subject: [PATCH 100/192] Update docs --- .../opentripplanner/netex/mapping/VehicleParkingMapper.java | 3 ++- .../street/model/vertex/VehicleParkingEntranceVertex.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java index ffb545d5dd9..84dfc201909 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java @@ -61,7 +61,8 @@ private static VehicleParkingSpaces mapCapacity(Parking parking) { var builder = VehicleParkingSpaces.builder(); int capacity = parking.getTotalCapacity().intValue(); - // we assume that if we have bicycle in vehicle types it's a bicycle parking lot + // we assume that if we have something bicycle-like in the vehicle types it's a bicycle parking + // lot // it's not possible in NeTEx to split the spaces between the types, so if you want that // you have to define two parking lots with the same coordinates if (hasBikes(parking)) { diff --git a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java index 91512c98c46..6ba13ecf9b8 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java @@ -53,10 +53,10 @@ public boolean isWalkAccessible() { } public boolean isLinkedToGraph() { - return !(hasLinks(getIncoming()) && hasLinks(getOutgoing())); + return !(hasNoLinkEdges(getIncoming()) && hasNoLinkEdges(getOutgoing())); } - private boolean hasLinks(Collection incoming) { + private boolean hasNoLinkEdges(Collection incoming) { return incoming.stream().allMatch(VehicleParkingEdge.class::isInstance); } } From aab96e6597b4a95a5d032aba18d6398ce03af8fb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jul 2024 11:39:54 +0200 Subject: [PATCH 101/192] Update Javadoc and implementation --- .../model/vertex/VehicleParkingEntranceVertex.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java index 6ba13ecf9b8..2f389f60817 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java @@ -52,11 +52,14 @@ public boolean isWalkAccessible() { return parkingEntrance.isWalkAccessible(); } + /** + * Is this vertex already linked to the graph with a {@link StreetVehicleParkingLink}? + */ public boolean isLinkedToGraph() { - return !(hasNoLinkEdges(getIncoming()) && hasNoLinkEdges(getOutgoing())); + return hasLink(getIncoming()) || hasLink(getOutgoing()); } - private boolean hasNoLinkEdges(Collection incoming) { - return incoming.stream().allMatch(VehicleParkingEdge.class::isInstance); + private boolean hasLink(Collection incoming) { + return incoming.stream().anyMatch(StreetVehicleParkingLink.class::isInstance); } } From 6df518f281b1dc30cc7454c0d8160d231ba11b1d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jul 2024 12:05:02 +0200 Subject: [PATCH 102/192] Add test for linking --- .../edge/StreetVehicleParkingLinkTest.java | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLinkTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLinkTest.java index 99de720a5a2..af03bd754bb 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLinkTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLinkTest.java @@ -9,6 +9,7 @@ import java.util.Set; 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; @@ -38,37 +39,25 @@ static Stream testCases() { @ParameterizedTest(name = "Parking[tags={0}], Request[not={1}, select={2}] should traverse={3}") @MethodSource("testCases") - void foo(Set parkingTags, Set not, Set select, boolean shouldTraverse) { + void parkingFilters( + Set parkingTags, + Set not, + Set select, + boolean shouldTraverse + ) { var streetVertex = intersectionVertex(1, 1); - var parking = VehicleParking - .builder() - .id(id("parking")) - .coordinate(new WgsCoordinate(1, 1)) - .tags(parkingTags) - .build(); - - var entrance = VehicleParkingEntrance - .builder() - .vehicleParking(parking) - .entranceId(id("entrance")) - .coordinate(new WgsCoordinate(1, 1)) - .name(new NonLocalizedString("entrance")) - .walkAccessible(true) - .carAccessible(true) - .build(); - - var entranceVertex = new VehicleParkingEntranceVertex(entrance); + final var entranceVertex = buildVertex(parkingTags); var req = StreetSearchRequest.of(); req.withMode(StreetMode.BIKE_TO_PARK); req.withPreferences(p -> - p.withBike(bike -> { + p.withBike(bike -> bike.withParking(parkingPreferences -> { parkingPreferences.withRequiredVehicleParkingTags(select); parkingPreferences.withBannedVehicleParkingTags(not); parkingPreferences.withCost(0); - }); - }) + }) + ) ); var edge = StreetVehicleParkingLink.createStreetVehicleParkingLink( @@ -84,6 +73,33 @@ void foo(Set parkingTags, Set not, Set select, boolean s } } + @Test + void isLinkedToGraph() { + var vertex = buildVertex(Set.of()); + assertFalse(vertex.isLinkedToGraph()); + } + + private static VehicleParkingEntranceVertex buildVertex(Set parkingTags) { + var parking = VehicleParking + .builder() + .id(id("parking")) + .coordinate(new WgsCoordinate(1, 1)) + .tags(parkingTags) + .build(); + + var entrance = VehicleParkingEntrance + .builder() + .vehicleParking(parking) + .entranceId(id("entrance")) + .coordinate(new WgsCoordinate(1, 1)) + .name(new NonLocalizedString("entrance")) + .walkAccessible(true) + .carAccessible(true) + .build(); + + return new VehicleParkingEntranceVertex(entrance); + } + private State[] traverse(Vertex fromV, Edge edge, StreetSearchRequest request) { var state = new State(fromV, request); From c12d713e32b708ef433908f4d5f0f4f41e4517b6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jul 2024 12:33:42 +0200 Subject: [PATCH 103/192] Add more tests --- .../edge/StreetVehicleParkingLinkTest.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLinkTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLinkTest.java index af03bd754bb..8a3316e44e0 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLinkTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLinkTest.java @@ -18,6 +18,7 @@ import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingEntrance; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.request.StreetSearchRequest; @@ -74,11 +75,31 @@ void parkingFilters( } @Test - void isLinkedToGraph() { + void notLinkedToGraph() { var vertex = buildVertex(Set.of()); assertFalse(vertex.isLinkedToGraph()); } + @Test + void linkedToGraphWithIncoming() { + var vertex = buildVertex(Set.of()); + var streetVertex = StreetModelForTest.intersectionVertex(1, 1); + vertex.addIncoming( + StreetVehicleParkingLink.createStreetVehicleParkingLink(streetVertex, vertex) + ); + assertTrue(vertex.isLinkedToGraph()); + } + + @Test + void linkedToGraphWithOutgoing() { + var vertex = buildVertex(Set.of()); + var streetVertex = StreetModelForTest.intersectionVertex(1, 1); + vertex.addOutgoing( + StreetVehicleParkingLink.createStreetVehicleParkingLink(streetVertex, vertex) + ); + assertTrue(vertex.isLinkedToGraph()); + } + private static VehicleParkingEntranceVertex buildVertex(Set parkingTags) { var parking = VehicleParking .builder() From 55257014ad99bd041ef5c3817a4163570785334b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 8 Jul 2024 17:37:04 +0200 Subject: [PATCH 104/192] Use HierarchicalMapById --- .../netex/index/NetexEntityIndex.java | 13 +++++-------- .../index/api/NetexEntityIndexReadOnlyView.java | 3 +-- .../opentripplanner/netex/mapping/NetexMapper.java | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java b/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java index 9841765f3a3..6a5308f91a3 100644 --- a/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java +++ b/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java @@ -1,9 +1,6 @@ package org.opentripplanner.netex.index; -import com.google.common.collect.ImmutableSet; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; import org.opentripplanner.netex.index.api.NetexEntityIndexReadOnlyView; import org.opentripplanner.netex.index.api.ReadOnlyHierarchicalMap; import org.opentripplanner.netex.index.api.ReadOnlyHierarchicalMapById; @@ -101,7 +98,7 @@ public class NetexEntityIndex { public final HierarchicalVersionMapById stopPlaceById; public final HierarchicalVersionMapById tariffZonesById; public final HierarchicalMapById brandingById; - public final Set parkings; + public final HierarchicalMapById parkings; // Relations between entities - The Netex XML sometimes relies on the // nested structure of the XML document, rater than explicit references. @@ -147,7 +144,7 @@ public NetexEntityIndex() { this.tariffZonesById = new HierarchicalVersionMapById<>(); this.brandingById = new HierarchicalMapById<>(); this.timeZone = new HierarchicalElement<>(); - this.parkings = new HashSet<>(0); + this.parkings = new HierarchicalMapById<>(); } /** @@ -190,7 +187,7 @@ public NetexEntityIndex(NetexEntityIndex parent) { this.tariffZonesById = new HierarchicalVersionMapById<>(parent.tariffZonesById); this.brandingById = new HierarchicalMapById<>(parent.brandingById); this.timeZone = new HierarchicalElement<>(parent.timeZone); - this.parkings = new HashSet<>(parent.parkings); + this.parkings = new HierarchicalMapById<>(parent.parkings); } /** @@ -361,8 +358,8 @@ public ReadOnlyHierarchicalVersionMapById getStopPlaceById() { } @Override - public ImmutableSet getParkings() { - return ImmutableSet.copyOf(parkings); + public ReadOnlyHierarchicalMapById getParkings() { + return parkings; } @Override diff --git a/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java b/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java index 9229f40c7f8..24a3d7a8ac4 100644 --- a/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java +++ b/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java @@ -1,6 +1,5 @@ package org.opentripplanner.netex.index.api; -import com.google.common.collect.ImmutableSet; import java.util.Collection; import org.rutebanken.netex.model.Authority; import org.rutebanken.netex.model.Branding; @@ -82,7 +81,7 @@ public interface NetexEntityIndexReadOnlyView { ReadOnlyHierarchicalVersionMapById getStopPlaceById(); - ImmutableSet getParkings(); + ReadOnlyHierarchicalMapById getParkings(); ReadOnlyHierarchicalVersionMapById getTariffZonesById(); diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index e6a0d724dd4..e88a75f750a 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -205,7 +205,7 @@ public void mapNetexToOtp(NetexEntityIndexReadOnlyView netexIndex) { public Collection mapVehicleParkings() { var mapper = new VehicleParkingMapper(idFactory); - return mapper.map(currentNetexIndex.getParkings()); + return mapper.map(currentNetexIndex.getParkings().localValues()); } /* PRIVATE METHODS */ From c15db15b82fee53046b744b3fc1e56a06e5cd9b1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 10 Jul 2024 12:00:23 +0200 Subject: [PATCH 105/192] Show quay ID in leg view --- .../src/components/ItineraryList/ItineraryLegDetails.tsx | 7 ++++++- client/src/hooks/useTripQuery.ts | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/client/src/components/ItineraryList/ItineraryLegDetails.tsx b/client/src/components/ItineraryList/ItineraryLegDetails.tsx index 5f0e2b381aa..4b711dc65e8 100644 --- a/client/src/components/ItineraryList/ItineraryLegDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryLegDetails.tsx @@ -24,7 +24,12 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean )}{' '}

- {leg.mode !== Mode.Foot && {leg.fromPlace.name}} {!isLast && → {leg.toPlace.name}} + {leg.mode !== Mode.Foot && ( + <> + {leg.fromPlace.name} →{' '} + + )}{' '} + {!isLast && {leg.toPlace.name}} ); diff --git a/client/src/hooks/useTripQuery.ts b/client/src/hooks/useTripQuery.ts index c9672d0b6d2..58fabd79ce6 100644 --- a/client/src/hooks/useTripQuery.ts +++ b/client/src/hooks/useTripQuery.ts @@ -52,9 +52,15 @@ const query = graphql(` duration fromPlace { name + quay { + id + } } toPlace { name + quay { + id + } } toEstimatedCall { destinationDisplay { From d770a67567da7724b99e480f8bf3741a436346e4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 10 Jul 2024 12:21:29 +0200 Subject: [PATCH 106/192] Add icon for stay-seated tranfers --- .../ItineraryList/ItineraryLegDetails.tsx | 27 +++++++++++++++++-- client/src/hooks/useTripQuery.ts | 6 +++++ client/src/static/img/stay-seated.svg | 8 ++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 client/src/static/img/stay-seated.svg diff --git a/client/src/components/ItineraryList/ItineraryLegDetails.tsx b/client/src/components/ItineraryList/ItineraryLegDetails.tsx index 4b711dc65e8..1990b7c62ff 100644 --- a/client/src/components/ItineraryList/ItineraryLegDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryLegDetails.tsx @@ -2,6 +2,25 @@ import { Leg, Mode } from '../../gql/graphql.ts'; import { LegTime } from './LegTime.tsx'; import { formatDistance } from '../../util/formatDistance.ts'; import { formatDuration } from '../../util/formatDuration.ts'; +import logo from '../../static/img/stay-seated.svg'; +import React from 'react'; + +const staySeatedIcon: (leg: Leg) => React.JSX.Element = (leg: Leg) => { + if (leg.interchangeFrom?.staySeated) { + return ( + Stay-seated transfer + ); + } else { + return <>; + } +}; export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean }) { return ( @@ -10,8 +29,12 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean {formatDistance(leg.distance)}, {formatDuration(leg.duration)}
- -{' '} - + {staySeatedIcon(leg)} + -
{leg.mode}{' '} diff --git a/client/src/hooks/useTripQuery.ts b/client/src/hooks/useTripQuery.ts index 58fabd79ce6..e50e7019b66 100644 --- a/client/src/hooks/useTripQuery.ts +++ b/client/src/hooks/useTripQuery.ts @@ -77,6 +77,12 @@ const query = graphql(` pointsOnLink { points } + interchangeTo { + staySeated + } + interchangeFrom { + staySeated + } } systemNotices { tag diff --git a/client/src/static/img/stay-seated.svg b/client/src/static/img/stay-seated.svg new file mode 100644 index 00000000000..a0e451c497f --- /dev/null +++ b/client/src/static/img/stay-seated.svg @@ -0,0 +1,8 @@ + + + + + + + + From 6d9ff333beed9ab229d382fe6bff324c0623946c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 11 Jul 2024 14:00:16 +0200 Subject: [PATCH 107/192] Update documentation on REST API [ci skip] --- docs/apis/Apis.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/apis/Apis.md b/docs/apis/Apis.md index ab6b41a25cd..9e8f31f6eeb 100644 --- a/docs/apis/Apis.md +++ b/docs/apis/Apis.md @@ -25,5 +25,5 @@ The [Geocoder API](../sandbox/GeocoderAPI.md) allows you to geocode stop names a The OTP REST API used to power many apps and frontends. For years it was the only way to access OTP programmatically. -Over time it has been replaced by the GraphQL APIs and is scheduled to be disabled by default -and eventually removed completely. It's therefore not recommended to use it. +Over time it has been replaced by the GraphQL APIs and is now disabled by default +and will eventually be removed completely. It's therefore not recommended to use it. From 7695a1efe9faeabc0b8e1ec6ce6ea35d7d5d9f9f Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Mon, 8 Jul 2024 08:04:32 +0200 Subject: [PATCH 108/192] Use transit editor service in SIRI timetable snapshot source --- .../ext/siri/AddedTripBuilderTest.java | 21 +++--- .../ext/siri/AddedTripBuilder.java | 46 ++++++------- .../ext/siri/SiriTimetableSnapshotSource.java | 14 ++-- .../service/DefaultTransitService.java | 69 +++++++++++++++++++ .../transit/service/TransitEditorService.java | 21 ++++++ .../transit/service/TransitModel.java | 2 - .../transit/service/TransitModelIndex.java | 4 -- .../transit/service/TransitService.java | 3 + 8 files changed, 132 insertions(+), 48 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java index b7431c16091..911fb015c27 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java @@ -35,6 +35,7 @@ import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; +import org.opentripplanner.transit.service.TransitEditorService; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.spi.UpdateError; import uk.org.siri.siri20.VehicleModesEnumeration; @@ -77,6 +78,7 @@ class AddedTripBuilderTest { private final Deduplicator DEDUPLICATOR = new Deduplicator(); private final TransitModel TRANSIT_MODEL = new TransitModel(STOP_MODEL, DEDUPLICATOR); + private TransitEditorService TRANSIT_SERVICE; private EntityResolver ENTITY_RESOLVER; @BeforeEach @@ -101,6 +103,7 @@ void setUp() { // Create transit model index TRANSIT_MODEL.index(); + TRANSIT_SERVICE = new DefaultTransitService(TRANSIT_MODEL); // Create the entity resolver only after the model has been indexed ENTITY_RESOLVER = @@ -110,7 +113,7 @@ void setUp() { @Test void testAddedTrip() { var addedTrip = new AddedTripBuilder( - TRANSIT_MODEL, + TRANSIT_SERVICE, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -239,7 +242,7 @@ void testAddedTrip() { @Test void testAddedTripOnAddedRoute() { var firstAddedTrip = new AddedTripBuilder( - TRANSIT_MODEL, + TRANSIT_SERVICE, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -265,7 +268,7 @@ void testAddedTripOnAddedRoute() { var tripId2 = TransitModelForTest.id("TRIP_ID_2"); var secondAddedTrip = new AddedTripBuilder( - TRANSIT_MODEL, + TRANSIT_SERVICE, ENTITY_RESOLVER, AbstractTransitEntity::getId, tripId2, @@ -316,7 +319,7 @@ void testAddedTripOnAddedRoute() { @Test void testAddedTripOnExistingRoute() { var addedTrip = new AddedTripBuilder( - TRANSIT_MODEL, + TRANSIT_SERVICE, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -347,7 +350,7 @@ void testAddedTripOnExistingRoute() { @Test void testAddedTripWithoutReplacedRoute() { var addedTrip = new AddedTripBuilder( - TRANSIT_MODEL, + TRANSIT_SERVICE, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -390,7 +393,7 @@ void testAddedTripWithoutReplacedRoute() { @Test void testAddedTripFailOnMissingServiceId() { var addedTrip = new AddedTripBuilder( - TRANSIT_MODEL, + TRANSIT_SERVICE, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -445,7 +448,7 @@ void testAddedTripFailOnNonIncreasingDwellTime() { ); var addedTrip = new AddedTripBuilder( - TRANSIT_MODEL, + TRANSIT_SERVICE, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -484,7 +487,7 @@ void testAddedTripFailOnTooFewCalls() { .build() ); var addedTrip = new AddedTripBuilder( - TRANSIT_MODEL, + TRANSIT_SERVICE, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -531,7 +534,7 @@ void testAddedTripFailOnUnknownStop() { .build() ); var addedTrip = new AddedTripBuilder( - TRANSIT_MODEL, + TRANSIT_SERVICE, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, diff --git a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java index aac485da4eb..be5b30b38b9 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java @@ -36,7 +36,7 @@ import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimesFactory; -import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.transit.service.TransitEditorService; import org.opentripplanner.updater.spi.DataValidationExceptionMapper; import org.opentripplanner.updater.spi.UpdateError; import org.rutebanken.netex.model.BusSubmodeEnumeration; @@ -52,7 +52,7 @@ class AddedTripBuilder { private static final Logger LOG = LoggerFactory.getLogger(AddedTripBuilder.class); - private final TransitModel transitModel; + private final TransitEditorService transitService; private final EntityResolver entityResolver; private final ZoneId timeZone; private final Function getTripPatternId; @@ -73,7 +73,7 @@ class AddedTripBuilder { AddedTripBuilder( EstimatedVehicleJourney estimatedVehicleJourney, - TransitModel transitModel, + TransitEditorService transitService, EntityResolver entityResolver, Function getTripPatternId ) { @@ -112,16 +112,16 @@ class AddedTripBuilder { calls = CallWrapper.of(estimatedVehicleJourney); - this.transitModel = transitModel; + this.transitService = transitService; this.entityResolver = entityResolver; this.getTripPatternId = getTripPatternId; - timeZone = transitModel.getTimeZone(); + timeZone = transitService.getTimeZone(); replacedTrips = getReplacedVehicleJourneys(estimatedVehicleJourney); } AddedTripBuilder( - TransitModel transitModel, + TransitEditorService transitService, EntityResolver entityResolver, Function getTripPatternId, FeedScopedId tripId, @@ -139,9 +139,9 @@ class AddedTripBuilder { String headsign, List replacedTrips ) { - this.transitModel = transitModel; + this.transitService = transitService; this.entityResolver = entityResolver; - this.timeZone = transitModel.getTimeZone(); + this.timeZone = transitService.getTimeZone(); this.getTripPatternId = getTripPatternId; this.tripId = tripId; this.operator = operator; @@ -168,7 +168,7 @@ Result build() { return UpdateError.result(tripId, NO_START_DATE); } - FeedScopedId calServiceId = transitModel.getOrCreateServiceIdForDate(serviceDate); + FeedScopedId calServiceId = transitService.getOrCreateServiceIdForDate(serviceDate); if (calServiceId == null) { return UpdateError.result(tripId, NO_START_DATE); } @@ -181,7 +181,7 @@ Result build() { } route = createRoute(agency); LOG.info("Adding route {} to transitModel.", route); - transitModel.getTransitModelIndex().addRoutes(route); + transitService.addRoutes(route); } Trip trip = createTrip(route, calServiceId); @@ -221,14 +221,14 @@ Result build() { RealTimeTripTimes tripTimes = TripTimesFactory.tripTimes( trip, aimedStopTimes, - transitModel.getDeduplicator() + transitService.getDeduplicator() ); // validate the scheduled trip times // they are in general superseded by real-time trip times // but in case of trip cancellation, OTP will fall back to scheduled trip times // therefore they must be valid tripTimes.validateNonIncreasingTimes(); - tripTimes.setServiceCode(transitModel.getServiceCodes().get(trip.getServiceId())); + tripTimes.setServiceCode(transitService.getServiceCodeForId(trip.getServiceId())); pattern.add(tripTimes); RealTimeTripTimes updatedTripTimes = tripTimes.copyScheduledTimes(); @@ -267,17 +267,14 @@ Result build() { // Adding trip to index necessary to include values in graphql-queries // TODO - SIRI: should more data be added to index? - transitModel.getTransitModelIndex().getTripForId().put(tripId, trip); - transitModel.getTransitModelIndex().getPatternForTrip().put(trip, pattern); - transitModel.getTransitModelIndex().getPatternsForRoute().put(route, pattern); - transitModel - .getTransitModelIndex() - .getTripOnServiceDateById() - .put(tripOnServiceDate.getId(), tripOnServiceDate); - transitModel - .getTransitModelIndex() - .getTripOnServiceDateForTripAndDay() - .put(new TripIdAndServiceDate(tripId, serviceDate), tripOnServiceDate); + transitService.addTripForId(tripId, trip); + transitService.addPatternForTrip(trip, pattern); + transitService.addPatternsForRoute(route, pattern); + transitService.addTripOnServiceDateById(tripOnServiceDate.getId(), tripOnServiceDate); + transitService.addTripOnServiceDateForTripAndDay( + new TripIdAndServiceDate(tripId, serviceDate), + tripOnServiceDate + ); return Result.success(new TripUpdate(stopPattern, updatedTripTimes, serviceDate)); } @@ -312,8 +309,7 @@ private Route createRoute(Agency agency) { */ @Nullable private Agency resolveAgency() { - return transitModel - .getTransitModelIndex() + return transitService .getAllRoutes() .stream() .filter(r -> r != null && r.getOperator() != null && r.getOperator().equals(operator)) diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index db9c1fcc441..7ab316bfc55 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -23,8 +23,8 @@ import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.TransitEditorService; import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.TimetableSnapshotSourceParameters; import org.opentripplanner.updater.spi.DataValidationExceptionMapper; import org.opentripplanner.updater.spi.UpdateError; @@ -56,9 +56,8 @@ public class SiriTimetableSnapshotSource implements TimetableSnapshotProvider { * messages. */ private final SiriTripPatternCache tripPatternCache; - private final TransitModel transitModel; - private final TransitService transitService; + private final TransitEditorService transitService; private final TimetableSnapshotManager snapshotManager; @@ -72,7 +71,6 @@ public SiriTimetableSnapshotSource( parameters, () -> LocalDate.now(transitModel.getTimeZone()) ); - this.transitModel = transitModel; this.transitService = new DefaultTransitService(transitModel); this.tripPatternCache = new SiriTripPatternCache(tripPatternIdGenerator, transitService::getPatternForTrip); @@ -115,7 +113,7 @@ public UpdateResult applyEstimatedTimetable( var journeys = estimatedJourneyVersion.getEstimatedVehicleJourneies(); LOG.debug("Handling {} EstimatedVehicleJourneys.", journeys.size()); for (EstimatedVehicleJourney journey : journeys) { - results.add(apply(journey, transitModel, fuzzyTripMatcher, entityResolver)); + results.add(apply(journey, transitService, fuzzyTripMatcher, entityResolver)); } } } @@ -135,7 +133,7 @@ public TimetableSnapshot getTimetableSnapshot() { private Result apply( EstimatedVehicleJourney journey, - TransitModel transitModel, + TransitEditorService transitService, @Nullable SiriFuzzyTripMatcher fuzzyTripMatcher, EntityResolver entityResolver ) { @@ -147,7 +145,7 @@ private Result apply( result = new AddedTripBuilder( journey, - transitModel, + transitService, entityResolver, tripPatternIdGenerator::generateUniqueTripPatternId ) @@ -265,7 +263,7 @@ private Result handleModifiedTrip( pattern, estimatedVehicleJourney, serviceDate, - transitModel.getTimeZone(), + transitService.getTimeZone(), entityResolver ) .build(); diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index ca88b7d3130..197860fc8a4 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -34,6 +34,7 @@ import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; +import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.GroupOfRoutes; import org.opentripplanner.transit.model.network.Route; @@ -181,6 +182,10 @@ public Route getRouteForId(FeedScopedId id) { return this.transitModelIndex.getRouteForId(id); } + /** + * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix + * this when doing the issue #3030. + */ @Override public void addRoutes(Route route) { this.transitModelIndex.addRoutes(route); @@ -259,6 +264,15 @@ public Trip getTripForId(FeedScopedId id) { return this.transitModelIndex.getTripForId().get(id); } + /** + * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix + * this when doing the issue #3030. + */ + @Override + public void addTripForId(FeedScopedId tripId, Trip trip) { + transitModelIndex.getTripForId().put(tripId, trip); + } + @Override public Collection getAllTrips() { OTPRequestTimeoutException.checkForTimeout(); @@ -276,6 +290,15 @@ public TripPattern getPatternForTrip(Trip trip) { return this.transitModelIndex.getPatternForTrip().get(trip); } + /** + * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix + * this when doing the issue #3030. + */ + @Override + public void addPatternForTrip(Trip trip, TripPattern pattern) { + transitModelIndex.getPatternForTrip().put(trip, pattern); + } + @Override public TripPattern getPatternForTrip(Trip trip, LocalDate serviceDate) { TripPattern realtimePattern = getRealtimeAddedTripPattern(trip.getId(), serviceDate); @@ -291,6 +314,15 @@ public Collection getPatternsForRoute(Route route) { return this.transitModelIndex.getPatternsForRoute().get(route); } + /** + * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix + * this when doing the issue #3030. + */ + @Override + public void addPatternsForRoute(Route route, TripPattern pattern) { + transitModelIndex.getPatternsForRoute().put(route, pattern); + } + @Override public MultiModalStation getMultiModalStationForStation(Station station) { return this.transitModel.getStopModel().getMultiModalStationForStation(station); @@ -475,6 +507,15 @@ public TripOnServiceDate getTripOnServiceDateById(FeedScopedId datedServiceJourn return transitModelIndex.getTripOnServiceDateById().get(datedServiceJourneyId); } + /** + * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix + * this when doing the issue #3030. + */ + @Override + public void addTripOnServiceDateById(FeedScopedId id, TripOnServiceDate tripOnServiceDate) { + transitModelIndex.getTripOnServiceDateById().put(id, tripOnServiceDate); + } + @Override public Collection getAllTripOnServiceDates() { return transitModelIndex.getTripOnServiceDateForTripAndDay().values(); @@ -487,6 +528,29 @@ public TripOnServiceDate getTripOnServiceDateForTripAndDay( return transitModelIndex.getTripOnServiceDateForTripAndDay().get(tripIdAndServiceDate); } + /** + * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix + * this when doing the issue #3030. + */ + @Override + public void addTripOnServiceDateForTripAndDay( + TripIdAndServiceDate tripIdAndServiceDate, + TripOnServiceDate tripOnServiceDate + ) { + transitModelIndex + .getTripOnServiceDateForTripAndDay() + .put(tripIdAndServiceDate, tripOnServiceDate); + } + + /** + * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix + * this when doing the issue #3030. + */ + @Override + public FeedScopedId getOrCreateServiceIdForDate(LocalDate serviceDate) { + return transitModel.getOrCreateServiceIdForDate(serviceDate); + } + @Override public void addTransitMode(TransitMode mode) { this.transitModel.addTransitMode(mode); @@ -579,6 +643,11 @@ public List getModesOfStopLocation(StopLocation stop) { return sortByOccurrenceAndReduce(getPatternModesOfStop(stop)).toList(); } + @Override + public Deduplicator getDeduplicator() { + return transitModel.getDeduplicator(); + } + /** * For each pattern visiting this {@link StopLocation} return its {@link TransitMode} */ diff --git a/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java b/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java index 7d2f99df71b..6a11fe902f2 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java @@ -1,10 +1,16 @@ package org.opentripplanner.transit.service; +import java.time.LocalDate; import org.opentripplanner.model.FeedInfo; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.framework.FeedScopedId; 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.Trip; +import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; /** * Entry point for requests (both read-only and read-write) towards the transit API. @@ -14,9 +20,24 @@ public interface TransitEditorService extends TransitService { void addFeedInfo(FeedInfo info); + void addPatternForTrip(Trip trip, TripPattern pattern); + + void addPatternsForRoute(Route route, TripPattern pattern); + void addRoutes(Route route); void addTransitMode(TransitMode mode); + void addTripForId(FeedScopedId tripId, Trip trip); + + void addTripOnServiceDateById(FeedScopedId id, TripOnServiceDate tripOnServiceDate); + + void addTripOnServiceDateForTripAndDay( + TripIdAndServiceDate tripIdAndServiceDate, + TripOnServiceDate tripOnServiceDate + ); + + FeedScopedId getOrCreateServiceIdForDate(LocalDate serviceDate); + void setTransitLayer(TransitLayer transitLayer); } diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModel.java b/src/main/java/org/opentripplanner/transit/service/TransitModel.java index d5211d5dd2e..27df0941c29 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModel.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModel.java @@ -254,8 +254,6 @@ public void updateCalendarServiceData( * Get or create a serviceId for a given date. This method is used when a new trip is added from a * realtime data update. It make sure the date is in the existing transit service period. *

- * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix - * - this when doing the issue #3030. * * @param serviceDate service date for the added service id * @return service-id for date if it exist or is created. If the given service date is outside the diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java b/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java index 84759ffae72..375e5c57b3d 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java @@ -126,10 +126,6 @@ public Route getRouteForId(FeedScopedId id) { return routeForId.get(id); } - /** - * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix - * - this when doing the issue #3030. - */ public void addRoutes(Route route) { routeForId.put(route.getId(), route); } diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index 83b65c44d12..8469f5ec186 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -25,6 +25,7 @@ import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; +import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.GroupOfRoutes; import org.opentripplanner.transit.model.network.Route; @@ -231,4 +232,6 @@ List stopTimesForPatternAtStop( * So, if more patterns of mode BUS than RAIL visit the stop, the result will be [BUS,RAIL]. */ List getModesOfStopLocation(StopLocation stop); + + Deduplicator getDeduplicator(); } From b508c79f6c8679b3c40a9bb555a22752b4e7ee12 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 12 Jul 2024 09:53:08 +0200 Subject: [PATCH 109/192] Make TimetableSnapshot state final --- .../model/TimetableSnapshot.java | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java index d7d9385d9b8..7d3f6181d44 100644 --- a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java +++ b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java @@ -93,7 +93,7 @@ public class TimetableSnapshot { * The compound key approach better reflects the fact that there should be only one Timetable per * TripPattern and date. */ - private Map> timetables = new HashMap<>(); + private final Map> timetables; /** * For cases where the trip pattern (sequence of stops visited) has been changed by a realtime @@ -101,7 +101,7 @@ public class TimetableSnapshot { * trip ID and the service date. * TODO RT_AB: clarify if this is an index or the original source of truth. */ - private Map realtimeAddedTripPattern = new HashMap<>(); + private final Map realtimeAddedTripPattern; /** * This is an index of TripPatterns, not the primary collection. It tracks which TripPatterns @@ -111,13 +111,13 @@ public class TimetableSnapshot { * more than once. * TODO RT_AB: More general handling of all realtime indexes outside primary data structures. */ - private SetMultimap patternsForStop = HashMultimap.create(); + private final SetMultimap patternsForStop; /** * Boolean value indicating that timetable snapshot is read only if true. Once it is true, it * shouldn't be possible to change it to false anymore. */ - private boolean readOnly = false; + private final boolean readOnly; /** * Boolean value indicating that this timetable snapshot contains changes compared to the state of @@ -125,6 +125,22 @@ public class TimetableSnapshot { */ private boolean dirty = false; + public TimetableSnapshot() { + this(new HashMap<>(), new HashMap<>(), HashMultimap.create(), false); + } + + private TimetableSnapshot( + Map> timetables, + Map realtimeAddedTripPattern, + SetMultimap patternsForStop, + boolean readOnly + ) { + this.timetables = timetables; + this.realtimeAddedTripPattern = realtimeAddedTripPattern; + this.patternsForStop = patternsForStop; + this.readOnly = readOnly; + } + /** * Returns an updated timetable for the specified pattern if one is available in this snapshot, or * the originally scheduled timetable if there are no updates in this snapshot. @@ -235,12 +251,15 @@ public TimetableSnapshot commit(TransitLayerUpdater transitLayerUpdater, boolean throw new ConcurrentModificationException("This TimetableSnapshot is read-only."); } - TimetableSnapshot ret = new TimetableSnapshot(); if (!force && !this.isDirty()) { return null; } - ret.timetables = Map.copyOf(timetables); - ret.realtimeAddedTripPattern = Map.copyOf(realtimeAddedTripPattern); + TimetableSnapshot ret = new TimetableSnapshot( + Map.copyOf(timetables), + Map.copyOf(realtimeAddedTripPattern), + ImmutableSetMultimap.copyOf(patternsForStop), + true + ); if (transitLayerUpdater != null) { transitLayerUpdater.update(dirtyTimetables, timetables); @@ -249,9 +268,6 @@ public TimetableSnapshot commit(TransitLayerUpdater transitLayerUpdater, boolean this.dirtyTimetables.clear(); this.dirty = false; - ret.patternsForStop = ImmutableSetMultimap.copyOf(patternsForStop); - - ret.readOnly = true; // mark the snapshot as henceforth immutable return ret; } From 126b60291e63428e716a00354ef0d23d7813e4bf Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 15 Jul 2024 10:18:03 +0200 Subject: [PATCH 110/192] Fix Javadoc [ci skip] --- .../java/org/opentripplanner/openstreetmap/model/OSMNode.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java index a1897d87124..d181cde4564 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMNode.java @@ -38,7 +38,8 @@ public boolean hasCrossingTrafficLight() { return hasTag("crossing") && "traffic_signals".equals(getTag("crossing")); } - /* Checks if this node is a barrier which prevents motor vehicle traffic + /** + * Checks if this node is a barrier which prevents motor vehicle traffic. * * @return true if it is */ From c6ae01a5d62fcb0ec61f0f4c832372bc41e00275 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 15 Jul 2024 10:30:42 +0200 Subject: [PATCH 111/192] Add test for allPassengerVehicles --- .../netex/mapping/VehicleParkingMapperTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java index 98dbf661839..b81075a5b0e 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.rutebanken.netex.model.ParkingVehicleEnumeration.AGRICULTURAL_VEHICLE; +import static org.rutebanken.netex.model.ParkingVehicleEnumeration.ALL_PASSENGER_VEHICLES; import static org.rutebanken.netex.model.ParkingVehicleEnumeration.CAR; import static org.rutebanken.netex.model.ParkingVehicleEnumeration.CAR_WITH_CARAVAN; import static org.rutebanken.netex.model.ParkingVehicleEnumeration.CYCLE; @@ -31,7 +32,7 @@ class VehicleParkingMapperTest { ); public static List> carCases() { - return List.of(Set.of(), Set.of(CAR, AGRICULTURAL_VEHICLE)); + return List.of(Set.of(), Set.of(CAR, AGRICULTURAL_VEHICLE, ALL_PASSENGER_VEHICLES)); } @ParameterizedTest From acaa41fa1167decf003db61a9b70a1c1da24319a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 15 Jul 2024 16:14:48 +0200 Subject: [PATCH 112/192] Take NeTEx hierarchy into account when mapping lots --- .../inspector/vector/KeyValue.java | 21 +++++++++++ .../vector/vertex/VertexPropertyMapper.java | 36 ++++++++++++++++--- .../model/impl/OtpTransitServiceBuilder.java | 11 ++++++ .../opentripplanner/netex/NetexBundle.java | 9 ----- .../opentripplanner/netex/NetexModule.java | 2 +- .../netex/index/NetexEntityIndex.java | 2 +- .../api/NetexEntityIndexReadOnlyView.java | 2 +- .../netex/mapping/NetexMapper.java | 25 ++++++++----- 8 files changed, 83 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java b/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java index 6c8b0f3aa4e..05429b77fb7 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java +++ b/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java @@ -1,7 +1,28 @@ package org.opentripplanner.inspector.vector; +import java.util.Collection; +import java.util.stream.Collectors; +import org.opentripplanner.transit.model.framework.FeedScopedId; + public record KeyValue(String key, Object value) { public static KeyValue kv(String key, Object value) { return new KeyValue(key, value); } + public static KeyValue kv(String key, FeedScopedId value) { + if(value !=null){ + return new KeyValue(key, value.toString()); + } + else { + return new KeyValue(key, null); + } + } + + /** + * Takes a key and a collection of values, calls toString on the values and joins them using + * comma as the separator. + */ + public static KeyValue kColl(String key, Collection value) { + var values = value.stream().map(Object::toString).collect(Collectors.joining(",")); + return new KeyValue(key, values); + } } diff --git a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java index 83b4a675fdb..01f5263b11a 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java @@ -1,16 +1,22 @@ package org.opentripplanner.inspector.vector.vertex; +import static org.opentripplanner.inspector.vector.KeyValue.kColl; import static org.opentripplanner.inspector.vector.KeyValue.kv; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.opentripplanner.apis.support.mapping.PropertyMapper; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.inspector.vector.KeyValue; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingEntrance; import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex; import org.opentripplanner.street.model.vertex.BarrierVertex; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.model.vertex.Vertex; +import org.opentripplanner.street.search.TraverseMode; public class VertexPropertyMapper extends PropertyMapper { @@ -23,14 +29,36 @@ protected Collection map(Vertex input) { List properties = switch (input) { case BarrierVertex v -> List.of(kv("permission", v.getBarrierPermissions().toString())); - case VehicleRentalPlaceVertex v -> List.of(kv("rentalId", v.getStation().getId())); + case VehicleRentalPlaceVertex v -> List.of(kv("rentalId", v.getStation())); case VehicleParkingEntranceVertex v -> List.of( - kv("rentalId", v.getVehicleParking().getId()), - kv("walkAccessible", Boolean.toString(v.isWalkAccessible())), - kv("carAccessible", Boolean.toString(v.isCarAccessible())) + kv("parkingId", v.getVehicleParking().getId()), + kColl("spacesFor", spacesFor(v.getVehicleParking())), + kColl("traversalPermission", traversalPermissions(v.getParkingEntrance())) ); default -> List.of(); }; return ListUtils.combine(baseProps, properties); } + + private Set spacesFor(VehicleParking vehicleParking) { + var ret = new HashSet(); + if (vehicleParking.hasAnyCarPlaces()) { + ret.add(TraverseMode.CAR); + } + if (vehicleParking.hasBicyclePlaces()) { + ret.add(TraverseMode.BICYCLE); + } + return ret; + } + + private Set traversalPermissions(VehicleParkingEntrance entrance) { + var ret = new HashSet(); + if (entrance.isCarAccessible()) { + ret.add(TraverseMode.CAR); + } + if (entrance.isWalkAccessible()) { + ret.add(TraverseMode.WALK); + } + return ret; + } } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 43c18cec59d..373b99f0bc6 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -24,6 +24,7 @@ import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.model.transfer.TransferPoint; import org.opentripplanner.routing.api.request.framework.TimePenalty; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.DefaultEntityById; @@ -116,6 +117,8 @@ public class OtpTransitServiceBuilder { private final EntityById groupOfRouteById = new DefaultEntityById<>(); + private final List vehicleParkings = new ArrayList<>(); + private final DataImportIssueStore issueStore; public OtpTransitServiceBuilder(StopModel stopModel, DataImportIssueStore issueStore) { @@ -264,6 +267,14 @@ public CalendarServiceData buildCalendarServiceData() { ); } + /** + * The list of parking lots contained in the transit data (so far only NeTEx). + * Note that parking lots can also be sourced from OSM data as well as realtime updaters. + */ + public List vehicleParkings() { + return vehicleParkings; + } + public OtpTransitService build() { return new OtpTransitServiceImpl(this); } diff --git a/src/main/java/org/opentripplanner/netex/NetexBundle.java b/src/main/java/org/opentripplanner/netex/NetexBundle.java index 7d80f431309..8d6f098de89 100644 --- a/src/main/java/org/opentripplanner/netex/NetexBundle.java +++ b/src/main/java/org/opentripplanner/netex/NetexBundle.java @@ -3,7 +3,6 @@ import jakarta.xml.bind.JAXBException; import java.io.Closeable; import java.io.IOException; -import java.util.Collection; import java.util.List; import java.util.Set; import org.opentripplanner.datastore.api.CompositeDataSource; @@ -18,7 +17,6 @@ import org.opentripplanner.netex.loader.parser.NetexDocumentParser; import org.opentripplanner.netex.mapping.NetexMapper; import org.opentripplanner.netex.validation.Validator; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.transit.model.framework.Deduplicator; import org.rutebanken.netex.model.PublicationDeliveryStructure; import org.slf4j.Logger; @@ -117,13 +115,6 @@ public void close() throws IOException { source.close(); } - /** - * Return the list of parking lots contained in the netex bundle. - */ - public Collection vehicleParkings() { - return mapper.mapVehicleParkings(); - } - /** Load all files entries in the bundle */ private void loadFileEntries() { // Load global shared files diff --git a/src/main/java/org/opentripplanner/netex/NetexModule.java b/src/main/java/org/opentripplanner/netex/NetexModule.java index c0390e9793d..2bf3403395c 100644 --- a/src/main/java/org/opentripplanner/netex/NetexModule.java +++ b/src/main/java/org/opentripplanner/netex/NetexModule.java @@ -102,7 +102,7 @@ public void buildGraph() { transitModel.validateTimeZones(); - var lots = netexBundle.vehicleParkings(); + var lots = transitBuilder.vehicleParkings(); graph.getVehicleParkingService().updateVehicleParking(lots, List.of()); var linker = new VehicleParkingHelper(graph); lots.forEach(linker::linkVehicleParkingToGraph); diff --git a/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java b/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java index 6a5308f91a3..0cb94f66a77 100644 --- a/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java +++ b/src/main/java/org/opentripplanner/netex/index/NetexEntityIndex.java @@ -358,7 +358,7 @@ public ReadOnlyHierarchicalVersionMapById getStopPlaceById() { } @Override - public ReadOnlyHierarchicalMapById getParkings() { + public ReadOnlyHierarchicalMapById getParkingsById() { return parkings; } diff --git a/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java b/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java index 24a3d7a8ac4..37b8e9790b9 100644 --- a/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java +++ b/src/main/java/org/opentripplanner/netex/index/api/NetexEntityIndexReadOnlyView.java @@ -81,7 +81,7 @@ public interface NetexEntityIndexReadOnlyView { ReadOnlyHierarchicalVersionMapById getStopPlaceById(); - ReadOnlyHierarchicalMapById getParkings(); + ReadOnlyHierarchicalMapById getParkingsById(); ReadOnlyHierarchicalVersionMapById getTariffZonesById(); diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index e88a75f750a..8e3fd6e166d 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -19,7 +19,6 @@ import org.opentripplanner.netex.mapping.calendar.CalendarServiceBuilder; import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; import org.opentripplanner.netex.mapping.support.NetexMapperIndexes; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.Deduplicator; @@ -78,9 +77,9 @@ public class NetexMapper { /** * Shared/cached entity index, used by more than one mapper. This index provides alternative - * indexes to netex entites, as well as global indexes to OTP domain objects needed in the mapping + * indexes to netex entities, as well as global indexes to OTP domain objects needed in the mapping * process. Some of these indexes are feed scoped, and some are file group level scoped. As a rule - * of tomb the indexes for OTP Model entities are global(small memory overhead), while the indexes + * of thumb the indexes for OTP Model entities are global(small memory overhead), while the indexes * for the Netex entities follow the main index {@link #currentNetexIndex}, hence sopped by file * group. */ @@ -159,7 +158,7 @@ public void finishUp() { /** *

- * This method mapes the last Netex file imported using the *local* entities in the hierarchical + * This method maps the last Netex file imported using the *local* entities in the hierarchical * {@link NetexEntityIndexReadOnlyView}. *

*

@@ -200,12 +199,9 @@ public void mapNetexToOtp(NetexEntityIndexReadOnlyView netexIndex) { mapTripPatterns(serviceIds); mapNoticeAssignments(); - addEntriesToGroupMapperForPostProcessingLater(); - } + mapVehicleParkings(); - public Collection mapVehicleParkings() { - var mapper = new VehicleParkingMapper(idFactory); - return mapper.map(currentNetexIndex.getParkings().localValues()); + addEntriesToGroupMapperForPostProcessingLater(); } /* PRIVATE METHODS */ @@ -525,6 +521,17 @@ private void addEntriesToGroupMapperForPostProcessingLater() { } } + private void mapVehicleParkings() { + var mapper = new VehicleParkingMapper(idFactory); + currentNetexIndex + .getParkingsById() + .localKeys() + .forEach(id -> { + var parking = mapper.map(currentNetexIndex.getParkingsById().lookup(id)); + transitBuilder.vehicleParkings().add(parking); + }); + } + /** * The start of period is used to find the valid entities based on the current time. This should * probably be configurable in the future, or even better incorporate the version number into the From ac046c67387920bc977f74281e5e284e0bff8bf4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 15 Jul 2024 18:06:11 +0200 Subject: [PATCH 113/192] Take missing capacity into account --- .../netex/mapping/NetexMapper.java | 6 ++-- .../netex/mapping/VehicleParkingMapper.java | 21 ++++++++---- .../mapping/VehicleParkingMapperTest.java | 34 +++++++++++++++---- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index 8e3fd6e166d..897260fbddb 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -522,13 +522,15 @@ private void addEntriesToGroupMapperForPostProcessingLater() { } private void mapVehicleParkings() { - var mapper = new VehicleParkingMapper(idFactory); + var mapper = new VehicleParkingMapper(idFactory, issueStore); currentNetexIndex .getParkingsById() .localKeys() .forEach(id -> { var parking = mapper.map(currentNetexIndex.getParkingsById().lookup(id)); - transitBuilder.vehicleParkings().add(parking); + if (parking != null) { + transitBuilder.vehicleParkings().add(parking); + } }); } diff --git a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java index 84dfc201909..7f78e7191ef 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java @@ -4,10 +4,10 @@ import static org.rutebanken.netex.model.ParkingVehicleEnumeration.E_CYCLE; import static org.rutebanken.netex.model.ParkingVehicleEnumeration.PEDAL_CYCLE; -import java.util.Collection; import java.util.Set; -import java.util.stream.Collectors; +import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; @@ -26,16 +26,23 @@ class VehicleParkingMapper { E_CYCLE, CYCLE ); + private final DataImportIssueStore issueStore; - VehicleParkingMapper(FeedScopedIdFactory idFactory) { + VehicleParkingMapper(FeedScopedIdFactory idFactory, DataImportIssueStore issueStore) { this.idFactory = idFactory; + this.issueStore = issueStore; } - Collection map(Collection parkings) { - return parkings.stream().map(this::map).collect(Collectors.toUnmodifiableSet()); - } - + @Nullable VehicleParking map(Parking parking) { + if (parking.getTotalCapacity() == null) { + issueStore.add( + "MissingParkingCapacity", + "NeTEx Parking %s does not contain totalCapacity", + parking.getId() + ); + return null; + } return VehicleParking .builder() .id(idFactory.createId(parking.getId())) diff --git a/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java index b81075a5b0e..bf56be1be1b 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/VehicleParkingMapperTest.java @@ -1,6 +1,7 @@ package org.opentripplanner.netex.mapping; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.rutebanken.netex.model.ParkingVehicleEnumeration.AGRICULTURAL_VEHICLE; import static org.rutebanken.netex.model.ParkingVehicleEnumeration.ALL_PASSENGER_VEHICLES; @@ -13,9 +14,13 @@ import java.math.BigInteger; import java.util.List; import java.util.Set; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.framework.geometry.WgsCoordinate; +import org.opentripplanner.graph_builder.issue.api.DataImportIssue; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.graph_builder.issue.service.DefaultDataImportIssueStore; import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; @@ -27,10 +32,6 @@ class VehicleParkingMapperTest { - private static final VehicleParkingMapper MAPPER = new VehicleParkingMapper( - new FeedScopedIdFactory("parking") - ); - public static List> carCases() { return List.of(Set.of(), Set.of(CAR, AGRICULTURAL_VEHICLE, ALL_PASSENGER_VEHICLES)); } @@ -38,7 +39,7 @@ public static List> carCases() { @ParameterizedTest @MethodSource("carCases") void mapCarLot(Set vehicleTypes) { - var vp = MAPPER.map(parking(vehicleTypes)); + var vp = mapper().map(parking(vehicleTypes)); assertCommonProperties(vp); assertTrue(vp.hasAnyCarPlaces()); assertEquals(VehicleParkingSpaces.builder().carSpaces(10).build(), vp.getCapacity()); @@ -51,12 +52,33 @@ public static List> bicycleCases() { @ParameterizedTest @MethodSource("bicycleCases") void mapBicycleLot(Set vehicleTypes) { - var vp = MAPPER.map(parking(vehicleTypes)); + var vp = mapper().map(parking(vehicleTypes)); assertCommonProperties(vp); assertTrue(vp.hasBicyclePlaces()); assertEquals(VehicleParkingSpaces.builder().bicycleSpaces(10).build(), vp.getCapacity()); } + @Test + void dropEmptyCapacity() { + var parking = parking(Set.of(CAR)); + parking.setTotalCapacity(null); + var issueStore = new DefaultDataImportIssueStore(); + var vp = mapper(issueStore).map(parking); + assertNull(vp); + assertEquals( + List.of("MissingParkingCapacity"), + issueStore.listIssues().stream().map(DataImportIssue::getType).toList() + ); + } + + private VehicleParkingMapper mapper() { + return mapper(DataImportIssueStore.NOOP); + } + + private static VehicleParkingMapper mapper(DataImportIssueStore issueStore) { + return new VehicleParkingMapper(new FeedScopedIdFactory("parking"), issueStore); + } + private static void assertCommonProperties(VehicleParking vp) { assertEquals("A name", vp.getName().toString()); assertEquals(new WgsCoordinate(10, 20), vp.getCoordinate()); From 618e0a1a7e2ea7ef438f68ee9c6a1319eb70d675 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 15 Jul 2024 18:20:39 +0200 Subject: [PATCH 114/192] Add more robust null check for issue --- .../netex/mapping/VehicleParkingMapper.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java index 7f78e7191ef..f0ca2aeb51f 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java @@ -38,8 +38,8 @@ VehicleParking map(Parking parking) { if (parking.getTotalCapacity() == null) { issueStore.add( "MissingParkingCapacity", - "NeTEx Parking %s does not contain totalCapacity", - parking.getId() + "NeTEx Parking '%s' does not contain totalCapacity", + parkingDebugId(parking) ); return null; } @@ -55,6 +55,18 @@ VehicleParking map(Parking parking) { .build(); } + private static String parkingDebugId(Parking parking) { + if (parking.getId() != null) { + return parking.getId(); + } else if (parking.getName() != null) { + return parking.getName().getValue(); + } else if (parking.getCentroid() != null) { + return parking.getCentroid().toString(); + } else { + return parking.toString(); + } + } + private VehicleParking.VehicleParkingEntranceCreator mapEntrance(Parking parking) { return builder -> builder From 3dda63ccbffb67727f95d96e6921b5e46f5aa02c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 02:00:58 +0000 Subject: [PATCH 115/192] fix(deps): update dependency edu.ucar:netcdf4 to v5.6.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cb331acccd5..b7f26812e99 100644 --- a/pom.xml +++ b/pom.xml @@ -64,7 +64,7 @@ 3.1.7 5.10.3 1.13.2 - 5.5.3 + 5.6.0 1.5.6 9.11.1 2.0.13 From 098123a06d344fbea7a84e6e7b3b3439b165b768 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 16 Jul 2024 11:57:05 +0200 Subject: [PATCH 116/192] Apply review suggestion --- .../ext/siri/AddedTripBuilderTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java index 911fb015c27..e322b0ddea9 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java @@ -78,7 +78,7 @@ class AddedTripBuilderTest { private final Deduplicator DEDUPLICATOR = new Deduplicator(); private final TransitModel TRANSIT_MODEL = new TransitModel(STOP_MODEL, DEDUPLICATOR); - private TransitEditorService TRANSIT_SERVICE; + private TransitEditorService transitService; private EntityResolver ENTITY_RESOLVER; @BeforeEach @@ -103,7 +103,7 @@ void setUp() { // Create transit model index TRANSIT_MODEL.index(); - TRANSIT_SERVICE = new DefaultTransitService(TRANSIT_MODEL); + transitService = new DefaultTransitService(TRANSIT_MODEL); // Create the entity resolver only after the model has been indexed ENTITY_RESOLVER = @@ -113,7 +113,7 @@ void setUp() { @Test void testAddedTrip() { var addedTrip = new AddedTripBuilder( - TRANSIT_SERVICE, + transitService, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -242,7 +242,7 @@ void testAddedTrip() { @Test void testAddedTripOnAddedRoute() { var firstAddedTrip = new AddedTripBuilder( - TRANSIT_SERVICE, + transitService, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -268,7 +268,7 @@ void testAddedTripOnAddedRoute() { var tripId2 = TransitModelForTest.id("TRIP_ID_2"); var secondAddedTrip = new AddedTripBuilder( - TRANSIT_SERVICE, + transitService, ENTITY_RESOLVER, AbstractTransitEntity::getId, tripId2, @@ -319,7 +319,7 @@ void testAddedTripOnAddedRoute() { @Test void testAddedTripOnExistingRoute() { var addedTrip = new AddedTripBuilder( - TRANSIT_SERVICE, + transitService, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -350,7 +350,7 @@ void testAddedTripOnExistingRoute() { @Test void testAddedTripWithoutReplacedRoute() { var addedTrip = new AddedTripBuilder( - TRANSIT_SERVICE, + transitService, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -393,7 +393,7 @@ void testAddedTripWithoutReplacedRoute() { @Test void testAddedTripFailOnMissingServiceId() { var addedTrip = new AddedTripBuilder( - TRANSIT_SERVICE, + transitService, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -448,7 +448,7 @@ void testAddedTripFailOnNonIncreasingDwellTime() { ); var addedTrip = new AddedTripBuilder( - TRANSIT_SERVICE, + transitService, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -487,7 +487,7 @@ void testAddedTripFailOnTooFewCalls() { .build() ); var addedTrip = new AddedTripBuilder( - TRANSIT_SERVICE, + transitService, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, @@ -534,7 +534,7 @@ void testAddedTripFailOnUnknownStop() { .build() ); var addedTrip = new AddedTripBuilder( - TRANSIT_SERVICE, + transitService, ENTITY_RESOLVER, AbstractTransitEntity::getId, TRIP_ID, From 949a6c7c080507a578440d8213f965f226d54795 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jul 2024 12:49:13 +0200 Subject: [PATCH 117/192] Add comment --- docs/examples/entur/build-config.json | 3 ++- .../opentripplanner/netex/mapping/VehicleParkingMapper.java | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/examples/entur/build-config.json b/docs/examples/entur/build-config.json index e9351882774..c8f6b4c9ccd 100644 --- a/docs/examples/entur/build-config.json +++ b/docs/examples/entur/build-config.json @@ -33,7 +33,8 @@ { "type": "netex", "source": "gs://${OTP_GCS_BUCKET}/outbound/netex/rb_norway-aggregated-netex-otp2.zip", - "feedId": "EN" + "feedId": "EN", + "ignoreParkingData": true } ], "osm": [ diff --git a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java index f0ca2aeb51f..862c5f0c648 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/VehicleParkingMapper.java @@ -55,6 +55,10 @@ VehicleParking map(Parking parking) { .build(); } + /** + * In the Nordic profile many fields of {@link Parking} are optional so even adding the ID to the + * issue store can lead to NPEs. For this reason we have a lot of fallbacks. + */ private static String parkingDebugId(Parking parking) { if (parking.getId() != null) { return parking.getId(); From 44edc443d47947b6bc9ea6294be44d0c03d911fa Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jul 2024 13:55:46 +0200 Subject: [PATCH 118/192] Add config option to ignore parking --- docs/BuildConfiguration.md | 2 + docs/examples/entur/build-config.json | 2 +- .../opentripplanner/netex/NetexBundle.java | 9 ++-- .../netex/config/IgnorableFeature.java | 6 +++ .../netex/config/NetexFeedParameters.java | 41 ++++++++++++++----- .../netex/configure/NetexConfigure.java | 2 +- .../loader/parser/NetexDocumentParser.java | 18 ++++---- .../netex/loader/parser/SiteFrameParser.java | 13 +++++- .../config/buildconfig/NetexConfig.java | 9 ++++ .../loader/parser/SiteFrameParserTest.java | 3 +- 10 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 src/main/java/org/opentripplanner/netex/config/IgnorableFeature.java diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index cefd8d2cf2f..d6c6c87295d 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -78,6 +78,7 @@ Sections follow that describe particular settings in more depth. |    [groupFilePattern](#nd_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | |    ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | |    [ignoreFilePattern](#nd_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | +|    ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `false` | 2.6 | |    noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | |    [sharedFilePattern](#nd_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | |    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | @@ -106,6 +107,7 @@ Sections follow that describe particular settings in more depth. |       [groupFilePattern](#tf_1_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | |       ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | |       [ignoreFilePattern](#tf_1_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | +|       ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `false` | 2.6 | |       noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | |       [sharedFilePattern](#tf_1_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | |       [sharedGroupFilePattern](#tf_1_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | diff --git a/docs/examples/entur/build-config.json b/docs/examples/entur/build-config.json index c8f6b4c9ccd..2acea588234 100644 --- a/docs/examples/entur/build-config.json +++ b/docs/examples/entur/build-config.json @@ -34,7 +34,7 @@ "type": "netex", "source": "gs://${OTP_GCS_BUCKET}/outbound/netex/rb_norway-aggregated-netex-otp2.zip", "feedId": "EN", - "ignoreParkingData": true + "ignoreParking": true } ], "osm": [ diff --git a/src/main/java/org/opentripplanner/netex/NetexBundle.java b/src/main/java/org/opentripplanner/netex/NetexBundle.java index 8d6f098de89..b9b8be224a9 100644 --- a/src/main/java/org/opentripplanner/netex/NetexBundle.java +++ b/src/main/java/org/opentripplanner/netex/NetexBundle.java @@ -9,6 +9,7 @@ import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.model.impl.OtpTransitServiceBuilder; +import org.opentripplanner.netex.config.IgnorableFeature; import org.opentripplanner.netex.config.NetexFeedParameters; import org.opentripplanner.netex.index.NetexEntityIndex; import org.opentripplanner.netex.loader.GroupEntries; @@ -45,7 +46,7 @@ public class NetexBundle implements Closeable { private final Set ferryIdsNotAllowedForBicycle; private final double maxStopToShapeSnapDistance; private final boolean noTransfersOnIsolatedStops; - private final boolean ignoreFareFrame; + private final Set ignoredFeatures; /** The NeTEx entities loaded from the input files and passed on to the mapper. */ private NetexEntityIndex index = new NetexEntityIndex(); /** Report errors to issue store */ @@ -62,7 +63,7 @@ public NetexBundle( Set ferryIdsNotAllowedForBicycle, double maxStopToShapeSnapDistance, boolean noTransfersOnIsolatedStops, - boolean ignoreFareFrame + Set ignorableFeatures ) { this.feedId = feedId; this.source = source; @@ -71,7 +72,7 @@ public NetexBundle( this.ferryIdsNotAllowedForBicycle = ferryIdsNotAllowedForBicycle; this.maxStopToShapeSnapDistance = maxStopToShapeSnapDistance; this.noTransfersOnIsolatedStops = noTransfersOnIsolatedStops; - this.ignoreFareFrame = ignoreFareFrame; + this.ignoredFeatures = Set.copyOf(ignorableFeatures); } /** load the bundle, map it to the OTP transit model and return */ @@ -179,7 +180,7 @@ private void loadSingeFileEntry(String fileDescription, DataSource entry) { LOG.info("reading entity {}: {}", fileDescription, entry.name()); issueStore.startProcessingSource(entry.name()); PublicationDeliveryStructure doc = xmlParser.parseXmlDoc(entry.asInputStream()); - NetexDocumentParser.parseAndPopulateIndex(index, doc, ignoreFareFrame); + NetexDocumentParser.parseAndPopulateIndex(index, doc, ignoredFeatures); } catch (JAXBException e) { throw new RuntimeException(e.getMessage(), e); } finally { diff --git a/src/main/java/org/opentripplanner/netex/config/IgnorableFeature.java b/src/main/java/org/opentripplanner/netex/config/IgnorableFeature.java new file mode 100644 index 00000000000..4aa44e062d6 --- /dev/null +++ b/src/main/java/org/opentripplanner/netex/config/IgnorableFeature.java @@ -0,0 +1,6 @@ +package org.opentripplanner.netex.config; + +public enum IgnorableFeature { + FARE_FRAME, + PARKING, +} diff --git a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java index cffecea0d48..4c0a50105b2 100644 --- a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java +++ b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java @@ -1,6 +1,8 @@ package org.opentripplanner.netex.config; import static java.util.Objects.requireNonNull; +import static org.opentripplanner.netex.config.IgnorableFeature.FARE_FRAME; +import static org.opentripplanner.netex.config.IgnorableFeature.PARKING; import java.net.URI; import java.util.Collection; @@ -29,7 +31,7 @@ public class NetexFeedParameters implements DataSourceConfig { private static final String SHARED_GROUP_FILE_PATTERN = "(\\w{3})-.*-shared\\.xml"; private static final String GROUP_FILE_PATTERN = "(\\w{3})-.*\\.xml"; private static final boolean NO_TRANSFERS_ON_ISOLATED_STOPS = false; - private static final boolean IGNORE_FARE_FRAME = false; + private static final Set IGNORED_FEATURES = Set.of(); private static final Set FERRY_IDS_NOT_ALLOWED_FOR_BICYCLE = Collections.emptySet(); @@ -48,7 +50,7 @@ public class NetexFeedParameters implements DataSourceConfig { private final String ignoreFilePattern; private final Set ferryIdsNotAllowedForBicycle; private final boolean noTransfersOnIsolatedStops; - private final boolean ignoreFareFrame; + private final Set ignoredFeatures; private NetexFeedParameters() { this.source = null; @@ -63,7 +65,7 @@ private NetexFeedParameters() { } this.ferryIdsNotAllowedForBicycle = FERRY_IDS_NOT_ALLOWED_FOR_BICYCLE; this.noTransfersOnIsolatedStops = NO_TRANSFERS_ON_ISOLATED_STOPS; - this.ignoreFareFrame = IGNORE_FARE_FRAME; + this.ignoredFeatures = IGNORED_FEATURES; } private NetexFeedParameters(Builder builder) { @@ -75,7 +77,7 @@ private NetexFeedParameters(Builder builder) { this.ignoreFilePattern = requireNonNull(builder.ignoreFilePattern); this.ferryIdsNotAllowedForBicycle = Set.copyOf(builder.ferryIdsNotAllowedForBicycle); this.noTransfersOnIsolatedStops = builder.noTransfersOnIsolatedStops; - this.ignoreFareFrame = builder.ignoreFareFrame; + this.ignoredFeatures = Set.copyOf(builder.ignoredFeatures); } public static Builder of() { @@ -127,7 +129,11 @@ public boolean noTransfersOnIsolatedStops() { /** See {@link org.opentripplanner.standalone.config.buildconfig.NetexConfig}. */ public boolean ignoreFareFrame() { - return ignoreFareFrame; + return ignoredFeatures.contains(FARE_FRAME); + } + + public boolean ignoreParking() { + return ignoredFeatures.contains(PARKING); } @Override @@ -142,7 +148,7 @@ public boolean equals(Object o) { sharedFilePattern.equals(that.sharedFilePattern) && sharedGroupFilePattern.equals(that.sharedGroupFilePattern) && groupFilePattern.equals(that.groupFilePattern) && - ignoreFareFrame == that.ignoreFareFrame && + ignoredFeatures == that.ignoredFeatures && ferryIdsNotAllowedForBicycle.equals(that.ferryIdsNotAllowedForBicycle) ); } @@ -156,7 +162,7 @@ public int hashCode() { sharedFilePattern, sharedGroupFilePattern, groupFilePattern, - ignoreFareFrame, + ignoredFeatures, ferryIdsNotAllowedForBicycle ); } @@ -171,11 +177,15 @@ public String toString() { .addStr("sharedGroupFilePattern", sharedGroupFilePattern, DEFAULT.sharedGroupFilePattern) .addStr("groupFilePattern", groupFilePattern, DEFAULT.groupFilePattern) .addStr("ignoreFilePattern", ignoreFilePattern, DEFAULT.ignoreFilePattern) - .addBoolIfTrue("ignoreFareFrame", ignoreFareFrame) + .addCol("ignoredFeatures", ignoredFeatures) .addCol("ferryIdsNotAllowedForBicycle", ferryIdsNotAllowedForBicycle, Set.of()) .toString(); } + public Set ignoredFeatures() { + return ignoredFeatures; + } + public static class Builder { private final NetexFeedParameters original; @@ -187,7 +197,7 @@ public static class Builder { private String ignoreFilePattern; private final Set ferryIdsNotAllowedForBicycle = new HashSet<>(); private boolean noTransfersOnIsolatedStops; - private boolean ignoreFareFrame; + private final Set ignoredFeatures; private Builder(NetexFeedParameters original) { this.original = original; @@ -199,7 +209,7 @@ private Builder(NetexFeedParameters original) { this.ignoreFilePattern = original.ignoreFilePattern; this.ferryIdsNotAllowedForBicycle.addAll(original.ferryIdsNotAllowedForBicycle); this.noTransfersOnIsolatedStops = original.noTransfersOnIsolatedStops; - this.ignoreFareFrame = original.ignoreFareFrame; + this.ignoredFeatures = new HashSet<>(original.ignoredFeatures); } public URI source() { @@ -247,7 +257,16 @@ public Builder withNoTransfersOnIsolatedStops(boolean noTransfersOnIsolatedStops } public Builder withIgnoreFareFrame(boolean ignoreFareFrame) { - this.ignoreFareFrame = ignoreFareFrame; + if (ignoreFareFrame) { + this.ignoredFeatures.add(FARE_FRAME); + } + return this; + } + + public Builder withIgnoreParking(boolean ignoreParking) { + if (ignoreParking) { + this.ignoredFeatures.add(PARKING); + } return this; } diff --git a/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java b/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java index 464c03f28e1..50c49836246 100644 --- a/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java +++ b/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java @@ -75,7 +75,7 @@ public NetexBundle netexBundle( config.ferryIdsNotAllowedForBicycle(), buildParams.maxStopToShapeSnapDistance, config.noTransfersOnIsolatedStops(), - config.ignoreFareFrame() + config.ignoredFeatures() ); } diff --git a/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java b/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java index 925b6dfd019..00ffa8a9d2f 100644 --- a/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java +++ b/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java @@ -1,8 +1,12 @@ package org.opentripplanner.netex.loader.parser; +import static org.opentripplanner.netex.config.IgnorableFeature.FARE_FRAME; + import jakarta.xml.bind.JAXBElement; import java.util.Collection; import java.util.List; +import java.util.Set; +import org.opentripplanner.netex.config.IgnorableFeature; import org.opentripplanner.netex.index.NetexEntityIndex; import org.rutebanken.netex.model.Common_VersionFrameStructure; import org.rutebanken.netex.model.CompositeFrame; @@ -30,11 +34,11 @@ public class NetexDocumentParser { private static final Logger LOG = LoggerFactory.getLogger(NetexDocumentParser.class); private final NetexEntityIndex netexIndex; - private final boolean ignoreFareFrame; + private final Set ignoredFeatures; - private NetexDocumentParser(NetexEntityIndex netexIndex, boolean ignoreFareFrame) { + private NetexDocumentParser(NetexEntityIndex netexIndex, Set ignoredFeatures) { this.netexIndex = netexIndex; - this.ignoreFareFrame = ignoreFareFrame; + this.ignoredFeatures = ignoredFeatures; } /** @@ -44,9 +48,9 @@ private NetexDocumentParser(NetexEntityIndex netexIndex, boolean ignoreFareFrame public static void parseAndPopulateIndex( NetexEntityIndex index, PublicationDeliveryStructure doc, - boolean ignoreFareFrame + Set ignoredFeatures ) { - new NetexDocumentParser(index, ignoreFareFrame).parse(doc); + new NetexDocumentParser(index, ignoredFeatures).parse(doc); } public static void finnishUp() { @@ -74,8 +78,8 @@ private void parseCommonFrame(Common_VersionFrameStructure value) { } else if (value instanceof ServiceFrame) { parse((ServiceFrame) value, new ServiceFrameParser(netexIndex.flexibleStopPlaceById)); } else if (value instanceof SiteFrame) { - parse((SiteFrame) value, new SiteFrameParser()); - } else if (!ignoreFareFrame && value instanceof FareFrame) { + parse((SiteFrame) value, new SiteFrameParser(ignoredFeatures)); + } else if (ignoredFeatures.contains(FARE_FRAME) && value instanceof FareFrame) { parse((FareFrame) value, new FareFrameParser()); } else if (value instanceof CompositeFrame) { // We recursively parse composite frames and content until there diff --git a/src/main/java/org/opentripplanner/netex/loader/parser/SiteFrameParser.java b/src/main/java/org/opentripplanner/netex/loader/parser/SiteFrameParser.java index 7651bbb39d9..38cd91ded98 100644 --- a/src/main/java/org/opentripplanner/netex/loader/parser/SiteFrameParser.java +++ b/src/main/java/org/opentripplanner/netex/loader/parser/SiteFrameParser.java @@ -1,10 +1,14 @@ package org.opentripplanner.netex.loader.parser; +import static org.opentripplanner.netex.config.IgnorableFeature.PARKING; + import jakarta.xml.bind.JAXBElement; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Set; import org.opentripplanner.framework.application.OTPFeature; +import org.opentripplanner.netex.config.IgnorableFeature; import org.opentripplanner.netex.index.NetexEntityIndex; import org.opentripplanner.netex.support.JAXBUtils; import org.rutebanken.netex.model.FlexibleStopPlace; @@ -36,8 +40,15 @@ class SiteFrameParser extends NetexParser { private final Collection tariffZones = new ArrayList<>(); private final Collection quays = new ArrayList<>(); + private final Collection parkings = new ArrayList<>(0); + private final Set ignoredFeatures; + + SiteFrameParser(Set ignoredFeatures) { + this.ignoredFeatures = ignoredFeatures; + } + @Override public void parse(Site_VersionFrameStructure frame) { if (frame.getStopPlaces() != null) { @@ -54,7 +65,7 @@ public void parse(Site_VersionFrameStructure frame) { parseTariffZones(frame.getTariffZones().getTariffZone()); } - if (frame.getParkings() != null) { + if (!ignoredFeatures.contains(PARKING) && frame.getParkings() != null) { parseParkings(frame.getParkings().getParking()); } // Keep list sorted alphabetically diff --git a/src/main/java/org/opentripplanner/standalone/config/buildconfig/NetexConfig.java b/src/main/java/org/opentripplanner/standalone/config/buildconfig/NetexConfig.java index d4cd4521e54..6be400b7549 100644 --- a/src/main/java/org/opentripplanner/standalone/config/buildconfig/NetexConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/buildconfig/NetexConfig.java @@ -3,6 +3,7 @@ import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_0; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_3; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_6; import org.opentripplanner.netex.config.NetexFeedParameters; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; @@ -164,6 +165,14 @@ private static NetexFeedParameters.Builder mapFilePatternParameters( .summary("Ignore contents of the FareFrame") .docDefaultValue(base.ignoreFareFrame()) .asBoolean(base.ignoreFareFrame()) + ) + .withIgnoreParking( + config + .of("ignoreParking") + .since(V2_6) + .summary("Ignore Parking elements.") + .docDefaultValue(base.ignoreParking()) + .asBoolean(base.ignoreFareFrame()) ); } diff --git a/src/test/java/org/opentripplanner/netex/loader/parser/SiteFrameParserTest.java b/src/test/java/org/opentripplanner/netex/loader/parser/SiteFrameParserTest.java index efd9365b84d..bc60cff2d0e 100644 --- a/src/test/java/org/opentripplanner/netex/loader/parser/SiteFrameParserTest.java +++ b/src/test/java/org/opentripplanner/netex/loader/parser/SiteFrameParserTest.java @@ -6,6 +6,7 @@ import jakarta.xml.bind.JAXBElement; import java.util.Collection; +import java.util.Set; import org.junit.jupiter.api.Test; import org.opentripplanner.netex.NetexTestDataSupport; import org.opentripplanner.netex.index.NetexEntityIndex; @@ -21,7 +22,7 @@ class SiteFrameParserTest { @Test void testParseQuays() { - SiteFrameParser siteFrameParser = new SiteFrameParser(); + SiteFrameParser siteFrameParser = new SiteFrameParser(Set.of()); SiteFrame siteFrame = OBJECT_FACTORY.createSiteFrame(); NetexEntityIndex netexEntityIndex = new NetexEntityIndex(); From 3a88dd313952d260aae8b4d17e9a5a96cfee2ba4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jul 2024 14:06:07 +0200 Subject: [PATCH 119/192] Add code reuse --- .../netex/config/NetexFeedParameters.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java index 4c0a50105b2..5ff5004d0c8 100644 --- a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java +++ b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java @@ -257,15 +257,18 @@ public Builder withNoTransfersOnIsolatedStops(boolean noTransfersOnIsolatedStops } public Builder withIgnoreFareFrame(boolean ignoreFareFrame) { - if (ignoreFareFrame) { - this.ignoredFeatures.add(FARE_FRAME); - } - return this; + return applyIgnore(ignoreFareFrame, FARE_FRAME); } public Builder withIgnoreParking(boolean ignoreParking) { - if (ignoreParking) { - this.ignoredFeatures.add(PARKING); + return applyIgnore(ignoreParking, PARKING); + } + + private Builder applyIgnore(boolean ignore, IgnorableFeature feature) { + if (ignore) { + ignoredFeatures.add(feature); + } else { + ignoredFeatures.remove(feature); } return this; } From e0f60f552b8d535d9c4bf396ca66249863559f94 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 16 Jul 2024 14:53:33 +0200 Subject: [PATCH 120/192] Fix TimetableSnapshot merge conflict --- .../service/DefaultTransitServiceTest.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index 0cf72dd4748..54733f084d6 100644 --- a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -6,7 +6,7 @@ import static org.opentripplanner.transit.model.basic.TransitMode.RAIL; import static org.opentripplanner.transit.model.basic.TransitMode.TRAM; -import com.google.common.collect.ImmutableSetMultimap; +import java.time.LocalDate; import java.util.Collection; import java.util.List; import java.util.Set; @@ -15,10 +15,13 @@ import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.Deduplicator; +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.RealTimeTripTimes; +import org.opentripplanner.transit.model.timetable.ScheduledTripTimes; class DefaultTransitServiceTest { @@ -36,6 +39,13 @@ class DefaultTransitServiceTest { static TripPattern FERRY_PATTERN = TEST_MODEL.pattern(FERRY).build(); static TripPattern BUS_PATTERN = TEST_MODEL.pattern(BUS).build(); + static StopPattern REAL_TIME_STOP_PATTERN = TransitModelForTest.stopPattern(STOP_A, STOP_B); + static TripPattern REAL_TIME_PATTERN = TEST_MODEL + .pattern(BUS) + .withStopPattern(REAL_TIME_STOP_PATTERN) + .withCreatedByRealtimeUpdater(true) + .build(); + @BeforeAll static void setup() { var stopModel = TEST_MODEL @@ -51,8 +61,16 @@ static void setup() { transitModel.initTimetableSnapshotProvider(() -> { TimetableSnapshot timetableSnapshot = new TimetableSnapshot(); - timetableSnapshot.setPatternsForStop(ImmutableSetMultimap.of(STOP_B, BUS_PATTERN)); - return timetableSnapshot; + RealTimeTripTimes tripTimes = RealTimeTripTimes.of( + ScheduledTripTimes + .of() + .withTrip(TransitModelForTest.trip("REAL_TIME_TRIP").build()) + .withDepartureTimes(new int[] { 0, 1 }) + .build() + ); + timetableSnapshot.update(REAL_TIME_PATTERN, tripTimes, LocalDate.now()); + + return timetableSnapshot.commit(); }); service = @@ -95,6 +113,6 @@ void getPatternForStopsWithoutRealTime() { @Test void getPatternForStopsWithRealTime() { Collection patternsForStop = service.getPatternsForStop(STOP_B, true); - assertEquals(Set.of(FERRY_PATTERN, RAIL_PATTERN, BUS_PATTERN), patternsForStop); + assertEquals(Set.of(FERRY_PATTERN, RAIL_PATTERN, REAL_TIME_PATTERN), patternsForStop); } } From 1238429266cf33bc079cbb6770f9e6f6e8ecd648 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jul 2024 15:19:03 +0200 Subject: [PATCH 121/192] Add Javadoc --- .../org/opentripplanner/netex/config/IgnorableFeature.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/opentripplanner/netex/config/IgnorableFeature.java b/src/main/java/org/opentripplanner/netex/config/IgnorableFeature.java index 4aa44e062d6..53fe7f87f48 100644 --- a/src/main/java/org/opentripplanner/netex/config/IgnorableFeature.java +++ b/src/main/java/org/opentripplanner/netex/config/IgnorableFeature.java @@ -1,5 +1,8 @@ package org.opentripplanner.netex.config; +/** + * Optional data that can be ignored during the NeTEx parsing process. + */ public enum IgnorableFeature { FARE_FRAME, PARKING, From cc659fade4f5a693d01176d7e9da664bf35c840f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jul 2024 15:20:28 +0200 Subject: [PATCH 122/192] Replace typo in 'finishUp' --- src/main/java/org/opentripplanner/netex/NetexBundle.java | 2 +- .../netex/loader/parser/NetexDocumentParser.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/NetexBundle.java b/src/main/java/org/opentripplanner/netex/NetexBundle.java index b9b8be224a9..3cd52cd246e 100644 --- a/src/main/java/org/opentripplanner/netex/NetexBundle.java +++ b/src/main/java/org/opentripplanner/netex/NetexBundle.java @@ -137,7 +137,7 @@ private void loadFileEntries() { }); } mapper.finishUp(); - NetexDocumentParser.finnishUp(); + NetexDocumentParser.finishUp(); } /** diff --git a/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java b/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java index 00ffa8a9d2f..763217f7e52 100644 --- a/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java +++ b/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java @@ -53,7 +53,7 @@ public static void parseAndPopulateIndex( new NetexDocumentParser(index, ignoredFeatures).parse(doc); } - public static void finnishUp() { + public static void finishUp() { ServiceFrameParser.logSummary(); } From b5e0ee6a5f609eb53b1892050a39b1eccc19ec66 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 16 Jul 2024 09:47:04 +0200 Subject: [PATCH 123/192] Enforce non-null coordinates on multimodal station --- .../netex/mapping/MultiModalStationMapper.java | 8 +++++--- .../org/opentripplanner/netex/mapping/NetexMapper.java | 5 +++-- .../transit/model/site/MultiModalStation.java | 2 +- .../transit/model/site/MultiModalStationTest.java | 2 ++ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/mapping/MultiModalStationMapper.java b/src/main/java/org/opentripplanner/netex/mapping/MultiModalStationMapper.java index ef4b0a25cfd..5a4fb23bbb9 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/MultiModalStationMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/MultiModalStationMapper.java @@ -1,6 +1,7 @@ package org.opentripplanner.netex.mapping; import java.util.Collection; +import javax.annotation.Nullable; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; @@ -21,6 +22,7 @@ public MultiModalStationMapper(DataImportIssueStore issueStore, FeedScopedIdFact this.idFactory = idFactory; } + @Nullable MultiModalStation map(StopPlace stopPlace, Collection childStations) { MultiModalStationBuilder multiModalStation = MultiModalStation .of(idFactory.createId(stopPlace.getId())) @@ -34,13 +36,13 @@ MultiModalStation map(StopPlace stopPlace, Collection childStations) { if (coordinate == null) { issueStore.add( "MultiModalStationWithoutCoordinates", - "MultiModal station {} does not contain any coordinates.", + "MultiModal station %s does not contain any coordinates.", multiModalStation.getId() ); + return null; } else { multiModalStation.withCoordinate(coordinate); + return multiModalStation.build(); } - - return multiModalStation.build(); } } diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index c3c9ad2d0ae..991c0477266 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -332,8 +332,9 @@ private void mapMultiModalStopPlaces() { .getStationsByMultiModalStationRfs() .get(multiModalStopPlace.getId()); var multiModalStation = mapper.map(multiModalStopPlace, stations); - - transitBuilder.stopModel().withMultiModalStation(multiModalStation); + if (multiModalStation != null) { + transitBuilder.stopModel().withMultiModalStation(multiModalStation); + } } } diff --git a/src/main/java/org/opentripplanner/transit/model/site/MultiModalStation.java b/src/main/java/org/opentripplanner/transit/model/site/MultiModalStation.java index 749b156656e..70f4924e8e1 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/MultiModalStation.java +++ b/src/main/java/org/opentripplanner/transit/model/site/MultiModalStation.java @@ -37,11 +37,11 @@ public class MultiModalStation super(builder.getId()); // Required fields this.childStations = Objects.requireNonNull(builder.childStations()); + this.coordinate = Objects.requireNonNull(builder.coordinate()); this.name = I18NString.assertHasValue(builder.name()); // Optional fields // TODO Make required - this.coordinate = builder.coordinate(); this.code = builder.code(); this.description = builder.description(); this.url = builder.url(); diff --git a/src/test/java/org/opentripplanner/transit/model/site/MultiModalStationTest.java b/src/test/java/org/opentripplanner/transit/model/site/MultiModalStationTest.java index 4ca8e8d33ea..615b0c49674 100644 --- a/src/test/java/org/opentripplanner/transit/model/site/MultiModalStationTest.java +++ b/src/test/java/org/opentripplanner/transit/model/site/MultiModalStationTest.java @@ -8,6 +8,7 @@ import java.util.Set; import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.transit.model._data.TransitModelForTest; @@ -26,6 +27,7 @@ class MultiModalStationTest { .of(TransitModelForTest.id(ID)) .withName(NAME) .withChildStations(CHILD_STATIONS) + .withCoordinate(new WgsCoordinate(1, 1)) .build(); @Test From 5bc0b78079eb2ec97a8e575cd3ec0ba9a2ee58a0 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 16 Jul 2024 13:55:27 +0200 Subject: [PATCH 124/192] Apply review suggestion --- .../mapping/MultiModalStationMapperTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/test/java/org/opentripplanner/netex/mapping/MultiModalStationMapperTest.java diff --git a/src/test/java/org/opentripplanner/netex/mapping/MultiModalStationMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/MultiModalStationMapperTest.java new file mode 100644 index 00000000000..435ba2878f2 --- /dev/null +++ b/src/test/java/org/opentripplanner/netex/mapping/MultiModalStationMapperTest.java @@ -0,0 +1,32 @@ +package org.opentripplanner.netex.mapping; + +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.graph_builder.issue.service.DefaultDataImportIssueStore; +import org.opentripplanner.netex.NetexTestDataSupport; +import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.rutebanken.netex.model.StopPlace; + +class MultiModalStationMapperTest { + + @Test + void testMissingCoordinates() { + DataImportIssueStore dataIssueStore = new DefaultDataImportIssueStore(); + FeedScopedIdFactory feedScopeIdFactory = new FeedScopedIdFactory(TransitModelForTest.FEED_ID); + MultiModalStationMapper multiModalStationMapper = new MultiModalStationMapper( + dataIssueStore, + feedScopeIdFactory + ); + StopPlace stopPlace = new StopPlace(); + stopPlace.setId(NetexTestDataSupport.STOP_PLACE_ID); + Assertions.assertNull(multiModalStationMapper.map(stopPlace, List.of())); + Assertions.assertEquals(1, dataIssueStore.listIssues().size()); + Assertions.assertEquals( + "MultiModalStationWithoutCoordinates", + dataIssueStore.listIssues().getFirst().getType() + ); + } +} From e58ff95647191fdfaed11ad7fbd1d7962942c801 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 16 Jul 2024 15:36:52 +0200 Subject: [PATCH 125/192] Apply review suggestion --- .../netex/mapping/MultiModalStationMapperTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/opentripplanner/netex/mapping/MultiModalStationMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/MultiModalStationMapperTest.java index 435ba2878f2..218942d29ef 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/MultiModalStationMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/MultiModalStationMapperTest.java @@ -1,7 +1,8 @@ package org.opentripplanner.netex.mapping; +import static org.junit.jupiter.api.Assertions.*; + import java.util.List; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.issue.service.DefaultDataImportIssueStore; @@ -22,9 +23,9 @@ void testMissingCoordinates() { ); StopPlace stopPlace = new StopPlace(); stopPlace.setId(NetexTestDataSupport.STOP_PLACE_ID); - Assertions.assertNull(multiModalStationMapper.map(stopPlace, List.of())); - Assertions.assertEquals(1, dataIssueStore.listIssues().size()); - Assertions.assertEquals( + assertNull(multiModalStationMapper.map(stopPlace, List.of())); + assertEquals(1, dataIssueStore.listIssues().size()); + assertEquals( "MultiModalStationWithoutCoordinates", dataIssueStore.listIssues().getFirst().getType() ); From dffd3d8bd7678a66c52f087a57529593d8f0b3e6 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 16 Jul 2024 15:33:09 +0000 Subject: [PATCH 126/192] Add changelog entry for #5971 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 6d84b377de7..1ae81820e72 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -46,6 +46,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Fix copy-on-write in TimetableSnapshot [#5941](https://github.com/opentripplanner/OpenTripPlanner/pull/5941) - Generate documentation for OSM tag mappers [#5929](https://github.com/opentripplanner/OpenTripPlanner/pull/5929) - Disable Legacy REST API by default [#5948](https://github.com/opentripplanner/OpenTripPlanner/pull/5948) +- Enforce non-null coordinates on multimodal station [#5971](https://github.com/opentripplanner/OpenTripPlanner/pull/5971) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From 990ce7c52a6a06f020a772c3e95df63d46b45d30 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 17 Jul 2024 11:39:32 +0200 Subject: [PATCH 127/192] Invert check --- .../netex/loader/parser/NetexDocumentParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java b/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java index 763217f7e52..f8058f2df8e 100644 --- a/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java +++ b/src/main/java/org/opentripplanner/netex/loader/parser/NetexDocumentParser.java @@ -79,7 +79,7 @@ private void parseCommonFrame(Common_VersionFrameStructure value) { parse((ServiceFrame) value, new ServiceFrameParser(netexIndex.flexibleStopPlaceById)); } else if (value instanceof SiteFrame) { parse((SiteFrame) value, new SiteFrameParser(ignoredFeatures)); - } else if (ignoredFeatures.contains(FARE_FRAME) && value instanceof FareFrame) { + } else if (!ignoredFeatures.contains(FARE_FRAME) && value instanceof FareFrame) { parse((FareFrame) value, new FareFrameParser()); } else if (value instanceof CompositeFrame) { // We recursively parse composite frames and content until there From f99542be1a47d4885db3a4c492eb0533594c408d Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 9 Jul 2024 15:27:48 +0200 Subject: [PATCH 128/192] Encapsulate TransitModelIndex --- .../ext/siri/AddedTripBuilderTest.java | 24 +++++------- .../module/DirectTransferGenerator.java | 4 +- .../transit/mappers/TransfersMapper.java | 6 +-- .../transit/mappers/TransitLayerMapper.java | 26 ++++++------- .../transit/mappers/TransitLayerUpdater.java | 14 +++---- .../configure/ConstructApplication.java | 8 ++-- .../service/DefaultTransitService.java | 11 ++++++ .../transit/service/TransitModel.java | 7 +++- .../transit/service/TransitModelIndex.java | 38 +++++++++---------- .../transit/service/TransitService.java | 11 ++++-- .../PollingVehiclePositionUpdater.java | 19 +++++----- .../apis/gtfs/GraphQLIntegrationTest.java | 29 +++++++------- .../graph/DefaultRoutingServiceTest.java | 21 +++++----- .../updater/trip/RealtimeTestEnvironment.java | 3 +- .../trip/TimetableSnapshotSourceTest.java | 11 +++--- .../trip/moduletests/delay/SkippedTest.java | 13 ++++--- 16 files changed, 126 insertions(+), 119 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java index e322b0ddea9..aa26985c5b9 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java @@ -154,37 +154,34 @@ void testAddedTrip() { assertEquals(SubMode.of(SUB_MODE), route.getNetexSubmode(), "submode should be mapped"); assertNotEquals(REPLACED_ROUTE, route, "Should not re-use replaced route"); - // Assert transit model index - var transitModelIndex = TRANSIT_MODEL.getTransitModelIndex(); - assertNotNull(transitModelIndex); assertEquals( route, - transitModelIndex.getRouteForId(TransitModelForTest.id(LINE_REF)), + transitService.getRouteForId(TransitModelForTest.id(LINE_REF)), "Route should be added to transit index" ); assertEquals( trip, - transitModelIndex.getTripForId().get(TRIP_ID), + transitService.getTripForId(TRIP_ID), "Route should be added to transit index" ); - var pattern = transitModelIndex.getPatternForTrip().get(trip); + var pattern = transitService.getPatternForTrip(trip); assertNotNull(pattern); assertEquals(route, pattern.getRoute()); assertTrue( - transitModelIndex + transitService .getServiceCodesRunningForDate() .get(SERVICE_DATE) .contains(TRANSIT_MODEL.getServiceCodes().get(trip.getServiceId())), "serviceId should be running on service date" ); assertNotNull( - transitModelIndex.getTripOnServiceDateById().get(TRIP_ID), + transitService.getTripOnServiceDateById(TRIP_ID), "TripOnServiceDate should be added to transit index by id" ); assertNotNull( - transitModelIndex - .getTripOnServiceDateForTripAndDay() - .get(new TripIdAndServiceDate(TRIP_ID, SERVICE_DATE)), + transitService.getTripOnServiceDateForTripAndDay( + new TripIdAndServiceDate(TRIP_ID, SERVICE_DATE) + ), "TripOnServiceDate should be added to transit index for trip and day" ); @@ -299,10 +296,7 @@ void testAddedTripOnAddedRoute() { Route route = secondTrip.getRoute(); assertSame(firstTrip.getRoute(), route, "route be reused from the first trip"); - // Assert transit model index - var transitModelIndex = TRANSIT_MODEL.getTransitModelIndex(); - assertNotNull(transitModelIndex); - assertEquals(2, transitModelIndex.getPatternsForRoute().get(route).size()); + assertEquals(2, transitService.getPatternsForRoute(route).size()); // Assert trip times var times = secondAddedTrip.successValue().tripTimes(); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 287a1a71c21..3137b66070f 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -67,9 +67,7 @@ public DirectTransferGenerator( @Override public void buildGraph() { /* Initialize transit model index which is needed by the nearby stop finder. */ - if (transitModel.getTransitModelIndex() == null) { - transitModel.index(); - } + transitModel.index(); /* The linker will use streets if they are available, or straight-line distance otherwise. */ NearbyStopFinder nearbyStopFinder = createNearbyStopFinder(); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java index 74a5d7a6352..c7e9ea05320 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransfersMapper.java @@ -6,7 +6,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.Transfer; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.service.StopModel; -import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.transit.service.TransitService; class TransfersMapper { @@ -14,7 +14,7 @@ class TransfersMapper { * Copy pre-calculated transfers from the original graph * @return a list where each element is a list of transfers for the corresponding stop index */ - static List> mapTransfers(StopModel stopModel, TransitModel transitModel) { + static List> mapTransfers(StopModel stopModel, TransitService transitService) { List> transferByStopIndex = new ArrayList<>(); for (int i = 0; i < stopModel.stopIndexSize(); ++i) { @@ -26,7 +26,7 @@ static List> mapTransfers(StopModel stopModel, TransitModel trans ArrayList list = new ArrayList<>(); - for (PathTransfer pathTransfer : transitModel.getTransfersByStop(stop)) { + for (PathTransfer pathTransfer : transitService.getTransfersByStop(stop)) { if (pathTransfer.to instanceof RegularStop) { int toStopIndex = pathTransfer.to.getIndex(); Transfer newTransfer; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java index 33a076bb8d6..8ce328fe1b6 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java @@ -27,8 +27,10 @@ import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopTransferPriority; import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.transit.service.TransitService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,10 +49,12 @@ public class TransitLayerMapper { private static final Logger LOG = LoggerFactory.getLogger(TransitLayerMapper.class); - private final TransitModel transitModel; + private final TransitService transitService; + private final StopModel stopModel; private TransitLayerMapper(TransitModel transitModel) { - this.transitModel = transitModel; + this.transitService = new DefaultTransitService(transitModel); + this.stopModel = transitModel.getStopModel(); } public static TransitLayer map( @@ -74,20 +78,19 @@ private TransitLayer map(TransitTuningParameters tuningParameters) { HashMap> tripPatternsByStopByDate; List> transferByStopIndex; ConstrainedTransfersForPatterns constrainedTransfers = null; - StopModel stopModel = transitModel.getStopModel(); LOG.info("Mapping transitLayer from TransitModel..."); - Collection allTripPatterns = transitModel.getAllTripPatterns(); + Collection allTripPatterns = transitService.getAllTripPatterns(); tripPatternsByStopByDate = mapTripPatterns(allTripPatterns); - transferByStopIndex = mapTransfers(stopModel, transitModel); + transferByStopIndex = mapTransfers(stopModel, transitService); TransferIndexGenerator transferIndexGenerator = null; if (OTPFeature.TransferConstraints.isOn()) { transferIndexGenerator = - new TransferIndexGenerator(transitModel.getTransferService().listAll(), allTripPatterns); + new TransferIndexGenerator(transitService.getTransferService().listAll(), allTripPatterns); constrainedTransfers = transferIndexGenerator.generateTransfers(); } @@ -98,9 +101,9 @@ private TransitLayer map(TransitTuningParameters tuningParameters) { return new TransitLayer( tripPatternsByStopByDate, transferByStopIndex, - transitModel.getTransferService(), + transitService.getTransferService(), stopModel, - transitModel.getTimeZone(), + transitService.getTimeZone(), transferCache, constrainedTransfers, transferIndexGenerator, @@ -118,13 +121,10 @@ private HashMap> mapTripPatterns( Collection allTripPatterns ) { TripPatternForDateMapper tripPatternForDateMapper = new TripPatternForDateMapper( - transitModel.getTransitModelIndex().getServiceCodesRunningForDate() + transitService.getServiceCodesRunningForDate() ); - Set allServiceDates = transitModel - .getTransitModelIndex() - .getServiceCodesRunningForDate() - .keySet(); + Set allServiceDates = transitService.getAllServiceCodes(); List tripPatternForDates = Collections.synchronizedList(new ArrayList<>()); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java index 5188bdef8b1..dc4e90683dd 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java @@ -2,7 +2,6 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.SetMultimap; -import gnu.trove.set.TIntSet; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; @@ -21,6 +20,7 @@ import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.transit.service.TransitService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,8 +41,7 @@ public class TransitLayerUpdater { private static final Logger LOG = LoggerFactory.getLogger(TransitLayerUpdater.class); private final TransitModel transitModel; - - private final Map serviceCodesRunningForDate; + private final TransitService transitService; /** * Cache the TripPatternForDates indexed on the original TripPatterns in order to avoid this @@ -58,12 +57,9 @@ public class TransitLayerUpdater { private final Map> tripPatternsRunningOnDateMapCache = new HashMap<>(); - public TransitLayerUpdater( - TransitModel transitModel, - Map serviceCodesRunningForDate - ) { + public TransitLayerUpdater(TransitModel transitModel, TransitService transitService) { this.transitModel = transitModel; - this.serviceCodesRunningForDate = serviceCodesRunningForDate; + this.transitService = transitService; } public void update( @@ -82,7 +78,7 @@ public void update( // Instantiate a TripPatternForDateMapper with the new TripPattern mappings TripPatternForDateMapper tripPatternForDateMapper = new TripPatternForDateMapper( - serviceCodesRunningForDate + transitService.getServiceCodesRunningForDate() ); Set datesToBeUpdated = new HashSet<>(); diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java index b1bc6888753..2817c4d1ba0 100644 --- a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java +++ b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java @@ -33,6 +33,7 @@ import org.opentripplanner.standalone.server.OTPWebApplication; import org.opentripplanner.street.model.StreetLimitationParameters; import org.opentripplanner.street.model.elevation.ElevationUtils; +import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.configure.UpdaterConfigurator; import org.opentripplanner.visualizer.GraphVisualizer; @@ -202,7 +203,7 @@ public static void creatTransitLayerForRaptor( TransitModel transitModel, TransitTuningParameters tuningParameters ) { - if (!transitModel.hasTransit() || transitModel.getTransitModelIndex() == null) { + if (!transitModel.hasTransit() || !transitModel.isIndexed()) { LOG.warn( "Cannot create Raptor data, that requires the graph to have transit data and be indexed." ); @@ -211,10 +212,7 @@ public static void creatTransitLayerForRaptor( transitModel.setTransitLayer(TransitLayerMapper.map(tuningParameters, transitModel)); transitModel.setRealtimeTransitLayer(new TransitLayer(transitModel.getTransitLayer())); transitModel.setTransitLayerUpdater( - new TransitLayerUpdater( - transitModel, - transitModel.getTransitModelIndex().getServiceCodesRunningForDate() - ) + new TransitLayerUpdater(transitModel, new DefaultTransitService(transitModel)) ); } diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 41d88726233..8fa5100e6d4 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -9,6 +9,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -659,6 +660,16 @@ public Deduplicator getDeduplicator() { return transitModel.getDeduplicator(); } + @Override + public Set getAllServiceCodes() { + return Collections.unmodifiableSet(transitModelIndex.getServiceCodesRunningForDate().keySet()); + } + + @Override + public Map getServiceCodesRunningForDate() { + return Collections.unmodifiableMap(transitModelIndex.getServiceCodesRunningForDate()); + } + /** * For each pattern visiting this {@link StopLocation} return its {@link TransitMode} */ diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModel.java b/src/main/java/org/opentripplanner/transit/service/TransitModel.java index c5a36e54e3a..84c7597d562 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModel.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModel.java @@ -539,10 +539,15 @@ public void setHasScheduledService(boolean hasScheduledService) { * The caller is responsible for calling the {@link #index()} method if it is a * possibility that the index is not initialized (during graph build). */ - public @Nullable TransitModelIndex getTransitModelIndex() { + @Nullable + TransitModelIndex getTransitModelIndex() { return index; } + public boolean isIndexed() { + return index != null; + } + public boolean hasFlexTrips() { return !flexTripsById.isEmpty(); } diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java b/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java index 333011e9baf..36ab937416c 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModelIndex.java @@ -34,7 +34,7 @@ * For performance reasons these indexes are not part of the serialized state of the graph. * They are rebuilt at runtime after graph deserialization. */ -public class TransitModelIndex { +class TransitModelIndex { private static final Logger LOG = LoggerFactory.getLogger(TransitModelIndex.class); @@ -117,20 +117,20 @@ public class TransitModelIndex { LOG.info("Transit Model index init complete."); } - public Agency getAgencyForId(FeedScopedId id) { + Agency getAgencyForId(FeedScopedId id) { return agencyForId.get(id); } - public Route getRouteForId(FeedScopedId id) { + Route getRouteForId(FeedScopedId id) { return routeForId.get(id); } - public void addRoutes(Route route) { + void addRoutes(Route route) { routeForId.put(route.getId(), route); } /** Dynamically generate the set of Routes passing though a Stop on demand. */ - public Set getRoutesForStop(StopLocation stop) { + Set getRoutesForStop(StopLocation stop) { Set routes = new HashSet<>(); for (TripPattern p : getPatternsForStop(stop)) { routes.add(p.getRoute()); @@ -138,11 +138,11 @@ public Set getRoutesForStop(StopLocation stop) { return routes; } - public Collection getPatternsForStop(StopLocation stop) { + Collection getPatternsForStop(StopLocation stop) { return patternsForStopId.get(stop); } - public Collection getTripsForStop(StopLocation stop) { + Collection getTripsForStop(StopLocation stop) { return getPatternsForStop(stop) .stream() .flatMap(TripPattern::scheduledTripsAsStream) @@ -152,43 +152,43 @@ public Collection getTripsForStop(StopLocation stop) { /** * Get a list of all operators spanning across all feeds. */ - public Collection getAllOperators() { + Collection getAllOperators() { return getOperatorForId().values(); } - public Map getOperatorForId() { + Map getOperatorForId() { return operatorForId; } - public Map getTripForId() { + Map getTripForId() { return tripForId; } - public Map getTripOnServiceDateById() { + Map getTripOnServiceDateById() { return tripOnServiceDateById; } - public Map getTripOnServiceDateForTripAndDay() { + Map getTripOnServiceDateForTripAndDay() { return tripOnServiceDateForTripAndDay; } - public Collection getAllRoutes() { + Collection getAllRoutes() { return routeForId.values(); } - public Map getPatternForTrip() { + Map getPatternForTrip() { return patternForTrip; } - public Multimap getPatternsForRoute() { + Multimap getPatternsForRoute() { return patternsForRoute; } - public Map getServiceCodesRunningForDate() { + Map getServiceCodesRunningForDate() { return serviceCodesRunningForDate; } - public FlexIndex getFlexIndex() { + FlexIndex getFlexIndex() { return flexIndex; } @@ -229,11 +229,11 @@ private void initalizeServiceCodesForDate(TransitModel transitModel) { } } - public Multimap getRoutesForGroupOfRoutes() { + Multimap getRoutesForGroupOfRoutes() { return routesForGroupOfRoutes; } - public Map getGroupOfRoutesForId() { + Map getGroupOfRoutesForId() { return groupOfRoutesForId; } } diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index 6624cb946ad..8f48999e2a1 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -8,8 +8,10 @@ import java.time.ZonedDateTime; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; +import javax.annotation.Nullable; import org.locationtech.jts.geom.Envelope; import org.opentripplanner.ext.flex.FlexIndex; import org.opentripplanner.model.FeedInfo; @@ -184,10 +186,7 @@ List stopTimesForPatternAtStop( GroupOfRoutes getGroupOfRoutesForId(FeedScopedId id); - /** - * Return the timetable for a given trip pattern and date, taking into account real-time updates. - * If no real-times update are applied, fall back to scheduled data. - */ + @Nullable Timetable getTimetableForTripPattern(TripPattern tripPattern, LocalDate serviceDate); TripPattern getRealtimeAddedTripPattern(FeedScopedId tripId, LocalDate serviceDate); @@ -254,4 +253,8 @@ List stopTimesForPatternAtStop( List getModesOfStopLocation(StopLocation stop); Deduplicator getDeduplicator(); + + Set getAllServiceCodes(); + + Map getServiceCodesRunningForDate(); } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java index 745418f5132..a1d1325560e 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java @@ -3,13 +3,13 @@ import com.google.transit.realtime.GtfsRealtime.VehiclePosition; import java.time.LocalDate; import java.util.List; -import java.util.Optional; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -45,18 +45,18 @@ public PollingVehiclePositionUpdater( super(params); this.vehiclePositionSource = new GtfsRealtimeHttpVehiclePositionSource(params.url(), params.headers()); - var index = transitModel.getTransitModelIndex(); + TransitService transitService = new DefaultTransitService(transitModel); var fuzzyTripMatcher = params.fuzzyTripMatching() - ? new GtfsRealtimeFuzzyTripMatcher(new DefaultTransitService(transitModel)) + ? new GtfsRealtimeFuzzyTripMatcher(transitService) : null; this.realtimeVehiclePatternMatcher = new RealtimeVehiclePatternMatcher( params.feedId(), - tripId -> index.getTripForId().get(tripId), - trip -> index.getPatternForTrip().get(trip), + transitService::getTripForId, + transitService::getPatternForTrip, (trip, date) -> getPatternIncludingRealtime(transitModel, trip, date), realtimeVehicleRepository, - transitModel.getTimeZone(), + transitService.getTimeZone(), fuzzyTripMatcher, params.vehiclePositionFeatures() ); @@ -99,9 +99,8 @@ private static TripPattern getPatternIncludingRealtime( Trip trip, LocalDate sd ) { - return Optional - .ofNullable(transitModel.getTimetableSnapshot()) - .map(snapshot -> snapshot.getRealtimeAddedTripPattern(trip.getId(), sd)) - .orElseGet(() -> transitModel.getTransitModelIndex().getPatternForTrip().get(trip)); + // TODO OTP2 RT_VP a new instance of DefaultTransitService must be created to retrieve + // the latest TimetableSnapshot + return (new DefaultTransitService(transitModel)).getPatternForTrip(trip, sd); } } diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 2fb7a8d9db2..79590ca2775 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -93,6 +93,7 @@ import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.TripTimesFactory; import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.TransitEditorService; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.transit.service.TransitService; @@ -196,8 +197,20 @@ static void setup() { .toList(); var busRoute = routes.stream().filter(r -> r.getMode().equals(BUS)).findFirst().get(); + TransitEditorService transitService = new DefaultTransitService(transitModel) { + private final TransitAlertService alertService = new TransitAlertServiceImpl(transitModel); + + @Override + public List getModesOfStopLocation(StopLocation stop) { + return List.of(BUS, FERRY); + } - routes.forEach(route -> transitModel.getTransitModelIndex().addRoutes(route)); + @Override + public TransitAlertService getTransitAlertService() { + return alertService; + } + }; + routes.forEach(transitService::addRoutes); var step1 = walkStep("street") .withRelativeDirection(RelativeDirection.DEPART) @@ -254,20 +267,6 @@ static void setup() { var emissions = new Emissions(new Grams(123.0)); i1.setEmissionsPerPerson(emissions); - var transitService = new DefaultTransitService(transitModel) { - private final TransitAlertService alertService = new TransitAlertServiceImpl(transitModel); - - @Override - public List getModesOfStopLocation(StopLocation stop) { - return List.of(BUS, FERRY); - } - - @Override - public TransitAlertService getTransitAlertService() { - return alertService; - } - }; - var alerts = ListUtils.combine(List.of(alert), getTransitAlert(entitySelector)); transitService.getTransitAlertService().setAlerts(alerts); diff --git a/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java b/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java index e9d286d5a92..e70ae9aa086 100644 --- a/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java +++ b/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java @@ -18,6 +18,8 @@ import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.TransitService; /** * Check that the graph index is created, that GTFS elements can be found in the index, and that the @@ -46,9 +48,10 @@ public void testIdLookup() { /* Agencies */ String feedId = transitModel.getFeedIds().iterator().next(); Agency agency; - agency = transitModel.getTransitModelIndex().getAgencyForId(new FeedScopedId(feedId, "azerty")); + TransitService transitService = new DefaultTransitService(transitModel); + agency = transitService.getAgencyForId(new FeedScopedId(feedId, "azerty")); assertNull(agency); - agency = transitModel.getTransitModelIndex().getAgencyForId(new FeedScopedId(feedId, "agency")); + agency = transitService.getAgencyForId(new FeedScopedId(feedId, "agency")); assertEquals(feedId + ":" + "agency", agency.getId().toString()); assertEquals("Fake Agency", agency.getName()); @@ -67,21 +70,19 @@ public void testIdLookup() { */ @Test public void testPatternsCoherent() { - for (Trip trip : transitModel.getTransitModelIndex().getTripForId().values()) { - TripPattern pattern = transitModel.getTransitModelIndex().getPatternForTrip().get(trip); + TransitService transitService = new DefaultTransitService(transitModel); + for (Trip trip : transitService.getAllTrips()) { + TripPattern pattern = transitService.getPatternForTrip(trip); assertTrue(pattern.scheduledTripsAsStream().anyMatch(t -> t.equals(trip))); } /* This one depends on a feed where each TripPattern appears on only one route. */ - for (Route route : transitModel.getTransitModelIndex().getAllRoutes()) { - for (TripPattern pattern : transitModel - .getTransitModelIndex() - .getPatternsForRoute() - .get(route)) { + for (Route route : transitService.getAllRoutes()) { + for (TripPattern pattern : transitService.getPatternsForRoute(route)) { assertEquals(pattern.getRoute(), route); } } for (var stop : transitModel.getStopModel().listStopLocations()) { - for (TripPattern pattern : transitModel.getTransitModelIndex().getPatternsForStop(stop)) { + for (TripPattern pattern : transitService.getPatternsForStop(stop)) { int stopPos = pattern.findStopPosition(stop); assertTrue(stopPos >= 0, "Stop position exist"); } diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index 34923fed9e4..fcf6974ed34 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -217,7 +217,8 @@ public DateTimeHelper getDateTimeHelper() { } public TripPattern getPatternForTrip(Trip trip) { - return transitModel.getTransitModelIndex().getPatternForTrip().get(trip); + var transitService = getTransitService(); + return transitService.getPatternForTrip(trip); } public TimetableSnapshot getTimetableSnapshot() { diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java index 7ae7b7386f3..64db5d63c01 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java @@ -33,7 +33,9 @@ import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; import org.opentripplanner.updater.TimetableSnapshotSourceParameters; @@ -52,11 +54,13 @@ public class TimetableSnapshotSourceTest { private final GtfsRealtimeFuzzyTripMatcher TRIP_MATCHER_NOOP = null; private String feedId; + private TransitService transitService; @BeforeEach public void setUp() { TestOtpModel model = ConstantsForTests.buildGtfsGraph(ConstantsForTests.SIMPLE_GTFS); transitModel = model.transitModel(); + transitService = new DefaultTransitService(transitModel); feedId = transitModel.getFeedIds().stream().findFirst().get(); } @@ -224,11 +228,8 @@ public void testHandleModifiedTrip() { // Original trip pattern { final FeedScopedId tripId = new FeedScopedId(feedId, modifiedTripId); - final Trip trip = transitModel.getTransitModelIndex().getTripForId().get(tripId); - final TripPattern originalTripPattern = transitModel - .getTransitModelIndex() - .getPatternForTrip() - .get(trip); + final Trip trip = transitService.getTripForId(tripId); + final TripPattern originalTripPattern = transitService.getPatternForTrip(trip); final Timetable originalTimetableForToday = snapshot.resolve( originalTripPattern, diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index c17130fbda6..ebd34cd390a 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -13,6 +13,9 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.transit.model.timetable.TripTimesStringBuilder; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; @@ -132,8 +135,8 @@ private static void assertOriginalTripPatternIsDeleted( RealtimeTestEnvironment env, FeedScopedId tripId ) { - var trip = env.transitModel.getTransitModelIndex().getTripForId().get(tripId); - var originalTripPattern = env.transitModel.getTransitModelIndex().getPatternForTrip().get(trip); + var trip = env.getTransitService().getTripForId(tripId); + var originalTripPattern = env.getTransitService().getPatternForTrip(trip); var snapshot = env.getTimetableSnapshot(); var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); @@ -178,10 +181,8 @@ private static void assertNewTripTimesIsUpdated( RealtimeTestEnvironment env, FeedScopedId tripId ) { - var originalTripPattern = env.transitModel - .getTransitModelIndex() - .getPatternForTrip() - .get(env.trip2); + var originalTripPattern = env.getTransitService() + .getPatternForTrip(env.trip2); var snapshot = env.getTimetableSnapshot(); var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); From f101f4950e0383551e4376050a4357d62f7cd21e Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Wed, 17 Jul 2024 09:12:59 +0200 Subject: [PATCH 129/192] Remove duplicate wrapping in unmodifiable map --- .../raptoradapter/transit/mappers/TripPatternForDateMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapper.java index 30551421138..cd00b9356dc 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapper.java @@ -41,7 +41,7 @@ public class TripPatternForDateMapper { * @param serviceCodesRunningForDate - READ ONLY */ TripPatternForDateMapper(Map serviceCodesRunningForDate) { - this.serviceCodesRunningForDate = Collections.unmodifiableMap(serviceCodesRunningForDate); + this.serviceCodesRunningForDate = serviceCodesRunningForDate; } /** From 8b0e4623049b736b55d12daf89110ffa5c65a072 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Wed, 17 Jul 2024 09:27:14 +0200 Subject: [PATCH 130/192] Use TransitEditorService in TransitLayerUpdater --- .../transit/mappers/TransitLayerUpdater.java | 15 ++++++--------- .../configure/ConstructApplication.java | 2 +- .../transit/service/DefaultTransitService.java | 10 ++++++++++ .../transit/service/TransitEditorService.java | 4 ++++ 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java index dc4e90683dd..934bec39c11 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java @@ -19,8 +19,7 @@ import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; -import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.transit.service.TransitService; +import org.opentripplanner.transit.service.TransitEditorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,8 +39,7 @@ public class TransitLayerUpdater { private static final Logger LOG = LoggerFactory.getLogger(TransitLayerUpdater.class); - private final TransitModel transitModel; - private final TransitService transitService; + private final TransitEditorService transitService; /** * Cache the TripPatternForDates indexed on the original TripPatterns in order to avoid this @@ -57,8 +55,7 @@ public class TransitLayerUpdater { private final Map> tripPatternsRunningOnDateMapCache = new HashMap<>(); - public TransitLayerUpdater(TransitModel transitModel, TransitService transitService) { - this.transitModel = transitModel; + public TransitLayerUpdater(TransitEditorService transitService) { this.transitService = transitService; } @@ -66,7 +63,7 @@ public void update( Set updatedTimetables, Map> timetables ) { - if (!transitModel.hasRealtimeTransitLayer()) { + if (!transitService.hasRealtimeTransitLayer()) { return; } @@ -74,7 +71,7 @@ public void update( // Make a shallow copy of the realtime transit layer. Only the objects that are copied will be // changed during this update process. - TransitLayer realtimeTransitLayer = new TransitLayer(transitModel.getRealtimeTransitLayer()); + TransitLayer realtimeTransitLayer = new TransitLayer(transitService.getRealtimeTransitLayer()); // Instantiate a TripPatternForDateMapper with the new TripPattern mappings TripPatternForDateMapper tripPatternForDateMapper = new TripPatternForDateMapper( @@ -225,7 +222,7 @@ public void update( // Switch out the reference with the updated realtimeTransitLayer. This is synchronized to // guarantee that the reference is set after all the fields have been updated. - transitModel.setRealtimeTransitLayer(realtimeTransitLayer); + transitService.setRealtimeTransitLayer(realtimeTransitLayer); LOG.debug( "UPDATING {} tripPatterns took {} ms", diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java index 2817c4d1ba0..7d07ca29f0e 100644 --- a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java +++ b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java @@ -212,7 +212,7 @@ public static void creatTransitLayerForRaptor( transitModel.setTransitLayer(TransitLayerMapper.map(tuningParameters, transitModel)); transitModel.setRealtimeTransitLayer(new TransitLayer(transitModel.getTransitLayer())); transitModel.setTransitLayerUpdater( - new TransitLayerUpdater(transitModel, new DefaultTransitService(transitModel)) + new TransitLayerUpdater(new DefaultTransitService(transitModel)) ); } diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 8fa5100e6d4..398cb806524 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -595,6 +595,16 @@ public void setTransitLayer(TransitLayer transitLayer) { this.transitModel.setTransitLayer(transitLayer); } + @Override + public void setRealtimeTransitLayer(TransitLayer realtimeTransitLayer) { + transitModel.setRealtimeTransitLayer(realtimeTransitLayer); + } + + @Override + public boolean hasRealtimeTransitLayer() { + return transitModel.hasRealtimeTransitLayer(); + } + @Override public CalendarService getCalendarService() { return this.transitModel.getCalendarService(); diff --git a/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java b/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java index 6a11fe902f2..3bdcf607363 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java @@ -40,4 +40,8 @@ void addTripOnServiceDateForTripAndDay( FeedScopedId getOrCreateServiceIdForDate(LocalDate serviceDate); void setTransitLayer(TransitLayer transitLayer); + + boolean hasRealtimeTransitLayer(); + + void setRealtimeTransitLayer(TransitLayer realtimeTransitLayer); } From 1b81847314d32de2187d40eeeed9598dcad03f46 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Wed, 17 Jul 2024 10:13:04 +0200 Subject: [PATCH 131/192] Update documentation --- .../org/opentripplanner/transit/service/TransitService.java | 4 ++++ .../vehicle_position/PollingVehiclePositionUpdater.java | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index 8f48999e2a1..94870643f71 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -186,6 +186,10 @@ List stopTimesForPatternAtStop( GroupOfRoutes getGroupOfRoutesForId(FeedScopedId id); + /** + * Return the timetable for a given trip pattern and date, taking into account real-time updates. + * If no real-times update are applied, fall back to scheduled data. + */ @Nullable Timetable getTimetableForTripPattern(TripPattern tripPattern, LocalDate serviceDate); diff --git a/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java index a1d1325560e..6b9391f3ea2 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java @@ -99,8 +99,8 @@ private static TripPattern getPatternIncludingRealtime( Trip trip, LocalDate sd ) { - // TODO OTP2 RT_VP a new instance of DefaultTransitService must be created to retrieve - // the latest TimetableSnapshot + // a new instance of DefaultTransitService must be created to retrieve + // the current TimetableSnapshot return (new DefaultTransitService(transitModel)).getPatternForTrip(trip, sd); } } From 7baf92b873f4a0d25e6507b74857a5fa7e6dee31 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Wed, 17 Jul 2024 10:13:17 +0200 Subject: [PATCH 132/192] Refactor unit tests --- .../ext/siri/AddedTripBuilderTest.java | 3 +- .../graph/DefaultRoutingServiceTest.java | 28 ++++++++++++------- .../updater/trip/RealtimeTestEnvironment.java | 3 +- .../trip/TimetableSnapshotSourceTest.java | 6 ++-- .../trip/moduletests/addition/AddedTest.java | 4 +-- .../trip/moduletests/delay/DelayedTest.java | 5 +--- .../trip/moduletests/delay/SkippedTest.java | 6 +--- 7 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java index aa26985c5b9..59995062eb7 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java @@ -169,8 +169,7 @@ void testAddedTrip() { assertEquals(route, pattern.getRoute()); assertTrue( transitService - .getServiceCodesRunningForDate() - .get(SERVICE_DATE) + .getServiceCodesRunningForDate(SERVICE_DATE) .contains(TRANSIT_MODEL.getServiceCodes().get(trip.getServiceId())), "serviceId should be running on service date" ); diff --git a/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java b/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java index e70ae9aa086..8c56bb89a1f 100644 --- a/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java +++ b/src/test/java/org/opentripplanner/routing/graph/DefaultRoutingServiceTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collection; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; @@ -29,6 +30,15 @@ */ public class DefaultRoutingServiceTest extends GtfsTest { + private TransitService transitService; + + @BeforeEach + @Override + public void setUp() throws Exception { + super.setUp(); + transitService = new DefaultTransitService(transitModel); + } + @Override public String getFeedName() { return "gtfs/simple"; @@ -46,9 +56,8 @@ public void testIdLookup() { } /* Agencies */ - String feedId = transitModel.getFeedIds().iterator().next(); + String feedId = transitService.getFeedIds().iterator().next(); Agency agency; - TransitService transitService = new DefaultTransitService(transitModel); agency = transitService.getAgencyForId(new FeedScopedId(feedId, "azerty")); assertNull(agency); agency = transitService.getAgencyForId(new FeedScopedId(feedId, "agency")); @@ -56,7 +65,7 @@ public void testIdLookup() { assertEquals("Fake Agency", agency.getName()); /* Stops */ - transitModel.getStopModel().getRegularStop(new FeedScopedId("X", "Y")); + transitService.getRegularStop(new FeedScopedId("X", "Y")); /* Trips */ // graph.index.tripForId; // graph.index.routeForId; @@ -70,7 +79,6 @@ public void testIdLookup() { */ @Test public void testPatternsCoherent() { - TransitService transitService = new DefaultTransitService(transitModel); for (Trip trip : transitService.getAllTrips()) { TripPattern pattern = transitService.getPatternForTrip(trip); assertTrue(pattern.scheduledTripsAsStream().anyMatch(t -> t.equals(trip))); @@ -81,7 +89,7 @@ public void testPatternsCoherent() { assertEquals(pattern.getRoute(), route); } } - for (var stop : transitModel.getStopModel().listStopLocations()) { + for (var stop : transitService.listStopLocations()) { for (TripPattern pattern : transitService.getPatternsForStop(stop)) { int stopPos = pattern.findStopPosition(stop); assertTrue(stopPos >= 0, "Stop position exist"); @@ -91,13 +99,13 @@ public void testPatternsCoherent() { @Test public void testSpatialIndex() { - String feedId = transitModel.getFeedIds().iterator().next(); + String feedId = transitService.getFeedIds().iterator().next(); FeedScopedId idJ = new FeedScopedId(feedId, "J"); - var stopJ = transitModel.getStopModel().getRegularStop(idJ); + var stopJ = transitService.getRegularStop(idJ); FeedScopedId idL = new FeedScopedId(feedId, "L"); - var stopL = transitModel.getStopModel().getRegularStop(idL); + var stopL = transitService.getRegularStop(idL); FeedScopedId idM = new FeedScopedId(feedId, "M"); - var stopM = transitModel.getStopModel().getRegularStop(idM); + var stopM = transitService.getRegularStop(idM); TransitStopVertex stopvJ = graph.getStopVertexForStopId(idJ); TransitStopVertex stopvL = graph.getStopVertexForStopId(idL); TransitStopVertex stopvM = graph.getStopVertexForStopId(idM); @@ -107,7 +115,7 @@ public void testSpatialIndex() { SphericalDistanceLibrary.metersToLonDegrees(100, stopJ.getLat()), SphericalDistanceLibrary.metersToDegrees(100) ); - Collection stops = transitModel.getStopModel().findRegularStops(env); + Collection stops = transitService.findRegularStops(env); assertTrue(stops.contains(stopJ)); assertTrue(stops.contains(stopL)); assertTrue(stops.contains(stopM)); diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index fcf6974ed34..6adce735fb0 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -217,8 +217,7 @@ public DateTimeHelper getDateTimeHelper() { } public TripPattern getPatternForTrip(Trip trip) { - var transitService = getTransitService(); - return transitService.getPatternForTrip(trip); + return getTransitService().getPatternForTrip(trip); } public TimetableSnapshot getTimetableSnapshot() { diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java index 64db5d63c01..84b53c95f9c 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java @@ -50,11 +50,11 @@ public class TimetableSnapshotSourceTest { ) .build(); private TransitModel transitModel; + private TransitService transitService; private final GtfsRealtimeFuzzyTripMatcher TRIP_MATCHER_NOOP = null; private String feedId; - private TransitService transitService; @BeforeEach public void setUp() { @@ -62,7 +62,7 @@ public void setUp() { transitModel = model.transitModel(); transitService = new DefaultTransitService(transitModel); - feedId = transitModel.getFeedIds().stream().findFirst().get(); + feedId = transitService.getFeedIds().stream().findFirst().get(); } @Test @@ -119,7 +119,7 @@ public void testHandleModifiedTrip() { tripDescriptorBuilder.setStartDate(ServiceDateUtils.asCompactString(SERVICE_DATE)); final long midnightSecondsSinceEpoch = ServiceDateUtils - .asStartOfService(SERVICE_DATE, transitModel.getTimeZone()) + .asStartOfService(SERVICE_DATE, transitService.getTimeZone()) .toEpochSecond(); final TripUpdate.Builder tripUpdateBuilder = TripUpdate.newBuilder(); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 8d3984cc546..8371c5dda3a 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -62,7 +62,7 @@ void addedTripWithNewRoute() { assertEquals(TripUpdateBuilder.ROUTE_NAME, route.getName()); assertEquals(TransitMode.RAIL, route.getMode()); - var fromTransitModel = env.transitModel.getTransitModelIndex().getRouteForId(route.getId()); + var fromTransitModel = env.getTransitService().getRouteForId(route.getId()); assertEquals(fromTransitModel, route); assertEquals(PickDrop.CALL_AGENCY, pattern.getBoardType(0)); @@ -117,7 +117,7 @@ void repeatedlyAddedTripWithNewRoute() { var secondRoute = secondPattern.getRoute(); assertSame(firstRoute, secondRoute); - assertNotNull(env.transitModel.getTransitModelIndex().getRouteForId(firstRoute.getId())); + assertNotNull(env.getTransitService().getRouteForId(firstRoute.getId())); } private TripPattern assertAddedTrip(String tripId, RealtimeTestEnvironment env) { diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java index 53805939e40..5298853f36d 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java @@ -86,10 +86,7 @@ void complexDelay() { var snapshot = env.getTimetableSnapshot(); - final TripPattern originalTripPattern = env.transitModel - .getTransitModelIndex() - .getPatternForTrip() - .get(env.trip2); + final TripPattern originalTripPattern = env.getTransitService().getPatternForTrip(env.trip2); var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index ebd34cd390a..de699324bb6 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -13,9 +13,6 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.timetable.RealTimeState; -import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripTimes; -import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.transit.model.timetable.TripTimesStringBuilder; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; @@ -181,8 +178,7 @@ private static void assertNewTripTimesIsUpdated( RealtimeTestEnvironment env, FeedScopedId tripId ) { - var originalTripPattern = env.getTransitService() - .getPatternForTrip(env.trip2); + var originalTripPattern = env.getTransitService().getPatternForTrip(env.trip2); var snapshot = env.getTimetableSnapshot(); var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); From 01266a83cd049cdb9ee973ee9e6ab3dcdfca2a74 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 18 Jul 2024 22:57:44 +0200 Subject: [PATCH 133/192] Auto-merge test reporter [ci skip] --- renovate.json5 | 1 + 1 file changed, 1 insertion(+) diff --git a/renovate.json5 b/renovate.json5 index 7b957c577e3..d8ba10984e5 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -113,6 +113,7 @@ "org.mockito:mockito-core", "com.tngtech.archunit:archunit", "org.apache.maven.plugins:maven-surefire-plugin", + "me.fabriciorby:maven-surefire-junit5-tree-reporter", "org.jacoco:jacoco-maven-plugin", // coverage plugin "org.apache.commons:commons-compress", // only used by tests // maven plugins From 4198402425875fad1d139d28ca4aecbdd40f61b5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 09:56:02 +0000 Subject: [PATCH 134/192] chore(deps): update dependency me.fabriciorby:maven-surefire-junit5-tree-reporter to v1.3.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b7f26812e99..02f87de47d6 100644 --- a/pom.xml +++ b/pom.xml @@ -247,7 +247,7 @@ me.fabriciorby maven-surefire-junit5-tree-reporter - 1.2.1 + 1.3.0 From cfe694786bd1db0b86aa6a030fbff5c9f9ec01c8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 19 Jul 2024 08:32:22 +0200 Subject: [PATCH 135/192] Add car rental to Transmodel street mode options --- .../opentripplanner/apis/transmodel/model/EnumTypes.java | 7 +++++++ .../org/opentripplanner/apis/transmodel/schema.graphql | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java b/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java index 6e63ddf3921..438c3d48052 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java @@ -434,6 +434,13 @@ public class EnumTypes { "the road, drive to a drop-off point along the road, and walk the rest of the way. " + "This can include various taxi-services or kiss & ride." ) + .value( + "car_rental", + StreetMode.CAR_RENTAL, + "Walk to a car rental point along " + + "the road, drive to a drop-off point along the road, and walk the rest of the way. " + + "This can include car rental at fixed locations or free-floating services." + ) .value( "flexible", StreetMode.FLEXIBLE, diff --git a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql index 64440780f78..72f682e4634 100644 --- a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql +++ b/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 : flexible, timePenalty : "10m + 1.30 t", costFactor : 1.3}], + 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}], "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: []" @@ -1773,6 +1773,8 @@ enum StreetMode { car_park "Walk to a pickup point along the road, drive to a drop-off point along the road, and walk the rest of the way. This can include various taxi-services or kiss & ride." car_pickup + "Walk to a car rental point along the road, drive to a drop-off point along the road, and walk the rest of the way. This can include car rental at fixed locations or free-floating services." + car_rental "Walk to an eligible pickup area for flexible transportation, ride to an eligible drop-off area and then walk the rest of the way." flexible "Walk only" From c3c1ea08a9da9711315a961a49633ed7b85e993c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:34:33 +0000 Subject: [PATCH 136/192] chore(deps): update dependency io.github.git-commit-id:git-commit-id-maven-plugin to v9.0.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 02f87de47d6..00165487fb3 100644 --- a/pom.xml +++ b/pom.xml @@ -321,7 +321,7 @@ but we need the Maven project version as well, so we perform substitution. --> io.github.git-commit-id git-commit-id-maven-plugin - 9.0.0 + 9.0.1 From d4943c0db68a23be213bd64210bf154f72e3704a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 19 Jul 2024 10:42:27 +0200 Subject: [PATCH 137/192] Add newline in client to trigger build --- client/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/client/README.md b/client/README.md index 54971062971..ad86e9666cb 100644 --- a/client/README.md +++ b/client/README.md @@ -63,3 +63,4 @@ or add it to a new `.env.development.local` file (this file will be ignored by g In production mode, the default is to access OTP via the same origin as the client (see `.env`). This behavior can also be modified by changing the previously mentioned environment variable at build-time. + From cc74ad82dc89810d219cba9434392a798bb890c5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 19 Jul 2024 10:46:47 +0200 Subject: [PATCH 138/192] Format code --- client/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/client/README.md b/client/README.md index ad86e9666cb..54971062971 100644 --- a/client/README.md +++ b/client/README.md @@ -63,4 +63,3 @@ or add it to a new `.env.development.local` file (this file will be ignored by g In production mode, the default is to access OTP via the same origin as the client (see `.env`). This behavior can also be modified by changing the previously mentioned environment variable at build-time. - From 6382fd8d4221c2a173d396a84504a77ce7c388b9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 19 Jul 2024 10:47:56 +0200 Subject: [PATCH 139/192] Add newline change to trigger build --- client/.env | 1 + 1 file changed, 1 insertion(+) diff --git a/client/.env b/client/.env index ca1d71c90e3..a2d09f3b312 100644 --- a/client/.env +++ b/client/.env @@ -1,2 +1,3 @@ VITE_API_URL=/otp/transmodel/v3 VITE_DEBUG_STYLE_URL=/otp/routers/default/inspector/vectortile/style.json + From 23bbf8915c6f462742dad904361cb937de28b03e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:58:00 +0000 Subject: [PATCH 140/192] fix(deps): update geotools.version to v31.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00165487fb3..ae3fdfec72d 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 154 - 31.2 + 31.3 2.51.1 2.17.2 3.1.7 From 0ff397da1fc18efeaee86b526b10f6c0faac797e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:26:06 +0000 Subject: [PATCH 141/192] chore(deps): update vitest monorepo to v2 --- client/package-lock.json | 804 ++++++++++++++++++++++----------------- client/package.json | 4 +- 2 files changed, 447 insertions(+), 361 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 3c4520eb745..a5c2422df3f 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -29,7 +29,7 @@ "@typescript-eslint/eslint-plugin": "7.14.1", "@typescript-eslint/parser": "7.14.1", "@vitejs/plugin-react": "4.3.1", - "@vitest/coverage-v8": "1.6.0", + "@vitest/coverage-v8": "2.0.4", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", @@ -41,7 +41,7 @@ "prettier": "3.3.2", "typescript": "5.5.2", "vite": "5.3.2", - "vitest": "1.6.0" + "vitest": "2.0.4" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2799,25 +2799,109 @@ "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, - "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==", + "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", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, "engines": { - "node": ">=8" + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "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": { - "@sinclair/typebox": "^0.27.8" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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, + "engines": { + "node": ">=8" } }, "node_modules/@jridgewell/gen-mapping": { @@ -3372,6 +3456,17 @@ "node": ">=10.12.0" } }, + "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" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -3610,12 +3705,6 @@ "win32" ] }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, "node_modules/@swc/helpers": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.7.tgz", @@ -4086,192 +4175,119 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", - "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.0.4.tgz", + "integrity": "sha512-i4lx/Wpg5zF1h2op7j0wdwuEQxaL/YTwwQaKuKMHYj7MMh8c7I4W7PNfOptZBCSBZI0z1qwn64o0pM/pA8Tz1g==", "dev": true, + "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.1", + "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.4", + "debug": "^4.3.5", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.4", - "istanbul-reports": "^3.1.6", - "magic-string": "^0.30.5", - "magicast": "^0.3.3", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0" + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.10", + "magicast": "^0.3.4", + "std-env": "^3.7.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.6.0" + "vitest": "2.0.4" } }, "node_modules/@vitest/expect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", - "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.4.tgz", + "integrity": "sha512-39jr5EguIoanChvBqe34I8m1hJFI4+jxvdOpD7gslZrVQBKhh8H9eD7J/LJX4zakrw23W+dITQTDqdt43xVcJw==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "chai": "^4.3.10" + "@vitest/spy": "2.0.4", + "@vitest/utils": "2.0.4", + "chai": "^5.1.1", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", - "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "node_modules/@vitest/pretty-format": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.4.tgz", + "integrity": "sha512-RYZl31STbNGqf4l2eQM1nvKPXE0NhC6Eq0suTTePc4mtMQ1Fn8qZmjV4emZdEdG2NOWGKSCrHZjmTqDCDoeFBw==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/utils": "1.6.0", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "node_modules/@vitest/runner": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.4.tgz", + "integrity": "sha512-Gk+9Su/2H2zNfNdeJR124gZckd5st4YoSuhF1Rebi37qTXKnqYyFCd9KP4vl2cQHbtuVKjfEKrNJxHHCW8thbQ==", "dev": true, + "license": "MIT", "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" + "@vitest/utils": "2.0.4", + "pathe": "^1.1.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", - "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.4.tgz", + "integrity": "sha512-or6Mzoz/pD7xTvuJMFYEtso1vJo1S5u6zBTinfl+7smGUhqybn6VjzCDMhmTyVOFWwkCMuNjmNNxnyXPgKDoPw==", "dev": true, + "license": "MIT", "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "2.0.4", + "magic-string": "^0.30.10", + "pathe": "^1.1.2" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/snapshot/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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@vitest/snapshot/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/snapshot/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, "node_modules/@vitest/spy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", - "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.4.tgz", + "integrity": "sha512-uTXU56TNoYrTohb+6CseP8IqNwlNdtPwEO0AWl+5j7NelS6x0xZZtP0bDWaLvOfUbaYwhhWp1guzXUxkC7mW7Q==", "dev": true, + "license": "MIT", "dependencies": { - "tinyspy": "^2.2.0" + "tinyspy": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", - "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.4.tgz", + "integrity": "sha512-Zc75QuuoJhOBnlo99ZVUkJIuq4Oj0zAkrQ2VzCqNCx6wAwViHEh5Fnp4fiJTE9rA+sAoXRf00Z9xGgfEzV6fzQ==", "dev": true, + "license": "MIT", "dependencies": { - "diff-sequences": "^29.6.3", + "@vitest/pretty-format": "2.0.4", "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" + "loupe": "^3.1.1", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/utils/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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@vitest/utils/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/utils/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, "node_modules/@whatwg-node/events": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.0.3.tgz", @@ -4325,15 +4341,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/agent-base": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", @@ -4643,12 +4650,13 @@ } }, "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "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": "*" + "node": ">=12" } }, "node_modules/assign-symbols": { @@ -5020,21 +5028,20 @@ } }, "node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", "dev": true, + "license": "MIT", "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=12" } }, "node_modules/chalk": { @@ -5098,15 +5105,13 @@ "dev": true }, "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, + "license": "MIT", "engines": { - "node": "*" + "node": ">= 16" } }, "node_modules/classnames": { @@ -5441,10 +5446,11 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -5473,13 +5479,11 @@ "dev": true }, "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "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, - "dependencies": { - "type-detect": "^4.0.0" - }, + "license": "MIT", "engines": { "node": ">=6" } @@ -5616,15 +5620,6 @@ "node": ">=0.10" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -5722,6 +5717,13 @@ "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.4.717", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.717.tgz", @@ -6500,6 +6502,7 @@ "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" } @@ -6826,6 +6829,36 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", + "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -6925,6 +6958,7 @@ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -8118,10 +8152,11 @@ } }, "node_modules/istanbul-lib-source-maps": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", - "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "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", @@ -8157,6 +8192,22 @@ "set-function-name": "^2.0.1" } }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jiti": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", @@ -8324,12 +8375,6 @@ "node": ">=6" } }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, "node_modules/jsonify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", @@ -8457,22 +8502,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", - "dev": true, - "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8587,10 +8616,11 @@ } }, "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", "dev": true, + "license": "MIT", "dependencies": { "get-func-name": "^2.0.1" } @@ -8633,26 +8663,25 @@ } }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" } }, "node_modules/magicast": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", - "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz", + "integrity": "sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "source-map-js": "^1.0.2" + "@babel/parser": "^7.24.4", + "@babel/types": "^7.24.0", + "source-map-js": "^1.2.0" } }, "node_modules/make-dir": { @@ -8853,16 +8882,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mlly": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", - "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" } }, "node_modules/ms": { @@ -9299,6 +9326,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -9439,6 +9473,30 @@ "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" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -9455,12 +9513,13 @@ "dev": true }, "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "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": "*" + "node": ">= 14.16" } }, "node_modules/pbf": { @@ -9493,17 +9552,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" - } - }, "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", @@ -10583,6 +10631,29 @@ "node": ">=8" } }, + "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", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -10687,6 +10758,20 @@ "node": ">=8" } }, + "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" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -10720,24 +10805,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", - "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", - "dev": true, - "dependencies": { - "js-tokens": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", - "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", - "dev": true - }, "node_modules/supercluster": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", @@ -10786,39 +10853,39 @@ "dev": true }, "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "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": "^7.1.4", - "minimatch": "^3.0.4" + "glob": "^10.4.1", + "minimatch": "^9.0.4" }, "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/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": ">=18" } }, - "node_modules/test-exclude/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/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": { - "brace-expansion": "^1.1.7" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/text-table": { @@ -10834,18 +10901,20 @@ "dev": true }, "node_modules/tinybench": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", - "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", - "dev": true + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", + "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "dev": true, + "license": "MIT" }, "node_modules/tinypool": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", - "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.0.tgz", + "integrity": "sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": "^18.0.0 || >=20.0.0" } }, "node_modules/tinyqueue": { @@ -10853,11 +10922,22 @@ "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" }, + "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": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", + "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -11015,15 +11095,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -11159,12 +11230,6 @@ "node": "*" } }, - "node_modules/ufo": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", - "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", - "dev": true - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -11398,15 +11463,16 @@ } }, "node_modules/vite-node": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", - "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.4.tgz", + "integrity": "sha512-ZpJVkxcakYtig5iakNeL7N3trufe3M6vGuzYAr4GsbCTwobDeyPJpE4cjDhhPluv8OvQCFzu2LWp6GkoKRITXA==", "dev": true, + "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", + "debug": "^4.3.5", + "pathe": "^1.1.2", + "tinyrainbow": "^1.2.0", "vite": "^5.0.0" }, "bin": { @@ -11420,31 +11486,31 @@ } }, "node_modules/vitest": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", - "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.4.tgz", + "integrity": "sha512-luNLDpfsnxw5QSW4bISPe6tkxVvv5wn2BBs/PuDRkhXZ319doZyLOBr1sjfB5yCEpTiU7xCAdViM8TNVGPwoog==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/expect": "1.6.0", - "@vitest/runner": "1.6.0", - "@vitest/snapshot": "1.6.0", - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", + "@ampproject/remapping": "^2.3.0", + "@vitest/expect": "2.0.4", + "@vitest/pretty-format": "^2.0.4", + "@vitest/runner": "2.0.4", + "@vitest/snapshot": "2.0.4", + "@vitest/spy": "2.0.4", + "@vitest/utils": "2.0.4", + "chai": "^5.1.1", + "debug": "^4.3.5", "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", + "magic-string": "^0.30.10", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "tinybench": "^2.8.0", + "tinypool": "^1.0.0", + "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "1.6.0", - "why-is-node-running": "^2.2.2" + "vite-node": "2.0.4", + "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" @@ -11458,8 +11524,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.0", - "@vitest/ui": "1.6.0", + "@vitest/browser": "2.0.4", + "@vitest/ui": "2.0.4", "happy-dom": "*", "jsdom": "*" }, @@ -11701,10 +11767,11 @@ } }, "node_modules/why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "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" @@ -11730,6 +11797,25 @@ "node": ">=8" } }, + "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", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/client/package.json b/client/package.json index 509c25752dd..1681ed56d1d 100644 --- a/client/package.json +++ b/client/package.json @@ -38,7 +38,7 @@ "@typescript-eslint/eslint-plugin": "7.14.1", "@typescript-eslint/parser": "7.14.1", "@vitejs/plugin-react": "4.3.1", - "@vitest/coverage-v8": "1.6.0", + "@vitest/coverage-v8": "2.0.4", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", @@ -50,6 +50,6 @@ "prettier": "3.3.2", "typescript": "5.5.2", "vite": "5.3.2", - "vitest": "1.6.0" + "vitest": "2.0.4" } } From 5765d5856d403f7e43579f7f7b08b9760cffe58f Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 22 Jul 2024 15:08:10 +0300 Subject: [PATCH 142/192] Clarify where logback extensions file should be in a container --- docs/Container-Image.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/Container-Image.md b/docs/Container-Image.md index d8e6db6d614..ed2441f5ac1 100644 --- a/docs/Container-Image.md +++ b/docs/Container-Image.md @@ -4,7 +4,9 @@ The CI pipeline deploys container images for runtimes like Docker, Kubernetes or [Dockerhub](https://hub.docker.com/r/opentripplanner/opentripplanner/tags). The image assumes you use a volume to mount the input data (GTFS/NeTex, OSM) and config files into -`/var/opentripplanner/`. When serving a graph it's also expected to be in this directory. +`/var/opentripplanner/`. When serving a graph it's also expected to be in this directory. If a logback +extensions file needs to be used, it should be mounted to the root location `/logback-include-extensions.xml` +instead of the `/var/opentripplanner/` directory. ## Quick start From 6764a08d89a599525c161c6370ee6f3ebcdc551c Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 23 Jul 2024 13:40:20 +0200 Subject: [PATCH 143/192] Fix megre conflict in TransitGroupPriorityServiceTest --- .../TransitGroupPriorityServiceTest.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java index 993eb14c424..fe2cb361f07 100644 --- a/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/model/network/grouppriority/TransitGroupPriorityServiceTest.java @@ -98,8 +98,14 @@ void lookupTransitGroupIdByAgency() { // Verify we get the same result with using the trip, not trip-pattern assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullTrip)); - assertEquals(EXP_GROUP_1, subject.lookupTransitGroupPriorityId(busB2.getTrip(0))); - assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR3.getTrip(0))); + assertEquals( + EXP_GROUP_1, + subject.lookupTransitGroupPriorityId(busB2.getScheduledTimetable().getTripTimes(0).getTrip()) + ); + assertEquals( + EXP_GROUP_2, + subject.lookupTransitGroupPriorityId(railR3.getScheduledTimetable().getTripTimes(0).getTrip()) + ); } @Test @@ -122,7 +128,10 @@ void lookupTransitPriorityGroupIdByGlobalMode() { // Verify we get the same result with using the trip, not trip-pattern assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(nullTrip)); - assertEquals(EXP_GROUP_2, subject.lookupTransitGroupPriorityId(railR1.getTrip(0))); + assertEquals( + EXP_GROUP_2, + subject.lookupTransitGroupPriorityId(railR1.getScheduledTimetable().getTripTimes(0).getTrip()) + ); } private static TestRouteData route( From 18985dbfb696ca53e17391a3909e0e922c78aa4b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 23 Jul 2024 15:01:12 +0200 Subject: [PATCH 144/192] Apply suggestions from code review Co-authored-by: Johan Torin --- .../org/opentripplanner/apis/transmodel/model/EnumTypes.java | 2 +- .../org/opentripplanner/apis/transmodel/schema.graphql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java b/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java index 438c3d48052..b325eac3653 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/EnumTypes.java @@ -439,7 +439,7 @@ public class EnumTypes { StreetMode.CAR_RENTAL, "Walk to a car rental point along " + "the road, drive to a drop-off point along the road, and walk the rest of the way. " + - "This can include car rental at fixed locations or free-floating services." + "This can include car rentals at fixed locations or free-floating services." ) .value( "flexible", diff --git a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql index 72f682e4634..0d2bf71dcc6 100644 --- a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql +++ b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql @@ -1773,7 +1773,7 @@ enum StreetMode { car_park "Walk to a pickup point along the road, drive to a drop-off point along the road, and walk the rest of the way. This can include various taxi-services or kiss & ride." car_pickup - "Walk to a car rental point along the road, drive to a drop-off point along the road, and walk the rest of the way. This can include car rental at fixed locations or free-floating services." + "Walk to a car rental point along the road, drive to a drop-off point along the road, and walk the rest of the way. This can include car rentals at fixed locations or free-floating services." car_rental "Walk to an eligible pickup area for flexible transportation, ride to an eligible drop-off area and then walk the rest of the way." flexible From 03dae4e1b6acd6b1c90d693d7b0614a1a50ba8a3 Mon Sep 17 00:00:00 2001 From: OTP Serialization Version Bot Date: Tue, 23 Jul 2024 13:02:27 +0000 Subject: [PATCH 145/192] Bump serialization version id for #5978 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ae3fdfec72d..2f007cbb214 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ - 154 + 155 31.3 2.51.1 From fc53d94216474ba06598ff9427a8b487b76cf93c Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 23 Jul 2024 15:05:04 +0200 Subject: [PATCH 146/192] Apply review suggestions --- .../transit/service/TransitEditorService.java | 13 +++++++++++++ .../PollingVehiclePositionUpdater.java | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java b/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java index 3bdcf607363..150d1749272 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java @@ -39,9 +39,22 @@ void addTripOnServiceDateForTripAndDay( FeedScopedId getOrCreateServiceIdForDate(LocalDate serviceDate); + /** + * Set the original, immutable, transit layer, + * based on scheduled data (not real-time data). + */ void setTransitLayer(TransitLayer transitLayer); + /** + * Return true if a real-time transit layer is present. + * The real-time transit layer is optional, + * it is present only when real-time updaters are configured. + */ boolean hasRealtimeTransitLayer(); + /** + * Publish the latest snapshot of the real-time transit layer. + * Should be called only when creating a new TransitLayer, from the graph writer thread. + */ void setRealtimeTransitLayer(TransitLayer realtimeTransitLayer); } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java index 6b9391f3ea2..f1355ca0fa4 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java @@ -45,6 +45,8 @@ public PollingVehiclePositionUpdater( super(params); this.vehiclePositionSource = new GtfsRealtimeHttpVehiclePositionSource(params.url(), params.headers()); + // TODO Inject TransitService, do not create it here. We currently do not + // support dagger injection in updaters, so this is ok for now. TransitService transitService = new DefaultTransitService(transitModel); var fuzzyTripMatcher = params.fuzzyTripMatching() ? new GtfsRealtimeFuzzyTripMatcher(transitService) From 7b1f7602612702aa41b60aef285db82a5587d6b8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 23 Jul 2024 15:55:33 +0200 Subject: [PATCH 147/192] Fix copy&paste error Co-authored-by: Henrik Abrahamsson <127481124+habrahamsson-skanetrafiken@users.noreply.github.com> --- .../standalone/config/buildconfig/NetexConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/standalone/config/buildconfig/NetexConfig.java b/src/main/java/org/opentripplanner/standalone/config/buildconfig/NetexConfig.java index 6be400b7549..52bccf605d8 100644 --- a/src/main/java/org/opentripplanner/standalone/config/buildconfig/NetexConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/buildconfig/NetexConfig.java @@ -172,7 +172,7 @@ private static NetexFeedParameters.Builder mapFilePatternParameters( .since(V2_6) .summary("Ignore Parking elements.") .docDefaultValue(base.ignoreParking()) - .asBoolean(base.ignoreFareFrame()) + .asBoolean(base.ignoreParking()) ); } From 18ef55532060d581062d3a3c79d669f6da2c3e33 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 23 Jul 2024 16:06:42 +0200 Subject: [PATCH 148/192] Move component into separate file --- .../ItineraryList/InterchangeInfo.tsx | 22 +++++++++++++++++++ .../ItineraryList/ItineraryLegDetails.tsx | 22 ++----------------- 2 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 client/src/components/ItineraryList/InterchangeInfo.tsx diff --git a/client/src/components/ItineraryList/InterchangeInfo.tsx b/client/src/components/ItineraryList/InterchangeInfo.tsx new file mode 100644 index 00000000000..0dbd7368c0e --- /dev/null +++ b/client/src/components/ItineraryList/InterchangeInfo.tsx @@ -0,0 +1,22 @@ +import logo from '../../static/img/stay-seated.svg'; +import { Leg } from '../../gql/graphql.ts'; + +/** + * Displays an icon if a leg has a stay-seated transfer from the previous one. + */ +export function InterchangeInfo({ leg }: { leg: Leg }) { + if (leg.interchangeFrom?.staySeated) { + return ( + Stay-seated transfer + ); + } else { + return <>; + } +} diff --git a/client/src/components/ItineraryList/ItineraryLegDetails.tsx b/client/src/components/ItineraryList/ItineraryLegDetails.tsx index 1990b7c62ff..56fdf430388 100644 --- a/client/src/components/ItineraryList/ItineraryLegDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryLegDetails.tsx @@ -2,25 +2,7 @@ import { Leg, Mode } from '../../gql/graphql.ts'; import { LegTime } from './LegTime.tsx'; import { formatDistance } from '../../util/formatDistance.ts'; import { formatDuration } from '../../util/formatDuration.ts'; -import logo from '../../static/img/stay-seated.svg'; -import React from 'react'; - -const staySeatedIcon: (leg: Leg) => React.JSX.Element = (leg: Leg) => { - if (leg.interchangeFrom?.staySeated) { - return ( - Stay-seated transfer - ); - } else { - return <>; - } -}; +import { InterchangeInfo } from './InterchangeInfo.tsx'; export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean }) { return ( @@ -29,7 +11,7 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean {formatDistance(leg.distance)}, {formatDuration(leg.duration)}

- {staySeatedIcon(leg)} + Date: Tue, 23 Jul 2024 16:09:17 +0200 Subject: [PATCH 149/192] Replace identity with equality --- .../org/opentripplanner/netex/config/NetexFeedParameters.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java index 5ff5004d0c8..f565ea5a187 100644 --- a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java +++ b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java @@ -148,7 +148,7 @@ public boolean equals(Object o) { sharedFilePattern.equals(that.sharedFilePattern) && sharedGroupFilePattern.equals(that.sharedGroupFilePattern) && groupFilePattern.equals(that.groupFilePattern) && - ignoredFeatures == that.ignoredFeatures && + ignoredFeatures.equals(that.ignoredFeatures) && ferryIdsNotAllowedForBicycle.equals(that.ferryIdsNotAllowedForBicycle) ); } From 1cfaa3800ddf777e47785329b27199cff3b8599a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 23 Jul 2024 16:22:57 +0200 Subject: [PATCH 150/192] Add Javadoc for KeyValue --- .../inspector/vector/KeyValue.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java b/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java index 8557ad0a465..edad5e1b295 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java +++ b/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java @@ -1,14 +1,30 @@ package org.opentripplanner.inspector.vector; +import jakarta.annotation.Nullable; import java.util.Collection; import java.util.stream.Collectors; import org.opentripplanner.transit.model.framework.FeedScopedId; +/** + * A key value pair that represents data being sent to the vector tile library for visualisation + * in a map (including popups). + *

+ * The underlying format (and library) supports only a limited number of Java types and silently + * drops those that aren't supported: https://github.com/CI-CMG/mapbox-vector-tile/blob/master/src/main/java/edu/colorado/cires/cmg/mvt/encoding/MvtValue.java#L18-L40 + *

+ * For this reason this class also has static initializer that automatically converts common + * OTP classes into vector tile-compatible strings. + */ public record KeyValue(String key, Object value) { public static KeyValue kv(String key, Object value) { return new KeyValue(key, value); } - public static KeyValue kv(String key, FeedScopedId value) { + + /** + * A {@link FeedScopedId} is not a type that can be converted to a vector tile feature property + * value. Therefore, we convert it to a string after performing a null check. + */ + public static KeyValue kv(String key, @Nullable FeedScopedId value) { if (value != null) { return new KeyValue(key, value.toString()); } else { From a2eaecc797bd9a58d8191d972822825ec1de76b6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 23 Jul 2024 16:31:05 +0200 Subject: [PATCH 151/192] Rename variable --- .../street/model/vertex/VehicleParkingEntranceVertex.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java index 2f389f60817..ba7a1540715 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java @@ -59,7 +59,7 @@ public boolean isLinkedToGraph() { return hasLink(getIncoming()) || hasLink(getOutgoing()); } - private boolean hasLink(Collection incoming) { - return incoming.stream().anyMatch(StreetVehicleParkingLink.class::isInstance); + private boolean hasLink(Collection edges) { + return edges.stream().anyMatch(StreetVehicleParkingLink.class::isInstance); } } From 2c34a4e8f5b9b381e07ad26a64dc263681169b38 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 12 Jul 2024 13:41:07 +0200 Subject: [PATCH 152/192] Remove lock on Timetable snapshot buffer --- .../ext/siri/SiriTimetableSnapshotSource.java | 14 +- .../trip/TimetableSnapshotManager.java | 44 +--- .../updater/trip/TimetableSnapshotSource.java | 198 +++++++++--------- 3 files changed, 106 insertions(+), 150 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index 7ab316bfc55..3284e1620bd 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -102,11 +102,10 @@ public UpdateResult applyEstimatedTimetable( List> results = new ArrayList<>(); - snapshotManager.withLock(() -> { - if (incrementality == FULL_DATASET) { - // Remove all updates from the buffer - snapshotManager.clearBuffer(feedId); - } + if (incrementality == FULL_DATASET) { + // Remove all updates from the buffer + snapshotManager.clearBuffer(feedId); + } for (var etDelivery : updates) { for (var estimatedJourneyVersion : etDelivery.getEstimatedJourneyVersionFrames()) { @@ -118,10 +117,9 @@ public UpdateResult applyEstimatedTimetable( } } - LOG.debug("message contains {} trip updates", updates.size()); + LOG.debug("message contains {} trip updates", updates.size()); - snapshotManager.purgeAndCommit(); - }); + snapshotManager.purgeAndCommit(); return UpdateResult.ofResults(results); } diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java index bf7c8c98919..71703df459a 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java @@ -2,7 +2,6 @@ import java.time.LocalDate; import java.util.Objects; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import javax.annotation.Nullable; import org.opentripplanner.framework.time.CountdownTimer; @@ -30,21 +29,10 @@ public final class TimetableSnapshotManager { private static final Logger LOG = LoggerFactory.getLogger(TimetableSnapshotManager.class); private final TransitLayerUpdater transitLayerUpdater; - /** - * Lock to indicate that buffer is in use - */ - private final ReentrantLock bufferLock = new ReentrantLock(true); /** - * The working copy of the timetable snapshot. Should not be visible to routing threads. Should - * only be modified by a thread that holds a lock on {@link #bufferLock}. All public methods that - * might modify this buffer will correctly acquire the lock. By design, only one thread should - * ever be writing to this buffer. - * TODO RT_AB: research and document why this lock is needed since only one thread should ever be - * writing to this buffer. One possible reason may be a need to suspend writes while indexing - * and swapping out the buffer. But the original idea was to make a new copy of the buffer - * before re-indexing it. While refactoring or rewriting parts of this system, we could throw - * an exception if a writing section is entered by more than one thread. + * The working copy of the timetable snapshot. Should not be visible to routing threads. + * By design, only one thread should ever be writing to this buffer. */ private final TimetableSnapshot buffer = new TimetableSnapshot(); @@ -99,18 +87,6 @@ public TimetableSnapshotManager( * to the snapshot to release resources. */ public TimetableSnapshot getTimetableSnapshot() { - // Try to get a lock on the buffer - if (bufferLock.tryLock()) { - // Make a new snapshot if necessary - try { - commitTimetableSnapshot(false); - return snapshot; - } finally { - bufferLock.unlock(); - } - } - // No lock could be obtained because there is either a snapshot commit busy or updates - // are applied at this moment, just return the current snapshot return snapshot; } @@ -205,22 +181,6 @@ private boolean purgeExpiredData() { return buffer.purgeExpiredData(previously); } - /** - * Execute a {@code Runnable} with a locked snapshot buffer and release the lock afterwards. While - * the action of locking and unlocking is not complicated to do for calling code, this method - * exists so that the lock instance is a private field. - */ - public void withLock(Runnable action) { - bufferLock.lock(); - - try { - action.run(); - } finally { - // Always release lock - bufferLock.unlock(); - } - } - /** * Clear all data of snapshot for the provided feed id */ diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index c452d5f58f8..10137b99837 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -156,121 +156,119 @@ public UpdateResult applyTripUpdates( Map failuresByRelationship = new HashMap<>(); List> results = new ArrayList<>(); - snapshotManager.withLock(() -> { - if (updateIncrementality == FULL_DATASET) { - // Remove all updates from the buffer - snapshotManager.clearBuffer(feedId); + if (updateIncrementality == FULL_DATASET) { + // Remove all updates from the buffer + snapshotManager.clearBuffer(feedId); + } + + LOG.debug("message contains {} trip updates", updates.size()); + int uIndex = 0; + for (TripUpdate tripUpdate : updates) { + if (!tripUpdate.hasTrip()) { + debug(feedId, "", "Missing TripDescriptor in gtfs-rt trip update: \n{}", tripUpdate); + continue; } - LOG.debug("message contains {} trip updates", updates.size()); - int uIndex = 0; - for (TripUpdate tripUpdate : updates) { - if (!tripUpdate.hasTrip()) { - debug(feedId, "", "Missing TripDescriptor in gtfs-rt trip update: \n{}", tripUpdate); - continue; - } + if (fuzzyTripMatcher != null) { + final TripDescriptor trip = fuzzyTripMatcher.match(feedId, tripUpdate.getTrip()); + tripUpdate = tripUpdate.toBuilder().setTrip(trip).build(); + } - if (fuzzyTripMatcher != null) { - final TripDescriptor trip = fuzzyTripMatcher.match(feedId, tripUpdate.getTrip()); - tripUpdate = tripUpdate.toBuilder().setTrip(trip).build(); - } + final TripDescriptor tripDescriptor = tripUpdate.getTrip(); + + if (!tripDescriptor.hasTripId() || tripDescriptor.getTripId().isBlank()) { + debug(feedId, "", "No trip id found for gtfs-rt trip update: \n{}", tripUpdate); + results.add(Result.failure(UpdateError.noTripId(INVALID_INPUT_STRUCTURE))); + continue; + } - final TripDescriptor tripDescriptor = tripUpdate.getTrip(); + FeedScopedId tripId = new FeedScopedId(feedId, tripUpdate.getTrip().getTripId()); - if (!tripDescriptor.hasTripId() || tripDescriptor.getTripId().isBlank()) { - debug(feedId, "", "No trip id found for gtfs-rt trip update: \n{}", tripUpdate); - results.add(Result.failure(UpdateError.noTripId(INVALID_INPUT_STRUCTURE))); + LocalDate serviceDate; + if (tripDescriptor.hasStartDate()) { + try { + serviceDate = ServiceDateUtils.parseString(tripDescriptor.getStartDate()); + } catch (final ParseException e) { + debug( + tripId, + "Failed to parse start date in gtfs-rt trip update: {}", + tripDescriptor.getStartDate() + ); continue; } + } else { + // TODO: figure out the correct service date. For the special case that a trip + // starts for example at 40:00, yesterday would probably be a better guess. + serviceDate = localDateNow.get(); + } + // Determine what kind of trip update this is + var scheduleRelationship = Objects.requireNonNullElse( + tripDescriptor.getScheduleRelationship(), + SCHEDULED + ); + if (updateIncrementality == DIFFERENTIAL) { + purgePatternModifications(scheduleRelationship, tripId, serviceDate); + } - FeedScopedId tripId = new FeedScopedId(feedId, tripUpdate.getTrip().getTripId()); + uIndex += 1; + LOG.debug("trip update #{} ({} updates) :", uIndex, tripUpdate.getStopTimeUpdateCount()); + LOG.trace("{}", tripUpdate); - LocalDate serviceDate; - if (tripDescriptor.hasStartDate()) { - try { - serviceDate = ServiceDateUtils.parseString(tripDescriptor.getStartDate()); - } catch (final ParseException e) { - debug( + Result result; + try { + result = + switch (scheduleRelationship) { + case SCHEDULED -> handleScheduledTrip( + tripUpdate, tripId, - "Failed to parse start date in gtfs-rt trip update: {}", - tripDescriptor.getStartDate() + serviceDate, + backwardsDelayPropagationType ); - continue; - } - } else { - // TODO: figure out the correct service date. For the special case that a trip - // starts for example at 40:00, yesterday would probably be a better guess. - serviceDate = localDateNow.get(); - } - // Determine what kind of trip update this is - var scheduleRelationship = Objects.requireNonNullElse( - tripDescriptor.getScheduleRelationship(), - SCHEDULED - ); - if (updateIncrementality == DIFFERENTIAL) { - purgePatternModifications(scheduleRelationship, tripId, serviceDate); - } - - uIndex += 1; - LOG.debug("trip update #{} ({} updates) :", uIndex, tripUpdate.getStopTimeUpdateCount()); - LOG.trace("{}", tripUpdate); - - Result result; - try { - result = - switch (scheduleRelationship) { - case SCHEDULED -> handleScheduledTrip( - tripUpdate, - tripId, - serviceDate, - backwardsDelayPropagationType - ); - case ADDED -> validateAndHandleAddedTrip( - tripUpdate, - tripDescriptor, - tripId, - serviceDate - ); - case CANCELED -> handleCanceledTrip( - tripId, - serviceDate, - CancelationType.CANCEL, - updateIncrementality - ); - case DELETED -> handleCanceledTrip( - tripId, - serviceDate, - CancelationType.DELETE, - updateIncrementality - ); - case REPLACEMENT -> validateAndHandleModifiedTrip( - tripUpdate, - tripDescriptor, - tripId, - serviceDate - ); - case UNSCHEDULED -> UpdateError.result(tripId, NOT_IMPLEMENTED_UNSCHEDULED); - case DUPLICATED -> UpdateError.result(tripId, NOT_IMPLEMENTED_DUPLICATED); - }; - } catch (DataValidationException e) { - result = DataValidationExceptionMapper.toResult(e); - } + case ADDED -> validateAndHandleAddedTrip( + tripUpdate, + tripDescriptor, + tripId, + serviceDate + ); + case CANCELED -> handleCanceledTrip( + tripId, + serviceDate, + CancelationType.CANCEL, + updateIncrementality + ); + case DELETED -> handleCanceledTrip( + tripId, + serviceDate, + CancelationType.DELETE, + updateIncrementality + ); + case REPLACEMENT -> validateAndHandleModifiedTrip( + tripUpdate, + tripDescriptor, + tripId, + serviceDate + ); + case UNSCHEDULED -> UpdateError.result(tripId, NOT_IMPLEMENTED_UNSCHEDULED); + case DUPLICATED -> UpdateError.result(tripId, NOT_IMPLEMENTED_DUPLICATED); + }; + } catch (DataValidationException e) { + result = DataValidationExceptionMapper.toResult(e); + } - results.add(result); - if (result.isFailure()) { - debug(tripId, "Failed to apply TripUpdate."); - LOG.trace(" Contents: {}", tripUpdate); - if (failuresByRelationship.containsKey(scheduleRelationship)) { - var c = failuresByRelationship.get(scheduleRelationship); - failuresByRelationship.put(scheduleRelationship, ++c); - } else { - failuresByRelationship.put(scheduleRelationship, 1); - } + results.add(result); + if (result.isFailure()) { + debug(tripId, "Failed to apply TripUpdate."); + LOG.trace(" Contents: {}", tripUpdate); + if (failuresByRelationship.containsKey(scheduleRelationship)) { + var c = failuresByRelationship.get(scheduleRelationship); + failuresByRelationship.put(scheduleRelationship, ++c); + } else { + failuresByRelationship.put(scheduleRelationship, 1); } } + } - snapshotManager.purgeAndCommit(); - }); + snapshotManager.purgeAndCommit(); var updateResult = UpdateResult.ofResults(results); From 892449a334f4cd611bc495acbc9e7707aee0d874 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 12 Jul 2024 13:56:54 +0200 Subject: [PATCH 153/192] Use current buffer from SiriTimetableSnapshotSource --- .../ext/siri/SiriTimetableSnapshotSource.java | 11 ++++++----- .../updater/trip/TimetableSnapshotManager.java | 6 ++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index 3284e1620bd..a7fc686c45b 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -129,6 +129,10 @@ public TimetableSnapshot getTimetableSnapshot() { return snapshotManager.getTimetableSnapshot(); } + private TimetableSnapshot getTimetableSnapshotBuffer() { + return snapshotManager.getTimetableSnapshotBuffer(); + } + private Result apply( EstimatedVehicleJourney journey, TransitEditorService transitService, @@ -193,13 +197,10 @@ private boolean shouldAddNewTrip( * Snapshot timetable is used as source if initialised, trip patterns scheduled timetable if not. */ private Timetable getCurrentTimetable(TripPattern tripPattern, LocalDate serviceDate) { - TimetableSnapshot timetableSnapshot = getTimetableSnapshot(); - if (timetableSnapshot != null) { - return timetableSnapshot.resolve(tripPattern, serviceDate); - } - return tripPattern.getScheduledTimetable(); + return getTimetableSnapshotBuffer().resolve(tripPattern, serviceDate); } + private Result handleModifiedTrip( @Nullable SiriFuzzyTripMatcher fuzzyTripMatcher, EntityResolver entityResolver, diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java index 71703df459a..3d95abd4853 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java @@ -90,6 +90,10 @@ public TimetableSnapshot getTimetableSnapshot() { return snapshot; } + public TimetableSnapshot getTimetableSnapshotBuffer() { + return buffer; + } + /** * Request a commit of the timetable snapshot. *

@@ -155,6 +159,8 @@ public void revertTripToScheduledTripPattern(FeedScopedId tripId, LocalDate serv buffer.revertTripToScheduledTripPattern(tripId, serviceDate); } + + /** * Remove realtime data from previous service dates from the snapshot. This is useful so that * instances that run for multiple days don't accumulate a lot of realtime data for past From 78dd01f38e64a4562448e2cd3acf51158d2fa36d Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 12 Jul 2024 14:11:31 +0200 Subject: [PATCH 154/192] use current buffer in TransitEditorService --- .../ext/siri/SiriTimetableSnapshotSource.java | 17 ++++++++--------- .../transit/service/DefaultTransitService.java | 8 ++++++++ .../updater/trip/TimetableSnapshotManager.java | 2 -- .../updater/trip/TimetableSnapshotSource.java | 3 ++- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index a7fc686c45b..f87582a5e4e 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -71,7 +71,7 @@ public SiriTimetableSnapshotSource( parameters, () -> LocalDate.now(transitModel.getTimeZone()) ); - this.transitService = new DefaultTransitService(transitModel); + this.transitService = new DefaultTransitService(transitModel, getTimetableSnapshotBuffer()); this.tripPatternCache = new SiriTripPatternCache(tripPatternIdGenerator, transitService::getPatternForTrip); @@ -107,15 +107,15 @@ public UpdateResult applyEstimatedTimetable( snapshotManager.clearBuffer(feedId); } - for (var etDelivery : updates) { - for (var estimatedJourneyVersion : etDelivery.getEstimatedJourneyVersionFrames()) { - var journeys = estimatedJourneyVersion.getEstimatedVehicleJourneies(); - LOG.debug("Handling {} EstimatedVehicleJourneys.", journeys.size()); - for (EstimatedVehicleJourney journey : journeys) { - results.add(apply(journey, transitService, fuzzyTripMatcher, entityResolver)); - } + for (var etDelivery : updates) { + for (var estimatedJourneyVersion : etDelivery.getEstimatedJourneyVersionFrames()) { + var journeys = estimatedJourneyVersion.getEstimatedVehicleJourneies(); + LOG.debug("Handling {} EstimatedVehicleJourneys.", journeys.size()); + for (EstimatedVehicleJourney journey : journeys) { + results.add(apply(journey, transitService, fuzzyTripMatcher, entityResolver)); } } + } LOG.debug("message contains {} trip updates", updates.size()); @@ -200,7 +200,6 @@ private Timetable getCurrentTimetable(TripPattern tripPattern, LocalDate service return getTimetableSnapshotBuffer().resolve(tripPattern, serviceDate); } - private Result handleModifiedTrip( @Nullable SiriFuzzyTripMatcher fuzzyTripMatcher, EntityResolver entityResolver, diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 398cb806524..b3d68b6ffa5 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -79,6 +79,14 @@ public DefaultTransitService(TransitModel transitModel) { this.transitModelIndex = transitModel.getTransitModelIndex(); } + public DefaultTransitService( + TransitModel transitModel, + TimetableSnapshot timetableSnapshotBuffer + ) { + this(transitModel); + this.timetableSnapshot = timetableSnapshotBuffer; + } + @Override public Collection getFeedIds() { return this.transitModel.getFeedIds(); diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java index 3d95abd4853..232a798d2f8 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java @@ -159,8 +159,6 @@ public void revertTripToScheduledTripPattern(FeedScopedId tripId, LocalDate serv buffer.revertTripToScheduledTripPattern(tripId, serviceDate); } - - /** * Remove realtime data from previous service dates from the snapshot. This is useful so that * instances that run for multiple days don't accumulate a lot of realtime data for past diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index 10137b99837..ac03ab62e60 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -119,7 +119,8 @@ public TimetableSnapshotSource( this.snapshotManager = new TimetableSnapshotManager(transitModel.getTransitLayerUpdater(), parameters, localDateNow); this.timeZone = transitModel.getTimeZone(); - this.transitService = new DefaultTransitService(transitModel); + this.transitService = + new DefaultTransitService(transitModel, snapshotManager.getTimetableSnapshotBuffer()); this.deduplicator = transitModel.getDeduplicator(); this.serviceCodes = transitModel.getServiceCodes(); this.localDateNow = localDateNow; From 20505506c09b6b54172e4bb0b95b4ec59b9ec902 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 16 Jul 2024 17:23:56 +0200 Subject: [PATCH 155/192] Reduce visibility of commitTimetableSnapshot --- .../opentripplanner/updater/trip/TimetableSnapshotManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java index 232a798d2f8..e435623ce85 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java @@ -102,7 +102,7 @@ public TimetableSnapshot getTimetableSnapshotBuffer() { * * @param force Force the committing of a new snapshot even if the above conditions are not met. */ - public void commitTimetableSnapshot(final boolean force) { + void commitTimetableSnapshot(final boolean force) { if (force || snapshotFrequencyThrottle.timeIsUp()) { if (force || buffer.isDirty()) { LOG.debug("Committing {}", buffer); From 79802f06a689989109159604f1c1747f3184b668 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 16 Jul 2024 17:24:06 +0200 Subject: [PATCH 156/192] Introduce timetable snapshot flush --- .../ext/siri/SiriTimetableSnapshotSource.java | 4 ++ .../model/TimetableSnapshot.java | 5 +++ .../configure/UpdaterConfigurator.java | 22 +++++++++++ .../updater/spi/TimetableSnapshotFlush.java | 37 +++++++++++++++++++ .../updater/trip/TimetableSnapshotSource.java | 4 ++ 5 files changed, 72 insertions(+) create mode 100644 src/main/java/org/opentripplanner/updater/spi/TimetableSnapshotFlush.java diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index f87582a5e4e..4237fa318d7 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -327,4 +327,8 @@ private boolean markScheduledTripAsDeleted(Trip trip, final LocalDate serviceDat return success; } + + public void flushBuffer() { + snapshotManager.purgeAndCommit(); + } } diff --git a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java index 7d3f6181d44..c0a7737abce 100644 --- a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java +++ b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java @@ -65,6 +65,11 @@ * up timetables on this class could conceivably be replaced with snapshotting entire views of the * transit network. It would also be possible to make the realtime version of Timetables or * TripTimes the primary view, and include references back to their scheduled versions. + *

+ * Implementation note: when a snapshot is committed, the mutable state of this class is stored + * in final fields and completely initialized in the constructor. This provides an additional + * guarantee of safe-publication without synchronization. + * (see final Field Semantics) */ public class TimetableSnapshot { diff --git a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 1e9b730ee3a..103120b7ecb 100644 --- a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource; import org.opentripplanner.ext.siri.updater.SiriETUpdater; import org.opentripplanner.ext.siri.updater.SiriSXUpdater; @@ -20,6 +21,7 @@ import org.opentripplanner.updater.UpdatersParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdater; 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; @@ -94,6 +96,9 @@ private void configure() { ); GraphUpdaterManager updaterManager = new GraphUpdaterManager(graph, transitModel, updaters); + + configureTimetableSnapshotFlush(updaterManager); + updaterManager.startUpdaters(); // Stop the updater manager if it contains nothing @@ -223,4 +228,21 @@ private TimetableSnapshotSource provideGtfsTimetableSnapshot() { } return gtfsTimetableSnapshotSource; } + + /** + * If SIRI or GTFS real-time updaters are in use, configure a periodic flush of the timetable + * snapshot. + */ + private void configureTimetableSnapshotFlush(GraphUpdaterManager updaterManager) { + if (siriTimetableSnapshotSource != null || gtfsTimetableSnapshotSource != null) { + updaterManager + .getScheduler() + .scheduleWithFixedDelay( + new TimetableSnapshotFlush(siriTimetableSnapshotSource, gtfsTimetableSnapshotSource), + 0, + updatersParameters.timetableSnapshotParameters().maxSnapshotFrequency().toSeconds(), + TimeUnit.SECONDS + ); + } + } } diff --git a/src/main/java/org/opentripplanner/updater/spi/TimetableSnapshotFlush.java b/src/main/java/org/opentripplanner/updater/spi/TimetableSnapshotFlush.java new file mode 100644 index 00000000000..8c9f43e987e --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/spi/TimetableSnapshotFlush.java @@ -0,0 +1,37 @@ +package org.opentripplanner.updater.spi; + +import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource; +import org.opentripplanner.updater.trip.TimetableSnapshotSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Flush the timetable snapshot buffer by committing pending changes. + */ +public class TimetableSnapshotFlush implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(TimetableSnapshotFlush.class); + + private final SiriTimetableSnapshotSource siriTimetableSnapshotSource; + private final TimetableSnapshotSource gtfsTimetableSnapshotSource; + + public TimetableSnapshotFlush( + SiriTimetableSnapshotSource siriTimetableSnapshotSource, + TimetableSnapshotSource gtfsTimetableSnapshotSource + ) { + this.siriTimetableSnapshotSource = siriTimetableSnapshotSource; + this.gtfsTimetableSnapshotSource = gtfsTimetableSnapshotSource; + } + + @Override + public void run() { + LOG.debug("Flushing timetable snapshot buffer"); + if (siriTimetableSnapshotSource != null) { + siriTimetableSnapshotSource.flushBuffer(); + } + if (gtfsTimetableSnapshotSource != null) { + gtfsTimetableSnapshotSource.flushBuffer(); + } + LOG.debug("Flushed timetable snapshot buffer"); + } +} diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index ac03ab62e60..bbbda0d9e4a 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -1097,4 +1097,8 @@ private enum CancelationType { CANCEL, DELETE, } + + public void flushBuffer() { + snapshotManager.purgeAndCommit(); + } } From 104ee668294cc2b4289a78cf48ce4c0e37f2c2c5 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 19 Jul 2024 10:17:00 +0200 Subject: [PATCH 157/192] PUblish timetable snapshot with ConcurrentPublished --- .../updater/trip/TimetableSnapshotManager.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java index e435623ce85..d0b4044433d 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java @@ -8,6 +8,7 @@ import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; +import org.opentripplanner.routing.util.ConcurrentPublished; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.TripPattern; @@ -38,9 +39,9 @@ public final class TimetableSnapshotManager { /** * The last committed snapshot that was handed off to a routing thread. This snapshot may be given - * to more than one routing thread if the maximum snapshot frequency is exceeded. + * to more than one routing thread. */ - private volatile TimetableSnapshot snapshot = null; + private final ConcurrentPublished snapshot = new ConcurrentPublished<>(); /** * If a timetable snapshot is requested less than this number of milliseconds after the previous @@ -87,7 +88,7 @@ public TimetableSnapshotManager( * to the snapshot to release resources. */ public TimetableSnapshot getTimetableSnapshot() { - return snapshot; + return snapshot.get(); } public TimetableSnapshot getTimetableSnapshotBuffer() { @@ -106,7 +107,7 @@ void commitTimetableSnapshot(final boolean force) { if (force || snapshotFrequencyThrottle.timeIsUp()) { if (force || buffer.isDirty()) { LOG.debug("Committing {}", buffer); - snapshot = buffer.commit(transitLayerUpdater, force); + snapshot.publish(buffer.commit(transitLayerUpdater, force)); // We only reset the timer when the snapshot is updated. This will cause the first // update to be committed after a silent period. This should not have any effect in From 0f7737441e167d82cc14b553f7ce1c43d134a886 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Fri, 19 Jul 2024 11:33:42 +0200 Subject: [PATCH 158/192] Commit snapshot only from the TimetableSnapshotFlush periodic task. --- .../ext/siri/SiriTimetableSnapshotSource.java | 5 ++-- .../trip/TimetableSnapshotManager.java | 26 +++---------------- .../updater/trip/TimetableSnapshotSource.java | 2 -- .../java/org/opentripplanner/GtfsTest.java | 1 + .../updater/trip/RealtimeTestEnvironment.java | 15 ++++++++++- .../trip/TimetableSnapshotManagerTest.java | 15 ++++------- .../trip/TimetableSnapshotSourceTest.java | 23 +--------------- 7 files changed, 28 insertions(+), 59 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index 4237fa318d7..0f92264d440 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -119,8 +119,6 @@ public UpdateResult applyEstimatedTimetable( LOG.debug("message contains {} trip updates", updates.size()); - snapshotManager.purgeAndCommit(); - return UpdateResult.ofResults(results); } @@ -328,6 +326,9 @@ private boolean markScheduledTripAsDeleted(Trip trip, final LocalDate serviceDat return success; } + /** + * Flush pending changes in the timetable snapshot buffer and publish a new snapshot. + */ public void flushBuffer() { snapshotManager.purgeAndCommit(); } diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java index d0b4044433d..37e741de598 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java @@ -4,7 +4,6 @@ import java.util.Objects; import java.util.function.Supplier; import javax.annotation.Nullable; -import org.opentripplanner.framework.time.CountdownTimer; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; @@ -43,13 +42,6 @@ public final class TimetableSnapshotManager { */ private final ConcurrentPublished snapshot = new ConcurrentPublished<>(); - /** - * If a timetable snapshot is requested less than this number of milliseconds after the previous - * snapshot, just return the same one. Throttles the potentially resource-consuming task of - * duplicating a TripPattern -> Timetable map and indexing the new Timetables. - */ - private final CountdownTimer snapshotFrequencyThrottle; - /** * Should expired real-time data be purged from the graph. * TODO RT_AB: Clarify exactly what "purge" means and in what circumstances would one turn it off. @@ -74,7 +66,6 @@ public TimetableSnapshotManager( Supplier localDateNow ) { this.transitLayerUpdater = transitLayerUpdater; - this.snapshotFrequencyThrottle = new CountdownTimer(parameters.maxSnapshotFrequency()); this.purgeExpiredData = parameters.purgeExpiredData(); this.localDateNow = Objects.requireNonNull(localDateNow); // Force commit so that snapshot initializes @@ -104,20 +95,11 @@ public TimetableSnapshot getTimetableSnapshotBuffer() { * @param force Force the committing of a new snapshot even if the above conditions are not met. */ void commitTimetableSnapshot(final boolean force) { - if (force || snapshotFrequencyThrottle.timeIsUp()) { - if (force || buffer.isDirty()) { - LOG.debug("Committing {}", buffer); - snapshot.publish(buffer.commit(transitLayerUpdater, force)); - - // We only reset the timer when the snapshot is updated. This will cause the first - // update to be committed after a silent period. This should not have any effect in - // a busy updater. It is however useful when manually testing the updater. - snapshotFrequencyThrottle.restart(); - } else { - LOG.debug("Buffer was unchanged, keeping old snapshot."); - } + if (force || buffer.isDirty()) { + LOG.debug("Committing {}", buffer); + snapshot.publish(buffer.commit(transitLayerUpdater, force)); } else { - LOG.debug("Snapshot frequency exceeded. Reusing snapshot {}", snapshot); + LOG.debug("Buffer was unchanged, keeping old snapshot."); } } diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index bbbda0d9e4a..baae30dfffa 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -269,8 +269,6 @@ public UpdateResult applyTripUpdates( } } - snapshotManager.purgeAndCommit(); - var updateResult = UpdateResult.ofResults(results); if (updateIncrementality == FULL_DATASET) { diff --git a/src/test/java/org/opentripplanner/GtfsTest.java b/src/test/java/org/opentripplanner/GtfsTest.java index baf12225c9a..074e31147a3 100644 --- a/src/test/java/org/opentripplanner/GtfsTest.java +++ b/src/test/java/org/opentripplanner/GtfsTest.java @@ -233,6 +233,7 @@ protected void setUp() throws Exception { updates, feedId.getId() ); + timetableSnapshotSource.flushBuffer(); alertsUpdateHandler.update(feedMessage); } catch (Exception exception) {} } diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index 6adce735fb0..3e053d12d45 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -221,6 +221,7 @@ public TripPattern getPatternForTrip(Trip trip) { } public TimetableSnapshot getTimetableSnapshot() { + commitTimetableSnapshot(); if (siriSource != null) { return siriSource.getTimetableSnapshot(); } else { @@ -300,7 +301,19 @@ private UpdateResult applyEstimatedTimetable( boolean fuzzyMatching ) { Objects.requireNonNull(siriSource, "Test environment is configured for GTFS-RT only"); - return getEstimatedTimetableHandler(fuzzyMatching).applyUpdate(updates, DIFFERENTIAL); + UpdateResult updateResult = getEstimatedTimetableHandler(fuzzyMatching) + .applyUpdate(updates, DIFFERENTIAL); + commitTimetableSnapshot(); + return updateResult; + } + + private void commitTimetableSnapshot() { + if (siriSource != null) { + siriSource.flushBuffer(); + } + if (gtfsSource != null) { + gtfsSource.flushBuffer(); + } } private Trip createTrip(String id, Route route, List stops) { diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotManagerTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotManagerTest.java index 8f5c278b586..384e8a41fd1 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotManagerTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotManagerTest.java @@ -63,19 +63,16 @@ SameAssert not() { static Stream purgeExpiredDataTestCases() { return Stream.of( - // purgeExpiredData maxSnapshotFrequency || snapshots PatternSnapshotA PatternSnapshotB - Arguments.of(Boolean.TRUE, -1, NotSame, NotSame), - Arguments.of(Boolean.FALSE, -1, NotSame, Same), - Arguments.of(Boolean.TRUE, 1000, NotSame, NotSame), - Arguments.of(Boolean.FALSE, 1000, Same, Same) + // purgeExpiredData || snapshots PatternSnapshotA PatternSnapshotB + Arguments.of(Boolean.TRUE, NotSame, NotSame), + Arguments.of(Boolean.FALSE, NotSame, Same) ); } - @ParameterizedTest(name = "purgeExpired: {0}, maxFrequency: {1} || {2} {3}") + @ParameterizedTest(name = "purgeExpired: {0} || {1} {2}") @MethodSource("purgeExpiredDataTestCases") public void testPurgeExpiredData( boolean purgeExpiredData, - int maxSnapshotFrequency, SameAssert expSnapshots, SameAssert expPatternAeqB ) { @@ -85,9 +82,7 @@ public void testPurgeExpiredData( var snapshotManager = new TimetableSnapshotManager( null, - TimetableSnapshotSourceParameters.DEFAULT - .withPurgeExpiredData(purgeExpiredData) - .withMaxSnapshotFrequency(Duration.ofMillis(maxSnapshotFrequency)), + TimetableSnapshotSourceParameters.DEFAULT.withPurgeExpiredData(purgeExpiredData), clock::get ); diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java index 84b53c95f9c..e53ba07cbcf 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java @@ -82,28 +82,6 @@ public void testGetSnapshot() { assertSame(snapshot, updater.getTimetableSnapshot()); } - @Test - public void testGetSnapshotWithMaxSnapshotFrequencyCleared() { - var updater = new TimetableSnapshotSource( - TimetableSnapshotSourceParameters.DEFAULT.withMaxSnapshotFrequency(Duration.ofMillis(-1)), - transitModel - ); - - final TimetableSnapshot snapshot = updater.getTimetableSnapshot(); - - updater.applyTripUpdates( - TRIP_MATCHER_NOOP, - REQUIRED_NO_DATA, - DIFFERENTIAL, - List.of(CANCELLATION), - feedId - ); - - final TimetableSnapshot newSnapshot = updater.getTimetableSnapshot(); - assertNotNull(newSnapshot); - assertNotSame(snapshot, newSnapshot); - } - @Test public void testHandleModifiedTrip() { // GIVEN @@ -221,6 +199,7 @@ public void testHandleModifiedTrip() { List.of(tripUpdate), feedId ); + updater.flushBuffer(); // THEN final TimetableSnapshot snapshot = updater.getTimetableSnapshot(); From 850a2be3ad01c4c0a712e7916313545809738a38 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 23 Jul 2024 12:04:34 +0200 Subject: [PATCH 159/192] Document transit editor service in TimetableSnapshotSource and SiriTimetableSnapshotSource --- .../ext/siri/SiriTimetableSnapshotSource.java | 20 +++++--- .../updater/trip/TimetableSnapshotSource.java | 46 +++++++++++-------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index 0f92264d440..9e2dfaff76b 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -57,7 +57,12 @@ public class SiriTimetableSnapshotSource implements TimetableSnapshotProvider { */ private final SiriTripPatternCache tripPatternCache; - private final TransitEditorService transitService; + /** + * Long-lived transit editor service that has access to the timetable snapshot buffer. + * This differs from the usual use case where the transit service refers to the latest published + * timetable snapshot. + */ + private final TransitEditorService transitEditorService; private final TimetableSnapshotManager snapshotManager; @@ -71,9 +76,10 @@ public SiriTimetableSnapshotSource( parameters, () -> LocalDate.now(transitModel.getTimeZone()) ); - this.transitService = new DefaultTransitService(transitModel, getTimetableSnapshotBuffer()); + this.transitEditorService = + new DefaultTransitService(transitModel, getTimetableSnapshotBuffer()); this.tripPatternCache = - new SiriTripPatternCache(tripPatternIdGenerator, transitService::getPatternForTrip); + new SiriTripPatternCache(tripPatternIdGenerator, transitEditorService::getPatternForTrip); transitModel.initTimetableSnapshotProvider(this); } @@ -112,7 +118,7 @@ public UpdateResult applyEstimatedTimetable( var journeys = estimatedJourneyVersion.getEstimatedVehicleJourneies(); LOG.debug("Handling {} EstimatedVehicleJourneys.", journeys.size()); for (EstimatedVehicleJourney journey : journeys) { - results.add(apply(journey, transitService, fuzzyTripMatcher, entityResolver)); + results.add(apply(journey, transitEditorService, fuzzyTripMatcher, entityResolver)); } } } @@ -224,7 +230,7 @@ private Result handleModifiedTrip( if (trip != null) { // Found exact match - pattern = transitService.getPatternForTrip(trip); + pattern = transitEditorService.getPatternForTrip(trip); } else if (fuzzyTripMatcher != null) { // No exact match found - search for trips based on arrival-times/stop-patterns TripAndPattern tripAndPattern = fuzzyTripMatcher.match( @@ -259,7 +265,7 @@ private Result handleModifiedTrip( pattern, estimatedVehicleJourney, serviceDate, - transitService.getTimeZone(), + transitEditorService.getTimeZone(), entityResolver ) .build(); @@ -306,7 +312,7 @@ private Result addTripToGraphAndBuffer(TripUpdate tr private boolean markScheduledTripAsDeleted(Trip trip, final LocalDate serviceDate) { boolean success = false; - final TripPattern pattern = transitService.getPatternForTrip(trip); + final TripPattern pattern = transitEditorService.getPatternForTrip(trip); if (pattern != null) { // Mark scheduled trip times for this trip in this pattern as deleted diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index baae30dfffa..56e3c380b67 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -91,7 +91,13 @@ public class TimetableSnapshotSource implements TimetableSnapshotProvider { private final TripPatternCache tripPatternCache = new TripPatternCache(); private final ZoneId timeZone; - private final TransitEditorService transitService; + + /** + * Long-lived transit editor service that has access to the timetable snapshot buffer. + * This differs from the usual use case where the transit service refers to the latest published + * timetable snapshot. + */ + private final TransitEditorService transitEditorService; private final Deduplicator deduplicator; @@ -119,7 +125,7 @@ public TimetableSnapshotSource( this.snapshotManager = new TimetableSnapshotManager(transitModel.getTransitLayerUpdater(), parameters, localDateNow); this.timeZone = transitModel.getTimeZone(); - this.transitService = + this.transitEditorService = new DefaultTransitService(transitModel, snapshotManager.getTimetableSnapshotBuffer()); this.deduplicator = transitModel.getDeduplicator(); this.serviceCodes = transitModel.getServiceCodes(); @@ -365,8 +371,8 @@ private Result handleScheduledTrip( return UpdateError.result(tripId, NO_UPDATES); } - final FeedScopedId serviceId = transitService.getTripForId(tripId).getServiceId(); - final Set serviceDates = transitService + final FeedScopedId serviceId = transitEditorService.getTripForId(tripId).getServiceId(); + final Set serviceDates = transitEditorService .getCalendarService() .getServiceDatesForServiceId(serviceId); if (!serviceDates.contains(serviceDate)) { @@ -409,7 +415,7 @@ private Result handleScheduledTrip( .cancelStops(skippedStopIndices) .build(); - final Trip trip = transitService.getTripForId(tripId); + final Trip trip = transitEditorService.getTripForId(tripId); // Get cached trip pattern or create one if it doesn't exist yet final TripPattern newPattern = tripPatternCache.getOrCreateTripPattern( newStopPattern, @@ -447,7 +453,7 @@ private Result validateAndHandleAddedTrip( // // Check whether trip id already exists in graph - final Trip trip = transitService.getTripForId(tripId); + final Trip trip = transitEditorService.getTripForId(tripId); if (trip != null) { // TODO: should we support this and add a new instantiation of this trip (making it @@ -501,7 +507,7 @@ private List removeUnknownStops(TripUpdate tripUpdate, FeedScope .filter(StopTimeUpdate::hasStopId) .filter(st -> { var stopId = new FeedScopedId(tripId.getFeedId(), st.getStopId()); - var stopFound = transitService.getRegularStop(stopId) != null; + var stopFound = transitEditorService.getRegularStop(stopId) != null; if (!stopFound) { debug(tripId, "Stop '{}' not found in graph. Removing from ADDED trip.", st.getStopId()); } @@ -550,7 +556,7 @@ private List checkNewStopTimeUpdatesAndFindStops( // Find stops if (stopTimeUpdate.hasStopId()) { // Find stop - final var stop = transitService.getRegularStop( + final var stop = transitEditorService.getRegularStop( new FeedScopedId(tripId.getFeedId(), stopTimeUpdate.getStopId()) ); if (stop != null) { @@ -633,7 +639,7 @@ private Result handleAddedTrip( tripBuilder.withRoute(route); // Find service ID running on this service date - final Set serviceIds = transitService + final Set serviceIds = transitEditorService .getCalendarService() .getServiceIdsOnDate(serviceDate); if (serviceIds.isEmpty()) { @@ -661,7 +667,7 @@ private Result handleAddedTrip( private Route getOrCreateRoute(TripDescriptor tripDescriptor, FeedScopedId tripId) { if (routeExists(tripId.getFeedId(), tripDescriptor)) { // Try to find route - return transitService.getRouteForId( + return transitEditorService.getRouteForId( new FeedScopedId(tripId.getFeedId(), tripDescriptor.getRouteId()) ); } @@ -676,7 +682,7 @@ else if ( var addedRouteExtension = AddedRoute.ofTripDescriptor(tripDescriptor); - var agency = transitService + var agency = transitEditorService .findAgencyById(new FeedScopedId(tripId.getFeedId(), addedRouteExtension.agencyId())) .orElseGet(() -> fallbackAgency(tripId.getFeedId())); @@ -692,7 +698,7 @@ else if ( builder.withUrl(addedRouteExtension.routeUrl()); var route = builder.build(); - transitService.addRoutes(route); + transitEditorService.addRoutes(route); return route; } // no information about the rout is given, so we create a dummy one @@ -708,7 +714,7 @@ else if ( I18NString longName = NonLocalizedString.ofNullable(tripDescriptor.getTripId()); builder.withLongName(longName); var route = builder.build(); - transitService.addRoutes(route); + transitEditorService.addRoutes(route); return route; } } @@ -720,14 +726,14 @@ private Agency fallbackAgency(String feedId) { return Agency .of(new FeedScopedId(feedId, "autogenerated-gtfs-rt-added-route")) .withName("Agency automatically added by GTFS-RT update") - .withTimezone(transitService.getTimeZone().toString()) + .withTimezone(transitEditorService.getTimeZone().toString()) .build(); } private boolean routeExists(String feedId, TripDescriptor tripDescriptor) { if (tripDescriptor.hasRouteId() && StringUtils.hasValue(tripDescriptor.getRouteId())) { var routeId = new FeedScopedId(feedId, tripDescriptor.getRouteId()); - return Objects.nonNull(transitService.getRouteForId(routeId)); + return Objects.nonNull(transitEditorService.getRouteForId(routeId)); } else { return false; } @@ -816,7 +822,7 @@ private Result addTripToGraphAndBuffer( // Create StopPattern final StopPattern stopPattern = new StopPattern(stopTimes); - final TripPattern originalTripPattern = transitService.getPatternForTrip(trip); + final TripPattern originalTripPattern = transitEditorService.getPatternForTrip(trip); // Get cached trip pattern or create one if it doesn't exist yet final TripPattern pattern = tripPatternCache.getOrCreateTripPattern( stopPattern, @@ -962,7 +968,7 @@ private Result validateAndHandleModifiedTrip( // // Check whether trip id already exists in graph - Trip trip = transitService.getTripForId(tripId); + Trip trip = transitEditorService.getTripForId(tripId); if (trip == null) { // TODO: should we support this and consider it an ADDED trip? @@ -977,7 +983,7 @@ private Result validateAndHandleModifiedTrip( return UpdateError.result(tripId, NO_START_DATE); } else { // Check whether service date is served by trip - final Set serviceIds = transitService + final Set serviceIds = transitEditorService .getCalendarService() .getServiceIdsOnDate(serviceDate); if (!serviceIds.contains(trip.getServiceId())) { @@ -1078,8 +1084,8 @@ private Result handleCanceledTrip( * @return trip pattern or null if no trip pattern was found */ private TripPattern getPatternForTripId(FeedScopedId tripId) { - Trip trip = transitService.getTripForId(tripId); - return transitService.getPatternForTrip(trip); + Trip trip = transitEditorService.getTripForId(tripId); + return transitEditorService.getPatternForTrip(trip); } private static void debug(FeedScopedId id, String message, Object... params) { From 7fe6999178a89e70dc0d40ceb7030199d9589998 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Tue, 23 Jul 2024 12:16:13 +0200 Subject: [PATCH 160/192] Ignore exceptions in TimetableSnapshotFlush --- .../updater/spi/TimetableSnapshotFlush.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/opentripplanner/updater/spi/TimetableSnapshotFlush.java b/src/main/java/org/opentripplanner/updater/spi/TimetableSnapshotFlush.java index 8c9f43e987e..3f5c8f4d23d 100644 --- a/src/main/java/org/opentripplanner/updater/spi/TimetableSnapshotFlush.java +++ b/src/main/java/org/opentripplanner/updater/spi/TimetableSnapshotFlush.java @@ -7,6 +7,8 @@ /** * Flush the timetable snapshot buffer by committing pending changes. + * Exceptions occurring during the flush are caught and ignored: the scheduler can then retry + * the task later. */ public class TimetableSnapshotFlush implements Runnable { @@ -25,13 +27,17 @@ public TimetableSnapshotFlush( @Override public void run() { - LOG.debug("Flushing timetable snapshot buffer"); - if (siriTimetableSnapshotSource != null) { - siriTimetableSnapshotSource.flushBuffer(); + try { + LOG.debug("Flushing timetable snapshot buffer"); + if (siriTimetableSnapshotSource != null) { + siriTimetableSnapshotSource.flushBuffer(); + } + if (gtfsTimetableSnapshotSource != null) { + gtfsTimetableSnapshotSource.flushBuffer(); + } + LOG.debug("Flushed timetable snapshot buffer"); + } catch (Throwable t) { + LOG.error("Error flushing timetable snapshot buffer", t); } - if (gtfsTimetableSnapshotSource != null) { - gtfsTimetableSnapshotSource.flushBuffer(); - } - LOG.debug("Flushed timetable snapshot buffer"); } } From d1d7f4304826b15467344fca6f82747a4b57cf06 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Wed, 24 Jul 2024 10:38:45 +0200 Subject: [PATCH 161/192] Commit snapshot while applying trip updates in RealtimeTestEnvironment --- .../updater/trip/RealtimeTestEnvironment.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index 3e053d12d45..bf6f743eac7 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -221,7 +221,6 @@ public TripPattern getPatternForTrip(Trip trip) { } public TimetableSnapshot getTimetableSnapshot() { - commitTimetableSnapshot(); if (siriSource != null) { return siriSource.getTimetableSnapshot(); } else { @@ -285,13 +284,15 @@ public UpdateResult applyTripUpdates( UpdateIncrementality incrementality ) { Objects.requireNonNull(gtfsSource, "Test environment is configured for SIRI only"); - return gtfsSource.applyTripUpdates( + UpdateResult updateResult = gtfsSource.applyTripUpdates( null, BackwardsDelayPropagationType.REQUIRED_NO_DATA, incrementality, updates, getFeedId() ); + commitTimetableSnapshot(); + return updateResult; } // private methods From 7f5bc33e283971aab856fcde8cf8ee07c2f4f9cd Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 19 Jul 2024 15:25:40 +0200 Subject: [PATCH 162/192] Add parsing of 'any' --- .../opentripplanner/netex/support/NetexVersionHelper.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java b/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java index 4e6cf4967c0..448d73a5f75 100644 --- a/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java +++ b/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java @@ -26,7 +26,11 @@ private NetexVersionHelper() {} * increasing integer. A bigger value indicate a later version. */ public static int versionOf(EntityInVersionStructure e) { - return Integer.parseInt(e.getVersion()); + if(e.getVersion().equals("any")){ + return 1; + } else{ + return Integer.parseInt(e.getVersion()); + } } /** From 85b9ec04de722407a55e1380748ed3ae7ad4c549 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 23 Jul 2024 19:53:53 +0200 Subject: [PATCH 163/192] Implement NeTEx version 'any' --- .../netex/support/NetexVersionHelper.java | 13 ++++++++----- .../netex/support/NetexVersionHelperTest.java | 19 +++++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java b/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java index 448d73a5f75..6ede322dfba 100644 --- a/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java +++ b/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java @@ -16,6 +16,9 @@ */ public class NetexVersionHelper { + private static final String ANY = "any"; + private static final int UNKNOWN_VERSION = -1; + /** * private constructor to prevent instantiation of utility class */ @@ -23,12 +26,12 @@ private NetexVersionHelper() {} /** * According to the Norwegian Netex profile the version number must be a positive - * increasing integer. A bigger value indicate a later version. + * increasing integer. A bigger value indicates a later version. */ public static int versionOf(EntityInVersionStructure e) { - if(e.getVersion().equals("any")){ - return 1; - } else{ + if (e.getVersion().equals(ANY)) { + return UNKNOWN_VERSION; + } else { return Integer.parseInt(e.getVersion()); } } @@ -38,7 +41,7 @@ public static int versionOf(EntityInVersionStructure e) { * elements exist in the collection {@code -1} is returned. */ public static int latestVersionIn(Collection list) { - return list.stream().mapToInt(NetexVersionHelper::versionOf).max().orElse(-1); + return list.stream().mapToInt(NetexVersionHelper::versionOf).max().orElse(UNKNOWN_VERSION); } /** diff --git a/src/test/java/org/opentripplanner/netex/support/NetexVersionHelperTest.java b/src/test/java/org/opentripplanner/netex/support/NetexVersionHelperTest.java index d55b573a5d5..1374d871154 100644 --- a/src/test/java/org/opentripplanner/netex/support/NetexVersionHelperTest.java +++ b/src/test/java/org/opentripplanner/netex/support/NetexVersionHelperTest.java @@ -19,32 +19,39 @@ import org.rutebanken.netex.model.EntityInVersionStructure; import org.rutebanken.netex.model.ValidBetween; -public class NetexVersionHelperTest { +class NetexVersionHelperTest { private static final EntityInVersionStructure E_VER_1 = new EntityInVersionStructure() .withVersion("1"); private static final EntityInVersionStructure E_VER_2 = new EntityInVersionStructure() .withVersion("2"); + private static final EntityInVersionStructure E_VER_ANY = new EntityInVersionStructure() + .withVersion("any"); @Test - public void versionOfTest() { + void versionOfTest() { assertEquals(1, versionOf(E_VER_1)); } @Test - public void latestVersionInTest() { + void any() { + assertEquals(-1, versionOf(E_VER_ANY)); + } + + @Test + void latestVersionInTest() { assertEquals(2, latestVersionIn(Arrays.asList(E_VER_1, E_VER_2))); assertEquals(-1, latestVersionIn(Collections.emptyList())); } @Test - public void lastestVersionedElementInTest() { + void lastestVersionedElementInTest() { assertEquals(E_VER_2, latestVersionedElementIn(Arrays.asList(E_VER_1, E_VER_2))); assertNull(latestVersionedElementIn(Collections.emptyList())); } @Test - public void comparingVersionTest() { + void comparingVersionTest() { // Given a comparator (subject under test) Comparator subject = comparingVersion(); // And a entity with version as the E_VER_1 entity @@ -62,7 +69,7 @@ public void comparingVersionTest() { } @Test - public void testFirstRelevantDateTime() { + void testFirstRelevantDateTime() { var may1st = LocalDateTime.of(2021, MAY, 1, 14, 0); var may2nd = LocalDateTime.of(2021, MAY, 2, 14, 0); var may3rd = LocalDateTime.of(2021, MAY, 3, 14, 0); From d71fcc11c07a137adc1a41401b608bb6f14a251b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 23 Jul 2024 20:25:20 +0200 Subject: [PATCH 164/192] Add documentation --- .../netex/support/NetexVersionHelper.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java b/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java index 6ede322dfba..8f7a7f14913 100644 --- a/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java +++ b/src/main/java/org/opentripplanner/netex/support/NetexVersionHelper.java @@ -16,7 +16,13 @@ */ public class NetexVersionHelper { + /** + * @see NetexVersionHelper#versionOf(EntityInVersionStructure) + */ private static final String ANY = "any"; + /** + * A special value that represents an unknown version. + */ private static final int UNKNOWN_VERSION = -1; /** @@ -27,6 +33,11 @@ private NetexVersionHelper() {} /** * According to the Norwegian Netex profile the version number must be a positive * increasing integer. A bigger value indicates a later version. + * However, the special value "any" is also supported and returns a constant meaning "unknown". + * The EPIP profile at + * http://netex.uk/netex/doc/2019.05.07-v1.1_FinalDraft/prCEN_TS_16614-PI_Profile_FV_%28E%29-2019-Final-Draft-v3.pdf (page 33) + * defines this as follows: "Use "any" if the VERSION is unknown (note that this will trigger NeTEx's + * XML automatic consistency check)." */ public static int versionOf(EntityInVersionStructure e) { if (e.getVersion().equals(ANY)) { From cd8e29eaaf94915be9588190b4e836e9aad48562 Mon Sep 17 00:00:00 2001 From: Vincent Paturet Date: Wed, 24 Jul 2024 15:46:28 +0200 Subject: [PATCH 165/192] Added Javadoc --- .../updater/trip/TimetableSnapshotManager.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java index 37e741de598..b5683a9a62e 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java @@ -82,6 +82,12 @@ public TimetableSnapshot getTimetableSnapshot() { return snapshot.get(); } + /** + * @return the current timetable snapshot buffer that contains pending changes (not yet published + * in a snapshot). + * This should be used in the context of an updater to build a TransitEditorService that sees all + * the changes applied so far by real-time updates. + */ public TimetableSnapshot getTimetableSnapshotBuffer() { return buffer; } From ae363f416f5ff830e5e7b0bec9a97e837a5465a6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 24 Jul 2024 16:31:44 +0200 Subject: [PATCH 166/192] Update src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java Co-authored-by: Thomas Gran --- .../org/opentripplanner/apis/gtfs/model/LocalDateRange.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java b/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java index d466534e65c..18a1a1624e3 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java @@ -27,9 +27,9 @@ public boolean startBeforeEnd() { public boolean contains(LocalDate date) { return ( ( - startInclusive() == null || date.isEqual(startInclusive()) || date.isAfter(startInclusive()) + startInclusive == null || date.isEqual(startInclusive) || date.isAfter(startInclusive) ) && - (endExclusive() == null || date.isBefore(endExclusive())) + (endExclusive == null || date.isBefore(endExclusive)) ); } } From 1ebfc42d98e6ef99bb149efdf0c5e9ffe6305dab Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 24 Jul 2024 19:41:26 +0200 Subject: [PATCH 167/192] Format code --- .../org/opentripplanner/apis/gtfs/model/LocalDateRange.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java b/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java index 18a1a1624e3..dfecfdcd960 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/model/LocalDateRange.java @@ -26,9 +26,7 @@ public boolean startBeforeEnd() { */ public boolean contains(LocalDate date) { return ( - ( - startInclusive == null || date.isEqual(startInclusive) || date.isAfter(startInclusive) - ) && + (startInclusive == null || date.isEqual(startInclusive) || date.isAfter(startInclusive)) && (endExclusive == null || date.isBefore(endExclusive)) ); } From 1a8ccb678b5d34862fffda39a172ec5b7ffc53bb Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 25 Jul 2024 05:46:51 +0000 Subject: [PATCH 168/192] Upgrade debug client to version 2024/07/2024-07-25T05:46 --- src/client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/index.html b/src/client/index.html index d406984f024..befb031cbdb 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +

From 73e03e95a45c3f5166cf7351066481b64f81005d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 05:46:54 +0000 Subject: [PATCH 169/192] chore(deps): update debug ui dependencies (non-major) --- client/package-lock.json | 309 +++++++++++++++++++-------------------- client/package.json | 20 +-- 2 files changed, 157 insertions(+), 172 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index a5c2422df3f..dc6c6dfa1a3 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14,33 +14,33 @@ "graphql-request": "7.1.0", "maplibre-gl": "4.5.0", "react": "18.3.1", - "react-bootstrap": "2.10.3", + "react-bootstrap": "2.10.4", "react-dom": "18.3.1", "react-map-gl": "7.1.7" }, "devDependencies": { "@graphql-codegen/cli": "5.0.2", - "@graphql-codegen/client-preset": "4.3.1", + "@graphql-codegen/client-preset": "4.3.2", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", "@testing-library/react": "16.0.0", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@typescript-eslint/eslint-plugin": "7.14.1", - "@typescript-eslint/parser": "7.14.1", + "@typescript-eslint/eslint-plugin": "7.17.0", + "@typescript-eslint/parser": "7.17.0", "@vitejs/plugin-react": "4.3.1", "@vitest/coverage-v8": "2.0.4", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-jsx-a11y": "6.9.0", - "eslint-plugin-react": "7.34.3", + "eslint-plugin-react": "7.35.0", "eslint-plugin-react-hooks": "4.6.2", - "eslint-plugin-react-refresh": "0.4.7", - "jsdom": "24.1.0", - "prettier": "3.3.2", - "typescript": "5.5.2", - "vite": "5.3.2", + "eslint-plugin-react-refresh": "0.4.9", + "jsdom": "24.1.1", + "prettier": "3.3.3", + "typescript": "5.5.4", + "vite": "5.3.4", "vitest": "2.0.4" } }, @@ -1861,21 +1861,21 @@ } }, "node_modules/@graphql-codegen/client-preset": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.3.1.tgz", - "integrity": "sha512-FHszBKhubbJkrZHwzUNfMUp9IkzufCfn/riTpIy5yA84Wq0AJSPFL7nWkG+h3azFPeznLfqo3KJmfzRb+xeFEA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.3.2.tgz", + "integrity": "sha512-42jHyG6u2uFDIVNvzue8zR529aPT16EYIJQmvMk8XuYHo3PneQVlWmQ3j2fBy+RuWCBzpJKPKm7IGSKiw19nmg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/template": "^7.20.7", "@graphql-codegen/add": "^5.0.3", - "@graphql-codegen/gql-tag-operations": "4.0.8", + "@graphql-codegen/gql-tag-operations": "4.0.9", "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-codegen/typed-document-node": "^5.0.8", - "@graphql-codegen/typescript": "^4.0.8", - "@graphql-codegen/typescript-operations": "^4.2.2", - "@graphql-codegen/visitor-plugin-common": "^5.3.0", + "@graphql-codegen/typed-document-node": "^5.0.9", + "@graphql-codegen/typescript": "^4.0.9", + "@graphql-codegen/typescript-operations": "^4.2.3", + "@graphql-codegen/visitor-plugin-common": "^5.3.1", "@graphql-tools/documents": "^1.0.0", "@graphql-tools/utils": "^10.0.0", "@graphql-typed-document-node/core": "3.2.0", @@ -1901,14 +1901,14 @@ } }, "node_modules/@graphql-codegen/gql-tag-operations": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.8.tgz", - "integrity": "sha512-slCICQOFbMfdL7mAZ6XUiOhcJl0yOKfqHFiULIlQJKpo8ey6NHsrtc8Q02ZF417BfTfZ/Qj7rmXhkc/dwY94ag==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.9.tgz", + "integrity": "sha512-lVgu1HClel896HqZAEjynatlU6eJrYOw+rh05DPgM150xvmb7Gz5TnRHA2vfwlDNIXDaToAIpz5RFfkjjnYM1Q==", "dev": true, "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-codegen/visitor-plugin-common": "5.3.0", + "@graphql-codegen/visitor-plugin-common": "5.3.1", "@graphql-tools/utils": "^10.0.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" @@ -1964,14 +1964,14 @@ } }, "node_modules/@graphql-codegen/typed-document-node": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.0.8.tgz", - "integrity": "sha512-ImJd1KwS0vYZiPVZzs8EOZ79V96zN0p1A1MJNpk/8CiJWpIi4FupLLfTMMYq5Rr0AZET+O/A+udw4LDjDrAWvg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.0.9.tgz", + "integrity": "sha512-Wx6fyA4vpfIbfNTMiWUECGnjqzKkJdEbZHxVMIegiCBPzBYPAJV4mZZcildLAfm2FtZcgW4YKtFoTbnbXqPB3w==", "dev": true, "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-codegen/visitor-plugin-common": "5.3.0", + "@graphql-codegen/visitor-plugin-common": "5.3.1", "auto-bind": "~4.0.0", "change-case-all": "1.0.15", "tslib": "~2.6.0" @@ -1981,15 +1981,15 @@ } }, "node_modules/@graphql-codegen/typescript": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.0.8.tgz", - "integrity": "sha512-kYS3SjGNnC9vgFS8N3vaxzRFkdXX2umMi1SOpHjMFCPjMe8NR0uNdW4nP9T0YEq+DvWgj+XojjpFy2oyz9q12w==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.0.9.tgz", + "integrity": "sha512-0O35DMR4d/ctuHL1Zo6mRUUzp0BoszKfeWsa6sCm/g70+S98+hEfTwZNDkQHylLxapiyjssF9uw/F+sXqejqLw==", "dev": true, "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.4", "@graphql-codegen/schema-ast": "^4.0.2", - "@graphql-codegen/visitor-plugin-common": "5.3.0", + "@graphql-codegen/visitor-plugin-common": "5.3.1", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, @@ -1998,15 +1998,15 @@ } }, "node_modules/@graphql-codegen/typescript-operations": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.2.2.tgz", - "integrity": "sha512-8FJHIAubM4r9ElLuuDAKhdOjainSwRHEmGIrtEgEwHARKhMk1Ttj6bpOQDisYlbDl4ZTHWEJCdNa9o9rgcl+9g==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.2.3.tgz", + "integrity": "sha512-6z7avSSOr03l5SyKbeDs7MzRyGwnQFSCqQm8Om5wIuoIgXVu2gXRmcJAY/I7SLdAy9xbF4Sho7XNqieFM2CAFQ==", "dev": true, "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-codegen/typescript": "^4.0.8", - "@graphql-codegen/visitor-plugin-common": "5.3.0", + "@graphql-codegen/typescript": "^4.0.9", + "@graphql-codegen/visitor-plugin-common": "5.3.1", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, @@ -2015,9 +2015,9 @@ } }, "node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.3.0.tgz", - "integrity": "sha512-+kUk7gRD/72Wfkjd7D96Lonh9k4lFw9d3O1+I07Jyja4QN9H42kdFEO0hM/b4Q9lLkI1yJ66Oym7lWz2Ikj3aw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.3.1.tgz", + "integrity": "sha512-MktoBdNZhSmugiDjmFl1z6rEUUaqyxtFJYWnDilE7onkPgyw//O0M+TuPBJPBWdyV6J2ond0Hdqtq+rkghgSIQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3943,17 +3943,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.14.1.tgz", - "integrity": "sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.17.0.tgz", + "integrity": "sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.14.1", - "@typescript-eslint/type-utils": "7.14.1", - "@typescript-eslint/utils": "7.14.1", - "@typescript-eslint/visitor-keys": "7.14.1", + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/type-utils": "7.17.0", + "@typescript-eslint/utils": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3977,16 +3977,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.14.1.tgz", - "integrity": "sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.17.0.tgz", + "integrity": "sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.14.1", - "@typescript-eslint/types": "7.14.1", - "@typescript-eslint/typescript-estree": "7.14.1", - "@typescript-eslint/visitor-keys": "7.14.1", + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/typescript-estree": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", "debug": "^4.3.4" }, "engines": { @@ -4006,14 +4006,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.14.1.tgz", - "integrity": "sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.17.0.tgz", + "integrity": "sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.14.1", - "@typescript-eslint/visitor-keys": "7.14.1" + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -4024,14 +4024,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.14.1.tgz", - "integrity": "sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.17.0.tgz", + "integrity": "sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.14.1", - "@typescript-eslint/utils": "7.14.1", + "@typescript-eslint/typescript-estree": "7.17.0", + "@typescript-eslint/utils": "7.17.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -4052,9 +4052,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.14.1.tgz", - "integrity": "sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.17.0.tgz", + "integrity": "sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==", "dev": true, "license": "MIT", "engines": { @@ -4066,14 +4066,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.14.1.tgz", - "integrity": "sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.17.0.tgz", + "integrity": "sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.14.1", - "@typescript-eslint/visitor-keys": "7.14.1", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4095,9 +4095,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "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": { @@ -4108,16 +4108,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.14.1.tgz", - "integrity": "sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.17.0.tgz", + "integrity": "sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.14.1", - "@typescript-eslint/types": "7.14.1", - "@typescript-eslint/typescript-estree": "7.14.1" + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/typescript-estree": "7.17.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -4131,13 +4131,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.14.1.tgz", - "integrity": "sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.17.0.tgz", + "integrity": "sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/types": "7.17.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -4578,18 +4578,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", @@ -6261,36 +6249,36 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz", - "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==", + "version": "7.35.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz", + "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==", "dev": true, "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "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.hasown": "^1.1.4", "object.values": "^1.2.0", "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.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react-hooks": { @@ -6306,9 +6294,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.7.tgz", - "integrity": "sha512-yrj+KInFmwuQS2UQcg1SF83ha1tuHC1jMQbRNyuWtlEzzKRDgAl7L4Yp4NlDUZTZNlWvHEzOtJhMi40R7JxcSw==", + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.9.tgz", + "integrity": "sha512-QK49YrBAo5CLNLseZ7sZgvgTy21E6NEw22eZqc4teZfH8pxV3yXc9XXOYfUI6JNpw7mfHNkAeWtBxrTyykB6HA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -7406,10 +7394,11 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -8244,9 +8233,9 @@ } }, "node_modules/jsdom": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.0.tgz", - "integrity": "sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA==", + "version": "24.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.1.tgz", + "integrity": "sha512-5O1wWV99Jhq4DV7rCLIoZ/UIhyQeDR7wHVyZAHAshbrvZsLs+Xzz7gtwnlJTJDjleiTKh54F4dXrX70vJQTyJQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8256,11 +8245,11 @@ "form-data": "^4.0.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.4", + "https-proxy-agent": "^7.0.5", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.10", + "nwsapi": "^2.2.12", "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.0", + "rrweb-cssom": "^0.7.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^4.1.4", @@ -8269,7 +8258,7 @@ "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0", - "ws": "^8.17.0", + "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "engines": { @@ -8285,9 +8274,9 @@ } }, "node_modules/jsdom/node_modules/rrweb-cssom": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.0.tgz", - "integrity": "sha512-KlSv0pm9kgQSRxXEMgtivPJ4h826YHsuob8pSHcfSZsSXGtvpEAie8S0AnXuObEJ7nhikOb4ahwxDm0H2yW17g==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", "dev": true, "license": "MIT" }, @@ -9052,9 +9041,9 @@ "dev": true }, "node_modules/nwsapi": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", - "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz", + "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==", "dev": true, "license": "MIT" }, @@ -9165,23 +9154,6 @@ "node": ">= 0.4" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.values": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", @@ -9535,10 +9507,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -9562,9 +9535,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.40", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", + "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==", "dev": true, "funding": [ { @@ -9580,9 +9553,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -9604,9 +9578,9 @@ } }, "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "license": "MIT", "bin": { @@ -9766,9 +9740,9 @@ } }, "node_modules/react-bootstrap": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.3.tgz", - "integrity": "sha512-cc1KAaQyj6Gr3AfA0eRRiUMSlRi3brDVcjc/o0E9y9XNW7ISo8TITrq8G8G3QTFe7VIhCiDt38k99AEFoLOolw==", + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.4.tgz", + "integrity": "sha512-W3398nBM2CBfmGP2evneEO3ZZwEMPtHs72q++eNw60uDGDAdiGn0f9yNys91eo7/y8CTF5Ke1C0QO8JFVPU40Q==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.24.7", @@ -10697,6 +10671,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -11181,9 +11166,9 @@ } }, "node_modules/typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "license": "Apache-2.0", "bin": { @@ -11407,14 +11392,14 @@ } }, "node_modules/vite": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.2.tgz", - "integrity": "sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.4.tgz", + "integrity": "sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.38", + "postcss": "^8.4.39", "rollup": "^4.13.0" }, "bin": { @@ -11823,9 +11808,9 @@ "dev": true }, "node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "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": { diff --git a/client/package.json b/client/package.json index 1681ed56d1d..8ce0ed0cbdb 100644 --- a/client/package.json +++ b/client/package.json @@ -23,33 +23,33 @@ "graphql-request": "7.1.0", "maplibre-gl": "4.5.0", "react": "18.3.1", - "react-bootstrap": "2.10.3", + "react-bootstrap": "2.10.4", "react-dom": "18.3.1", "react-map-gl": "7.1.7" }, "devDependencies": { "@graphql-codegen/cli": "5.0.2", - "@graphql-codegen/client-preset": "4.3.1", + "@graphql-codegen/client-preset": "4.3.2", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", "@testing-library/react": "16.0.0", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@typescript-eslint/eslint-plugin": "7.14.1", - "@typescript-eslint/parser": "7.14.1", + "@typescript-eslint/eslint-plugin": "7.17.0", + "@typescript-eslint/parser": "7.17.0", "@vitejs/plugin-react": "4.3.1", "@vitest/coverage-v8": "2.0.4", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-jsx-a11y": "6.9.0", - "eslint-plugin-react": "7.34.3", + "eslint-plugin-react": "7.35.0", "eslint-plugin-react-hooks": "4.6.2", - "eslint-plugin-react-refresh": "0.4.7", - "jsdom": "24.1.0", - "prettier": "3.3.2", - "typescript": "5.5.2", - "vite": "5.3.2", + "eslint-plugin-react-refresh": "0.4.9", + "jsdom": "24.1.1", + "prettier": "3.3.3", + "typescript": "5.5.4", + "vite": "5.3.4", "vitest": "2.0.4" } } From 09ced423d25d5f3d12b9ea9d060551d9f5acea51 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 25 Jul 2024 05:57:08 +0000 Subject: [PATCH 170/192] Upgrade debug client to version 2024/07/2024-07-25T05:56 --- src/client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/index.html b/src/client/index.html index befb031cbdb..863eb7b09a3 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
From 3ee1b463b4aa24c1f54c2315a81d534c9d9448e0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 25 Jul 2024 11:05:42 +0200 Subject: [PATCH 171/192] Return null --- client/src/components/ItineraryList/InterchangeInfo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/ItineraryList/InterchangeInfo.tsx b/client/src/components/ItineraryList/InterchangeInfo.tsx index 0dbd7368c0e..b095564d8da 100644 --- a/client/src/components/ItineraryList/InterchangeInfo.tsx +++ b/client/src/components/ItineraryList/InterchangeInfo.tsx @@ -17,6 +17,6 @@ export function InterchangeInfo({ leg }: { leg: Leg }) { /> ); } else { - return <>; + return null; } } From 80fedfa9275e046de3583bdc52077ccefb0cc963 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 25 Jul 2024 11:47:49 +0200 Subject: [PATCH 172/192] Make method private --- .../apis/gtfs/mapping/LocalDateRangeMapperTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java index 81b3b283151..ebf3a81d681 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java @@ -16,7 +16,7 @@ class LocalDateRangeMapperTest { private static final LocalDate DATE = LocalDate.parse("2024-05-27"); - public static List noFilterCases() { + private static List noFilterCases() { var list = new ArrayList(); list.add(null); list.add(new GraphQLTypes.GraphQLLocalDateRangeInput(Map.of())); From 3cf6130f82e3062ca25a280865fcc164fff15168 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 25 Jul 2024 12:07:45 +0200 Subject: [PATCH 173/192] Make method private --- .../apis/gtfs/mapping/LocalDateRangeMapperTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java b/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java index ebf3a81d681..cbd771df933 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/mapping/LocalDateRangeMapperTest.java @@ -29,7 +29,7 @@ void hasNoServiceDateFilter(GraphQLTypes.GraphQLLocalDateRangeInput input) { assertFalse(LocalDateRangeUtil.hasServiceDateFilter(input)); } - public static List> hasFilterCases() { + private static List> hasFilterCases() { return List.of(Map.of("start", DATE), Map.of("end", DATE), Map.of("start", DATE, "end", DATE)); } From 6eae01914321b774bb5511578cc0afefdf5463a6 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 25 Jul 2024 16:16:54 +0200 Subject: [PATCH 174/192] Ignore NeTEx parking by default --- docs/BuildConfiguration.md | 4 ++-- .../org/opentripplanner/netex/config/NetexFeedParameters.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index d6c6c87295d..6a6b42cd664 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -78,7 +78,7 @@ Sections follow that describe particular settings in more depth. |    [groupFilePattern](#nd_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | |    ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | |    [ignoreFilePattern](#nd_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | -|    ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `false` | 2.6 | +|    ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | |    noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | |    [sharedFilePattern](#nd_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | |    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | @@ -107,7 +107,7 @@ Sections follow that describe particular settings in more depth. |       [groupFilePattern](#tf_1_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | |       ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | |       [ignoreFilePattern](#tf_1_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | -|       ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `false` | 2.6 | +|       ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | |       noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | |       [sharedFilePattern](#tf_1_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | |       [sharedGroupFilePattern](#tf_1_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | diff --git a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java index f565ea5a187..0c6c75c4db3 100644 --- a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java +++ b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java @@ -31,7 +31,7 @@ public class NetexFeedParameters implements DataSourceConfig { private static final String SHARED_GROUP_FILE_PATTERN = "(\\w{3})-.*-shared\\.xml"; private static final String GROUP_FILE_PATTERN = "(\\w{3})-.*\\.xml"; private static final boolean NO_TRANSFERS_ON_ISOLATED_STOPS = false; - private static final Set IGNORED_FEATURES = Set.of(); + private static final Set IGNORED_FEATURES = Set.of(PARKING); private static final Set FERRY_IDS_NOT_ALLOWED_FOR_BICYCLE = Collections.emptySet(); From f21ea0f88641eab3a48c8f380c9ee40dc432d4b9 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 25 Jul 2024 14:20:15 +0000 Subject: [PATCH 175/192] Upgrade debug client to version 2024/07/2024-07-25T14:19 --- src/client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/index.html b/src/client/index.html index 863eb7b09a3..9ddee23eea6 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
From 984f199fe40d8d89410806fb8091fcf65ad63581 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 25 Jul 2024 14:20:31 +0000 Subject: [PATCH 176/192] Add changelog entry for #5977 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 1ae81820e72..5070247f39d 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -47,6 +47,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Generate documentation for OSM tag mappers [#5929](https://github.com/opentripplanner/OpenTripPlanner/pull/5929) - Disable Legacy REST API by default [#5948](https://github.com/opentripplanner/OpenTripPlanner/pull/5948) - Enforce non-null coordinates on multimodal station [#5971](https://github.com/opentripplanner/OpenTripPlanner/pull/5971) +- Add car rental to Transmodel street mode options [#5977](https://github.com/opentripplanner/OpenTripPlanner/pull/5977) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From ed6b165cf06a1e5ebdd90ddcdcfe8fc0709da664 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 25 Jul 2024 16:25:00 +0200 Subject: [PATCH 177/192] Update tests --- .../opentripplanner/netex/config/NetexFeedParametersTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/opentripplanner/netex/config/NetexFeedParametersTest.java b/src/test/java/org/opentripplanner/netex/config/NetexFeedParametersTest.java index d010e3a0d9e..93624afe339 100644 --- a/src/test/java/org/opentripplanner/netex/config/NetexFeedParametersTest.java +++ b/src/test/java/org/opentripplanner/netex/config/NetexFeedParametersTest.java @@ -89,7 +89,7 @@ void testCopyOfEqualsAndHashCode() { @Test void testToString() { - assertEquals("NetexFeedParameters{}", DEFAULT.toString()); + assertEquals("NetexFeedParameters{ignoredFeatures: [PARKING]}", DEFAULT.toString()); assertEquals( "NetexFeedParameters{" + "source: https://my.test.com, " + @@ -98,6 +98,7 @@ void testToString() { "sharedGroupFilePattern: '[sharedGoupFil]+', " + "groupFilePattern: '[groupFile]+', " + "ignoreFilePattern: '[ignoreFl]+', " + + "ignoredFeatures: [PARKING], " + "ferryIdsNotAllowedForBicycle: [Ferry:Id]" + "}", subject.toString() From ae6fbc835826bb35bad8940a61a0f81c53a4e5f5 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 25 Jul 2024 20:40:22 +0000 Subject: [PATCH 178/192] Upgrade debug client to version 2024/07/2024-07-25T20:39 --- src/client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/index.html b/src/client/index.html index 9ddee23eea6..767f8b1d45a 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
From 0afceddfb826ec84e9799b950665bceae1fd2cec Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 25 Jul 2024 20:40:38 +0000 Subject: [PATCH 179/192] Add changelog entry for #5962 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 5070247f39d..3c53c292aff 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -48,6 +48,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Disable Legacy REST API by default [#5948](https://github.com/opentripplanner/OpenTripPlanner/pull/5948) - Enforce non-null coordinates on multimodal station [#5971](https://github.com/opentripplanner/OpenTripPlanner/pull/5971) - Add car rental to Transmodel street mode options [#5977](https://github.com/opentripplanner/OpenTripPlanner/pull/5977) +- Add debug information for stop/quay ID and stay-seated transfers [#5962](https://github.com/opentripplanner/OpenTripPlanner/pull/5962) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From d6cf79d8c7fb9d7e56884d9e90d4973edd3a75f5 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Fri, 26 Jul 2024 07:25:12 +0000 Subject: [PATCH 180/192] Add changelog entry for #5983 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 3c53c292aff..e1e839a7bd5 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -49,6 +49,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Enforce non-null coordinates on multimodal station [#5971](https://github.com/opentripplanner/OpenTripPlanner/pull/5971) - Add car rental to Transmodel street mode options [#5977](https://github.com/opentripplanner/OpenTripPlanner/pull/5977) - Add debug information for stop/quay ID and stay-seated transfers [#5962](https://github.com/opentripplanner/OpenTripPlanner/pull/5962) +- Handle NeTEx `any` version [#5983](https://github.com/opentripplanner/OpenTripPlanner/pull/5983) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From 278732253780fdd8b449aef41b9c5871c5fc33a4 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Fri, 26 Jul 2024 09:58:43 +0000 Subject: [PATCH 181/192] Add changelog entry for #5919 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index e1e839a7bd5..1527e18c873 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -50,6 +50,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Add car rental to Transmodel street mode options [#5977](https://github.com/opentripplanner/OpenTripPlanner/pull/5977) - Add debug information for stop/quay ID and stay-seated transfers [#5962](https://github.com/opentripplanner/OpenTripPlanner/pull/5962) - Handle NeTEx `any` version [#5983](https://github.com/opentripplanner/OpenTripPlanner/pull/5983) +- Keep at least one result for min-transfers and each transit-group in itinerary-group-filter [#5919](https://github.com/opentripplanner/OpenTripPlanner/pull/5919) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From a3147304f73578f5eac4a409020331398731285c Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Mon, 29 Jul 2024 12:37:40 +0000 Subject: [PATCH 182/192] Add changelog entry for #5946 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 1527e18c873..d9650be1dcb 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -51,6 +51,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Add debug information for stop/quay ID and stay-seated transfers [#5962](https://github.com/opentripplanner/OpenTripPlanner/pull/5962) - Handle NeTEx `any` version [#5983](https://github.com/opentripplanner/OpenTripPlanner/pull/5983) - Keep at least one result for min-transfers and each transit-group in itinerary-group-filter [#5919](https://github.com/opentripplanner/OpenTripPlanner/pull/5919) +- Extract parking lots from NeTEx feeds [#5946](https://github.com/opentripplanner/OpenTripPlanner/pull/5946) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From 4c33d38bedd62348321de9537af901cec5358dbd Mon Sep 17 00:00:00 2001 From: OTP Serialization Version Bot Date: Mon, 29 Jul 2024 12:38:00 +0000 Subject: [PATCH 183/192] Bump serialization version id for #5946 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2f007cbb214..9d5aa615260 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ - 155 + 156 31.3 2.51.1 From f2d24aefb8f80f38c8ddb9d5765d17dfc7c94b81 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Mon, 29 Jul 2024 12:38:58 +0000 Subject: [PATCH 184/192] Upgrade debug client to version 2024/07/2024-07-29T12:38 --- src/client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/index.html b/src/client/index.html index 767f8b1d45a..fd0c2e937f7 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
From 18625fc58ee915eaf04821a1646a8076046dfbce Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 30 Jul 2024 08:29:51 +0000 Subject: [PATCH 185/192] Add changelog entry for #5869 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index d9650be1dcb..64840e36790 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -52,6 +52,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Handle NeTEx `any` version [#5983](https://github.com/opentripplanner/OpenTripPlanner/pull/5983) - Keep at least one result for min-transfers and each transit-group in itinerary-group-filter [#5919](https://github.com/opentripplanner/OpenTripPlanner/pull/5919) - Extract parking lots from NeTEx feeds [#5946](https://github.com/opentripplanner/OpenTripPlanner/pull/5946) +- Filter routes and patterns by service date in GTFS GraphQL API [#5869](https://github.com/opentripplanner/OpenTripPlanner/pull/5869) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From 44864d67c0ff19e3f01596110a291e7b5d69dc47 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:48:03 +0000 Subject: [PATCH 186/192] Update Debug UI dependencies (non-major) --- client/package-lock.json | 192 +++++++++++++++++++-------------------- client/package.json | 12 +-- 2 files changed, 102 insertions(+), 102 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index dc6c6dfa1a3..9e2798660ec 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -20,16 +20,16 @@ }, "devDependencies": { "@graphql-codegen/cli": "5.0.2", - "@graphql-codegen/client-preset": "4.3.2", + "@graphql-codegen/client-preset": "4.3.3", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", "@testing-library/react": "16.0.0", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@typescript-eslint/eslint-plugin": "7.17.0", - "@typescript-eslint/parser": "7.17.0", + "@typescript-eslint/eslint-plugin": "7.18.0", + "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.1", - "@vitest/coverage-v8": "2.0.4", + "@vitest/coverage-v8": "2.0.5", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", @@ -40,8 +40,8 @@ "jsdom": "24.1.1", "prettier": "3.3.3", "typescript": "5.5.4", - "vite": "5.3.4", - "vitest": "2.0.4" + "vite": "5.3.5", + "vitest": "2.0.5" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1861,9 +1861,9 @@ } }, "node_modules/@graphql-codegen/client-preset": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.3.2.tgz", - "integrity": "sha512-42jHyG6u2uFDIVNvzue8zR529aPT16EYIJQmvMk8XuYHo3PneQVlWmQ3j2fBy+RuWCBzpJKPKm7IGSKiw19nmg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.3.3.tgz", + "integrity": "sha512-IrDsSVe8bkKtxgVfKPHzjL9tYlv7KEpA59R4gZLqx/t2WIJncW1i0OMvoz9tgoZsFEs8OKKgXZbnwPZ/Qf1kEw==", "dev": true, "license": "MIT", "dependencies": { @@ -3943,17 +3943,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.17.0.tgz", - "integrity": "sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==", + "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==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.17.0", - "@typescript-eslint/type-utils": "7.17.0", - "@typescript-eslint/utils": "7.17.0", - "@typescript-eslint/visitor-keys": "7.17.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", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3977,16 +3977,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.17.0", - "@typescript-eslint/types": "7.17.0", - "@typescript-eslint/typescript-estree": "7.17.0", - "@typescript-eslint/visitor-keys": "7.17.0", + "@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", "debug": "^4.3.4" }, "engines": { @@ -4006,14 +4006,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.17.0.tgz", - "integrity": "sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.17.0", - "@typescript-eslint/visitor-keys": "7.17.0" + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -4024,14 +4024,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.17.0.tgz", - "integrity": "sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.17.0", - "@typescript-eslint/utils": "7.17.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -4052,9 +4052,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.17.0.tgz", - "integrity": "sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==", + "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==", "dev": true, "license": "MIT", "engines": { @@ -4066,14 +4066,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.17.0.tgz", - "integrity": "sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==", + "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==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.17.0", - "@typescript-eslint/visitor-keys": "7.17.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4108,16 +4108,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.17.0.tgz", - "integrity": "sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==", + "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==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.17.0", - "@typescript-eslint/types": "7.17.0", - "@typescript-eslint/typescript-estree": "7.17.0" + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -4131,13 +4131,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.17.0.tgz", - "integrity": "sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -4175,9 +4175,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.0.4.tgz", - "integrity": "sha512-i4lx/Wpg5zF1h2op7j0wdwuEQxaL/YTwwQaKuKMHYj7MMh8c7I4W7PNfOptZBCSBZI0z1qwn64o0pM/pA8Tz1g==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.0.5.tgz", + "integrity": "sha512-qeFcySCg5FLO2bHHSa0tAZAOnAUbp4L6/A5JDuj9+bt53JREl8hpLjLHEWF0e/gWc8INVpJaqA7+Ene2rclpZg==", "dev": true, "license": "MIT", "dependencies": { @@ -4198,18 +4198,18 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "2.0.4" + "vitest": "2.0.5" } }, "node_modules/@vitest/expect": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.4.tgz", - "integrity": "sha512-39jr5EguIoanChvBqe34I8m1hJFI4+jxvdOpD7gslZrVQBKhh8H9eD7J/LJX4zakrw23W+dITQTDqdt43xVcJw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", + "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.0.4", - "@vitest/utils": "2.0.4", + "@vitest/spy": "2.0.5", + "@vitest/utils": "2.0.5", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -4218,9 +4218,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.4.tgz", - "integrity": "sha512-RYZl31STbNGqf4l2eQM1nvKPXE0NhC6Eq0suTTePc4mtMQ1Fn8qZmjV4emZdEdG2NOWGKSCrHZjmTqDCDoeFBw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", + "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4231,13 +4231,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.4.tgz", - "integrity": "sha512-Gk+9Su/2H2zNfNdeJR124gZckd5st4YoSuhF1Rebi37qTXKnqYyFCd9KP4vl2cQHbtuVKjfEKrNJxHHCW8thbQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.5.tgz", + "integrity": "sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.0.4", + "@vitest/utils": "2.0.5", "pathe": "^1.1.2" }, "funding": { @@ -4245,13 +4245,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.4.tgz", - "integrity": "sha512-or6Mzoz/pD7xTvuJMFYEtso1vJo1S5u6zBTinfl+7smGUhqybn6VjzCDMhmTyVOFWwkCMuNjmNNxnyXPgKDoPw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.5.tgz", + "integrity": "sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.0.4", + "@vitest/pretty-format": "2.0.5", "magic-string": "^0.30.10", "pathe": "^1.1.2" }, @@ -4260,9 +4260,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.4.tgz", - "integrity": "sha512-uTXU56TNoYrTohb+6CseP8IqNwlNdtPwEO0AWl+5j7NelS6x0xZZtP0bDWaLvOfUbaYwhhWp1guzXUxkC7mW7Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", + "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", "dev": true, "license": "MIT", "dependencies": { @@ -4273,13 +4273,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.4.tgz", - "integrity": "sha512-Zc75QuuoJhOBnlo99ZVUkJIuq4Oj0zAkrQ2VzCqNCx6wAwViHEh5Fnp4fiJTE9rA+sAoXRf00Z9xGgfEzV6fzQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", + "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.0.4", + "@vitest/pretty-format": "2.0.5", "estree-walker": "^3.0.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" @@ -11392,9 +11392,9 @@ } }, "node_modules/vite": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.4.tgz", - "integrity": "sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz", + "integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==", "dev": true, "license": "MIT", "dependencies": { @@ -11448,9 +11448,9 @@ } }, "node_modules/vite-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.4.tgz", - "integrity": "sha512-ZpJVkxcakYtig5iakNeL7N3trufe3M6vGuzYAr4GsbCTwobDeyPJpE4cjDhhPluv8OvQCFzu2LWp6GkoKRITXA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.5.tgz", + "integrity": "sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==", "dev": true, "license": "MIT", "dependencies": { @@ -11471,19 +11471,19 @@ } }, "node_modules/vitest": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.4.tgz", - "integrity": "sha512-luNLDpfsnxw5QSW4bISPe6tkxVvv5wn2BBs/PuDRkhXZ319doZyLOBr1sjfB5yCEpTiU7xCAdViM8TNVGPwoog==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.5.tgz", + "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", - "@vitest/expect": "2.0.4", - "@vitest/pretty-format": "^2.0.4", - "@vitest/runner": "2.0.4", - "@vitest/snapshot": "2.0.4", - "@vitest/spy": "2.0.4", - "@vitest/utils": "2.0.4", + "@vitest/expect": "2.0.5", + "@vitest/pretty-format": "^2.0.5", + "@vitest/runner": "2.0.5", + "@vitest/snapshot": "2.0.5", + "@vitest/spy": "2.0.5", + "@vitest/utils": "2.0.5", "chai": "^5.1.1", "debug": "^4.3.5", "execa": "^8.0.1", @@ -11494,7 +11494,7 @@ "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.0.4", + "vite-node": "2.0.5", "why-is-node-running": "^2.3.0" }, "bin": { @@ -11509,8 +11509,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.0.4", - "@vitest/ui": "2.0.4", + "@vitest/browser": "2.0.5", + "@vitest/ui": "2.0.5", "happy-dom": "*", "jsdom": "*" }, diff --git a/client/package.json b/client/package.json index 8ce0ed0cbdb..fe8836e8d36 100644 --- a/client/package.json +++ b/client/package.json @@ -29,16 +29,16 @@ }, "devDependencies": { "@graphql-codegen/cli": "5.0.2", - "@graphql-codegen/client-preset": "4.3.2", + "@graphql-codegen/client-preset": "4.3.3", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", "@testing-library/react": "16.0.0", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "@typescript-eslint/eslint-plugin": "7.17.0", - "@typescript-eslint/parser": "7.17.0", + "@typescript-eslint/eslint-plugin": "7.18.0", + "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.1", - "@vitest/coverage-v8": "2.0.4", + "@vitest/coverage-v8": "2.0.5", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", @@ -49,7 +49,7 @@ "jsdom": "24.1.1", "prettier": "3.3.3", "typescript": "5.5.4", - "vite": "5.3.4", - "vitest": "2.0.4" + "vite": "5.3.5", + "vitest": "2.0.5" } } From d04c26c2212597f5a4efef2d55f6ace8c731ed86 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 31 Jul 2024 22:24:36 +0200 Subject: [PATCH 187/192] Automerge truth, fixes #5988 [ci skip] --- renovate.json5 | 1 + 1 file changed, 1 insertion(+) diff --git a/renovate.json5 b/renovate.json5 index d8ba10984e5..5ac10731e05 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -114,6 +114,7 @@ "com.tngtech.archunit:archunit", "org.apache.maven.plugins:maven-surefire-plugin", "me.fabriciorby:maven-surefire-junit5-tree-reporter", + "com.google.truth:truth", "org.jacoco:jacoco-maven-plugin", // coverage plugin "org.apache.commons:commons-compress", // only used by tests // maven plugins From b04d1d56052fbd199e76d004d9d48afbc592bef0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 31 Jul 2024 22:36:37 +0200 Subject: [PATCH 188/192] Schedule test dependency upgrades once a month [ci skip] --- renovate.json5 | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/renovate.json5 b/renovate.json5 index 5ac10731e05..98ec1f24fbe 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -44,6 +44,23 @@ }, // some dependencies that we auto-merge release very often and even the auto-merges create a lot of // noise, so we slow it down a bit + { + "matchPackageNames": [ + "org.mockito:mockito-core", + "com.tngtech.archunit:archunit", + "org.apache.maven.plugins:maven-surefire-plugin", + "me.fabriciorby:maven-surefire-junit5-tree-reporter", + "com.google.truth:truth", + "org.jacoco:jacoco-maven-plugin", // coverage plugin + "org.apache.commons:commons-compress" // only used by tests + ], + "matchPackagePrefixes": [ + "org.junit.jupiter:", + ], + "groupName": "Test dependencies", + "automerge": true, + "schedule": "on the 17th day of the month" + }, { "matchPackageNames": [ "org.mobilitydata:gbfs-java-model" @@ -110,13 +127,6 @@ { "description": "automatically merge test, logging and build dependencies", "matchPackageNames": [ - "org.mockito:mockito-core", - "com.tngtech.archunit:archunit", - "org.apache.maven.plugins:maven-surefire-plugin", - "me.fabriciorby:maven-surefire-junit5-tree-reporter", - "com.google.truth:truth", - "org.jacoco:jacoco-maven-plugin", // coverage plugin - "org.apache.commons:commons-compress", // only used by tests // maven plugins "org.codehaus.mojo:build-helper-maven-plugin", "org.apache.maven.plugins:maven-source-plugin", @@ -128,7 +138,6 @@ "org.sonatype.plugins:nexus-staging-maven-plugin" ], "matchPackagePrefixes": [ - "org.junit.jupiter:", "org.slf4j:" ], "automerge": true, From 669d042ae3aad3c8b03762f464c67b73b5ae5646 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 1 Aug 2024 06:46:11 +0000 Subject: [PATCH 189/192] Upgrade debug client to version 2024/08/2024-08-01T06:45 --- src/client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/index.html b/src/client/index.html index fd0c2e937f7..d1734898ca0 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
From 74949a120c1a2c254a3c54623a5b3c06ae3d6c38 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 1 Aug 2024 22:54:32 +0200 Subject: [PATCH 190/192] Improve test assertion --- .../java/org/opentripplanner/smoketest/SeattleSmokeTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/opentripplanner/smoketest/SeattleSmokeTest.java b/src/test/java/org/opentripplanner/smoketest/SeattleSmokeTest.java index 94e4b3dea1a..506ee2732b9 100644 --- a/src/test/java/org/opentripplanner/smoketest/SeattleSmokeTest.java +++ b/src/test/java/org/opentripplanner/smoketest/SeattleSmokeTest.java @@ -1,5 +1,6 @@ package org.opentripplanner.smoketest; +import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -160,8 +161,8 @@ public void sharedStop() throws IOException { var first = itineraries.getFirst(); var leg = first.transitLegs().getFirst(); - assertEquals("510", leg.route().shortName().get()); - assertEquals("Sound Transit", leg.route().agency().name()); + assertThat(Set.of("510", "415")).contains(leg.route().shortName().get()); + assertThat(Set.of("Sound Transit", "Community Transit")).contains(leg.route().agency().name()); var stop = leg.from().stop().get(); assertEquals("Olive Way & 6th Ave", stop.name()); From db62d62d046a017833a516836edd45c2abd3f82f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 4 Aug 2024 23:26:23 +0200 Subject: [PATCH 191/192] Fix spelling [ci skip] --- docs/RouteRequest.md | 4 ++-- .../standalone/config/routerequest/RouteRequestConfig.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/RouteRequest.md b/docs/RouteRequest.md index 14d937eeb63..a8baaf1603b 100644 --- a/docs/RouteRequest.md +++ b/docs/RouteRequest.md @@ -254,7 +254,7 @@ This is a performance limit and should therefore be set high. Results close to t guaranteed to be optimal. Use itinerary-filters to limit what is presented to the client. The duration can be set per mode(`maxDirectStreetDurationForMode`), because some street modes searches are much more resource intensive than others. A default value is applied if the mode specific value -do not exist." +does not exist."

maxJourneyDuration

@@ -403,7 +403,7 @@ This is a performance limit and should therefore be set high. Results close to t guaranteed to be optimal. Use itinerary-filters to limit what is presented to the client. The duration can be set per mode(`maxDurationForMode`), because some street modes searches are much more resource intensive than others. A default value is applied if the mode specific value -do not exist. +does not exist.

maxStopCount

diff --git a/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java index 6fa33aac267..dc91388458a 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java @@ -514,7 +514,7 @@ the access legs used. In other cases where the access(CAR) is faster than transi guaranteed to be optimal. Use itinerary-filters to limit what is presented to the client. The duration can be set per mode(`maxDurationForMode`), because some street modes searches are much more resource intensive than others. A default value is applied if the mode specific value -do not exist. +does not exist. """ ) .asDuration(dftAccessEgress.maxDuration().defaultValue()), @@ -554,7 +554,7 @@ duration can be set per mode(`maxDurationForMode`), because some street modes se guaranteed to be optimal. Use itinerary-filters to limit what is presented to the client. The duration can be set per mode(`maxDirectStreetDurationForMode`), because some street modes searches are much more resource intensive than others. A default value is applied if the mode specific value -do not exist." +does not exist." """ ) .asDuration(dft.maxDirectDuration().defaultValue()), From 0ceb95109ea758f177c14cd8dcf9b69c4ea5d238 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 6 Aug 2024 13:09:38 +0200 Subject: [PATCH 192/192] Fix conversion of parent station id in vector tiles --- .../layers/stops/StopsLayerTest.java | 75 ++++++++++--------- .../stops/DigitransitStopPropertyMapper.java | 7 +- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerTest.java index 4c3e60701bd..7760eee13b8 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerTest.java @@ -1,56 +1,60 @@ package org.opentripplanner.ext.vectortiles.layers.stops; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import java.util.HashMap; import java.util.Locale; import java.util.Map; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.opentripplanner.ext.vectortiles.layers.TestTransitService; +import org.opentripplanner.framework.geometry.WgsCoordinate; +import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.TranslatedString; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; public class StopsLayerTest { - private RegularStop stop; + private static final I18NString NAME_TRANSLATIONS = TranslatedString.getI18NString( + new HashMap<>() { + { + put(null, "name"); + put("de", "nameDE"); + } + }, + false, + false + ); + private static final I18NString DESC_TRANSLATIONS = TranslatedString.getI18NString( + new HashMap<>() { + { + put(null, "desc"); + put("de", "descDE"); + } + }, + false, + false + ); - @BeforeEach - public void setUp() { - var nameTranslations = TranslatedString.getI18NString( - new HashMap<>() { - { - put(null, "name"); - put("de", "nameDE"); - } - }, - false, - false - ); - var descTranslations = TranslatedString.getI18NString( - new HashMap<>() { - { - put(null, "desc"); - put("de", "descDE"); - } - }, - false, - false - ); - stop = - StopModel - .of() - .regularStop(new FeedScopedId("F", "name")) - .withName(nameTranslations) - .withDescription(descTranslations) - .withCoordinate(50, 10) - .build(); - } + private static final Station STATION = Station + .of(id("station1")) + .withCoordinate(WgsCoordinate.GREENWICH) + .withName(I18NString.of("A Station")) + .build(); + private static final RegularStop STOP = StopModel + .of() + .regularStop(new FeedScopedId("F", "name")) + .withName(NAME_TRANSLATIONS) + .withDescription(DESC_TRANSLATIONS) + .withCoordinate(50, 10) + .withParentStation(STATION) + .build(); @Test public void digitransitStopPropertyMapperTest() { @@ -65,12 +69,13 @@ public void digitransitStopPropertyMapperTest() { ); Map map = new HashMap<>(); - mapper.map(stop).forEach(o -> map.put(o.key(), o.value())); + mapper.map(STOP).forEach(o -> map.put(o.key(), o.value())); assertEquals("F:name", map.get("gtfsId")); assertEquals("name", map.get("name")); assertEquals("desc", map.get("desc")); assertEquals("[{\"gtfsType\":100}]", map.get("routes")); + assertEquals(STATION.getId().toString(), map.get("parentStation")); } @Test @@ -86,7 +91,7 @@ public void digitransitStopPropertyMapperTranslationTest() { ); Map map = new HashMap<>(); - mapper.map(stop).forEach(o -> map.put(o.key(), o.value())); + mapper.map(STOP).forEach(o -> map.put(o.key(), o.value())); assertEquals("nameDE", map.get("name")); assertEquals("descDE", map.get("desc")); diff --git a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/DigitransitStopPropertyMapper.java b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/DigitransitStopPropertyMapper.java index d10e221b1d5..edf9c7d8188 100644 --- a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/DigitransitStopPropertyMapper.java +++ b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/DigitransitStopPropertyMapper.java @@ -1,5 +1,7 @@ package org.opentripplanner.ext.vectortiles.layers.stops; +import static org.opentripplanner.inspector.vector.KeyValue.kv; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Collection; @@ -52,10 +54,7 @@ protected static Collection getBaseKeyValues( new KeyValue("desc", i18NStringMapper.mapToApi(stop.getDescription())), new KeyValue("type", getType(transitService, stop)), new KeyValue("routes", getRoutes(transitService, stop)), - new KeyValue( - "parentStation", - stop.getParentStation() != null ? stop.getParentStation().getId() : null - ) + kv("parentStation", stop.getParentStation() != null ? stop.getParentStation().getId() : null) ); }