From 4264ab8ed8933574a2272ccb34d39756b43a3f2b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 21 Dec 2023 12:08:51 +0100 Subject: [PATCH 01/86] Introduce fallback zone id if none could be derived from the system --- .../api/common/RoutingResource.java | 3 +- .../apis/gtfs/mapping/RouteRequestMapper.java | 3 +- .../framework/application/ZoneIdFallback.java | 39 +++++++++++++++++++ .../framework/logging/Throttle.java | 4 ++ .../mapping/GraphPathToItineraryMapper.java | 3 +- .../service/DefaultRoutingService.java | 3 +- 6 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/opentripplanner/framework/application/ZoneIdFallback.java diff --git a/src/main/java/org/opentripplanner/api/common/RoutingResource.java b/src/main/java/org/opentripplanner/api/common/RoutingResource.java index 9cb4f139bc4..a97a8368187 100644 --- a/src/main/java/org/opentripplanner/api/common/RoutingResource.java +++ b/src/main/java/org/opentripplanner/api/common/RoutingResource.java @@ -21,6 +21,7 @@ import org.opentripplanner.api.parameter.QualifiedModeSet; import org.opentripplanner.ext.dataoverlay.api.DataOverlayParameters; import org.opentripplanner.framework.application.OTPFeature; +import org.opentripplanner.framework.application.ZoneIdFallback; import org.opentripplanner.framework.lang.StringUtils; import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.routing.api.request.RouteRequest; @@ -711,7 +712,7 @@ protected RouteRequest buildRequest(MultivaluedMap queryParamete { //FIXME: move into setter method on routing request - ZoneId tz = serverContext.transitService().getTimeZone(); + ZoneId tz = ZoneIdFallback.zoneId(serverContext.transitService().getTimeZone()); if (date == null && time != null) { // Time was provided but not date LOG.debug("parsing ISO datetime {}", time); try { diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java index ab9e8bce823..7b47f3231c9 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java @@ -16,6 +16,7 @@ import org.opentripplanner.api.parameter.QualifiedMode; import org.opentripplanner.api.parameter.QualifiedModeSet; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; +import org.opentripplanner.framework.application.ZoneIdFallback; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.GenericLocation; import org.opentripplanner.routing.api.request.RouteRequest; @@ -54,7 +55,7 @@ public static RouteRequest toRouteRequest( request.setDateTime( environment.getArgument("date"), environment.getArgument("time"), - context.transitService().getTimeZone() + ZoneIdFallback.zoneId(context.transitService().getTimeZone()) ); callWith.argument("wheelchair", request::setWheelchair); diff --git a/src/main/java/org/opentripplanner/framework/application/ZoneIdFallback.java b/src/main/java/org/opentripplanner/framework/application/ZoneIdFallback.java new file mode 100644 index 00000000000..260fb40f0bf --- /dev/null +++ b/src/main/java/org/opentripplanner/framework/application/ZoneIdFallback.java @@ -0,0 +1,39 @@ +package org.opentripplanner.framework.application; + +import java.time.ZoneId; +import javax.annotation.Nullable; +import org.opentripplanner.framework.logging.Throttle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provides a fallback mechanism for retrieving a zone id (=time zone). + * If a ZoneId is not provided, it returns a default fallback ZoneId (UTC). + *

+ * This situation happens when you don't load any transit data into the graph but want to route + * anyway, perhaps only on the street network. + */ +public class ZoneIdFallback { + + private static final Logger LOG = LoggerFactory.getLogger(ZoneIdFallback.class); + private static final ZoneId FALLBACK = ZoneId.of("UTC"); + private static final Throttle THROTTLE = Throttle.ofOneMinute(); + + /** + * Accepts a nullable zone id (time zone) and returns UTC as the fallback. + */ + public static ZoneId zoneId(@Nullable ZoneId id) { + if (id == null) { + THROTTLE.throttle(() -> { + LOG.warn( + "Your instance doesn't contain a time zone (which is usually derived from transit data). Assuming {}.", + FALLBACK + ); + LOG.warn("Please double-check that transit data was correctly loaded."); + }); + return FALLBACK; + } else { + return id; + } + } +} diff --git a/src/main/java/org/opentripplanner/framework/logging/Throttle.java b/src/main/java/org/opentripplanner/framework/logging/Throttle.java index 47417aa974a..631d59a2697 100644 --- a/src/main/java/org/opentripplanner/framework/logging/Throttle.java +++ b/src/main/java/org/opentripplanner/framework/logging/Throttle.java @@ -35,6 +35,10 @@ public static Throttle ofOneSecond() { return new Throttle(1000); } + public static Throttle ofOneMinute() { + return new Throttle(1000 * 60); + } + public String setupInfo() { return setupInfo; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java index 9858ccfee50..c363689d03d 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java @@ -16,6 +16,7 @@ import org.opentripplanner.ext.flex.FlexibleTransitLeg; import org.opentripplanner.ext.flex.edgetype.FlexTripEdge; import org.opentripplanner.framework.application.OTPFeature; +import org.opentripplanner.framework.application.ZoneIdFallback; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.plan.ElevationProfile; @@ -58,7 +59,7 @@ public GraphPathToItineraryMapper( StreetNotesService streetNotesService, double ellipsoidToGeoidDifference ) { - this.timeZone = timeZone; + this.timeZone = ZoneIdFallback.zoneId(timeZone); this.streetNotesService = streetNotesService; this.ellipsoidToGeoidDifference = ellipsoidToGeoidDifference; } diff --git a/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java b/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java index 320ffb2117d..a9a887588dd 100644 --- a/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java +++ b/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java @@ -2,6 +2,7 @@ import java.time.ZoneId; import org.opentripplanner.framework.application.OTPRequestTimeoutException; +import org.opentripplanner.framework.application.ZoneIdFallback; import org.opentripplanner.framework.tostring.MultiLineToStringBuilder; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.routing.algorithm.RoutingWorker; @@ -30,7 +31,7 @@ public class DefaultRoutingService implements RoutingService { public DefaultRoutingService(OtpServerRequestContext serverContext) { this.serverContext = serverContext; - this.timeZone = serverContext.transitService().getTimeZone(); + this.timeZone = ZoneIdFallback.zoneId(serverContext.transitService().getTimeZone()); } @Override From bf2bef8b6795720a1aa65911f40d17c6174fdd0d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 21 Dec 2023 12:18:40 +0100 Subject: [PATCH 02/86] Move fallback class --- .../java/org/opentripplanner/api/common/RoutingResource.java | 2 +- .../{framework/application => api/support}/ZoneIdFallback.java | 2 +- .../opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java | 2 +- .../routing/algorithm/mapping/GraphPathToItineraryMapper.java | 2 +- .../opentripplanner/routing/service/DefaultRoutingService.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/org/opentripplanner/{framework/application => api/support}/ZoneIdFallback.java (96%) diff --git a/src/main/java/org/opentripplanner/api/common/RoutingResource.java b/src/main/java/org/opentripplanner/api/common/RoutingResource.java index a97a8368187..dab06e970ce 100644 --- a/src/main/java/org/opentripplanner/api/common/RoutingResource.java +++ b/src/main/java/org/opentripplanner/api/common/RoutingResource.java @@ -19,9 +19,9 @@ import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import org.opentripplanner.api.parameter.QualifiedModeSet; +import org.opentripplanner.api.support.ZoneIdFallback; import org.opentripplanner.ext.dataoverlay.api.DataOverlayParameters; import org.opentripplanner.framework.application.OTPFeature; -import org.opentripplanner.framework.application.ZoneIdFallback; import org.opentripplanner.framework.lang.StringUtils; import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.routing.api.request.RouteRequest; diff --git a/src/main/java/org/opentripplanner/framework/application/ZoneIdFallback.java b/src/main/java/org/opentripplanner/api/support/ZoneIdFallback.java similarity index 96% rename from src/main/java/org/opentripplanner/framework/application/ZoneIdFallback.java rename to src/main/java/org/opentripplanner/api/support/ZoneIdFallback.java index 260fb40f0bf..0175337e2d4 100644 --- a/src/main/java/org/opentripplanner/framework/application/ZoneIdFallback.java +++ b/src/main/java/org/opentripplanner/api/support/ZoneIdFallback.java @@ -1,4 +1,4 @@ -package org.opentripplanner.framework.application; +package org.opentripplanner.api.support; import java.time.ZoneId; import javax.annotation.Nullable; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java index 7b47f3231c9..306abf8945f 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java @@ -15,8 +15,8 @@ import org.opentripplanner.api.common.LocationStringParser; import org.opentripplanner.api.parameter.QualifiedMode; import org.opentripplanner.api.parameter.QualifiedModeSet; +import org.opentripplanner.api.support.ZoneIdFallback; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; -import org.opentripplanner.framework.application.ZoneIdFallback; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.GenericLocation; import org.opentripplanner.routing.api.request.RouteRequest; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java index c363689d03d..ee86f8de720 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java @@ -12,11 +12,11 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.impl.PackedCoordinateSequence; +import org.opentripplanner.api.support.ZoneIdFallback; import org.opentripplanner.astar.model.GraphPath; import org.opentripplanner.ext.flex.FlexibleTransitLeg; import org.opentripplanner.ext.flex.edgetype.FlexTripEdge; import org.opentripplanner.framework.application.OTPFeature; -import org.opentripplanner.framework.application.ZoneIdFallback; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.plan.ElevationProfile; diff --git a/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java b/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java index a9a887588dd..92ccd2cc786 100644 --- a/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java +++ b/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java @@ -1,8 +1,8 @@ package org.opentripplanner.routing.service; import java.time.ZoneId; +import org.opentripplanner.api.support.ZoneIdFallback; import org.opentripplanner.framework.application.OTPRequestTimeoutException; -import org.opentripplanner.framework.application.ZoneIdFallback; import org.opentripplanner.framework.tostring.MultiLineToStringBuilder; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.routing.algorithm.RoutingWorker; From 8c8967a5a1ac66aa57123cfc92fa513a2a8b6fee Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 21 Dec 2023 12:24:28 +0100 Subject: [PATCH 03/86] Add test --- .../api/support/ZoneIdFallbackTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/test/java/org/opentripplanner/api/support/ZoneIdFallbackTest.java diff --git a/src/test/java/org/opentripplanner/api/support/ZoneIdFallbackTest.java b/src/test/java/org/opentripplanner/api/support/ZoneIdFallbackTest.java new file mode 100644 index 00000000000..02013f0856b --- /dev/null +++ b/src/test/java/org/opentripplanner/api/support/ZoneIdFallbackTest.java @@ -0,0 +1,19 @@ +package org.opentripplanner.api.support; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.opentripplanner._support.time.ZoneIds; + +class ZoneIdFallbackTest { + + @Test + void fallback() { + assertEquals(ZoneIds.UTC, ZoneIdFallback.zoneId(null)); + } + + @Test + void keepOriginal() { + assertEquals(ZoneIds.BERLIN, ZoneIdFallback.zoneId(ZoneIds.BERLIN)); + } +} From 22f05019e5493209b38c2930b21cd684c138d4b6 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Wed, 20 Dec 2023 12:54:28 +0100 Subject: [PATCH 04/86] refactor: Rename TransitGroupPriority to TransitPriorityGroup --- docs/RouteRequest.md | 24 +++++----- .../common/RequestToPreferencesMapper.java | 4 +- .../api/common/RoutingResource.java | 4 +- .../preferences/TransitPreferencesMapper.java | 2 +- .../apis/transmodel/model/plan/TripQuery.java | 14 +++--- .../raptor/api/model/RaptorTripPattern.java | 2 +- .../api/request/MultiCriteriaRequest.java | 10 ++--- ...java => RaptorTransitGroupCalculator.java} | 8 ++-- .../internalapi/PassThroughPointsService.java | 2 +- .../configure/McRangeRaptorConfig.java | 13 +++--- ...a => TransitGroupPriorityRideFactory.java} | 21 ++++----- ...p32n.java => TransitGroupPriority32n.java} | 20 ++++----- .../transit/mappers/RaptorRequestMapper.java | 8 ++-- .../request/PriorityGroupConfigurator.java | 20 ++++----- .../transit/request/PriorityGroupMatcher.java | 8 ++-- .../RaptorRoutingRequestTransitData.java | 6 +-- ...aptorRoutingRequestTransitDataCreator.java | 2 +- .../preference/TransitPreferences.java | 26 +++++------ .../api/request/request/TransitRequest.java | 16 +++---- ...oupSelect.java => TransitGroupSelect.java} | 20 ++++----- .../routerequest/RouteRequestConfig.java | 21 ++++----- ...g.java => TransitGroupPriorityConfig.java} | 30 ++++++------- .../apis/transmodel/schema.graphql | 10 ++--- .../moduletests/K01_TransitPriorityTest.java | 16 +++---- .../K02_TransitPriorityDestinationTest.java | 17 +++---- ....java => TransitGroupPriority32nTest.java} | 44 +++++++++---------- .../PriorityGroupConfiguratorTest.java | 40 ++++++++--------- .../request/PriorityGroupMatcherTest.java | 18 ++++---- .../preference/TransitPreferencesTest.java | 6 +-- 29 files changed, 207 insertions(+), 225 deletions(-) rename src/main/java/org/opentripplanner/raptor/api/request/{RaptorTransitPriorityGroupCalculator.java => RaptorTransitGroupCalculator.java} (71%) rename src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/{TransitPriorityGroupRideFactory.java => TransitGroupPriorityRideFactory.java} (67%) rename src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/{TransitPriorityGroup32n.java => TransitGroupPriority32n.java} (76%) rename src/main/java/org/opentripplanner/routing/api/request/request/filter/{TransitPriorityGroupSelect.java => TransitGroupSelect.java} (87%) rename src/main/java/org/opentripplanner/standalone/config/routerequest/{TransitPriorityGroupConfig.java => TransitGroupPriorityConfig.java} (76%) rename src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/{TransitPriorityGroup32nTest.java => TransitGroupPriority32nTest.java} (51%) diff --git a/docs/RouteRequest.md b/docs/RouteRequest.md index 9e891cf732d..cef482eb781 100644 --- a/docs/RouteRequest.md +++ b/docs/RouteRequest.md @@ -57,7 +57,7 @@ and in the [transferRequests in build-config.json](BuildConfiguration.md#transfe | numItineraries | `integer` | The maximum number of itineraries to return. | *Optional* | `50` | 2.0 | | [optimize](#rd_optimize) | `enum` | The set of characteristics that the user wants to optimize for. | *Optional* | `"safe"` | 2.0 | | [otherThanPreferredRoutesPenalty](#rd_otherThanPreferredRoutesPenalty) | `integer` | Penalty added for using every route that is not preferred if user set any route as preferred. | *Optional* | `300` | 2.0 | -| [relaxTransitPriorityGroup](#rd_relaxTransitPriorityGroup) | `string` | The relax function for transit-priority-groups | *Optional* | `"0s + 1.00 t"` | 2.5 | +| [relaxTransitGroupPriority](#rd_relaxTransitGroupPriority) | `string` | The relax function for transit-group-priority | *Optional* | `"0s + 1.00 t"` | 2.5 | | [relaxTransitSearchGeneralizedCostAtDestination](#rd_relaxTransitSearchGeneralizedCostAtDestination) | `double` | Whether non-optimal transit paths at the destination should be returned | *Optional* | | 2.3 | | [searchWindow](#rd_searchWindow) | `duration` | The duration of the search-window. | *Optional* | | 2.0 | | stairsReluctance | `double` | Used instead of walkReluctance for stairs. | *Optional* | `2.0` | 2.0 | @@ -108,7 +108,7 @@ and in the [transferRequests in build-config.json](BuildConfiguration.md#transfe |    [extraStopBoardAlightCostsFactor](#rd_to_extraStopBoardAlightCostsFactor) | `double` | Add an extra board- and alight-cost for prioritized stops. | *Optional* | `0.0` | 2.1 | |    [minSafeWaitTimeFactor](#rd_to_minSafeWaitTimeFactor) | `double` | Used to set a maximum wait-time cost, base on min-safe-transfer-time. | *Optional* | `5.0` | 2.1 | |    [optimizeTransferWaitTime](#rd_to_optimizeTransferWaitTime) | `boolean` | This enables the transfer wait time optimization. | *Optional* | `true` | 2.1 | -| [transitPriorityGroups](#rd_transitPriorityGroups) | `object` | Transit priority groups configuration | *Optional* | | 2.5 | +| [transitGroupPriority](#rd_transitGroupPriority) | `object` | Group transit patterns and give each group a mutual advantage in the Raptor search. | *Optional* | | 2.5 | | [transitReluctanceForMode](#rd_transitReluctanceForMode) | `enum map of double` | Transit reluctance for a given transport mode | *Optional* | | 2.1 | | [unpreferred](#rd_unpreferred) | `object` | Parameters listing authorities or lines that preferably should not be used in trip patters. | *Optional* | | 2.2 | |    [agencies](#rd_unpreferred_agencies) | `feed-scoped-id[]` | The ids of the agencies that incur an extra cost when being used. Format: `FeedId:AgencyId` | *Optional* | | 2.2 | @@ -247,16 +247,16 @@ Penalty added for using every route that is not preferred if user set any route We return number of seconds that we are willing to wait for preferred route. -

relaxTransitPriorityGroup

+

relaxTransitGroupPriority

**Since version:** `2.5` ∙ **Type:** `string` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"0s + 1.00 t"` **Path:** /routingDefaults -The relax function for transit-priority-groups +The relax function for transit-group-priority -A path is considered optimal if the generalized-cost is less than the -generalized-cost of another path. If this parameter is set, the comparison is relaxed -further if they belong to different transit-priority-groups. +A path is considered optimal if the generalized-cost is less than the generalized-cost of +another path. If this parameter is set, the comparison is relaxed further if they belong +to different transit groups.

relaxTransitSearchGeneralizedCostAtDestination

@@ -812,22 +812,20 @@ This enables the transfer wait time optimization. If not enabled generalizedCost function is used to pick the optimal transfer point. -

transitPriorityGroups

+

transitGroupPriority

**Since version:** `2.5` ∙ **Type:** `object` ∙ **Cardinality:** `Optional` **Path:** /routingDefaults -Transit priority groups configuration +Group transit patterns and give each group a mutual advantage in the Raptor search. Use this to separate transit patterns into groups. Each group will be given a group-id. A path (multiple legs) will then have a set of group-ids based on the group-id from each leg. Hence, two paths with a different set of group-ids will BOTH be optimal unless the cost is -worse than the relaxation specified in the `relaxTransitPriorityGroup` parameter. This is +worse than the relaxation specified in the `relaxTransitGroupPriority` parameter. This is only available in the TransmodelAPI for now. -Unmatched patterns are put in the BASE priority-group (group id: 0). This group is special. -If a path only have legs in the base group, then that path dominates other paths, but other -paths must be better to make it. +Unmatched patterns are put in the BASE priority-group. **THIS IS STILL AN EXPERIMENTAL FEATURE - IT MAY CHANGE WITHOUT ANY NOTICE!** diff --git a/src/main/java/org/opentripplanner/api/common/RequestToPreferencesMapper.java b/src/main/java/org/opentripplanner/api/common/RequestToPreferencesMapper.java index aa827ae5b4c..6a8b71fe939 100644 --- a/src/main/java/org/opentripplanner/api/common/RequestToPreferencesMapper.java +++ b/src/main/java/org/opentripplanner/api/common/RequestToPreferencesMapper.java @@ -95,9 +95,9 @@ private BoardAndAlightSlack mapTransit() { setIfNotNull(req.otherThanPreferredRoutesPenalty, tr::setOtherThanPreferredRoutesPenalty); setIfNotNull(req.ignoreRealtimeUpdates, tr::setIgnoreRealtimeUpdates); - if (req.relaxTransitPriorityGroup != null) { + if (req.relaxTransitGroupPriority != null) { tr.withTransitGroupPriorityGeneralizedCostSlack( - CostLinearFunction.of(req.relaxTransitPriorityGroup) + CostLinearFunction.of(req.relaxTransitGroupPriority) ); } else { setIfNotNull( diff --git a/src/main/java/org/opentripplanner/api/common/RoutingResource.java b/src/main/java/org/opentripplanner/api/common/RoutingResource.java index 9cb4f139bc4..72812234b29 100644 --- a/src/main/java/org/opentripplanner/api/common/RoutingResource.java +++ b/src/main/java/org/opentripplanner/api/common/RoutingResource.java @@ -658,8 +658,8 @@ public abstract class RoutingResource { @QueryParam("useVehicleParkingAvailabilityInformation") protected Boolean useVehicleParkingAvailabilityInformation; - @QueryParam("relaxTransitPriorityGroup") - protected String relaxTransitPriorityGroup; + @QueryParam("relaxTransitGroupPriority") + protected String relaxTransitGroupPriority; /** * Whether non-optimal transit paths at the destination should be returned. diff --git a/src/main/java/org/opentripplanner/apis/transmodel/mapping/preferences/TransitPreferencesMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/preferences/TransitPreferencesMapper.java index caa8ebf7715..d4bf92b828c 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/preferences/TransitPreferencesMapper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/mapping/preferences/TransitPreferencesMapper.java @@ -34,7 +34,7 @@ public static void mapTransitPreferences( callWith.argument("includePlannedCancellations", transit::setIncludePlannedCancellations); callWith.argument("includeRealtimeCancellations", transit::setIncludeRealtimeCancellations); callWith.argument( - "relaxTransitPriorityGroup", + "relaxTransitGroupPriority", transit::withTransitGroupPriorityGeneralizedCostSlack ); callWith.argument( diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/TripQuery.java b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/TripQuery.java index d06c5954444..52bafc1f28b 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/TripQuery.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/TripQuery.java @@ -277,16 +277,16 @@ public static GraphQLFieldDefinition create( .argument( GraphQLArgument .newArgument() - .name("relaxTransitPriorityGroup") + .name("relaxTransitGroupPriority") .description( """ Relax generalized-cost when comparing trips with a different set of - transit-priority-groups. The groups are set server side for service-journey and + transit-group-priorities. The groups are set server side for service-journey and can not be configured in the API. This mainly helps to return competition neutral - services. Long distance authorities are put in different transit-priority-groups. + services. Long distance authorities are put in different transit-groups. This relaxes the comparison inside the routing engine for each stop-arrival. If two - paths have a different set of transit-priority-groups, then the generalized-cost + paths have a different set of transit-group-priorities, then the generalized-cost comparison is relaxed. The final set of paths are filtered through the normal itinerary-filters. @@ -298,9 +298,9 @@ public static GraphQLFieldDefinition create( ) .type(RelaxCostType.INPUT_TYPE) .defaultValueLiteral( - preferences.transit().relaxTransitPriorityGroup().isNormal() + preferences.transit().relaxTransitGroupPriority().isNormal() ? NullValue.of() - : RelaxCostType.valueOf(preferences.transit().relaxTransitPriorityGroup()) + : RelaxCostType.valueOf(preferences.transit().relaxTransitGroupPriority()) ) .build() ) @@ -523,7 +523,7 @@ public static GraphQLFieldDefinition create( GraphQLArgument .newArgument() .name("relaxTransitSearchGeneralizedCostAtDestination") - .deprecate("This is replaced by 'relaxTransitPriorityGroup'.") + .deprecate("This is replaced by 'relaxTransitGroupPriority'.") .description( """ Whether non-optimal transit paths at the destination should be returned. Let c be the diff --git a/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripPattern.java b/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripPattern.java index 98a2533486b..e5be1cab0d5 100644 --- a/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripPattern.java +++ b/src/main/java/org/opentripplanner/raptor/api/model/RaptorTripPattern.java @@ -46,7 +46,7 @@ public interface RaptorTripPattern { int slackIndex(); /** - * A pattern may belong to a transit-priority-group. Each group is given an advantage during + * A pattern may belong to a transit-group-priority. Each group is given an advantage during * the multi-criteria search, so the best alternative for each group is found. */ int priorityGroupId(); 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 64cd9dae1a5..368b4660922 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 RaptorTransitPriorityGroupCalculator transitPriorityCalculator; + private final RaptorTransitGroupCalculator 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 RaptorTransitPriorityGroupCalculator transitPriorityCalculator; + private RaptorTransitGroupCalculator transitPriorityCalculator; private List passThroughPoints; private Double relaxCostAtDestination; @@ -163,11 +163,11 @@ public Builder withRelaxC1(RelaxFunction relaxC1) { } @Nullable - public RaptorTransitPriorityGroupCalculator transitPriorityCalculator() { + public RaptorTransitGroupCalculator transitPriorityCalculator() { return transitPriorityCalculator; } - public Builder withTransitPriorityCalculator(RaptorTransitPriorityGroupCalculator value) { + public Builder withTransitPriorityCalculator(RaptorTransitGroupCalculator value) { transitPriorityCalculator = value; return this; } diff --git a/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitPriorityGroupCalculator.java b/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupCalculator.java similarity index 71% rename from src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitPriorityGroupCalculator.java rename to src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupCalculator.java index c3890fa47b3..b5f0598415e 100644 --- a/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitPriorityGroupCalculator.java +++ b/src/main/java/org/opentripplanner/raptor/api/request/RaptorTransitGroupCalculator.java @@ -2,19 +2,19 @@ import org.opentripplanner.raptor.api.model.DominanceFunction; -public interface RaptorTransitPriorityGroupCalculator { +public interface RaptorTransitGroupCalculator { /** - * Merge in the trip transit priority group id with an existing set. Note! Both the set + * Merge in the transit group id with an existing set. Note! Both the set * and the group id type is {@code int}. * * @param currentGroupIds the set of groupIds for all legs in a path. * @param boardingGroupId the transit group id to add to the given set. * @return the new computed set of groupIds */ - int mergeTransitPriorityGroupIds(int currentGroupIds, int boardingGroupId); + int mergeGroupIds(int currentGroupIds, int boardingGroupId); /** - * This is the dominance function to use for comparing transit-priority-groupIds. + * This is the dominance function to use for comparing transit-groups. * It is critical that the implementation is "static" so it can be inlined, since it * is run in the innermost loop of Raptor. */ diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/PassThroughPointsService.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/PassThroughPointsService.java index 69899b55688..4932d9c46fe 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/PassThroughPointsService.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/internalapi/PassThroughPointsService.java @@ -54,7 +54,7 @@ default boolean isNoop() { void updateC2Value(int currentPathC2, IntConsumer update); /** - * This is the dominance function to use for comparing transit-priority-groupIds. + * This is the dominance function to use for comparing transit-group-priorityIds. * It is critical that the implementation is "static" so it can be inlined, since it * is run in the innermost loop of Raptor. */ 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 e7aa07fb914..8eef90950dd 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.RaptorTransitPriorityGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; import org.opentripplanner.raptor.rangeraptor.context.SearchContext; import org.opentripplanner.raptor.rangeraptor.internalapi.Heuristics; import org.opentripplanner.raptor.rangeraptor.internalapi.ParetoSetCost; @@ -29,7 +29,7 @@ import org.opentripplanner.raptor.rangeraptor.multicriteria.ride.c1.PatternRideC1; import org.opentripplanner.raptor.rangeraptor.multicriteria.ride.c2.PassThroughRideFactory; import org.opentripplanner.raptor.rangeraptor.multicriteria.ride.c2.PatternRideC2; -import org.opentripplanner.raptor.rangeraptor.multicriteria.ride.c2.TransitPriorityGroupRideFactory; +import org.opentripplanner.raptor.rangeraptor.multicriteria.ride.c2.TransitGroupPriorityRideFactory; import org.opentripplanner.raptor.rangeraptor.path.DestinationArrivalPaths; import org.opentripplanner.raptor.rangeraptor.path.configure.PathConfig; import org.opentripplanner.raptor.util.paretoset.ParetoComparator; @@ -173,7 +173,8 @@ private MultiCriteriaRequest mcRequest() { } /** - * Currently "transit-priority-groups" is the only feature using two multi-criteria(c2). + * Use c2 in the search, this is use-case specific. For example the pass-through or + * transit-group-priority features uses the c2 value. */ private boolean includeC2() { return mcRequest().includeC2(); @@ -184,7 +185,7 @@ private PatternRideFactory> createPatternRideC2Factory() { return new PassThroughRideFactory<>(passThroughPointsService); } if (isTransitPriority()) { - return new TransitPriorityGroupRideFactory<>(getTransitPriorityGroupCalculator()); + return new TransitGroupPriorityRideFactory<>(getTransitGroupPriorityCalculator()); } throw new IllegalStateException("Only pass-through and transit-priority uses c2."); } @@ -195,12 +196,12 @@ private DominanceFunction dominanceFunctionC2() { return passThroughPointsService.dominanceFunction(); } if (isTransitPriority()) { - return getTransitPriorityGroupCalculator().dominanceFunction(); + return getTransitGroupPriorityCalculator().dominanceFunction(); } return null; } - private RaptorTransitPriorityGroupCalculator getTransitPriorityGroupCalculator() { + private RaptorTransitGroupCalculator getTransitGroupPriorityCalculator() { return mcRequest().transitPriorityCalculator().orElseThrow(); } diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitPriorityGroupRideFactory.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java similarity index 67% rename from src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitPriorityGroupRideFactory.java rename to src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java index eca049233b9..5d65c40d021 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitPriorityGroupRideFactory.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/ride/c2/TransitGroupPriorityRideFactory.java @@ -2,25 +2,25 @@ import org.opentripplanner.raptor.api.model.RaptorTripPattern; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; -import org.opentripplanner.raptor.api.request.RaptorTransitPriorityGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; import org.opentripplanner.raptor.rangeraptor.multicriteria.arrivals.McStopArrival; import org.opentripplanner.raptor.rangeraptor.multicriteria.ride.PatternRide; import org.opentripplanner.raptor.rangeraptor.multicriteria.ride.PatternRideFactory; /** - * This factory creates new {@link PatternRide}s and merge in transit-priority-group ids + * This factory creates new {@link PatternRide}s and merge in transit-group-priority ids * into c2. */ -public class TransitPriorityGroupRideFactory +public class TransitGroupPriorityRideFactory implements PatternRideFactory> { private int currentPatternGroupPriority; - private final RaptorTransitPriorityGroupCalculator transitPriorityGroupCalculator; + private final RaptorTransitGroupCalculator transitGroupPriorityCalculator; - public TransitPriorityGroupRideFactory( - RaptorTransitPriorityGroupCalculator transitPriorityGroupCalculator + public TransitGroupPriorityRideFactory( + RaptorTransitGroupCalculator transitGroupPriorityCalculator ) { - this.transitPriorityGroupCalculator = transitPriorityGroupCalculator; + this.transitGroupPriorityCalculator = transitGroupPriorityCalculator; } @Override @@ -52,12 +52,9 @@ public void prepareForTransitWith(RaptorTripPattern pattern) { } /** - * Currently transit-priority-group is the only usage of c2 + * Currently transit-group-priority is the only usage of c2 */ private int calculateC2(int c2) { - return transitPriorityGroupCalculator.mergeTransitPriorityGroupIds( - c2, - currentPatternGroupPriority - ); + return transitGroupPriorityCalculator.mergeGroupIds(c2, currentPatternGroupPriority); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitPriorityGroup32n.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32n.java similarity index 76% rename from src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitPriorityGroup32n.java rename to src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32n.java index 9b744932b8b..feb3f6f7b3a 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitPriorityGroup32n.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32n.java @@ -1,33 +1,33 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority; import org.opentripplanner.raptor.api.model.DominanceFunction; -import org.opentripplanner.raptor.api.request.RaptorTransitPriorityGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; /** * 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 TransitPriorityGroup32n { +public class TransitGroupPriority32n { private static final int GROUP_ZERO = 0; private static final int MIN_SEQ_NO = 0; private static final int MAX_SEQ_NO = 32; - public static RaptorTransitPriorityGroupCalculator priorityCalculator() { - return new RaptorTransitPriorityGroupCalculator() { + public static RaptorTransitGroupCalculator priorityCalculator() { + return new RaptorTransitGroupCalculator() { @Override - public int mergeTransitPriorityGroupIds(int currentGroupIds, int boardingGroupId) { + public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { return mergeInGroupId(currentGroupIds, boardingGroupId); } @Override public DominanceFunction dominanceFunction() { - return TransitPriorityGroup32n::dominate; + return TransitGroupPriority32n::dominate; } @Override public String toString() { - return "TransitPriorityGroup32nCalculator{}"; + return "TransitGroupPriority32nCalculator{}"; } }; } @@ -42,7 +42,7 @@ public static boolean dominate(int left, int right) { @Override public String toString() { - return "TransitPriorityGroup32n{}"; + return "TransitGroupPriority32n{}"; } /** @@ -64,12 +64,12 @@ public static int mergeInGroupId(final int currentSetOfGroupIds, final int newGr private static void assertValidGroupSeqNo(int priorityGroupIndex) { if (priorityGroupIndex < MIN_SEQ_NO) { throw new IllegalArgumentException( - "Transit priority group can not be a negative number: " + priorityGroupIndex + "Transit group priority can not be a negative number: " + priorityGroupIndex ); } if (priorityGroupIndex > MAX_SEQ_NO) { throw new IllegalArgumentException( - "Transit priority group exceeds max number of groups: " + + "Transit group priority exceeds max number of groups: " + priorityGroupIndex + " (MAX=" + MAX_SEQ_NO + 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 75c1bcf7214..e5384933750 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 @@ -21,7 +21,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.router.performance.PerformanceTimersForRaptor; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripSchedule; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitPriorityGroup32n; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; import org.opentripplanner.transit.model.site.StopLocation; @@ -117,9 +117,9 @@ private RaptorRequest doMap() { builder.withMultiCriteria(mcBuilder -> { var pt = preferences.transit(); var r = pt.raptor(); - if (!pt.relaxTransitPriorityGroup().isNormal()) { - mcBuilder.withTransitPriorityCalculator(TransitPriorityGroup32n.priorityCalculator()); - mcBuilder.withRelaxC1(mapRelaxCost(pt.relaxTransitPriorityGroup())); + if (!pt.relaxTransitGroupPriority().isNormal()) { + mcBuilder.withTransitPriorityCalculator(TransitGroupPriority32n.priorityCalculator()); + mcBuilder.withRelaxC1(mapRelaxCost(pt.relaxTransitGroupPriority())); } else { mcBuilder.withPassThroughPoints(mapPassThroughPoints()); r.relaxGeneralizedCostAtDestination().ifPresent(mcBuilder::withRelaxCostAtDestination); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java index 25006af49d8..826b9c09a13 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java @@ -8,14 +8,14 @@ import java.util.List; import java.util.stream.Stream; import org.opentripplanner.framework.lang.ArrayUtils; -import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitPriorityGroup32n; -import org.opentripplanner.routing.api.request.request.filter.TransitPriorityGroupSelect; +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.RoutingTripPattern; /** * This class dynamically builds an index of transit-group-ids from the - * provided {@link TransitPriorityGroupSelect}s while serving the caller with + * 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. *

@@ -44,7 +44,7 @@ public class PriorityGroupConfigurator { */ private static final int GROUP_INDEX_COUNTER_START = 1; - private final int baseGroupId = TransitPriorityGroup32n.groupId(GROUP_INDEX_COUNTER_START); + 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; @@ -63,8 +63,8 @@ private PriorityGroupConfigurator() { } private PriorityGroupConfigurator( - Collection byAgency, - Collection global + Collection byAgency, + Collection global ) { this.agencyMatchers = PriorityGroupMatcher.of(byAgency); this.globalMatchers = PriorityGroupMatcher.of(global); @@ -80,8 +80,8 @@ public static PriorityGroupConfigurator empty() { } public static PriorityGroupConfigurator of( - Collection byAgency, - Collection global + Collection byAgency, + Collection global ) { if (Stream.of(byAgency, global).allMatch(Collection::isEmpty)) { return empty(); @@ -94,7 +94,7 @@ public static PriorityGroupConfigurator of( *

* @throws IllegalArgumentException if more than 32 group-ids are requested. */ - public int lookupTransitPriorityGroupId(RoutingTripPattern tripPattern) { + public int lookupTransitGroupPriorityId(RoutingTripPattern tripPattern) { if (!enabled || tripPattern == null) { return baseGroupId; } @@ -128,7 +128,7 @@ public int baseGroupId() { } private int nextGroupId() { - return TransitPriorityGroup32n.groupId(++groupIndexCounter); + return TransitGroupPriority32n.groupId(++groupIndexCounter); } /** Pair of matcher and groupId. Used only inside this class. */ diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcher.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcher.java index 4d8a0475239..c017f2862ab 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcher.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcher.java @@ -15,13 +15,13 @@ import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.opentripplanner.routing.api.request.request.filter.TransitPriorityGroupSelect; +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 TransitPriorityGroupSelect} into a matcher. + * This class turns a {@link TransitGroupSelect} into a matcher. *

* Design: It uses the composite design pattern. A matcher is created for each * value in the "select", then the list of non-empty matchers is merged into @@ -42,7 +42,7 @@ boolean isEmpty() { } }; - public static PriorityGroupMatcher of(TransitPriorityGroupSelect select) { + public static PriorityGroupMatcher of(TransitGroupSelect select) { if (select.isEmpty()) { return NOOP; } @@ -65,7 +65,7 @@ public static PriorityGroupMatcher of(TransitPriorityGroupSelect select) { return andOf(list); } - static PriorityGroupMatcher[] of(Collection selectors) { + static PriorityGroupMatcher[] of(Collection selectors) { return selectors .stream() .map(PriorityGroupMatcher::of) 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 f1212b2f545..4338593d59d 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 @@ -92,7 +92,7 @@ public RaptorRoutingRequestTransitData( additionalPastSearchDays, additionalFutureSearchDays, filter, - createTransitPriorityGroupConfigurator(request) + createTransitGroupPriorityConfigurator(request) ); this.patternIndex = transitDataCreator.createPatternIndex(tripPatterns); this.activeTripPatternsPerStop = transitDataCreator.createTripPatternsPerStop(tripPatterns); @@ -243,8 +243,8 @@ public RaptorConstrainedBoardingSearch transferConstraintsReverseS return new ConstrainedBoardingSearch(false, toStopTransfers, fromStopTransfers); } - private PriorityGroupConfigurator createTransitPriorityGroupConfigurator(RouteRequest request) { - if (request.preferences().transit().relaxTransitPriorityGroup().isNormal()) { + private PriorityGroupConfigurator createTransitGroupPriorityConfigurator(RouteRequest request) { + if (request.preferences().transit().relaxTransitGroupPriority().isNormal()) { return PriorityGroupConfigurator.empty(); } var transitRequest = request.journey().transit(); 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 0cb155facd5..b8f915d6eb4 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 @@ -147,7 +147,7 @@ static List merge( tripPattern.getAlightingPossible(), BoardAlight.ALIGHT ), - priorityGroupConfigurator.lookupTransitPriorityGroupId(tripPattern) + priorityGroupConfigurator.lookupTransitGroupPriorityId(tripPattern) ) ); } diff --git a/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java b/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java index da5c4aad60d..c0a090937d9 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java +++ b/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java @@ -26,7 +26,7 @@ public final class TransitPreferences implements Serializable { private final Map reluctanceForMode; private final Cost otherThanPreferredRoutesPenalty; private final CostLinearFunction unpreferredCost; - private final CostLinearFunction relaxTransitPriorityGroup; + private final CostLinearFunction relaxTransitGroupPriority; private final boolean ignoreRealtimeUpdates; private final boolean includePlannedCancellations; private final boolean includeRealtimeCancellations; @@ -37,7 +37,7 @@ private TransitPreferences() { this.reluctanceForMode = Map.of(); this.otherThanPreferredRoutesPenalty = Cost.costOfMinutes(5); this.unpreferredCost = CostLinearFunction.NORMAL; - this.relaxTransitPriorityGroup = CostLinearFunction.NORMAL; + this.relaxTransitGroupPriority = CostLinearFunction.NORMAL; this.ignoreRealtimeUpdates = false; this.includePlannedCancellations = false; this.includeRealtimeCancellations = false; @@ -50,7 +50,7 @@ private TransitPreferences(Builder builder) { this.reluctanceForMode = Map.copyOf(requireNonNull(builder.reluctanceForMode)); this.otherThanPreferredRoutesPenalty = builder.otherThanPreferredRoutesPenalty; this.unpreferredCost = requireNonNull(builder.unpreferredCost); - this.relaxTransitPriorityGroup = Objects.requireNonNull(builder.relaxTransitPriorityGroup); + this.relaxTransitGroupPriority = Objects.requireNonNull(builder.relaxTransitGroupPriority); this.ignoreRealtimeUpdates = builder.ignoreRealtimeUpdates; this.includePlannedCancellations = builder.includePlannedCancellations; this.includeRealtimeCancellations = builder.includeRealtimeCancellations; @@ -128,13 +128,13 @@ public CostLinearFunction unpreferredCost() { } /** - * This is used to relax the cost when comparing transit-priority-groups. The default is the - * NORMAL function({@code f(x) = x}. This is the same as not using priority-groups. The + * This is used to relax the cost when comparing transit-groups. The default is the + * NORMAL function({@code f(t) = t}. This is the same as not using priority-groups. The * coefficient must be in range {@code [1.0 to 4.0]} and the constant must be in range * {@code [$0 to $1440(4h)]}. */ - public CostLinearFunction relaxTransitPriorityGroup() { - return relaxTransitPriorityGroup; + public CostLinearFunction relaxTransitGroupPriority() { + return relaxTransitGroupPriority; } /** @@ -176,7 +176,7 @@ public boolean equals(Object o) { reluctanceForMode.equals(that.reluctanceForMode) && otherThanPreferredRoutesPenalty == that.otherThanPreferredRoutesPenalty && unpreferredCost.equals(that.unpreferredCost) && - Objects.equals(relaxTransitPriorityGroup, that.relaxTransitPriorityGroup) && + Objects.equals(relaxTransitGroupPriority, that.relaxTransitGroupPriority) && ignoreRealtimeUpdates == that.ignoreRealtimeUpdates && includePlannedCancellations == that.includePlannedCancellations && includeRealtimeCancellations == that.includeRealtimeCancellations && @@ -192,7 +192,7 @@ public int hashCode() { reluctanceForMode, otherThanPreferredRoutesPenalty, unpreferredCost, - relaxTransitPriorityGroup, + relaxTransitGroupPriority, ignoreRealtimeUpdates, includePlannedCancellations, includeRealtimeCancellations, @@ -213,7 +213,7 @@ public String toString() { DEFAULT.otherThanPreferredRoutesPenalty ) .addObj("unpreferredCost", unpreferredCost, DEFAULT.unpreferredCost) - .addObj("relaxTransitPriorityGroup", relaxTransitPriorityGroup, CostLinearFunction.NORMAL) + .addObj("relaxTransitGroupPriority", relaxTransitGroupPriority, CostLinearFunction.NORMAL) .addBoolIfTrue( "ignoreRealtimeUpdates", ignoreRealtimeUpdates != DEFAULT.ignoreRealtimeUpdates @@ -240,7 +240,7 @@ public static class Builder { private Map reluctanceForMode; private Cost otherThanPreferredRoutesPenalty; private CostLinearFunction unpreferredCost; - private CostLinearFunction relaxTransitPriorityGroup; + private CostLinearFunction relaxTransitGroupPriority; private boolean ignoreRealtimeUpdates; private boolean includePlannedCancellations; private boolean includeRealtimeCancellations; @@ -253,7 +253,7 @@ public Builder(TransitPreferences original) { this.reluctanceForMode = original.reluctanceForMode; this.otherThanPreferredRoutesPenalty = original.otherThanPreferredRoutesPenalty; this.unpreferredCost = original.unpreferredCost; - this.relaxTransitPriorityGroup = original.relaxTransitPriorityGroup; + this.relaxTransitGroupPriority = original.relaxTransitGroupPriority; this.ignoreRealtimeUpdates = original.ignoreRealtimeUpdates; this.includePlannedCancellations = original.includePlannedCancellations; this.includeRealtimeCancellations = original.includeRealtimeCancellations; @@ -303,7 +303,7 @@ public Builder setUnpreferredCostString(String constFunction) { } public Builder withTransitGroupPriorityGeneralizedCostSlack(CostLinearFunction value) { - this.relaxTransitPriorityGroup = value; + this.relaxTransitGroupPriority = value; return this; } diff --git a/src/main/java/org/opentripplanner/routing/api/request/request/TransitRequest.java b/src/main/java/org/opentripplanner/routing/api/request/request/TransitRequest.java index 67a56249328..b5626c479a0 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/request/TransitRequest.java +++ b/src/main/java/org/opentripplanner/routing/api/request/request/TransitRequest.java @@ -8,7 +8,7 @@ import org.opentripplanner.routing.api.request.DebugRaptor; import org.opentripplanner.routing.api.request.request.filter.AllowAllTransitFilter; import org.opentripplanner.routing.api.request.request.filter.TransitFilter; -import org.opentripplanner.routing.api.request.request.filter.TransitPriorityGroupSelect; +import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.framework.FeedScopedId; // TODO VIA: Javadoc @@ -31,8 +31,8 @@ public class TransitRequest implements Cloneable, Serializable { private List unpreferredRoutes = List.of(); - private List priorityGroupsByAgency = new ArrayList<>(); - private List priorityGroupsGlobal = new ArrayList<>(); + private List priorityGroupsByAgency = new ArrayList<>(); + private List priorityGroupsGlobal = new ArrayList<>(); private DebugRaptor raptorDebugging = new DebugRaptor(); public void setBannedTripsFromString(String ids) { @@ -64,16 +64,14 @@ public void setFilters(List filters) { *

* Note! Entities that are not matched are put in the BASE-GROUP with id 0. */ - public List priorityGroupsByAgency() { + public List priorityGroupsByAgency() { return priorityGroupsByAgency; } /** * All patterns matching the same select will be assigned the same group-id. */ - public void addPriorityGroupsByAgency( - Collection priorityGroupsByAgency - ) { + public void addPriorityGroupsByAgency(Collection priorityGroupsByAgency) { this.priorityGroupsByAgency.addAll(priorityGroupsByAgency); } @@ -82,11 +80,11 @@ public void addPriorityGroupsByAgency( *

* Note! Entities that are not matched are put in the BASE-GROUP with id 0. */ - public List priorityGroupsGlobal() { + public List priorityGroupsGlobal() { return priorityGroupsGlobal; } - public void addPriorityGroupsGlobal(Collection priorityGroupsGlobal) { + public void addPriorityGroupsGlobal(Collection priorityGroupsGlobal) { this.priorityGroupsGlobal.addAll(priorityGroupsGlobal); } diff --git a/src/main/java/org/opentripplanner/routing/api/request/request/filter/TransitPriorityGroupSelect.java b/src/main/java/org/opentripplanner/routing/api/request/request/filter/TransitGroupSelect.java similarity index 87% rename from src/main/java/org/opentripplanner/routing/api/request/request/filter/TransitPriorityGroupSelect.java rename to src/main/java/org/opentripplanner/routing/api/request/request/filter/TransitGroupSelect.java index 6d763e9c3bc..dfa5daa0e31 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/request/filter/TransitPriorityGroupSelect.java +++ b/src/main/java/org/opentripplanner/routing/api/request/request/filter/TransitGroupSelect.java @@ -21,23 +21,23 @@ *

  • {@code Entity(mode:SUBWAY, agency:A3)}
  • * */ -public class TransitPriorityGroupSelect { +public class TransitGroupSelect { - private static final TransitPriorityGroupSelect DEFAULT = new TransitPriorityGroupSelect(); + private static final TransitGroupSelect DEFAULT = new TransitGroupSelect(); private final List modes; private final List subModeRegexp; private final List agencyIds; private final List routeIds; - public TransitPriorityGroupSelect() { + public TransitGroupSelect() { this.modes = List.of(); this.subModeRegexp = List.of(); this.agencyIds = List.of(); this.routeIds = List.of(); } - private TransitPriorityGroupSelect(Builder builder) { + private TransitGroupSelect(Builder builder) { // Sort and keep only unique entries, this make this // implementation consistent for eq/hc/toString. this.modes = @@ -77,7 +77,7 @@ public boolean isEmpty() { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - TransitPriorityGroupSelect that = (TransitPriorityGroupSelect) o; + TransitGroupSelect that = (TransitGroupSelect) o; return ( Objects.equals(modes, that.modes) && Objects.equals(subModeRegexp, that.subModeRegexp) && @@ -96,7 +96,7 @@ public String toString() { return isEmpty() ? "TransitGroupSelect{ EMPTY }" : ToStringBuilder - .of(TransitPriorityGroupSelect.class) + .of(TransitGroupSelect.class) .addCol("modes", modes) .addCol("subModeRegexp", subModeRegexp) .addCol("agencyIds", agencyIds) @@ -106,13 +106,13 @@ public String toString() { public static class Builder { - private final TransitPriorityGroupSelect original; + private final TransitGroupSelect original; private final List modes; private final List subModeRegexp; private final List agencyIds; private final List routeIds; - public Builder(TransitPriorityGroupSelect original) { + public Builder(TransitGroupSelect original) { this.original = original; this.modes = new ArrayList<>(original.modes); this.subModeRegexp = new ArrayList<>(original.subModeRegexp); @@ -140,8 +140,8 @@ public Builder addRouteIds(Collection routeIds) { return this; } - public TransitPriorityGroupSelect build() { - var obj = new TransitPriorityGroupSelect(this); + public TransitGroupSelect build() { + var obj = new TransitGroupSelect(this); return original.equals(obj) ? original : obj; } } 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 f6a79ea9192..1f3807f46b6 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java @@ -167,7 +167,7 @@ cost function. The cost function (`unpreferredCost`) is defined as a linear func .asFeedScopedIds(request.journey().transit().unpreferredAgencies()) ); - TransitPriorityGroupConfig.mapTransitRequest(c, request.journey().transit()); + TransitGroupPriorityConfig.mapTransitRequest(c, request.journey().transit()); // Map preferences request.withPreferences(preferences -> mapPreferences(c, request, preferences)); @@ -297,25 +297,26 @@ The board time is added to the time when going from the stop (offboard) to onboa .asCostLinearFunction(dft.unpreferredCost()) ); - String relaxTransitPriorityGroupValue = c - .of("relaxTransitPriorityGroup") + String relaxTransitGroupPriorityValue = c + .of("relaxTransitGroupPriority") .since(V2_5) - .summary("The relax function for transit-priority-groups") + .summary("The relax function for transit-group-priority") .description( """ - A path is considered optimal if the generalized-cost is less than the - generalized-cost of another path. If this parameter is set, the comparison is relaxed - further if they belong to different transit-priority-groups. + A path is considered optimal if the generalized-cost is less than the generalized-cost of + another path. If this parameter is set, the comparison is relaxed further if they belong + to different transit groups. """ ) - .asString(dft.relaxTransitPriorityGroup().toString()); + .asString(dft.relaxTransitGroupPriority().toString()); - if (relaxTransitPriorityGroupValue != null) { + if (relaxTransitGroupPriorityValue != null) { builder.withTransitGroupPriorityGeneralizedCostSlack( - CostLinearFunction.of(relaxTransitPriorityGroupValue) + CostLinearFunction.of(relaxTransitGroupPriorityValue) ); } + // TODO REMOVE THIS builder.withRaptor(it -> c .of("relaxTransitSearchGeneralizedCostAtDestination") diff --git a/src/main/java/org/opentripplanner/standalone/config/routerequest/TransitPriorityGroupConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerequest/TransitGroupPriorityConfig.java similarity index 76% rename from src/main/java/org/opentripplanner/standalone/config/routerequest/TransitPriorityGroupConfig.java rename to src/main/java/org/opentripplanner/standalone/config/routerequest/TransitGroupPriorityConfig.java index 51faafc7cbf..e5f1ccd784a 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerequest/TransitPriorityGroupConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerequest/TransitGroupPriorityConfig.java @@ -6,36 +6,36 @@ import java.util.Collection; import java.util.List; import org.opentripplanner.routing.api.request.request.TransitRequest; -import org.opentripplanner.routing.api.request.request.filter.TransitPriorityGroupSelect; +import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.standalone.config.framework.json.OtpVersion; import org.opentripplanner.transit.model.basic.TransitMode; -public class TransitPriorityGroupConfig { +public class TransitGroupPriorityConfig { public static void mapTransitRequest(NodeAdapter root, TransitRequest transit) { var c = root - .of("transitPriorityGroups") + .of("transitGroupPriority") .since(OtpVersion.V2_5) - .summary("Transit priority groups configuration") + .summary( + "Group transit patterns and give each group a mutual advantage in the Raptor search." + ) .description( """ Use this to separate transit patterns into groups. Each group will be given a group-id. A path (multiple legs) will then have a set of group-ids based on the group-id from each leg. Hence, two paths with a different set of group-ids will BOTH be optimal unless the cost is - worse than the relaxation specified in the `relaxTransitPriorityGroup` parameter. This is + worse than the relaxation specified in the `relaxTransitGroupPriority` parameter. This is only available in the TransmodelAPI for now. - Unmatched patterns are put in the BASE priority-group (group id: 0). This group is special. - If a path only have legs in the base group, then that path dominates other paths, but other - paths must be better to make it. + Unmatched patterns are put in the BASE priority-group. """ ) .experimentalFeature() .asObject(); transit.addPriorityGroupsByAgency( - TransitPriorityGroupConfig.mapList( + TransitGroupPriorityConfig.mapList( c, "byAgency", "All groups here are split by agency. For example if you list mode " + @@ -44,7 +44,7 @@ public static void mapTransitRequest(NodeAdapter root, TransitRequest transit) { ) ); transit.addPriorityGroupsGlobal( - TransitPriorityGroupConfig.mapList( + TransitGroupPriorityConfig.mapList( c, "global", "All services matching a 'global' group will get the same group-id. Use this " + @@ -53,7 +53,7 @@ public static void mapTransitRequest(NodeAdapter root, TransitRequest transit) { ); } - private static Collection mapList( + private static Collection mapList( NodeAdapter root, String parameterName, String description @@ -61,13 +61,13 @@ private static Collection mapList( return root .of(parameterName) .since(V2_5) - .summary("Configuration for transit priority groups.") + .summary("List of transit groups.") .description(description + " The max total number of group-ids are 32, so be careful.") - .asObjects(TransitPriorityGroupConfig::mapTransitGroupSelect); + .asObjects(TransitGroupPriorityConfig::mapTransitGroupSelect); } - private static TransitPriorityGroupSelect mapTransitGroupSelect(NodeAdapter c) { - return TransitPriorityGroupSelect + private static TransitGroupSelect mapTransitGroupSelect(NodeAdapter c) { + return TransitGroupSelect .of() .addModes( c diff --git a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql index 349c05f63dd..ab79917b9cc 100644 --- a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql +++ b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql @@ -825,12 +825,12 @@ type QueryType { passThroughPoints: [PassThroughPoint!], """ Relax generalized-cost when comparing trips with a different set of - transit-priority-groups. The groups are set server side for service-journey and + transit-group-priorities. The groups are set server side for service-journey and can not be configured in the API. This mainly helps to return competition neutral - services. Long distance authorities are put in different transit-priority-groups. + services. Long distance authorities are put in different transit-groups. This relaxes the comparison inside the routing engine for each stop-arrival. If two - paths have a different set of transit-priority-groups, then the generalized-cost + paths have a different set of transit-group-priorities, then the generalized-cost comparison is relaxed. The final set of paths are filtered through the normal itinerary-filters. @@ -839,7 +839,7 @@ type QueryType { THIS IS STILL AN EXPERIMENTAL FEATURE - IT MAY CHANGE WITHOUT ANY NOTICE! """ - relaxTransitPriorityGroup: RelaxCostInput = null, + relaxTransitGroupPriority: RelaxCostInput = null, """ Whether non-optimal transit paths at the destination should be returned. Let c be the existing minimum pareto optimal generalized-cost to beat. Then a trip with cost c' is @@ -852,7 +852,7 @@ type QueryType { Values less than 1.0 is not allowed, and values greater than 2.0 are not supported, due to performance reasons. """ - relaxTransitSearchGeneralizedCostAtDestination: Float = null @deprecated(reason : "This is replaced by 'relaxTransitPriorityGroup'."), + relaxTransitSearchGeneralizedCostAtDestination: Float = null @deprecated(reason : "This is replaced by 'relaxTransitGroupPriority'."), """ The length of the search-window in minutes. This parameter is optional. 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 c1db11be779..43351b06eb8 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.api.model.DominanceFunction; import org.opentripplanner.raptor.api.request.RaptorProfile; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; -import org.opentripplanner.raptor.api.request.RaptorTransitPriorityGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; @@ -33,9 +33,9 @@ */ public class K01_TransitPriorityTest { - private static final RaptorTransitPriorityGroupCalculator PRIORITY_GROUP_CALCULATOR = new RaptorTransitPriorityGroupCalculator() { + private static final RaptorTransitGroupCalculator PRIORITY_GROUP_CALCULATOR = new RaptorTransitGroupCalculator() { @Override - public int mergeTransitPriorityGroupIds(int currentGroupIds, int boardingGroupId) { + public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { return currentGroupIds | boardingGroupId; } @@ -51,14 +51,8 @@ public DominanceFunction dominanceFunction() { private static final int GROUP_A = 0x01; private static final int GROUP_B = 0x02; private static final int GROUP_C = 0x04; - private static final int GROUP_AB = PRIORITY_GROUP_CALCULATOR.mergeTransitPriorityGroupIds( - GROUP_A, - GROUP_B - ); - private static final int GROUP_AC = PRIORITY_GROUP_CALCULATOR.mergeTransitPriorityGroupIds( - GROUP_A, - GROUP_C - ); + private static final int GROUP_AB = PRIORITY_GROUP_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_B); + private static final int GROUP_AC = PRIORITY_GROUP_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_C); private static final int C1_SLACK_90s = RaptorCostConverter.toRaptorCost(90); private final TestTransitData data = new TestTransitData(); 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 c7b64cb5b9e..6954ac0d41a 100644 --- a/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java +++ b/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java @@ -8,7 +8,6 @@ import static org.opentripplanner.raptor._data.RaptorTestConstants.STOP_D; import static org.opentripplanner.raptor._data.RaptorTestConstants.STOP_E; import static org.opentripplanner.raptor._data.RaptorTestConstants.STOP_F; -import static org.opentripplanner.raptor._data.RaptorTestConstants.STOP_G; import static org.opentripplanner.raptor._data.RaptorTestConstants.T00_00; import static org.opentripplanner.raptor._data.RaptorTestConstants.T01_00; import static org.opentripplanner.raptor._data.api.PathUtils.pathsToString; @@ -26,7 +25,7 @@ import org.opentripplanner.raptor.api.model.DominanceFunction; import org.opentripplanner.raptor.api.request.RaptorProfile; import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; -import org.opentripplanner.raptor.api.request.RaptorTransitPriorityGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; @@ -37,9 +36,9 @@ */ public class K02_TransitPriorityDestinationTest { - private static final RaptorTransitPriorityGroupCalculator PRIORITY_GROUP_CALCULATOR = new RaptorTransitPriorityGroupCalculator() { + private static final RaptorTransitGroupCalculator PRIORITY_GROUP_CALCULATOR = new RaptorTransitGroupCalculator() { @Override - public int mergeTransitPriorityGroupIds(int currentGroupIds, int boardingGroupId) { + public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { return currentGroupIds | boardingGroupId; } @@ -55,14 +54,8 @@ public DominanceFunction dominanceFunction() { private static final int GROUP_A = 0x01; private static final int GROUP_B = 0x02; private static final int GROUP_C = 0x04; - private static final int GROUP_AB = PRIORITY_GROUP_CALCULATOR.mergeTransitPriorityGroupIds( - GROUP_A, - GROUP_B - ); - private static final int GROUP_AC = PRIORITY_GROUP_CALCULATOR.mergeTransitPriorityGroupIds( - GROUP_A, - GROUP_C - ); + private static final int GROUP_AB = PRIORITY_GROUP_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_B); + private static final int GROUP_AC = PRIORITY_GROUP_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_C); private static final int C1_SLACK_90s = RaptorCostConverter.toRaptorCost(90); private final TestTransitData data = new TestTransitData(); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitPriorityGroup32nTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java similarity index 51% rename from src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitPriorityGroup32nTest.java rename to src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java index 85083a3ee6a..2713a190dbf 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitPriorityGroup32nTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/grouppriority/TransitGroupPriority32nTest.java @@ -6,9 +6,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; -import org.opentripplanner.raptor.api.request.RaptorTransitPriorityGroupCalculator; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; -class TransitPriorityGroup32nTest { +class TransitGroupPriority32nTest { private static final int GROUP_INDEX_0 = 0; private static final int GROUP_INDEX_1 = 1; @@ -16,35 +16,35 @@ class TransitPriorityGroup32nTest { private static final int GROUP_INDEX_30 = 30; private static final int GROUP_INDEX_31 = 31; - private static final int GROUP_0 = TransitPriorityGroup32n.groupId(GROUP_INDEX_0); - private static final int GROUP_1 = TransitPriorityGroup32n.groupId(GROUP_INDEX_1); - private static final int GROUP_2 = TransitPriorityGroup32n.groupId(GROUP_INDEX_2); - private static final int GROUP_30 = TransitPriorityGroup32n.groupId(GROUP_INDEX_30); - private static final int GROUP_31 = TransitPriorityGroup32n.groupId(GROUP_INDEX_31); - private static final RaptorTransitPriorityGroupCalculator subjct = TransitPriorityGroup32n.priorityCalculator(); + 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(); @Test void groupId() { - assertEqualsHex(0x00_00_00_00, TransitPriorityGroup32n.groupId(0)); - assertEqualsHex(0x00_00_00_01, TransitPriorityGroup32n.groupId(1)); - assertEqualsHex(0x00_00_00_02, TransitPriorityGroup32n.groupId(2)); - assertEqualsHex(0x00_00_00_04, TransitPriorityGroup32n.groupId(3)); - assertEqualsHex(0x40_00_00_00, TransitPriorityGroup32n.groupId(31)); - assertEqualsHex(0x80_00_00_00, TransitPriorityGroup32n.groupId(32)); + 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)); - assertThrows(IllegalArgumentException.class, () -> TransitPriorityGroup32n.groupId(-1)); - assertThrows(IllegalArgumentException.class, () -> TransitPriorityGroup32n.groupId(33)); + assertThrows(IllegalArgumentException.class, () -> TransitGroupPriority32n.groupId(-1)); + assertThrows(IllegalArgumentException.class, () -> TransitGroupPriority32n.groupId(33)); } @Test - void mergeTransitPriorityGroupIds() { - assertEqualsHex(GROUP_0, subjct.mergeTransitPriorityGroupIds(GROUP_0, GROUP_0)); - assertEqualsHex(GROUP_1, subjct.mergeTransitPriorityGroupIds(GROUP_1, GROUP_1)); - assertEqualsHex(GROUP_0 | GROUP_1, subjct.mergeTransitPriorityGroupIds(GROUP_0, GROUP_1)); - assertEqualsHex(GROUP_30 | GROUP_31, subjct.mergeTransitPriorityGroupIds(GROUP_30, GROUP_31)); + 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 | GROUP_1 | GROUP_2 | GROUP_30 | GROUP_31, - subjct.mergeTransitPriorityGroupIds(GROUP_0 | GROUP_1 | GROUP_2 | GROUP_30, GROUP_31) + subjct.mergeGroupIds(GROUP_0 | GROUP_1 | GROUP_2 | GROUP_30, GROUP_31) ); } diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java index 777aee5352c..cc4bb09f01e 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java @@ -7,7 +7,7 @@ import java.util.List; import org.junit.jupiter.api.Test; -import org.opentripplanner.routing.api.request.request.filter.TransitPriorityGroupSelect; +import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.network.RoutingTripPattern; import org.opentripplanner.transit.model.site.RegularStop; @@ -69,14 +69,14 @@ class PriorityGroupConfiguratorTest { @Test void emptyConfigurationShouldReturnGroupZero() { var subject = PriorityGroupConfigurator.of(List.of(), List.of()); - assertEquals(subject.baseGroupId(), subject.lookupTransitPriorityGroupId(railR1)); - assertEquals(subject.baseGroupId(), subject.lookupTransitPriorityGroupId(busB2)); - assertEquals(subject.baseGroupId(), subject.lookupTransitPriorityGroupId(null)); + assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(railR1)); + assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(busB2)); + assertEquals(subject.baseGroupId(), subject.lookupTransitGroupPriorityId(null)); } @Test - void lookupTransitPriorityGroupIdByAgency() { - var select = TransitPriorityGroupSelect + void lookupTransitGroupIdByAgency() { + var select = TransitGroupSelect .of() .addModes(List.of(TransitMode.BUS, TransitMode.RAIL)) .build(); @@ -85,12 +85,12 @@ void lookupTransitPriorityGroupIdByAgency() { var subject = PriorityGroupConfigurator.of(List.of(select), List.of()); // Agency groups are indexed (group-id set) at request time - assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitPriorityGroupId(null)); - assertEquals(EXP_GROUP_1, subject.lookupTransitPriorityGroupId(busB2)); - assertEquals(EXP_GROUP_2, subject.lookupTransitPriorityGroupId(railR3)); - assertEquals(EXP_GROUP_3, subject.lookupTransitPriorityGroupId(railR1)); - assertEquals(EXP_GROUP_2, subject.lookupTransitPriorityGroupId(busB3)); - assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitPriorityGroupId(ferryF3)); + assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(null)); + 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)); } @Test @@ -99,17 +99,17 @@ void lookupTransitPriorityGroupIdByGlobalMode() { var subject = PriorityGroupConfigurator.of( List.of(), List.of( - TransitPriorityGroupSelect.of().addModes(List.of(TransitMode.BUS)).build(), - TransitPriorityGroupSelect.of().addModes(List.of(TransitMode.RAIL)).build() + TransitGroupSelect.of().addModes(List.of(TransitMode.BUS)).build(), + TransitGroupSelect.of().addModes(List.of(TransitMode.RAIL)).build() ) ); - assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitPriorityGroupId(null)); - assertEquals(EXP_GROUP_2, subject.lookupTransitPriorityGroupId(railR1)); - assertEquals(EXP_GROUP_1, subject.lookupTransitPriorityGroupId(busB2)); - assertEquals(EXP_GROUP_2, subject.lookupTransitPriorityGroupId(railR3)); - assertEquals(EXP_GROUP_1, subject.lookupTransitPriorityGroupId(busB3)); - assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitPriorityGroupId(ferryF3)); + assertEquals(EXP_GROUP_ID_BASE, subject.lookupTransitGroupPriorityId(null)); + 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)); } private static TestRouteData route( diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcherTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcherTest.java index 880d8bc9b45..91d0142f9ef 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcherTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupMatcherTest.java @@ -6,7 +6,7 @@ import java.util.List; import org.junit.jupiter.api.Test; -import org.opentripplanner.routing.api.request.request.filter.TransitPriorityGroupSelect; +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; @@ -35,7 +35,7 @@ class PriorityGroupMatcherTest { @Test void testMode() { var m = PriorityGroupMatcher.of( - TransitPriorityGroupSelect.of().addModes(List.of(TransitMode.BUS, TransitMode.TRAM)).build() + TransitGroupSelect.of().addModes(List.of(TransitMode.BUS, TransitMode.TRAM)).build() ); assertEquals("Mode(BUS | TRAM)", m.toString()); assertFalse(m.isEmpty()); @@ -47,10 +47,10 @@ void testMode() { @Test void testAgencyIds() { var m1 = PriorityGroupMatcher.of( - TransitPriorityGroupSelect.of().addAgencyIds(List.of(r1agencyId)).build() + TransitGroupSelect.of().addAgencyIds(List.of(r1agencyId)).build() ); var m2 = PriorityGroupMatcher.of( - TransitPriorityGroupSelect.of().addAgencyIds(List.of(r1agencyId, anyId)).build() + TransitGroupSelect.of().addAgencyIds(List.of(r1agencyId, anyId)).build() ); var matchers = List.of(m1, m2); @@ -68,10 +68,10 @@ void testAgencyIds() { @Test void routeIds() { var m1 = PriorityGroupMatcher.of( - TransitPriorityGroupSelect.of().addRouteIds(List.of(r1routeId)).build() + TransitGroupSelect.of().addRouteIds(List.of(r1routeId)).build() ); var m2 = PriorityGroupMatcher.of( - TransitPriorityGroupSelect.of().addRouteIds(List.of(r1routeId, anyId)).build() + TransitGroupSelect.of().addRouteIds(List.of(r1routeId, anyId)).build() ); var matchers = List.of(m1, m2); @@ -89,7 +89,7 @@ void routeIds() { @Test void testSubMode() { var subject = PriorityGroupMatcher.of( - TransitPriorityGroupSelect.of().addSubModeRegexp(List.of(".*local.*")).build() + TransitGroupSelect.of().addSubModeRegexp(List.of(".*local.*")).build() ); assertEquals("SubModeRegexp(.*local.*)", subject.toString()); @@ -103,7 +103,7 @@ void testSubMode() { @Test void testAnd() { var subject = PriorityGroupMatcher.of( - TransitPriorityGroupSelect + TransitGroupSelect .of() .addSubModeRegexp(List.of("express")) .addRouteIds(List.of(r1routeId)) @@ -125,7 +125,7 @@ void testAnd() { @Test void testToString() { var subject = PriorityGroupMatcher.of( - TransitPriorityGroupSelect + TransitGroupSelect .of() .addModes(List.of(TransitMode.BUS, TransitMode.TRAM)) .addAgencyIds(List.of(anyId, r1agencyId)) diff --git a/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java b/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java index 811c4a70b29..eb48de51867 100644 --- a/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java +++ b/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java @@ -77,8 +77,8 @@ void unpreferredCost() { } @Test - void relaxTransitPriorityGroup() { - assertEquals(TRANSIT_GROUP_PRIORITY_RELAX, subject.relaxTransitPriorityGroup()); + void relaxTransitGroupPriority() { + assertEquals(TRANSIT_GROUP_PRIORITY_RELAX, subject.relaxTransitGroupPriority()); } @Test @@ -125,7 +125,7 @@ void testToString() { "reluctanceForMode: {AIRPLANE=2.1}, " + "otherThanPreferredRoutesPenalty: $350, " + "unpreferredCost: 5m + 1.15 t, " + - "relaxTransitPriorityGroup: 5m + 1.50 t, " + + "relaxTransitGroupPriority: 5m + 1.50 t, " + "ignoreRealtimeUpdates, " + "includePlannedCancellations, " + "includeRealtimeCancellations, " + From 4c537c8a2f6ee9d1872d35892a31432972e03168 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Wed, 20 Dec 2023 14:55:33 +0100 Subject: [PATCH 05/86] fix: Properly implement the `relaxTransitGroupPriority` parameter in Transmodel API --- .../common/RequestToPreferencesMapper.java | 4 +- .../preferences/TransitPreferencesMapper.java | 8 +- .../transmodel/model/plan/RelaxCostType.java | 45 +++++++--- .../apis/transmodel/model/plan/TripQuery.java | 2 +- .../graphql/scalar/CostScalarFactory.java | 83 +++++++++++++++++++ .../preference/TransitPreferences.java | 2 +- .../routerequest/RouteRequestConfig.java | 4 +- .../apis/transmodel/schema.graphql | 13 +-- .../model/plan/RelaxCostTypeTest.java | 71 ++++++++++++++++ .../preference/TransitPreferencesTest.java | 2 +- 10 files changed, 209 insertions(+), 25 deletions(-) create mode 100644 src/main/java/org/opentripplanner/framework/graphql/scalar/CostScalarFactory.java create mode 100644 src/test/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostTypeTest.java diff --git a/src/main/java/org/opentripplanner/api/common/RequestToPreferencesMapper.java b/src/main/java/org/opentripplanner/api/common/RequestToPreferencesMapper.java index 6a8b71fe939..5c64d04a5d5 100644 --- a/src/main/java/org/opentripplanner/api/common/RequestToPreferencesMapper.java +++ b/src/main/java/org/opentripplanner/api/common/RequestToPreferencesMapper.java @@ -96,9 +96,7 @@ private BoardAndAlightSlack mapTransit() { setIfNotNull(req.ignoreRealtimeUpdates, tr::setIgnoreRealtimeUpdates); if (req.relaxTransitGroupPriority != null) { - tr.withTransitGroupPriorityGeneralizedCostSlack( - CostLinearFunction.of(req.relaxTransitGroupPriority) - ); + tr.withRelaxTransitGroupPriority(CostLinearFunction.of(req.relaxTransitGroupPriority)); } else { setIfNotNull( req.relaxTransitSearchGeneralizedCostAtDestination, diff --git a/src/main/java/org/opentripplanner/apis/transmodel/mapping/preferences/TransitPreferencesMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/preferences/TransitPreferencesMapper.java index d4bf92b828c..28643bf8199 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/preferences/TransitPreferencesMapper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/mapping/preferences/TransitPreferencesMapper.java @@ -1,8 +1,11 @@ package org.opentripplanner.apis.transmodel.mapping.preferences; import graphql.schema.DataFetchingEnvironment; +import java.util.Map; import org.opentripplanner.apis.transmodel.model.TransportModeSlack; +import org.opentripplanner.apis.transmodel.model.plan.RelaxCostType; import org.opentripplanner.apis.transmodel.support.DataFetcherDecorator; +import org.opentripplanner.routing.api.request.framework.CostLinearFunction; import org.opentripplanner.routing.api.request.preference.TransitPreferences; public class TransitPreferencesMapper { @@ -35,7 +38,10 @@ public static void mapTransitPreferences( callWith.argument("includeRealtimeCancellations", transit::setIncludeRealtimeCancellations); callWith.argument( "relaxTransitGroupPriority", - transit::withTransitGroupPriorityGeneralizedCostSlack + it -> + transit.withRelaxTransitGroupPriority( + RelaxCostType.mapToDomain((Map) it, CostLinearFunction.NORMAL) + ) ); callWith.argument( "relaxTransitSearchGeneralizedCostAtDestination", diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostType.java index 3bd3ed129ef..d3455083695 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostType.java @@ -2,13 +2,15 @@ import graphql.Scalars; import graphql.language.FloatValue; -import graphql.language.IntValue; import graphql.language.ObjectField; import graphql.language.ObjectValue; +import graphql.language.StringValue; import graphql.schema.GraphQLInputObjectField; import graphql.schema.GraphQLInputObjectType; -import graphql.schema.GraphQLList; -import graphql.schema.GraphQLNonNull; +import java.util.Map; +import org.opentripplanner.framework.graphql.scalar.CostScalarFactory; +import org.opentripplanner.framework.model.Cost; +import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; public class RelaxCostType { @@ -26,8 +28,8 @@ public class RelaxCostType { with twice as high cost as another one, is accepted. A `constant=$300` means a "fixed" constant is added to the limit. A `{ratio=1.0, constant=0}` is said to be the NORMAL relaxed cost - the limit is the same as the cost used to calculate the limit. The NORMAL is usually - the default. We can express the RelaxCost as a function `f(x) = constant + ratio * x`. - `f(x)=x` is the NORMAL function. + the default. We can express the RelaxCost as a function `f(t) = constant + ratio * t`. + `f(t)=t` is the NORMAL function. """ ) .field( @@ -44,11 +46,12 @@ public class RelaxCostType { .newInputObjectField() .name(CONSTANT) .description( - "The constant value to add to the limit. Must be a positive number. The unit" + - " is cost-seconds." + "The constant value to add to the limit. Must be a positive number. The value is" + + "equivalent to transit-cost-seconds. Integers is treated as seconds, but you may use " + + "the duration format. Example: '3665 = 'DT1h1m5s' = '1h1m5s'." ) - .defaultValueLiteral(IntValue.of(0)) - .type(new GraphQLList(new GraphQLNonNull(Scalars.GraphQLID))) + .defaultValueProgrammatic("0s") + .type(CostScalarFactory.costScalar()) .build() ) .build(); @@ -63,9 +66,31 @@ public static ObjectValue valueOf(CostLinearFunction value) { ObjectField .newObjectField() .name(CONSTANT) - .value(IntValue.of(value.constant().toSeconds())) + // We only use this to display default value (this is an input type), so using the + // lenient OTP version of duration is ok - it is slightly more readable. + .value(StringValue.of(DurationUtils.durationToStr(value.constant().asDuration()))) .build() ) .build(); } + + public static CostLinearFunction mapToDomain( + Map input, + CostLinearFunction defaultValue + ) { + if (input == null || input.isEmpty()) { + return defaultValue; + } + + double ratio = 1.0; + Cost constant = Cost.ZERO; + + if (input.containsKey(RATIO)) { + ratio = (Double) input.get(RATIO); + } + if (input.containsKey(CONSTANT)) { + constant = (Cost) input.get(CONSTANT); + } + return CostLinearFunction.of(constant, ratio); + } } diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/TripQuery.java b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/TripQuery.java index 52bafc1f28b..410ec0ee74a 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/TripQuery.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/TripQuery.java @@ -291,7 +291,7 @@ public static GraphQLFieldDefinition create( itinerary-filters. - The `ratio` must be greater or equal to 1.0 and less then 1.2. - - The `slack` must be greater or equal to 0 and less then 3600. + - The `constant` must be greater or equal to '0s' and less then '1h'. THIS IS STILL AN EXPERIMENTAL FEATURE - IT MAY CHANGE WITHOUT ANY NOTICE! """.stripIndent() diff --git a/src/main/java/org/opentripplanner/framework/graphql/scalar/CostScalarFactory.java b/src/main/java/org/opentripplanner/framework/graphql/scalar/CostScalarFactory.java new file mode 100644 index 00000000000..6f710328174 --- /dev/null +++ b/src/main/java/org/opentripplanner/framework/graphql/scalar/CostScalarFactory.java @@ -0,0 +1,83 @@ +package org.opentripplanner.framework.graphql.scalar; + +import graphql.GraphQLContext; +import graphql.execution.CoercedVariables; +import graphql.language.StringValue; +import graphql.language.Value; +import graphql.schema.Coercing; +import graphql.schema.CoercingParseLiteralException; +import graphql.schema.CoercingParseValueException; +import graphql.schema.GraphQLScalarType; +import java.util.Locale; +import java.util.NoSuchElementException; +import javax.annotation.Nonnull; +import org.opentripplanner.framework.model.Cost; +import org.opentripplanner.framework.time.DurationUtils; + +public class CostScalarFactory { + + private static final String TYPENAME = "Cost"; + + private static final String DOCUMENTATION = + "A cost value, normally a value of 1 is equivalent to riding transit for 1 second, " + + "but it might not depending on the use-case. Format: 3665 = DT1h1m5s = 1h1m5s"; + + private static final GraphQLScalarType SCALAR_INSTANCE = createCostScalar(); + + private CostScalarFactory() {} + + public static GraphQLScalarType costScalar() { + return SCALAR_INSTANCE; + } + + private static GraphQLScalarType createCostScalar() { + return GraphQLScalarType + .newScalar() + .name(TYPENAME) + .description(DOCUMENTATION) + .coercing(createCoercing()) + .build(); + } + + private static String serializeCost(Cost cost) { + return cost.asDuration().toString(); + } + + private static Cost parseCost(String input) throws CoercingParseValueException { + try { + return Cost.fromDuration(DurationUtils.parseSecondsOrDuration(input).orElseThrow()); + } catch (IllegalArgumentException | NoSuchElementException e) { + throw new CoercingParseValueException(e.getMessage(), e); + } + } + + private static Coercing createCoercing() { + return new Coercing<>() { + @Override + public String serialize(@Nonnull Object result, GraphQLContext c, Locale l) { + return serializeCost((Cost) result); + } + + @Override + public Cost parseValue(Object input, GraphQLContext c, Locale l) + throws CoercingParseValueException { + return parseCost((String) input); + } + + @Override + public Cost parseLiteral(Value input, CoercedVariables v, GraphQLContext c, Locale l) + throws CoercingParseLiteralException { + if (input instanceof StringValue stringValue) { + return parseCost(stringValue.getValue()); + } + return null; + } + + @Override + @Nonnull + public Value valueToLiteral(Object input, GraphQLContext c, Locale l) { + return StringValue.of((String) input); + } + }; + } +} diff --git a/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java b/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java index c0a090937d9..9c54fcb6d5c 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java +++ b/src/main/java/org/opentripplanner/routing/api/request/preference/TransitPreferences.java @@ -302,7 +302,7 @@ public Builder setUnpreferredCostString(String constFunction) { return setUnpreferredCost(CostLinearFunction.of(constFunction)); } - public Builder withTransitGroupPriorityGeneralizedCostSlack(CostLinearFunction value) { + public Builder withRelaxTransitGroupPriority(CostLinearFunction value) { this.relaxTransitGroupPriority = value; return this; } 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 1f3807f46b6..a975848e260 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java @@ -311,9 +311,7 @@ The board time is added to the time when going from the stop (offboard) to onboa .asString(dft.relaxTransitGroupPriority().toString()); if (relaxTransitGroupPriorityValue != null) { - builder.withTransitGroupPriorityGeneralizedCostSlack( - CostLinearFunction.of(relaxTransitGroupPriorityValue) - ); + builder.withRelaxTransitGroupPriority(CostLinearFunction.of(relaxTransitGroupPriorityValue)); } // TODO REMOVE THIS diff --git a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql index ab79917b9cc..b5d1dc00a39 100644 --- a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql +++ b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql @@ -835,7 +835,7 @@ type QueryType { itinerary-filters. - The `ratio` must be greater or equal to 1.0 and less then 1.2. - - The `slack` must be greater or equal to 0 and less then 3600. + - The `constant` must be greater or equal to '0s' and less then '1h'. THIS IS STILL AN EXPERIMENTAL FEATURE - IT MAY CHANGE WITHOUT ANY NOTICE! """ @@ -1867,6 +1867,9 @@ enum WheelchairBoarding { "List of coordinates like: [[60.89, 11.12], [62.56, 12.10]]" scalar Coordinates +"A cost value, normally a value of 1 is equivalent to riding transit for 1 second, but it might not depending on the use-case. Format: 3665 = DT1h1m5s = 1h1m5s" +scalar Cost + "Local date using the ISO 8601 format: `YYYY-MM-DD`. Example: `2020-05-17`." scalar Date @@ -2028,12 +2031,12 @@ This is used to include more results into the result. A `ratio=2.0` means a path with twice as high cost as another one, is accepted. A `constant=$300` means a "fixed" constant is added to the limit. A `{ratio=1.0, constant=0}` is said to be the NORMAL relaxed cost - the limit is the same as the cost used to calculate the limit. The NORMAL is usually -the default. We can express the RelaxCost as a function `f(x) = constant + ratio * x`. -`f(x)=x` is the NORMAL function. +the default. We can express the RelaxCost as a function `f(t) = constant + ratio * t`. +`f(t)=t` is the NORMAL function. """ input RelaxCostInput { - "The constant value to add to the limit. Must be a positive number. The unit is cost-seconds." - constant: [ID!] = 0 + "The constant value to add to the limit. Must be a positive number. The value isequivalent to transit-cost-seconds. Integers is treated as seconds, but you may use the duration format. Example: '3665 = 'DT1h1m5s' = '1h1m5s'." + constant: Cost = "0s" "The factor to multiply with the 'other cost'. Minimum value is 1.0." ratio: Float = 1.0 } diff --git a/src/test/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostTypeTest.java b/src/test/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostTypeTest.java new file mode 100644 index 00000000000..b4da249742c --- /dev/null +++ b/src/test/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostTypeTest.java @@ -0,0 +1,71 @@ +package org.opentripplanner.apis.transmodel.model.plan; + +import static org.junit.jupiter.api.Assertions.*; +import static org.opentripplanner.apis.transmodel.model.plan.RelaxCostType.CONSTANT; +import static org.opentripplanner.apis.transmodel.model.plan.RelaxCostType.RATIO; + +import graphql.language.FloatValue; +import graphql.language.ObjectField; +import graphql.language.ObjectValue; +import graphql.language.StringValue; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.model.Cost; +import org.opentripplanner.routing.api.request.framework.CostLinearFunction; + +class RelaxCostTypeTest { + + @Test + void valueOf() { + assertEquals( + ObjectValue + .newObjectValue() + .objectField(ObjectField.newObjectField().name(RATIO).value(FloatValue.of(1.0)).build()) + .objectField( + ObjectField.newObjectField().name(CONSTANT).value(StringValue.of("0s")).build() + ) + .build() + .toString(), + RelaxCostType.valueOf(CostLinearFunction.NORMAL).toString() + ); + assertEquals( + ObjectValue + .newObjectValue() + .objectField(ObjectField.newObjectField().name(RATIO).value(FloatValue.of(1.3)).build()) + .objectField( + ObjectField.newObjectField().name(CONSTANT).value(StringValue.of("1m7s")).build() + ) + .build() + .toString(), + RelaxCostType.valueOf(CostLinearFunction.of(Cost.costOfSeconds(67), 1.3)).toString() + ); + } + + @Test + void mapToDomain() { + Map input; + + input = Map.of(RATIO, 1.0, CONSTANT, Cost.ZERO); + assertEquals( + CostLinearFunction.NORMAL, + RelaxCostType.mapToDomain(input, CostLinearFunction.ZERO) + ); + + input = Map.of(RATIO, 0.0, CONSTANT, Cost.ZERO); + assertEquals( + CostLinearFunction.ZERO, + RelaxCostType.mapToDomain(input, CostLinearFunction.ZERO) + ); + + input = Map.of(RATIO, 1.7, CONSTANT, Cost.costOfSeconds(3600 + 3 * 60 + 7)); + assertEquals( + CostLinearFunction.of("1h3m7s + 1.7t"), + RelaxCostType.mapToDomain(input, CostLinearFunction.ZERO) + ); + assertEquals( + CostLinearFunction.NORMAL, + RelaxCostType.mapToDomain(null, CostLinearFunction.NORMAL) + ); + assertEquals(CostLinearFunction.ZERO, RelaxCostType.mapToDomain(null, CostLinearFunction.ZERO)); + } +} diff --git a/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java b/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java index eb48de51867..b77a7d9085b 100644 --- a/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java +++ b/src/test/java/org/opentripplanner/routing/api/request/preference/TransitPreferencesTest.java @@ -42,7 +42,7 @@ class TransitPreferencesTest { .setUnpreferredCost(UNPREFERRED_COST) .withBoardSlack(b -> b.withDefault(D45s).with(TransitMode.AIRPLANE, D35m)) .withAlightSlack(b -> b.withDefault(D15s).with(TransitMode.AIRPLANE, D25m)) - .withTransitGroupPriorityGeneralizedCostSlack(TRANSIT_GROUP_PRIORITY_RELAX) + .withRelaxTransitGroupPriority(TRANSIT_GROUP_PRIORITY_RELAX) .setIgnoreRealtimeUpdates(IGNORE_REALTIME_UPDATES) .setIncludePlannedCancellations(INCLUDE_PLANNED_CANCELLATIONS) .setIncludeRealtimeCancellations(INCLUDE_REALTIME_CANCELLATIONS) From ce558ec12dd1a00e695875badae520c31068a1c9 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 21 Dec 2023 16:28:49 +0100 Subject: [PATCH 06/86] refactor: Cleanup transit-group-priority module-tests --- .../moduletests/K01_TransitPriorityTest.java | 44 +++------------- .../K02_TransitPriorityDestinationTest.java | 44 +++------------- .../support/TestGroupPriorityCalculator.java | 51 +++++++++++++++++++ 3 files changed, 63 insertions(+), 76 deletions(-) create mode 100644 src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java 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 43351b06eb8..1795663aaba 100644 --- a/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java +++ b/src/test/java/org/opentripplanner/raptor/moduletests/K01_TransitPriorityTest.java @@ -12,6 +12,9 @@ import static org.opentripplanner.raptor._data.transit.TestRoute.route; import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; +import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_A; +import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_B; +import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_C; import java.time.Duration; import org.junit.jupiter.api.BeforeEach; @@ -24,6 +27,7 @@ import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; import org.opentripplanner.raptor.configure.RaptorConfig; +import org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; /** @@ -33,26 +37,8 @@ */ public class K01_TransitPriorityTest { - private static final RaptorTransitGroupCalculator PRIORITY_GROUP_CALCULATOR = new RaptorTransitGroupCalculator() { - @Override - public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { - return currentGroupIds | boardingGroupId; - } - - /** - * Left dominate right, if right has at least one priority group not in left. - */ - @Override - public DominanceFunction dominanceFunction() { - return (l, r) -> ((l ^ r) & r) != 0; - } - }; - - private static final int GROUP_A = 0x01; - private static final int GROUP_B = 0x02; - private static final int GROUP_C = 0x04; - private static final int GROUP_AB = PRIORITY_GROUP_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_B); - private static final int GROUP_AC = PRIORITY_GROUP_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_C); + private static final RaptorTransitGroupCalculator PRIORITY_GROUP_CALCULATOR = + TestGroupPriorityCalculator.PRIORITY_CALCULATOR; private static final int C1_SLACK_90s = RaptorCostConverter.toRaptorCost(90); private final TestTransitData data = new TestTransitData(); @@ -102,7 +88,6 @@ private void prepareRequest() { ); // Add 1 second access/egress paths requestBuilder.searchParams().addAccessPaths(walk(STOP_B, 1)).addEgressPaths(walk(STOP_C, 1)); - assetGroupCalculatorIsSetupCorrect(); } @Test @@ -116,21 +101,4 @@ public void transitPriority() { pathsToString(raptorService.route(requestBuilder.build(), data)) ); } - - /** - * Make sure the calculator and group setup is done correct. - */ - void assetGroupCalculatorIsSetupCorrect() { - var d = PRIORITY_GROUP_CALCULATOR.dominanceFunction(); - - assertTrue(d.leftDominateRight(GROUP_A, GROUP_B)); - assertTrue(d.leftDominateRight(GROUP_B, GROUP_A)); - assertFalse(d.leftDominateRight(GROUP_A, GROUP_A)); - // 3 = 1&2, 5 = 1&4 - assertTrue(d.leftDominateRight(GROUP_A, GROUP_AB)); - assertFalse(d.leftDominateRight(GROUP_AB, GROUP_A)); - assertFalse(d.leftDominateRight(GROUP_AB, GROUP_AB)); - assertTrue(d.leftDominateRight(GROUP_AB, GROUP_AC)); - assertTrue(d.leftDominateRight(GROUP_AC, GROUP_AB)); - } } 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 6954ac0d41a..a2185acef5e 100644 --- a/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java +++ b/src/test/java/org/opentripplanner/raptor/moduletests/K02_TransitPriorityDestinationTest.java @@ -15,6 +15,9 @@ import static org.opentripplanner.raptor._data.transit.TestRoute.route; import static org.opentripplanner.raptor._data.transit.TestTripPattern.pattern; import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; +import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_A; +import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_B; +import static org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator.GROUP_C; import java.time.Duration; import org.junit.jupiter.api.BeforeEach; @@ -27,6 +30,7 @@ import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; import org.opentripplanner.raptor.configure.RaptorConfig; +import org.opentripplanner.raptor.moduletests.support.TestGroupPriorityCalculator; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; /** @@ -36,26 +40,8 @@ */ public class K02_TransitPriorityDestinationTest { - private static final RaptorTransitGroupCalculator PRIORITY_GROUP_CALCULATOR = new RaptorTransitGroupCalculator() { - @Override - public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { - return currentGroupIds | boardingGroupId; - } - - /** - * Left dominate right, if right has at least one priority group not in left. - */ - @Override - public DominanceFunction dominanceFunction() { - return (l, r) -> ((l ^ r) & r) != 0; - } - }; - - private static final int GROUP_A = 0x01; - private static final int GROUP_B = 0x02; - private static final int GROUP_C = 0x04; - private static final int GROUP_AB = PRIORITY_GROUP_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_B); - private static final int GROUP_AC = PRIORITY_GROUP_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_C); + private static final RaptorTransitGroupCalculator PRIORITY_GROUP_CALCULATOR = + TestGroupPriorityCalculator.PRIORITY_CALCULATOR; private static final int C1_SLACK_90s = RaptorCostConverter.toRaptorCost(90); private final TestTransitData data = new TestTransitData(); @@ -110,7 +96,6 @@ private void prepareRequest() { .addEgressPaths(walk(STOP_D, 1)) .addEgressPaths(walk(STOP_E, 1)) .addEgressPaths(walk(STOP_F, 1)); - assetGroupCalculatorIsSetupCorrect(); } @Test @@ -124,21 +109,4 @@ public void transitPriority() { pathsToString(raptorService.route(requestBuilder.build(), data)) ); } - - /** - * Make sure the calculator and group setup is done correct. - */ - void assetGroupCalculatorIsSetupCorrect() { - var d = PRIORITY_GROUP_CALCULATOR.dominanceFunction(); - - assertTrue(d.leftDominateRight(GROUP_A, GROUP_B)); - assertTrue(d.leftDominateRight(GROUP_B, GROUP_A)); - assertFalse(d.leftDominateRight(GROUP_A, GROUP_A)); - // 3 = 1&2, 5 = 1&4 - assertTrue(d.leftDominateRight(GROUP_A, GROUP_AB)); - assertFalse(d.leftDominateRight(GROUP_AB, GROUP_A)); - assertFalse(d.leftDominateRight(GROUP_AB, GROUP_AB)); - assertTrue(d.leftDominateRight(GROUP_AB, GROUP_AC)); - assertTrue(d.leftDominateRight(GROUP_AC, GROUP_AB)); - } } diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java b/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java new file mode 100644 index 00000000000..cdbe82f18a6 --- /dev/null +++ b/src/test/java/org/opentripplanner/raptor/moduletests/support/TestGroupPriorityCalculator.java @@ -0,0 +1,51 @@ +package org.opentripplanner.raptor.moduletests.support; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.raptor.api.model.DominanceFunction; +import org.opentripplanner.raptor.api.request.RaptorTransitGroupCalculator; + +public class TestGroupPriorityCalculator implements RaptorTransitGroupCalculator { + + public static final RaptorTransitGroupCalculator PRIORITY_CALCULATOR = new TestGroupPriorityCalculator(); + + public static final int GROUP_A = 0x01; + public static final int GROUP_B = 0x02; + public static final int GROUP_C = 0x04; + + private static final int GROUP_AB = PRIORITY_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_B); + private static final int GROUP_AC = PRIORITY_CALCULATOR.mergeGroupIds(GROUP_A, GROUP_C); + + @Override + public int mergeGroupIds(int currentGroupIds, int boardingGroupId) { + return currentGroupIds | boardingGroupId; + } + + /** + * Left dominate right, if right has at least one priority group not in left. + */ + @Override + public DominanceFunction dominanceFunction() { + return (l, r) -> ((l ^ r) & r) != 0; + } + + /** + * Make sure the calculator and group setup is done correct. + */ + @Test + void assetGroupCalculatorIsSetupCorrect() { + var d = PRIORITY_CALCULATOR.dominanceFunction(); + + assertTrue(d.leftDominateRight(GROUP_A, GROUP_B)); + assertTrue(d.leftDominateRight(GROUP_B, GROUP_A)); + assertFalse(d.leftDominateRight(GROUP_A, GROUP_A)); + // 3 = 1&2, 5 = 1&4 + assertTrue(d.leftDominateRight(GROUP_A, GROUP_AB)); + assertFalse(d.leftDominateRight(GROUP_AB, GROUP_A)); + assertFalse(d.leftDominateRight(GROUP_AB, GROUP_AB)); + assertTrue(d.leftDominateRight(GROUP_AB, GROUP_AC)); + assertTrue(d.leftDominateRight(GROUP_AC, GROUP_AB)); + } +} From 2218c1af12110e1d84730f25a76870cf94e856ec Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 19 Dec 2023 18:52:42 +0100 Subject: [PATCH 07/86] Add new debug vector tiles --- .../api/configuration/APIEndpoints.java | 2 +- .../GraphInspectorVectorTileResource.java | 58 +++++++++++++++--- .../apis/vectortiles/MapboxStyleJson.java | 53 ++++++++++++++++ .../vector/RegularStopsLayerBuilder.java | 60 +++++++++++++++++++ 4 files changed, 164 insertions(+), 9 deletions(-) rename src/main/java/org/opentripplanner/{api/resource => apis/vectortiles}/GraphInspectorVectorTileResource.java (72%) create mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/MapboxStyleJson.java create mode 100644 src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java diff --git a/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java b/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java index b0d38aa00cd..64bbb2896a2 100644 --- a/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java +++ b/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java @@ -19,13 +19,13 @@ import java.util.List; import org.opentripplanner.api.resource.BikeRental; import org.opentripplanner.api.resource.GraphInspectorTileResource; -import org.opentripplanner.api.resource.GraphInspectorVectorTileResource; import org.opentripplanner.api.resource.PlannerResource; import org.opentripplanner.api.resource.Routers; import org.opentripplanner.api.resource.ServerInfo; import org.opentripplanner.api.resource.UpdaterStatusResource; import org.opentripplanner.apis.gtfs.GtfsGraphQLAPI; import org.opentripplanner.apis.transmodel.TransmodelAPI; +import org.opentripplanner.apis.vectortiles.GraphInspectorVectorTileResource; import org.opentripplanner.ext.actuator.ActuatorAPI; import org.opentripplanner.ext.geocoder.GeocoderResource; import org.opentripplanner.ext.parkAndRideApi.ParkAndRideResource; diff --git a/src/main/java/org/opentripplanner/api/resource/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java similarity index 72% rename from src/main/java/org/opentripplanner/api/resource/GraphInspectorVectorTileResource.java rename to src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index 2c21e0396ce..04d2630a301 100644 --- a/src/main/java/org/opentripplanner/api/resource/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -1,4 +1,4 @@ -package org.opentripplanner.api.resource; +package org.opentripplanner.apis.vectortiles; import static org.opentripplanner.framework.io.HttpUtils.APPLICATION_X_PROTOBUF; @@ -14,13 +14,20 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.function.Predicate; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; import org.glassfish.grizzly.http.server.Request; import org.opentripplanner.api.model.TileJson; +import org.opentripplanner.apis.vectortiles.MapboxStyleJson.LayerStyleBuilder; +import org.opentripplanner.apis.vectortiles.MapboxStyleJson.VectorTileSource; +import org.opentripplanner.framework.io.HttpUtils; import org.opentripplanner.inspector.vector.AreaStopsLayerBuilder; import org.opentripplanner.inspector.vector.LayerBuilder; import org.opentripplanner.inspector.vector.LayerParameters; +import org.opentripplanner.inspector.vector.RegularStopsLayerBuilder; import org.opentripplanner.inspector.vector.VectorTileResponseFactory; import org.opentripplanner.inspector.vector.geofencing.GeofencingZonesLayerBuilder; import org.opentripplanner.model.FeedInfo; @@ -34,6 +41,7 @@ public class GraphInspectorVectorTileResource { private static final List> DEBUG_LAYERS = List.of( + new LayerParams("regularStops", LayerType.RegularStop), new LayerParams("areaStops", LayerType.AreaStop), new LayerParams("geofencingZones", LayerType.GeofencingZones) ); @@ -84,13 +92,7 @@ public TileJson getTileJson( @PathParam("layers") String requestedLayers ) { var envelope = serverContext.worldEnvelopeService().envelope().orElseThrow(); - List feedInfos = serverContext - .transitService() - .getFeedIds() - .stream() - .map(serverContext.transitService()::getFeedInfo) - .filter(Predicate.not(Objects::isNull)) - .toList(); + List feedInfos = feedInfos(); return new TileJson( uri, @@ -103,18 +105,58 @@ public TileJson getTileJson( ); } + @GET + @Path("/style.json") + @Produces(MediaType.APPLICATION_JSON) + public MapboxStyleJson getTileJson(@Context UriInfo uri, @Context HttpHeaders headers) { + var base = HttpUtils.getBaseAddress(uri, headers); + final String allLayers = DEBUG_LAYERS + .stream() + .map(LayerParameters::name) + .collect(Collectors.joining(",")); + var url = + base + + "/otp/routers/" + + ignoreRouterId + + "/inspector/vectortile/" + + allLayers + + "/tilejson.json"; + return new MapboxStyleJson( + "OTP Debug Tiles", + Map.of("debug", new VectorTileSource("vector", url)), + List.of(LayerStyleBuilder.ofId("regular-stop").source("regularStops").circleColor("#f73109").build()) + ); + } + + @Nonnull + private List feedInfos() { + return serverContext + .transitService() + .getFeedIds() + .stream() + .map(serverContext.transitService()::getFeedInfo) + .filter(Predicate.not(Objects::isNull)) + .toList(); + } + private static LayerBuilder createLayerBuilder( LayerParameters layerParameters, Locale locale, OtpServerRequestContext context ) { return switch (layerParameters.type()) { + case RegularStop -> new RegularStopsLayerBuilder( + context.transitService(), + layerParameters, + locale + ); case AreaStop -> new AreaStopsLayerBuilder(context.transitService(), layerParameters, locale); case GeofencingZones -> new GeofencingZonesLayerBuilder(context.graph(), layerParameters); }; } private enum LayerType { + RegularStop, AreaStop, GeofencingZones, } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/MapboxStyleJson.java b/src/main/java/org/opentripplanner/apis/vectortiles/MapboxStyleJson.java new file mode 100644 index 00000000000..72f92a9c02e --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/vectortiles/MapboxStyleJson.java @@ -0,0 +1,53 @@ +package org.opentripplanner.apis.vectortiles; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.opentripplanner.framework.json.ObjectMappers; + +public record MapboxStyleJson( + String name, + Map sources, + List layers +) { + public record VectorTileSource(String type, String url) {} + + public static class LayerStyleBuilder { + + private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); + private final Map props = new HashMap<>(); + private final Map paint = new HashMap<>(); + + public static LayerStyleBuilder ofId(String id) { + return new LayerStyleBuilder(id); + } + + private LayerStyleBuilder(String id) { + props.put("id", id); + } + + /** + * Which vector tile source this should apply to. + */ + public LayerStyleBuilder source(String source) { + props.put("source", source); + return this; + } + + public LayerStyleBuilder circleColor(String color) { + paint.put("circle-color", color); + return this; + } + + public JsonNode build() { + var copy = new HashMap<>(props); + if(!paint.isEmpty()) { + copy.put("paint", paint); + } + return OBJECT_MAPPER.valueToTree(copy); + } + + } +} diff --git a/src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java new file mode 100644 index 00000000000..c4bf0d88a23 --- /dev/null +++ b/src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java @@ -0,0 +1,60 @@ +package org.opentripplanner.inspector.vector; + +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.opentripplanner.apis.common.mapping.PropertyMapper; +import org.opentripplanner.transit.model.site.AreaStop; +import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.service.TransitService; + +/** + * A vector tile layer containing all {@link RegularStop}s inside the vector tile bounds. + */ +public class RegularStopsLayerBuilder extends LayerBuilder { + + private static final Map mappers = Map.of( + MapperType.DebugClient, + DebugClientAreaStopPropertyMapper::create + ); + private final Function> findAreaStops; + + public RegularStopsLayerBuilder( + TransitService transitService, + LayerParameters layerParameters, + Locale locale + ) { + super( + mappers.get(MapperType.valueOf(layerParameters.mapper())).build(transitService, locale), + layerParameters.name(), + layerParameters.expansionFactor() + ); + this.findAreaStops = transitService::findRegularStop; + } + + @Override + protected List getGeometries(Envelope query) { + return findAreaStops + .apply(query) + .stream() + .map(stop -> { + Geometry geometry = stop.getGeometry().copy(); + geometry.setUserData(stop); + return geometry; + }) + .toList(); + } + + enum MapperType { + DebugClient, + } + + @FunctionalInterface + private interface MapperFactory { + PropertyMapper build(TransitService transitService, Locale locale); + } +} From 176187c5dba4a735fbd78171d98c5dda0c29c1cb Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 19 Dec 2023 18:48:30 +0100 Subject: [PATCH 08/86] Add stop layer to tile.json --- .../GraphInspectorVectorTileResource.java | 40 ++++++- .../apis/vectortiles/MapboxStyleJson.java | 53 --------- .../vectortiles/model/LayerStyleBuilder.java | 105 ++++++++++++++++++ .../vectortiles/model/MapboxStyleJson.java | 46 ++++++++ .../apis/vectortiles/model/TileSource.java | 25 +++++ .../vector/AreaStopsLayerBuilder.java | 10 +- .../DebugClientAreaStopPropertyMapper.java | 14 +-- .../vector/RegularStopsLayerBuilder.java | 20 +--- .../vector/AreaStopLayerBuilderTest.java | 5 +- 9 files changed, 225 insertions(+), 93 deletions(-) delete mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/MapboxStyleJson.java create mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java create mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java create mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index 04d2630a301..8df1fd9e388 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -14,15 +14,17 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.glassfish.grizzly.http.server.Request; import org.opentripplanner.api.model.TileJson; -import org.opentripplanner.apis.vectortiles.MapboxStyleJson.LayerStyleBuilder; -import org.opentripplanner.apis.vectortiles.MapboxStyleJson.VectorTileSource; +import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder; +import org.opentripplanner.apis.vectortiles.model.MapboxStyleJson; +import org.opentripplanner.apis.vectortiles.model.TileSource; +import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource; +import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; import org.opentripplanner.framework.io.HttpUtils; import org.opentripplanner.inspector.vector.AreaStopsLayerBuilder; import org.opentripplanner.inspector.vector.LayerBuilder; @@ -121,10 +123,38 @@ public MapboxStyleJson getTileJson(@Context UriInfo uri, @Context HttpHeaders he "/inspector/vectortile/" + allLayers + "/tilejson.json"; + var vectorSource = new VectorSource("debug", url); + var backgroundSource = new RasterSource( + "background", + List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"), + 256 + ); + List sources = List.of( + backgroundSource, + vectorSource + ); return new MapboxStyleJson( "OTP Debug Tiles", - Map.of("debug", new VectorTileSource("vector", url)), - List.of(LayerStyleBuilder.ofId("regular-stop").source("regularStops").circleColor("#f73109").build()) + sources, + List.of( + LayerStyleBuilder + .ofId("background") + .typeRaster() + .source(backgroundSource) + .minZoom(0) + .maxZoom(22) + .build(), + LayerStyleBuilder + .ofId("regular-stop") + .source(vectorSource) + .sourceLayer("regularStops") + .typeCircle() + .circleStroke("#140d0e", 1) + .circleColor("#fcf9fa") + .minZoom(13) + .maxZoom(22) + .build() + ) ); } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/MapboxStyleJson.java b/src/main/java/org/opentripplanner/apis/vectortiles/MapboxStyleJson.java deleted file mode 100644 index 72f92a9c02e..00000000000 --- a/src/main/java/org/opentripplanner/apis/vectortiles/MapboxStyleJson.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.opentripplanner.apis.vectortiles; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.opentripplanner.framework.json.ObjectMappers; - -public record MapboxStyleJson( - String name, - Map sources, - List layers -) { - public record VectorTileSource(String type, String url) {} - - public static class LayerStyleBuilder { - - private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); - private final Map props = new HashMap<>(); - private final Map paint = new HashMap<>(); - - public static LayerStyleBuilder ofId(String id) { - return new LayerStyleBuilder(id); - } - - private LayerStyleBuilder(String id) { - props.put("id", id); - } - - /** - * Which vector tile source this should apply to. - */ - public LayerStyleBuilder source(String source) { - props.put("source", source); - return this; - } - - public LayerStyleBuilder circleColor(String color) { - paint.put("circle-color", color); - return this; - } - - public JsonNode build() { - var copy = new HashMap<>(props); - if(!paint.isEmpty()) { - copy.put("paint", paint); - } - return OBJECT_MAPPER.valueToTree(copy); - } - - } -} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java new file mode 100644 index 00000000000..65a009ec53e --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java @@ -0,0 +1,105 @@ +package org.opentripplanner.apis.vectortiles.model; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; +import org.opentripplanner.framework.json.ObjectMappers; + +/** + * Builds a Maplibre/Mapbox vector tile + * layer style. + */ +public class LayerStyleBuilder { + + private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); + private static final String TYPE = "type"; + private static final String SOURCE_LAYER = "source-layer"; + private final Map props = new HashMap<>(); + private final Map paint = new HashMap<>(); + + public static LayerStyleBuilder ofId(String id) { + return new LayerStyleBuilder(id); + } + + public enum LayerType { + Circle, + Raster, + } + + private LayerStyleBuilder(String id) { + props.put("id", id); + } + + public LayerStyleBuilder minZoom(int i) { + props.put("minzoom", i); + return this; + } + + public LayerStyleBuilder maxZoom(int i) { + props.put("maxzoom", i); + return this; + } + + /** + * Which vector tile source this should apply to. + */ + public LayerStyleBuilder source(TileSource source) { + props.put("source", source.id()); + return this; + } + + public LayerStyleBuilder sourceLayer(String source) { + props.put(SOURCE_LAYER, source); + return this; + } + + public LayerStyleBuilder typeRaster() { + return type(LayerType.Raster); + } + + public LayerStyleBuilder typeCircle() { + return type(LayerType.Circle); + } + + private LayerStyleBuilder type(LayerType type) { + props.put(TYPE, type.name().toLowerCase()); + return this; + } + + public LayerStyleBuilder circleColor(String color) { + paint.put("circle-color", validateColor(color)); + return this; + } + + public LayerStyleBuilder circleStroke(String color, int width) { + paint.put("circle-stroke-color", validateColor(color)); + paint.put("circle-stroke-width", width); + return this; + } + + public JsonNode build() { + validate(); + + var copy = new HashMap<>(props); + if (!paint.isEmpty()) { + copy.put("paint", paint); + } + return OBJECT_MAPPER.valueToTree(copy); + } + + private String validateColor(String color) { + if (!color.startsWith("#")) { + throw new IllegalArgumentException("Colors must start with '#'"); + } + return color; + } + + private void validate() { + Stream + .of(TYPE) + .forEach(p -> Objects.requireNonNull(props.get(p), "%s must be set".formatted(p))); + } +} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java new file mode 100644 index 00000000000..bf84b697b92 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java @@ -0,0 +1,46 @@ +package org.opentripplanner.apis.vectortiles.model; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class MapboxStyleJson { + + private final String name; + private final List sources; + private final List layers; + + public MapboxStyleJson( + String name, + List sources, + List layers + ) { + this.name = name; + this.sources = sources; + this.layers = layers; + } + + @JsonSerialize + public int version() { + return 8; + } + + @JsonSerialize + public String name() { + return name; + } + + @JsonSerialize + public Map sources() { + var output = new HashMap(); + sources.forEach(s -> output.put(s.id(), s)); + return output; + } + + @JsonSerialize + public List layers() { + return layers; + } +} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java new file mode 100644 index 00000000000..2e404d9a1eb --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java @@ -0,0 +1,25 @@ +package org.opentripplanner.apis.vectortiles.model; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.util.List; + +public sealed interface TileSource { + @JsonSerialize + String type(); + + String id(); + + record VectorSource(String id, String url) implements TileSource { + @Override + public String type() { + return "vector"; + } + } + + record RasterSource(String id, List tiles, int tileSize) implements TileSource { + @Override + public String type() { + return "raster"; + } + } +} diff --git a/src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java index 1604cf7d8d1..6ce7b0962ba 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java +++ b/src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java @@ -3,23 +3,19 @@ import java.util.Collection; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.function.Function; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.api.mapping.PropertyMapper; import org.opentripplanner.transit.model.site.AreaStop; +import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.service.TransitService; /** * A vector tile layer containing all {@link AreaStop}s inside the vector tile bounds. */ -public class AreaStopsLayerBuilder extends LayerBuilder { +public class AreaStopsLayerBuilder extends LayerBuilder { - private static final Map mappers = Map.of( - MapperType.DebugClient, - DebugClientAreaStopPropertyMapper::create - ); private final Function> findAreaStops; public AreaStopsLayerBuilder( @@ -28,7 +24,7 @@ public AreaStopsLayerBuilder( Locale locale ) { super( - mappers.get(MapperType.valueOf(layerParameters.mapper())).build(transitService, locale), + new DebugClientAreaStopPropertyMapper(locale), layerParameters.name(), layerParameters.expansionFactor() ); diff --git a/src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java index 88f7a17385b..1d507140378 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java @@ -5,29 +5,29 @@ import java.util.Locale; import org.opentripplanner.api.mapping.I18NStringMapper; import org.opentripplanner.api.mapping.PropertyMapper; -import org.opentripplanner.transit.model.site.AreaStop; +import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.service.TransitService; /** * A {@link PropertyMapper} for the {@link AreaStopsLayerBuilder} for the OTP debug client. */ -public class DebugClientAreaStopPropertyMapper extends PropertyMapper { +public class DebugClientAreaStopPropertyMapper extends PropertyMapper { private final I18NStringMapper i18NStringMapper; - public DebugClientAreaStopPropertyMapper(TransitService transitService, Locale locale) { + public DebugClientAreaStopPropertyMapper(Locale locale) { this.i18NStringMapper = new I18NStringMapper(locale); } - public static PropertyMapper create(TransitService transitService, Locale locale) { - return new DebugClientAreaStopPropertyMapper(transitService, locale); + public static PropertyMapper create(TransitService ignored, Locale locale) { + return new DebugClientAreaStopPropertyMapper(locale); } @Override - protected Collection map(AreaStop input) { + protected Collection map(StopLocation input) { return List.of( new KeyValue("id", input.getId().toString()), - new KeyValue("name", i18NStringMapper.mapNonnullToApi(input.getName())) + new KeyValue("name", i18NStringMapper.mapToApi(input.getName())) ); } } diff --git a/src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java index c4bf0d88a23..26e3f8726ae 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java +++ b/src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java @@ -3,24 +3,18 @@ import java.util.Collection; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.function.Function; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.apis.common.mapping.PropertyMapper; -import org.opentripplanner.transit.model.site.AreaStop; import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.service.TransitService; /** * A vector tile layer containing all {@link RegularStop}s inside the vector tile bounds. */ -public class RegularStopsLayerBuilder extends LayerBuilder { +public class RegularStopsLayerBuilder extends LayerBuilder { - private static final Map mappers = Map.of( - MapperType.DebugClient, - DebugClientAreaStopPropertyMapper::create - ); private final Function> findAreaStops; public RegularStopsLayerBuilder( @@ -29,7 +23,7 @@ public RegularStopsLayerBuilder( Locale locale ) { super( - mappers.get(MapperType.valueOf(layerParameters.mapper())).build(transitService, locale), + new DebugClientAreaStopPropertyMapper(locale), layerParameters.name(), layerParameters.expansionFactor() ); @@ -49,12 +43,4 @@ protected List getGeometries(Envelope query) { .toList(); } - enum MapperType { - DebugClient, - } - - @FunctionalInterface - private interface MapperFactory { - PropertyMapper build(TransitService transitService, Locale locale); - } } diff --git a/src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java b/src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java index 09b64adcf75..6f2ca872f21 100644 --- a/src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java +++ b/src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java @@ -38,10 +38,7 @@ class AreaStopLayerBuilderTest { @Test void map() { - var subject = new DebugClientAreaStopPropertyMapper( - new DefaultTransitService(new TransitModel()), - Locale.ENGLISH - ); + var subject = new DebugClientAreaStopPropertyMapper(Locale.ENGLISH); var properties = subject.map(areaStop); From 7aaf79ff43d1421786ac5c0e4c8297d783b7b06b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 19 Dec 2023 23:41:36 +0100 Subject: [PATCH 09/86] Move classes into their own packages --- .../GraphInspectorVectorTileResource.java | 9 ++--- .../vectortiles/model/LayerStyleBuilder.java | 5 +++ .../vectortiles/model/MapboxStyleJson.java | 6 +-- .../opentripplanner/astar/model/BinHeap.java | 12 ------ .../vector/edge/EdgeLayerBuilder.java | 40 +++++++++++++++++++ .../vector/edge/EdgePropertyMapper.java | 18 +++++++++ .../GeofencingZonesLayerBuilder.java | 17 +------- .../{ => stop}/AreaStopsLayerBuilder.java | 18 ++------- .../{ => stop}/RegularStopsLayerBuilder.java | 7 ++-- .../StopLocationPropertyMapper.java} | 9 +++-- .../{ => stop}/AreaStopLayerBuilderTest.java | 7 ++-- 11 files changed, 84 insertions(+), 64 deletions(-) create mode 100644 src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java create mode 100644 src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java rename src/main/java/org/opentripplanner/inspector/vector/{ => stop}/AreaStopsLayerBuilder.java (77%) rename src/main/java/org/opentripplanner/inspector/vector/{ => stop}/RegularStopsLayerBuilder.java (84%) rename src/main/java/org/opentripplanner/inspector/vector/{DebugClientAreaStopPropertyMapper.java => stop/StopLocationPropertyMapper.java} (74%) rename src/test/java/org/opentripplanner/inspector/vector/{ => stop}/AreaStopLayerBuilderTest.java (86%) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index 8df1fd9e388..9d083bd5e03 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -26,12 +26,12 @@ import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource; import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; import org.opentripplanner.framework.io.HttpUtils; -import org.opentripplanner.inspector.vector.AreaStopsLayerBuilder; import org.opentripplanner.inspector.vector.LayerBuilder; import org.opentripplanner.inspector.vector.LayerParameters; -import org.opentripplanner.inspector.vector.RegularStopsLayerBuilder; import org.opentripplanner.inspector.vector.VectorTileResponseFactory; import org.opentripplanner.inspector.vector.geofencing.GeofencingZonesLayerBuilder; +import org.opentripplanner.inspector.vector.stop.AreaStopsLayerBuilder; +import org.opentripplanner.inspector.vector.stop.RegularStopsLayerBuilder; import org.opentripplanner.model.FeedInfo; import org.opentripplanner.standalone.api.OtpServerRequestContext; @@ -129,10 +129,7 @@ public MapboxStyleJson getTileJson(@Context UriInfo uri, @Context HttpHeaders he List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"), 256 ); - List sources = List.of( - backgroundSource, - vectorSource - ); + List sources = List.of(backgroundSource, vectorSource); return new MapboxStyleJson( "OTP Debug Tiles", sources, diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java index 65a009ec53e..759ce7eeb30 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java @@ -51,6 +51,11 @@ public LayerStyleBuilder source(TileSource source) { return this; } + /** + * For vector tile sources, specify which source layer in the tile the styles should apply to. + * There is an unfortunate collision in the name "layer" as it can both refer to a styling layer + * and the layer inside the vector tile. + */ public LayerStyleBuilder sourceLayer(String source) { props.put(SOURCE_LAYER, source); return this; diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java index bf84b697b92..6461d697bfc 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java @@ -12,11 +12,7 @@ public final class MapboxStyleJson { private final List sources; private final List layers; - public MapboxStyleJson( - String name, - List sources, - List layers - ) { + public MapboxStyleJson(String name, List sources, List layers) { this.name = name; this.sources = sources; this.layers = layers; diff --git a/src/main/java/org/opentripplanner/astar/model/BinHeap.java b/src/main/java/org/opentripplanner/astar/model/BinHeap.java index 9bc2b0762a8..1e9b540a77a 100644 --- a/src/main/java/org/opentripplanner/astar/model/BinHeap.java +++ b/src/main/java/org/opentripplanner/astar/model/BinHeap.java @@ -79,14 +79,6 @@ public void rekey(T e, double p) { prio[i] = p; } - public void dump() { - for (int i = 0; i <= capacity; i++) { - String topMarker = (i > size) ? "(UNUSED)" : ""; - System.out.printf("%d\t%f\t%s\t%s\n", i, prio[i], elem[i], topMarker); - } - System.out.printf("-----------------------\n"); - } - public void reset() { // empties the queue in one operation size = 0; @@ -135,8 +127,4 @@ public void resize(int capacity) { prio = Arrays.copyOf(prio, capacity + 1); elem = Arrays.copyOf(elem, capacity + 1); } - - public int getCapacity() { - return capacity; - } } diff --git a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java new file mode 100644 index 00000000000..3c1bc3436d8 --- /dev/null +++ b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java @@ -0,0 +1,40 @@ +package org.opentripplanner.inspector.vector.edge; + +import java.util.List; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.opentripplanner.framework.geometry.GeometryUtils; +import org.opentripplanner.inspector.vector.LayerBuilder; +import org.opentripplanner.inspector.vector.LayerParameters; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.routing.graph.index.StreetIndex; +import org.opentripplanner.street.model.edge.Edge; +import org.opentripplanner.street.model.vertex.Vertex; +import org.opentripplanner.transit.model.site.AreaStop; + +/** + * A vector tile layer containing all {@link AreaStop}s inside the vector tile bounds. + */ +public class EdgeLayerBuilder extends LayerBuilder { + + private final StreetIndex streetIndex; + + public EdgeLayerBuilder(Graph graph, LayerParameters layerParameters) { + super(new EdgePropertyMapper(), layerParameters.name(), layerParameters.expansionFactor()); + this.streetIndex = graph.getStreetIndex(); + } + + @Override + protected List getGeometries(Envelope query) { + return streetIndex + .getEdgesForEnvelope(query) + .stream() + .filter(edge -> edge.getGeometry() != null) + .map(edge -> { + Geometry geometry = edge.getGeometry().copy(); + geometry.setUserData(edge); + return geometry; + }) + .toList(); + } +} diff --git a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java new file mode 100644 index 00000000000..936c965c9f5 --- /dev/null +++ b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -0,0 +1,18 @@ +package org.opentripplanner.inspector.vector.edge; + +import java.util.Collection; +import java.util.List; +import org.opentripplanner.api.mapping.PropertyMapper; +import org.opentripplanner.inspector.vector.KeyValue; +import org.opentripplanner.street.model.edge.Edge; + +/** + * A {@link PropertyMapper} for the {@link EdgeLayerBuilder} for the OTP debug client. + */ +public class EdgePropertyMapper extends PropertyMapper { + + @Override + protected Collection map(Edge edge) { + return List.of(new KeyValue("java-class", edge.getClass().getSimpleName())); + } +} diff --git a/src/main/java/org/opentripplanner/inspector/vector/geofencing/GeofencingZonesLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/geofencing/GeofencingZonesLayerBuilder.java index 8a77b8502ea..24be8d202a8 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/geofencing/GeofencingZonesLayerBuilder.java +++ b/src/main/java/org/opentripplanner/inspector/vector/geofencing/GeofencingZonesLayerBuilder.java @@ -1,10 +1,8 @@ package org.opentripplanner.inspector.vector.geofencing; import java.util.List; -import java.util.Map; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.api.mapping.PropertyMapper; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.inspector.vector.LayerBuilder; import org.opentripplanner.inspector.vector.LayerParameters; @@ -19,15 +17,11 @@ */ public class GeofencingZonesLayerBuilder extends LayerBuilder { - private static final Map mappers = Map.of( - MapperType.DebugClient, - transitService -> new GeofencingZonesPropertyMapper() - ); private final StreetIndex streetIndex; public GeofencingZonesLayerBuilder(Graph graph, LayerParameters layerParameters) { super( - mappers.get(MapperType.valueOf(layerParameters.mapper())).build(graph), + new GeofencingZonesPropertyMapper(), layerParameters.name(), layerParameters.expansionFactor() ); @@ -47,13 +41,4 @@ protected List getGeometries(Envelope query) { }) .toList(); } - - enum MapperType { - DebugClient, - } - - @FunctionalInterface - private interface MapperFactory { - PropertyMapper build(Graph transitService); - } } diff --git a/src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/stop/AreaStopsLayerBuilder.java similarity index 77% rename from src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java rename to src/main/java/org/opentripplanner/inspector/vector/stop/AreaStopsLayerBuilder.java index 6ce7b0962ba..d1a2552e4e2 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/AreaStopsLayerBuilder.java +++ b/src/main/java/org/opentripplanner/inspector/vector/stop/AreaStopsLayerBuilder.java @@ -1,4 +1,4 @@ -package org.opentripplanner.inspector.vector; +package org.opentripplanner.inspector.vector.stop; import java.util.Collection; import java.util.List; @@ -6,7 +6,8 @@ import java.util.function.Function; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.api.mapping.PropertyMapper; +import org.opentripplanner.inspector.vector.LayerBuilder; +import org.opentripplanner.inspector.vector.LayerParameters; import org.opentripplanner.transit.model.site.AreaStop; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.service.TransitService; @@ -24,7 +25,7 @@ public AreaStopsLayerBuilder( Locale locale ) { super( - new DebugClientAreaStopPropertyMapper(locale), + new StopLocationPropertyMapper(locale), layerParameters.name(), layerParameters.expansionFactor() ); @@ -38,20 +39,9 @@ protected List getGeometries(Envelope query) { .stream() .map(areaStop -> { Geometry geometry = areaStop.getGeometry().copy(); - geometry.setUserData(areaStop); - return geometry; }) .toList(); } - - enum MapperType { - DebugClient, - } - - @FunctionalInterface - private interface MapperFactory { - PropertyMapper build(TransitService transitService, Locale locale); - } } diff --git a/src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/stop/RegularStopsLayerBuilder.java similarity index 84% rename from src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java rename to src/main/java/org/opentripplanner/inspector/vector/stop/RegularStopsLayerBuilder.java index 26e3f8726ae..f943e23eb88 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/RegularStopsLayerBuilder.java +++ b/src/main/java/org/opentripplanner/inspector/vector/stop/RegularStopsLayerBuilder.java @@ -1,4 +1,4 @@ -package org.opentripplanner.inspector.vector; +package org.opentripplanner.inspector.vector.stop; import java.util.Collection; import java.util.List; @@ -6,6 +6,8 @@ import java.util.function.Function; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; +import org.opentripplanner.inspector.vector.LayerBuilder; +import org.opentripplanner.inspector.vector.LayerParameters; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.service.TransitService; @@ -23,7 +25,7 @@ public RegularStopsLayerBuilder( Locale locale ) { super( - new DebugClientAreaStopPropertyMapper(locale), + new StopLocationPropertyMapper(locale), layerParameters.name(), layerParameters.expansionFactor() ); @@ -42,5 +44,4 @@ protected List getGeometries(Envelope query) { }) .toList(); } - } diff --git a/src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java similarity index 74% rename from src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java rename to src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java index 1d507140378..d2e1accf483 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/DebugClientAreaStopPropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java @@ -1,26 +1,27 @@ -package org.opentripplanner.inspector.vector; +package org.opentripplanner.inspector.vector.stop; import java.util.Collection; import java.util.List; import java.util.Locale; import org.opentripplanner.api.mapping.I18NStringMapper; import org.opentripplanner.api.mapping.PropertyMapper; +import org.opentripplanner.inspector.vector.KeyValue; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.service.TransitService; /** * A {@link PropertyMapper} for the {@link AreaStopsLayerBuilder} for the OTP debug client. */ -public class DebugClientAreaStopPropertyMapper extends PropertyMapper { +public class StopLocationPropertyMapper extends PropertyMapper { private final I18NStringMapper i18NStringMapper; - public DebugClientAreaStopPropertyMapper(Locale locale) { + public StopLocationPropertyMapper(Locale locale) { this.i18NStringMapper = new I18NStringMapper(locale); } public static PropertyMapper create(TransitService ignored, Locale locale) { - return new DebugClientAreaStopPropertyMapper(locale); + return new StopLocationPropertyMapper(locale); } @Override diff --git a/src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java b/src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java similarity index 86% rename from src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java rename to src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java index 6f2ca872f21..3fea49ab236 100644 --- a/src/test/java/org/opentripplanner/inspector/vector/AreaStopLayerBuilderTest.java +++ b/src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.inspector.vector; +package org.opentripplanner.inspector.vector.stop; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -9,12 +9,11 @@ import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.inspector.vector.KeyValue; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.AreaStop; -import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.StopModelBuilder; -import org.opentripplanner.transit.service.TransitModel; class AreaStopLayerBuilderTest { @@ -38,7 +37,7 @@ class AreaStopLayerBuilderTest { @Test void map() { - var subject = new DebugClientAreaStopPropertyMapper(Locale.ENGLISH); + var subject = new StopLocationPropertyMapper(Locale.ENGLISH); var properties = subject.map(areaStop); From 033000a2afa7921730bf41a64fb5517b7eaf6a2f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 20 Dec 2023 11:20:08 +0100 Subject: [PATCH 10/86] Move styles into separate class --- .../apis/vectortiles/DebugStyleJson.java | 43 +++++++++++++++++++ .../GraphInspectorVectorTileResource.java | 35 +-------------- .../vectortiles/model/LayerStyleBuilder.java | 2 +- .../vectortiles/model/MapboxStyleJson.java | 4 +- 4 files changed, 47 insertions(+), 37 deletions(-) create mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleJson.java diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleJson.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleJson.java new file mode 100644 index 00000000000..75592cbc0cd --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleJson.java @@ -0,0 +1,43 @@ +package org.opentripplanner.apis.vectortiles; + +import java.util.List; +import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder; +import org.opentripplanner.apis.vectortiles.model.MapboxStyleJson; +import org.opentripplanner.apis.vectortiles.model.TileSource; +import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource; +import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; + +public class DebugStyleJson { + + private static final RasterSource BACKGROUND_SOURCE = new RasterSource( + "background", + List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"), + 256 + ); + + static MapboxStyleJson build(String url) { + var vectorSource = new VectorSource("debug", url); + List sources = List.of(BACKGROUND_SOURCE, vectorSource); + return new MapboxStyleJson( + "OTP Debug Tiles", + sources, + List.of( + LayerStyleBuilder + .ofId("background") + .typeRaster() + .source(BACKGROUND_SOURCE) + .minZoom(0) + .maxZoom(22), + LayerStyleBuilder + .ofId("regular-stop") + .typeCircle() + .source(vectorSource) + .sourceLayer("regularStops") + .circleStroke("#140d0e", 1) + .circleColor("#fcf9fa") + .minZoom(13) + .maxZoom(22) + ) + ); + } +} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index 9d083bd5e03..6d23e533aad 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -20,11 +20,7 @@ import javax.annotation.Nonnull; import org.glassfish.grizzly.http.server.Request; import org.opentripplanner.api.model.TileJson; -import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder; import org.opentripplanner.apis.vectortiles.model.MapboxStyleJson; -import org.opentripplanner.apis.vectortiles.model.TileSource; -import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource; -import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; import org.opentripplanner.framework.io.HttpUtils; import org.opentripplanner.inspector.vector.LayerBuilder; import org.opentripplanner.inspector.vector.LayerParameters; @@ -123,36 +119,7 @@ public MapboxStyleJson getTileJson(@Context UriInfo uri, @Context HttpHeaders he "/inspector/vectortile/" + allLayers + "/tilejson.json"; - var vectorSource = new VectorSource("debug", url); - var backgroundSource = new RasterSource( - "background", - List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"), - 256 - ); - List sources = List.of(backgroundSource, vectorSource); - return new MapboxStyleJson( - "OTP Debug Tiles", - sources, - List.of( - LayerStyleBuilder - .ofId("background") - .typeRaster() - .source(backgroundSource) - .minZoom(0) - .maxZoom(22) - .build(), - LayerStyleBuilder - .ofId("regular-stop") - .source(vectorSource) - .sourceLayer("regularStops") - .typeCircle() - .circleStroke("#140d0e", 1) - .circleColor("#fcf9fa") - .minZoom(13) - .maxZoom(22) - .build() - ) - ); + return DebugStyleJson.build(url); } @Nonnull diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java index 759ce7eeb30..d549f2938e0 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java @@ -85,7 +85,7 @@ public LayerStyleBuilder circleStroke(String color, int width) { return this; } - public JsonNode build() { + public JsonNode toJson() { validate(); var copy = new HashMap<>(props); diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java index 6461d697bfc..6214a5aac65 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java @@ -12,10 +12,10 @@ public final class MapboxStyleJson { private final List sources; private final List layers; - public MapboxStyleJson(String name, List sources, List layers) { + public MapboxStyleJson(String name, List sources, List layers) { this.name = name; this.sources = sources; - this.layers = layers; + this.layers = layers.stream().map(LayerStyleBuilder::toJson).toList(); } @JsonSerialize From 57f07ddac6a2c34973b4f591b49ad20bec83e02f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 21 Dec 2023 08:30:45 +0100 Subject: [PATCH 11/86] Use style.json from server --- client-next/src/components/MapView/MapView.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 011d9408148..258a925a142 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -4,7 +4,6 @@ import { TripPattern, TripQuery, TripQueryVariables } from '../../gql/graphql.ts import { NavigationMarkers } from './NavigationMarkers.tsx'; import { LegLines } from './LegLines.tsx'; import { useMapDoubleClick } from './useMapDoubleClick.ts'; -import { mapStyle } from './mapStyle.ts'; import { useState } from 'react'; import { ContextMenuPopup } from './ContextMenuPopup.tsx'; @@ -37,12 +36,21 @@ export function MapView({ // @ts-ignore mapLib={import('maplibre-gl')} // @ts-ignore - mapStyle={mapStyle} + mapStyle="http://localhost:8080/otp/routers/default/inspector/vectortile/style.json" initialViewState={initialViewState} onDblClick={onMapDoubleClick} onContextMenu={(e) => { setShowPopup(e.lngLat); }} + interactiveLayerIds={["regular-stop"]} + onClick={e => { + console.log(e.features); + }} + // put lat/long in URL and pan to it on page reload + hash={true} + // disable pitching and rotating the map + touchPitch={false} + dragRotate={false} > Date: Thu, 21 Dec 2023 22:03:33 +0100 Subject: [PATCH 12/86] Refactor dependencies of map style classes --- ...ebugStyleJson.java => DebugStyleSpec.java} | 16 +++--- .../GraphInspectorVectorTileResource.java | 50 +++++++++---------- .../apis/vectortiles/model/LayerParams.java | 15 ++++++ .../vectortiles/model/LayerStyleBuilder.java | 7 +++ .../apis/vectortiles/model/LayerType.java | 7 +++ .../{MapboxStyleJson.java => StyleSpec.java} | 8 ++- .../apis/vectortiles/model/TileSource.java | 6 +++ .../framework/io/HttpUtils.java | 6 +-- 8 files changed, 77 insertions(+), 38 deletions(-) rename src/main/java/org/opentripplanner/apis/vectortiles/{DebugStyleJson.java => DebugStyleSpec.java} (70%) create mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java create mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java rename src/main/java/org/opentripplanner/apis/vectortiles/model/{MapboxStyleJson.java => StyleSpec.java} (77%) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleJson.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java similarity index 70% rename from src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleJson.java rename to src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 75592cbc0cd..9121d671c06 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleJson.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -2,12 +2,12 @@ import java.util.List; import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder; -import org.opentripplanner.apis.vectortiles.model.MapboxStyleJson; +import org.opentripplanner.apis.vectortiles.model.StyleSpec; import org.opentripplanner.apis.vectortiles.model.TileSource; import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource; import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; -public class DebugStyleJson { +public class DebugStyleSpec { private static final RasterSource BACKGROUND_SOURCE = new RasterSource( "background", @@ -15,10 +15,11 @@ public class DebugStyleJson { 256 ); - static MapboxStyleJson build(String url) { - var vectorSource = new VectorSource("debug", url); - List sources = List.of(BACKGROUND_SOURCE, vectorSource); - return new MapboxStyleJson( + public record VectorSourceLayer(VectorSource vectorSource, String vectorLayer) {} + + static StyleSpec build(VectorSource debugSource, VectorSourceLayer regularStops) { + List sources = List.of(BACKGROUND_SOURCE, debugSource); + return new StyleSpec( "OTP Debug Tiles", sources, List.of( @@ -31,8 +32,7 @@ static MapboxStyleJson build(String url) { LayerStyleBuilder .ofId("regular-stop") .typeCircle() - .source(vectorSource) - .sourceLayer("regularStops") + .vectorSourceLayer(regularStops) .circleStroke("#140d0e", 1) .circleColor("#fcf9fa") .minZoom(13) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index 6d23e533aad..7e800b5639b 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -20,7 +20,10 @@ import javax.annotation.Nonnull; import org.glassfish.grizzly.http.server.Request; import org.opentripplanner.api.model.TileJson; -import org.opentripplanner.apis.vectortiles.model.MapboxStyleJson; +import org.opentripplanner.apis.vectortiles.model.LayerParams; +import org.opentripplanner.apis.vectortiles.model.LayerType; +import org.opentripplanner.apis.vectortiles.model.StyleSpec; +import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; import org.opentripplanner.framework.io.HttpUtils; import org.opentripplanner.inspector.vector.LayerBuilder; import org.opentripplanner.inspector.vector.LayerParameters; @@ -38,10 +41,19 @@ @Path("/routers/{ignoreRouterId}/inspector/vectortile") public class GraphInspectorVectorTileResource { + private static final LayerParams REGULAR_STOPS = new LayerParams( + "regularStops", + LayerType.RegularStop + ); + private static final LayerParams AREA_STOPS = new LayerParams("areaStops", LayerType.AreaStop); + private static final LayerParams GEOFENCING_ZONES = new LayerParams( + "geofencingZones", + LayerType.GeofencingZones + ); private static final List> DEBUG_LAYERS = List.of( - new LayerParams("regularStops", LayerType.RegularStop), - new LayerParams("areaStops", LayerType.AreaStop), - new LayerParams("geofencingZones", LayerType.GeofencingZones) + REGULAR_STOPS, + AREA_STOPS, + GEOFENCING_ZONES ); private final OtpServerRequestContext serverContext; @@ -106,20 +118,21 @@ public TileJson getTileJson( @GET @Path("/style.json") @Produces(MediaType.APPLICATION_JSON) - public MapboxStyleJson getTileJson(@Context UriInfo uri, @Context HttpHeaders headers) { + public StyleSpec getTileJson(@Context UriInfo uri, @Context HttpHeaders headers) { var base = HttpUtils.getBaseAddress(uri, headers); final String allLayers = DEBUG_LAYERS .stream() .map(LayerParameters::name) .collect(Collectors.joining(",")); var url = - base + - "/otp/routers/" + - ignoreRouterId + - "/inspector/vectortile/" + - allLayers + - "/tilejson.json"; - return DebugStyleJson.build(url); + "%s/otp/routers/%s/inspector/vectortile/%s/tilejson.json".formatted( + base, + ignoreRouterId, + allLayers + ); + + var vectorSource = new VectorSource("debug", url); + return DebugStyleSpec.build(vectorSource, REGULAR_STOPS.toVectorSourceLayer(vectorSource)); } @Nonnull @@ -148,17 +161,4 @@ private static LayerBuilder createLayerBuilder( case GeofencingZones -> new GeofencingZonesLayerBuilder(context.graph(), layerParameters); }; } - - private enum LayerType { - RegularStop, - AreaStop, - GeofencingZones, - } - - private record LayerParams(String name, LayerType type) implements LayerParameters { - @Override - public String mapper() { - return "DebugClient"; - } - } } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java new file mode 100644 index 00000000000..2f1e6eb7109 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java @@ -0,0 +1,15 @@ +package org.opentripplanner.apis.vectortiles.model; + +import org.opentripplanner.apis.vectortiles.DebugStyleSpec; +import org.opentripplanner.inspector.vector.LayerParameters; + +public record LayerParams(String name, LayerType type) implements LayerParameters { + @Override + public String mapper() { + return "DebugClient"; + } + + public DebugStyleSpec.VectorSourceLayer toVectorSourceLayer(TileSource.VectorSource source) { + return new DebugStyleSpec.VectorSourceLayer(source, name); + } +} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java index d549f2938e0..0e1006027f2 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Stream; +import org.opentripplanner.apis.vectortiles.DebugStyleSpec.VectorSourceLayer; import org.opentripplanner.framework.json.ObjectMappers; /** @@ -24,6 +25,12 @@ public static LayerStyleBuilder ofId(String id) { return new LayerStyleBuilder(id); } + public LayerStyleBuilder vectorSourceLayer(VectorSourceLayer source) { + source(source.vectorSource()); + sourceLayer(source.vectorLayer()); + return this; + } + public enum LayerType { Circle, Raster, diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java new file mode 100644 index 00000000000..f4cb7a636fa --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java @@ -0,0 +1,7 @@ +package org.opentripplanner.apis.vectortiles.model; + +public enum LayerType { + RegularStop, + AreaStop, + GeofencingZones, +} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java similarity index 77% rename from src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java rename to src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java index 6214a5aac65..090573d467d 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/MapboxStyleJson.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java @@ -6,13 +6,17 @@ import java.util.List; import java.util.Map; -public final class MapboxStyleJson { +/** + * Represents a style specification for Maplibre/Mapbox vector tile layers. + * https://maplibre.org/maplibre-style-spec/root/ + */ +public final class StyleSpec { private final String name; private final List sources; private final List layers; - public MapboxStyleJson(String name, List sources, List layers) { + public StyleSpec(String name, List sources, List layers) { this.name = name; this.sources = sources; this.layers = layers.stream().map(LayerStyleBuilder::toJson).toList(); diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java index 2e404d9a1eb..debf3070782 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java @@ -9,6 +9,9 @@ public sealed interface TileSource { String id(); + /** + * Represents a vector tile source. + */ record VectorSource(String id, String url) implements TileSource { @Override public String type() { @@ -16,6 +19,9 @@ public String type() { } } + /** + * Represents a raster-based source for map tiles. + */ record RasterSource(String id, List tiles, int tileSize) implements TileSource { @Override public String type() { diff --git a/src/main/java/org/opentripplanner/framework/io/HttpUtils.java b/src/main/java/org/opentripplanner/framework/io/HttpUtils.java index 3450cf0786c..4981a8ab91b 100644 --- a/src/main/java/org/opentripplanner/framework/io/HttpUtils.java +++ b/src/main/java/org/opentripplanner/framework/io/HttpUtils.java @@ -24,16 +24,16 @@ private HttpUtils() {} public static String getBaseAddress(UriInfo uri, HttpHeaders headers) { String protocol; if (headers.getRequestHeader(HEADER_X_FORWARDED_PROTO) != null) { - protocol = headers.getRequestHeader(HEADER_X_FORWARDED_PROTO).get(0); + protocol = headers.getRequestHeader(HEADER_X_FORWARDED_PROTO).getFirst(); } else { protocol = uri.getRequestUri().getScheme(); } String host; if (headers.getRequestHeader(HEADER_X_FORWARDED_HOST) != null) { - host = headers.getRequestHeader(HEADER_X_FORWARDED_HOST).get(0); + host = headers.getRequestHeader(HEADER_X_FORWARDED_HOST).getFirst(); } else if (headers.getRequestHeader(HEADER_HOST) != null) { - host = headers.getRequestHeader(HEADER_HOST).get(0); + host = headers.getRequestHeader(HEADER_HOST).getFirst(); } else { host = uri.getBaseUri().getHost() + ":" + uri.getBaseUri().getPort(); } From ffd8ae6c80720888f8cff37ff0a772a0a28c2a9c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 22 Dec 2023 11:33:55 +0100 Subject: [PATCH 13/86] Add popup data --- .../src/components/MapView/MapView.tsx | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 258a925a142..9409a0cc68a 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -1,4 +1,4 @@ -import { LngLat, Map, NavigationControl } from 'react-map-gl'; +import { LngLat, Map, MapboxGeoJSONFeature, NavigationControl, Popup } from 'react-map-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { TripPattern, TripQuery, TripQueryVariables } from '../../gql/graphql.ts'; import { NavigationMarkers } from './NavigationMarkers.tsx'; @@ -6,6 +6,7 @@ import { LegLines } from './LegLines.tsx'; import { useMapDoubleClick } from './useMapDoubleClick.ts'; import { useState } from 'react'; import { ContextMenuPopup } from './ContextMenuPopup.tsx'; +import { Table } from 'react-bootstrap'; // TODO: this should be configurable const initialViewState = { @@ -14,6 +15,13 @@ const initialViewState = { zoom: 4, }; +class PopupData { + constructor( + public coordinates: LngLat, + public feature: MapboxGeoJSONFeature, + ) {} +} + export function MapView({ tripQueryVariables, setTripQueryVariables, @@ -28,7 +36,8 @@ export function MapView({ loading: boolean; }) { const onMapDoubleClick = useMapDoubleClick({ tripQueryVariables, setTripQueryVariables }); - const [showPopup, setShowPopup] = useState(null); + const [showContextPopup, setShowContextPopup] = useState(null); + const [showPropsPopup, setShowPropsPopup] = useState(null); return (
    @@ -40,11 +49,12 @@ export function MapView({ initialViewState={initialViewState} onDblClick={onMapDoubleClick} onContextMenu={(e) => { - setShowPopup(e.lngLat); + setShowContextPopup(e.lngLat); }} - interactiveLayerIds={["regular-stop"]} - onClick={e => { - console.log(e.features); + interactiveLayerIds={['regular-stop']} + onClick={(e) => { + const props = e.features[0]; + setShowPropsPopup(new PopupData(e.lngLat, props)); }} // put lat/long in URL and pan to it on page reload hash={true} @@ -61,14 +71,31 @@ export function MapView({ {tripQueryResult?.trip.tripPatterns.length && ( )} - {showPopup && ( + {showContextPopup && ( setShowPopup(null)} + coordinates={showContextPopup} + onClose={() => setShowContextPopup(null)} /> )} + {showPropsPopup && ( + setShowPropsPopup(null)} + > + + {Object.entries(showPropsPopup.feature.properties).map(([key, value]) => ( + + + + + ))} +
    {key}{value}
    +
    + )}
    ); From da28cf241fc0cd67d634be6a0fe05e05b73a72e5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 30 Dec 2023 20:11:32 +0100 Subject: [PATCH 14/86] Remove edge layer builder --- .../vector/edge/EdgeLayerBuilder.java | 40 ------------------- .../vector/edge/EdgePropertyMapper.java | 18 --------- 2 files changed, 58 deletions(-) delete mode 100644 src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java delete mode 100644 src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java diff --git a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java deleted file mode 100644 index 3c1bc3436d8..00000000000 --- a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.opentripplanner.inspector.vector.edge; - -import java.util.List; -import org.locationtech.jts.geom.Envelope; -import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.framework.geometry.GeometryUtils; -import org.opentripplanner.inspector.vector.LayerBuilder; -import org.opentripplanner.inspector.vector.LayerParameters; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.routing.graph.index.StreetIndex; -import org.opentripplanner.street.model.edge.Edge; -import org.opentripplanner.street.model.vertex.Vertex; -import org.opentripplanner.transit.model.site.AreaStop; - -/** - * A vector tile layer containing all {@link AreaStop}s inside the vector tile bounds. - */ -public class EdgeLayerBuilder extends LayerBuilder { - - private final StreetIndex streetIndex; - - public EdgeLayerBuilder(Graph graph, LayerParameters layerParameters) { - super(new EdgePropertyMapper(), layerParameters.name(), layerParameters.expansionFactor()); - this.streetIndex = graph.getStreetIndex(); - } - - @Override - protected List getGeometries(Envelope query) { - return streetIndex - .getEdgesForEnvelope(query) - .stream() - .filter(edge -> edge.getGeometry() != null) - .map(edge -> { - Geometry geometry = edge.getGeometry().copy(); - geometry.setUserData(edge); - return geometry; - }) - .toList(); - } -} diff --git a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java deleted file mode 100644 index 936c965c9f5..00000000000 --- a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.opentripplanner.inspector.vector.edge; - -import java.util.Collection; -import java.util.List; -import org.opentripplanner.api.mapping.PropertyMapper; -import org.opentripplanner.inspector.vector.KeyValue; -import org.opentripplanner.street.model.edge.Edge; - -/** - * A {@link PropertyMapper} for the {@link EdgeLayerBuilder} for the OTP debug client. - */ -public class EdgePropertyMapper extends PropertyMapper { - - @Override - protected Collection map(Edge edge) { - return List.of(new KeyValue("java-class", edge.getClass().getSimpleName())); - } -} From 18eb243f84aab078770d66fd2f8cceff74dd47ec Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 30 Dec 2023 23:31:49 +0100 Subject: [PATCH 15/86] Make styles a but prettier --- .../src/components/MapView/MapView.tsx | 12 +++-- .../apis/vectortiles/DebugStyleSpec.java | 6 ++- .../GraphInspectorVectorTileResource.java | 15 +++--- .../inspector/vector/KeyValue.java | 6 ++- .../vector/stop/AreaStopsLayerBuilder.java | 47 ------------------- ...ayerBuilder.java => StopLayerBuilder.java} | 15 +++--- .../stop/StopLocationPropertyMapper.java | 14 +++--- 7 files changed, 39 insertions(+), 76 deletions(-) delete mode 100644 src/main/java/org/opentripplanner/inspector/vector/stop/AreaStopsLayerBuilder.java rename src/main/java/org/opentripplanner/inspector/vector/stop/{RegularStopsLayerBuilder.java => StopLayerBuilder.java} (73%) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 9409a0cc68a..a565ae22db9 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -86,12 +86,14 @@ export function MapView({ closeButton={true} onClose={() => setShowPropsPopup(null)} > - +
    {Object.entries(showPropsPopup.feature.properties).map(([key, value]) => ( - - - - + + + + + + ))}
    {key}{value}
    {key}{value}
    diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 9121d671c06..c3dfc3d06f1 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -7,6 +7,10 @@ import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource; import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; +/** + * A Mapbox/Mapblibre style specification for rendering debug information about transit and + * street data. + */ public class DebugStyleSpec { private static final RasterSource BACKGROUND_SOURCE = new RasterSource( @@ -33,7 +37,7 @@ static StyleSpec build(VectorSource debugSource, VectorSourceLayer regularStops) .ofId("regular-stop") .typeCircle() .vectorSourceLayer(regularStops) - .circleStroke("#140d0e", 1) + .circleStroke("#140d0e", 2) .circleColor("#fcf9fa") .minZoom(13) .maxZoom(22) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index 7e800b5639b..c27d38b260c 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -29,8 +29,7 @@ import org.opentripplanner.inspector.vector.LayerParameters; import org.opentripplanner.inspector.vector.VectorTileResponseFactory; import org.opentripplanner.inspector.vector.geofencing.GeofencingZonesLayerBuilder; -import org.opentripplanner.inspector.vector.stop.AreaStopsLayerBuilder; -import org.opentripplanner.inspector.vector.stop.RegularStopsLayerBuilder; +import org.opentripplanner.inspector.vector.stop.StopLayerBuilder; import org.opentripplanner.model.FeedInfo; import org.opentripplanner.standalone.api.OtpServerRequestContext; @@ -152,12 +151,16 @@ private static LayerBuilder createLayerBuilder( OtpServerRequestContext context ) { return switch (layerParameters.type()) { - case RegularStop -> new RegularStopsLayerBuilder( - context.transitService(), + case RegularStop -> new StopLayerBuilder<>( layerParameters, - locale + locale, + e -> context.transitService().findRegularStop(e) + ); + case AreaStop -> new StopLayerBuilder<>( + layerParameters, + locale, + e -> context.transitService().findAreaStops(e) ); - case AreaStop -> new AreaStopsLayerBuilder(context.transitService(), layerParameters, locale); case GeofencingZones -> new GeofencingZonesLayerBuilder(context.graph(), layerParameters); }; } diff --git a/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java b/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java index d57afd3429e..6c8b0f3aa4e 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java +++ b/src/main/java/org/opentripplanner/inspector/vector/KeyValue.java @@ -1,3 +1,7 @@ package org.opentripplanner.inspector.vector; -public record KeyValue(String key, Object value) {} +public record KeyValue(String key, Object value) { + public static KeyValue kv(String key, Object value) { + return new KeyValue(key, value); + } +} diff --git a/src/main/java/org/opentripplanner/inspector/vector/stop/AreaStopsLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/stop/AreaStopsLayerBuilder.java deleted file mode 100644 index d1a2552e4e2..00000000000 --- a/src/main/java/org/opentripplanner/inspector/vector/stop/AreaStopsLayerBuilder.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.opentripplanner.inspector.vector.stop; - -import java.util.Collection; -import java.util.List; -import java.util.Locale; -import java.util.function.Function; -import org.locationtech.jts.geom.Envelope; -import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.inspector.vector.LayerBuilder; -import org.opentripplanner.inspector.vector.LayerParameters; -import org.opentripplanner.transit.model.site.AreaStop; -import org.opentripplanner.transit.model.site.StopLocation; -import org.opentripplanner.transit.service.TransitService; - -/** - * A vector tile layer containing all {@link AreaStop}s inside the vector tile bounds. - */ -public class AreaStopsLayerBuilder extends LayerBuilder { - - private final Function> findAreaStops; - - public AreaStopsLayerBuilder( - TransitService transitService, - LayerParameters layerParameters, - Locale locale - ) { - super( - new StopLocationPropertyMapper(locale), - layerParameters.name(), - layerParameters.expansionFactor() - ); - this.findAreaStops = transitService::findAreaStops; - } - - @Override - protected List getGeometries(Envelope query) { - return findAreaStops - .apply(query) - .stream() - .map(areaStop -> { - Geometry geometry = areaStop.getGeometry().copy(); - geometry.setUserData(areaStop); - return geometry; - }) - .toList(); - } -} diff --git a/src/main/java/org/opentripplanner/inspector/vector/stop/RegularStopsLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java similarity index 73% rename from src/main/java/org/opentripplanner/inspector/vector/stop/RegularStopsLayerBuilder.java rename to src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java index f943e23eb88..40784ab5b3b 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/stop/RegularStopsLayerBuilder.java +++ b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java @@ -10,31 +10,30 @@ import org.opentripplanner.inspector.vector.LayerParameters; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; -import org.opentripplanner.transit.service.TransitService; /** * A vector tile layer containing all {@link RegularStop}s inside the vector tile bounds. */ -public class RegularStopsLayerBuilder extends LayerBuilder { +public class StopLayerBuilder extends LayerBuilder { - private final Function> findAreaStops; + private final Function> findStops; - public RegularStopsLayerBuilder( - TransitService transitService, + public StopLayerBuilder( LayerParameters layerParameters, - Locale locale + Locale locale, + Function> findStops ) { super( new StopLocationPropertyMapper(locale), layerParameters.name(), layerParameters.expansionFactor() ); - this.findAreaStops = transitService::findRegularStop; + this.findStops = findStops; } @Override protected List getGeometries(Envelope query) { - return findAreaStops + return findStops .apply(query) .stream() .map(stop -> { diff --git a/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java index d2e1accf483..44729bcb407 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java @@ -1,5 +1,7 @@ package org.opentripplanner.inspector.vector.stop; +import static org.opentripplanner.inspector.vector.KeyValue.kv; + import java.util.Collection; import java.util.List; import java.util.Locale; @@ -7,7 +9,6 @@ import org.opentripplanner.api.mapping.PropertyMapper; import org.opentripplanner.inspector.vector.KeyValue; import org.opentripplanner.transit.model.site.StopLocation; -import org.opentripplanner.transit.service.TransitService; /** * A {@link PropertyMapper} for the {@link AreaStopsLayerBuilder} for the OTP debug client. @@ -20,15 +21,12 @@ public StopLocationPropertyMapper(Locale locale) { this.i18NStringMapper = new I18NStringMapper(locale); } - public static PropertyMapper create(TransitService ignored, Locale locale) { - return new StopLocationPropertyMapper(locale); - } - @Override - protected Collection map(StopLocation input) { + protected Collection map(StopLocation stop) { return List.of( - new KeyValue("id", input.getId().toString()), - new KeyValue("name", i18NStringMapper.mapToApi(input.getName())) + kv("name", i18NStringMapper.mapToApi(stop.getName())), + kv("id", stop.getId().toString()), + kv("parentId", stop.isPartOfStation() ? stop.getParentStation().getId().toString() : null) ); } } From 7cfbae80109ecd00c9240ca9c2e803206eae8fd8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 31 Dec 2023 00:04:49 +0100 Subject: [PATCH 16/86] Add test for debug style spec --- .../vectortiles/model/LayerStyleBuilder.java | 3 +- .../apis/vectortiles/DebugStyleSpecTest.java | 25 +++++++++++ .../vector/stop/AreaStopLayerBuilderTest.java | 16 ++------ .../test/support/ResourceLoader.java | 11 +++++ .../apis/vectortiles/style.json | 41 +++++++++++++++++++ 5 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java create mode 100644 src/test/resources/org/opentripplanner/apis/vectortiles/style.json diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java index 0e1006027f2..41144611f92 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java @@ -27,8 +27,7 @@ public static LayerStyleBuilder ofId(String id) { public LayerStyleBuilder vectorSourceLayer(VectorSourceLayer source) { source(source.vectorSource()); - sourceLayer(source.vectorLayer()); - return this; + return sourceLayer(source.vectorLayer()); } public enum LayerType { diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java new file mode 100644 index 00000000000..4fd9204c2a9 --- /dev/null +++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -0,0 +1,25 @@ +package org.opentripplanner.apis.vectortiles; + +import static org.opentripplanner.test.support.JsonAssertions.assertEqualJson; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.apis.vectortiles.DebugStyleSpec.VectorSourceLayer; +import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; +import org.opentripplanner.framework.json.ObjectMappers; +import org.opentripplanner.test.support.ResourceLoader; + +class DebugStyleSpecTest { + + private final ResourceLoader RES = ResourceLoader.of(this); + + @Test + void spec() { + var vectorSource = new VectorSource("vectorSource", "https://example.com"); + var regularStops = new VectorSourceLayer(vectorSource, "regularStops"); + var spec = DebugStyleSpec.build(vectorSource, regularStops); + + var json = ObjectMappers.ignoringExtraFields().valueToTree(spec).toPrettyString(); + var expectation = RES.fileToString("style.json"); + assertEqualJson(expectation, json); + } +} diff --git a/src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java b/src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java index 3fea49ab236..231f3ddce59 100644 --- a/src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java +++ b/src/test/java/org/opentripplanner/inspector/vector/stop/AreaStopLayerBuilderTest.java @@ -1,12 +1,10 @@ package org.opentripplanner.inspector.vector.stop; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Locale; import org.junit.jupiter.api.Test; -import org.locationtech.jts.geom.Coordinate; -import org.opentripplanner.framework.geometry.GeometryUtils; +import org.opentripplanner._support.geometry.Polygons; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.inspector.vector.KeyValue; @@ -17,22 +15,15 @@ class AreaStopLayerBuilderTest { - private static final Coordinate[] COORDINATES = { - new Coordinate(0, 0), - new Coordinate(0, 1), - new Coordinate(1, 1), - new Coordinate(1, 0), - new Coordinate(0, 0), - }; private static final FeedScopedId ID = new FeedScopedId("FEED", "ID"); - private static final I18NString NAME = new NonLocalizedString("Test stop"); + private static final I18NString NAME = I18NString.of("Test stop"); private final StopModelBuilder stopModelBuilder = StopModel.of(); private final AreaStop areaStop = stopModelBuilder .areaStop(ID) .withName(NAME) - .withGeometry(GeometryUtils.getGeometryFactory().createPolygon(COORDINATES)) + .withGeometry(Polygons.BERLIN) .build(); @Test @@ -41,7 +32,6 @@ void map() { var properties = subject.map(areaStop); - assertEquals(2, properties.size()); assertTrue(properties.contains(new KeyValue("id", ID.toString()))); assertTrue(properties.contains(new KeyValue("name", NAME.toString()))); } diff --git a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java index 38fe02a8c74..5eb51cac55a 100644 --- a/src/test/java/org/opentripplanner/test/support/ResourceLoader.java +++ b/src/test/java/org/opentripplanner/test/support/ResourceLoader.java @@ -55,6 +55,17 @@ public File file(String path) { return file; } + /** + * Returns the string content of a file. + */ + public String fileToString(String p) { + try { + return Files.readString(file(p).toPath()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** * Return a URL for the given resource. */ diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json new file mode 100644 index 00000000000..f0ed87090c3 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -0,0 +1,41 @@ +{ + "name": "OTP Debug Tiles", + "sources": { + "background": { + "id": "background", + "tiles": [ + "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" + ], + "tileSize": 256, + "type": "raster" + }, + "vectorSource": { + "id": "vectorSource", + "url": "https://example.com", + "type": "vector" + } + }, + "layers": [ + { + "id": "background", + "source": "background", + "type": "raster", + "maxzoom": 22, + "minzoom": 0 + }, + { + "maxzoom": 22, + "paint": { + "circle-stroke-width": 2, + "circle-color": "#fcf9fa", + "circle-stroke-color": "#140d0e" + }, + "id": "regular-stop", + "source": "vectorSource", + "source-layer": "regularStops", + "type": "circle", + "minzoom": 13 + } + ], + "version": 8 +} From c7395d8282ea247e4d4a1d808efb740c0a6a6a79 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 31 Dec 2023 00:13:31 +0100 Subject: [PATCH 17/86] Save a JSON round trip --- .../apis/vectortiles/DebugStyleSpecTest.java | 2 +- .../test/support/JsonAssertions.java | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index 4fd9204c2a9..d685e07a2f2 100644 --- a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -18,7 +18,7 @@ void spec() { var regularStops = new VectorSourceLayer(vectorSource, "regularStops"); var spec = DebugStyleSpec.build(vectorSource, regularStops); - var json = ObjectMappers.ignoringExtraFields().valueToTree(spec).toPrettyString(); + var json = ObjectMappers.ignoringExtraFields().valueToTree(spec); var expectation = RES.fileToString("style.json"); assertEqualJson(expectation, json); } diff --git a/src/test/java/org/opentripplanner/test/support/JsonAssertions.java b/src/test/java/org/opentripplanner/test/support/JsonAssertions.java index f3942c16f3b..2dab1e96190 100644 --- a/src/test/java/org/opentripplanner/test/support/JsonAssertions.java +++ b/src/test/java/org/opentripplanner/test/support/JsonAssertions.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.opentripplanner.standalone.config.framework.json.JsonSupport; @@ -15,9 +16,19 @@ public class JsonAssertions { */ public static void assertEqualJson(String expected, String actual) { try { - var act = MAPPER.readTree(actual); + assertEqualJson(expected, MAPPER.readTree(actual)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + /** + * @see JsonAssertions#assertEqualJson(String, String) + */ + public static void assertEqualJson(String expected, JsonNode actual) { + try { var exp = MAPPER.readTree(expected); - assertEquals(JsonSupport.prettyPrint(exp), JsonSupport.prettyPrint(act)); + assertEquals(JsonSupport.prettyPrint(exp), JsonSupport.prettyPrint(actual)); } catch (JsonProcessingException e) { throw new RuntimeException(e); } From 934b809480f9f4976f95d7f5d93125a1d7d4e1e9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 2 Jan 2024 16:59:58 +0100 Subject: [PATCH 18/86] Add attribution, remove client side style --- .../src/components/MapView/mapStyle.ts | 19 ------------------- .../apis/vectortiles/DebugStyleSpec.java | 3 ++- .../apis/vectortiles/model/TileSource.java | 2 +- .../apis/vectortiles/style.json | 1 + 4 files changed, 4 insertions(+), 21 deletions(-) delete mode 100644 client-next/src/components/MapView/mapStyle.ts diff --git a/client-next/src/components/MapView/mapStyle.ts b/client-next/src/components/MapView/mapStyle.ts deleted file mode 100644 index ecaa88c0354..00000000000 --- a/client-next/src/components/MapView/mapStyle.ts +++ /dev/null @@ -1,19 +0,0 @@ -export const mapStyle = { - version: 8, - sources: { - osm: { - type: 'raster', - tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'], - tileSize: 256, - attribution: '© OpenStreetMap Contributors', - maxzoom: 19, - }, - }, - layers: [ - { - id: 'osm', - type: 'raster', - source: 'osm', // This must match the source key above - }, - ], -}; diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index c3dfc3d06f1..9a4fe9be123 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -16,7 +16,8 @@ public class DebugStyleSpec { private static final RasterSource BACKGROUND_SOURCE = new RasterSource( "background", List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"), - 256 + 256, + "© OpenStreetMap Contributors" ); public record VectorSourceLayer(VectorSource vectorSource, String vectorLayer) {} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java index debf3070782..d9dbe50f68e 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java @@ -22,7 +22,7 @@ public String type() { /** * Represents a raster-based source for map tiles. */ - record RasterSource(String id, List tiles, int tileSize) implements TileSource { + record RasterSource(String id, List tiles, int tileSize, String attribution) implements TileSource { @Override public String type() { return "raster"; diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index f0ed87090c3..6f95471a667 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -7,6 +7,7 @@ "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" ], "tileSize": 256, + "attribution" : "© OpenStreetMap Contributors", "type": "raster" }, "vectorSource": { From 826a8429e14afe04ea48dbe8f4e5061d4cb303fd Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 2 Jan 2024 17:09:44 +0100 Subject: [PATCH 19/86] Add documentation, fix frontend code --- client-next/src/components/MapView/MapView.tsx | 10 +++++----- .../apis/vectortiles/model/LayerParams.java | 11 ++++++++--- .../apis/vectortiles/model/StyleSpec.java | 2 ++ .../apis/vectortiles/model/TileSource.java | 8 ++++++-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index a565ae22db9..c3879dd2a6b 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -87,14 +87,14 @@ export function MapView({ onClose={() => setShowPropsPopup(null)} > - {Object.entries(showPropsPopup.feature.properties).map(([key, value]) => ( - - + + {Object.entries(showPropsPopup.feature.properties).map(([key, value]) => ( + - - ))} + ))} +
    {key} {value}
    )} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java index 2f1e6eb7109..7365e8972da 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java @@ -1,6 +1,7 @@ package org.opentripplanner.apis.vectortiles.model; -import org.opentripplanner.apis.vectortiles.DebugStyleSpec; +import org.opentripplanner.apis.vectortiles.DebugStyleSpec.VectorSourceLayer; +import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; import org.opentripplanner.inspector.vector.LayerParameters; public record LayerParams(String name, LayerType type) implements LayerParameters { @@ -9,7 +10,11 @@ public String mapper() { return "DebugClient"; } - public DebugStyleSpec.VectorSourceLayer toVectorSourceLayer(TileSource.VectorSource source) { - return new DebugStyleSpec.VectorSourceLayer(source, name); + /** + * Convert these params to a vector source layer so that it can be used in the style for rendering + * in the frontend. + */ + public VectorSourceLayer toVectorSourceLayer(VectorSource source) { + return new VectorSourceLayer(source, name); } } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java index 090573d467d..84e19f25364 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java @@ -9,6 +9,8 @@ /** * Represents a style specification for Maplibre/Mapbox vector tile layers. * https://maplibre.org/maplibre-style-spec/root/ + *

    + * Maplibre uses these to render vector maps in the browser. */ public final class StyleSpec { diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java index d9dbe50f68e..e3186cc1d99 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java @@ -3,6 +3,9 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.List; +/** + * Represent a data source where Maplibre can fetch data for rendering directly in the browser. + */ public sealed interface TileSource { @JsonSerialize String type(); @@ -10,7 +13,7 @@ public sealed interface TileSource { String id(); /** - * Represents a vector tile source. + * Represents a vector tile source which is rendered into a map in the browser. */ record VectorSource(String id, String url) implements TileSource { @Override @@ -20,7 +23,8 @@ public String type() { } /** - * Represents a raster-based source for map tiles. + * Represents a raster-based source for map tiles. These are used mainly for background + * map layers with vector data being rendered on top of it. */ record RasterSource(String id, List tiles, int tileSize, String attribution) implements TileSource { @Override From c8642006f32c59f94007bcb532bc2eb51f8f1804 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 2 Jan 2024 21:57:30 +0100 Subject: [PATCH 20/86] Make TS compiler happy --- client-next/src/components/MapView/MapView.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index c3879dd2a6b..d03c4869067 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -53,8 +53,10 @@ export function MapView({ }} interactiveLayerIds={['regular-stop']} onClick={(e) => { - const props = e.features[0]; - setShowPropsPopup(new PopupData(e.lngLat, props)); + if (e.features) { + const props = e.features[0]; + setShowPropsPopup(new PopupData(e.lngLat, props)); + } }} // put lat/long in URL and pan to it on page reload hash={true} @@ -79,7 +81,7 @@ export function MapView({ onClose={() => setShowContextPopup(null)} /> )} - {showPropsPopup && ( + {showPropsPopup && showPropsPopup.feature && showPropsPopup.feature.properties && ( Date: Tue, 2 Jan 2024 22:00:55 +0100 Subject: [PATCH 21/86] Convert from class to type --- client-next/src/components/MapView/MapView.tsx | 9 ++------- .../apis/vectortiles/model/TileSource.java | 3 ++- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index d03c4869067..9ecc35c27aa 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -15,12 +15,7 @@ const initialViewState = { zoom: 4, }; -class PopupData { - constructor( - public coordinates: LngLat, - public feature: MapboxGeoJSONFeature, - ) {} -} +type PopupData = { coordinates: LngLat; feature: MapboxGeoJSONFeature }; export function MapView({ tripQueryVariables, @@ -55,7 +50,7 @@ export function MapView({ onClick={(e) => { if (e.features) { const props = e.features[0]; - setShowPropsPopup(new PopupData(e.lngLat, props)); + setShowPropsPopup({ coordinates: e.lngLat, feature: props }); } }} // put lat/long in URL and pan to it on page reload diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java index e3186cc1d99..06af294a4f0 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java @@ -26,7 +26,8 @@ public String type() { * Represents a raster-based source for map tiles. These are used mainly for background * map layers with vector data being rendered on top of it. */ - record RasterSource(String id, List tiles, int tileSize, String attribution) implements TileSource { + record RasterSource(String id, List tiles, int tileSize, String attribution) + implements TileSource { @Override public String type() { return "raster"; From 1cbce1fcd83b0af48f8f9d5cc62c522ce621fbea Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 2 Jan 2024 22:15:40 +0100 Subject: [PATCH 22/86] Use optional chaining --- client-next/src/components/MapView/MapView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 9ecc35c27aa..95c898c4a28 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -76,7 +76,7 @@ export function MapView({ onClose={() => setShowContextPopup(null)} /> )} - {showPropsPopup && showPropsPopup.feature && showPropsPopup.feature.properties && ( + {showPropsPopup?.feature?.properties && ( Date: Thu, 4 Jan 2024 17:46:18 +0100 Subject: [PATCH 23/86] Remove GTFS-RT websocket updater --- pom.xml | 21 -- .../config/routerconfig/UpdatersConfig.java | 9 - .../WebsocketGtfsRealtimeUpdaterConfig.java | 24 -- .../updater/UpdatersParameters.java | 3 - .../configure/UpdaterConfigurator.java | 10 - .../trip/WebsocketGtfsRealtimeUpdater.java | 211 ------------------ ...ebsocketGtfsRealtimeUpdaterParameters.java | 45 ---- 7 files changed, 323 deletions(-) delete mode 100644 src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/WebsocketGtfsRealtimeUpdaterConfig.java delete mode 100644 src/main/java/org/opentripplanner/updater/trip/WebsocketGtfsRealtimeUpdater.java delete mode 100644 src/main/java/org/opentripplanner/updater/trip/WebsocketGtfsRealtimeUpdaterParameters.java diff --git a/pom.xml b/pom.xml index de8baecefd9..0e03a0ccc7d 100644 --- a/pom.xml +++ b/pom.xml @@ -551,20 +551,6 @@ import - - - io.netty - netty-bom - 4.1.100.Final - pom - import - - @@ -842,13 +828,6 @@ protobuf-java - - - - org.asynchttpclient - async-http-client - 2.12.3 - org.onebusaway diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java index 0f61fa4f7a2..08371660b8a 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/UpdatersConfig.java @@ -14,7 +14,6 @@ import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.VEHICLE_PARKING; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.VEHICLE_POSITIONS; import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.VEHICLE_RENTAL; -import static org.opentripplanner.standalone.config.routerconfig.UpdatersConfig.Type.WEBSOCKET_GTFS_RT_UPDATER; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; @@ -39,7 +38,6 @@ import org.opentripplanner.standalone.config.routerconfig.updaters.VehicleParkingUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.VehicleRentalUpdaterConfig; -import org.opentripplanner.standalone.config.routerconfig.updaters.WebsocketGtfsRealtimeUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.azure.SiriAzureETUpdaterConfig; import org.opentripplanner.standalone.config.routerconfig.updaters.azure.SiriAzureSXUpdaterConfig; import org.opentripplanner.standalone.config.sandbox.VehicleRentalServiceDirectoryFetcherConfig; @@ -48,7 +46,6 @@ import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; -import org.opentripplanner.updater.trip.WebsocketGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; import org.opentripplanner.updater.vehicle_position.VehiclePositionsUpdaterParameters; import org.opentripplanner.updater.vehicle_rental.VehicleRentalUpdaterParameters; @@ -185,11 +182,6 @@ public List getSiriSXUpdaterParameters() { return getParameters(SIRI_SX_UPDATER); } - @Override - public List getWebsocketGtfsRealtimeUpdaterParameters() { - return getParameters(WEBSOCKET_GTFS_RT_UPDATER); - } - @Override public List getMqttGtfsRealtimeUpdaterParameters() { return getParameters(MQTT_GTFS_RT_UPDATER); @@ -222,7 +214,6 @@ public enum Type { BIKE_RENTAL(VehicleRentalUpdaterConfig::create), VEHICLE_RENTAL(VehicleRentalUpdaterConfig::create), STOP_TIME_UPDATER(PollingTripUpdaterConfig::create), - WEBSOCKET_GTFS_RT_UPDATER(WebsocketGtfsRealtimeUpdaterConfig::create), MQTT_GTFS_RT_UPDATER(MqttGtfsRealtimeUpdaterConfig::create), REAL_TIME_ALERTS(GtfsRealtimeAlertsUpdaterConfig::create), VEHICLE_POSITIONS(VehiclePositionsUpdaterConfig::create), diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/WebsocketGtfsRealtimeUpdaterConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/WebsocketGtfsRealtimeUpdaterConfig.java deleted file mode 100644 index 5f1c203ca43..00000000000 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/WebsocketGtfsRealtimeUpdaterConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.opentripplanner.standalone.config.routerconfig.updaters; - -import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V1_5; - -import org.opentripplanner.standalone.config.framework.json.NodeAdapter; -import org.opentripplanner.updater.trip.BackwardsDelayPropagationType; -import org.opentripplanner.updater.trip.WebsocketGtfsRealtimeUpdaterParameters; - -public class WebsocketGtfsRealtimeUpdaterConfig { - - public static WebsocketGtfsRealtimeUpdaterParameters create(String configRef, NodeAdapter c) { - return new WebsocketGtfsRealtimeUpdaterParameters( - configRef, - c.of("feedId").since(V1_5).summary("TODO").asString(), - c.of("url").since(V1_5).summary("TODO").asString(null), - c.of("reconnectPeriodSec").since(V1_5).summary("TODO").asInt(60), - c - .of("backwardsDelayPropagationType") - .since(V1_5) - .summary("TODO") - .asEnum(BackwardsDelayPropagationType.REQUIRED_NO_DATA) - ); - } -} diff --git a/src/main/java/org/opentripplanner/updater/UpdatersParameters.java b/src/main/java/org/opentripplanner/updater/UpdatersParameters.java index 8a7c422eb03..a955e757100 100644 --- a/src/main/java/org/opentripplanner/updater/UpdatersParameters.java +++ b/src/main/java/org/opentripplanner/updater/UpdatersParameters.java @@ -10,7 +10,6 @@ import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdaterParameters; import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.trip.PollingTripUpdaterParameters; -import org.opentripplanner.updater.trip.WebsocketGtfsRealtimeUpdaterParameters; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; import org.opentripplanner.updater.vehicle_position.VehiclePositionsUpdaterParameters; import org.opentripplanner.updater.vehicle_rental.VehicleRentalUpdaterParameters; @@ -34,8 +33,6 @@ public interface UpdatersParameters { List getSiriSXUpdaterParameters(); - List getWebsocketGtfsRealtimeUpdaterParameters(); - List getMqttGtfsRealtimeUpdaterParameters(); List getVehicleParkingUpdaterParameters(); diff --git a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 037bd080f47..c755578da41 100644 --- a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -23,14 +23,11 @@ import org.opentripplanner.updater.trip.MqttGtfsRealtimeUpdater; import org.opentripplanner.updater.trip.PollingTripUpdater; import org.opentripplanner.updater.trip.TimetableSnapshotSource; -import org.opentripplanner.updater.trip.WebsocketGtfsRealtimeUpdater; import org.opentripplanner.updater.vehicle_parking.VehicleParkingDataSourceFactory; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdater; import org.opentripplanner.updater.vehicle_position.PollingVehiclePositionUpdater; import org.opentripplanner.updater.vehicle_rental.VehicleRentalUpdater; import org.opentripplanner.updater.vehicle_rental.datasources.VehicleRentalDataSourceFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Sets up and starts all the graph updaters. @@ -41,8 +38,6 @@ */ public class UpdaterConfigurator { - private static final Logger LOG = LoggerFactory.getLogger(UpdaterConfigurator.class); - private final Graph graph; private final TransitModel transitModel; private final UpdatersParameters updatersParameters; @@ -177,11 +172,6 @@ private List createUpdatersFromConfig() { for (var configItem : updatersParameters.getSiriSXUpdaterParameters()) { updaters.add(new SiriSXUpdater(configItem, transitModel)); } - for (var configItem : updatersParameters.getWebsocketGtfsRealtimeUpdaterParameters()) { - updaters.add( - new WebsocketGtfsRealtimeUpdater(configItem, provideGtfsTimetableSnapshot(), transitModel) - ); - } for (var configItem : updatersParameters.getMqttGtfsRealtimeUpdaterParameters()) { updaters.add( new MqttGtfsRealtimeUpdater(configItem, transitModel, provideGtfsTimetableSnapshot()) diff --git a/src/main/java/org/opentripplanner/updater/trip/WebsocketGtfsRealtimeUpdater.java b/src/main/java/org/opentripplanner/updater/trip/WebsocketGtfsRealtimeUpdater.java deleted file mode 100644 index 007c73d9b9b..00000000000 --- a/src/main/java/org/opentripplanner/updater/trip/WebsocketGtfsRealtimeUpdater.java +++ /dev/null @@ -1,211 +0,0 @@ -package org.opentripplanner.updater.trip; - -import static org.asynchttpclient.Dsl.asyncHttpClient; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.transit.realtime.GtfsRealtime; -import com.google.transit.realtime.GtfsRealtime.FeedEntity; -import com.google.transit.realtime.GtfsRealtime.FeedMessage; -import com.google.transit.realtime.GtfsRealtime.TripUpdate; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.function.Consumer; -import org.asynchttpclient.AsyncHttpClient; -import org.asynchttpclient.ws.WebSocket; -import org.asynchttpclient.ws.WebSocketListener; -import org.asynchttpclient.ws.WebSocketUpgradeHandler; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; -import org.opentripplanner.updater.spi.GraphUpdater; -import org.opentripplanner.updater.spi.UpdateResult; -import org.opentripplanner.updater.spi.WriteToGraphCallback; -import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class starts an HTTP client which opens a websocket connection to a GTFS-RT data source. A - * callback is registered which handles incoming GTFS-RT messages as they stream in by placing a - * GTFS-RT decoder Runnable task in the single-threaded executor for handling. - * - *

    - * websocket.type = websocket-gtfs-rt-updater
    - * websocket.defaultAgencyId = agency
    - * websocket.url = ws://localhost:8088/tripUpdates
    - * 
    - */ -public class WebsocketGtfsRealtimeUpdater implements GraphUpdater { - - private static final Logger LOG = LoggerFactory.getLogger(WebsocketGtfsRealtimeUpdater.class); - - /** - * Number of seconds to wait before checking again whether we are still connected - */ - private static final int CHECK_CONNECTION_PERIOD_SEC = 1; - - /** - * Url of the websocket server - */ - private final String url; - - /** - * The ID for the static feed to which these TripUpdates are applied - */ - private final String feedId; - - /** - * The number of seconds to wait before reconnecting after a failed connection. - */ - private final int reconnectPeriodSec; - - private final String configRef; - - private final BackwardsDelayPropagationType backwardsDelayPropagationType; - - private final TimetableSnapshotSource snapshotSource; - - /** - * Parent update manager. Is used to execute graph writer runnables. - */ - private WriteToGraphCallback saveResultOnGraph; - - private GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher; - - private final Consumer recordMetrics; - - public WebsocketGtfsRealtimeUpdater( - WebsocketGtfsRealtimeUpdaterParameters parameters, - TimetableSnapshotSource snapshotSource, - TransitModel transitModel - ) { - this.configRef = parameters.configRef(); - this.url = parameters.url(); - this.feedId = parameters.feedId(); - this.reconnectPeriodSec = parameters.getReconnectPeriodSec(); - this.backwardsDelayPropagationType = parameters.getBackwardsDelayPropagationType(); - this.snapshotSource = snapshotSource; - this.fuzzyTripMatcher = - new GtfsRealtimeFuzzyTripMatcher(new DefaultTransitService(transitModel)); - this.recordMetrics = TripUpdateMetrics.streaming(parameters); - } - - @Override - public void setGraphUpdaterManager(WriteToGraphCallback saveResultOnGraph) { - this.saveResultOnGraph = saveResultOnGraph; - } - - @Override - public void run() throws InterruptedException, IOException { - while (true) { - AsyncHttpClient client = asyncHttpClient(); - WebSocketListener listener = new Listener(); - WebSocketUpgradeHandler handler = new WebSocketUpgradeHandler.Builder() - .addWebSocketListener(listener) - .build(); - WebSocket socket = null; - boolean connectionSuccessful = true; - // Try to create a websocket connection - try { - socket = client.prepareGet(url).execute(handler).get(); - LOG.info("Successfully connected to {}.", url); - } catch (ExecutionException e) { - LOG.error("Could not connect to {}: {}", url, e.getCause().getMessage()); - connectionSuccessful = false; - } catch (Exception e) { - LOG.error("Unknown exception when trying to connect to {}.", url, e); - connectionSuccessful = false; - } - - // If connection was unsuccessful, wait some time before trying again - if (!connectionSuccessful) { - Thread.sleep(reconnectPeriodSec * 1000); - } - - // Keep checking whether connection is still open - while (true) { - if (socket == null || !socket.isOpen()) { - // The connection is closed somehow, try to reconnect - if (connectionSuccessful) { - LOG.warn("Connection to {} was lost. Trying to reconnect...", url); - } - break; - } - Thread.sleep(CHECK_CONNECTION_PERIOD_SEC * 1000); - } - - client.close(); - } - } - - @Override - public String getConfigRef() { - return configRef; - } - - /** - * Auxiliary class to handle incoming messages via the websocket connection - */ - private class Listener implements WebSocketListener { - - @Override - public void onOpen(WebSocket websocket) {} - - @Override - public void onClose(WebSocket websocket, int code, String reason) {} - - @Override - public void onError(Throwable t) {} - - @Override - public void onBinaryFrame(byte[] message, boolean finalFragment, int rsv) { - FeedMessage feedMessage; - List feedEntityList; - List updates = null; - boolean fullDataset = true; - try { - // Decode message - feedMessage = FeedMessage.PARSER.parseFrom(message); - feedEntityList = feedMessage.getEntityList(); - - // Change fullDataset value if this is an incremental update - if ( - feedMessage.hasHeader() && - feedMessage.getHeader().hasIncrementality() && - feedMessage - .getHeader() - .getIncrementality() - .equals(GtfsRealtime.FeedHeader.Incrementality.DIFFERENTIAL) - ) { - fullDataset = false; - } - - // Create List of TripUpdates - updates = new ArrayList<>(feedEntityList.size()); - for (FeedEntity feedEntity : feedEntityList) { - if (feedEntity.hasTripUpdate()) { - updates.add(feedEntity.getTripUpdate()); - } - } - } catch (InvalidProtocolBufferException e) { - LOG.error("Could not decode gtfs-rt message:", e); - } - - if (updates != null) { - // Handle trip updates via graph writer runnable - TripUpdateGraphWriterRunnable runnable = new TripUpdateGraphWriterRunnable( - snapshotSource, - fuzzyTripMatcher, - backwardsDelayPropagationType, - fullDataset, - updates, - feedId, - recordMetrics - ); - saveResultOnGraph.execute(runnable); - } - } - } -} diff --git a/src/main/java/org/opentripplanner/updater/trip/WebsocketGtfsRealtimeUpdaterParameters.java b/src/main/java/org/opentripplanner/updater/trip/WebsocketGtfsRealtimeUpdaterParameters.java deleted file mode 100644 index d5eebd748af..00000000000 --- a/src/main/java/org/opentripplanner/updater/trip/WebsocketGtfsRealtimeUpdaterParameters.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.opentripplanner.updater.trip; - -public class WebsocketGtfsRealtimeUpdaterParameters implements UrlUpdaterParameters { - - private final String configRef; - private final String feedId; - private final String url; - private final int reconnectPeriodSec; - private final BackwardsDelayPropagationType backwardsDelayPropagationType; - - public WebsocketGtfsRealtimeUpdaterParameters( - String configRef, - String feedId, - String url, - int reconnectPeriodSec, - BackwardsDelayPropagationType backwardsDelayPropagationType - ) { - this.configRef = configRef; - this.feedId = feedId; - this.url = url; - this.reconnectPeriodSec = reconnectPeriodSec; - this.backwardsDelayPropagationType = backwardsDelayPropagationType; - } - - public String url() { - return url; - } - - public String feedId() { - return feedId; - } - - int getReconnectPeriodSec() { - return reconnectPeriodSec; - } - - /** The config name/type for the updater. Used to reference the configuration element. */ - public String configRef() { - return configRef; - } - - public BackwardsDelayPropagationType getBackwardsDelayPropagationType() { - return backwardsDelayPropagationType; - } -} From 4a29056bac782b0ee70868f6d5d136a8473ccdc5 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 4 Jan 2024 17:47:52 +0100 Subject: [PATCH 24/86] Remove websocket documentation --- doc-templates/UpdaterConfig.md | 12 ----- docs/RouterConfiguration.md | 4 -- docs/UpdaterConfig.md | 50 ------------------- docs/sandbox/SiriUpdater.md | 30 +++++------ .../standalone/config/router-config.json | 5 -- 5 files changed, 15 insertions(+), 86 deletions(-) diff --git a/doc-templates/UpdaterConfig.md b/doc-templates/UpdaterConfig.md index 90c699fcf28..a239317f244 100644 --- a/doc-templates/UpdaterConfig.md +++ b/doc-templates/UpdaterConfig.md @@ -45,18 +45,6 @@ The information is downloaded in a single HTTP request and polled regularly. -### TripUpdates via WebSocket - -This updater doesn't poll a data source but opens a persistent connection to the GTFS-RT provider, -which then sends incremental updates immediately as they become available. - -The [OneBusAway GTFS-realtime exporter project](https://github.com/OneBusAway/onebusaway-gtfs-realtime-exporter) -provides this kind of streaming, incremental updates over a websocket rather than a single large -file. - - - - ### Vehicle Positions VehiclePositions give the location of some or all vehicles currently in service, in terms of diff --git a/docs/RouterConfiguration.md b/docs/RouterConfiguration.md index 65f50260ee5..30e423f29b1 100644 --- a/docs/RouterConfiguration.md +++ b/docs/RouterConfiguration.md @@ -740,10 +740,6 @@ Used to group requests when monitoring OTP. "position" ] }, - { - "type" : "websocket-gtfs-rt-updater", - "feedId" : "ov" - }, { "type" : "siri-et-updater", "url" : "https://example.com/some/path", diff --git a/docs/UpdaterConfig.md b/docs/UpdaterConfig.md index 6b744794642..6c219d07ddf 100644 --- a/docs/UpdaterConfig.md +++ b/docs/UpdaterConfig.md @@ -165,56 +165,6 @@ HTTP headers to add to the request. Any header key, value can be inserted. -### TripUpdates via WebSocket - -This updater doesn't poll a data source but opens a persistent connection to the GTFS-RT provider, -which then sends incremental updates immediately as they become available. - -The [OneBusAway GTFS-realtime exporter project](https://github.com/OneBusAway/onebusaway-gtfs-realtime-exporter) -provides this kind of streaming, incremental updates over a websocket rather than a single large -file. - - - - -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|-----------------------------------------------------------------------|:---------:|--------------------------|:----------:|----------------------|:-----:| -| type = "websocket-gtfs-rt-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [backwardsDelayPropagationType](#u__7__backwardsDelayPropagationType) | `enum` | TODO | *Optional* | `"required-no-data"` | 1.5 | -| feedId | `string` | TODO | *Required* | | 1.5 | -| reconnectPeriodSec | `integer` | TODO | *Optional* | `60` | 1.5 | -| url | `string` | TODO | *Optional* | | 1.5 | - - -##### Parameter details - -

    backwardsDelayPropagationType

    - -**Since version:** `1.5` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"required-no-data"` -**Path:** /updaters/[7] -**Enum values:** `required-no-data` | `required` | `always` - -TODO - - - -##### Example configuration - -```JSON -// router-config.json -{ - "updaters" : [ - { - "type" : "websocket-gtfs-rt-updater", - "feedId" : "ov" - } - ] -} -``` - - - - ### Vehicle Positions VehiclePositions give the location of some or all vehicles currently in service, in terms of diff --git a/docs/sandbox/SiriUpdater.md b/docs/sandbox/SiriUpdater.md index f6c4c3f999f..7730df67d12 100644 --- a/docs/sandbox/SiriUpdater.md +++ b/docs/sandbox/SiriUpdater.md @@ -37,16 +37,16 @@ To enable the SIRI updater you need to add it to the updaters section of the `ro | previewInterval | `duration` | TODO | *Optional* | | 2.0 | | requestorRef | `string` | The requester reference. | *Optional* | | 2.0 | | timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.0 | -| [url](#u__8__url) | `string` | The URL to send the HTTP requests to. | *Required* | | 2.0 | -| [headers](#u__8__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | +| [url](#u__7__url) | `string` | The URL to send the HTTP requests to. | *Required* | | 2.0 | +| [headers](#u__7__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | ##### Parameter details -

    url

    +

    url

    **Since version:** `2.0` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[8] +**Path:** /updaters/[7] The URL to send the HTTP requests to. @@ -58,10 +58,10 @@ renamed by the loader when processed: -

    headers

    +

    headers

    **Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[8] +**Path:** /updaters/[7] HTTP headers to add to the request. Any header key, value can be inserted. @@ -97,21 +97,21 @@ HTTP headers to add to the request. Any header key, value can be inserted. |---------------------------------|:---------------:|--------------------------------------------------------------------------------------------------------|:----------:|---------------|:-----:| | type = "siri-sx-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | | blockReadinessUntilInitialized | `boolean` | Whether catching up with the updates should block the readiness check from returning a 'ready' result. | *Optional* | `false` | 2.0 | -| [earlyStart](#u__9__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | +| [earlyStart](#u__8__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | | feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.0 | | frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.0 | | requestorRef | `string` | The requester reference. | *Optional* | | 2.0 | | timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.0 | -| [url](#u__9__url) | `string` | The URL to send the HTTP requests to. Supports http/https and file protocol. | *Required* | | 2.0 | -| [headers](#u__9__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | +| [url](#u__8__url) | `string` | The URL to send the HTTP requests to. Supports http/https and file protocol. | *Required* | | 2.0 | +| [headers](#u__8__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | ##### Parameter details -

    earlyStart

    +

    earlyStart

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

    url

    +

    url

    **Since version:** `2.0` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[9] +**Path:** /updaters/[8] The URL to send the HTTP requests to. Supports http/https and file protocol. @@ -135,10 +135,10 @@ renamed by the loader when processed: -

    headers

    +

    headers

    **Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[9] +**Path:** /updaters/[8] HTTP headers to add to the request. Any header key, value can be inserted. diff --git a/src/test/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index 69f859d784d..1a5eec22a28 100644 --- a/src/test/resources/standalone/config/router-config.json +++ b/src/test/resources/standalone/config/router-config.json @@ -295,11 +295,6 @@ "fuzzyTripMatching": false, "features": ["position"] }, - // Streaming differential GTFS-RT TripUpdates over websockets - { - "type": "websocket-gtfs-rt-updater", - "feedId": "ov" - }, // Siri-ET over HTTP { "type": "siri-et-updater", From 629ec1b0129bdfbd78f7467869a28ef13806250c Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 2 Jan 2024 17:56:47 +0100 Subject: [PATCH 25/86] feature: Filter away none optimal access/egress paths for multi-criteria Raptor --- .../transit/AccessEgressFunctions.java | 94 +++++++++++++++---- .../rangeraptor/transit/AccessPaths.java | 5 +- .../rangeraptor/transit/EgressPaths.java | 5 +- .../transit/AccessEgressFunctionsTest.java | 91 ++++++++++++++++-- .../rangeraptor/transit/EgressPathsTest.java | 11 +-- 5 files changed, 171 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java index 5059be84312..275a0f85cf3 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java @@ -5,12 +5,15 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.function.Predicate; import java.util.function.ToIntFunction; import java.util.stream.Collectors; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.util.paretoset.ParetoComparator; import org.opentripplanner.raptor.util.paretoset.ParetoSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class contains functions used by the {@link AccessPaths} and {@link EgressPaths} classes. @@ -23,6 +26,8 @@ */ public final class AccessEgressFunctions { + private static final Logger LOG = LoggerFactory.getLogger(AccessEgressFunctions.class); + /** * Filter standard(not multi-criteria) Raptor access and egress paths. A path is pareto optimal * for a given stop if @@ -47,32 +52,64 @@ public final class AccessEgressFunctions { * */ private static final ParetoComparator STANDARD_COMPARATOR = (l, r) -> - (l.stopReachedOnBoard() && !r.stopReachedOnBoard()) || - r.hasOpeningHours() || - (l.numberOfRides() < r.numberOfRides()) || - (l.durationInSeconds() < r.durationInSeconds()); + ( + l.stopReachedOnBoard() && + !r.stopReachedOnBoard() || + r.hasOpeningHours() || + l.numberOfRides() < r.numberOfRides() || + l.durationInSeconds() < r.durationInSeconds() + ); + + /** + * Filter Multi-criteria Raptor access and egress paths. This can be used to wash + * access/egress paths - paths that are not optimal using this should not be passed into + * Raptor - it is a bug. + */ + private static final ParetoComparator MC_COMPARATOR = (l, r) -> + ( + (l.stopReachedOnBoard() && !r.stopReachedOnBoard()) || + r.hasOpeningHours() || + l.numberOfRides() < r.numberOfRides() || + l.durationInSeconds() < r.durationInSeconds() || + l.c1() < r.c1() + ); /** private constructor to prevent instantiation of utils class. */ private AccessEgressFunctions() {} + /** + * Filter non-optimal paths away for the standard search. This method does not + * look at the c1 value. + */ static Collection removeNoneOptimalPathsForStandardRaptor( Collection paths ) { - // To avoid too many items in the pareto set we first group the paths by stop, - // for each stop we filter it down to the optimal pareto set. We could do this - // for multi-criteria as well, but it is likely not so important. The focus for - // the mc-set should be that the list of access/egress created in OTP should not - // contain to many non-optimal paths. - var mapByStop = groupByStop(paths); - var set = new ParetoSet<>(STANDARD_COMPARATOR); - Collection result = new ArrayList<>(); + return removeNoneOptimalPaths(paths, STANDARD_COMPARATOR); + } - mapByStop.forEachValue(list -> { - set.clear(); - set.addAll(list); - result.addAll(set); - return true; - }); + /** + * Filter non-optimal paths away for the multi-criteria search. This method should in theory + * not remove any paths since the caller should not pass in duplicates, but it turns out that + * this happens, so we do it. + */ + static Collection removeNoneOptimalPathsForMcRaptor( + Collection paths + ) { + var result = removeNoneOptimalPaths(paths, MC_COMPARATOR); + if (LOG.isDebugEnabled() && result.size() < paths.size()) { + var duplicates = new ArrayList<>(paths); + duplicates.removeAll(result); + // Note! This does not provide enough information to solve/debug this problem, but this is + // not a problem in Raptor, so we do not want to add more specific logging here - this does + // however document that the problem exist. Turn on debug logging and move the start/end + // coordinate around until you see this message. + // + // See https://github.com/opentripplanner/OpenTripPlanner/issues/5601 + LOG.warn( + "Duplicate access/egress paths passed into raptor:\n\t" + + duplicates.stream().map(Objects::toString).collect(Collectors.joining("\n\t")) + ); + } return result; } @@ -96,6 +133,27 @@ static TIntObjectMap> groupByStop(Collection removeNoneOptimalPaths( + Collection paths, + ParetoComparator comparator + ) { + var mapByStop = groupByStop(paths); + var set = new ParetoSet<>(comparator); + var result = new ArrayList(); + + for (int stop : mapByStop.keys()) { + var list = mapByStop.get(stop); + set.clear(); + set.addAll(list); + result.addAll(set); + } + return result; + } + private static List getOrCreate( int key, TIntObjectMap> map diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java index 940937f82db..dd757ac0415 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java @@ -1,6 +1,7 @@ package org.opentripplanner.raptor.rangeraptor.transit; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.groupByRound; +import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForMcRaptor; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForStandardRaptor; import gnu.trove.map.TIntObjectMap; @@ -57,7 +58,9 @@ public int calculateMaxNumberOfRides() { * This method is static and package local to enable unit-testing. */ public static AccessPaths create(Collection paths, RaptorProfile profile) { - if (!profile.is(RaptorProfile.MULTI_CRITERIA)) { + if (profile.is(RaptorProfile.MULTI_CRITERIA)) { + paths = removeNoneOptimalPathsForMcRaptor(paths); + } else { paths = removeNoneOptimalPathsForStandardRaptor(paths); } return new AccessPaths( diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java index 2038ab543df..a1688ff8f5b 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java @@ -2,6 +2,7 @@ import static org.opentripplanner.raptor.api.request.RaptorProfile.MULTI_CRITERIA; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.groupByStop; +import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForMcRaptor; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForStandardRaptor; import gnu.trove.map.TIntObjectMap; @@ -31,7 +32,9 @@ private EgressPaths(TIntObjectMap> pathsByStop) { * This method is static and package local to enable unit-testing. */ public static EgressPaths create(Collection paths, RaptorProfile profile) { - if (!MULTI_CRITERIA.is(profile)) { + if (MULTI_CRITERIA.is(profile)) { + paths = removeNoneOptimalPathsForMcRaptor(paths); + } else { paths = removeNoneOptimalPathsForStandardRaptor(paths); } return new EgressPaths(groupByStop(paths)); diff --git a/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java b/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java index eab9ee418cf..041d74786d7 100644 --- a/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java +++ b/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java @@ -6,6 +6,7 @@ import static org.opentripplanner.raptor._data.transit.TestAccessEgress.flexAndWalk; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.groupByRound; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.groupByStop; +import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForMcRaptor; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForStandardRaptor; import java.util.Arrays; @@ -24,19 +25,26 @@ class AccessEgressFunctionsTest implements RaptorTestConstants { public static final int TRANSFER_SLACK = D1m; private static final int STOP = 8; - - private static final RaptorAccessEgress WALK_10m = TestAccessEgress.walk(STOP, D10m); - private static final RaptorAccessEgress WALK_8m = TestAccessEgress.walk(STOP, D8m); - private static final RaptorAccessEgress FLEX_1x_10m = flex(STOP, D10m, 1); - private static final RaptorAccessEgress FLEX_1x_8m = flex(STOP, D8m, 1); - private static final RaptorAccessEgress FLEX_2x_8m = flex(STOP, D8m, 2); - private static final RaptorAccessEgress FLEX_AND_WALK_1x_8m = flexAndWalk(STOP, D8m, 1); + private static final int C1 = 1000; + private static final int C1_LOW = 999; + + private static final RaptorAccessEgress WALK_10m = TestAccessEgress.walk(STOP, D10m, C1); + private static final RaptorAccessEgress WALK_10m_C1_LOW = TestAccessEgress.walk( + STOP, + D10m, + C1_LOW + ); + private static final RaptorAccessEgress WALK_8m = TestAccessEgress.walk(STOP, D8m, C1); + private static final RaptorAccessEgress FLEX_1x_10m = flex(STOP, D10m, 1, C1); + private static final RaptorAccessEgress FLEX_1x_8m = flex(STOP, D8m, 1, C1); + private static final RaptorAccessEgress FLEX_2x_8m = flex(STOP, D8m, 2, C1); + private static final RaptorAccessEgress FLEX_AND_WALK_1x_8m = flexAndWalk(STOP, D8m, 1, C1); private static final RaptorAccessEgress WALK_W_OPENING_HOURS_8m = TestAccessEgress - .walk(STOP, D8m) + .walk(STOP, D8m, C1) .openingHours(T00_00, T01_00); private static final RaptorAccessEgress WALK_W_OPENING_HOURS_8m_OTHER = TestAccessEgress - .walk(STOP, D8m) + .walk(STOP, D8m, C1) .openingHours(T00_10, T01_00); @Test @@ -101,6 +109,71 @@ void removeNoneOptimalPathsForStandardRaptorTest() { ); } + @Test + void removeNoneOptimalPathsForMcRaptorTest() { + // Empty set + assertElements(List.of(), removeNoneOptimalPathsForMcRaptor(List.of())); + + // One element + assertElements(List.of(WALK_8m), removeNoneOptimalPathsForMcRaptor(List.of(WALK_8m))); + + // Lowest cost + assertElements( + List.of(WALK_10m_C1_LOW), + removeNoneOptimalPathsForMcRaptor(List.of(WALK_10m, WALK_10m_C1_LOW)) + ); + + // Shortest duration + assertElements(List.of(WALK_8m), removeNoneOptimalPathsForMcRaptor(List.of(WALK_8m, WALK_10m))); + + // Fewest rides + assertElements( + List.of(FLEX_1x_8m), + removeNoneOptimalPathsForMcRaptor(List.of(FLEX_1x_8m, FLEX_2x_8m)) + ); + + // Arriving at the stop on-board, and by-foot. + // OnBoard is better because we can do a transfer walk to nearby stops. + assertElements( + List.of(FLEX_1x_8m), + removeNoneOptimalPathsForMcRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_8m)) + ); + + // Flex+walk is faster, flex arrive on-board, both is optimal + assertElements( + List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_10m), + removeNoneOptimalPathsForStandardRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_10m)) + ); + + // Walk has few rides, and Flex is faster - both is optimal + assertElements( + List.of(WALK_10m, FLEX_1x_8m), + removeNoneOptimalPathsForMcRaptor(List.of(WALK_10m, FLEX_1x_8m)) + ); + + // Walk without opening hours is better than with, because it can be time-shifted without + // any constraints + assertElements( + List.of(WALK_8m), + removeNoneOptimalPathsForMcRaptor(List.of(WALK_8m, WALK_W_OPENING_HOURS_8m)) + ); + + // Walk with opening hours can NOT dominate another access/egress without - even if it is + // faster. The reason is that it may not be allowed to time-shift it to the desired time. + assertElements( + List.of(WALK_10m, WALK_W_OPENING_HOURS_8m), + removeNoneOptimalPathsForMcRaptor(List.of(WALK_10m, WALK_W_OPENING_HOURS_8m)) + ); + + // If two paths both have opening hours, both should be accepted. + assertElements( + List.of(WALK_W_OPENING_HOURS_8m, WALK_W_OPENING_HOURS_8m_OTHER), + removeNoneOptimalPathsForMcRaptor( + List.of(WALK_W_OPENING_HOURS_8m, WALK_W_OPENING_HOURS_8m_OTHER) + ) + ); + } + @Test void groupByRoundTest() { // Map one element diff --git a/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPathsTest.java b/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPathsTest.java index 6d3b25c1d31..144904fa3e6 100644 --- a/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPathsTest.java +++ b/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPathsTest.java @@ -38,7 +38,7 @@ class EgressPathsTest { // Number of rides, smallest is better flex(STOP_D, D1m, 2), flex(STOP_D, D1m, 3), - // Opening Hours dominate each other(no check on overlapping) + // Opening Hours dominate each other (no check on overlapping) walk(STOP_E, D2m), walk(STOP_E, D1m).openingHours("10:00", "11:45"), walk(STOP_E, D1m).openingHours("11:30", "12:30"), @@ -83,18 +83,15 @@ void listAll() { """.strip(), subjectStd.listAll().stream().map(Object::toString).sorted().collect(Collectors.joining("\n")) ); + assertEquals( """ Flex 1m C₁120 1x ~ 3 Flex 1m C₁120 1x ~ 4 Flex 1m C₁120 2x ~ 5 - Flex 1m C₁120 3x ~ 5 - Flex 2m C₁240 1x ~ 3 - Flex+Walk 1m C₁120 1x ~ 4 Walk 1m C₁120 Open(10:00 11:45) ~ 6 Walk 1m C₁120 Open(11:30 12:30) ~ 6 Walk 1m C₁120 ~ 2 - Walk 2m C₁240 Open(14:00 14:00) ~ 6 Walk 2m C₁240 ~ 6 """.strip(), subjectMc.listAll().stream().map(Object::toString).sorted().collect(Collectors.joining("\n")) @@ -107,8 +104,10 @@ void walkToDestinationEgressStops() { toString(new int[] { STOP_A, STOP_E }), toString(subjectStd.egressesWitchStartByWalking()) ); + + //[2, 6] assertEquals( - toString(new int[] { STOP_A, STOP_C, STOP_E }), + toString(new int[] { STOP_A, STOP_E }), toString(subjectMc.egressesWitchStartByWalking()) ); } From 17d20c9d41a3d6345549c5fd8416fbdcd21d948e Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Wed, 3 Jan 2024 16:08:30 +0100 Subject: [PATCH 26/86] feature: Enable Raptor debug logging by event type --- .../transit/mappers/RaptorRequestMapper.java | 21 ++++++++++++++++--- .../routing/api/request/DebugEventType.java | 11 ++++++++++ .../routing/api/request/DebugRaptor.java | 19 ++++++++++++++++- 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/opentripplanner/routing/api/request/DebugEventType.java 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 75c1bcf7214..e63546a87a2 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 @@ -13,6 +13,7 @@ import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.model.RaptorConstants; import org.opentripplanner.raptor.api.model.RelaxFunction; +import org.opentripplanner.raptor.api.request.DebugRequestBuilder; import org.opentripplanner.raptor.api.request.Optimization; import org.opentripplanner.raptor.api.request.PassThroughPoint; import org.opentripplanner.raptor.api.request.RaptorRequest; @@ -22,6 +23,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.TripSchedule; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.RaptorCostConverter; import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitPriorityGroup32n; +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.site.StopLocation; @@ -156,10 +158,11 @@ private RaptorRequest doMap() { .addStops(raptorDebugging.stops()) .setPath(raptorDebugging.path()) .debugPathFromStopIndex(raptorDebugging.debugPathFromStopIndex()) - .stopArrivalListener(debugLogger::stopArrivalLister) - .patternRideDebugListener(debugLogger::patternRideLister) - .pathFilteringListener(debugLogger::pathFilteringListener) .logger(debugLogger); + + for (var type : raptorDebugging.eventTypes()) { + addLogListenerForEachEventTypeRequested(debug, type, debugLogger); + } } if (!request.timetableView() && request.arriveBy()) { @@ -209,4 +212,16 @@ private int relativeTime(Instant time) { } return (int) (time.getEpochSecond() - transitSearchTimeZeroEpocSecond); } + + private static void addLogListenerForEachEventTypeRequested( + DebugRequestBuilder target, + DebugEventType type, + SystemErrDebugLogger logger + ) { + switch (type) { + case STOP_ARRIVALS -> target.stopArrivalListener(logger::stopArrivalLister); + case PATTERN_RIDES -> target.patternRideDebugListener(logger::patternRideLister); + case DESTINATION_ARRIVALS -> target.pathFilteringListener(logger::pathFilteringListener); + } + } } diff --git a/src/main/java/org/opentripplanner/routing/api/request/DebugEventType.java b/src/main/java/org/opentripplanner/routing/api/request/DebugEventType.java new file mode 100644 index 00000000000..09f9dbbe732 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/api/request/DebugEventType.java @@ -0,0 +1,11 @@ +package org.opentripplanner.routing.api.request; + +/** + * Raptor check paths in 3 different places. The debugger can print events + * for each of these. + */ +public enum DebugEventType { + STOP_ARRIVALS, + PATTERN_RIDES, + DESTINATION_ARRIVALS, +} diff --git a/src/main/java/org/opentripplanner/routing/api/request/DebugRaptor.java b/src/main/java/org/opentripplanner/routing/api/request/DebugRaptor.java index 7d6c1472eee..0b9ff4f0c30 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/DebugRaptor.java +++ b/src/main/java/org/opentripplanner/routing/api/request/DebugRaptor.java @@ -3,7 +3,11 @@ import java.io.Serial; import java.io.Serializable; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Set; import java.util.regex.Pattern; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.slf4j.Logger; @@ -46,14 +50,16 @@ public class DebugRaptor implements Serializable { private List stops = List.of(); private List path = List.of(); private int debugPathFromStopIndex = 0; + private Set eventTypes = EnumSet.noneOf(DebugEventType.class); public DebugRaptor() {} - /** Avoid using clone(), use copy-constructor instead(Josh Bloch). */ + /** Avoid using clone(), use copy-constructor instead (Josh Bloch). */ public DebugRaptor(DebugRaptor other) { this.stops = List.copyOf(other.stops); this.path = List.copyOf(other.path); this.debugPathFromStopIndex = other.debugPathFromStopIndex; + this.eventTypes = EnumSet.copyOf(other.eventTypes); } public boolean isEnabled() { @@ -90,12 +96,23 @@ public int debugPathFromStopIndex() { return debugPathFromStopIndex; } + public Set eventTypes() { + return Collections.unmodifiableSet(eventTypes); + } + + public DebugRaptor withEventTypes(Collection eventTypes) { + this.eventTypes.clear(); + this.eventTypes.addAll(eventTypes); + return this; + } + @Override public String toString() { return ToStringBuilder .of(DebugRaptor.class) .addObj("stops", toString(stops, FIRST_STOP_INDEX)) .addObj("path", toString(path, debugPathFromStopIndex)) + .addCol("eventType", eventTypes) .toString(); } From a3e426fce2e9f35e062a5d0e5c705f5113f95af2 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Wed, 3 Jan 2024 19:21:07 +0100 Subject: [PATCH 27/86] sandbox: Extract log config and rearrange OTP Startup View --- .../InteractiveOtpMain.java | 19 ++- .../ext/interactivelauncher/Model.java | 55 ++++---- .../OtpDebugController.java | 35 +++++ .../ext/interactivelauncher/SetupResult.java | 99 ------------- .../{ => logging}/DebugLoggingSupport.java | 33 ++--- .../interactivelauncher/logging/LogModel.java | 56 ++++++++ .../interactivelauncher/logging/LogView.java | 34 +++++ .../logging/OTPDebugLoggers.java | 19 +++ .../DataSourceRootView.java} | 17 ++- .../startup/DataSourcesView.java | 133 ++++++++++++++++++ .../{views => startup}/MainView.java | 127 +++++------------ .../{views => startup}/OptionsView.java | 64 ++++----- .../StartOtpButtonView.java | 4 +- .../startup/StatusBar.java | 15 ++ .../{ => support}/SearchForOtpConfig.java | 10 +- .../{views => support}/ViewUtils.java | 23 +-- .../views/DataSourcesView.java | 84 ----------- .../interactivelauncher/views/StatusBar.java | 15 -- 18 files changed, 434 insertions(+), 408 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java delete mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/SetupResult.java rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{ => logging}/DebugLoggingSupport.java (61%) create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogModel.java create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogView.java create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/OTPDebugLoggers.java rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{views/SearchDirectoryView.java => startup/DataSourceRootView.java} (80%) create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{views => startup}/MainView.java (55%) rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{views => startup}/OptionsView.java (63%) rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{views => startup}/StartOtpButtonView.java (82%) create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StatusBar.java rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{ => support}/SearchForOtpConfig.java (85%) rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{views => support}/ViewUtils.java (53%) delete mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/views/DataSourcesView.java delete mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/views/StatusBar.java diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java index 1f9227b3be6..d61ae6bfcd3 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java @@ -1,19 +1,18 @@ package org.opentripplanner.ext.interactivelauncher; -import static org.opentripplanner.ext.interactivelauncher.DebugLoggingSupport.configureDebugLogging; - -import org.opentripplanner.ext.interactivelauncher.views.MainView; +import org.opentripplanner.ext.interactivelauncher.startup.MainView; import org.opentripplanner.standalone.OTPMain; /** - * This class provide a main method to start a GUI which can start OTPMain. + * This class provides a main method to start a GUI which can start OTPMain. *

    - * The UI allow the user to select a OTP configuration data set. The list of data location is - * created by searching the a root data source directory. + * The UI allows the user to select the OTP configuration dataset. The list of data locations is + * created by searching the root data source directory. *

    - * The user then select what he/she want OTP to do. The settings are stored in the - * .interactive_otp_main.json file in the folder InteractiveOtpMain is started. The - * settings from the last run is loaded next time InteractiveOtpMain is started. + * The user then selects what he/she wants OTP to do. + * The settings are stored in the + * .interactive_otp_main.json file in the folder InteractiveOtpMain is started. + * The settings from the last run are loaded the next time InteractiveOtpMain is started. */ public class InteractiveOtpMain { @@ -32,7 +31,7 @@ private void run() { private void startOtp() { model.save(); - configureDebugLogging(model.getDebugLogging()); + new OtpDebugController(model).start(); System.out.println("Start OTP: " + model + "\n"); OTPMain.main(model.asOtpArgs()); diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java index ee2dde3e684..1550f8bc1ab 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java @@ -6,11 +6,10 @@ import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.function.Consumer; +import org.opentripplanner.ext.interactivelauncher.logging.LogModel; +import org.opentripplanner.ext.interactivelauncher.support.SearchForOtpConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,8 +19,6 @@ public class Model implements Serializable { private static final File MODEL_FILE = new File("interactive_otp_main.json"); - private final Map debugLogging = new HashMap<>(); - @JsonIgnore private transient Consumer commandLineChange; @@ -32,13 +29,16 @@ public class Model implements Serializable { private boolean saveGraph = false; private boolean serveGraph = true; private boolean visualizer = false; + private LogModel logModel; - public Model() { - setupListOfDebugLoggers(); - } + public Model() {} public static Model load() { - return MODEL_FILE.exists() ? readFromFile() : new Model(); + var model = MODEL_FILE.exists() ? readFromFile() : createNew(); + // Setup callbacks + model.logModel.init(model::save); + + return model; } public void subscribeCmdLineUpdates(Consumer commandLineChange) { @@ -134,19 +134,6 @@ public void setVisualizer(boolean visualizer) { notifyChangeListener(); } - public Map getDebugLogging() { - return debugLogging; - } - - public void setDebugLogging(Map map) { - for (Entry e : map.entrySet()) { - // Only keep entries that exist in the log config - if (debugLogging.containsKey(e.getKey())) { - debugLogging.put(e.getKey(), e.getValue()); - } - } - } - @Override public String toString() { return ( @@ -174,6 +161,10 @@ public void save() { } } + public LogModel getLogModel() { + return logModel; + } + @JsonIgnore String getDataSourceDirectory() { if (dataSource == null) { @@ -210,16 +201,26 @@ String[] asOtpArgs() { return args.toArray(new String[0]); } + private static Model createNew() { + var model = new Model(); + model.logModel = new LogModel(); + model.logModel.initFromConfig(); + model.setupCallbacks(); + return model; + } + private static Model readFromFile() { try { - return new ObjectMapper().readValue(MODEL_FILE, Model.class); + var model = new ObjectMapper().readValue(MODEL_FILE, Model.class); + model.setupCallbacks(); + return model; } catch (IOException e) { System.err.println( "Unable to read the InteractiveOtpMain state cache. If the model changed this " + "is expected, and it will work next time. Cause: " + e.getMessage() ); - return new Model(); + return createNew(); } } @@ -237,9 +238,7 @@ private boolean buildStreetOnly() { return buildStreet && !buildTransit; } - private void setupListOfDebugLoggers() { - for (String log : DebugLoggingSupport.getLogs()) { - debugLogging.put(log, Boolean.FALSE); - } + private void setupCallbacks() { + logModel.init(this::save); } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java new file mode 100644 index 00000000000..95635c5bb0c --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java @@ -0,0 +1,35 @@ +package org.opentripplanner.ext.interactivelauncher; + +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.BACKGROUND; + +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import org.opentripplanner.ext.interactivelauncher.logging.LogModel; +import org.opentripplanner.ext.interactivelauncher.logging.LogView; + +public class OtpDebugController { + + private final JFrame debugFrame = new JFrame("OTP Debug Controller"); + + public OtpDebugController(Model model) { + var tabPanel = new JTabbedPane(); + tabPanel.addTab("Logging", createLogPanel(model.getLogModel())); + tabPanel.addTab("Raptor", new JPanel()); + debugFrame.add(tabPanel); + debugFrame.getContentPane().setBackground(BACKGROUND); + start(); + } + + private static JComponent createLogPanel(LogModel logModel) { + return new LogView(logModel).panel(); + } + + public void start() { + debugFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + debugFrame.pack(); + debugFrame.setLocationRelativeTo(null); + debugFrame.setVisible(true); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/SetupResult.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/SetupResult.java deleted file mode 100644 index a6ecf5e7229..00000000000 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/SetupResult.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.opentripplanner.ext.interactivelauncher; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class SetupResult { - - private final File configDataDir; - private final boolean buildStreet; - private final boolean buildTransit; - private final boolean saveGraph; - private final boolean serveGraph; - - public SetupResult( - File configDataDir, - boolean buildStreet, - boolean buildTransit, - boolean saveGraph, - boolean serveGraph - ) { - this.configDataDir = configDataDir; - this.buildStreet = buildStreet; - this.buildTransit = buildTransit; - this.saveGraph = saveGraph; - this.serveGraph = serveGraph; - } - - @Override - public String toString() { - return ( - "SetupResult{" + - "configDataDir=" + - configDataDir.getAbsolutePath() + - (buildStreet ? ", buildStreet" : "") + - (buildTransit ? ", buildTransit" : "") + - (saveGraph ? ", saveGraph" : "") + - (serveGraph ? ", serveGraph" : "") + - '}' - ); - } - - public String toCliString() { - return String.join(" ", asOtpArgs()); - } - - File configDataDir() { - return configDataDir; - } - - boolean buildStreet() { - return buildStreet; - } - - boolean buildTransit() { - return buildTransit; - } - - boolean buildAll() { - return buildStreet && buildTransit; - } - - boolean buildStreetOnly() { - return buildStreet && !buildTransit; - } - - boolean saveGraph() { - return saveGraph; - } - - boolean serveGraph() { - return serveGraph; - } - - String[] asOtpArgs() { - List args = new ArrayList<>(); - - if (buildAll()) { - args.add("--build"); - } else if (buildStreet) { - args.add("--buildStreet"); - } else if (buildTransit) { - args.add("--loadStreet"); - } else { - args.add("--load"); - } - - if (saveGraph && (buildTransit || buildStreet)) { - args.add("--save"); - } - if (serveGraph && !buildStreetOnly()) { - args.add("--serve"); - } - - args.add(configDataDir.getAbsolutePath()); - - return args.toArray(new String[0]); - } -} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/DebugLoggingSupport.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/DebugLoggingSupport.java similarity index 61% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/DebugLoggingSupport.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/DebugLoggingSupport.java index e0b07a8c79e..fcfa323680f 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/DebugLoggingSupport.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/DebugLoggingSupport.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.interactivelauncher; +package org.opentripplanner.ext.interactivelauncher.logging; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; @@ -10,13 +10,13 @@ import org.slf4j.LoggerFactory; /** - * Responsible for integration with the OTP Debug log configuraton, reading loggers from the slf4j + * Responsible for integration with the OTP Debug log configuration, reading loggers from the slf4j * context and setting DEBUG level on selected loggers back. *

    - * The log names are transformed to be more human readable: + * The log names are transformed to be more human-readable: *

    org.opentripplanner.routing.algorithm  -->  o.o.routing.algorithm
    */ -public class DebugLoggingSupport { +class DebugLoggingSupport { private static final String OTP = Pattern.quote("org.opentripplanner.") + ".*"; private static final String GRAPHQL = Pattern.quote("fea"); @@ -26,29 +26,26 @@ public class DebugLoggingSupport { "(" + OTP + "|" + GRAPHQL + "|" + NAMED_LOGGERS + ")" ); - public static List getLogs() { + static List getDebugLoggers() { List result = new ArrayList<>(); LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); for (Logger log : context.getLoggerList()) { var name = log.getName(); - if (!name.equals("ROOT") && LOG_MATCHER_PATTERN.matcher(name).matches()) { - result.add(logDisplayName(name)); + if (name.equals("ROOT") || log.getLevel() == null) { + continue; + } + if (log.getLevel().toInt() <= Level.DEBUG.toInt()) { + if (LOG_MATCHER_PATTERN.matcher(name).matches()) { + result.add(name); + } } } return result; } - public static void configureDebugLogging(Map loggers) { + static void configureDebugLogging(String logger, boolean debug) { LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - for (Logger log : context.getLoggerList()) { - var name = logDisplayName(log.getName()); - if (loggers.getOrDefault(name, false)) { - log.setLevel(Level.DEBUG); - } - } - } - - private static String logDisplayName(String name) { - return name.replace("org.opentripplanner.", "o.o."); + var log = context.getLogger(logger); + log.setLevel(debug ? Level.DEBUG : Level.INFO); } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogModel.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogModel.java new file mode 100644 index 00000000000..536c8d1dc87 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogModel.java @@ -0,0 +1,56 @@ +package org.opentripplanner.ext.interactivelauncher.logging; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.io.Serializable; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Responsible for storing the selected loggers to debug. This is + * serialized to store the user preferences between runs. + */ +public class LogModel implements Serializable { + + private final Set activeLoggers = new HashSet<>(); + + @JsonIgnore + private Runnable saveCallback; + + public LogModel() {} + + /** Need to set this manually to support JSON serialization. */ + public void init(Runnable saveCallback) { + this.saveCallback = saveCallback; + } + + /** Needed to do JSON serialization. */ + public Collection getActiveLoggers() { + return List.copyOf(activeLoggers); + } + + /** Needed to do JSON serialization. */ + public void setActiveLoggers(Collection loggers) { + this.activeLoggers.clear(); + this.activeLoggers.addAll(loggers); + } + + public void initFromConfig() { + activeLoggers.addAll(DebugLoggingSupport.getDebugLoggers()); + } + + boolean isLoggerEnabled(String name) { + return activeLoggers.contains(name); + } + + void turnLoggerOnOff(String name, boolean enable) { + if (enable) { + activeLoggers.add(name); + } else { + activeLoggers.remove(name); + } + DebugLoggingSupport.configureDebugLogging(name, enable); + saveCallback.run(); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogView.java new file mode 100644 index 00000000000..a71f90eb697 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogView.java @@ -0,0 +1,34 @@ +package org.opentripplanner.ext.interactivelauncher.logging; + +import javax.swing.Box; +import javax.swing.JCheckBox; +import javax.swing.JComponent; + +/** + * Display a list of loggers to turn on/off. + */ +public class LogView { + + private final Box panel = Box.createVerticalBox(); + private final LogModel model; + + public LogView(LogModel model) { + this.model = model; + OTPDebugLoggers.list().forEach(this::add); + } + + public JComponent panel() { + return panel; + } + + private void add(OTPDebugLoggers logger) { + var box = new JCheckBox(logger.label()); + box.setSelected(model.isLoggerEnabled(logger.logger())); + box.addActionListener(e -> selectLogger(logger.logger(), box.isSelected())); + panel.add(box); + } + + private void selectLogger(String logger, boolean selected) { + model.turnLoggerOnOff(logger, selected); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/OTPDebugLoggers.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/OTPDebugLoggers.java new file mode 100644 index 00000000000..4663b6017de --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/OTPDebugLoggers.java @@ -0,0 +1,19 @@ +package org.opentripplanner.ext.interactivelauncher.logging; + +import java.util.List; + +record OTPDebugLoggers(String label, String logger) { + static List list() { + return List.of( + of("Data import issues", "DATA_IMPORT_ISSUES"), + of("All OTP debuggers", "org.opentripplanner"), + of("OTP request/response", "org.opentripplanner.routing.service.DefaultRoutingService"), + of("Raptor request/response", "org.opentripplanner.raptor.RaptorService"), + of("Transfer Optimization", "org.opentripplanner.routing.algorithm.transferoptimization") + ); + } + + private static OTPDebugLoggers of(String label, String logger) { + return new OTPDebugLoggers(label, logger); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/SearchDirectoryView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourceRootView.java similarity index 80% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/views/SearchDirectoryView.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourceRootView.java index ef054e2c879..e5ba9136e9d 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/SearchDirectoryView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourceRootView.java @@ -1,7 +1,7 @@ -package org.opentripplanner.ext.interactivelauncher.views; +package org.opentripplanner.ext.interactivelauncher.startup; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.BG_STATUS_BAR; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.FG_STATUS_BAR; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.BG_STATUS_BAR; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.FG_STATUS_BAR; import java.awt.Component; import java.awt.Dimension; @@ -9,18 +9,20 @@ import java.util.function.Consumer; import javax.swing.Box; import javax.swing.JButton; +import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JTextField; +import org.opentripplanner.ext.interactivelauncher.support.ViewUtils; -public class SearchDirectoryView { +class DataSourceRootView { private final Box panel; private final JTextField fileTxt = new JTextField(); private final JButton searchBtn = new JButton("Open"); private final Consumer rootDirChangedListener; - public SearchDirectoryView(String dir, Consumer rootDirChangedListener) { + DataSourceRootView(String dir, Consumer rootDirChangedListener) { this.fileTxt.setText(dir); this.rootDirChangedListener = rootDirChangedListener; @@ -35,9 +37,6 @@ public SearchDirectoryView(String dir, Consumer rootDirChangedListener) fileTxt.setEditable(false); fileTxt.setBackground(BG_STATUS_BAR); fileTxt.setForeground(FG_STATUS_BAR); - //var d = minWidth(fileTxt.getPreferredSize(), 460); - //fileTxt.setMinimumSize(d); - //fileTxt.setPreferredSize(d); // Add text field and open button Box box = Box.createHorizontalBox(); @@ -48,7 +47,7 @@ public SearchDirectoryView(String dir, Consumer rootDirChangedListener) panel.add(box); } - public Box panel() { + public JComponent panel() { return panel; } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java new file mode 100644 index 00000000000..07052b9cd29 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java @@ -0,0 +1,133 @@ +package org.opentripplanner.ext.interactivelauncher.startup; + +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addComp; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addHorizontalGlue; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addVerticalSectionSpace; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.util.List; +import javax.swing.Box; +import javax.swing.ButtonGroup; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JRadioButton; +import org.opentripplanner.ext.interactivelauncher.Model; +import org.opentripplanner.ext.interactivelauncher.support.ViewUtils; + +class DataSourcesView { + + /* + |-----------------------------------------------| + | Label | + |-----------------------------------------------| + | ( ) List 1 | ( ) List 2 | ( ) List 3 | + | ( ) List 1 | ( ) List 2 | ( ) List 3 | + |-----------------------------------------------| + */ + + private final Box mainPanel = Box.createVerticalBox(); + private final Box listPanel = Box.createHorizontalBox(); + private final Model model; + + public DataSourcesView(Model model) { + this.model = model; + setupDataSources(); + + JLabel label = new JLabel("Select data source"); + + listPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + + addComp(label, mainPanel); + addVerticalSectionSpace(mainPanel); + addComp(listPanel, mainPanel); + } + + public JComponent panel() { + return mainPanel; + } + + public void onRootDirChange() { + model.setDataSource(null); + listPanel.removeAll(); + setupDataSources(); + listPanel.repaint(); + } + + public void onDataSourceChange(ActionEvent e) { + model.setDataSource(e.getActionCommand()); + } + + private void setupDataSources() { + final List values = model.getDataSourceOptions(); + + if (values.isEmpty()) { + model.setDataSource(null); + JLabel label = new JLabel(""); + label.setBackground(ViewUtils.BG_STATUS_BAR); + label.setForeground(ViewUtils.FG_STATUS_BAR); + addComp(label, listPanel); + return; + } + + String selectedValue = model.getDataSource(); + + if (selectedValue == null) { + selectedValue = values.get(0); + model.setDataSource(selectedValue); + } + + ButtonGroup selectDataSourceRadioGroup = new ButtonGroup(); + + List valuesSorted = values.stream().sorted().toList(); + int size = valuesSorted.size(); + + // Split the list of configuration in one, two or three columns depending on the + // number of configurations found. + if (size <= 10) { + addListPanel(valuesSorted, selectedValue, selectDataSourceRadioGroup); + } else if (size <= 20) { + int half = size / 2; + addListPanel(valuesSorted.subList(0, half), selectedValue, selectDataSourceRadioGroup); + addHorizontalGlue(listPanel); + addListPanel(valuesSorted.subList(half, size), selectedValue, selectDataSourceRadioGroup); + } else { + int third = size / 3; + addListPanel(valuesSorted.subList(0, third), selectedValue, selectDataSourceRadioGroup); + addHorizontalGlue(listPanel); + addListPanel( + valuesSorted.subList(third, third * 2), + selectedValue, + selectDataSourceRadioGroup + ); + addHorizontalGlue(listPanel); + addListPanel( + valuesSorted.subList(third * 2, size), + selectedValue, + selectDataSourceRadioGroup + ); + } + } + + private void addListPanel( + List values, + String selectedValue, + ButtonGroup selectDataSourceRadioGroup + ) { + Box column = Box.createVerticalBox(); + + for (String name : values) { + boolean selected = selectedValue.equals(name); + JRadioButton radioBtn = newRadioBtn(selectDataSourceRadioGroup, name, selected); + radioBtn.addActionListener(this::onDataSourceChange); + addComp(radioBtn, column); + } + addComp(column, listPanel); + } + + private static JRadioButton newRadioBtn(ButtonGroup group, String name, boolean selected) { + JRadioButton radioButton = new JRadioButton(name, selected); + group.add(radioButton); + return radioButton; + } +} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/MainView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/MainView.java similarity index 55% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/views/MainView.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/MainView.java index 44ede02e3eb..d147624aa61 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/MainView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/MainView.java @@ -1,11 +1,10 @@ -package org.opentripplanner.ext.interactivelauncher.views; +package org.opentripplanner.ext.interactivelauncher.startup; import static java.awt.GridBagConstraints.BOTH; import static java.awt.GridBagConstraints.CENTER; -import static java.awt.GridBagConstraints.NONE; -import static java.awt.GridBagConstraints.NORTH; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.BACKGROUND; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.debugLayout; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.BACKGROUND; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.debugLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -18,101 +17,32 @@ public class MainView { - /** Margins between components (IN) */ private static final int M_IN = 10; - - /** Margins around frame boarder (OUT) */ private static final int M_OUT = 2 * M_IN; + private static final Insets DEFAULT_INSETS = new Insets(M_OUT, M_OUT, M_IN, M_OUT); + private static final Insets SMALL_INSETS = new Insets(M_OUT, M_OUT, M_IN, M_OUT); + private static int Y = 0; /* - The application have the following 4 panels: + The application have the following panels: + +-----------------------------------+ + | Root dir [Open] | + +-----------------------------------+ + | Config Dirs Panel | + +-----------------------------------+ + | Options Panel | +-----------------------------------+ - | Root dir [Open] | - +-------------------+---------------+ - | | | - | Config Dirs Panel | Options Panel | - | | | - +-------------------+---------------+ - | Start OTP Main Panel | + | [ Start OTP ] | +-----------------------------------+ | Status Bar | +-----------------------------------+ */ - // Root dir view - private static final GridBagConstraints CONFIG_SOURCE_DIR_PANEL_CONSTRAINTS = new GridBagConstraints( - 0, - 0, - 2, - 1, - 1.0, - 0.0, - NORTH, - BOTH, - new Insets(M_OUT, M_OUT, M_IN, M_IN), - 0, - 0 - ); - - // List of locations - private static final GridBagConstraints CONFIG_DIRS_PANEL_CONSTRAINTS = new GridBagConstraints( - 0, - 1, - 1, - 1, - 1.0, - 1.0, - NORTH, - NONE, - new Insets(M_OUT, M_OUT, M_IN, M_IN), - 0, - 0 - ); - - // Options panel - private static final GridBagConstraints OPTIONS_PANEL_CONSTRAINTS = new GridBagConstraints( - 1, - 1, - 1, - 1, - 1.0, - 1.0, - NORTH, - NONE, - new Insets(M_OUT, M_IN, M_IN, M_OUT), - 0, - 0 - ); - - // Run btn and status - private static final GridBagConstraints START_OTP_BUTTON_PANEL_CONSTRAINTS = new GridBagConstraints( - 0, - 2, - 2, - 1, - 1.0, - 1.0, - CENTER, - BOTH, - new Insets(M_IN, M_OUT, M_IN, M_OUT), - 0, - 0 - ); - - // Run btn and status - private static final GridBagConstraints STATUS_BAR_CONSTRAINTS = new GridBagConstraints( - 0, - 3, - 2, - 1, - 1.0, - 0.0, - CENTER, - BOTH, - new Insets(M_IN, 0, 0, 0), - 40, - 0 - ); + private static final GridBagConstraints DATA_SOURCE_ROOT_PANEL_CONSTRAINTS = gbc(0f); + private static final GridBagConstraints DATA_SOURCE_LIST_PANEL_CONSTRAINTS = gbc(1f); + private static final GridBagConstraints OPTIONS_PANEL_CONSTRAINTS = gbc(1f); + private static final GridBagConstraints START_BUTTON_PANEL_CONSTRAINTS = gbc(0f); + private static final GridBagConstraints STATUS_BAR_CONSTRAINTS = gbc(0f, SMALL_INSETS, 40); private final JFrame mainFrame = new JFrame("Setup and Run OTP Main"); @@ -134,7 +64,7 @@ public MainView(Runnable otpStarter, Model model) throws HeadlessException { innerPanel.setLayout(layout); innerPanel.setBackground(BACKGROUND); - var sourceDirectoryView = new SearchDirectoryView( + var sourceDirectoryView = new DataSourceRootView( model.getRootDirectory(), this::onRootDirChanged ); @@ -142,16 +72,17 @@ public MainView(Runnable otpStarter, Model model) throws HeadlessException { this.optionsView = new OptionsView(model); this.startOtpButtonView = new StartOtpButtonView(); - innerPanel.add(sourceDirectoryView.panel(), CONFIG_SOURCE_DIR_PANEL_CONSTRAINTS); - innerPanel.add(dataSourcesView.panel(), CONFIG_DIRS_PANEL_CONSTRAINTS); + innerPanel.add(sourceDirectoryView.panel(), DATA_SOURCE_ROOT_PANEL_CONSTRAINTS); + innerPanel.add(dataSourcesView.panel(), DATA_SOURCE_LIST_PANEL_CONSTRAINTS); innerPanel.add(optionsView.panel(), OPTIONS_PANEL_CONSTRAINTS); - innerPanel.add(startOtpButtonView.panel(), START_OTP_BUTTON_PANEL_CONSTRAINTS); + innerPanel.add(startOtpButtonView.panel(), START_BUTTON_PANEL_CONSTRAINTS); innerPanel.add(statusBarTxt, STATUS_BAR_CONSTRAINTS); // Setup action listeners startOtpButtonView.addActionListener(e -> startOtp()); debugLayout( + sourceDirectoryView.panel(), dataSourcesView.panel(), optionsView.panel(), startOtpButtonView.panel(), @@ -186,4 +117,12 @@ private void startOtp() { mainFrame.dispose(); otpStarter.run(); } + + private static GridBagConstraints gbc(float weighty) { + return gbc(weighty, DEFAULT_INSETS, 0); + } + + private static GridBagConstraints gbc(float weighty, Insets insets, int ipadx) { + return new GridBagConstraints(0, Y++, 1, 1, 1.0, weighty, CENTER, HORIZONTAL, insets, ipadx, 0); + } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/OptionsView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java similarity index 63% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/views/OptionsView.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java index 08923f3af88..3d98bbf0cfc 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/OptionsView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java @@ -1,20 +1,18 @@ -package org.opentripplanner.ext.interactivelauncher.views; +package org.opentripplanner.ext.interactivelauncher.startup; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.addComp; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.addSectionDoubleSpace; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.addSectionSpace; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addComp; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addVerticalSectionSpace; -import java.util.List; import java.util.function.Consumer; -import java.util.stream.Collectors; import javax.swing.Box; import javax.swing.JCheckBox; +import javax.swing.JComponent; import javax.swing.JLabel; import org.opentripplanner.ext.interactivelauncher.Model; class OptionsView { - private final Box panel = Box.createVerticalBox(); + private final Box panel = Box.createHorizontalBox(); private final JCheckBox buildStreetGraphChk; private final JCheckBox buildTransitGraphChk; private final JCheckBox saveGraphChk; @@ -30,28 +28,41 @@ class OptionsView { this.startOptServerChk = new JCheckBox("Serve graph", model.isServeGraph()); this.startOptVisualizerChk = new JCheckBox("Visualizer", model.isVisualizer()); - addComp(new JLabel("Build graph"), panel); - addSectionSpace(panel); - addComp(buildStreetGraphChk, panel); - addComp(buildTransitGraphChk, panel); - addSectionDoubleSpace(panel); + panel.add(Box.createGlue()); + addComp(createBuildBox(), panel); + panel.add(Box.createGlue()); + addComp(createActionBox(), panel); + panel.add(Box.createGlue()); // Toggle [ ] save on/off buildStreetGraphChk.addActionListener(e -> onBuildGraphChkChanged()); buildTransitGraphChk.addActionListener(e -> onBuildGraphChkChanged()); startOptServerChk.addActionListener(e -> onStartOptServerChkChanged()); - addComp(new JLabel("Actions"), panel); - addSectionSpace(panel); - addComp(saveGraphChk, panel); - addComp(startOptServerChk, panel); - addComp(startOptVisualizerChk, panel); - - addDebugCheckBoxes(model); - addSectionDoubleSpace(panel); + //addSectionDoubleSpace(panel); bindCheckBoxesToModel(); } + private JComponent createBuildBox() { + var buildBox = Box.createVerticalBox(); + addComp(new JLabel("Build graph"), buildBox); + addVerticalSectionSpace(buildBox); + addComp(buildStreetGraphChk, buildBox); + addComp(buildTransitGraphChk, buildBox); + buildBox.add(Box.createVerticalGlue()); + return buildBox; + } + + private JComponent createActionBox() { + var actionBox = Box.createVerticalBox(); + addComp(new JLabel("Actions"), actionBox); + addVerticalSectionSpace(actionBox); + addComp(saveGraphChk, actionBox); + addComp(startOptServerChk, actionBox); + addComp(startOptVisualizerChk, actionBox); + return actionBox; + } + Box panel() { return panel; } @@ -64,19 +75,6 @@ void bind(JCheckBox box, Consumer modelUpdate) { box.addActionListener(l -> modelUpdate.accept(box.isSelected() && box.isEnabled())); } - private void addDebugCheckBoxes(Model model) { - addSectionSpace(panel); - addComp(new JLabel("Debug logging"), panel); - addSectionSpace(panel); - var entries = model.getDebugLogging(); - List keys = entries.keySet().stream().sorted().collect(Collectors.toList()); - for (String name : keys) { - JCheckBox box = new JCheckBox(name, entries.get(name)); - box.addActionListener(l -> model.getDebugLogging().put(name, box.isSelected())); - addComp(box, panel); - } - } - private void bindCheckBoxesToModel() { bind(buildStreetGraphChk, model::setBuildStreet); bind(buildTransitGraphChk, model::setBuildTransit); diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/StartOtpButtonView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StartOtpButtonView.java similarity index 82% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/views/StartOtpButtonView.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StartOtpButtonView.java index 5c0c1e7a621..3050b7c5c62 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/StartOtpButtonView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StartOtpButtonView.java @@ -1,6 +1,6 @@ -package org.opentripplanner.ext.interactivelauncher.views; +package org.opentripplanner.ext.interactivelauncher.startup; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.adjustSize; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.adjustSize; import java.awt.event.ActionListener; import javax.swing.Box; diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StatusBar.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StatusBar.java new file mode 100644 index 00000000000..f88c77c9f75 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StatusBar.java @@ -0,0 +1,15 @@ +package org.opentripplanner.ext.interactivelauncher.startup; + +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.BG_STATUS_BAR; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.FG_STATUS_BAR; + +import javax.swing.JTextField; + +class StatusBar extends JTextField { + + public StatusBar() { + setEditable(false); + setBackground(BG_STATUS_BAR); + setForeground(FG_STATUS_BAR); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/SearchForOtpConfig.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/support/SearchForOtpConfig.java similarity index 85% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/SearchForOtpConfig.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/support/SearchForOtpConfig.java index ccf6be72e20..6b0574f0fdf 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/SearchForOtpConfig.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/support/SearchForOtpConfig.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.interactivelauncher; +package org.opentripplanner.ext.interactivelauncher.support; import java.io.File; import java.util.Arrays; @@ -10,17 +10,17 @@ /** * Search for directories containing OTP configuration files. The search is recursive and searches - * sub-directories 10 levels deep. + * subdirectories 10 levels deep. */ -class SearchForOtpConfig { +public class SearchForOtpConfig { private static final int DEPTH_LIMIT = 10; private static final Pattern EXCLUDE_DIR = Pattern.compile( "(otp1|archive|\\..*|te?mp|target|docs?|src|source|resource)" ); - static List search(File rootDir) { - return recursiveSearch(rootDir, DEPTH_LIMIT).collect(Collectors.toUnmodifiableList()); + public static List search(File rootDir) { + return recursiveSearch(rootDir, DEPTH_LIMIT).toList(); } @SuppressWarnings("ConstantConditions") diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/ViewUtils.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/support/ViewUtils.java similarity index 53% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/views/ViewUtils.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/support/ViewUtils.java index 14f6f589109..1e0c94fb588 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/ViewUtils.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/support/ViewUtils.java @@ -1,35 +1,36 @@ -package org.opentripplanner.ext.interactivelauncher.views; +package org.opentripplanner.ext.interactivelauncher.support; import java.awt.Color; +import java.awt.Container; import java.awt.Dimension; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JComponent; -final class ViewUtils { +public final class ViewUtils { private static final boolean DEBUG_LAYOUT = false; static final int SECTION_SPACE = 10; - static final Color BACKGROUND = new Color(0xe0, 0xf0, 0xff); - static final Color BG_STATUS_BAR = new Color(0xd0, 0xe0, 0xf0); - static final Color FG_STATUS_BAR = new Color(0, 0, 0x80); + public static final Color BACKGROUND = new Color(0xe0, 0xf0, 0xff); + public static final Color BG_STATUS_BAR = new Color(0xd0, 0xe0, 0xf0); + public static final Color FG_STATUS_BAR = new Color(0, 0, 0x80); - static void addSectionSpace(Box panel) { + public static void addVerticalSectionSpace(Box panel) { panel.add(Box.createVerticalStrut(SECTION_SPACE)); } - static void addSectionDoubleSpace(Box panel) { - panel.add(Box.createVerticalStrut(2 * SECTION_SPACE)); + public static void addHorizontalGlue(Box box) { + box.add(Box.createHorizontalGlue()); } - static void addComp(JComponent c, Box panel) { + public static void addComp(JComponent c, Container panel) { if (DEBUG_LAYOUT) { c.setBorder(BorderFactory.createLineBorder(Color.green)); } panel.add(c); } - static void debugLayout(JComponent... components) { + public static void debugLayout(JComponent... components) { if (DEBUG_LAYOUT) { for (JComponent c : components) { c.setBorder(BorderFactory.createLineBorder(Color.red)); @@ -37,7 +38,7 @@ static void debugLayout(JComponent... components) { } } - static void adjustSize(JComponent c, int dWidth, int dHeight) { + public static void adjustSize(JComponent c, int dWidth, int dHeight) { Dimension d0 = c.getPreferredSize(); Dimension d = new Dimension(d0.width + dWidth, d0.height + dHeight); c.setMinimumSize(d); diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/DataSourcesView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/DataSourcesView.java deleted file mode 100644 index c7faa43779d..00000000000 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/DataSourcesView.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.opentripplanner.ext.interactivelauncher.views; - -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.addComp; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.addSectionDoubleSpace; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.addSectionSpace; - -import java.awt.event.ActionEvent; -import java.util.List; -import java.util.stream.Collectors; -import javax.swing.Box; -import javax.swing.ButtonGroup; -import javax.swing.JLabel; -import javax.swing.JRadioButton; -import org.opentripplanner.ext.interactivelauncher.Model; - -class DataSourcesView { - - private final Box panel = Box.createVerticalBox(); - private final Box dataSourceSelectionPanel = Box.createVerticalBox(); - private final Model model; - - public DataSourcesView(Model model) { - this.model = model; - - setupDataSources(); - - addComp(new JLabel("Select data source"), panel); - addSectionSpace(panel); - addComp(dataSourceSelectionPanel, panel); - addSectionDoubleSpace(panel); - } - - public Box panel() { - return panel; - } - - public void onRootDirChange() { - model.setDataSource(null); - dataSourceSelectionPanel.removeAll(); - setupDataSources(); - panel.repaint(); - } - - public void onDataSourceChange(ActionEvent e) { - model.setDataSource(e.getActionCommand()); - } - - private static JRadioButton newRadioBtn(ButtonGroup group, String name, boolean selected) { - JRadioButton radioButton = new JRadioButton(name, selected); - group.add(radioButton); - return radioButton; - } - - private void setupDataSources() { - final List values = model.getDataSourceOptions(); - - if (values.isEmpty()) { - model.setDataSource(null); - JLabel label = new JLabel(""); - label.setBackground(ViewUtils.BG_STATUS_BAR); - label.setForeground(ViewUtils.FG_STATUS_BAR); - addComp(label, dataSourceSelectionPanel); - return; - } - - String selectedValue = model.getDataSource(); - - if (selectedValue == null) { - selectedValue = values.get(0); - model.setDataSource(selectedValue); - } - - ButtonGroup selectDataSourceRadioGroup = new ButtonGroup(); - - List valuesSorted = values.stream().sorted().collect(Collectors.toList()); - - for (String name : valuesSorted) { - boolean selected = selectedValue.equals(name); - JRadioButton radioBtn = newRadioBtn(selectDataSourceRadioGroup, name, selected); - radioBtn.addActionListener(this::onDataSourceChange); - addComp(radioBtn, dataSourceSelectionPanel); - } - } -} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/StatusBar.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/StatusBar.java deleted file mode 100644 index faffa9d87d2..00000000000 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/views/StatusBar.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.opentripplanner.ext.interactivelauncher.views; - -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.BG_STATUS_BAR; -import static org.opentripplanner.ext.interactivelauncher.views.ViewUtils.FG_STATUS_BAR; - -import javax.swing.JTextField; - -public class StatusBar extends JTextField { - - public StatusBar() { - setEditable(false); - setBackground(BG_STATUS_BAR); - setForeground(FG_STATUS_BAR); - } -} From cb10208be9e800d93bb2b8c1621698d12740cc3e Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 01:38:18 +0100 Subject: [PATCH 28/86] refactor: Extend the main code, so the interactive launcher can intercept the request. --- .../api/LauncherRequestDecorator.java | 15 ++++++++++++++ .../InteractiveLauncherModule.java | 20 +++++++++++++++++++ .../ConstructApplicationFactory.java | 2 ++ .../configure/ConstructApplicationModule.java | 8 ++++++-- 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/api/LauncherRequestDecorator.java create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/configuration/InteractiveLauncherModule.java diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/api/LauncherRequestDecorator.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/api/LauncherRequestDecorator.java new file mode 100644 index 00000000000..99c28bad260 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/api/LauncherRequestDecorator.java @@ -0,0 +1,15 @@ +package org.opentripplanner.ext.interactivelauncher.api; + +import org.opentripplanner.routing.api.request.RouteRequest; + +/** + * Allow the interactive launcher intercept planing requests. + */ +public interface LauncherRequestDecorator { + /** + * The launcher may use this method to change the default plan request. Note! It is the DEFAULT + * request witch is passed in here, then the request-specific values are applied on top + * of that. + */ + RouteRequest intercept(RouteRequest defaultRequest); +} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/configuration/InteractiveLauncherModule.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/configuration/InteractiveLauncherModule.java new file mode 100644 index 00000000000..2df46b74127 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/configuration/InteractiveLauncherModule.java @@ -0,0 +1,20 @@ +package org.opentripplanner.ext.interactivelauncher.configuration; + +import dagger.Module; +import dagger.Provides; +import org.opentripplanner.ext.interactivelauncher.api.LauncherRequestDecorator; + +@Module +public class InteractiveLauncherModule { + + static LauncherRequestDecorator decorator = request -> request; + + static void enable(LauncherRequestDecorator decorator) { + InteractiveLauncherModule.decorator = decorator; + } + + @Provides + LauncherRequestDecorator requestDecorator() { + return decorator; + } +} diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java index 097fcfb4f7a..a4f0ee49652 100644 --- a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java +++ b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java @@ -6,6 +6,7 @@ import javax.annotation.Nullable; import org.opentripplanner.ext.emissions.EmissionsDataModel; import org.opentripplanner.ext.emissions.EmissionsServiceModule; +import org.opentripplanner.ext.interactivelauncher.configuration.InteractiveLauncherModule; import org.opentripplanner.ext.ridehailing.configure.RideHailingServicesModule; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; import org.opentripplanner.ext.stopconsolidation.configure.StopConsolidationServiceModule; @@ -50,6 +51,7 @@ RideHailingServicesModule.class, EmissionsServiceModule.class, StopConsolidationServiceModule.class, + InteractiveLauncherModule.class, } ) public interface ConstructApplicationFactory { diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java index 2ff6de29b3d..c9d7253b0be 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.interactivelauncher.api.LauncherRequestDecorator; import org.opentripplanner.ext.ridehailing.RideHailingService; import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; import org.opentripplanner.raptor.configure.RaptorConfig; @@ -36,11 +37,14 @@ OtpServerRequestContext providesServerContext( List rideHailingServices, @Nullable StopConsolidationService stopConsolidationService, @Nullable TraverseVisitor traverseVisitor, - EmissionsService emissionsService + EmissionsService emissionsService, + LauncherRequestDecorator launcherRequestDecorator ) { + var defaultRequest = launcherRequestDecorator.intercept(routerConfig.routingRequestDefaults()); + return DefaultServerRequestContext.create( routerConfig.transitTuningConfig(), - routerConfig.routingRequestDefaults(), + defaultRequest, raptorConfig, graph, transitService, From bd16e4046bd85c00ca35e7113fde8b6fe0b588b6 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 14:07:19 +0100 Subject: [PATCH 29/86] refactor: Cleanup SystemErrDebugLogger --- .../raptor/rangeraptor/SystemErrDebugLogger.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java index 060d3a2e018..951721f81a7 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java @@ -43,14 +43,14 @@ public class SystemErrDebugLogger implements DebugLogger { private final Table arrivalTable = Table .of() .withAlights(Center, Center, Right, Right, Right, Right, Left, Left) - .withHeaders("ARRIVAL", "LEG", "RND", "STOP", "ARRIVE", "COST", "TRIP", "DETAILS") + .withHeaders("ARRIVAL", "LEG", "RND", "STOP", "ARRIVE", "C₁", "TRIP", "DETAILS") .withMinWidths(9, 7, 3, 5, 8, 9, 24, 0) .build(); private final Table pathTable = Table .of() .withAlights(Center, Center, Right, Right, Right, Right, Right, Right, Left) - .withHeaders(">>> PATH", "TR", "FROM", "TO", "START", "END", "DURATION", "COST", "DETAILS") - .withMinWidths(9, 2, 5, 5, 8, 8, 8, 6, 0) + .withHeaders(">>> PATH", "TR", "FROM", "TO", "START", "END", "DURATION", "C₁", "DETAILS") + .withMinWidths(9, 2, 5, 5, 8, 8, 8, 9, 0) .build(); private boolean forwardSearch = true; private int lastIterationTime = NOT_SET; @@ -112,6 +112,7 @@ public void pathFilteringListener(DebugEvent> e) { RaptorPath p = e.element(); var aLeg = p.accessLeg(); var eLeg = p.egressLeg(); + println( pathTable.rowAsText( e.action().toString(), From eb259ee3b247095206bfaff0876f00bd733250b9 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 15:59:41 +0100 Subject: [PATCH 30/86] refactor: Create a separate model for StartUp --- .../InteractiveOtpMain.java | 6 +- .../ext/interactivelauncher/Model.java | 187 +----------------- .../startup/DataSourcesView.java | 5 +- .../interactivelauncher/startup/MainView.java | 6 +- .../startup/OptionsView.java | 5 +- .../startup/StartupModel.java | 178 +++++++++++++++++ 6 files changed, 192 insertions(+), 195 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StartupModel.java diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java index d61ae6bfcd3..f5621c45278 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java @@ -24,16 +24,14 @@ public static void main(String[] args) { private void run() { this.model = Model.load(); - MainView frame = new MainView(new Thread(this::startOtp)::start, model); + MainView frame = new MainView(new Thread(this::startOtp)::start, model.getStartupModel()); frame.start(); } private void startOtp() { model.save(); - new OtpDebugController(model).start(); - System.out.println("Start OTP: " + model + "\n"); - OTPMain.main(model.asOtpArgs()); + OTPMain.main(model.getStartupModel().asOtpArgs()); } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java index 1550f8bc1ab..8e0c2b353a7 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java @@ -1,34 +1,17 @@ package org.opentripplanner.ext.interactivelauncher; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; import org.opentripplanner.ext.interactivelauncher.logging.LogModel; -import org.opentripplanner.ext.interactivelauncher.support.SearchForOtpConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.opentripplanner.ext.interactivelauncher.startup.StartupModel; public class Model implements Serializable { - private static final Logger LOG = LoggerFactory.getLogger(Model.class); - private static final File MODEL_FILE = new File("interactive_otp_main.json"); - @JsonIgnore - private transient Consumer commandLineChange; - - private String rootDirectory = null; - private String dataSource = null; - private boolean buildStreet = false; - private boolean buildTransit = true; - private boolean saveGraph = false; - private boolean serveGraph = true; - private boolean visualizer = false; + private StartupModel startupModel; private LogModel logModel; public Model() {} @@ -41,116 +24,12 @@ public static Model load() { return model; } - public void subscribeCmdLineUpdates(Consumer commandLineChange) { - this.commandLineChange = commandLineChange; - } - - @SuppressWarnings("AccessOfSystemProperties") - public String getRootDirectory() { - return rootDirectory == null ? System.getProperty("user.dir") : rootDirectory; - } - - public void setRootDirectory(String rootDirectory) { - // If the persisted JSON do not contain the rootDirectory, then avoid setting it - if (rootDirectory != null) { - this.rootDirectory = rootDirectory; - } - notifyChangeListener(); - } - - public String getDataSource() { - return dataSource; - } - - public void setDataSource(String dataSource) { - this.dataSource = dataSource; - notifyChangeListener(); - } - - @JsonIgnore - public List getDataSourceOptions() { - List dataSourceOptions = new ArrayList<>(); - File rootDir = new File(getRootDirectory()); - List dirs = SearchForOtpConfig.search(rootDir); - // Add 1 char for the path-separator-character - int length = rootDir.getAbsolutePath().length() + 1; - - for (File dir : dirs) { - var path = dir.getAbsolutePath(); - if (path.length() <= length) { - LOG.warn( - "The root directory contains a config file, choose " + - "the parent directory or delete the config file." - ); - continue; - } - dataSourceOptions.add(path.substring(length)); - } - return dataSourceOptions; - } - - public boolean isBuildStreet() { - return buildStreet; - } - - public void setBuildStreet(boolean buildStreet) { - this.buildStreet = buildStreet; - notifyChangeListener(); - } - - public boolean isBuildTransit() { - return buildTransit; - } - - public void setBuildTransit(boolean buildTransit) { - this.buildTransit = buildTransit; - notifyChangeListener(); - } - - public boolean isSaveGraph() { - return saveGraph; - } - - public void setSaveGraph(boolean saveGraph) { - this.saveGraph = saveGraph; - notifyChangeListener(); - } - - public boolean isServeGraph() { - return serveGraph; - } - - public void setServeGraph(boolean serveGraph) { - this.serveGraph = serveGraph; - notifyChangeListener(); + public StartupModel getStartupModel() { + return startupModel; } - public boolean isVisualizer() { - return visualizer; - } - - public void setVisualizer(boolean visualizer) { - this.visualizer = visualizer; - notifyChangeListener(); - } - - @Override - public String toString() { - return ( - "(" + - "data-source-dir: " + - getDataSourceDirectory() + - (buildStreet ? ", buildStreet" : "") + - (buildTransit ? ", buildTransit" : "") + - (saveGraph ? ", saveGraph" : "") + - (serveGraph ? ", serveGraph" : "") + - (visualizer ? ", visualizer" : "") + - ')' - ); - } - - public String toCliString() { - return String.join(" ", asOtpArgs()); + public LogModel getLogModel() { + return logModel; } public void save() { @@ -161,46 +40,6 @@ public void save() { } } - public LogModel getLogModel() { - return logModel; - } - - @JsonIgnore - String getDataSourceDirectory() { - if (dataSource == null) { - return "DATA_SOURCE_NOT_SET"; - } - return rootDirectory + File.separatorChar + dataSource; - } - - String[] asOtpArgs() { - List args = new ArrayList<>(); - - if (buildAll()) { - args.add("--build"); - } else if (buildStreet) { - args.add("--buildStreet"); - } else if (buildTransit) { - args.add("--loadStreet"); - } else { - args.add("--load"); - } - - if (saveGraph && (buildTransit || buildStreet)) { - args.add("--save"); - } - if (serveGraph && !buildStreetOnly()) { - args.add("--serve"); - } - if (serveGraph && !buildStreetOnly() && visualizer) { - args.add("--visualize"); - } - - args.add(getDataSourceDirectory()); - - return args.toArray(new String[0]); - } - private static Model createNew() { var model = new Model(); model.logModel = new LogModel(); @@ -224,20 +63,6 @@ private static Model readFromFile() { } } - private void notifyChangeListener() { - if (commandLineChange != null) { - commandLineChange.accept(toCliString()); - } - } - - private boolean buildAll() { - return buildStreet && buildTransit; - } - - private boolean buildStreetOnly() { - return buildStreet && !buildTransit; - } - private void setupCallbacks() { logModel.init(this::save); } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java index 07052b9cd29..d8787287681 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java @@ -12,7 +12,6 @@ import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JRadioButton; -import org.opentripplanner.ext.interactivelauncher.Model; import org.opentripplanner.ext.interactivelauncher.support.ViewUtils; class DataSourcesView { @@ -28,9 +27,9 @@ class DataSourcesView { private final Box mainPanel = Box.createVerticalBox(); private final Box listPanel = Box.createHorizontalBox(); - private final Model model; + private final StartupModel model; - public DataSourcesView(Model model) { + public DataSourcesView(StartupModel model) { this.model = model; setupDataSources(); diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/MainView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/MainView.java index d147624aa61..6db2508196c 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/MainView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/MainView.java @@ -1,6 +1,5 @@ package org.opentripplanner.ext.interactivelauncher.startup; -import static java.awt.GridBagConstraints.BOTH; import static java.awt.GridBagConstraints.CENTER; import static java.awt.GridBagConstraints.HORIZONTAL; import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.BACKGROUND; @@ -13,7 +12,6 @@ import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; -import org.opentripplanner.ext.interactivelauncher.Model; public class MainView { @@ -50,9 +48,9 @@ public class MainView { private final OptionsView optionsView; private final StartOtpButtonView startOtpButtonView; private final Runnable otpStarter; - private final Model model; + private final StartupModel model; - public MainView(Runnable otpStarter, Model model) throws HeadlessException { + public MainView(Runnable otpStarter, StartupModel model) throws HeadlessException { var innerPanel = new JPanel(); var statusBarTxt = new StatusBar(); diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java index 3d98bbf0cfc..a8c4327a2d4 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java @@ -8,7 +8,6 @@ import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; -import org.opentripplanner.ext.interactivelauncher.Model; class OptionsView { @@ -18,9 +17,9 @@ class OptionsView { private final JCheckBox saveGraphChk; private final JCheckBox startOptServerChk; private final JCheckBox startOptVisualizerChk; - private final Model model; + private final StartupModel model; - OptionsView(Model model) { + OptionsView(StartupModel model) { this.model = model; this.buildStreetGraphChk = new JCheckBox("Street graph", model.isBuildStreet()); this.buildTransitGraphChk = new JCheckBox("Transit graph", model.isBuildTransit()); diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StartupModel.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StartupModel.java new file mode 100644 index 00000000000..5b803b2318c --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/StartupModel.java @@ -0,0 +1,178 @@ +package org.opentripplanner.ext.interactivelauncher.startup; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import org.opentripplanner.ext.interactivelauncher.support.SearchForOtpConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StartupModel { + + private static final Logger LOG = LoggerFactory.getLogger(StartupModel.class); + + @JsonIgnore + private transient Consumer commandLineChange; + + private String rootDirectory = null; + private String dataSource = null; + private boolean buildStreet = false; + private boolean buildTransit = true; + private boolean saveGraph = false; + private boolean serveGraph = true; + private boolean visualizer = false; + + public void subscribeCmdLineUpdates(Consumer commandLineChange) { + this.commandLineChange = commandLineChange; + } + + @SuppressWarnings("AccessOfSystemProperties") + public String getRootDirectory() { + return rootDirectory == null ? System.getProperty("user.dir") : rootDirectory; + } + + public void setRootDirectory(String rootDirectory) { + // If the persisted JSON do not contain the rootDirectory, then avoid setting it + if (rootDirectory != null) { + this.rootDirectory = rootDirectory; + } + notifyChangeListener(); + } + + public String getDataSource() { + return dataSource; + } + + public void setDataSource(String dataSource) { + this.dataSource = dataSource; + notifyChangeListener(); + } + + @JsonIgnore + public List getDataSourceOptions() { + List dataSourceOptions = new ArrayList<>(); + File rootDir = new File(getRootDirectory()); + List dirs = SearchForOtpConfig.search(rootDir); + // Add 1 char for the path-separator-character + int length = rootDir.getAbsolutePath().length() + 1; + + for (File dir : dirs) { + var path = dir.getAbsolutePath(); + if (path.length() <= length) { + LOG.warn( + "The root directory contains a config file, choose " + + "the parent directory or delete the config file." + ); + continue; + } + dataSourceOptions.add(path.substring(length)); + } + return dataSourceOptions; + } + + public boolean isBuildStreet() { + return buildStreet; + } + + public void setBuildStreet(boolean buildStreet) { + this.buildStreet = buildStreet; + notifyChangeListener(); + } + + public boolean isBuildTransit() { + return buildTransit; + } + + public void setBuildTransit(boolean buildTransit) { + this.buildTransit = buildTransit; + notifyChangeListener(); + } + + public boolean isSaveGraph() { + return saveGraph; + } + + public void setSaveGraph(boolean saveGraph) { + this.saveGraph = saveGraph; + notifyChangeListener(); + } + + public boolean isServeGraph() { + return serveGraph; + } + + public void setServeGraph(boolean serveGraph) { + this.serveGraph = serveGraph; + notifyChangeListener(); + } + + public boolean isVisualizer() { + return visualizer; + } + + public void setVisualizer(boolean visualizer) { + this.visualizer = visualizer; + notifyChangeListener(); + } + + @Override + public String toString() { + return String.join("", asOtpArgs()); + } + + public String toCliString() { + return String.join(" ", asOtpArgs()); + } + + private void notifyChangeListener() { + if (commandLineChange != null) { + commandLineChange.accept(toCliString()); + } + } + + @JsonIgnore + String getDataSourceDirectory() { + if (dataSource == null) { + return "DATA_SOURCE_NOT_SET"; + } + return getRootDirectory() + File.separatorChar + dataSource; + } + + public String[] asOtpArgs() { + List args = new ArrayList<>(); + + if (buildAll()) { + args.add("--build"); + } else if (buildStreet) { + args.add("--buildStreet"); + } else if (buildTransit) { + args.add("--loadStreet"); + } else { + args.add("--load"); + } + + if (saveGraph && (buildTransit || buildStreet)) { + args.add("--save"); + } + if (serveGraph && !buildStreetOnly()) { + args.add("--serve"); + } + if (serveGraph && !buildStreetOnly() && visualizer) { + args.add("--visualize"); + } + + args.add(getDataSourceDirectory()); + + return args.toArray(new String[0]); + } + + private boolean buildAll() { + return buildStreet && buildTransit; + } + + private boolean buildStreetOnly() { + return buildStreet && !buildTransit; + } +} From 50cc04e15678ca3d74cc3931cf3c6ccb436a67c2 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 16:02:52 +0100 Subject: [PATCH 31/86] refactor: Create debug package in o.o.ext.interactivelauncher --- .../org/opentripplanner/ext/interactivelauncher/Model.java | 2 +- .../ext/interactivelauncher/OtpDebugController.java | 4 ++-- .../{ => debug}/logging/DebugLoggingSupport.java | 3 +-- .../ext/interactivelauncher/{ => debug}/logging/LogModel.java | 2 +- .../ext/interactivelauncher/{ => debug}/logging/LogView.java | 2 +- .../{ => debug}/logging/OTPDebugLoggers.java | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{ => debug}/logging/DebugLoggingSupport.java (95%) rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{ => debug}/logging/LogModel.java (95%) rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{ => debug}/logging/LogView.java (92%) rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{ => debug}/logging/OTPDebugLoggers.java (90%) diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java index 8e0c2b353a7..d44e7c0e340 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java @@ -4,7 +4,7 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; -import org.opentripplanner.ext.interactivelauncher.logging.LogModel; +import org.opentripplanner.ext.interactivelauncher.debug.logging.LogModel; import org.opentripplanner.ext.interactivelauncher.startup.StartupModel; public class Model implements Serializable { diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java index 95635c5bb0c..57ef7f70644 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java @@ -6,8 +6,8 @@ import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTabbedPane; -import org.opentripplanner.ext.interactivelauncher.logging.LogModel; -import org.opentripplanner.ext.interactivelauncher.logging.LogView; +import org.opentripplanner.ext.interactivelauncher.debug.logging.LogModel; +import org.opentripplanner.ext.interactivelauncher.debug.logging.LogView; public class OtpDebugController { diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/DebugLoggingSupport.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggingSupport.java similarity index 95% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/DebugLoggingSupport.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggingSupport.java index fcfa323680f..9985f8eb079 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/DebugLoggingSupport.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggingSupport.java @@ -1,11 +1,10 @@ -package org.opentripplanner.ext.interactivelauncher.logging; +package org.opentripplanner.ext.interactivelauncher.debug.logging; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.regex.Pattern; import org.slf4j.LoggerFactory; diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogModel.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java similarity index 95% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogModel.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java index 536c8d1dc87..7f7695ad2e5 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogModel.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.interactivelauncher.logging; +package org.opentripplanner.ext.interactivelauncher.debug.logging; import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java similarity index 92% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogView.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java index a71f90eb697..d4cea4d0baf 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/LogView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.interactivelauncher.logging; +package org.opentripplanner.ext.interactivelauncher.debug.logging; import javax.swing.Box; import javax.swing.JCheckBox; diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/OTPDebugLoggers.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/OTPDebugLoggers.java similarity index 90% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/OTPDebugLoggers.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/OTPDebugLoggers.java index 4663b6017de..a38cb5cab58 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/logging/OTPDebugLoggers.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/OTPDebugLoggers.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.interactivelauncher.logging; +package org.opentripplanner.ext.interactivelauncher.debug.logging; import java.util.List; From 6d890c774d5a3a8379f520e0a7b50b44c15d4de1 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 16:04:59 +0100 Subject: [PATCH 32/86] refactor: Rename OTPDebugLoggers to DebugLoggers --- .../logging/{OTPDebugLoggers.java => DebugLoggers.java} | 8 ++++---- .../ext/interactivelauncher/debug/logging/LogView.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/{OTPDebugLoggers.java => DebugLoggers.java} (71%) diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/OTPDebugLoggers.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java similarity index 71% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/OTPDebugLoggers.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java index a38cb5cab58..0635b9a3675 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/OTPDebugLoggers.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java @@ -2,8 +2,8 @@ import java.util.List; -record OTPDebugLoggers(String label, String logger) { - static List list() { +record DebugLoggers(String label, String logger) { + static List list() { return List.of( of("Data import issues", "DATA_IMPORT_ISSUES"), of("All OTP debuggers", "org.opentripplanner"), @@ -13,7 +13,7 @@ static List list() { ); } - private static OTPDebugLoggers of(String label, String logger) { - return new OTPDebugLoggers(label, logger); + private static DebugLoggers of(String label, String logger) { + return new DebugLoggers(label, logger); } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java index d4cea4d0baf..6417e487ca7 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java @@ -14,14 +14,14 @@ public class LogView { public LogView(LogModel model) { this.model = model; - OTPDebugLoggers.list().forEach(this::add); + DebugLoggers.list().forEach(this::add); } public JComponent panel() { return panel; } - private void add(OTPDebugLoggers logger) { + private void add(DebugLoggers logger) { var box = new JCheckBox(logger.label()); box.setSelected(model.isLoggerEnabled(logger.logger())); box.addActionListener(e -> selectLogger(logger.logger(), box.isSelected())); From 578cc48867fea053193b9c37697022de23f49561 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 16:09:31 +0100 Subject: [PATCH 33/86] refactor: Extract addLabel utility method --- .../ext/interactivelauncher/startup/DataSourcesView.java | 9 ++++----- .../ext/interactivelauncher/startup/OptionsView.java | 6 +++--- .../ext/interactivelauncher/support/ViewUtils.java | 5 +++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java index d8787287681..e7b1814faa2 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/DataSourcesView.java @@ -2,6 +2,7 @@ import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addComp; import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addHorizontalGlue; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addLabel; import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addVerticalSectionSpace; import java.awt.Component; @@ -33,12 +34,10 @@ public DataSourcesView(StartupModel model) { this.model = model; setupDataSources(); - JLabel label = new JLabel("Select data source"); + addLabel("Select data source", mainPanel); + addVerticalSectionSpace(mainPanel); listPanel.setAlignmentX(Component.LEFT_ALIGNMENT); - - addComp(label, mainPanel); - addVerticalSectionSpace(mainPanel); addComp(listPanel, mainPanel); } @@ -62,7 +61,7 @@ private void setupDataSources() { if (values.isEmpty()) { model.setDataSource(null); - JLabel label = new JLabel(""); + var label = new JLabel(""); label.setBackground(ViewUtils.BG_STATUS_BAR); label.setForeground(ViewUtils.FG_STATUS_BAR); addComp(label, listPanel); diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java index a8c4327a2d4..08907346a76 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/startup/OptionsView.java @@ -1,13 +1,13 @@ package org.opentripplanner.ext.interactivelauncher.startup; import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addComp; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addLabel; import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addVerticalSectionSpace; import java.util.function.Consumer; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JComponent; -import javax.swing.JLabel; class OptionsView { @@ -44,7 +44,7 @@ class OptionsView { private JComponent createBuildBox() { var buildBox = Box.createVerticalBox(); - addComp(new JLabel("Build graph"), buildBox); + addLabel("Build graph", buildBox); addVerticalSectionSpace(buildBox); addComp(buildStreetGraphChk, buildBox); addComp(buildTransitGraphChk, buildBox); @@ -54,7 +54,7 @@ private JComponent createBuildBox() { private JComponent createActionBox() { var actionBox = Box.createVerticalBox(); - addComp(new JLabel("Actions"), actionBox); + addLabel("Actions", actionBox); addVerticalSectionSpace(actionBox); addComp(saveGraphChk, actionBox); addComp(startOptServerChk, actionBox); diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/support/ViewUtils.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/support/ViewUtils.java index 1e0c94fb588..f906f063058 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/support/ViewUtils.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/support/ViewUtils.java @@ -6,6 +6,7 @@ import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JComponent; +import javax.swing.JLabel; public final class ViewUtils { @@ -23,6 +24,10 @@ public static void addHorizontalGlue(Box box) { box.add(Box.createHorizontalGlue()); } + public static void addLabel(String label, Container panel) { + addComp(new JLabel(label), panel); + } + public static void addComp(JComponent c, Container panel) { if (DEBUG_LAYOUT) { c.setBorder(BorderFactory.createLineBorder(Color.green)); From 0756cad80168ce9ca6e43b464a4dfcb86382239e Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 16:13:21 +0100 Subject: [PATCH 34/86] InteractiveLauncherModule --- .../configuration/InteractiveLauncherModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/configuration/InteractiveLauncherModule.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/configuration/InteractiveLauncherModule.java index 2df46b74127..9acd0298122 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/configuration/InteractiveLauncherModule.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/configuration/InteractiveLauncherModule.java @@ -9,7 +9,7 @@ public class InteractiveLauncherModule { static LauncherRequestDecorator decorator = request -> request; - static void enable(LauncherRequestDecorator decorator) { + public static void setRequestInterceptor(LauncherRequestDecorator decorator) { InteractiveLauncherModule.decorator = decorator; } From 5b131b2992348b3ebb5b308b0053803bfb2b6e55 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 16:31:25 +0100 Subject: [PATCH 35/86] refactor: Cleanup Model in interactive launcher This fixes a few small bugs/improve the JSON serialization --- .../ext/interactivelauncher/Model.java | 32 ++++++++++--------- .../debug/logging/LogModel.java | 14 +++++--- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java index d44e7c0e340..45d8583f66d 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java @@ -1,6 +1,8 @@ package org.opentripplanner.ext.interactivelauncher; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import java.io.File; import java.io.IOException; import java.io.Serializable; @@ -17,11 +19,7 @@ public class Model implements Serializable { public Model() {} public static Model load() { - var model = MODEL_FILE.exists() ? readFromFile() : createNew(); - // Setup callbacks - model.logModel.init(model::save); - - return model; + return MODEL_FILE.exists() ? readFromFile() : createNew(); } public StartupModel getStartupModel() { @@ -34,25 +32,22 @@ public LogModel getLogModel() { public void save() { try { - new ObjectMapper().writeValue(MODEL_FILE, this); + var mapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true); + mapper.writeValue(MODEL_FILE, this); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } } private static Model createNew() { - var model = new Model(); - model.logModel = new LogModel(); - model.logModel.initFromConfig(); - model.setupCallbacks(); - return model; + return new Model().initSubModels(); } private static Model readFromFile() { try { - var model = new ObjectMapper().readValue(MODEL_FILE, Model.class); - model.setupCallbacks(); - return model; + var mapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return mapper.readValue(MODEL_FILE, Model.class).initSubModels(); } catch (IOException e) { System.err.println( "Unable to read the InteractiveOtpMain state cache. If the model changed this " + @@ -63,7 +58,14 @@ private static Model readFromFile() { } } - private void setupCallbacks() { + private Model initSubModels() { + if (startupModel == null) { + startupModel = new StartupModel(); + } + if (logModel == null) { + logModel = LogModel.createFromConfig(); + } logModel.init(this::save); + return this; } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java index 7f7695ad2e5..d6500fa060f 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java @@ -20,6 +20,12 @@ public class LogModel implements Serializable { public LogModel() {} + public static LogModel createFromConfig() { + var model = new LogModel(); + model.initFromConfig(); + return model; + } + /** Need to set this manually to support JSON serialization. */ public void init(Runnable saveCallback) { this.saveCallback = saveCallback; @@ -36,10 +42,6 @@ public void setActiveLoggers(Collection loggers) { this.activeLoggers.addAll(loggers); } - public void initFromConfig() { - activeLoggers.addAll(DebugLoggingSupport.getDebugLoggers()); - } - boolean isLoggerEnabled(String name) { return activeLoggers.contains(name); } @@ -53,4 +55,8 @@ void turnLoggerOnOff(String name, boolean enable) { DebugLoggingSupport.configureDebugLogging(name, enable); saveCallback.run(); } + + private void initFromConfig() { + activeLoggers.addAll(DebugLoggingSupport.getDebugLoggers()); + } } From dc06bb91ba827d3753ff1a41259e162d24ec398d Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 16:34:33 +0100 Subject: [PATCH 36/86] refactor: Cleanup interactive launcher debug loggers --- .../debug/logging/DebugLoggers.java | 15 +++++--- .../debug/logging/DebugLoggingSupport.java | 2 +- .../debug/logging/LogModel.java | 34 +++++++++++++++---- .../debug/logging/LogView.java | 8 ++--- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java index 0635b9a3675..48f87abf2ab 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggers.java @@ -2,8 +2,9 @@ import java.util.List; -record DebugLoggers(String label, String logger) { - static List list() { +class DebugLoggers { + + static List list() { return List.of( of("Data import issues", "DATA_IMPORT_ISSUES"), of("All OTP debuggers", "org.opentripplanner"), @@ -13,7 +14,13 @@ static List list() { ); } - private static DebugLoggers of(String label, String logger) { - return new DebugLoggers(label, logger); + static List listLoggers() { + return list().stream().map(Entry::logger).toList(); + } + + private static Entry of(String label, String logger) { + return new Entry(label, logger); } + + record Entry(String label, String logger) {} } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggingSupport.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggingSupport.java index 9985f8eb079..09eddcfdf78 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggingSupport.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/DebugLoggingSupport.java @@ -25,7 +25,7 @@ class DebugLoggingSupport { "(" + OTP + "|" + GRAPHQL + "|" + NAMED_LOGGERS + ")" ); - static List getDebugLoggers() { + static List listConfiguredDebugLoggers() { List result = new ArrayList<>(); LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); for (Logger log : context.getLoggerList()) { diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java index d6500fa060f..df59ccaa968 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogModel.java @@ -31,15 +31,18 @@ public void init(Runnable saveCallback) { this.saveCallback = saveCallback; } - /** Needed to do JSON serialization. */ + /** Used by JSON serialization. */ public Collection getActiveLoggers() { return List.copyOf(activeLoggers); } - /** Needed to do JSON serialization. */ + /** Used by JSON deserialization. */ public void setActiveLoggers(Collection loggers) { this.activeLoggers.clear(); this.activeLoggers.addAll(loggers); + for (var logger : activeLoggers) { + DebugLoggingSupport.configureDebugLogging(logger, true); + } } boolean isLoggerEnabled(String name) { @@ -48,15 +51,32 @@ boolean isLoggerEnabled(String name) { void turnLoggerOnOff(String name, boolean enable) { if (enable) { - activeLoggers.add(name); + if (!activeLoggers.contains(name)) { + activeLoggers.add(name); + DebugLoggingSupport.configureDebugLogging(name, enable); + save(); + } } else { - activeLoggers.remove(name); + if (activeLoggers.contains(name)) { + activeLoggers.remove(name); + DebugLoggingSupport.configureDebugLogging(name, enable); + save(); + } } - DebugLoggingSupport.configureDebugLogging(name, enable); - saveCallback.run(); } private void initFromConfig() { - activeLoggers.addAll(DebugLoggingSupport.getDebugLoggers()); + var debugLoggers = DebugLoggers.listLoggers(); + for (var logger : DebugLoggingSupport.listConfiguredDebugLoggers()) { + if (debugLoggers.contains(logger)) { + activeLoggers.add(logger); + } + } + } + + private void save() { + if (saveCallback != null) { + saveCallback.run(); + } } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java index 6417e487ca7..52c1774c019 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java @@ -21,10 +21,10 @@ public JComponent panel() { return panel; } - private void add(DebugLoggers logger) { - var box = new JCheckBox(logger.label()); - box.setSelected(model.isLoggerEnabled(logger.logger())); - box.addActionListener(e -> selectLogger(logger.logger(), box.isSelected())); + private void add(DebugLoggers.Entry entry) { + var box = new JCheckBox(entry.label()); + box.setSelected(model.isLoggerEnabled(entry.logger())); + box.addActionListener(e -> selectLogger(entry.logger(), box.isSelected())); panel.add(box); } From 61af2c9aa13b89b389743637444b2be8eb6b89e0 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 16:38:47 +0100 Subject: [PATCH 37/86] refactor: move OtpDebugController to o.o.ext.interactivelauncher.debug --- .../interactivelauncher/{ => debug}/OtpDebugController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename src/ext/java/org/opentripplanner/ext/interactivelauncher/{ => debug}/OtpDebugController.java (90%) diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/OtpDebugController.java similarity index 90% rename from src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java rename to src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/OtpDebugController.java index 57ef7f70644..25e3c8c7629 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/OtpDebugController.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/OtpDebugController.java @@ -1,4 +1,4 @@ -package org.opentripplanner.ext.interactivelauncher; +package org.opentripplanner.ext.interactivelauncher.debug; import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.BACKGROUND; @@ -6,6 +6,7 @@ import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTabbedPane; +import org.opentripplanner.ext.interactivelauncher.Model; import org.opentripplanner.ext.interactivelauncher.debug.logging.LogModel; import org.opentripplanner.ext.interactivelauncher.debug.logging.LogView; From 39f2c39ae43990712a81f7e526e6da2f9383eff9 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 17:03:17 +0100 Subject: [PATCH 38/86] feature: Ann UI to set Raptor debug parameters The UI is used to instrument the Raptor search and log events at decision points during routing. --- .../InteractiveOtpMain.java | 9 +- .../ext/interactivelauncher/Model.java | 24 +++-- .../debug/OtpDebugController.java | 25 ++--- .../debug/raptor/RaptorDebugModel.java | 97 +++++++++++++++++++ .../debug/raptor/RaptorDebugView.java | 87 +++++++++++++++++ 5 files changed, 222 insertions(+), 20 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/raptor/RaptorDebugModel.java create mode 100644 src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/raptor/RaptorDebugView.java diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java index f5621c45278..43061ee2b62 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/InteractiveOtpMain.java @@ -1,5 +1,7 @@ package org.opentripplanner.ext.interactivelauncher; +import org.opentripplanner.ext.interactivelauncher.configuration.InteractiveLauncherModule; +import org.opentripplanner.ext.interactivelauncher.debug.OtpDebugController; import org.opentripplanner.ext.interactivelauncher.startup.MainView; import org.opentripplanner.standalone.OTPMain; @@ -29,9 +31,14 @@ private void run() { } private void startOtp() { - model.save(); + startDebugControllerAndSetupRequestInterceptor(); System.out.println("Start OTP: " + model + "\n"); OTPMain.main(model.getStartupModel().asOtpArgs()); } + + private void startDebugControllerAndSetupRequestInterceptor() { + new OtpDebugController(model).start(); + InteractiveLauncherModule.setRequestInterceptor(model.getRaptorDebugModel()); + } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java index 45d8583f66d..59682082b90 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/Model.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.Serializable; import org.opentripplanner.ext.interactivelauncher.debug.logging.LogModel; +import org.opentripplanner.ext.interactivelauncher.debug.raptor.RaptorDebugModel; import org.opentripplanner.ext.interactivelauncher.startup.StartupModel; public class Model implements Serializable { @@ -15,6 +16,7 @@ public class Model implements Serializable { private StartupModel startupModel; private LogModel logModel; + private RaptorDebugModel raptorDebugModel; public Model() {} @@ -30,13 +32,8 @@ public LogModel getLogModel() { return logModel; } - public void save() { - try { - var mapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true); - mapper.writeValue(MODEL_FILE, this); - } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); - } + public RaptorDebugModel getRaptorDebugModel() { + return raptorDebugModel; } private static Model createNew() { @@ -58,6 +55,15 @@ private static Model readFromFile() { } } + private void save() { + try { + var mapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true); + mapper.writeValue(MODEL_FILE, this); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + private Model initSubModels() { if (startupModel == null) { startupModel = new StartupModel(); @@ -65,7 +71,11 @@ private Model initSubModels() { if (logModel == null) { logModel = LogModel.createFromConfig(); } + if (raptorDebugModel == null) { + raptorDebugModel = new RaptorDebugModel(); + } logModel.init(this::save); + raptorDebugModel.init(this::save); return this; } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/OtpDebugController.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/OtpDebugController.java index 25e3c8c7629..9e41fe3412d 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/OtpDebugController.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/OtpDebugController.java @@ -2,29 +2,23 @@ import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.BACKGROUND; -import javax.swing.JComponent; import javax.swing.JFrame; -import javax.swing.JPanel; import javax.swing.JTabbedPane; import org.opentripplanner.ext.interactivelauncher.Model; -import org.opentripplanner.ext.interactivelauncher.debug.logging.LogModel; import org.opentripplanner.ext.interactivelauncher.debug.logging.LogView; +import org.opentripplanner.ext.interactivelauncher.debug.raptor.RaptorDebugView; +/** + * This controller/UI allows changing the debug loggers and setting the raptor + * debug parameters for incoming rute requests. + */ public class OtpDebugController { private final JFrame debugFrame = new JFrame("OTP Debug Controller"); public OtpDebugController(Model model) { - var tabPanel = new JTabbedPane(); - tabPanel.addTab("Logging", createLogPanel(model.getLogModel())); - tabPanel.addTab("Raptor", new JPanel()); - debugFrame.add(tabPanel); + debugFrame.add(createTabbedPane(model)); debugFrame.getContentPane().setBackground(BACKGROUND); - start(); - } - - private static JComponent createLogPanel(LogModel logModel) { - return new LogView(logModel).panel(); } public void start() { @@ -33,4 +27,11 @@ public void start() { debugFrame.setLocationRelativeTo(null); debugFrame.setVisible(true); } + + private static JTabbedPane createTabbedPane(Model model) { + var tabPanel = new JTabbedPane(); + tabPanel.addTab("Logging", new LogView(model.getLogModel()).panel()); + tabPanel.addTab("Raptor", new RaptorDebugView(model.getRaptorDebugModel()).panel()); + return tabPanel; + } } diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/raptor/RaptorDebugModel.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/raptor/RaptorDebugModel.java new file mode 100644 index 00000000000..41d6ed6be1a --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/raptor/RaptorDebugModel.java @@ -0,0 +1,97 @@ +package org.opentripplanner.ext.interactivelauncher.debug.raptor; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Objects; +import java.util.Set; +import javax.annotation.Nullable; +import org.opentripplanner.ext.interactivelauncher.api.LauncherRequestDecorator; +import org.opentripplanner.framework.lang.StringUtils; +import org.opentripplanner.routing.api.request.DebugEventType; +import org.opentripplanner.routing.api.request.RouteRequest; + +public class RaptorDebugModel implements LauncherRequestDecorator { + + private final Set eventTypes = EnumSet.noneOf(DebugEventType.class); + private String stops = null; + private String path = null; + private Runnable saveCallback; + + public RaptorDebugModel() {} + + public void init(Runnable saveCallback) { + this.saveCallback = saveCallback; + } + + /** Used by JSON serialization */ + public Set getEventTypes() { + return Collections.unmodifiableSet(eventTypes); + } + + /** Used by JSON serialization */ + public void setEventTypes(Collection eventTypes) { + this.eventTypes.clear(); + this.eventTypes.addAll(eventTypes); + } + + public void enableEventTypes(DebugEventType eventType, boolean enable) { + if (enable) { + if (!isEventTypeSet(eventType)) { + this.eventTypes.add(eventType); + save(); + } + } else { + if (isEventTypeSet(eventType)) { + this.eventTypes.remove(eventType); + save(); + } + } + } + + @Nullable + public String getStops() { + return stops; + } + + public void setStops(@Nullable String stops) { + stops = StringUtils.hasValue(stops) ? stops : null; + if (!Objects.equals(this.stops, stops)) { + this.stops = stops; + save(); + } + } + + @Nullable + public String getPath() { + return path; + } + + public void setPath(@Nullable String path) { + path = StringUtils.hasValue(path) ? path : null; + if (!Objects.equals(this.path, path)) { + this.path = path; + save(); + } + } + + public boolean isEventTypeSet(DebugEventType eventType) { + return eventTypes.contains(eventType); + } + + @Override + public RouteRequest intercept(RouteRequest defaultRequest) { + var newRequest = defaultRequest.clone(); + var debug = newRequest.journey().transit().raptorDebugging(); + debug.withEventTypes(eventTypes); + debug.withStops(stops); + debug.withPath(path); + return newRequest; + } + + private void save() { + if (saveCallback != null) { + saveCallback.run(); + } + } +} diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/raptor/RaptorDebugView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/raptor/RaptorDebugView.java new file mode 100644 index 00000000000..186b8d14837 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/raptor/RaptorDebugView.java @@ -0,0 +1,87 @@ +package org.opentripplanner.ext.interactivelauncher.debug.raptor; + +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addComp; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addLabel; +import static org.opentripplanner.ext.interactivelauncher.support.ViewUtils.addVerticalSectionSpace; +import static org.opentripplanner.routing.api.request.DebugEventType.DESTINATION_ARRIVALS; +import static org.opentripplanner.routing.api.request.DebugEventType.PATTERN_RIDES; +import static org.opentripplanner.routing.api.request.DebugEventType.STOP_ARRIVALS; + +import java.awt.Component; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.util.function.Consumer; +import javax.swing.Box; +import javax.swing.JCheckBox; +import javax.swing.JTextField; +import org.opentripplanner.routing.api.request.DebugEventType; + +/** + * This UI is used to set Raptor debug parameters, instrument the Raptor + * search, and log event at decision points during routing. + */ +public class RaptorDebugView { + + private final RaptorDebugModel model; + private final Box panel = Box.createVerticalBox(); + private final JCheckBox logStopArrivalsChk = new JCheckBox("Stop arrivals"); + private final JCheckBox logPatternRidesChk = new JCheckBox("Pattern rides"); + private final JCheckBox logDestinationArrivalsChk = new JCheckBox("Destination arrivals"); + private final JTextField stopsTxt = new JTextField(40); + private final JTextField pathTxt = new JTextField(40); + + public RaptorDebugView(RaptorDebugModel model) { + this.model = model; + + addLabel("Log Raptor events for", panel); + addComp(logStopArrivalsChk, panel); + addComp(logPatternRidesChk, panel); + addComp(logDestinationArrivalsChk, panel); + addVerticalSectionSpace(panel); + + addLabel("A list of stops to debug", panel); + addComp(stopsTxt, panel); + addVerticalSectionSpace(panel); + addLabel("A a path (as a list of stops) to debug", panel); + addComp(pathTxt, panel); + addVerticalSectionSpace(panel); + + initValues(); + setupActionListeners(); + } + + private void initValues() { + logStopArrivalsChk.setSelected(model.isEventTypeSet(STOP_ARRIVALS)); + logPatternRidesChk.setSelected(model.isEventTypeSet(PATTERN_RIDES)); + logDestinationArrivalsChk.setSelected(model.isEventTypeSet(DESTINATION_ARRIVALS)); + stopsTxt.setText(model.getStops()); + pathTxt.setText(model.getPath()); + } + + private void setupActionListeners() { + setupActionListenerChkBox(logStopArrivalsChk, STOP_ARRIVALS); + setupActionListenerChkBox(logPatternRidesChk, PATTERN_RIDES); + setupActionListenerChkBox(logDestinationArrivalsChk, DESTINATION_ARRIVALS); + setupActionListenerTextField(stopsTxt, model::setStops); + setupActionListenerTextField(pathTxt, model::setPath); + } + + public Component panel() { + return panel; + } + + private void setupActionListenerChkBox(JCheckBox box, DebugEventType type) { + box.addActionListener(l -> model.enableEventTypes(type, box.isSelected())); + } + + private static void setupActionListenerTextField(JTextField txtField, Consumer model) { + txtField.addFocusListener( + new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + model.accept(txtField.getText()); + } + } + ); + } +} From c4d2e6ea7d74e73e7ec9a397b597068764f98fa3 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 18:13:41 +0100 Subject: [PATCH 39/86] refactor: Add tooltip to debug loggers --- .../ext/interactivelauncher/debug/logging/LogView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java index 52c1774c019..ae8be59b07d 100644 --- a/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java +++ b/src/ext/java/org/opentripplanner/ext/interactivelauncher/debug/logging/LogView.java @@ -23,6 +23,7 @@ public JComponent panel() { private void add(DebugLoggers.Entry entry) { var box = new JCheckBox(entry.label()); + box.setToolTipText("Logger: " + entry.logger()); box.setSelected(model.isLoggerEnabled(entry.logger())); box.addActionListener(e -> selectLogger(entry.logger(), box.isSelected())); panel.add(box); From 10da47f62ba8cb1e78dfea65a7979c119dc76c5d Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Thu, 4 Jan 2024 18:18:13 +0100 Subject: [PATCH 40/86] Apply suggestions from code review Co-authored-by: Johan Torin --- .../apis/transmodel/model/plan/RelaxCostType.java | 8 ++++---- .../org/opentripplanner/apis/transmodel/schema.graphql | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostType.java index d3455083695..41435c83a2f 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/RelaxCostType.java @@ -46,8 +46,8 @@ public class RelaxCostType { .newInputObjectField() .name(CONSTANT) .description( - "The constant value to add to the limit. Must be a positive number. The value is" + - "equivalent to transit-cost-seconds. Integers is treated as seconds, but you may use " + + "The constant value to add to the limit. Must be a positive number. The value is " + + "equivalent to transit-cost-seconds. Integers are treated as seconds, but you may use " + "the duration format. Example: '3665 = 'DT1h1m5s' = '1h1m5s'." ) .defaultValueProgrammatic("0s") @@ -66,8 +66,8 @@ public static ObjectValue valueOf(CostLinearFunction value) { ObjectField .newObjectField() .name(CONSTANT) - // We only use this to display default value (this is an input type), so using the - // lenient OTP version of duration is ok - it is slightly more readable. + // We only use this to display the default value (this is an input type), so using + // the lenient OTP version of duration is ok - it is slightly more readable. .value(StringValue.of(DurationUtils.durationToStr(value.constant().asDuration()))) .build() ) diff --git a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql index b5d1dc00a39..e73b074df3f 100644 --- a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql +++ b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql @@ -2035,7 +2035,7 @@ the default. We can express the RelaxCost as a function `f(t) = constant + ratio `f(t)=t` is the NORMAL function. """ input RelaxCostInput { - "The constant value to add to the limit. Must be a positive number. The value isequivalent to transit-cost-seconds. Integers is treated as seconds, but you may use the duration format. Example: '3665 = 'DT1h1m5s' = '1h1m5s'." + "The constant value to add to the limit. Must be a positive number. The value is equivalent to transit-cost-seconds. Integers are treated as seconds, but you may use the duration format. Example: '3665 = 'DT1h1m5s' = '1h1m5s'." constant: Cost = "0s" "The factor to multiply with the 'other cost'. Minimum value is 1.0." ratio: Float = 1.0 From bed4effd5ea740572583055ea1385c37381e7765 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Jan 2024 12:31:29 +0100 Subject: [PATCH 41/86] Rename the feature flag DebugClient to DebugUi, remove separate one for the debug tiles --- docs/Configuration.md | 63 +++++++++---------- .../api/configuration/APIEndpoints.java | 6 +- .../framework/application/OTPFeature.java | 23 ++++--- .../standalone/server/GrizzlyServer.java | 2 +- 4 files changed, 50 insertions(+), 44 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index f1c0af287ab..7a81e68fb4a 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -219,38 +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. | ✓️ | | -| `APIGraphInspectorTile` | Enable the inspector endpoint for graph information for inspection/debugging purpose. | ✓️ | | -| `APIUpdaterStatus` | Enable endpoint for graph updaters status. | ✓️ | | -| `ConsiderPatternsForDirectTransfers` | Enable limiting transfers so that there is only a single transfer to each pattern. | ✓️ | | -| `DebugClient` | Enable the debug web client located at the root of the web server. | ✓️ | | -| `FloatingBike` | Enable floating bike routing. | ✓️ | | -| `GtfsGraphQlApi` | Enable GTFS GraphQL API. | ✓️ | | -| `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 Transmodel (NeTEx) GraphQL API. | ✓️ | ✓️ | -| `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. | | ✓️ | -| `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. | | ✓️ | -| `SandboxAPITravelTime` | Enable the isochrone/travel time surface API. | | ✓️ | -| `TransferAnalyzer` | Analyze transfers during graph build. | | ✓️ | -| `VehicleToStopHeuristics` | Enable improved heuristic for park-and-ride queries. | | ✓️ | +| 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. | | ✓️ | +| `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. | | ✓️ | +| `SandboxAPITravelTime` | Enable the isochrone/travel time surface API. | | ✓️ | +| `TransferAnalyzer` | Analyze transfers during graph build. | | ✓️ | +| `VehicleToStopHeuristics` | Enable improved heuristic for park-and-ride queries. | | ✓️ | diff --git a/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java b/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java index 64bbb2896a2..79556302f04 100644 --- a/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java +++ b/src/main/java/org/opentripplanner/api/configuration/APIEndpoints.java @@ -1,10 +1,10 @@ package org.opentripplanner.api.configuration; import static org.opentripplanner.framework.application.OTPFeature.APIBikeRental; -import static org.opentripplanner.framework.application.OTPFeature.APIGraphInspectorTile; import static org.opentripplanner.framework.application.OTPFeature.APIServerInfo; import static org.opentripplanner.framework.application.OTPFeature.APIUpdaterStatus; import static org.opentripplanner.framework.application.OTPFeature.ActuatorAPI; +import static org.opentripplanner.framework.application.OTPFeature.DebugUi; import static org.opentripplanner.framework.application.OTPFeature.GtfsGraphQlApi; import static org.opentripplanner.framework.application.OTPFeature.ReportApi; import static org.opentripplanner.framework.application.OTPFeature.SandboxAPIGeocoder; @@ -51,10 +51,10 @@ private APIEndpoints() { // Add feature enabled APIs, these can be enabled by default, some is not. // See the OTPFeature enum for details. addIfEnabled(APIBikeRental, BikeRental.class); - addIfEnabled(APIGraphInspectorTile, GraphInspectorTileResource.class); - addIfEnabled(APIGraphInspectorTile, GraphInspectorVectorTileResource.class); addIfEnabled(APIServerInfo, ServerInfo.class); addIfEnabled(APIUpdaterStatus, UpdaterStatusResource.class); + addIfEnabled(DebugUi, GraphInspectorTileResource.class); + addIfEnabled(DebugUi, GraphInspectorVectorTileResource.class); addIfEnabled(GtfsGraphQlApi, GtfsGraphQLAPI.class); addIfEnabled(TransmodelGraphQlApi, TransmodelAPI.class); diff --git a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java index 05d1284a883..308710ef5a0 100644 --- a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java +++ b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java @@ -16,20 +16,23 @@ public enum OTPFeature { APIBikeRental(true, false, "Enable the bike rental endpoint."), APIServerInfo(true, false, "Enable the server info endpoint."), - APIGraphInspectorTile( - true, - false, - "Enable the inspector endpoint for graph information for inspection/debugging purpose." - ), APIUpdaterStatus(true, false, "Enable endpoint for graph updaters status."), ConsiderPatternsForDirectTransfers( true, false, "Enable limiting transfers so that there is only a single transfer to each pattern." ), - DebugClient(true, false, "Enable the debug web client located at the root of the web server."), + DebugUi( + true, + false, + """ + 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(true, false, "Enable floating bike routing."), - GtfsGraphQlApi(true, false, "Enable GTFS GraphQL API."), + GtfsGraphQlApi(true, false, "Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md)."), GtfsGraphQlApiRentalStationFuzzyMatching( false, false, @@ -63,7 +66,11 @@ public enum OTPFeature { false, "Enforce transfers to happen according to the _transfers.txt_ (GTFS) and Interchanges (NeTEx). Turning this _off_ will increase the routing performance a little." ), - TransmodelGraphQlApi(true, true, "Enable Transmodel (NeTEx) GraphQL API."), + TransmodelGraphQlApi( + true, + true, + "Enable the [Transmodel (NeTEx) GraphQL API](apis/TransmodelApi.md)." + ), /* Sandbox extension features - Must be turned OFF by default */ diff --git a/src/main/java/org/opentripplanner/standalone/server/GrizzlyServer.java b/src/main/java/org/opentripplanner/standalone/server/GrizzlyServer.java index c422e9c24f3..7dc7c87f735 100644 --- a/src/main/java/org/opentripplanner/standalone/server/GrizzlyServer.java +++ b/src/main/java/org/opentripplanner/standalone/server/GrizzlyServer.java @@ -102,7 +102,7 @@ public void run() { httpServer.getServerConfiguration().addHttpHandler(dynamicHandler, "/otp/"); /* 2. A static content handler to serve the client JS apps etc. from the classpath. */ - if (OTPFeature.DebugClient.isOn()) { + if (OTPFeature.DebugUi.isOn()) { CLStaticHttpHandler staticHandler = new CLStaticHttpHandler( GrizzlyServer.class.getClassLoader(), "/client/" From 4b9889101a59b4cfce3704a7e6e23db8315f7b25 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Jan 2024 12:49:06 +0100 Subject: [PATCH 42/86] Update comment --- .../inspector/vector/stop/StopLayerBuilder.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java index 40784ab5b3b..70ce6a58735 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java +++ b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLayerBuilder.java @@ -8,11 +8,14 @@ import org.locationtech.jts.geom.Geometry; import org.opentripplanner.inspector.vector.LayerBuilder; import org.opentripplanner.inspector.vector.LayerParameters; +import org.opentripplanner.transit.model.site.AreaStop; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; /** - * A vector tile layer containing all {@link RegularStop}s inside the vector tile bounds. + * A vector tile layer for {@link StopLocation}s inside the vector tile bounds. These can be further + * filtered to get only a subset of stop implementations like {@link RegularStop} + * or {@link AreaStop}. */ public class StopLayerBuilder extends LayerBuilder { From 2b65d5702838d92a71a84923fb81afd8eb765a5c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Jan 2024 13:14:20 +0100 Subject: [PATCH 43/86] Make mapStyle configurable --- client-next/.env | 3 ++- client-next/.env.development | 3 ++- client-next/src/components/MapView/MapView.tsx | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/client-next/.env b/client-next/.env index 003970b4e1f..e8a9667bc23 100644 --- a/client-next/.env +++ b/client-next/.env @@ -1 +1,2 @@ -VITE_API_URL=/otp/routers/default/transmodel/index/graphql \ No newline at end of file +VITE_API_URL=/otp/routers/default/transmodel/index/graphql +VITE_DEBUG_STYLE_URL=/otp/routers/default/inspector/vectortile/style.json diff --git a/client-next/.env.development b/client-next/.env.development index e11b45c4411..b10ac31fdf9 100644 --- a/client-next/.env.development +++ b/client-next/.env.development @@ -1 +1,2 @@ -VITE_API_URL=http://localhost:8080/otp/routers/default/transmodel/index/graphql \ No newline at end of file +VITE_API_URL=http://localhost:8080/otp/routers/default/transmodel/index/graphql +VITE_DEBUG_STYLE_URL=http://localhost:8080/otp/routers/default/inspector/vectortile/style.json \ No newline at end of file diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 95c898c4a28..e401b1756e6 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -15,6 +15,8 @@ const initialViewState = { zoom: 4, }; +const styleUrl = import.meta.env.VITE_DEBUG_STYLE_URL; + type PopupData = { coordinates: LngLat; feature: MapboxGeoJSONFeature }; export function MapView({ @@ -40,7 +42,7 @@ export function MapView({ // @ts-ignore mapLib={import('maplibre-gl')} // @ts-ignore - mapStyle="http://localhost:8080/otp/routers/default/inspector/vectortile/style.json" + mapStyle={styleUrl} initialViewState={initialViewState} onDblClick={onMapDoubleClick} onContextMenu={(e) => { From 756a2890d67f805111250364d6822962fb0c7ae9 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Jan 2024 13:16:58 +0100 Subject: [PATCH 44/86] Use proper copyright symbol --- .../org/opentripplanner/apis/vectortiles/DebugStyleSpec.java | 2 +- .../resources/org/opentripplanner/apis/vectortiles/style.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 9a4fe9be123..ff933901cf8 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -17,7 +17,7 @@ public class DebugStyleSpec { "background", List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"), 256, - "© OpenStreetMap Contributors" + "© OpenStreetMap Contributors" ); public record VectorSourceLayer(VectorSource vectorSource, String vectorLayer) {} diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 6f95471a667..f5bb18f6f6a 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -7,7 +7,7 @@ "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" ], "tileSize": 256, - "attribution" : "© OpenStreetMap Contributors", + "attribution" : "© OpenStreetMap Contributors", "type": "raster" }, "vectorSource": { From 3c8fd3ed0696b0a41e1f3e0a6999c8f372ee25b8 Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 9 Jan 2024 20:26:26 +0100 Subject: [PATCH 45/86] review: Six spelling removeNone -> removeNon --- .../transit/AccessEgressFunctions.java | 10 ++-- .../rangeraptor/transit/AccessPaths.java | 8 +-- .../rangeraptor/transit/EgressPaths.java | 8 +-- .../transit/AccessEgressFunctionsTest.java | 50 +++++++++---------- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java index 275a0f85cf3..d4bf4d1ef92 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java @@ -81,10 +81,10 @@ private AccessEgressFunctions() {} * Filter non-optimal paths away for the standard search. This method does not * look at the c1 value. */ - static Collection removeNoneOptimalPathsForStandardRaptor( + static Collection removeNonOptimalPathsForStandardRaptor( Collection paths ) { - return removeNoneOptimalPaths(paths, STANDARD_COMPARATOR); + return removeNonOptimalPaths(paths, STANDARD_COMPARATOR); } /** @@ -92,10 +92,10 @@ static Collection removeNoneOptimalPathsForStandardRaptor( * not remove any paths since the caller should not pass in duplicates, but it turns out that * this happens, so we do it. */ - static Collection removeNoneOptimalPathsForMcRaptor( + static Collection removeNonOptimalPathsForMcRaptor( Collection paths ) { - var result = removeNoneOptimalPaths(paths, MC_COMPARATOR); + var result = removeNonOptimalPaths(paths, MC_COMPARATOR); if (LOG.isDebugEnabled() && result.size() < paths.size()) { var duplicates = new ArrayList<>(paths); duplicates.removeAll(result); @@ -137,7 +137,7 @@ static TIntObjectMap> groupByStop(Collection removeNoneOptimalPaths( + static Collection removeNonOptimalPaths( Collection paths, ParetoComparator comparator ) { diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java index dd757ac0415..5c26a10db23 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessPaths.java @@ -1,8 +1,8 @@ package org.opentripplanner.raptor.rangeraptor.transit; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.groupByRound; -import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForMcRaptor; -import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForStandardRaptor; +import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNonOptimalPathsForMcRaptor; +import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNonOptimalPathsForStandardRaptor; import gnu.trove.map.TIntObjectMap; import java.util.Arrays; @@ -59,9 +59,9 @@ public int calculateMaxNumberOfRides() { */ public static AccessPaths create(Collection paths, RaptorProfile profile) { if (profile.is(RaptorProfile.MULTI_CRITERIA)) { - paths = removeNoneOptimalPathsForMcRaptor(paths); + paths = removeNonOptimalPathsForMcRaptor(paths); } else { - paths = removeNoneOptimalPathsForStandardRaptor(paths); + paths = removeNonOptimalPathsForStandardRaptor(paths); } return new AccessPaths( groupByRound(paths, RaptorAccessEgress::stopReachedByWalking), diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java index a1688ff8f5b..374fc050782 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/EgressPaths.java @@ -2,8 +2,8 @@ import static org.opentripplanner.raptor.api.request.RaptorProfile.MULTI_CRITERIA; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.groupByStop; -import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForMcRaptor; -import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForStandardRaptor; +import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNonOptimalPathsForMcRaptor; +import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNonOptimalPathsForStandardRaptor; import gnu.trove.map.TIntObjectMap; import java.util.Collection; @@ -33,9 +33,9 @@ private EgressPaths(TIntObjectMap> pathsByStop) { */ public static EgressPaths create(Collection paths, RaptorProfile profile) { if (MULTI_CRITERIA.is(profile)) { - paths = removeNoneOptimalPathsForMcRaptor(paths); + paths = removeNonOptimalPathsForMcRaptor(paths); } else { - paths = removeNoneOptimalPathsForStandardRaptor(paths); + paths = removeNonOptimalPathsForStandardRaptor(paths); } return new EgressPaths(groupByStop(paths)); } diff --git a/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java b/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java index 041d74786d7..1d4f90557b0 100644 --- a/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java +++ b/src/test/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctionsTest.java @@ -6,8 +6,8 @@ import static org.opentripplanner.raptor._data.transit.TestAccessEgress.flexAndWalk; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.groupByRound; import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.groupByStop; -import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForMcRaptor; -import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNoneOptimalPathsForStandardRaptor; +import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNonOptimalPathsForMcRaptor; +import static org.opentripplanner.raptor.rangeraptor.transit.AccessEgressFunctions.removeNonOptimalPathsForStandardRaptor; import java.util.Arrays; import java.util.Collection; @@ -48,127 +48,127 @@ class AccessEgressFunctionsTest implements RaptorTestConstants { .openingHours(T00_10, T01_00); @Test - void removeNoneOptimalPathsForStandardRaptorTest() { + void removeNonOptimalPathsForStandardRaptorTest() { // Empty set - assertElements(List.of(), removeNoneOptimalPathsForStandardRaptor(List.of())); + assertElements(List.of(), removeNonOptimalPathsForStandardRaptor(List.of())); // One element - assertElements(List.of(WALK_8m), removeNoneOptimalPathsForStandardRaptor(List.of(WALK_8m))); + assertElements(List.of(WALK_8m), removeNonOptimalPathsForStandardRaptor(List.of(WALK_8m))); // Shortest duration assertElements( List.of(WALK_8m), - removeNoneOptimalPathsForStandardRaptor(List.of(WALK_8m, WALK_10m)) + removeNonOptimalPathsForStandardRaptor(List.of(WALK_8m, WALK_10m)) ); // Fewest rides assertElements( List.of(FLEX_1x_8m), - removeNoneOptimalPathsForStandardRaptor(List.of(FLEX_1x_8m, FLEX_2x_8m)) + removeNonOptimalPathsForStandardRaptor(List.of(FLEX_1x_8m, FLEX_2x_8m)) ); // Arriving at the stop on-board, and by-foot. // OnBoard is better because we can do a transfer walk to nearby stops. assertElements( List.of(FLEX_1x_8m), - removeNoneOptimalPathsForStandardRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_8m)) + removeNonOptimalPathsForStandardRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_8m)) ); // Flex+walk is faster, flex arrive on-board, both is optimal assertElements( List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_10m), - removeNoneOptimalPathsForStandardRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_10m)) + removeNonOptimalPathsForStandardRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_10m)) ); // Walk has few rides, and Flex is faster - both is optimal assertElements( List.of(WALK_10m, FLEX_1x_8m), - removeNoneOptimalPathsForStandardRaptor(List.of(WALK_10m, FLEX_1x_8m)) + removeNonOptimalPathsForStandardRaptor(List.of(WALK_10m, FLEX_1x_8m)) ); // Walk without opening hours is better than with, because it can be time-shifted without // any constraints assertElements( List.of(WALK_8m), - removeNoneOptimalPathsForStandardRaptor(List.of(WALK_8m, WALK_W_OPENING_HOURS_8m)) + removeNonOptimalPathsForStandardRaptor(List.of(WALK_8m, WALK_W_OPENING_HOURS_8m)) ); // Walk with opening hours can NOT dominate another access/egress without - even if it is // faster. The reason is that it may not be allowed to time-shift it to the desired time. assertElements( List.of(WALK_10m, WALK_W_OPENING_HOURS_8m), - removeNoneOptimalPathsForStandardRaptor(List.of(WALK_10m, WALK_W_OPENING_HOURS_8m)) + removeNonOptimalPathsForStandardRaptor(List.of(WALK_10m, WALK_W_OPENING_HOURS_8m)) ); // If two paths both have opening hours, both should be accepted. assertElements( List.of(WALK_W_OPENING_HOURS_8m, WALK_W_OPENING_HOURS_8m_OTHER), - removeNoneOptimalPathsForStandardRaptor( + removeNonOptimalPathsForStandardRaptor( List.of(WALK_W_OPENING_HOURS_8m, WALK_W_OPENING_HOURS_8m_OTHER) ) ); } @Test - void removeNoneOptimalPathsForMcRaptorTest() { + void removeNonOptimalPathsForMcRaptorTest() { // Empty set - assertElements(List.of(), removeNoneOptimalPathsForMcRaptor(List.of())); + assertElements(List.of(), removeNonOptimalPathsForMcRaptor(List.of())); // One element - assertElements(List.of(WALK_8m), removeNoneOptimalPathsForMcRaptor(List.of(WALK_8m))); + assertElements(List.of(WALK_8m), removeNonOptimalPathsForMcRaptor(List.of(WALK_8m))); // Lowest cost assertElements( List.of(WALK_10m_C1_LOW), - removeNoneOptimalPathsForMcRaptor(List.of(WALK_10m, WALK_10m_C1_LOW)) + removeNonOptimalPathsForMcRaptor(List.of(WALK_10m, WALK_10m_C1_LOW)) ); // Shortest duration - assertElements(List.of(WALK_8m), removeNoneOptimalPathsForMcRaptor(List.of(WALK_8m, WALK_10m))); + assertElements(List.of(WALK_8m), removeNonOptimalPathsForMcRaptor(List.of(WALK_8m, WALK_10m))); // Fewest rides assertElements( List.of(FLEX_1x_8m), - removeNoneOptimalPathsForMcRaptor(List.of(FLEX_1x_8m, FLEX_2x_8m)) + removeNonOptimalPathsForMcRaptor(List.of(FLEX_1x_8m, FLEX_2x_8m)) ); // Arriving at the stop on-board, and by-foot. // OnBoard is better because we can do a transfer walk to nearby stops. assertElements( List.of(FLEX_1x_8m), - removeNoneOptimalPathsForMcRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_8m)) + removeNonOptimalPathsForMcRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_8m)) ); // Flex+walk is faster, flex arrive on-board, both is optimal assertElements( List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_10m), - removeNoneOptimalPathsForStandardRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_10m)) + removeNonOptimalPathsForStandardRaptor(List.of(FLEX_AND_WALK_1x_8m, FLEX_1x_10m)) ); // Walk has few rides, and Flex is faster - both is optimal assertElements( List.of(WALK_10m, FLEX_1x_8m), - removeNoneOptimalPathsForMcRaptor(List.of(WALK_10m, FLEX_1x_8m)) + removeNonOptimalPathsForMcRaptor(List.of(WALK_10m, FLEX_1x_8m)) ); // Walk without opening hours is better than with, because it can be time-shifted without // any constraints assertElements( List.of(WALK_8m), - removeNoneOptimalPathsForMcRaptor(List.of(WALK_8m, WALK_W_OPENING_HOURS_8m)) + removeNonOptimalPathsForMcRaptor(List.of(WALK_8m, WALK_W_OPENING_HOURS_8m)) ); // Walk with opening hours can NOT dominate another access/egress without - even if it is // faster. The reason is that it may not be allowed to time-shift it to the desired time. assertElements( List.of(WALK_10m, WALK_W_OPENING_HOURS_8m), - removeNoneOptimalPathsForMcRaptor(List.of(WALK_10m, WALK_W_OPENING_HOURS_8m)) + removeNonOptimalPathsForMcRaptor(List.of(WALK_10m, WALK_W_OPENING_HOURS_8m)) ); // If two paths both have opening hours, both should be accepted. assertElements( List.of(WALK_W_OPENING_HOURS_8m, WALK_W_OPENING_HOURS_8m_OTHER), - removeNoneOptimalPathsForMcRaptor( + removeNonOptimalPathsForMcRaptor( List.of(WALK_W_OPENING_HOURS_8m, WALK_W_OPENING_HOURS_8m_OTHER) ) ); From de319cdf178a35f85e114d28945ab5c51e9cb2ad Mon Sep 17 00:00:00 2001 From: Thomas Gran Date: Tue, 9 Jan 2024 20:30:21 +0100 Subject: [PATCH 46/86] review: Make sure ParetoComparator is as readable as possible using white-space. --- .../rangeraptor/transit/AccessEgressFunctions.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java index d4bf4d1ef92..1ab737ae907 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/transit/AccessEgressFunctions.java @@ -29,7 +29,7 @@ public final class AccessEgressFunctions { private static final Logger LOG = LoggerFactory.getLogger(AccessEgressFunctions.class); /** - * Filter standard(not multi-criteria) Raptor access and egress paths. A path is pareto optimal + * Filter standard (not multi-criteria) Raptor access and egress paths. A path is pareto optimal * for a given stop if *
      *
    1. @@ -53,8 +53,7 @@ public final class AccessEgressFunctions { */ private static final ParetoComparator STANDARD_COMPARATOR = (l, r) -> ( - l.stopReachedOnBoard() && - !r.stopReachedOnBoard() || + (l.stopReachedOnBoard() && !r.stopReachedOnBoard()) || r.hasOpeningHours() || l.numberOfRides() < r.numberOfRides() || l.durationInSeconds() < r.durationInSeconds() @@ -66,13 +65,7 @@ public final class AccessEgressFunctions { * Raptor - it is a bug. */ private static final ParetoComparator MC_COMPARATOR = (l, r) -> - ( - (l.stopReachedOnBoard() && !r.stopReachedOnBoard()) || - r.hasOpeningHours() || - l.numberOfRides() < r.numberOfRides() || - l.durationInSeconds() < r.durationInSeconds() || - l.c1() < r.c1() - ); + STANDARD_COMPARATOR.leftDominanceExist(l, r) || l.c1() < r.c1(); /** private constructor to prevent instantiation of utils class. */ private AccessEgressFunctions() {} From 4b7cffa65b032e57925eb16b4807e45e0b385d7e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 10 Jan 2024 16:41:32 +0100 Subject: [PATCH 47/86] Update dev meeting calendar instructions --- docs/Developers-Guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Developers-Guide.md b/docs/Developers-Guide.md index e12d9cf6c1b..8a68f83f301 100644 --- a/docs/Developers-Guide.md +++ b/docs/Developers-Guide.md @@ -63,8 +63,8 @@ There are several ways to get involved: * Create pull requests citing the relevant issue. -* Join developer meetings hosted twice a week. Check the specific times - on [this calendar](https://calendar.google.com/calendar/u/0/embed?src=ormbltvsqb6adl80ejgudt0glc@group.calendar.google.com) +* Join developer meetings hosted twice a week. Check the specific times and URLs + on [this page](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/CONTRIBUTING.md#developer-meetings) ### Branches and Branch Protection From 41b5cf0fce39a93b59f4dbbca22134cf748217f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Erik=20St=C3=B8wer?= Date: Wed, 10 Jan 2024 22:20:11 +0100 Subject: [PATCH 48/86] Improve: Pin all dependencies, past and future --- client-next/.npmrc | 1 + client-next/package-lock.json | 50 +++++++++++++++++------------------ client-next/package.json | 50 +++++++++++++++++------------------ 3 files changed, 51 insertions(+), 50 deletions(-) create mode 100644 client-next/.npmrc diff --git a/client-next/.npmrc b/client-next/.npmrc new file mode 100644 index 00000000000..449691b70fd --- /dev/null +++ b/client-next/.npmrc @@ -0,0 +1 @@ +save-exact=true \ No newline at end of file diff --git a/client-next/package-lock.json b/client-next/package-lock.json index 85a29784489..19909ba109d 100644 --- a/client-next/package-lock.json +++ b/client-next/package-lock.json @@ -8,36 +8,36 @@ "name": "otp-debug-client-next", "version": "0.0.0", "dependencies": { - "@googlemaps/polyline-codec": "^1.0.28", - "bootstrap": "^5.3.1", - "graphql": "^16.8.0", - "graphql-request": "^6.1.0", - "maplibre-gl": "^3.3.0", - "react": "^18.2.0", - "react-bootstrap": "^2.8.0", - "react-dom": "^18.2.0", - "react-map-gl": "^7.1.5" + "@googlemaps/polyline-codec": "1.0.28", + "bootstrap": "5.3.1", + "graphql": "16.8.0", + "graphql-request": "6.1.0", + "maplibre-gl": "3.3.0", + "react": "18.2.0", + "react-bootstrap": "2.8.0", + "react-dom": "18.2.0", + "react-map-gl": "7.1.5" }, "devDependencies": { "@graphql-codegen/cli": "5.0.0", "@graphql-codegen/client-preset": "4.1.0", "@graphql-codegen/introspection": "4.0.0", - "@parcel/watcher": "^2.3.0", - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "@vitejs/plugin-react": "^4.0.3", - "eslint": "^8.45.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "prettier": "^3.0.3", - "typescript": "^5.2.2", - "vite": "^4.4.5" + "@parcel/watcher": "2.3.0", + "@types/react": "18.2.21", + "@types/react-dom": "18.2.7", + "@typescript-eslint/eslint-plugin": "6.5.0", + "@typescript-eslint/parser": "6.5.0", + "@vitejs/plugin-react": "4.0.4", + "eslint": "8.48.0", + "eslint-config-prettier": "9.0.0", + "eslint-plugin-import": "2.28.1", + "eslint-plugin-jsx-a11y": "6.7.1", + "eslint-plugin-react": "7.33.2", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-refresh": "0.4.3", + "prettier": "3.0.3", + "typescript": "5.2.2", + "vite": "4.4.9" } }, "node_modules/@aashutoshrathi/word-wrap": { diff --git a/client-next/package.json b/client-next/package.json index 4d453ed37ca..79d2c7942f1 100644 --- a/client-next/package.json +++ b/client-next/package.json @@ -15,35 +15,35 @@ "codegen": "graphql-codegen --config codegen.ts" }, "dependencies": { - "@googlemaps/polyline-codec": "^1.0.28", - "bootstrap": "^5.3.1", - "graphql": "^16.8.0", - "graphql-request": "^6.1.0", - "maplibre-gl": "^3.3.0", - "react": "^18.2.0", - "react-bootstrap": "^2.8.0", - "react-dom": "^18.2.0", - "react-map-gl": "^7.1.5" + "@googlemaps/polyline-codec": "1.0.28", + "bootstrap": "5.3.1", + "graphql": "16.8.0", + "graphql-request": "6.1.0", + "maplibre-gl": "3.3.0", + "react": "18.2.0", + "react-bootstrap": "2.8.0", + "react-dom": "18.2.0", + "react-map-gl": "7.1.5" }, "devDependencies": { "@graphql-codegen/cli": "5.0.0", "@graphql-codegen/client-preset": "4.1.0", "@graphql-codegen/introspection": "4.0.0", - "@parcel/watcher": "^2.3.0", - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "@vitejs/plugin-react": "^4.0.3", - "eslint": "^8.45.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "prettier": "^3.0.3", - "typescript": "^5.2.2", - "vite": "^4.4.5" + "@parcel/watcher": "2.3.0", + "@types/react": "18.2.21", + "@types/react-dom": "18.2.7", + "@typescript-eslint/eslint-plugin": "6.5.0", + "@typescript-eslint/parser": "6.5.0", + "@vitejs/plugin-react": "4.0.4", + "eslint": "8.48.0", + "eslint-config-prettier": "9.0.0", + "eslint-plugin-import": "2.28.1", + "eslint-plugin-jsx-a11y": "6.7.1", + "eslint-plugin-react": "7.33.2", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-refresh": "0.4.3", + "prettier": "3.0.3", + "typescript": "5.2.2", + "vite": "4.4.9" } } From 5972e5d4260192621c62be4019c63f71f327c005 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 11 Jan 2024 10:26:37 +0100 Subject: [PATCH 49/86] Resolve merge artifacts --- .../inspector/vector/stop/StopLocationPropertyMapper.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java index 44729bcb407..ab9685dd0f6 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/stop/StopLocationPropertyMapper.java @@ -5,13 +5,13 @@ import java.util.Collection; import java.util.List; import java.util.Locale; -import org.opentripplanner.api.mapping.I18NStringMapper; -import org.opentripplanner.api.mapping.PropertyMapper; +import org.opentripplanner.apis.support.mapping.PropertyMapper; +import org.opentripplanner.framework.i18n.I18NStringMapper; import org.opentripplanner.inspector.vector.KeyValue; import org.opentripplanner.transit.model.site.StopLocation; /** - * A {@link PropertyMapper} for the {@link AreaStopsLayerBuilder} for the OTP debug client. + * A {@link PropertyMapper} for the {@link StopLocationPropertyMapper} for the OTP debug client. */ public class StopLocationPropertyMapper extends PropertyMapper { From 4fd4325127655df8f0fb7c0ea26a672b961e7e2d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 11 Jan 2024 10:27:32 +0100 Subject: [PATCH 50/86] Update docs --- docs/Configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index b376b6b91dc..d43ff150926 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -241,7 +241,7 @@ Here is a list of all features which can be toggled on/off and their default val | `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. | ✓️ | ✓️ | +| `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! | | | From c5dc28984a83a968660c2478c2a6b3301fa7a6d4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 11 Jan 2024 12:14:00 +0100 Subject: [PATCH 51/86] Apply review feedback --- .../MapView/GeometryPropertyPopup.tsx | 27 ++++++++++++ .../src/components/MapView/MapView.tsx | 43 +++++++++---------- 2 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 client-next/src/components/MapView/GeometryPropertyPopup.tsx diff --git a/client-next/src/components/MapView/GeometryPropertyPopup.tsx b/client-next/src/components/MapView/GeometryPropertyPopup.tsx new file mode 100644 index 00000000000..d2b55689270 --- /dev/null +++ b/client-next/src/components/MapView/GeometryPropertyPopup.tsx @@ -0,0 +1,27 @@ +import { LngLat, Popup } from 'react-map-gl'; +import { Table } from 'react-bootstrap'; + +export function GeometryPropertyPopup({ + coordinates, + properties, + onClose, +}: { + coordinates: LngLat; + properties: { [s: string]: string }; + onClose: () => void; +}) { + return ( + onClose()}> + + + {Object.entries(properties).map(([key, value]) => ( + + + + + ))} + +
      {key}{value}
      +
      + ); +} diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index e401b1756e6..1b17e31cb88 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -1,4 +1,4 @@ -import { LngLat, Map, MapboxGeoJSONFeature, NavigationControl, Popup } from 'react-map-gl'; +import { LngLat, Map, MapboxGeoJSONFeature, NavigationControl } from 'react-map-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { TripPattern, TripQuery, TripQueryVariables } from '../../gql/graphql.ts'; import { NavigationMarkers } from './NavigationMarkers.tsx'; @@ -6,7 +6,7 @@ import { LegLines } from './LegLines.tsx'; import { useMapDoubleClick } from './useMapDoubleClick.ts'; import { useState } from 'react'; import { ContextMenuPopup } from './ContextMenuPopup.tsx'; -import { Table } from 'react-bootstrap'; +import { GeometryPropertyPopup } from './GeometryPropertyPopup.tsx'; // TODO: this should be configurable const initialViewState = { @@ -35,6 +35,20 @@ export function MapView({ const onMapDoubleClick = useMapDoubleClick({ tripQueryVariables, setTripQueryVariables }); const [showContextPopup, setShowContextPopup] = useState(null); const [showPropsPopup, setShowPropsPopup] = useState(null); + const showFeaturePropPopup = ( + e: mapboxgl.MapMouseEvent & { + features?: mapboxgl.MapboxGeoJSONFeature[] | undefined; + }, + setShowPropsPopup: (value: ((prevState: PopupData | null) => PopupData | null) | PopupData | null) => void, + ) => { + if (e.features) { + // if you click on a cluster of map features it's possible that there are multiple + // to select from. we are using the first one instead of presenting a selection UI. + // you can always zoom in closer if you want to make a more specific click. + const feature = e.features[0]; + setShowPropsPopup({ coordinates: e.lngLat, feature: feature }); + } + }; return (
      @@ -50,10 +64,7 @@ export function MapView({ }} interactiveLayerIds={['regular-stop']} onClick={(e) => { - if (e.features) { - const props = e.features[0]; - setShowPropsPopup({ coordinates: e.lngLat, feature: props }); - } + showFeaturePropPopup(e, setShowPropsPopup); }} // put lat/long in URL and pan to it on page reload hash={true} @@ -79,23 +90,11 @@ export function MapView({ /> )} {showPropsPopup?.feature?.properties && ( - setShowPropsPopup(null)} - > - - - {Object.entries(showPropsPopup.feature.properties).map(([key, value]) => ( - - - - - ))} - -
      {key}{value}
      -
      + /> )}
      From c4eb78c44f87c5668e9d26cbe91e0ce95a7851c2 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 11 Jan 2024 13:07:49 +0000 Subject: [PATCH 52/86] Upgrade debug client to version 2024/01/2024-01-11T13:07 --- src/client/debug-client-preview/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/debug-client-preview/index.html b/src/client/debug-client-preview/index.html index dfb4915efcc..d9bf3a2af16 100644 --- a/src/client/debug-client-preview/index.html +++ b/src/client/debug-client-preview/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
      From 24d5929747795ab39bb820683ceb3d51a6fabeea Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 11 Jan 2024 14:33:38 +0100 Subject: [PATCH 53/86] Remove extra function definition --- client-next/src/components/MapView/MapView.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 1b17e31cb88..57e718ff187 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -39,7 +39,6 @@ export function MapView({ e: mapboxgl.MapMouseEvent & { features?: mapboxgl.MapboxGeoJSONFeature[] | undefined; }, - setShowPropsPopup: (value: ((prevState: PopupData | null) => PopupData | null) | PopupData | null) => void, ) => { if (e.features) { // if you click on a cluster of map features it's possible that there are multiple @@ -64,7 +63,7 @@ export function MapView({ }} interactiveLayerIds={['regular-stop']} onClick={(e) => { - showFeaturePropPopup(e, setShowPropsPopup); + showFeaturePropPopup(e); }} // put lat/long in URL and pan to it on page reload hash={true} From bdffc557f115126147506c8f793d2797abe2891c Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 11 Jan 2024 14:43:21 +0000 Subject: [PATCH 54/86] Add changelog entry for #5604 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 7458295b9ba..fa3615b90b3 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -72,6 +72,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Adding situation-version to TransmodelGraphQL API [#5592](https://github.com/opentripplanner/OpenTripPlanner/pull/5592) - Move REST API into sandbox [#5580](https://github.com/opentripplanner/OpenTripPlanner/pull/5580) - Fix high walk reluctance leading to zero egress results for rental searches [#5605](https://github.com/opentripplanner/OpenTripPlanner/pull/5605) +- Remove GTFS-RT websocket updater [#5604](https://github.com/opentripplanner/OpenTripPlanner/pull/5604) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From b554800ebf90b66628590e79b7ce2702a6325a5f Mon Sep 17 00:00:00 2001 From: OTP Serialization Version Bot Date: Thu, 11 Jan 2024 14:43:37 +0000 Subject: [PATCH 55/86] Bump serialization version id for #5604 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6567356b392..abd70a60749 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ - 135 + 136 30.1 2.50 From cf5d875fbf4dd003023e4a506ac8a84d0175d8a7 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 11 Jan 2024 15:50:30 +0100 Subject: [PATCH 56/86] Remove extra method call indirection --- client-next/src/components/MapView/MapView.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 57e718ff187..5b6223a5dee 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -62,9 +62,7 @@ export function MapView({ setShowContextPopup(e.lngLat); }} interactiveLayerIds={['regular-stop']} - onClick={(e) => { - showFeaturePropPopup(e); - }} + onClick={showFeaturePropPopup} // put lat/long in URL and pan to it on page reload hash={true} // disable pitching and rotating the map From 6ab81142e4a17029e3a86a5b41e7cbab96af69f2 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Thu, 11 Jan 2024 16:03:16 +0000 Subject: [PATCH 57/86] Add changelog entry for #5602 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index fa3615b90b3..50846c3fce7 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -73,6 +73,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Move REST API into sandbox [#5580](https://github.com/opentripplanner/OpenTripPlanner/pull/5580) - Fix high walk reluctance leading to zero egress results for rental searches [#5605](https://github.com/opentripplanner/OpenTripPlanner/pull/5605) - Remove GTFS-RT websocket updater [#5604](https://github.com/opentripplanner/OpenTripPlanner/pull/5604) +- Add stop layer to new Debug UI [#5602](https://github.com/opentripplanner/OpenTripPlanner/pull/5602) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From ab694b8efb80bf856a1fb468ef03a9d97c514cfe Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 11 Jan 2024 16:04:27 +0000 Subject: [PATCH 58/86] Upgrade debug client to version 2024/01/2024-01-11T16:03 --- src/client/debug-client-preview/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/debug-client-preview/index.html b/src/client/debug-client-preview/index.html index d9bf3a2af16..5f77418dbe6 100644 --- a/src/client/debug-client-preview/index.html +++ b/src/client/debug-client-preview/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
      From c39c366b27d50602e5507c9206cd18696e3f6d48 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 11 Jan 2024 16:19:11 +0100 Subject: [PATCH 59/86] Add documentation about MQTT updater --- doc-templates/UpdaterConfig.md | 13 ++- docs/RouterConfiguration.md | 7 ++ docs/UpdaterConfig.md | 82 +++++++++++++++++-- docs/sandbox/SiriUpdater.md | 30 +++---- .../standalone/config/router-config.json | 8 ++ 5 files changed, 115 insertions(+), 25 deletions(-) diff --git a/doc-templates/UpdaterConfig.md b/doc-templates/UpdaterConfig.md index a239317f244..57152671ec7 100644 --- a/doc-templates/UpdaterConfig.md +++ b/doc-templates/UpdaterConfig.md @@ -8,8 +8,8 @@ # Updater configuration -This section covers all options that can be set in the *router-config.json* in the -[updaters](RouterConfiguration.md) section. +This section covers options that can be set in the updaters section of `router-config.json`. +See the parameter summary and examples in the router configuration documentation Real-time data are those that are not added to OTP during the graph build phase but during runtime. @@ -44,6 +44,15 @@ The information is downloaded in a single HTTP request and polled regularly. +### Streaming TripUpdates via MQTT + +This updater connects to an MQTT broker and processes TripUpdates in a streaming fashion. This means +that they will be applied individually in near-realtime rather than in batches at a certain interval. + +This system powers the realtime updates in Helsinki and more information can be found +[on Github](https://github.com/HSLdevcom/transitdata). + + ### Vehicle Positions diff --git a/docs/RouterConfiguration.md b/docs/RouterConfiguration.md index 30e423f29b1..5ca87a2aefa 100644 --- a/docs/RouterConfiguration.md +++ b/docs/RouterConfiguration.md @@ -727,6 +727,13 @@ Used to group requests when monitoring OTP. "Authorization" : "A-Token" } }, + { + "type" : "mqtt-gtfs-rt-updater", + "url" : "tcp://pred.rt.hsl.fi", + "topic" : "gtfsrt/v2/fi/hsl/tu", + "feedId" : "HSL", + "fuzzyTripMatching" : true + }, { "type" : "vehicle-positions", "url" : "https://s3.amazonaws.com/kcm-alerts-realtime-prod/vehiclepositions.pb", diff --git a/docs/UpdaterConfig.md b/docs/UpdaterConfig.md index 6c219d07ddf..212a2d55370 100644 --- a/docs/UpdaterConfig.md +++ b/docs/UpdaterConfig.md @@ -8,8 +8,8 @@ # Updater configuration -This section covers all options that can be set in the *router-config.json* in the -[updaters](RouterConfiguration.md) section. +This section covers options that can be set in the updaters section of `router-config.json`. +See the parameter summary and examples in the router configuration documentation Real-time data are those that are not added to OTP during the graph build phase but during runtime. @@ -164,6 +164,72 @@ HTTP headers to add to the request. Any header key, value can be inserted. +### Streaming TripUpdates via MQTT + +This updater connects to an MQTT broker and processes TripUpdates in a streaming fashion. This means +that they will be applied individually in near-realtime rather than in batches at a certain interval. + +This system powers the realtime updates in Helsinki and more information can be found +[on Github](https://github.com/HSLdevcom/transitdata). + + + + +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|-----------------------------------------------------------------------|:---------:|----------------------------------------------|:----------:|----------------------|:-----:| +| type = "mqtt-gtfs-rt-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | +| [backwardsDelayPropagationType](#u__6__backwardsDelayPropagationType) | `enum` | How backwards propagation should be handled. | *Optional* | `"required-no-data"` | 2.2 | +| feedId | `string` | The feed id to apply the updates to. | *Required* | | na | +| fuzzyTripMatching | `boolean` | Whether to match trips fuzzily. | *Optional* | `false` | na | +| qos | `integer` | QOS level. | *Optional* | `0` | na | +| topic | `string` | The topic to subscribe to. | *Required* | | na | +| url | `string` | URL of the MQTT broker. | *Required* | | na | + + +##### Parameter details + +

      backwardsDelayPropagationType

      + +**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"required-no-data"` +**Path:** /updaters/[6] +**Enum values:** `required-no-data` | `required` | `always` + +How backwards propagation should be handled. + + REQUIRED_NO_DATA: + Default value. Only propagates delays backwards when it is required to ensure that the times + are increasing, and it sets the NO_DATA flag on the stops so these automatically updated times + are not exposed through APIs. + + REQUIRED: + Only propagates delays backwards when it is required to ensure that the times are increasing. + The updated times are exposed through APIs. + + ALWAYS: + Propagates delays backwards on stops with no estimates regardless if it's required or not. + The updated times are exposed through APIs. + + + + +##### Example configuration + +```JSON +// router-config.json +{ + "updaters" : [ + { + "type" : "mqtt-gtfs-rt-updater", + "url" : "tcp://pred.rt.hsl.fi", + "topic" : "gtfsrt/v2/fi/hsl/tu", + "feedId" : "HSL", + "fuzzyTripMatching" : true + } + ] +} +``` + + ### Vehicle Positions @@ -181,24 +247,24 @@ The information is downloaded in a single HTTP request and polled regularly. | frequency | `duration` | How often the positions should be updated. | *Optional* | `"PT1M"` | 2.2 | | fuzzyTripMatching | `boolean` | Whether to match trips fuzzily. | *Optional* | `false` | 2.5 | | url | `uri` | The URL of GTFS-RT protobuf HTTP resource to download the positions from. | *Required* | | 2.2 | -| [features](#u__6__features) | `enum set` | Which features of GTFS RT vehicle positions should be loaded into OTP. | *Optional* | | 2.5 | -| [headers](#u__6__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | +| [features](#u__7__features) | `enum set` | Which features of GTFS RT vehicle positions should be loaded into OTP. | *Optional* | | 2.5 | +| [headers](#u__7__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | ##### Parameter details -

      features

      +

      features

      **Since version:** `2.5` ∙ **Type:** `enum set` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[6] +**Path:** /updaters/[7] **Enum values:** `position` | `stop-position` | `occupancy` Which features of GTFS RT vehicle positions should be loaded into OTP. -

      headers

      +

      headers

      **Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[6] +**Path:** /updaters/[7] HTTP headers to add to the request. Any header key, value can be inserted. diff --git a/docs/sandbox/SiriUpdater.md b/docs/sandbox/SiriUpdater.md index 7730df67d12..f6c4c3f999f 100644 --- a/docs/sandbox/SiriUpdater.md +++ b/docs/sandbox/SiriUpdater.md @@ -37,16 +37,16 @@ To enable the SIRI updater you need to add it to the updaters section of the `ro | previewInterval | `duration` | TODO | *Optional* | | 2.0 | | requestorRef | `string` | The requester reference. | *Optional* | | 2.0 | | timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.0 | -| [url](#u__7__url) | `string` | The URL to send the HTTP requests to. | *Required* | | 2.0 | -| [headers](#u__7__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | +| [url](#u__8__url) | `string` | The URL to send the HTTP requests to. | *Required* | | 2.0 | +| [headers](#u__8__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | ##### Parameter details -

      url

      +

      url

      **Since version:** `2.0` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[7] +**Path:** /updaters/[8] The URL to send the HTTP requests to. @@ -58,10 +58,10 @@ renamed by the loader when processed: -

      headers

      +

      headers

      **Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[7] +**Path:** /updaters/[8] HTTP headers to add to the request. Any header key, value can be inserted. @@ -97,21 +97,21 @@ HTTP headers to add to the request. Any header key, value can be inserted. |---------------------------------|:---------------:|--------------------------------------------------------------------------------------------------------|:----------:|---------------|:-----:| | type = "siri-sx-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | | blockReadinessUntilInitialized | `boolean` | Whether catching up with the updates should block the readiness check from returning a 'ready' result. | *Optional* | `false` | 2.0 | -| [earlyStart](#u__8__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | +| [earlyStart](#u__9__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | | feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.0 | | frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.0 | | requestorRef | `string` | The requester reference. | *Optional* | | 2.0 | | timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.0 | -| [url](#u__8__url) | `string` | The URL to send the HTTP requests to. Supports http/https and file protocol. | *Required* | | 2.0 | -| [headers](#u__8__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | +| [url](#u__9__url) | `string` | The URL to send the HTTP requests to. Supports http/https and file protocol. | *Required* | | 2.0 | +| [headers](#u__9__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | ##### Parameter details -

      earlyStart

      +

      earlyStart

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

      url

      +

      url

      **Since version:** `2.0` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[8] +**Path:** /updaters/[9] The URL to send the HTTP requests to. Supports http/https and file protocol. @@ -135,10 +135,10 @@ renamed by the loader when processed: -

      headers

      +

      headers

      **Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[8] +**Path:** /updaters/[9] HTTP headers to add to the request. Any header key, value can be inserted. diff --git a/src/test/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index 1a5eec22a28..5abb5ef87a6 100644 --- a/src/test/resources/standalone/config/router-config.json +++ b/src/test/resources/standalone/config/router-config.json @@ -283,6 +283,14 @@ "Authorization": "A-Token" } }, + // Streaming GTFS-RT TripUpdates through an MQTT broker + { + "type": "mqtt-gtfs-rt-updater", + "url": "tcp://pred.rt.hsl.fi", + "topic": "gtfsrt/v2/fi/hsl/tu", + "feedId": "HSL", + "fuzzyTripMatching": true + }, // Polling for GTFS-RT Vehicle Positions - output can be fetched via trip pattern GraphQL API { "type": "vehicle-positions", From 31896ea58e69190f6520fc39e268f9c9509f7501 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 02:09:23 +0000 Subject: [PATCH 60/86] Update dependency org.mockito:mockito-core to v5.9.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index abd70a60749..e5f86fe5b66 100644 --- a/pom.xml +++ b/pom.xml @@ -713,7 +713,7 @@ org.mockito mockito-core - 5.8.0 + 5.9.0 test From 28a46b4898828145b00e53ee8d9d8da28c578bda Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 11 Jan 2024 23:16:02 +0100 Subject: [PATCH 61/86] Add debug vector layer for displaying edges --- .../src/components/MapView/MapView.tsx | 18 +++--- .../apis/vectortiles/DebugStyleSpec.java | 61 ++++++++++++++++--- .../GraphInspectorVectorTileResource.java | 22 ++++--- .../vectortiles/model/LayerStyleBuilder.java | 33 ++++++++++ .../apis/vectortiles/model/LayerType.java | 1 + .../vector/edges/EdgeLayerBuilder.java | 34 +++++++++++ .../vector/edges/EdgePropertyMapper.java | 30 +++++++++ .../apis/vectortiles/DebugStyleSpecTest.java | 3 +- 8 files changed, 178 insertions(+), 24 deletions(-) create mode 100644 src/main/java/org/opentripplanner/inspector/vector/edges/EdgeLayerBuilder.java create mode 100644 src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 5b6223a5dee..87c8a61e256 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -1,12 +1,12 @@ -import { LngLat, Map, MapboxGeoJSONFeature, NavigationControl } from 'react-map-gl'; +import {LngLat, Map, MapboxGeoJSONFeature, NavigationControl} from 'react-map-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; -import { TripPattern, TripQuery, TripQueryVariables } from '../../gql/graphql.ts'; -import { NavigationMarkers } from './NavigationMarkers.tsx'; -import { LegLines } from './LegLines.tsx'; -import { useMapDoubleClick } from './useMapDoubleClick.ts'; -import { useState } from 'react'; -import { ContextMenuPopup } from './ContextMenuPopup.tsx'; -import { GeometryPropertyPopup } from './GeometryPropertyPopup.tsx'; +import {TripPattern, TripQuery, TripQueryVariables} from '../../gql/graphql.ts'; +import {NavigationMarkers} from './NavigationMarkers.tsx'; +import {LegLines} from './LegLines.tsx'; +import {useMapDoubleClick} from './useMapDoubleClick.ts'; +import {useState} from 'react'; +import {ContextMenuPopup} from './ContextMenuPopup.tsx'; +import {GeometryPropertyPopup} from './GeometryPropertyPopup.tsx'; // TODO: this should be configurable const initialViewState = { @@ -61,7 +61,7 @@ export function MapView({ onContextMenu={(e) => { setShowContextPopup(e.lngLat); }} - interactiveLayerIds={['regular-stop']} + interactiveLayerIds={['regular-stop', 'edge-fallback', 'edge', 'link']} onClick={showFeaturePropPopup} // put lat/long in URL and pan to it on page reload hash={true} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index ff933901cf8..47ad5d5be36 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -6,6 +6,14 @@ import org.opentripplanner.apis.vectortiles.model.TileSource; import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource; import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; +import org.opentripplanner.street.model.edge.AreaEdge; +import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; +import org.opentripplanner.street.model.edge.EscalatorEdge; +import org.opentripplanner.street.model.edge.StreetEdge; +import org.opentripplanner.street.model.edge.StreetTransitEntranceLink; +import org.opentripplanner.street.model.edge.StreetTransitStopLink; +import org.opentripplanner.street.model.edge.TemporaryFreeEdge; +import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge; /** * A Mapbox/Mapblibre style specification for rendering debug information about transit and @@ -19,21 +27,60 @@ public class DebugStyleSpec { 256, "© OpenStreetMap Contributors" ); + private static final String MAGENTA = "#f21d52"; + private static final String YELLOW = "#e2d40d"; + private static final String GREY = "#8e8e89"; + private static final int MAX_ZOOM = 23; public record VectorSourceLayer(VectorSource vectorSource, String vectorLayer) {} - static StyleSpec build(VectorSource debugSource, VectorSourceLayer regularStops) { + static StyleSpec build( + VectorSource debugSource, + VectorSourceLayer regularStops, + VectorSourceLayer edges + ) { List sources = List.of(BACKGROUND_SOURCE, debugSource); return new StyleSpec( "OTP Debug Tiles", sources, List.of( + LayerStyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0), LayerStyleBuilder - .ofId("background") - .typeRaster() - .source(BACKGROUND_SOURCE) - .minZoom(0) - .maxZoom(22), + .ofId("edge-fallback") + .typeLine() + .vectorSourceLayer(edges) + .lineColor(GREY) + .lineWidth(3) + .minZoom(15) + .maxZoom(MAX_ZOOM), + LayerStyleBuilder + .ofId("edge") + .typeLine() + .vectorSourceLayer(edges) + .lineColor(MAGENTA) + .edgeFilter( + StreetEdge.class, + AreaEdge.class, + EscalatorEdge.class, + TemporaryPartialStreetEdge.class, + TemporaryFreeEdge.class + ) + .lineWidth(3) + .minZoom(13) + .maxZoom(MAX_ZOOM), + LayerStyleBuilder + .ofId("link") + .typeLine() + .vectorSourceLayer(edges) + .lineColor(YELLOW) + .edgeFilter( + StreetTransitStopLink.class, + StreetTransitEntranceLink.class, + BoardingLocationToStopLink.class + ) + .lineWidth(3) + .minZoom(13) + .maxZoom(MAX_ZOOM), LayerStyleBuilder .ofId("regular-stop") .typeCircle() @@ -41,7 +88,7 @@ static StyleSpec build(VectorSource debugSource, VectorSourceLayer regularStops) .circleStroke("#140d0e", 2) .circleColor("#fcf9fa") .minZoom(13) - .maxZoom(22) + .maxZoom(MAX_ZOOM) ) ); } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index 67f92f01ee3..e2008eaea29 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -1,5 +1,8 @@ package org.opentripplanner.apis.vectortiles; +import static org.opentripplanner.apis.vectortiles.model.LayerType.Edges; +import static org.opentripplanner.apis.vectortiles.model.LayerType.GeofencingZones; +import static org.opentripplanner.apis.vectortiles.model.LayerType.RegularStop; import static org.opentripplanner.framework.io.HttpUtils.APPLICATION_X_PROTOBUF; import jakarta.ws.rs.GET; @@ -28,6 +31,7 @@ import org.opentripplanner.inspector.vector.LayerBuilder; import org.opentripplanner.inspector.vector.LayerParameters; import org.opentripplanner.inspector.vector.VectorTileResponseFactory; +import org.opentripplanner.inspector.vector.edges.EdgeLayerBuilder; import org.opentripplanner.inspector.vector.geofencing.GeofencingZonesLayerBuilder; import org.opentripplanner.inspector.vector.stop.StopLayerBuilder; import org.opentripplanner.model.FeedInfo; @@ -40,19 +44,18 @@ @Path("/routers/{ignoreRouterId}/inspector/vectortile") public class GraphInspectorVectorTileResource { - private static final LayerParams REGULAR_STOPS = new LayerParams( - "regularStops", - LayerType.RegularStop - ); + private static final LayerParams REGULAR_STOPS = new LayerParams("regularStops", RegularStop); private static final LayerParams AREA_STOPS = new LayerParams("areaStops", LayerType.AreaStop); private static final LayerParams GEOFENCING_ZONES = new LayerParams( "geofencingZones", - LayerType.GeofencingZones + GeofencingZones ); + private static final LayerParams EDGES = new LayerParams("edges", Edges); private static final List> DEBUG_LAYERS = List.of( REGULAR_STOPS, AREA_STOPS, - GEOFENCING_ZONES + GEOFENCING_ZONES, + EDGES ); private final OtpServerRequestContext serverContext; @@ -131,7 +134,11 @@ public StyleSpec getTileJson(@Context UriInfo uri, @Context HttpHeaders headers) ); var vectorSource = new VectorSource("debug", url); - return DebugStyleSpec.build(vectorSource, REGULAR_STOPS.toVectorSourceLayer(vectorSource)); + return DebugStyleSpec.build( + vectorSource, + REGULAR_STOPS.toVectorSourceLayer(vectorSource), + EDGES.toVectorSourceLayer(vectorSource) + ); } @Nonnull @@ -162,6 +169,7 @@ private static LayerBuilder createLayerBuilder( e -> context.transitService().findAreaStops(e) ); case GeofencingZones -> new GeofencingZonesLayerBuilder(context.graph(), layerParameters); + case Edges -> new EdgeLayerBuilder(context.graph(), layerParameters); }; } } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java index 41144611f92..52455102ccd 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java @@ -2,12 +2,16 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Stream; import org.opentripplanner.apis.vectortiles.DebugStyleSpec.VectorSourceLayer; +import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.json.ObjectMappers; +import org.opentripplanner.street.model.edge.Edge; /** * Builds a Maplibre/Mapbox vector tile @@ -20,6 +24,7 @@ public class LayerStyleBuilder { private static final String SOURCE_LAYER = "source-layer"; private final Map props = new HashMap<>(); private final Map paint = new HashMap<>(); + private List filter = List.of(); public static LayerStyleBuilder ofId(String id) { return new LayerStyleBuilder(id); @@ -32,6 +37,7 @@ public LayerStyleBuilder vectorSourceLayer(VectorSourceLayer source) { public enum LayerType { Circle, + Line, Raster, } @@ -75,6 +81,10 @@ public LayerStyleBuilder typeCircle() { return type(LayerType.Circle); } + public LayerStyleBuilder typeLine() { + return type(LayerType.Line); + } + private LayerStyleBuilder type(LayerType type) { props.put(TYPE, type.name().toLowerCase()); return this; @@ -91,6 +101,26 @@ public LayerStyleBuilder circleStroke(String color, int width) { return this; } + // Line styling + + public LayerStyleBuilder lineColor(String color) { + paint.put("line-color", validateColor(color)); + return this; + } + + public LayerStyleBuilder lineWidth(float width) { + paint.put("line-width", width); + return this; + } + + // filtering edge + @SafeVarargs + public final LayerStyleBuilder edgeFilter(Class... classToFilter) { + var clazzes = Arrays.stream(classToFilter).map(Class::getSimpleName).toList(); + filter = ListUtils.combine(List.of("in", "class"), clazzes); + return this; + } + public JsonNode toJson() { validate(); @@ -98,6 +128,9 @@ public JsonNode toJson() { if (!paint.isEmpty()) { copy.put("paint", paint); } + if (!filter.isEmpty()) { + copy.put("filter", filter); + } return OBJECT_MAPPER.valueToTree(copy); } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java index f4cb7a636fa..4324a511a6b 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java @@ -4,4 +4,5 @@ public enum LayerType { RegularStop, AreaStop, GeofencingZones, + Edges, } diff --git a/src/main/java/org/opentripplanner/inspector/vector/edges/EdgeLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/edges/EdgeLayerBuilder.java new file mode 100644 index 00000000000..60e65f7b35a --- /dev/null +++ b/src/main/java/org/opentripplanner/inspector/vector/edges/EdgeLayerBuilder.java @@ -0,0 +1,34 @@ +package org.opentripplanner.inspector.vector.edges; + +import java.util.List; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.opentripplanner.inspector.vector.LayerBuilder; +import org.opentripplanner.inspector.vector.LayerParameters; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.routing.graph.index.StreetIndex; +import org.opentripplanner.street.model.edge.Edge; + +public class EdgeLayerBuilder extends LayerBuilder { + + private final StreetIndex streetIndex; + + public EdgeLayerBuilder(Graph graph, LayerParameters layerParameters) { + super(new EdgePropertyMapper(), layerParameters.name(), layerParameters.expansionFactor()); + this.streetIndex = graph.getStreetIndex(); + } + + @Override + protected List getGeometries(Envelope query) { + return streetIndex + .getEdgesForEnvelope(query) + .stream() + .filter(e -> e.getGeometry() != null) + .map(edge -> { + Geometry geometry = edge.getGeometry(); + geometry.setUserData(edge); + return geometry; + }) + .toList(); + } +} diff --git a/src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java new file mode 100644 index 00000000000..393f7132798 --- /dev/null +++ b/src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java @@ -0,0 +1,30 @@ +package org.opentripplanner.inspector.vector.edges; + +import static org.opentripplanner.inspector.vector.KeyValue.kv; + +import java.util.Collection; +import java.util.List; +import org.opentripplanner.apis.support.mapping.PropertyMapper; +import org.opentripplanner.framework.collection.ListUtils; +import org.opentripplanner.inspector.vector.KeyValue; +import org.opentripplanner.street.model.edge.Edge; +import org.opentripplanner.street.model.edge.EscalatorEdge; +import org.opentripplanner.street.model.edge.StreetEdge; + +public class EdgePropertyMapper extends PropertyMapper { + + @Override + protected Collection map(Edge input) { + List baseProps = List.of(kv("class", input.getClass().getSimpleName())); + List properties = + switch (input) { + case StreetEdge e -> List.of( + kv("permission", e.getPermission().toString()), + kv("bicycleSafetyFactor", e.getBicycleSafetyFactor()) + ); + case EscalatorEdge e -> List.of(kv("distance", e.getDistanceMeters())); + default -> List.of(); + }; + return ListUtils.combine(baseProps, properties); + } +} diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index d685e07a2f2..5cca178550a 100644 --- a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -16,7 +16,8 @@ class DebugStyleSpecTest { void spec() { var vectorSource = new VectorSource("vectorSource", "https://example.com"); var regularStops = new VectorSourceLayer(vectorSource, "regularStops"); - var spec = DebugStyleSpec.build(vectorSource, regularStops); + var edges = new VectorSourceLayer(vectorSource, "edges"); + var spec = DebugStyleSpec.build(vectorSource, regularStops, edges); var json = ObjectMappers.ignoringExtraFields().valueToTree(spec); var expectation = RES.fileToString("style.json"); From a74d878b70426f3c8a46ef075729978b8020b35f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 12 Jan 2024 00:00:43 +0100 Subject: [PATCH 62/86] Finetune edge styling --- .../apis/vectortiles/DebugStyleSpec.java | 20 ++- .../vectortiles/model/LayerStyleBuilder.java | 29 +++- .../vector/edges/EdgePropertyMapper.java | 3 +- .../apis/vectortiles/DebugStyleSpecTest.java | 4 +- .../apis/vectortiles/style.json | 145 ++++++++++++++---- 5 files changed, 161 insertions(+), 40 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 47ad5d5be36..1508575d336 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -2,16 +2,20 @@ import java.util.List; import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder; +import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder.ZoomDependentNumber; +import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder.ZoomStop; import org.opentripplanner.apis.vectortiles.model.StyleSpec; import org.opentripplanner.apis.vectortiles.model.TileSource; import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource; import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; +import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; import org.opentripplanner.street.model.edge.EscalatorEdge; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetTransitEntranceLink; import org.opentripplanner.street.model.edge.StreetTransitStopLink; +import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.TemporaryFreeEdge; import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge; @@ -31,6 +35,10 @@ public class DebugStyleSpec { private static final String YELLOW = "#e2d40d"; private static final String GREY = "#8e8e89"; private static final int MAX_ZOOM = 23; + private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( + 1.3f, + List.of(new ZoomStop(13, 1), new ZoomStop(MAX_ZOOM, 10)) + ); public record VectorSourceLayer(VectorSource vectorSource, String vectorLayer) {} @@ -50,7 +58,7 @@ static StyleSpec build( .typeLine() .vectorSourceLayer(edges) .lineColor(GREY) - .lineWidth(3) + .lineWidth(LINE_WIDTH) .minZoom(15) .maxZoom(MAX_ZOOM), LayerStyleBuilder @@ -65,7 +73,7 @@ static StyleSpec build( TemporaryPartialStreetEdge.class, TemporaryFreeEdge.class ) - .lineWidth(3) + .lineWidth(LINE_WIDTH) .minZoom(13) .maxZoom(MAX_ZOOM), LayerStyleBuilder @@ -76,9 +84,11 @@ static StyleSpec build( .edgeFilter( StreetTransitStopLink.class, StreetTransitEntranceLink.class, - BoardingLocationToStopLink.class + BoardingLocationToStopLink.class, + StreetVehicleRentalLink.class, + StreetVehicleParkingLink.class ) - .lineWidth(3) + .lineWidth(LINE_WIDTH) .minZoom(13) .maxZoom(MAX_ZOOM), LayerStyleBuilder @@ -87,7 +97,7 @@ static StyleSpec build( .vectorSourceLayer(regularStops) .circleStroke("#140d0e", 2) .circleColor("#fcf9fa") - .minZoom(13) + .minZoom(11) .maxZoom(MAX_ZOOM) ) ); diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java index 52455102ccd..ea93bdde6a2 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Arrays; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -22,8 +22,8 @@ public class LayerStyleBuilder { private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); private static final String TYPE = "type"; private static final String SOURCE_LAYER = "source-layer"; - private final Map props = new HashMap<>(); - private final Map paint = new HashMap<>(); + private final Map props = new LinkedHashMap<>(); + private final Map paint = new LinkedHashMap<>(); private List filter = List.of(); public static LayerStyleBuilder ofId(String id) { @@ -113,6 +113,11 @@ public LayerStyleBuilder lineWidth(float width) { return this; } + public LayerStyleBuilder lineWidth(ZoomDependentNumber zoomStops) { + paint.put("line-width", zoomStops.toJson()); + return this; + } + // filtering edge @SafeVarargs public final LayerStyleBuilder edgeFilter(Class... classToFilter) { @@ -124,7 +129,7 @@ public final LayerStyleBuilder edgeFilter(Class... classToFilter public JsonNode toJson() { validate(); - var copy = new HashMap<>(props); + var copy = new LinkedHashMap<>(props); if (!paint.isEmpty()) { copy.put("paint", paint); } @@ -146,4 +151,20 @@ private void validate() { .of(TYPE) .forEach(p -> Objects.requireNonNull(props.get(p), "%s must be set".formatted(p))); } + + public record ZoomStop(int zoom, float value) { + public List toList() { + return List.of(zoom, value); + } + } + + public record ZoomDependentNumber(float base, List stops) { + public JsonNode toJson() { + var props = new LinkedHashMap<>(); + props.put("base", base); + var vals = stops.stream().map(ZoomStop::toList).toList(); + props.put("stops", vals); + return OBJECT_MAPPER.valueToTree(props); + } + } } diff --git a/src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java index 393f7132798..5bfc2dc6df7 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java @@ -1,5 +1,6 @@ package org.opentripplanner.inspector.vector.edges; +import static org.opentripplanner.framework.lang.DoubleUtils.roundTo2Decimals; import static org.opentripplanner.inspector.vector.KeyValue.kv; import java.util.Collection; @@ -20,7 +21,7 @@ protected Collection map(Edge input) { switch (input) { case StreetEdge e -> List.of( kv("permission", e.getPermission().toString()), - kv("bicycleSafetyFactor", e.getBicycleSafetyFactor()) + kv("bicycleSafetyFactor", roundTo2Decimals(e.getBicycleSafetyFactor())) ); case EscalatorEdge e -> List.of(kv("distance", e.getDistanceMeters())); default -> List.of(); diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index 5cca178550a..9a18da3e5c9 100644 --- a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -10,7 +10,7 @@ class DebugStyleSpecTest { - private final ResourceLoader RES = ResourceLoader.of(this); + private final ResourceLoader RESOURCES = ResourceLoader.of(this); @Test void spec() { @@ -20,7 +20,7 @@ void spec() { var spec = DebugStyleSpec.build(vectorSource, regularStops, edges); var json = ObjectMappers.ignoringExtraFields().valueToTree(spec); - var expectation = RES.fileToString("style.json"); + var expectation = RESOURCES.fileToString("style.json"); assertEqualJson(expectation, json); } } diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index f5bb18f6f6a..df9f73c3f26 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -1,42 +1,131 @@ { - "name": "OTP Debug Tiles", - "sources": { - "background": { - "id": "background", - "tiles": [ + "name" : "OTP Debug Tiles", + "sources" : { + "background" : { + "id" : "background", + "tiles" : [ "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" ], - "tileSize": 256, + "tileSize" : 256, "attribution" : "© OpenStreetMap Contributors", - "type": "raster" + "type" : "raster" }, - "vectorSource": { - "id": "vectorSource", - "url": "https://example.com", - "type": "vector" + "vectorSource" : { + "id" : "vectorSource", + "url" : "https://example.com", + "type" : "vector" } }, - "layers": [ + "layers" : [ { - "id": "background", - "source": "background", - "type": "raster", - "maxzoom": 22, - "minzoom": 0 + "id" : "background", + "type" : "raster", + "source" : "background", + "minzoom" : 0 }, { - "maxzoom": 22, - "paint": { - "circle-stroke-width": 2, - "circle-color": "#fcf9fa", - "circle-stroke-color": "#140d0e" + "id" : "edge-fallback", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 15, + "maxzoom" : 23, + "paint" : { + "line-color" : "#8e8e89", + "line-width" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 2.0 + ], + [ + 23, + 10.0 + ] + ] + } + } + }, + { + "id" : "edge", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "line-color" : "#f21d52", + "line-width" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 2.0 + ], + [ + 23, + 10.0 + ] + ] + } }, - "id": "regular-stop", - "source": "vectorSource", - "source-layer": "regularStops", - "type": "circle", - "minzoom": 13 + "filter" : [ + "in", + "class", + "StreetEdge", + "AreaEdge", + "EscalatorEdge", + "TemporaryPartialStreetEdge", + "TemporaryFreeEdge" + ] + }, + { + "id" : "link", + "type" : "line", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 13, + "maxzoom" : 23, + "paint" : { + "line-color" : "#e2d40d", + "line-width" : { + "base" : 1.3, + "stops" : [ + [ + 13, + 2.0 + ], + [ + 23, + 10.0 + ] + ] + } + }, + "filter" : [ + "in", + "class", + "StreetTransitStopLink", + "StreetTransitEntranceLink", + "BoardingLocationToStopLink", + "StreetVehicleRentalLink", + "StreetVehicleParkingLink" + ] + }, + { + "id" : "regular-stop", + "type" : "circle", + "source" : "vectorSource", + "source-layer" : "regularStops", + "minzoom" : 11, + "maxzoom" : 23, + "paint" : { + "circle-stroke-color" : "#140d0e", + "circle-stroke-width" : 2, + "circle-color" : "#fcf9fa" + } } ], - "version": 8 + "version" : 8 } From b6fde751915e5178040f79da64d073935205ad11 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 12 Jan 2024 13:20:52 +0100 Subject: [PATCH 63/86] Add more styles --- .../MapView/GeometryPropertyPopup.tsx | 8 ++++++- .../src/components/MapView/MapView.tsx | 16 ++++++------- .../apis/vectortiles/DebugStyleSpec.java | 3 ++- .../vectortiles/model/LayerStyleBuilder.java | 24 ++++++++++++++++++- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/client-next/src/components/MapView/GeometryPropertyPopup.tsx b/client-next/src/components/MapView/GeometryPropertyPopup.tsx index d2b55689270..5492d15d472 100644 --- a/client-next/src/components/MapView/GeometryPropertyPopup.tsx +++ b/client-next/src/components/MapView/GeometryPropertyPopup.tsx @@ -11,7 +11,13 @@ export function GeometryPropertyPopup({ onClose: () => void; }) { return ( - onClose()}> + onClose()} + maxWidth="350px" + > {Object.entries(properties).map(([key, value]) => ( diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 87c8a61e256..47193658bb0 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -1,12 +1,12 @@ -import {LngLat, Map, MapboxGeoJSONFeature, NavigationControl} from 'react-map-gl'; +import { LngLat, Map, MapboxGeoJSONFeature, NavigationControl } from 'react-map-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; -import {TripPattern, TripQuery, TripQueryVariables} from '../../gql/graphql.ts'; -import {NavigationMarkers} from './NavigationMarkers.tsx'; -import {LegLines} from './LegLines.tsx'; -import {useMapDoubleClick} from './useMapDoubleClick.ts'; -import {useState} from 'react'; -import {ContextMenuPopup} from './ContextMenuPopup.tsx'; -import {GeometryPropertyPopup} from './GeometryPropertyPopup.tsx'; +import { TripPattern, TripQuery, TripQueryVariables } from '../../gql/graphql.ts'; +import { NavigationMarkers } from './NavigationMarkers.tsx'; +import { LegLines } from './LegLines.tsx'; +import { useMapDoubleClick } from './useMapDoubleClick.ts'; +import { useState } from 'react'; +import { ContextMenuPopup } from './ContextMenuPopup.tsx'; +import { GeometryPropertyPopup } from './GeometryPropertyPopup.tsx'; // TODO: this should be configurable const initialViewState = { diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 1508575d336..b3470ed579e 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -37,7 +37,7 @@ public class DebugStyleSpec { private static final int MAX_ZOOM = 23; private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( 1.3f, - List.of(new ZoomStop(13, 1), new ZoomStop(MAX_ZOOM, 10)) + List.of(new ZoomStop(13, 0.5f), new ZoomStop(MAX_ZOOM, 10)) ); public record VectorSourceLayer(VectorSource vectorSource, String vectorLayer) {} @@ -96,6 +96,7 @@ static StyleSpec build( .typeCircle() .vectorSourceLayer(regularStops) .circleStroke("#140d0e", 2) + .circleRadius(new ZoomDependentNumber(1, List.of(new ZoomStop(11,1), new ZoomStop(MAX_ZOOM, 10)))) .circleColor("#fcf9fa") .minZoom(11) .maxZoom(MAX_ZOOM) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java index ea93bdde6a2..96ad10a644c 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java @@ -24,6 +24,8 @@ public class LayerStyleBuilder { private static final String SOURCE_LAYER = "source-layer"; private final Map props = new LinkedHashMap<>(); private final Map paint = new LinkedHashMap<>(); + private final Map layout = new LinkedHashMap<>(); + private final Map line = new LinkedHashMap<>(); private List filter = List.of(); public static LayerStyleBuilder ofId(String id) { @@ -35,6 +37,8 @@ public LayerStyleBuilder vectorSourceLayer(VectorSourceLayer source) { return sourceLayer(source.vectorLayer()); } + + public enum LayerType { Circle, Line, @@ -82,7 +86,9 @@ public LayerStyleBuilder typeCircle() { } public LayerStyleBuilder typeLine() { - return type(LayerType.Line); + type(LayerType.Line); + line.put("line-cap", "round"); + return this; } private LayerStyleBuilder type(LayerType type) { @@ -101,6 +107,11 @@ public LayerStyleBuilder circleStroke(String color, int width) { return this; } + public LayerStyleBuilder circleRadius(ZoomDependentNumber radius) { + paint.put("circle-radius", radius.toJson()); + return this; + } + // Line styling public LayerStyleBuilder lineColor(String color) { @@ -118,6 +129,11 @@ public LayerStyleBuilder lineWidth(ZoomDependentNumber zoomStops) { return this; } + public LayerStyleBuilder intiallyHidden() { + layout.put("visibility", "none"); + return this; + } + // filtering edge @SafeVarargs public final LayerStyleBuilder edgeFilter(Class... classToFilter) { @@ -136,6 +152,12 @@ public JsonNode toJson() { if (!filter.isEmpty()) { copy.put("filter", filter); } + if (!layout.isEmpty()) { + copy.put("layout", layout); + } + if (!line.isEmpty()) { + copy.put("line", line); + } return OBJECT_MAPPER.valueToTree(copy); } From f49b2160f2fe070629cdf2e17fbe8c01317a8dc4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 13 Jan 2024 21:23:42 +0100 Subject: [PATCH 64/86] Finetune layer selector --- client-next/package-lock.json | 56 ++++++------- client-next/package.json | 2 +- .../components/MapView/ContextMenuPopup.tsx | 2 +- .../MapView/GeometryPropertyPopup.tsx | 2 +- .../src/components/MapView/LayerControl.tsx | 78 +++++++++++++++++++ .../src/components/MapView/MapView.tsx | 10 ++- .../components/MapView/useMapDoubleClick.ts | 2 +- client-next/src/style.css | 9 +++ .../apis/vectortiles/DebugStyleSpec.java | 11 ++- 9 files changed, 133 insertions(+), 39 deletions(-) create mode 100644 client-next/src/components/MapView/LayerControl.tsx diff --git a/client-next/package-lock.json b/client-next/package-lock.json index 19909ba109d..11894b5b246 100644 --- a/client-next/package-lock.json +++ b/client-next/package-lock.json @@ -12,7 +12,7 @@ "bootstrap": "5.3.1", "graphql": "16.8.0", "graphql-request": "6.1.0", - "maplibre-gl": "3.3.0", + "maplibre-gl": "3.6.2", "react": "18.2.0", "react-bootstrap": "2.8.0", "react-dom": "18.2.0", @@ -2864,9 +2864,9 @@ } }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.0.tgz", - "integrity": "sha512-ZbhX9CTV+Z7vHwkRIasDOwTSzr76e8Q6a55RMsAibjyX6+P0ZNL1qAKNzOjjBDP3+aEfNMl7hHo5knuY6pTAUQ==", + "version": "19.3.3", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz", + "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", @@ -3306,9 +3306,9 @@ } }, "node_modules/@types/geojson": { - "version": "7946.0.10", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", - "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + "version": "7946.0.13", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz", + "integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==" }, "node_modules/@types/js-yaml": { "version": "4.0.5", @@ -3335,14 +3335,14 @@ "dev": true }, "node_modules/@types/mapbox__point-geometry": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz", - "integrity": "sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA==" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==" }, "node_modules/@types/mapbox__vector-tile": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz", - "integrity": "sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", "dependencies": { "@types/geojson": "*", "@types/mapbox__point-geometry": "*", @@ -3364,9 +3364,9 @@ "dev": true }, "node_modules/@types/pbf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.2.tgz", - "integrity": "sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ==" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" }, "node_modules/@types/prop-types": { "version": "15.7.5", @@ -3412,9 +3412,9 @@ "dev": true }, "node_modules/@types/supercluster": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.0.tgz", - "integrity": "sha512-6JapQ2GmEkH66r23BK49I+u6zczVDGTtiJEVvKDYZVSm/vepWaJuTq6BXzJ6I4agG5s8vA1KM7m/gXWDg03O4Q==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", "dependencies": { "@types/geojson": "*" } @@ -7379,9 +7379,9 @@ } }, "node_modules/maplibre-gl": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-3.3.0.tgz", - "integrity": "sha512-LDia3b8u2S8qtl50n8TYJM0IPLzfc01KDc71LNuydvDiEXAGBI5togty+juVtUipRZZjs4dAW6xhgrabc6lIgw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-3.6.2.tgz", + "integrity": "sha512-krg2KFIdOpLPngONDhP6ixCoWl5kbdMINP0moMSJFVX7wX1Clm2M9hlNKXS8vBGlVWwR5R3ZfI6IPrYz7c+aCQ==", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", @@ -7390,12 +7390,12 @@ "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^19.3.0", - "@types/geojson": "^7946.0.10", - "@types/mapbox__point-geometry": "^0.1.2", - "@types/mapbox__vector-tile": "^1.3.0", - "@types/pbf": "^3.0.2", - "@types/supercluster": "^7.1.0", + "@maplibre/maplibre-gl-style-spec": "^19.3.3", + "@types/geojson": "^7946.0.13", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", "earcut": "^2.2.4", "geojson-vt": "^3.2.1", "gl-matrix": "^3.4.3", diff --git a/client-next/package.json b/client-next/package.json index 79d2c7942f1..6388122befc 100644 --- a/client-next/package.json +++ b/client-next/package.json @@ -19,7 +19,7 @@ "bootstrap": "5.3.1", "graphql": "16.8.0", "graphql-request": "6.1.0", - "maplibre-gl": "3.3.0", + "maplibre-gl": "3.6.2", "react": "18.2.0", "react-bootstrap": "2.8.0", "react-dom": "18.2.0", diff --git a/client-next/src/components/MapView/ContextMenuPopup.tsx b/client-next/src/components/MapView/ContextMenuPopup.tsx index 52dd3858298..7d51a4c21ad 100644 --- a/client-next/src/components/MapView/ContextMenuPopup.tsx +++ b/client-next/src/components/MapView/ContextMenuPopup.tsx @@ -1,5 +1,5 @@ import { TripQueryVariables } from '../../gql/graphql.ts'; -import { LngLat, Popup } from 'react-map-gl'; +import { LngLat, Popup } from 'react-map-gl/maplibre'; import { Button, ButtonGroup } from 'react-bootstrap'; export function ContextMenuPopup({ diff --git a/client-next/src/components/MapView/GeometryPropertyPopup.tsx b/client-next/src/components/MapView/GeometryPropertyPopup.tsx index 5492d15d472..3ecedb2c67d 100644 --- a/client-next/src/components/MapView/GeometryPropertyPopup.tsx +++ b/client-next/src/components/MapView/GeometryPropertyPopup.tsx @@ -1,4 +1,4 @@ -import { LngLat, Popup } from 'react-map-gl'; +import { LngLat, Popup } from 'react-map-gl/maplibre'; import { Table } from 'react-bootstrap'; export function GeometryPropertyPopup({ diff --git a/client-next/src/components/MapView/LayerControl.tsx b/client-next/src/components/MapView/LayerControl.tsx new file mode 100644 index 00000000000..783d0411673 --- /dev/null +++ b/client-next/src/components/MapView/LayerControl.tsx @@ -0,0 +1,78 @@ +import type { ControlPosition } from 'react-map-gl'; +import { useControl } from 'react-map-gl'; +import { Map } from 'maplibre-gl'; + +type LayerControlProps = { + position?: ControlPosition; +}; + +class LayerList { + private map: Map | null = null; + private _container: HTMLDivElement; + + onAdd(map: Map) { + this.map = map; + this._container = document.createElement('div'); + this._container.className = 'maplibregl-ctrl maplibregl-ctrl-group layer-select'; + + map?.on('load', () => { + while (this._container.firstChild) { + this._container.removeChild(this._container.firstChild); + } + + const h3 = document.createElement('h6'); + h3.textContent = 'Debug layers'; + this._container.appendChild(h3); + + map + ?.getLayersOrder() + .map((l) => map.getLayer(l)) + .filter((s) => s?.type !== 'raster') + .reverse() + .forEach((layer) => { + const div = document.createElement('div'); + const input = document.createElement('input'); + input.type = 'checkbox'; + input.value = layer?.id; + input.onchange = (e) => { + e.preventDefault(); + e.stopPropagation(); + + if (this.layerVisible(layer)) { + map.setLayoutProperty(layer.id, 'visibility', 'none'); + } else { + map.setLayoutProperty(layer.id, 'visibility', 'visible'); + } + }; + const visible = map.getLayoutProperty(layer.id, 'visibility') !== 'none'; + input.checked = visible; + const label = document.createElement('label'); + label.textContent = layer.id; + div.appendChild(input); + div.appendChild(label); + this._container.appendChild(div); + }); + }); + + return this._container; + } + + private layerVisible(layer: { id: string }) { + return this.map?.getLayoutProperty(layer.id, 'visibility') !== 'none'; + } + + onCreate() {} + + onRemove() { + this._container.parentNode.removeChild(this._container); + this.map = undefined; + } +} + +export default function LayerListControl(props: LayerControlProps) { + useControl(() => new LayerList(), { + position: props.position, + }); + + return null; +} diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 47193658bb0..b192fb6fdad 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -1,4 +1,4 @@ -import { LngLat, Map, MapboxGeoJSONFeature, NavigationControl } from 'react-map-gl'; +import { LngLat, Map, MapGeoJSONFeature, MapMouseEvent, NavigationControl } from 'react-map-gl/maplibre'; import 'maplibre-gl/dist/maplibre-gl.css'; import { TripPattern, TripQuery, TripQueryVariables } from '../../gql/graphql.ts'; import { NavigationMarkers } from './NavigationMarkers.tsx'; @@ -7,6 +7,7 @@ import { useMapDoubleClick } from './useMapDoubleClick.ts'; import { useState } from 'react'; import { ContextMenuPopup } from './ContextMenuPopup.tsx'; import { GeometryPropertyPopup } from './GeometryPropertyPopup.tsx'; +import LayerListControl from './LayerControl.tsx'; // TODO: this should be configurable const initialViewState = { @@ -17,7 +18,7 @@ const initialViewState = { const styleUrl = import.meta.env.VITE_DEBUG_STYLE_URL; -type PopupData = { coordinates: LngLat; feature: MapboxGeoJSONFeature }; +type PopupData = { coordinates: LngLat; feature: MapGeoJSONFeature }; export function MapView({ tripQueryVariables, @@ -36,8 +37,8 @@ export function MapView({ const [showContextPopup, setShowContextPopup] = useState(null); const [showPropsPopup, setShowPropsPopup] = useState(null); const showFeaturePropPopup = ( - e: mapboxgl.MapMouseEvent & { - features?: mapboxgl.MapboxGeoJSONFeature[] | undefined; + e: MapMouseEvent & { + features?: MapGeoJSONFeature[] | undefined; }, ) => { if (e.features) { @@ -75,6 +76,7 @@ export function MapView({ setTripQueryVariables={setTripQueryVariables} loading={loading} /> + {tripQueryResult?.trip.tripPatterns.length && ( )} diff --git a/client-next/src/components/MapView/useMapDoubleClick.ts b/client-next/src/components/MapView/useMapDoubleClick.ts index 35cb1d76a62..eb9217aa167 100644 --- a/client-next/src/components/MapView/useMapDoubleClick.ts +++ b/client-next/src/components/MapView/useMapDoubleClick.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react'; import { TripQueryVariables } from '../../gql/graphql.ts'; -import { LngLat, MapLayerMouseEvent } from 'react-map-gl'; +import { LngLat, MapLayerMouseEvent } from 'react-map-gl/maplibre'; const setCoordinates = (tripQueryVariables: TripQueryVariables, lngLat: LngLat, key: 'from' | 'to') => ({ ...tripQueryVariables, diff --git a/client-next/src/style.css b/client-next/src/style.css index b7661779991..9a538686043 100644 --- a/client-next/src/style.css +++ b/client-next/src/style.css @@ -119,3 +119,12 @@ font-size: 14px; padding-left: 2px; } + +/* debug layer selector */ + +.maplibregl-ctrl-group.layer-select { + padding: 10px; +} +.maplibregl-ctrl-group.layer-select label { + margin-left: 6px; +} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index b3470ed579e..e9dc771932d 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -11,7 +11,9 @@ import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; +import org.opentripplanner.street.model.edge.ElevatorHopEdge; import org.opentripplanner.street.model.edge.EscalatorEdge; +import org.opentripplanner.street.model.edge.PathwayEdge; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetTransitEntranceLink; import org.opentripplanner.street.model.edge.StreetTransitStopLink; @@ -60,7 +62,8 @@ static StyleSpec build( .lineColor(GREY) .lineWidth(LINE_WIDTH) .minZoom(15) - .maxZoom(MAX_ZOOM), + .maxZoom(MAX_ZOOM) + .intiallyHidden(), LayerStyleBuilder .ofId("edge") .typeLine() @@ -70,12 +73,14 @@ static StyleSpec build( StreetEdge.class, AreaEdge.class, EscalatorEdge.class, + PathwayEdge.class, + ElevatorHopEdge.class, TemporaryPartialStreetEdge.class, TemporaryFreeEdge.class ) .lineWidth(LINE_WIDTH) .minZoom(13) - .maxZoom(MAX_ZOOM), + .maxZoom(MAX_ZOOM).intiallyHidden(), LayerStyleBuilder .ofId("link") .typeLine() @@ -90,7 +95,7 @@ static StyleSpec build( ) .lineWidth(LINE_WIDTH) .minZoom(13) - .maxZoom(MAX_ZOOM), + .maxZoom(MAX_ZOOM).intiallyHidden(), LayerStyleBuilder .ofId("regular-stop") .typeCircle() From 32d070d01634f5c7c87810046ee5ce5567ba11ff Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 13 Jan 2024 23:48:16 +0100 Subject: [PATCH 65/86] Finetune layer selector --- .../apis/vectortiles/DebugStyleSpec.java | 10 +++++++--- .../apis/vectortiles/model/LayerStyleBuilder.java | 2 -- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index e9dc771932d..7c14e66d475 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -80,7 +80,8 @@ static StyleSpec build( ) .lineWidth(LINE_WIDTH) .minZoom(13) - .maxZoom(MAX_ZOOM).intiallyHidden(), + .maxZoom(MAX_ZOOM) + .intiallyHidden(), LayerStyleBuilder .ofId("link") .typeLine() @@ -95,13 +96,16 @@ static StyleSpec build( ) .lineWidth(LINE_WIDTH) .minZoom(13) - .maxZoom(MAX_ZOOM).intiallyHidden(), + .maxZoom(MAX_ZOOM) + .intiallyHidden(), LayerStyleBuilder .ofId("regular-stop") .typeCircle() .vectorSourceLayer(regularStops) .circleStroke("#140d0e", 2) - .circleRadius(new ZoomDependentNumber(1, List.of(new ZoomStop(11,1), new ZoomStop(MAX_ZOOM, 10)))) + .circleRadius( + new ZoomDependentNumber(1, List.of(new ZoomStop(11, 1), new ZoomStop(MAX_ZOOM, 10))) + ) .circleColor("#fcf9fa") .minZoom(11) .maxZoom(MAX_ZOOM) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java index 96ad10a644c..585e3bce2e1 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java @@ -37,8 +37,6 @@ public LayerStyleBuilder vectorSourceLayer(VectorSourceLayer source) { return sourceLayer(source.vectorLayer()); } - - public enum LayerType { Circle, Line, From 73ec23ebf42aa4225963e680af080cabe488b5ed Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 14 Jan 2024 11:24:09 +0100 Subject: [PATCH 66/86] Improve TypeScript --- .../src/components/MapView/LayerControl.tsx | 69 ++++++++++--------- .../src/components/MapView/MapView.tsx | 2 +- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/client-next/src/components/MapView/LayerControl.tsx b/client-next/src/components/MapView/LayerControl.tsx index 783d0411673..4d9974f66e2 100644 --- a/client-next/src/components/MapView/LayerControl.tsx +++ b/client-next/src/components/MapView/LayerControl.tsx @@ -3,58 +3,59 @@ import { useControl } from 'react-map-gl'; import { Map } from 'maplibre-gl'; type LayerControlProps = { - position?: ControlPosition; + position: ControlPosition; }; class LayerList { private map: Map | null = null; - private _container: HTMLDivElement; + private readonly container: HTMLDivElement = document.createElement('div'); onAdd(map: Map) { this.map = map; - this._container = document.createElement('div'); - this._container.className = 'maplibregl-ctrl maplibregl-ctrl-group layer-select'; + this.container.className = 'maplibregl-ctrl maplibregl-ctrl-group layer-select'; - map?.on('load', () => { - while (this._container.firstChild) { - this._container.removeChild(this._container.firstChild); + map.on('load', () => { + // clean on + while (this.container.firstChild) { + this.container.removeChild(this.container.firstChild); } - const h3 = document.createElement('h6'); - h3.textContent = 'Debug layers'; - this._container.appendChild(h3); + const title = document.createElement('h6'); + title.textContent = 'Debug layers'; + this.container.appendChild(title); map - ?.getLayersOrder() + .getLayersOrder() .map((l) => map.getLayer(l)) .filter((s) => s?.type !== 'raster') .reverse() .forEach((layer) => { - const div = document.createElement('div'); - const input = document.createElement('input'); - input.type = 'checkbox'; - input.value = layer?.id; - input.onchange = (e) => { - e.preventDefault(); - e.stopPropagation(); + if (layer) { + const div = document.createElement('div'); + const input = document.createElement('input'); + input.type = 'checkbox'; + input.value = layer?.id; + input.onchange = (e) => { + e.preventDefault(); + e.stopPropagation(); - if (this.layerVisible(layer)) { - map.setLayoutProperty(layer.id, 'visibility', 'none'); - } else { - map.setLayoutProperty(layer.id, 'visibility', 'visible'); - } - }; - const visible = map.getLayoutProperty(layer.id, 'visibility') !== 'none'; - input.checked = visible; - const label = document.createElement('label'); - label.textContent = layer.id; - div.appendChild(input); - div.appendChild(label); - this._container.appendChild(div); + if (this.layerVisible(layer)) { + map.setLayoutProperty(layer.id, 'visibility', 'none'); + } else { + map.setLayoutProperty(layer.id, 'visibility', 'visible'); + } + }; + input.checked = this.layerVisible(layer); + const label = document.createElement('label'); + label.textContent = layer.id; + div.appendChild(input); + div.appendChild(label); + this.container.appendChild(div); + } }); }); - return this._container; + return this.container; } private layerVisible(layer: { id: string }) { @@ -64,8 +65,8 @@ class LayerList { onCreate() {} onRemove() { - this._container.parentNode.removeChild(this._container); - this.map = undefined; + this.container.parentNode?.removeChild(this.container); + this.map = null; } } diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index b192fb6fdad..a3c925a2519 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -76,7 +76,7 @@ export function MapView({ setTripQueryVariables={setTripQueryVariables} loading={loading} /> - + {tripQueryResult?.trip.tripPatterns.length && ( )} From 95e4d1559f2645ef9957bea2f7361504743a3074 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sun, 14 Jan 2024 23:23:38 +0100 Subject: [PATCH 67/86] Finetune map layers --- .../src/components/MapView/LayerControl.tsx | 21 ++++++++----------- .../apis/vectortiles/DebugStyleSpec.java | 11 +--------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/client-next/src/components/MapView/LayerControl.tsx b/client-next/src/components/MapView/LayerControl.tsx index 4d9974f66e2..4566c3679fc 100644 --- a/client-next/src/components/MapView/LayerControl.tsx +++ b/client-next/src/components/MapView/LayerControl.tsx @@ -1,17 +1,15 @@ import type { ControlPosition } from 'react-map-gl'; import { useControl } from 'react-map-gl'; -import { Map } from 'maplibre-gl'; +import { IControl, Map} from 'maplibre-gl'; type LayerControlProps = { position: ControlPosition; }; -class LayerList { - private map: Map | null = null; +class LayerList implements IControl { private readonly container: HTMLDivElement = document.createElement('div'); onAdd(map: Map) { - this.map = map; this.container.className = 'maplibregl-ctrl maplibregl-ctrl-group layer-select'; map.on('load', () => { @@ -34,20 +32,22 @@ class LayerList { const div = document.createElement('div'); const input = document.createElement('input'); input.type = 'checkbox'; - input.value = layer?.id; + input.value = layer.id; + input.id = layer.id; input.onchange = (e) => { e.preventDefault(); e.stopPropagation(); - if (this.layerVisible(layer)) { + if (this.layerVisible(map, layer)) { map.setLayoutProperty(layer.id, 'visibility', 'none'); } else { map.setLayoutProperty(layer.id, 'visibility', 'visible'); } }; - input.checked = this.layerVisible(layer); + input.checked = this.layerVisible(map, layer); const label = document.createElement('label'); label.textContent = layer.id; + label.htmlFor = layer.id; div.appendChild(input); div.appendChild(label); this.container.appendChild(div); @@ -58,15 +58,12 @@ class LayerList { return this.container; } - private layerVisible(layer: { id: string }) { - return this.map?.getLayoutProperty(layer.id, 'visibility') !== 'none'; + private layerVisible(map: Map, layer: { id: string }) { + return map.getLayoutProperty(layer.id, 'visibility') !== 'none'; } - onCreate() {} - onRemove() { this.container.parentNode?.removeChild(this.container); - this.map = null; } } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 7c14e66d475..05e91d9e6b7 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -55,15 +55,6 @@ static StyleSpec build( sources, List.of( LayerStyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0), - LayerStyleBuilder - .ofId("edge-fallback") - .typeLine() - .vectorSourceLayer(edges) - .lineColor(GREY) - .lineWidth(LINE_WIDTH) - .minZoom(15) - .maxZoom(MAX_ZOOM) - .intiallyHidden(), LayerStyleBuilder .ofId("edge") .typeLine() @@ -107,7 +98,7 @@ static StyleSpec build( new ZoomDependentNumber(1, List.of(new ZoomStop(11, 1), new ZoomStop(MAX_ZOOM, 10))) ) .circleColor("#fcf9fa") - .minZoom(11) + .minZoom(10) .maxZoom(MAX_ZOOM) ) ); From 58781d940adaf3526e699342344d2d93dd89d6be Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 15 Jan 2024 10:24:50 +0100 Subject: [PATCH 68/86] Rename to StyleBuilder --- .../apis/vectortiles/DebugStyleSpec.java | 41 ++++++----- .../GraphInspectorVectorTileResource.java | 39 +++++++---- .../apis/vectortiles/model/LayerParams.java | 1 - ...yerStyleBuilder.java => StyleBuilder.java} | 68 ++++++++----------- .../apis/vectortiles/model/StyleSpec.java | 7 +- .../apis/vectortiles/model/TileSource.java | 2 +- .../vectortiles/model/VectorSourceLayer.java | 8 +++ .../model/ZoomDependentNumber.java | 31 +++++++++ .../apis/vectortiles/DebugStyleSpecTest.java | 6 +- .../apis/vectortiles/style.json | 65 +++++++++--------- 10 files changed, 153 insertions(+), 115 deletions(-) rename src/main/java/org/opentripplanner/apis/vectortiles/model/{LayerStyleBuilder.java => StyleBuilder.java} (67%) create mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/model/VectorSourceLayer.java create mode 100644 src/main/java/org/opentripplanner/apis/vectortiles/model/ZoomDependentNumber.java diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 05e91d9e6b7..2393125f780 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -1,13 +1,15 @@ package org.opentripplanner.apis.vectortiles; import java.util.List; -import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder; -import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder.ZoomDependentNumber; -import org.opentripplanner.apis.vectortiles.model.LayerStyleBuilder.ZoomStop; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.opentripplanner.apis.vectortiles.model.StyleBuilder; import org.opentripplanner.apis.vectortiles.model.StyleSpec; import org.opentripplanner.apis.vectortiles.model.TileSource; import org.opentripplanner.apis.vectortiles.model.TileSource.RasterSource; -import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; +import org.opentripplanner.apis.vectortiles.model.VectorSourceLayer; +import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber; +import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber.ZoomStop; import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; @@ -27,35 +29,32 @@ */ public class DebugStyleSpec { - private static final RasterSource BACKGROUND_SOURCE = new RasterSource( + private static final TileSource BACKGROUND_SOURCE = new RasterSource( "background", List.of("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"), + 19, 256, "© OpenStreetMap Contributors" ); private static final String MAGENTA = "#f21d52"; - private static final String YELLOW = "#e2d40d"; - private static final String GREY = "#8e8e89"; + private static final String GREEN = "#22DD9E"; private static final int MAX_ZOOM = 23; private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( 1.3f, List.of(new ZoomStop(13, 0.5f), new ZoomStop(MAX_ZOOM, 10)) ); - public record VectorSourceLayer(VectorSource vectorSource, String vectorLayer) {} - - static StyleSpec build( - VectorSource debugSource, - VectorSourceLayer regularStops, - VectorSourceLayer edges - ) { - List sources = List.of(BACKGROUND_SOURCE, debugSource); + static StyleSpec build(VectorSourceLayer regularStops, VectorSourceLayer edges) { + var vectorSources = Stream.of(regularStops, edges).map(VectorSourceLayer::vectorSource); + var allSources = Stream + .concat(Stream.of(BACKGROUND_SOURCE), vectorSources) + .collect(Collectors.toSet()); return new StyleSpec( "OTP Debug Tiles", - sources, + allSources, List.of( - LayerStyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0), - LayerStyleBuilder + StyleBuilder.ofId("background").typeRaster().source(BACKGROUND_SOURCE).minZoom(0), + StyleBuilder .ofId("edge") .typeLine() .vectorSourceLayer(edges) @@ -73,11 +72,11 @@ static StyleSpec build( .minZoom(13) .maxZoom(MAX_ZOOM) .intiallyHidden(), - LayerStyleBuilder + StyleBuilder .ofId("link") .typeLine() .vectorSourceLayer(edges) - .lineColor(YELLOW) + .lineColor(GREEN) .edgeFilter( StreetTransitStopLink.class, StreetTransitEntranceLink.class, @@ -89,7 +88,7 @@ static StyleSpec build( .minZoom(13) .maxZoom(MAX_ZOOM) .intiallyHidden(), - LayerStyleBuilder + StyleBuilder .ofId("regular-stop") .typeCircle() .vectorSourceLayer(regularStops) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index e2008eaea29..b43cd5c109f 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -122,25 +122,36 @@ public TileJson getTileJson( @Produces(MediaType.APPLICATION_JSON) public StyleSpec getTileJson(@Context UriInfo uri, @Context HttpHeaders headers) { var base = HttpUtils.getBaseAddress(uri, headers); - final String allLayers = DEBUG_LAYERS - .stream() - .map(LayerParameters::name) - .collect(Collectors.joining(",")); - var url = - "%s/otp/routers/%s/inspector/vectortile/%s/tilejson.json".formatted( - base, - ignoreRouterId, - allLayers - ); - var vectorSource = new VectorSource("debug", url); + // these two could also be loaded together but are put into separate sources because + // the stops are fast and the edges are relatively slow + var stopsSource = new VectorSource( + "stops", + tileJsonUrl(base, List.of(REGULAR_STOPS, AREA_STOPS)) + ); + var streetSource = new VectorSource( + "street", + tileJsonUrl(base, List.of(EDGES, GEOFENCING_ZONES)) + ); + return DebugStyleSpec.build( - vectorSource, - REGULAR_STOPS.toVectorSourceLayer(vectorSource), - EDGES.toVectorSourceLayer(vectorSource) + REGULAR_STOPS.toVectorSourceLayer(stopsSource), + EDGES.toVectorSourceLayer(streetSource) ); } + private String tileJsonUrl(String base, List> layers) { + final String allLayers = layers + .stream() + .map(LayerParameters::name) + .collect(Collectors.joining(",")); + return "%s/otp/routers/%s/inspector/vectortile/%s/tilejson.json".formatted( + base, + ignoreRouterId, + allLayers + ); + } + @Nonnull private List feedInfos() { return serverContext diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java index 7365e8972da..4ae7691db23 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerParams.java @@ -1,6 +1,5 @@ package org.opentripplanner.apis.vectortiles.model; -import org.opentripplanner.apis.vectortiles.DebugStyleSpec.VectorSourceLayer; import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; import org.opentripplanner.inspector.vector.LayerParameters; diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java similarity index 67% rename from src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java rename to src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index 585e3bce2e1..f60531b461a 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerStyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -8,7 +8,6 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Stream; -import org.opentripplanner.apis.vectortiles.DebugStyleSpec.VectorSourceLayer; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.street.model.edge.Edge; @@ -17,7 +16,7 @@ * Builds a Maplibre/Mapbox vector tile * layer style. */ -public class LayerStyleBuilder { +public class StyleBuilder { private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); private static final String TYPE = "type"; @@ -28,11 +27,11 @@ public class LayerStyleBuilder { private final Map line = new LinkedHashMap<>(); private List filter = List.of(); - public static LayerStyleBuilder ofId(String id) { - return new LayerStyleBuilder(id); + public static StyleBuilder ofId(String id) { + return new StyleBuilder(id); } - public LayerStyleBuilder vectorSourceLayer(VectorSourceLayer source) { + public StyleBuilder vectorSourceLayer(VectorSourceLayer source) { source(source.vectorSource()); return sourceLayer(source.vectorLayer()); } @@ -43,16 +42,16 @@ public enum LayerType { Raster, } - private LayerStyleBuilder(String id) { + private StyleBuilder(String id) { props.put("id", id); } - public LayerStyleBuilder minZoom(int i) { + public StyleBuilder minZoom(int i) { props.put("minzoom", i); return this; } - public LayerStyleBuilder maxZoom(int i) { + public StyleBuilder maxZoom(int i) { props.put("maxzoom", i); return this; } @@ -60,7 +59,7 @@ public LayerStyleBuilder maxZoom(int i) { /** * Which vector tile source this should apply to. */ - public LayerStyleBuilder source(TileSource source) { + public StyleBuilder source(TileSource source) { props.put("source", source.id()); return this; } @@ -70,71 +69,76 @@ public LayerStyleBuilder source(TileSource source) { * There is an unfortunate collision in the name "layer" as it can both refer to a styling layer * and the layer inside the vector tile. */ - public LayerStyleBuilder sourceLayer(String source) { + public StyleBuilder sourceLayer(String source) { props.put(SOURCE_LAYER, source); return this; } - public LayerStyleBuilder typeRaster() { + public StyleBuilder typeRaster() { return type(LayerType.Raster); } - public LayerStyleBuilder typeCircle() { + public StyleBuilder typeCircle() { return type(LayerType.Circle); } - public LayerStyleBuilder typeLine() { + public StyleBuilder typeLine() { type(LayerType.Line); - line.put("line-cap", "round"); + layout.put("line-cap", "round"); return this; } - private LayerStyleBuilder type(LayerType type) { + private StyleBuilder type(LayerType type) { props.put(TYPE, type.name().toLowerCase()); return this; } - public LayerStyleBuilder circleColor(String color) { + public StyleBuilder circleColor(String color) { paint.put("circle-color", validateColor(color)); return this; } - public LayerStyleBuilder circleStroke(String color, int width) { + public StyleBuilder circleStroke(String color, int width) { paint.put("circle-stroke-color", validateColor(color)); paint.put("circle-stroke-width", width); return this; } - public LayerStyleBuilder circleRadius(ZoomDependentNumber radius) { + public StyleBuilder circleRadius(ZoomDependentNumber radius) { paint.put("circle-radius", radius.toJson()); return this; } // Line styling - public LayerStyleBuilder lineColor(String color) { + public StyleBuilder lineColor(String color) { paint.put("line-color", validateColor(color)); return this; } - public LayerStyleBuilder lineWidth(float width) { + public StyleBuilder lineWidth(float width) { paint.put("line-width", width); return this; } - public LayerStyleBuilder lineWidth(ZoomDependentNumber zoomStops) { + public StyleBuilder lineWidth(ZoomDependentNumber zoomStops) { paint.put("line-width", zoomStops.toJson()); return this; } - public LayerStyleBuilder intiallyHidden() { + /** + * Hide this layer when the debug client starts. It can be made visible in the UI later. + */ + public StyleBuilder intiallyHidden() { layout.put("visibility", "none"); return this; } - // filtering edge + /** + * Only apply the style to the given edges. + */ @SafeVarargs - public final LayerStyleBuilder edgeFilter(Class... classToFilter) { + 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; @@ -171,20 +175,4 @@ private void validate() { .of(TYPE) .forEach(p -> Objects.requireNonNull(props.get(p), "%s must be set".formatted(p))); } - - public record ZoomStop(int zoom, float value) { - public List toList() { - return List.of(zoom, value); - } - } - - public record ZoomDependentNumber(float base, List stops) { - public JsonNode toJson() { - var props = new LinkedHashMap<>(); - props.put("base", base); - var vals = stops.stream().map(ZoomStop::toList).toList(); - props.put("stops", vals); - return OBJECT_MAPPER.valueToTree(props); - } - } } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java index 84e19f25364..64f680ed202 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -15,13 +16,13 @@ public final class StyleSpec { private final String name; - private final List sources; + private final Collection sources; private final List layers; - public StyleSpec(String name, List sources, List layers) { + public StyleSpec(String name, Collection sources, List layers) { this.name = name; this.sources = sources; - this.layers = layers.stream().map(LayerStyleBuilder::toJson).toList(); + this.layers = layers.stream().map(StyleBuilder::toJson).toList(); } @JsonSerialize diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java index 06af294a4f0..088ce63d10a 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/TileSource.java @@ -26,7 +26,7 @@ public String type() { * Represents a raster-based source for map tiles. These are used mainly for background * map layers with vector data being rendered on top of it. */ - record RasterSource(String id, List tiles, int tileSize, String attribution) + record RasterSource(String id, List tiles, int maxzoom, int tileSize, String attribution) implements TileSource { @Override public String type() { diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/VectorSourceLayer.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/VectorSourceLayer.java new file mode 100644 index 00000000000..61f1b852d5a --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/VectorSourceLayer.java @@ -0,0 +1,8 @@ +package org.opentripplanner.apis.vectortiles.model; + +/** + * A vector source layer for use in a Maplibre style spec. It contains both the name of the layer + * inside the tile and a reference to the source (which in turn has the URL where to fetch the + * data). + */ +public record VectorSourceLayer(TileSource.VectorSource vectorSource, String vectorLayer) {} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/ZoomDependentNumber.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/ZoomDependentNumber.java new file mode 100644 index 00000000000..d83c4b63495 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/ZoomDependentNumber.java @@ -0,0 +1,31 @@ +package org.opentripplanner.apis.vectortiles.model; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.LinkedHashMap; +import java.util.List; +import org.opentripplanner.framework.json.ObjectMappers; + +/** + * A style parameter that allows you to specify a number that changes dependent on the zoom level. + */ +public record ZoomDependentNumber(float base, List stops) { + private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); + public JsonNode toJson() { + var props = new LinkedHashMap<>(); + props.put("base", base); + var vals = stops.stream().map(ZoomStop::toList).toList(); + props.put("stops", vals); + return OBJECT_MAPPER.valueToTree(props); + } + + /** + * @param zoom The zoom level. + * @param value What the value should be at the specified zoom. + */ + public record ZoomStop(int zoom, float value) { + public List toList() { + return List.of(zoom, value); + } + } +} diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index 9a18da3e5c9..693ddbc2d79 100644 --- a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -3,8 +3,8 @@ import static org.opentripplanner.test.support.JsonAssertions.assertEqualJson; import org.junit.jupiter.api.Test; -import org.opentripplanner.apis.vectortiles.DebugStyleSpec.VectorSourceLayer; import org.opentripplanner.apis.vectortiles.model.TileSource.VectorSource; +import org.opentripplanner.apis.vectortiles.model.VectorSourceLayer; import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.test.support.ResourceLoader; @@ -15,9 +15,9 @@ class DebugStyleSpecTest { @Test void spec() { var vectorSource = new VectorSource("vectorSource", "https://example.com"); - var regularStops = new VectorSourceLayer(vectorSource, "regularStops"); + var stops = new VectorSourceLayer(vectorSource, "stops"); var edges = new VectorSourceLayer(vectorSource, "edges"); - var spec = DebugStyleSpec.build(vectorSource, regularStops, edges); + var spec = DebugStyleSpec.build(stops, edges); var json = ObjectMappers.ignoringExtraFields().valueToTree(spec); var expectation = RESOURCES.fileToString("style.json"); diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index df9f73c3f26..06a602a8cd3 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -1,4 +1,5 @@ { + "name" : "OTP Debug Tiles", "sources" : { "background" : { @@ -6,6 +7,7 @@ "tiles" : [ "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" ], + "maxzoom" : 19, "tileSize" : 256, "attribution" : "© OpenStreetMap Contributors", "type" : "raster" @@ -23,30 +25,6 @@ "source" : "background", "minzoom" : 0 }, - { - "id" : "edge-fallback", - "type" : "line", - "source" : "vectorSource", - "source-layer" : "edges", - "minzoom" : 15, - "maxzoom" : 23, - "paint" : { - "line-color" : "#8e8e89", - "line-width" : { - "base" : 1.3, - "stops" : [ - [ - 13, - 2.0 - ], - [ - 23, - 10.0 - ] - ] - } - } - }, { "id" : "edge", "type" : "line", @@ -61,7 +39,7 @@ "stops" : [ [ 13, - 2.0 + 0.5 ], [ 23, @@ -76,9 +54,15 @@ "StreetEdge", "AreaEdge", "EscalatorEdge", + "PathwayEdge", + "ElevatorHopEdge", "TemporaryPartialStreetEdge", "TemporaryFreeEdge" - ] + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + } }, { "id" : "link", @@ -88,13 +72,13 @@ "minzoom" : 13, "maxzoom" : 23, "paint" : { - "line-color" : "#e2d40d", + "line-color" : "#22DD9E", "line-width" : { "base" : 1.3, "stops" : [ [ 13, - 2.0 + 0.5 ], [ 23, @@ -111,21 +95,38 @@ "BoardingLocationToStopLink", "StreetVehicleRentalLink", "StreetVehicleParkingLink" - ] + ], + "layout" : { + "line-cap" : "round", + "visibility" : "none" + } }, { "id" : "regular-stop", "type" : "circle", "source" : "vectorSource", - "source-layer" : "regularStops", - "minzoom" : 11, + "source-layer" : "stops", + "minzoom" : 10, "maxzoom" : 23, "paint" : { "circle-stroke-color" : "#140d0e", "circle-stroke-width" : 2, + "circle-radius" : { + "base" : 1.0, + "stops" : [ + [ + 11, + 1.0 + ], + [ + 23, + 10.0 + ] + ] + }, "circle-color" : "#fcf9fa" } } ], "version" : 8 -} +} \ No newline at end of file From 760f939fb8a2febb6a5f19c3450444b1cdec5c30 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 15 Jan 2024 11:15:56 +0100 Subject: [PATCH 69/86] Rename class, document --- .../src/components/MapView/LayerControl.tsx | 14 ++++++++++---- client-next/src/components/MapView/MapView.tsx | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/client-next/src/components/MapView/LayerControl.tsx b/client-next/src/components/MapView/LayerControl.tsx index 4566c3679fc..237ea8ab150 100644 --- a/client-next/src/components/MapView/LayerControl.tsx +++ b/client-next/src/components/MapView/LayerControl.tsx @@ -1,12 +1,18 @@ import type { ControlPosition } from 'react-map-gl'; import { useControl } from 'react-map-gl'; -import { IControl, Map} from 'maplibre-gl'; +import { IControl, Map } from 'maplibre-gl'; type LayerControlProps = { position: ControlPosition; }; -class LayerList implements IControl { +/** + * A maplibre control that allows you to switch vector tile layers on and off. + * + * It appears that you cannot use React elements but have to drop down to raw DOM. Please correct + * me if I'm wrong. + */ +class LayerControl implements IControl { private readonly container: HTMLDivElement = document.createElement('div'); onAdd(map: Map) { @@ -67,8 +73,8 @@ class LayerList implements IControl { } } -export default function LayerListControl(props: LayerControlProps) { - useControl(() => new LayerList(), { +export default function DebugLayerControl(props: LayerControlProps) { + useControl(() => new LayerControl(), { position: props.position, }); diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index a3c925a2519..930c9ff9a58 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -7,7 +7,7 @@ import { useMapDoubleClick } from './useMapDoubleClick.ts'; import { useState } from 'react'; import { ContextMenuPopup } from './ContextMenuPopup.tsx'; import { GeometryPropertyPopup } from './GeometryPropertyPopup.tsx'; -import LayerListControl from './LayerControl.tsx'; +import DebugLayerControl from './LayerControl.tsx'; // TODO: this should be configurable const initialViewState = { @@ -76,7 +76,7 @@ export function MapView({ setTripQueryVariables={setTripQueryVariables} loading={loading} /> - + {tripQueryResult?.trip.tripPatterns.length && ( )} From 92a4b0c4f7f47cef3c3e5d84f2c6906a85270b34 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 15 Jan 2024 11:27:51 +0100 Subject: [PATCH 70/86] Automatically pan to correct world envelope --- .../src/components/MapView/MapView.tsx | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 930c9ff9a58..074c699ad0c 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -1,4 +1,12 @@ -import { LngLat, Map, MapGeoJSONFeature, MapMouseEvent, NavigationControl } from 'react-map-gl/maplibre'; +import { + LngLat, + Map, + MapEvent, + MapGeoJSONFeature, + MapMouseEvent, + NavigationControl, + VectorTileSource, +} from 'react-map-gl/maplibre'; import 'maplibre-gl/dist/maplibre-gl.css'; import { TripPattern, TripQuery, TripQueryVariables } from '../../gql/graphql.ts'; import { NavigationMarkers } from './NavigationMarkers.tsx'; @@ -9,13 +17,6 @@ import { ContextMenuPopup } from './ContextMenuPopup.tsx'; import { GeometryPropertyPopup } from './GeometryPropertyPopup.tsx'; import DebugLayerControl from './LayerControl.tsx'; -// TODO: this should be configurable -const initialViewState = { - latitude: 60.7554885, - longitude: 10.2332855, - zoom: 4, -}; - const styleUrl = import.meta.env.VITE_DEBUG_STYLE_URL; type PopupData = { coordinates: LngLat; feature: MapGeoJSONFeature }; @@ -49,6 +50,17 @@ export function MapView({ setShowPropsPopup({ coordinates: e.lngLat, feature: feature }); } }; + const panToWorldEnvelopeIfRequired = (e: MapEvent) => { + const map = e.target; + // if we are really far zoomed out and show the entire world it means that we are not starting + // in a location selected from the URL hash. + // in such a case we pan to the area that is specified in the stop's tile bounds, which is + // provided by the WorldEnvelopeService + if (map.getZoom() < 2) { + const source = map.getSource('stops') as VectorTileSource; + map.fitBounds(source.bounds, { maxDuration: 50, linear: true }); + } + }; return (
      @@ -57,7 +69,6 @@ export function MapView({ mapLib={import('maplibre-gl')} // @ts-ignore mapStyle={styleUrl} - initialViewState={initialViewState} onDblClick={onMapDoubleClick} onContextMenu={(e) => { setShowContextPopup(e.lngLat); @@ -69,6 +80,7 @@ export function MapView({ // disable pitching and rotating the map touchPitch={false} dragRotate={false} + onLoad={panToWorldEnvelopeIfRequired} > Date: Mon, 15 Jan 2024 11:38:05 +0100 Subject: [PATCH 71/86] Last polishing --- client-next/src/components/MapView/MapView.tsx | 4 +++- .../apis/vectortiles/GraphInspectorVectorTileResource.java | 2 +- .../inspector/vector/{edges => edge}/EdgeLayerBuilder.java | 5 ++++- .../inspector/vector/{edges => edge}/EdgePropertyMapper.java | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) rename src/main/java/org/opentripplanner/inspector/vector/{edges => edge}/EdgeLayerBuilder.java (90%) rename src/main/java/org/opentripplanner/inspector/vector/{edges => edge}/EdgePropertyMapper.java (95%) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 074c699ad0c..fa0bfbd022b 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -54,7 +54,7 @@ export function MapView({ const map = e.target; // if we are really far zoomed out and show the entire world it means that we are not starting // in a location selected from the URL hash. - // in such a case we pan to the area that is specified in the stop's tile bounds, which is + // in such a case we pan to the area that is specified in the tile bounds, which is // provided by the WorldEnvelopeService if (map.getZoom() < 2) { const source = map.getSource('stops') as VectorTileSource; @@ -73,6 +73,8 @@ export function MapView({ onContextMenu={(e) => { setShowContextPopup(e.lngLat); }} + // 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', 'edge-fallback', 'edge', 'link']} onClick={showFeaturePropPopup} // put lat/long in URL and pan to it on page reload diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index b43cd5c109f..c8cd3a089f3 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -31,7 +31,7 @@ import org.opentripplanner.inspector.vector.LayerBuilder; import org.opentripplanner.inspector.vector.LayerParameters; import org.opentripplanner.inspector.vector.VectorTileResponseFactory; -import org.opentripplanner.inspector.vector.edges.EdgeLayerBuilder; +import org.opentripplanner.inspector.vector.edge.EdgeLayerBuilder; import org.opentripplanner.inspector.vector.geofencing.GeofencingZonesLayerBuilder; import org.opentripplanner.inspector.vector.stop.StopLayerBuilder; import org.opentripplanner.model.FeedInfo; diff --git a/src/main/java/org/opentripplanner/inspector/vector/edges/EdgeLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java similarity index 90% rename from src/main/java/org/opentripplanner/inspector/vector/edges/EdgeLayerBuilder.java rename to src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java index 60e65f7b35a..3823f91f039 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/edges/EdgeLayerBuilder.java +++ b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgeLayerBuilder.java @@ -1,4 +1,4 @@ -package org.opentripplanner.inspector.vector.edges; +package org.opentripplanner.inspector.vector.edge; import java.util.List; import org.locationtech.jts.geom.Envelope; @@ -9,6 +9,9 @@ import org.opentripplanner.routing.graph.index.StreetIndex; import org.opentripplanner.street.model.edge.Edge; +/** + * Selects all edges to be displayed for debugging. + */ public class EdgeLayerBuilder extends LayerBuilder { private final StreetIndex streetIndex; diff --git a/src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java similarity index 95% rename from src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java rename to src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java index 5bfc2dc6df7..fb65d0b5d3b 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/edges/EdgePropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -1,4 +1,4 @@ -package org.opentripplanner.inspector.vector.edges; +package org.opentripplanner.inspector.vector.edge; import static org.opentripplanner.framework.lang.DoubleUtils.roundTo2Decimals; import static org.opentripplanner.inspector.vector.KeyValue.kv; From 61ab71e1bffb7c68038a01e242bfd6965ffbf9ad Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 15 Jan 2024 17:35:17 +0100 Subject: [PATCH 72/86] Add vertices layer builder --- .../GraphInspectorVectorTileResource.java | 3 ++ .../apis/vectortiles/model/LayerType.java | 1 + .../vector/vertex/VertexLayerBuilder.java | 37 +++++++++++++++++++ .../vector/vertex/VertexPropertyMapper.java | 29 +++++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 src/main/java/org/opentripplanner/inspector/vector/vertex/VertexLayerBuilder.java create mode 100644 src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index c8cd3a089f3..6870f523fcf 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -34,6 +34,7 @@ import org.opentripplanner.inspector.vector.edge.EdgeLayerBuilder; import org.opentripplanner.inspector.vector.geofencing.GeofencingZonesLayerBuilder; import org.opentripplanner.inspector.vector.stop.StopLayerBuilder; +import org.opentripplanner.inspector.vector.vertex.VertexLayerBuilder; import org.opentripplanner.model.FeedInfo; import org.opentripplanner.standalone.api.OtpServerRequestContext; @@ -51,6 +52,7 @@ public class GraphInspectorVectorTileResource { GeofencingZones ); private static final LayerParams EDGES = new LayerParams("edges", Edges); + private static final LayerParams VERTICES = new LayerParams("vertices", Edges); private static final List> DEBUG_LAYERS = List.of( REGULAR_STOPS, AREA_STOPS, @@ -181,6 +183,7 @@ private static LayerBuilder createLayerBuilder( ); case GeofencingZones -> new GeofencingZonesLayerBuilder(context.graph(), layerParameters); case Edges -> new EdgeLayerBuilder(context.graph(), layerParameters); + case Vertices -> new VertexLayerBuilder(context.graph(), layerParameters); }; } } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java index 4324a511a6b..4d73b0a14bd 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java @@ -5,4 +5,5 @@ public enum LayerType { AreaStop, GeofencingZones, Edges, + Vertices } diff --git a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexLayerBuilder.java new file mode 100644 index 00000000000..e7aadb6be9e --- /dev/null +++ b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexLayerBuilder.java @@ -0,0 +1,37 @@ +package org.opentripplanner.inspector.vector.vertex; + +import java.util.List; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.opentripplanner.inspector.vector.LayerBuilder; +import org.opentripplanner.inspector.vector.LayerParameters; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.routing.graph.index.StreetIndex; +import org.opentripplanner.street.model.vertex.Vertex; + +/** + * Selects all vertices to be displayed for debugging. + */ +public class VertexLayerBuilder extends LayerBuilder { + + private final StreetIndex streetIndex; + + public VertexLayerBuilder(Graph graph, LayerParameters layerParameters) { + super(new VertexPropertyMapper(), layerParameters.name(), layerParameters.expansionFactor()); + this.streetIndex = graph.getStreetIndex(); + } + + @Override + protected List getGeometries(Envelope query) { + return streetIndex + .getEdgesForEnvelope(query) + .stream() + .filter(e -> e.getGeometry() != null) + .map(edge -> { + Geometry geometry = edge.getGeometry(); + geometry.setUserData(edge); + return geometry; + }) + .toList(); + } +} diff --git a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java new file mode 100644 index 00000000000..b2deda9647c --- /dev/null +++ b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java @@ -0,0 +1,29 @@ +package org.opentripplanner.inspector.vector.vertex; + +import static org.opentripplanner.inspector.vector.KeyValue.kv; + +import java.util.Collection; +import java.util.List; +import org.opentripplanner.apis.support.mapping.PropertyMapper; +import org.opentripplanner.framework.collection.ListUtils; +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.Vertex; + +public class VertexPropertyMapper extends PropertyMapper { + + @Override + protected Collection map(Vertex input) { + List baseProps = List.of(kv("class", input.getClass().getSimpleName())); + List properties = + switch (input) { + case BarrierVertex v -> List.of( + kv("permission", v.getBarrierPermissions().toString()) + ); + case VehicleRentalPlaceVertex v -> List.of(kv("rentalId", v.getStation().getId())); + default -> List.of(); + }; + return ListUtils.combine(baseProps, properties); + } +} From c9bbc7d75b26eae9b2c537741d83c418e9abe4a1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jan 2024 07:20:07 +0100 Subject: [PATCH 73/86] Add vertices to debug tiles --- .../src/components/MapView/MapView.tsx | 4 +-- .../apis/vectortiles/DebugStyleSpec.java | 25 ++++++++++++++++--- .../GraphInspectorVectorTileResource.java | 19 ++++++++------ .../apis/vectortiles/model/LayerType.java | 4 +-- .../vector/vertex/VertexLayerBuilder.java | 10 ++++---- .../vector/vertex/VertexPropertyMapper.java | 9 ++++--- .../apis/vectortiles/DebugStyleSpecTest.java | 2 +- 7 files changed, 48 insertions(+), 25 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index fa0bfbd022b..11aa41476d3 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -58,7 +58,7 @@ export function MapView({ // provided by the WorldEnvelopeService if (map.getZoom() < 2) { const source = map.getSource('stops') as VectorTileSource; - map.fitBounds(source.bounds, { maxDuration: 50, linear: true }); + map.fitBounds(source.bounds, { maxDuration: 50, linear: true}); } }; @@ -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', 'edge-fallback', 'edge', 'link']} + interactiveLayerIds={['regular-stop', 'vertex', 'edge', 'link']} onClick={showFeaturePropPopup} // put lat/long in URL and pan to it on page reload hash={true} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 2393125f780..fd3254bdb05 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -38,14 +38,22 @@ public class DebugStyleSpec { ); private static final String MAGENTA = "#f21d52"; private static final String GREEN = "#22DD9E"; + private static final String PURPLE = "#BC55F2"; private static final int MAX_ZOOM = 23; private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( 1.3f, List.of(new ZoomStop(13, 0.5f), new ZoomStop(MAX_ZOOM, 10)) ); + private static final String BLACK = "#140d0e"; - static StyleSpec build(VectorSourceLayer regularStops, VectorSourceLayer edges) { - var vectorSources = Stream.of(regularStops, edges).map(VectorSourceLayer::vectorSource); + static StyleSpec build( + VectorSourceLayer regularStops, + VectorSourceLayer edges, + VectorSourceLayer vertices + ) { + var vectorSources = Stream + .of(regularStops, edges, vertices) + .map(VectorSourceLayer::vectorSource); var allSources = Stream .concat(Stream.of(BACKGROUND_SOURCE), vectorSources) .collect(Collectors.toSet()); @@ -88,11 +96,22 @@ static StyleSpec build(VectorSourceLayer regularStops, VectorSourceLayer edges) .minZoom(13) .maxZoom(MAX_ZOOM) .intiallyHidden(), + StyleBuilder + .ofId("vertex") + .typeCircle() + .vectorSourceLayer(vertices) + .circleStroke(BLACK, 2) + .circleRadius( + new ZoomDependentNumber(1, List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 5))) + ) + .circleColor(PURPLE) + .minZoom(15) + .maxZoom(MAX_ZOOM), StyleBuilder .ofId("regular-stop") .typeCircle() .vectorSourceLayer(regularStops) - .circleStroke("#140d0e", 2) + .circleStroke(BLACK, 2) .circleRadius( new ZoomDependentNumber(1, List.of(new ZoomStop(11, 1), new ZoomStop(MAX_ZOOM, 10))) ) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index 6870f523fcf..5d0778eae6d 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -1,8 +1,9 @@ package org.opentripplanner.apis.vectortiles; -import static org.opentripplanner.apis.vectortiles.model.LayerType.Edges; +import static org.opentripplanner.apis.vectortiles.model.LayerType.Edge; import static org.opentripplanner.apis.vectortiles.model.LayerType.GeofencingZones; import static org.opentripplanner.apis.vectortiles.model.LayerType.RegularStop; +import static org.opentripplanner.apis.vectortiles.model.LayerType.Vertex; import static org.opentripplanner.framework.io.HttpUtils.APPLICATION_X_PROTOBUF; import jakarta.ws.rs.GET; @@ -51,13 +52,14 @@ public class GraphInspectorVectorTileResource { "geofencingZones", GeofencingZones ); - private static final LayerParams EDGES = new LayerParams("edges", Edges); - private static final LayerParams VERTICES = new LayerParams("vertices", Edges); + private static final LayerParams EDGES = new LayerParams("edges", Edge); + private static final LayerParams VERTICES = new LayerParams("vertices", Vertex); private static final List> DEBUG_LAYERS = List.of( REGULAR_STOPS, AREA_STOPS, GEOFENCING_ZONES, - EDGES + EDGES, + VERTICES ); private final OtpServerRequestContext serverContext; @@ -133,12 +135,13 @@ public StyleSpec getTileJson(@Context UriInfo uri, @Context HttpHeaders headers) ); var streetSource = new VectorSource( "street", - tileJsonUrl(base, List.of(EDGES, GEOFENCING_ZONES)) + tileJsonUrl(base, List.of(EDGES, GEOFENCING_ZONES, VERTICES)) ); return DebugStyleSpec.build( REGULAR_STOPS.toVectorSourceLayer(stopsSource), - EDGES.toVectorSourceLayer(streetSource) + EDGES.toVectorSourceLayer(streetSource), + VERTICES.toVectorSourceLayer(streetSource) ); } @@ -182,8 +185,8 @@ private static LayerBuilder createLayerBuilder( e -> context.transitService().findAreaStops(e) ); case GeofencingZones -> new GeofencingZonesLayerBuilder(context.graph(), layerParameters); - case Edges -> new EdgeLayerBuilder(context.graph(), layerParameters); - case Vertices -> new VertexLayerBuilder(context.graph(), layerParameters); + case Edge -> new EdgeLayerBuilder(context.graph(), layerParameters); + case Vertex -> new VertexLayerBuilder(context.graph(), layerParameters); }; } } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java index 4d73b0a14bd..ece75f29b60 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/LayerType.java @@ -4,6 +4,6 @@ public enum LayerType { RegularStop, AreaStop, GeofencingZones, - Edges, - Vertices + Edge, + Vertex, } diff --git a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexLayerBuilder.java b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexLayerBuilder.java index e7aadb6be9e..0b3bb79b72e 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexLayerBuilder.java +++ b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexLayerBuilder.java @@ -3,6 +3,7 @@ import java.util.List; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; +import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.inspector.vector.LayerBuilder; import org.opentripplanner.inspector.vector.LayerParameters; import org.opentripplanner.routing.graph.Graph; @@ -24,12 +25,11 @@ public VertexLayerBuilder(Graph graph, LayerParameters layerParameters) { @Override protected List getGeometries(Envelope query) { return streetIndex - .getEdgesForEnvelope(query) + .getVerticesForEnvelope(query) .stream() - .filter(e -> e.getGeometry() != null) - .map(edge -> { - Geometry geometry = edge.getGeometry(); - geometry.setUserData(edge); + .map(vertex -> { + Geometry geometry = GeometryUtils.getGeometryFactory().createPoint(vertex.getCoordinate()); + geometry.setUserData(vertex); return geometry; }) .toList(); 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 b2deda9647c..a493269cc3b 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/vertex/VertexPropertyMapper.java @@ -15,12 +15,13 @@ public class VertexPropertyMapper extends PropertyMapper { @Override protected Collection map(Vertex input) { - List baseProps = List.of(kv("class", input.getClass().getSimpleName())); + List baseProps = List.of( + kv("class", input.getClass().getSimpleName()), + kv("label", input.getLabel().toString()) + ); List properties = switch (input) { - case BarrierVertex v -> List.of( - kv("permission", v.getBarrierPermissions().toString()) - ); + case BarrierVertex v -> List.of(kv("permission", v.getBarrierPermissions().toString())); case VehicleRentalPlaceVertex v -> List.of(kv("rentalId", v.getStation().getId())); default -> List.of(); }; diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index 693ddbc2d79..af7598285e1 100644 --- a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -17,7 +17,7 @@ void spec() { var vectorSource = new VectorSource("vectorSource", "https://example.com"); var stops = new VectorSourceLayer(vectorSource, "stops"); var edges = new VectorSourceLayer(vectorSource, "edges"); - var spec = DebugStyleSpec.build(stops, edges); + var spec = DebugStyleSpec.build(stops, edges, VERTICES.toVectorSourceLayer(streetSource)); var json = ObjectMappers.ignoringExtraFields().valueToTree(spec); var expectation = RESOURCES.fileToString("style.json"); From c6306b8bd0eb50f9e4d02a5a75159c9d99d07f45 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jan 2024 11:24:05 +0100 Subject: [PATCH 74/86] Set 2.0 as the version the MQTT updater was added --- docs/UpdaterConfig.md | 10 +++++----- .../updaters/MqttGtfsRealtimeUpdaterConfig.java | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/UpdaterConfig.md b/docs/UpdaterConfig.md index 212a2d55370..a819a898240 100644 --- a/docs/UpdaterConfig.md +++ b/docs/UpdaterConfig.md @@ -179,11 +179,11 @@ This system powers the realtime updates in Helsinki and more information can be |-----------------------------------------------------------------------|:---------:|----------------------------------------------|:----------:|----------------------|:-----:| | type = "mqtt-gtfs-rt-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | | [backwardsDelayPropagationType](#u__6__backwardsDelayPropagationType) | `enum` | How backwards propagation should be handled. | *Optional* | `"required-no-data"` | 2.2 | -| feedId | `string` | The feed id to apply the updates to. | *Required* | | na | -| fuzzyTripMatching | `boolean` | Whether to match trips fuzzily. | *Optional* | `false` | na | -| qos | `integer` | QOS level. | *Optional* | `0` | na | -| topic | `string` | The topic to subscribe to. | *Required* | | na | -| url | `string` | URL of the MQTT broker. | *Required* | | na | +| feedId | `string` | The feed id to apply the updates to. | *Required* | | 2.0 | +| fuzzyTripMatching | `boolean` | Whether to match trips fuzzily. | *Optional* | `false` | 2.0 | +| qos | `integer` | QOS level. | *Optional* | `0` | 2.0 | +| topic | `string` | The topic to subscribe to. | *Required* | | 2.0 | +| url | `string` | URL of the MQTT broker. | *Required* | | 2.0 | ##### Parameter details diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/MqttGtfsRealtimeUpdaterConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/MqttGtfsRealtimeUpdaterConfig.java index 372bd36227e..ccc4ca5e80f 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/MqttGtfsRealtimeUpdaterConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/MqttGtfsRealtimeUpdaterConfig.java @@ -1,6 +1,6 @@ package org.opentripplanner.standalone.config.routerconfig.updaters; -import static org.opentripplanner.standalone.config.framework.json.OtpVersion.NA; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_0; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; @@ -12,13 +12,13 @@ public class MqttGtfsRealtimeUpdaterConfig { public static MqttGtfsRealtimeUpdaterParameters create(String configRef, NodeAdapter c) { return new MqttGtfsRealtimeUpdaterParameters( configRef, - c.of("feedId").since(NA).summary("The feed id to apply the updates to.").asString(), - c.of("url").since(NA).summary("URL of the MQTT broker.").asString(), - c.of("topic").since(NA).summary("The topic to subscribe to.").asString(), - c.of("qos").since(NA).summary("QOS level.").asInt(0), + c.of("feedId").since(V2_0).summary("The feed id to apply the updates to.").asString(), + c.of("url").since(V2_0).summary("URL of the MQTT broker.").asString(), + c.of("topic").since(V2_0).summary("The topic to subscribe to.").asString(), + c.of("qos").since(V2_0).summary("QOS level.").asInt(0), c .of("fuzzyTripMatching") - .since(NA) + .since(V2_0) .summary("Whether to match trips fuzzily.") .asBoolean(false), c From cec3655f5e55b2b2666fe554b407f6322c52f15d Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jan 2024 13:02:03 +0100 Subject: [PATCH 75/86] Add comment to visualizer [ci skip] --- .../java/org/opentripplanner/visualizer/GraphVisualizer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/opentripplanner/visualizer/GraphVisualizer.java b/src/main/java/org/opentripplanner/visualizer/GraphVisualizer.java index 73516b0348f..9f8bbffb22d 100644 --- a/src/main/java/org/opentripplanner/visualizer/GraphVisualizer.java +++ b/src/main/java/org/opentripplanner/visualizer/GraphVisualizer.java @@ -157,6 +157,10 @@ public DisplayVertex getElementAt(int index) { * TransitStops only, and allows a user to select stops, examine incoming and outgoing edges, and * examine trip patterns. It's meant mainly for debugging, so it's totally OK if it develops (say) a * bunch of weird buttons designed to debug specific cases. + *

      + * 2024-01-26: We talked about the visualizer in the developer meeting and while the code is a bit + * dusty, we decided that we want to keep the option open to build make the visualization of routing + * steps work again in the future and won't delete it. */ public class GraphVisualizer extends JFrame implements VertexSelectionListener { From f1a9be02e20373fa33776e9d38d3478ede8f6932 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jan 2024 13:17:30 +0100 Subject: [PATCH 76/86] Update vertex styles --- .../src/components/MapView/MapView.tsx | 2 +- .../apis/vectortiles/DebugStyleSpec.java | 13 ++++-- .../apis/vectortiles/model/StyleBuilder.java | 6 +++ .../apis/vectortiles/DebugStyleSpecTest.java | 3 +- .../apis/vectortiles/style.json | 44 ++++++++++++++++++- 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 11aa41476d3..51cb39068f8 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -58,7 +58,7 @@ export function MapView({ // provided by the WorldEnvelopeService if (map.getZoom() < 2) { const source = map.getSource('stops') as VectorTileSource; - map.fitBounds(source.bounds, { maxDuration: 50, linear: true}); + map.fitBounds(source.bounds, { maxDuration: 50, linear: true }); } }; diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index fd3254bdb05..f7ca564b149 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -39,12 +39,16 @@ public class DebugStyleSpec { private static final String MAGENTA = "#f21d52"; private static final String GREEN = "#22DD9E"; private static final String PURPLE = "#BC55F2"; + private static final String BLACK = "#140d0e"; private static final int MAX_ZOOM = 23; private static final ZoomDependentNumber LINE_WIDTH = new ZoomDependentNumber( 1.3f, List.of(new ZoomStop(13, 0.5f), new ZoomStop(MAX_ZOOM, 10)) ); - private static final String BLACK = "#140d0e"; + private static final ZoomDependentNumber CIRCLE_STROKE = new ZoomDependentNumber( + 1, + List.of(new ZoomStop(15, 0.2f), new ZoomStop(MAX_ZOOM, 3)) + ); static StyleSpec build( VectorSourceLayer regularStops, @@ -100,13 +104,14 @@ static StyleSpec build( .ofId("vertex") .typeCircle() .vectorSourceLayer(vertices) - .circleStroke(BLACK, 2) + .circleStroke(BLACK, CIRCLE_STROKE) .circleRadius( - new ZoomDependentNumber(1, List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 5))) + new ZoomDependentNumber(1, List.of(new ZoomStop(15, 1), new ZoomStop(MAX_ZOOM, 7))) ) .circleColor(PURPLE) .minZoom(15) - .maxZoom(MAX_ZOOM), + .maxZoom(MAX_ZOOM) + .intiallyHidden(), StyleBuilder .ofId("regular-stop") .typeCircle() 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 f60531b461a..3b2f36d3156 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -104,6 +104,12 @@ public StyleBuilder circleStroke(String color, int width) { return this; } + public StyleBuilder circleStroke(String color, ZoomDependentNumber width) { + paint.put("circle-stroke-color", validateColor(color)); + paint.put("circle-stroke-width", width.toJson()); + return this; + } + public StyleBuilder circleRadius(ZoomDependentNumber radius) { paint.put("circle-radius", radius.toJson()); return this; diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index af7598285e1..befd34a3b38 100644 --- a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -17,7 +17,8 @@ void spec() { var vectorSource = new VectorSource("vectorSource", "https://example.com"); var stops = new VectorSourceLayer(vectorSource, "stops"); var edges = new VectorSourceLayer(vectorSource, "edges"); - var spec = DebugStyleSpec.build(stops, edges, VERTICES.toVectorSourceLayer(streetSource)); + var vertices = new VectorSourceLayer(vectorSource, "vertices"); + var spec = DebugStyleSpec.build(stops, edges, vertices); var json = ObjectMappers.ignoringExtraFields().valueToTree(spec); var expectation = RESOURCES.fileToString("style.json"); diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 06a602a8cd3..9555f32c1e5 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -1,5 +1,4 @@ { - "name" : "OTP Debug Tiles", "sources" : { "background" : { @@ -101,6 +100,47 @@ "visibility" : "none" } }, + { + "id" : "vertex", + "type" : "circle", + "source" : "vectorSource", + "source-layer" : "vertices", + "minzoom" : 15, + "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" : [ + [ + 15, + 1.0 + ], + [ + 23, + 7.0 + ] + ] + }, + "circle-color" : "#BC55F2" + }, + "layout" : { + "visibility" : "none" + } + }, { "id" : "regular-stop", "type" : "circle", @@ -129,4 +169,4 @@ } ], "version" : 8 -} \ No newline at end of file +} From 9e806f7957f7c487d6647b80fd11166cbdbff80f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jan 2024 14:41:09 +0100 Subject: [PATCH 77/86] Move fallback class into framework.time package --- .../opentripplanner/ext/restapi/resources/RoutingResource.java | 2 +- .../opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java | 2 +- .../{api/support => framework/time}/ZoneIdFallback.java | 2 +- .../routing/algorithm/mapping/GraphPathToItineraryMapper.java | 2 +- .../opentripplanner/routing/service/DefaultRoutingService.java | 2 +- .../{api/support => framework/time}/ZoneIdFallbackTest.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename src/main/java/org/opentripplanner/{api/support => framework/time}/ZoneIdFallback.java (96%) rename src/test/java/org/opentripplanner/{api/support => framework/time}/ZoneIdFallbackTest.java (89%) diff --git a/src/ext/java/org/opentripplanner/ext/restapi/resources/RoutingResource.java b/src/ext/java/org/opentripplanner/ext/restapi/resources/RoutingResource.java index ae63a95feaa..bfb3d6ed15a 100644 --- a/src/ext/java/org/opentripplanner/ext/restapi/resources/RoutingResource.java +++ b/src/ext/java/org/opentripplanner/ext/restapi/resources/RoutingResource.java @@ -19,11 +19,11 @@ import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import org.opentripplanner.api.parameter.QualifiedModeSet; -import org.opentripplanner.api.support.ZoneIdFallback; import org.opentripplanner.ext.dataoverlay.api.DataOverlayParameters; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.lang.StringUtils; import org.opentripplanner.framework.time.DurationUtils; +import org.opentripplanner.framework.time.ZoneIdFallback; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.preference.ItineraryFilterDebugProfile; import org.opentripplanner.routing.api.request.request.filter.SelectRequest; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java index e4094aaa888..2eeaf229b11 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RouteRequestMapper.java @@ -15,9 +15,9 @@ import org.opentripplanner.api.common.LocationStringParser; import org.opentripplanner.api.parameter.QualifiedMode; import org.opentripplanner.api.parameter.QualifiedModeSet; -import org.opentripplanner.api.support.ZoneIdFallback; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.framework.graphql.GraphQLUtils; +import org.opentripplanner.framework.time.ZoneIdFallback; import org.opentripplanner.model.GenericLocation; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; diff --git a/src/main/java/org/opentripplanner/api/support/ZoneIdFallback.java b/src/main/java/org/opentripplanner/framework/time/ZoneIdFallback.java similarity index 96% rename from src/main/java/org/opentripplanner/api/support/ZoneIdFallback.java rename to src/main/java/org/opentripplanner/framework/time/ZoneIdFallback.java index 0175337e2d4..2d43216e4b2 100644 --- a/src/main/java/org/opentripplanner/api/support/ZoneIdFallback.java +++ b/src/main/java/org/opentripplanner/framework/time/ZoneIdFallback.java @@ -1,4 +1,4 @@ -package org.opentripplanner.api.support; +package org.opentripplanner.framework.time; import java.time.ZoneId; import javax.annotation.Nullable; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java index ee86f8de720..11302098f25 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/GraphPathToItineraryMapper.java @@ -12,13 +12,13 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.impl.PackedCoordinateSequence; -import org.opentripplanner.api.support.ZoneIdFallback; import org.opentripplanner.astar.model.GraphPath; import org.opentripplanner.ext.flex.FlexibleTransitLeg; import org.opentripplanner.ext.flex.edgetype.FlexTripEdge; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.framework.time.ZoneIdFallback; import org.opentripplanner.model.plan.ElevationProfile; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; diff --git a/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java b/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java index dd5d5615be8..01736aac80e 100644 --- a/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java +++ b/src/main/java/org/opentripplanner/routing/service/DefaultRoutingService.java @@ -1,8 +1,8 @@ package org.opentripplanner.routing.service; import java.time.ZoneId; -import org.opentripplanner.api.support.ZoneIdFallback; import org.opentripplanner.framework.application.OTPRequestTimeoutException; +import org.opentripplanner.framework.time.ZoneIdFallback; import org.opentripplanner.framework.tostring.MultiLineToStringBuilder; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.routing.algorithm.RoutingWorker; diff --git a/src/test/java/org/opentripplanner/api/support/ZoneIdFallbackTest.java b/src/test/java/org/opentripplanner/framework/time/ZoneIdFallbackTest.java similarity index 89% rename from src/test/java/org/opentripplanner/api/support/ZoneIdFallbackTest.java rename to src/test/java/org/opentripplanner/framework/time/ZoneIdFallbackTest.java index 02013f0856b..b69e3ee2ff1 100644 --- a/src/test/java/org/opentripplanner/api/support/ZoneIdFallbackTest.java +++ b/src/test/java/org/opentripplanner/framework/time/ZoneIdFallbackTest.java @@ -1,4 +1,4 @@ -package org.opentripplanner.api.support; +package org.opentripplanner.framework.time; import static org.junit.jupiter.api.Assertions.assertEquals; From 83dd2282948c083ddd64653b617ed3fd0d8cb14b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jan 2024 14:46:55 +0100 Subject: [PATCH 78/86] Allow dependency on logging package --- .../opentripplanner/framework/FrameworkArchitectureTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/opentripplanner/framework/FrameworkArchitectureTest.java b/src/test/java/org/opentripplanner/framework/FrameworkArchitectureTest.java index e1e00076a73..9e44dd05dda 100644 --- a/src/test/java/org/opentripplanner/framework/FrameworkArchitectureTest.java +++ b/src/test/java/org/opentripplanner/framework/FrameworkArchitectureTest.java @@ -97,7 +97,7 @@ void enforceTextPackageDependencies() { @Test void enforceTimePackageDependencies() { - TIME.verify(); + TIME.dependsOn(LOGGING).verify(); } @Test From 216bea8239631ef9c99e72b7180f7c50a55b8127 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jan 2024 17:21:50 +0100 Subject: [PATCH 79/86] Change path of GTFS API to /otp/gtfs/v1 --- docs/apis/GTFS-GraphQL-API.md | 4 +- magidoc.mjs | 2 +- src/client/graphiql/index.html | 2 +- .../opentripplanner/apis/APIEndpoints.java | 2 + .../apis/gtfs/GtfsGraphQLAPI.java | 94 ++++--------------- .../graphql/GraphQLResponseSerializer.java | 26 ----- 6 files changed, 23 insertions(+), 107 deletions(-) diff --git a/docs/apis/GTFS-GraphQL-API.md b/docs/apis/GTFS-GraphQL-API.md index a9937afc6b2..b67b2882ec9 100644 --- a/docs/apis/GTFS-GraphQL-API.md +++ b/docs/apis/GTFS-GraphQL-API.md @@ -6,7 +6,7 @@ used heavily by [digitransit-ui](https://github.com/HSLdevcom/digitransit-ui). [otp-react-redux](https://github.com/opentripplanner/otp-react-redux) has also migrated to this API in 2023. ## URLs - - GraphQL endpoint: [`http://localhost:8080/otp/routers/default/index/graphql`](http://localhost:8080/otp/routers/default/index/graphql) + - GraphQL endpoint: [`http://localhost:8080/otp/gtfs/v1`](http://localhost:8080/otp/gtfs/v1) - HTML schema documentation: [https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/](https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/) - Built-in visual GraphQL client: [http://localhost:8080/graphiql](http://localhost:8080/graphiql) @@ -22,7 +22,7 @@ A complete example that fetches the list of all stops from OTP is: ``` curl --request POST \ - --url http://localhost:8080/otp/routers/default/index/graphql \ + --url http://localhost:8080/otp/gtfs/v1 \ --header 'Content-Type: application/json' \ --header 'OTPTimeout: 180000' \ --data '{"query":"query stops {\n stops {\n gtfsId\n name\n }\n}\n","operationName":"stops"}' diff --git a/magidoc.mjs b/magidoc.mjs index 44997e3fc9d..a02976f4bcc 100644 --- a/magidoc.mjs +++ b/magidoc.mjs @@ -17,7 +17,7 @@ export default { This is the static documentation of the OTP GraphQL GTFS API. The GraphQL endpoint of your instance, which you should point your tooling to, is -\`http://localhost:8080/otp/routers/default/index/graphql\` +\`http://localhost:8080/otp/gtfs/v1\` Please also check out the interactive API explorer built into every instance and available at http://localhost:8080/graphiql diff --git a/src/client/graphiql/index.html b/src/client/graphiql/index.html index 44016557a92..d96f736b287 100644 --- a/src/client/graphiql/index.html +++ b/src/client/graphiql/index.html @@ -152,7 +152,7 @@ let apiFlavor = parameters.flavor || "gtfs"; let urls = { - gtfs: '/otp/routers/default/index/graphql', + gtfs: '/otp/gtfs/v1', transmodel: '/otp/routers/default/transmodel/index/graphql' } diff --git a/src/main/java/org/opentripplanner/apis/APIEndpoints.java b/src/main/java/org/opentripplanner/apis/APIEndpoints.java index fc49c02431f..00fcb61f439 100644 --- a/src/main/java/org/opentripplanner/apis/APIEndpoints.java +++ b/src/main/java/org/opentripplanner/apis/APIEndpoints.java @@ -51,6 +51,8 @@ private APIEndpoints() { addIfEnabled(APIServerInfo, ServerInfo.class); addIfEnabled(APIUpdaterStatus, UpdaterStatusResource.class); addIfEnabled(GtfsGraphQlApi, GtfsGraphQLAPI.class); + // scheduled to be removed and only here for backwards compatibility + addIfEnabled(GtfsGraphQlApi, GtfsGraphQLAPI.GtfsGraphQLAPIOldPath.class); addIfEnabled(TransmodelGraphQlApi, TransmodelAPI.class); // Sandbox extension APIs diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLAPI.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLAPI.java index b9b93190816..fd135d05544 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLAPI.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLAPI.java @@ -1,7 +1,6 @@ package org.opentripplanner.apis.gtfs; import com.fasterxml.jackson.databind.ObjectMapper; -import graphql.ExecutionResult; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.HeaderParam; @@ -14,39 +13,40 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; -import org.opentripplanner.framework.graphql.GraphQLResponseSerializer; import org.opentripplanner.standalone.api.OtpServerRequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@Path("/routers/{ignoreRouterId}/index/graphql") -@Produces(MediaType.APPLICATION_JSON) // One @Produces annotation for all endpoints. +@Path("/gtfs/v1/") +@Produces(MediaType.APPLICATION_JSON) public class GtfsGraphQLAPI { - @SuppressWarnings("unused") private static final Logger LOG = LoggerFactory.getLogger(GtfsGraphQLAPI.class); private final OtpServerRequestContext serverContext; private final ObjectMapper deserializer = new ObjectMapper(); - public GtfsGraphQLAPI( - @Context OtpServerRequestContext serverContext, - /** - * @deprecated The support for multiple routers are removed from OTP2. - * See https://github.com/opentripplanner/OpenTripPlanner/issues/2760 - */ - @Deprecated @PathParam("ignoreRouterId") String ignoreRouterId - ) { + public GtfsGraphQLAPI(@Context OtpServerRequestContext serverContext) { this.serverContext = serverContext; } + /** + * This class is only here for backwards-compatibility. It will be removed in the future. + */ + @Path("/routers/{ignoreRouterId}/index/graphql") + public static class GtfsGraphQLAPIOldPath extends GtfsGraphQLAPI { + + public GtfsGraphQLAPIOldPath( + @Context OtpServerRequestContext serverContext, + @PathParam("ignoreRouterId") String ignore + ) { + super(serverContext); + } + } + @POST @Consumes(MediaType.APPLICATION_JSON) public Response getGraphQL( @@ -120,64 +120,4 @@ public Response getGraphQL( GraphQLRequestContext.ofServerContext(serverContext) ); } - - @POST - @Path("/batch") - @Consumes(MediaType.APPLICATION_JSON) - public Response getGraphQLBatch( - List> queries, - @HeaderParam("OTPTimeout") @DefaultValue("30000") int timeout, - @HeaderParam("OTPMaxResolves") @DefaultValue("1000000") int maxResolves, - @Context HttpHeaders headers - ) { - List> futures = new ArrayList<>(); - Locale locale = headers.getAcceptableLanguages().size() > 0 - ? headers.getAcceptableLanguages().get(0) - : serverContext.defaultLocale(); - - for (HashMap query : queries) { - Map variables; - if (query.get("variables") instanceof Map) { - variables = (Map) query.get("variables"); - } else if ( - query.get("variables") instanceof String && ((String) query.get("variables")).length() > 0 - ) { - try { - variables = deserializer.readValue((String) query.get("variables"), Map.class); - } catch (IOException e) { - return Response - .status(Response.Status.BAD_REQUEST) - .type(MediaType.TEXT_PLAIN_TYPE) - .entity("Variables must be a valid json object") - .build(); - } - } else { - variables = null; - } - String operationName = (String) query.getOrDefault("operationName", null); - - futures.add(() -> - GtfsGraphQLIndex.getGraphQLExecutionResult( - (String) query.get("query"), - variables, - operationName, - maxResolves, - timeout, - locale, - GraphQLRequestContext.ofServerContext(serverContext) - ) - ); - } - - try { - List> results = GtfsGraphQLIndex.threadPool.invokeAll(futures); - return Response - .status(Response.Status.OK) - .entity(GraphQLResponseSerializer.serializeBatch(queries, results)) - .build(); - } catch (InterruptedException e) { - LOG.error("Batch query interrupted", e); - throw new RuntimeException(e); - } - } } diff --git a/src/main/java/org/opentripplanner/framework/graphql/GraphQLResponseSerializer.java b/src/main/java/org/opentripplanner/framework/graphql/GraphQLResponseSerializer.java index cb3b146a113..1497f63285d 100644 --- a/src/main/java/org/opentripplanner/framework/graphql/GraphQLResponseSerializer.java +++ b/src/main/java/org/opentripplanner/framework/graphql/GraphQLResponseSerializer.java @@ -33,30 +33,4 @@ public static String serialize(ExecutionResult executionResult) { throw new RuntimeException(e); } } - - public static String serializeBatch( - List> queries, - List> futures - ) { - List> responses = new LinkedList<>(); - for (int i = 0; i < queries.size(); i++) { - ExecutionResult executionResult; - // Try each request separately, returning both completed and failed responses is ok - try { - executionResult = futures.get(i).get(); - } catch (InterruptedException | ExecutionException e) { - executionResult = new AbortExecutionException(e).toExecutionResult(); - } - responses.add( - Map.of("id", queries.get(i).get("id"), "payload", executionResult.toSpecification()) - ); - } - - try { - return objectMapper.writeValueAsString(responses); - } catch (JsonProcessingException e) { - LOG.error("Unable to serialize response", e); - throw new RuntimeException(e); - } - } } From 91609ec3c7127ac6b3f53f7e152fcd1600d2a2e8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Jan 2024 22:59:31 +0100 Subject: [PATCH 80/86] Remove VehicleToStopHeuristics from examples [ci skip] --- docs/examples/ibi/atlanta/otp-config.json | 1 - docs/examples/ibi/portland/otp-config.json | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/examples/ibi/atlanta/otp-config.json b/docs/examples/ibi/atlanta/otp-config.json index b1c4b530cf2..2269e35d6ec 100644 --- a/docs/examples/ibi/atlanta/otp-config.json +++ b/docs/examples/ibi/atlanta/otp-config.json @@ -2,7 +2,6 @@ "otpFeatures" : { "FlexRouting": true, "GtfsGraphQlApi": true, - "VehicleToStopHeuristics": true, "ParallelRouting": true } } diff --git a/docs/examples/ibi/portland/otp-config.json b/docs/examples/ibi/portland/otp-config.json index 3227d4d90ca..2f4b1f586cc 100644 --- a/docs/examples/ibi/portland/otp-config.json +++ b/docs/examples/ibi/portland/otp-config.json @@ -1,6 +1,5 @@ { "otpFeatures" : { - "GtfsGraphQlApi": true, - "VehicleToStopHeuristics": true + "GtfsGraphQlApi": true } } From 25c7fc85607298c7eb3654d23d40f099a9cad373 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Wed, 17 Jan 2024 10:41:04 +0000 Subject: [PATCH 81/86] Add changelog entry for #4652 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 50846c3fce7..b26ab6ba080 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -74,6 +74,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Fix high walk reluctance leading to zero egress results for rental searches [#5605](https://github.com/opentripplanner/OpenTripPlanner/pull/5605) - Remove GTFS-RT websocket updater [#5604](https://github.com/opentripplanner/OpenTripPlanner/pull/5604) - Add stop layer to new Debug UI [#5602](https://github.com/opentripplanner/OpenTripPlanner/pull/5602) +- Use fallback timezone if no transit data is loaded [#4652](https://github.com/opentripplanner/OpenTripPlanner/pull/4652) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From 7826c0dc2074bb59716f73c74b93ae5d257828d4 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Wed, 17 Jan 2024 11:49:59 +0000 Subject: [PATCH 82/86] Add changelog entry for #5581 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index b26ab6ba080..4af1393c748 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -75,6 +75,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Remove GTFS-RT websocket updater [#5604](https://github.com/opentripplanner/OpenTripPlanner/pull/5604) - Add stop layer to new Debug UI [#5602](https://github.com/opentripplanner/OpenTripPlanner/pull/5602) - Use fallback timezone if no transit data is loaded [#4652](https://github.com/opentripplanner/OpenTripPlanner/pull/4652) +- Add new path for GTFS GraphQL API, remove batch feature [#5581](https://github.com/opentripplanner/OpenTripPlanner/pull/5581) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) From fc1225926a42f2716a6e61d17cbd39788a19c647 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 01:43:24 +0000 Subject: [PATCH 83/86] Update dependency org.entur.gbfs:gbfs-java-model to v3.0.20 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e5f86fe5b66..c3f7392bb42 100644 --- a/pom.xml +++ b/pom.xml @@ -688,7 +688,7 @@ org.entur.gbfs gbfs-java-model - 3.0.16 + 3.0.20 From 71decf9e3849e0bc11776d2fbd4f45c06fa9969c Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Thu, 18 Jan 2024 06:10:41 +0000 Subject: [PATCH 84/86] Upgrade debug client to version 2024/01/2024-01-18T06:10 --- src/client/debug-client-preview/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/debug-client-preview/index.html b/src/client/debug-client-preview/index.html index 5f77418dbe6..e9443e240c4 100644 --- a/src/client/debug-client-preview/index.html +++ b/src/client/debug-client-preview/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +

      From 5451b6de87b07ff3cb6a77ce3186d6348086d1ed Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 18 Jan 2024 09:06:21 +0100 Subject: [PATCH 85/86] Remove mention of REST in README [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 582511ce7f2..4486818745b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ OpenTripPlanner (OTP) is an open source multi-modal trip planner, focusing on travel by scheduled public transportation in combination with bicycling, walking, and mobility services including bike share and ride hailing. Its server component runs on any platform with a Java virtual machine ( -including Linux, Mac, and Windows). It exposes REST and GraphQL APIs that can be accessed by various +including Linux, Mac, and Windows). It exposes GraphQL APIs that can be accessed by various clients including open source Javascript components and native mobile applications. It builds its representation of the transportation network from open data in open standard file formats (primarily GTFS and OpenStreetMap). It applies real-time updates and alerts with immediate visibility to From 88955a789e8e21e73e760722a833849b9fe63a98 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 18 Jan 2024 19:19:40 +0100 Subject: [PATCH 86/86] Fix computation of accessibility score [ci skip] --- .../ext/accessibilityscore/AccessibilityScoreFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext/java/org/opentripplanner/ext/accessibilityscore/AccessibilityScoreFilter.java b/src/ext/java/org/opentripplanner/ext/accessibilityscore/AccessibilityScoreFilter.java index 46862b020d5..d31d30e29fa 100644 --- a/src/ext/java/org/opentripplanner/ext/accessibilityscore/AccessibilityScoreFilter.java +++ b/src/ext/java/org/opentripplanner/ext/accessibilityscore/AccessibilityScoreFilter.java @@ -37,7 +37,7 @@ public static Float compute(List legs) { public static float compute(ScheduledTransitLeg leg) { var fromStop = leg.getFrom().stop.getWheelchairAccessibility(); - var toStop = leg.getFrom().stop.getWheelchairAccessibility(); + var toStop = leg.getTo().stop.getWheelchairAccessibility(); var trip = leg.getTripWheelchairAccessibility(); var values = List.of(trip, fromStop, toStop);