From b1ed4fffe80281836c9c4362c851e24edd56700f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 12 Feb 2024 17:59:06 +0100 Subject: [PATCH 01/20] Add flex duration factors and offsets --- .../gtfs/mapping/StopTimeMapper.java | 16 +++++++ .../org/opentripplanner/model/StopTime.java | 47 ++++++++++--------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java index fc75236f4e4..2c01dda3e1b 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java @@ -1,5 +1,8 @@ package org.opentripplanner.gtfs.mapping; +import static org.onebusaway.gtfs.model.StopTime.MISSING_VALUE; + +import java.time.Duration; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -102,6 +105,19 @@ private StopTime doMap(org.onebusaway.gtfs.model.StopTime rhs) { lhs.setFarePeriodId(rhs.getFarePeriodId()); lhs.setFlexWindowStart(rhs.getStartPickupDropOffWindow()); lhs.setFlexWindowEnd(rhs.getEndPickupDropOffWindow()); + if(rhs.getMeanDurationOffset() != MISSING_VALUE) { + lhs.setMeanDurationFactor(rhs.getMeanDurationFactor()); + } + if(rhs.getMeanDurationOffset() != MISSING_VALUE) { + lhs.setMeanDurationOffset(Duration.ofSeconds((long) rhs.getMeanDurationOffset())); + } + if(rhs.getSafeDurationFactor() != MISSING_VALUE){ + lhs.setSafeDurationFactor(rhs.getSafeDurationFactor()); + } + if(rhs.getSafeDurationOffset()!= MISSING_VALUE){ + lhs.setSafeDurationOffset(Duration.ofSeconds((long) rhs.getSafeDurationOffset())); + } + lhs.setFlexContinuousPickup( PickDropMapper.mapFlexContinuousPickDrop(rhs.getContinuousPickup()) ); diff --git a/src/main/java/org/opentripplanner/model/StopTime.java b/src/main/java/org/opentripplanner/model/StopTime.java index 2ae04484426..8320e532c2c 100644 --- a/src/main/java/org/opentripplanner/model/StopTime.java +++ b/src/main/java/org/opentripplanner/model/StopTime.java @@ -1,6 +1,7 @@ /* This file is based on code copied from project OneBusAway, see the LICENSE file for further information. */ package org.opentripplanner.model; +import java.time.Duration; import java.util.List; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.time.TimeUtils; @@ -50,6 +51,14 @@ public final class StopTime implements Comparable { private int flexWindowEnd = MISSING_VALUE; + private double meanDurationFactor = MISSING_VALUE; + + private Duration meanDurationOffset = Duration.ZERO; + + private double safeDurationFactor = MISSING_VALUE; + + private Duration safeDurationOffset = Duration.ZERO; + // Disabled by default private PickDrop flexContinuousPickup = PickDrop.NONE; @@ -62,28 +71,6 @@ public final class StopTime implements Comparable { public StopTime() {} - public StopTime(StopTime st) { - this.trip = st.trip; - this.stop = st.stop; - this.arrivalTime = st.arrivalTime; - this.departureTime = st.departureTime; - this.timepoint = st.timepoint; - this.stopSequence = st.stopSequence; - this.stopHeadsign = st.stopHeadsign; - this.routeShortName = st.routeShortName; - this.pickupType = st.pickupType; - this.dropOffType = st.dropOffType; - this.shapeDistTraveled = st.shapeDistTraveled; - this.farePeriodId = st.farePeriodId; - this.flexWindowStart = st.flexWindowStart; - this.flexWindowEnd = st.flexWindowEnd; - this.flexContinuousPickup = st.flexContinuousPickup; - this.flexContinuousDropOff = st.flexContinuousDropOff; - this.dropOffBookingInfo = st.dropOffBookingInfo; - this.pickupBookingInfo = st.pickupBookingInfo; - this.headsignVias = st.headsignVias; - } - /** * The id is used to navigate/link StopTime to other entities (Map from StopTime.id -> Entity.id). * There is no need to navigate in the opposite direction. The StopTime id is NOT stored in a @@ -310,6 +297,22 @@ public String toString() { ); } + public void setMeanDurationFactor(double meanDurationFactor) { + this.meanDurationFactor = meanDurationFactor; + } + + public void setMeanDurationOffset(Duration meanDurationOffset) { + this.meanDurationOffset = meanDurationOffset; + } + + public void setSafeDurationOffset(Duration safeDurationOffset) { + this.safeDurationOffset = safeDurationOffset; + } + + public void setSafeDurationFactor(double safeDurationFactor) { + this.safeDurationFactor = safeDurationFactor; + } + private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { From 95527bbac94835a4032e43a34eb5872bed8bbb25 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 14 Feb 2024 17:23:12 +0100 Subject: [PATCH 02/20] Add initial implementation of flex duration factors --- .../DurationFactorCalculator.java | 26 +++++++++++++++++++ .../ext/flex/flexpathcalculator/FlexPath.java | 6 +++++ .../ext/flex/trip/StopTimeWindow.java | 13 ++++++++++ .../ext/flex/trip/UnscheduledTrip.java | 12 +++++---- .../gtfs/mapping/StopTimeMapper.java | 8 +++--- .../org/opentripplanner/model/StopTime.java | 15 +++++++++++ 6 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java new file mode 100644 index 00000000000..c4bc23848d7 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java @@ -0,0 +1,26 @@ +package org.opentripplanner.ext.flex.flexpathcalculator; + +import java.time.Duration; +import javax.annotation.Nullable; +import org.opentripplanner.street.model.vertex.Vertex; + +public class DurationFactorCalculator implements FlexPathCalculator { + + private final FlexPathCalculator underlying; + + public DurationFactorCalculator(FlexPathCalculator underlying) { + this.underlying = underlying; + } + + @Nullable + @Override + public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex) { + var path = underlying.calculateFlexPath(fromv, tov, fromStopIndex, toStopIndex); + + if (path == null) { + return null; + } else { + return path.withDurationFactors(1.5f, Duration.ofMinutes(10)); + } + } +} diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index 5c5557890d6..c36a59524d1 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -1,5 +1,6 @@ package org.opentripplanner.ext.flex.flexpathcalculator; +import java.time.Duration; import java.util.function.Supplier; import org.locationtech.jts.geom.LineString; @@ -32,4 +33,9 @@ public LineString getGeometry() { } return geometry; } + + public FlexPath withDurationFactors(float factor, Duration offset) { + int updatedDuration = (int) ((durationSeconds * factor) + offset.toSeconds()); + return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); + } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java b/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java index 5b7ebbbc575..821fe290cbb 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java @@ -3,6 +3,7 @@ import static org.opentripplanner.model.StopTime.MISSING_VALUE; import java.io.Serializable; +import java.util.OptionalDouble; import org.opentripplanner.framework.lang.IntRange; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; @@ -18,6 +19,8 @@ class StopTimeWindow implements Serializable { private final PickDrop pickupType; private final PickDrop dropOffType; + private final double meanDurationFactor; + StopTimeWindow(StopTime st) { stop = st.getStop(); @@ -32,6 +35,12 @@ class StopTimeWindow implements Serializable { // Do not allow for pickup/dropoff if times are not available pickupType = start == MISSING_VALUE ? PickDrop.NONE : st.getPickupType(); dropOffType = end == MISSING_VALUE ? PickDrop.NONE : st.getDropOffType(); + + if (st.meanDurationFactor().isPresent()) { + meanDurationFactor = st.meanDurationFactor().getAsDouble(); + } else { + meanDurationFactor = 1; + } } public StopLocation stop() { @@ -58,6 +67,10 @@ public IntRange timeWindow() { return IntRange.ofInclusive(start, end); } + public double meanDurationFactor() { + return meanDurationFactor; + } + private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index c5968507676..10b00b6ab98 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -14,6 +14,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; +import org.opentripplanner.ext.flex.flexpathcalculator.DurationFactorCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; @@ -81,8 +82,6 @@ public static UnscheduledTripBuilder of(FeedScopedId id) { * - One or more stop times with a flexible time window but no fixed stop in between them */ public static boolean isUnscheduledTrip(List stopTimes) { - Predicate hasFlexWindow = st -> - st.getFlexWindowStart() != MISSING_VALUE || st.getFlexWindowEnd() != MISSING_VALUE; Predicate hasContinuousStops = stopTime -> stopTime.getFlexContinuousDropOff() != NONE || stopTime.getFlexContinuousPickup() != NONE; if (stopTimes.isEmpty()) { @@ -90,9 +89,9 @@ public static boolean isUnscheduledTrip(List stopTimes) { } else if (stopTimes.stream().anyMatch(hasContinuousStops)) { return false; } else if (N_STOPS.contains(stopTimes.size())) { - return stopTimes.stream().anyMatch(hasFlexWindow); + return stopTimes.stream().anyMatch(StopTime::hasFlexWindow); } else { - return stopTimes.stream().allMatch(hasFlexWindow); + return stopTimes.stream().allMatch(StopTime::hasFlexWindow); } } @@ -120,6 +119,9 @@ public Stream getFlexAccessTemplates( } else { indices = IntStream.range(fromIndex + 1, lastIndexInTrip + 1); } + + var updatedCalculator = new DurationFactorCalculator(calculator); + // check for every stop after fromIndex if you can alight, if so return a template return indices // if you cannot alight at an index, the trip is not possible @@ -137,7 +139,7 @@ public Stream getFlexAccessTemplates( alightStop.index, alightStop.stop, date, - calculator, + updatedCalculator, config ) ); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java index 2c01dda3e1b..0a1f6269e4f 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java @@ -105,16 +105,16 @@ private StopTime doMap(org.onebusaway.gtfs.model.StopTime rhs) { lhs.setFarePeriodId(rhs.getFarePeriodId()); lhs.setFlexWindowStart(rhs.getStartPickupDropOffWindow()); lhs.setFlexWindowEnd(rhs.getEndPickupDropOffWindow()); - if(rhs.getMeanDurationOffset() != MISSING_VALUE) { + if (rhs.getMeanDurationOffset() != MISSING_VALUE) { lhs.setMeanDurationFactor(rhs.getMeanDurationFactor()); } - if(rhs.getMeanDurationOffset() != MISSING_VALUE) { + if (rhs.getMeanDurationOffset() != MISSING_VALUE) { lhs.setMeanDurationOffset(Duration.ofSeconds((long) rhs.getMeanDurationOffset())); } - if(rhs.getSafeDurationFactor() != MISSING_VALUE){ + if (rhs.getSafeDurationFactor() != MISSING_VALUE) { lhs.setSafeDurationFactor(rhs.getSafeDurationFactor()); } - if(rhs.getSafeDurationOffset()!= MISSING_VALUE){ + if (rhs.getSafeDurationOffset() != MISSING_VALUE) { lhs.setSafeDurationOffset(Duration.ofSeconds((long) rhs.getSafeDurationOffset())); } diff --git a/src/main/java/org/opentripplanner/model/StopTime.java b/src/main/java/org/opentripplanner/model/StopTime.java index 8320e532c2c..64045bbbbfe 100644 --- a/src/main/java/org/opentripplanner/model/StopTime.java +++ b/src/main/java/org/opentripplanner/model/StopTime.java @@ -3,6 +3,7 @@ import java.time.Duration; import java.util.List; +import java.util.OptionalDouble; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.time.TimeUtils; import org.opentripplanner.transit.model.site.StopLocation; @@ -313,6 +314,13 @@ public void setSafeDurationFactor(double safeDurationFactor) { this.safeDurationFactor = safeDurationFactor; } + public OptionalDouble meanDurationFactor() { + if (meanDurationFactor == MISSING_VALUE) { + return OptionalDouble.empty(); + } + return OptionalDouble.of(meanDurationFactor); + } + private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { @@ -322,4 +330,11 @@ private static int getAvailableTime(int... times) { return MISSING_VALUE; } + + /** + * Does this stop time define a flex window? + */ + public boolean hasFlexWindow() { + return flexWindowStart != MISSING_VALUE || flexWindowEnd != MISSING_VALUE; + } } From ea0960aa70e7cceff521777fd9ebb9f69ea50e75 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Mar 2024 09:27:24 +0100 Subject: [PATCH 03/20] Remove stop-time-based factors --- .../ext/flex/trip/StopTimeWindow.java | 13 -------- .../gtfs/mapping/StopTimeMapper.java | 15 --------- .../org/opentripplanner/model/StopTime.java | 33 ------------------- 3 files changed, 61 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java b/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java index 821fe290cbb..5b7ebbbc575 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java @@ -3,7 +3,6 @@ import static org.opentripplanner.model.StopTime.MISSING_VALUE; import java.io.Serializable; -import java.util.OptionalDouble; import org.opentripplanner.framework.lang.IntRange; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; @@ -19,8 +18,6 @@ class StopTimeWindow implements Serializable { private final PickDrop pickupType; private final PickDrop dropOffType; - private final double meanDurationFactor; - StopTimeWindow(StopTime st) { stop = st.getStop(); @@ -35,12 +32,6 @@ class StopTimeWindow implements Serializable { // Do not allow for pickup/dropoff if times are not available pickupType = start == MISSING_VALUE ? PickDrop.NONE : st.getPickupType(); dropOffType = end == MISSING_VALUE ? PickDrop.NONE : st.getDropOffType(); - - if (st.meanDurationFactor().isPresent()) { - meanDurationFactor = st.meanDurationFactor().getAsDouble(); - } else { - meanDurationFactor = 1; - } } public StopLocation stop() { @@ -67,10 +58,6 @@ public IntRange timeWindow() { return IntRange.ofInclusive(start, end); } - public double meanDurationFactor() { - return meanDurationFactor; - } - private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java index 0a1f6269e4f..67b250c5061 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java @@ -1,8 +1,5 @@ package org.opentripplanner.gtfs.mapping; -import static org.onebusaway.gtfs.model.StopTime.MISSING_VALUE; - -import java.time.Duration; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -105,18 +102,6 @@ private StopTime doMap(org.onebusaway.gtfs.model.StopTime rhs) { lhs.setFarePeriodId(rhs.getFarePeriodId()); lhs.setFlexWindowStart(rhs.getStartPickupDropOffWindow()); lhs.setFlexWindowEnd(rhs.getEndPickupDropOffWindow()); - if (rhs.getMeanDurationOffset() != MISSING_VALUE) { - lhs.setMeanDurationFactor(rhs.getMeanDurationFactor()); - } - if (rhs.getMeanDurationOffset() != MISSING_VALUE) { - lhs.setMeanDurationOffset(Duration.ofSeconds((long) rhs.getMeanDurationOffset())); - } - if (rhs.getSafeDurationFactor() != MISSING_VALUE) { - lhs.setSafeDurationFactor(rhs.getSafeDurationFactor()); - } - if (rhs.getSafeDurationOffset() != MISSING_VALUE) { - lhs.setSafeDurationOffset(Duration.ofSeconds((long) rhs.getSafeDurationOffset())); - } lhs.setFlexContinuousPickup( PickDropMapper.mapFlexContinuousPickDrop(rhs.getContinuousPickup()) diff --git a/src/main/java/org/opentripplanner/model/StopTime.java b/src/main/java/org/opentripplanner/model/StopTime.java index 64045bbbbfe..e753b8d2885 100644 --- a/src/main/java/org/opentripplanner/model/StopTime.java +++ b/src/main/java/org/opentripplanner/model/StopTime.java @@ -1,9 +1,7 @@ /* This file is based on code copied from project OneBusAway, see the LICENSE file for further information. */ package org.opentripplanner.model; -import java.time.Duration; import java.util.List; -import java.util.OptionalDouble; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.time.TimeUtils; import org.opentripplanner.transit.model.site.StopLocation; @@ -52,14 +50,6 @@ public final class StopTime implements Comparable { private int flexWindowEnd = MISSING_VALUE; - private double meanDurationFactor = MISSING_VALUE; - - private Duration meanDurationOffset = Duration.ZERO; - - private double safeDurationFactor = MISSING_VALUE; - - private Duration safeDurationOffset = Duration.ZERO; - // Disabled by default private PickDrop flexContinuousPickup = PickDrop.NONE; @@ -298,29 +288,6 @@ public String toString() { ); } - public void setMeanDurationFactor(double meanDurationFactor) { - this.meanDurationFactor = meanDurationFactor; - } - - public void setMeanDurationOffset(Duration meanDurationOffset) { - this.meanDurationOffset = meanDurationOffset; - } - - public void setSafeDurationOffset(Duration safeDurationOffset) { - this.safeDurationOffset = safeDurationOffset; - } - - public void setSafeDurationFactor(double safeDurationFactor) { - this.safeDurationFactor = safeDurationFactor; - } - - public OptionalDouble meanDurationFactor() { - if (meanDurationFactor == MISSING_VALUE) { - return OptionalDouble.empty(); - } - return OptionalDouble.of(meanDurationFactor); - } - private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { From a0eb06655a65deb18005d9ba5f2441c072d3fa59 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Mar 2024 10:29:47 +0100 Subject: [PATCH 04/20] Move factors into calcultor --- .../DurationFactorCalculator.java | 14 +++++++++----- .../ext/flex/trip/UnscheduledTrip.java | 3 ++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java index c4bc23848d7..24a4d665fe6 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java @@ -6,21 +6,25 @@ public class DurationFactorCalculator implements FlexPathCalculator { - private final FlexPathCalculator underlying; + private final FlexPathCalculator delegate; + private final float factor; + private final Duration offset; - public DurationFactorCalculator(FlexPathCalculator underlying) { - this.underlying = underlying; + public DurationFactorCalculator(FlexPathCalculator delegate, float factor, Duration offset) { + this.delegate = delegate; + this.factor = factor; + this.offset = offset; } @Nullable @Override public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex) { - var path = underlying.calculateFlexPath(fromv, tov, fromStopIndex, toStopIndex); + var path = delegate.calculateFlexPath(fromv, tov, fromStopIndex, toStopIndex); if (path == null) { return null; } else { - return path.withDurationFactors(1.5f, Duration.ofMinutes(10)); + return path.withDurationFactors(factor, offset); } } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index 10b00b6ab98..e2ab49045d6 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -3,6 +3,7 @@ import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -120,7 +121,7 @@ public Stream getFlexAccessTemplates( indices = IntStream.range(fromIndex + 1, lastIndexInTrip + 1); } - var updatedCalculator = new DurationFactorCalculator(calculator); + var updatedCalculator = new DurationFactorCalculator(calculator, 1.5f, Duration.ofMinutes(20)); // check for every stop after fromIndex if you can alight, if so return a template return indices From cc7601275a2ba2551028b40d229da7afd799188c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Mar 2024 14:24:04 +0100 Subject: [PATCH 05/20] Encapsulate factors, add parsing --- .../ext/flex/FlexTripsMapper.java | 9 ++++++- .../DurationFactorCalculator.java | 12 ++++----- .../ext/flex/flexpathcalculator/FlexPath.java | 6 ++--- .../ext/flex/trip/FlexDurationFactors.java | 27 +++++++++++++++++++ .../ext/flex/trip/UnscheduledTrip.java | 15 +++++++++-- .../ext/flex/trip/UnscheduledTripBuilder.java | 10 +++++++ .../GTFSToOtpTransitServiceMapper.java | 1 + .../gtfs/mapping/TripMapper.java | 26 ++++++++++++++++-- .../model/impl/OtpTransitServiceBuilder.java | 9 +++++++ 9 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index f5ab34cce49..d4918e8ed33 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; @@ -34,10 +35,16 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); + var factors = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationFactors.ZERO); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { result.add( - UnscheduledTrip.of(trip.getId()).withTrip(trip).withStopTimes(stopTimes).build() + UnscheduledTrip + .of(trip.getId()) + .withTrip(trip) + .withStopTimes(stopTimes) + .withDurationFactors(factors) + .build() ); } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { result.add( diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java index 24a4d665fe6..3e5a6b1db79 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java @@ -1,19 +1,17 @@ package org.opentripplanner.ext.flex.flexpathcalculator; -import java.time.Duration; import javax.annotation.Nullable; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; import org.opentripplanner.street.model.vertex.Vertex; public class DurationFactorCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; - private final float factor; - private final Duration offset; + private final FlexDurationFactors factors; - public DurationFactorCalculator(FlexPathCalculator delegate, float factor, Duration offset) { + public DurationFactorCalculator(FlexPathCalculator delegate, FlexDurationFactors factors) { this.delegate = delegate; - this.factor = factor; - this.offset = offset; + this.factors = factors; } @Nullable @@ -24,7 +22,7 @@ public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, i if (path == null) { return null; } else { - return path.withDurationFactors(factor, offset); + return path.withDurationFactors(factors); } } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index c36a59524d1..7ba3aad43d4 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -1,8 +1,8 @@ package org.opentripplanner.ext.flex.flexpathcalculator; -import java.time.Duration; import java.util.function.Supplier; import org.locationtech.jts.geom.LineString; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; /** * This class contains the results from a FlexPathCalculator. @@ -34,8 +34,8 @@ public LineString getGeometry() { return geometry; } - public FlexPath withDurationFactors(float factor, Duration offset) { - int updatedDuration = (int) ((durationSeconds * factor) + offset.toSeconds()); + public FlexPath withDurationFactors(FlexDurationFactors factors) { + int updatedDuration = (int) ((durationSeconds * factors.factor()) + factors.offsetInSeconds()); return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java new file mode 100644 index 00000000000..41b610730d4 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java @@ -0,0 +1,27 @@ +package org.opentripplanner.ext.flex.trip; + +import java.time.Duration; + +public class FlexDurationFactors { + + public static FlexDurationFactors ZERO = new FlexDurationFactors(Duration.ZERO, 1); + private final int offset; + private final float factor; + + public FlexDurationFactors(Duration offset, float factor) { + this.offset = (int) offset.toSeconds(); + this.factor = factor; + } + + public float factor() { + return factor; + } + + public int offsetInSeconds() { + return offset; + } + + boolean nonZero() { + return offset != 0 && factor != 1.0; + } +} diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index e2ab49045d6..9c71696c02b 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -3,9 +3,9 @@ import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; -import java.time.Duration; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -52,6 +52,8 @@ public class UnscheduledTrip extends FlexTrip stopTimes = builder.stopTimes(); @@ -69,6 +71,7 @@ public UnscheduledTrip(UnscheduledTripBuilder builder) { this.dropOffBookingInfos[i] = stopTimes.get(0).getDropOffBookingInfo(); this.pickupBookingInfos[i] = stopTimes.get(0).getPickupBookingInfo(); } + this.duractionFactors = Objects.requireNonNull(builder.durationFactors()); } public static UnscheduledTripBuilder of(FeedScopedId id) { @@ -121,7 +124,7 @@ public Stream getFlexAccessTemplates( indices = IntStream.range(fromIndex + 1, lastIndexInTrip + 1); } - var updatedCalculator = new DurationFactorCalculator(calculator, 1.5f, Duration.ofMinutes(20)); + final var updatedCalculator = flexPathCalculator(calculator); // check for every stop after fromIndex if you can alight, if so return a template return indices @@ -146,6 +149,14 @@ public Stream getFlexAccessTemplates( ); } + private FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { + if (duractionFactors.nonZero()) { + return new DurationFactorCalculator(calculator, duractionFactors); + } else { + return calculator; + } + } + @Override public Stream getFlexEgressTemplates( NearbyStop egress, diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index 678b7fcce5e..e2d296d8154 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -8,6 +8,7 @@ public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; + private FlexDurationFactors durationFactors = FlexDurationFactors.ZERO; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -29,6 +30,15 @@ public List stopTimes() { return stopTimes; } + public UnscheduledTripBuilder withDurationFactors(FlexDurationFactors factors) { + this.durationFactors = factors; + return this; + } + + public FlexDurationFactors durationFactors() { + return durationFactors; + } + @Override UnscheduledTripBuilder instance() { return this; diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index 4d5fe6bd051..15a4c2e28b7 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -171,6 +171,7 @@ public void mapStopTripAndRouteDataIntoBuilder() { builder.getPathways().addAll(pathwayMapper.map(data.getAllPathways())); builder.getStopTimesSortedByTrip().addAll(stopTimeMapper.map(data.getAllStopTimes())); + builder.getFlexDurationFactors().putAll(tripMapper.flexDurationFactors()); builder.getTripsById().addAll(tripMapper.map(data.getAllTrips())); fareRulesBuilder.fareAttributes().addAll(fareAttributeMapper.map(data.getAllFareAttributes())); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index a80ae035ed1..77d6eb879b0 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -1,8 +1,11 @@ package org.opentripplanner.gtfs.mapping; +import java.time.Duration; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; import org.opentripplanner.framework.collection.MapUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.timetable.Trip; @@ -12,9 +15,10 @@ class TripMapper { private final RouteMapper routeMapper; private final DirectionMapper directionMapper; - private TranslationHelper translationHelper; + private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); + private final Map flexDurationFactors = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -38,6 +42,13 @@ Collection getMappedTrips() { return mappedTrips.values(); } + /** + * The map of flex duration factors per flex trip. + */ + Map flexDurationFactors() { + return flexDurationFactors; + } + private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { var lhs = Trip.of(AgencyAndIdMapper.mapAgencyAndId(rhs.getId())); @@ -62,6 +73,17 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { lhs.withBikesAllowed(BikeAccessMapper.mapForTrip(rhs)); lhs.withGtfsFareId(rhs.getFareId()); - return lhs.build(); + var trip = lhs.build(); + mapFlexDurationFactots(rhs).ifPresent(f -> flexDurationFactors.put(trip, f)); + return trip; + } + + private Optional mapFlexDurationFactots(org.onebusaway.gtfs.model.Trip rhs) { + if (rhs.getMeanDurationFactor() == null && rhs.getMeanDurationOffset() == null) { + return Optional.empty(); + } else { + var offset = Duration.ofSeconds(rhs.getMeanDurationOffset().longValue()); + return Optional.of(new FlexDurationFactors(offset, rhs.getMeanDurationFactor().floatValue())); + } } } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 382d6042a05..e4d0d76cbe3 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -3,10 +3,13 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed; @@ -92,6 +95,8 @@ public class OtpTransitServiceBuilder { private final TripStopTimes stopTimesByTrip = new TripStopTimes(); + private final Map flexDurationFactors = new HashMap<>(); + private final EntityById fareZonesById = new DefaultEntityById<>(); private final List transfers = new ArrayList<>(); @@ -209,6 +214,10 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } + public Map getFlexDurationFactors() { + return flexDurationFactors; + } + public EntityById getFareZonesById() { return fareZonesById; } From dca331bff6144a4d51902bcfe78115bac00c40ee Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Mar 2024 15:07:31 +0100 Subject: [PATCH 06/20] Make factors serializable --- .../ext/flex/FlexTripsMapper.java | 2 +- .../ext/flex/trip/FlexDurationFactors.java | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index d4918e8ed33..819b51f9df2 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -35,9 +35,9 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); - var factors = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationFactors.ZERO); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { + var factors = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationFactors.ZERO); result.add( UnscheduledTrip .of(trip.getId()) diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java index 41b610730d4..adcb25dc1e4 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java @@ -1,15 +1,21 @@ package org.opentripplanner.ext.flex.trip; +import java.io.Serializable; import java.time.Duration; +import org.opentripplanner.framework.time.DurationUtils; +import org.opentripplanner.framework.tostring.ToStringBuilder; -public class FlexDurationFactors { +public class FlexDurationFactors implements Serializable { public static FlexDurationFactors ZERO = new FlexDurationFactors(Duration.ZERO, 1); private final int offset; private final float factor; public FlexDurationFactors(Duration offset, float factor) { - this.offset = (int) offset.toSeconds(); + if (factor < 0.1) { + throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); + } + this.offset = (int) DurationUtils.requireNonNegative(offset).toSeconds(); this.factor = factor; } @@ -24,4 +30,13 @@ public int offsetInSeconds() { boolean nonZero() { return offset != 0 && factor != 1.0; } + + @Override + public String toString() { + return ToStringBuilder + .of(FlexDurationFactors.class) + .addNum("factor", factor) + .addDurationSec("offset", offset) + .toString(); + } } From 721845e1b7287a8186b4abdf517501b51474ded7 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Apr 2024 12:28:37 +0200 Subject: [PATCH 07/20] Use safe instead of mean values --- .../mapping/GTFSToOtpTransitServiceMapper.java | 2 +- .../opentripplanner/gtfs/mapping/TripMapper.java | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index 15a4c2e28b7..4ee1cdcf1d7 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -171,7 +171,7 @@ public void mapStopTripAndRouteDataIntoBuilder() { builder.getPathways().addAll(pathwayMapper.map(data.getAllPathways())); builder.getStopTimesSortedByTrip().addAll(stopTimeMapper.map(data.getAllStopTimes())); - builder.getFlexDurationFactors().putAll(tripMapper.flexDurationFactors()); + builder.getFlexDurationFactors().putAll(tripMapper.flexSafeDurationFactors()); builder.getTripsById().addAll(tripMapper.map(data.getAllTrips())); fareRulesBuilder.fareAttributes().addAll(fareAttributeMapper.map(data.getAllFareAttributes())); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index 77d6eb879b0..0164e7d77f9 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexDurationFactors = new HashMap<>(); + private final Map flexSafeDurationFactors = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,8 +45,8 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexDurationFactors() { - return flexDurationFactors; + Map flexSafeDurationFactors() { + return flexSafeDurationFactors; } private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { @@ -74,16 +74,16 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { lhs.withGtfsFareId(rhs.getFareId()); var trip = lhs.build(); - mapFlexDurationFactots(rhs).ifPresent(f -> flexDurationFactors.put(trip, f)); + mapSafeDurationFactors(rhs).ifPresent(f -> flexSafeDurationFactors.put(trip, f)); return trip; } - private Optional mapFlexDurationFactots(org.onebusaway.gtfs.model.Trip rhs) { - if (rhs.getMeanDurationFactor() == null && rhs.getMeanDurationOffset() == null) { + private Optional mapSafeDurationFactors(org.onebusaway.gtfs.model.Trip rhs) { + if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { - var offset = Duration.ofSeconds(rhs.getMeanDurationOffset().longValue()); - return Optional.of(new FlexDurationFactors(offset, rhs.getMeanDurationFactor().floatValue())); + var offset = Duration.ofSeconds(rhs.getSafeDurationOffset().longValue()); + return Optional.of(new FlexDurationFactors(offset, rhs.getSafeDurationFactor().floatValue())); } } } From abcfa935489f7dfdc735ec92e7dead8cd7ca4f1c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Apr 2024 12:32:15 +0200 Subject: [PATCH 08/20] Use correct booking info instances --- .../org/opentripplanner/ext/flex/trip/UnscheduledTrip.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index 9c71696c02b..712a446c7ed 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -68,8 +68,8 @@ public UnscheduledTrip(UnscheduledTripBuilder builder) { for (int i = 0; i < size; i++) { this.stopTimes[i] = new StopTimeWindow(stopTimes.get(i)); - this.dropOffBookingInfos[i] = stopTimes.get(0).getDropOffBookingInfo(); - this.pickupBookingInfos[i] = stopTimes.get(0).getPickupBookingInfo(); + this.dropOffBookingInfos[i] = stopTimes.get(i).getDropOffBookingInfo(); + this.pickupBookingInfos[i] = stopTimes.get(i).getPickupBookingInfo(); } this.duractionFactors = Objects.requireNonNull(builder.durationFactors()); } From 5066052c4452afdc15f10896f4c01ee510415dab Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Apr 2024 16:05:31 +0200 Subject: [PATCH 09/20] Implement duration modifier for ScheduledDeviated trip --- .../ext/flex/FlexTripsMapper.java | 14 ++++--- .../DurationFactorCalculator.java | 6 +-- .../ext/flex/flexpathcalculator/FlexPath.java | 4 +- .../ScheduledFlexPathCalculator.java | 9 +++-- ...Factors.java => FlexDurationModifier.java} | 14 ++++--- .../ext/flex/trip/ScheduledDeviatedTrip.java | 39 +++++++++++++++++-- .../trip/ScheduledDeviatedTripBuilder.java | 10 +++++ .../ext/flex/trip/UnscheduledTrip.java | 8 ++-- .../ext/flex/trip/UnscheduledTripBuilder.java | 10 ++--- .../gtfs/mapping/TripMapper.java | 14 ++++--- .../model/impl/OtpTransitServiceBuilder.java | 7 ++-- 11 files changed, 96 insertions(+), 39 deletions(-) rename src/ext/java/org/opentripplanner/ext/flex/trip/{FlexDurationFactors.java => FlexDurationModifier.java} (68%) diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 819b51f9df2..5881bc798a8 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; @@ -35,20 +35,24 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); - + var modifier = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationModifier.NONE); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { - var factors = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationFactors.ZERO); result.add( UnscheduledTrip .of(trip.getId()) .withTrip(trip) .withStopTimes(stopTimes) - .withDurationFactors(factors) + .withDurationModifier(modifier) .build() ); } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { result.add( - ScheduledDeviatedTrip.of(trip.getId()).withTrip(trip).withStopTimes(stopTimes).build() + ScheduledDeviatedTrip + .of(trip.getId()) + .withTrip(trip) + .withStopTimes(stopTimes) + .withDurationModifier(modifier) + .build() ); } else if (hasContinuousStops(stopTimes) && FlexTrip.containsFlexStops(stopTimes)) { store.add( diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java index 3e5a6b1db79..e0873fab187 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java @@ -1,15 +1,15 @@ package org.opentripplanner.ext.flex.flexpathcalculator; import javax.annotation.Nullable; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; import org.opentripplanner.street.model.vertex.Vertex; public class DurationFactorCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; - private final FlexDurationFactors factors; + private final FlexDurationModifier factors; - public DurationFactorCalculator(FlexPathCalculator delegate, FlexDurationFactors factors) { + public DurationFactorCalculator(FlexPathCalculator delegate, FlexDurationModifier factors) { this.delegate = delegate; this.factors = factors; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index 7ba3aad43d4..22752726ae7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -2,7 +2,7 @@ import java.util.function.Supplier; import org.locationtech.jts.geom.LineString; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; /** * This class contains the results from a FlexPathCalculator. @@ -34,7 +34,7 @@ public LineString getGeometry() { return geometry; } - public FlexPath withDurationFactors(FlexDurationFactors factors) { + public FlexPath withDurationFactors(FlexDurationModifier factors) { int updatedDuration = (int) ((durationSeconds * factors.factor()) + factors.offsetInSeconds()); return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); } diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java index 7d953abc4fd..cd5228dada5 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java @@ -20,7 +20,7 @@ public ScheduledFlexPathCalculator(FlexPathCalculator flexPathCalculator, FlexTr @Override public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex) { - FlexPath flexPath = flexPathCalculator.calculateFlexPath( + final var flexPath = flexPathCalculator.calculateFlexPath( fromv, tov, fromStopIndex, @@ -29,7 +29,6 @@ public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, i if (flexPath == null) { return null; } - int distance = flexPath.distanceMeters; int departureTime = trip.earliestDepartureTime( Integer.MIN_VALUE, fromStopIndex, @@ -50,6 +49,10 @@ public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, i if (departureTime >= arrivalTime) { return null; } - return new FlexPath(distance, arrivalTime - departureTime, flexPath::getGeometry); + return new FlexPath( + flexPath.distanceMeters, + arrivalTime - departureTime, + flexPath::getGeometry + ); } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java similarity index 68% rename from src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java rename to src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java index adcb25dc1e4..60f795d7280 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java @@ -5,13 +5,13 @@ import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; -public class FlexDurationFactors implements Serializable { +public class FlexDurationModifier implements Serializable { - public static FlexDurationFactors ZERO = new FlexDurationFactors(Duration.ZERO, 1); + public static FlexDurationModifier NONE = new FlexDurationModifier(Duration.ZERO, 1); private final int offset; private final float factor; - public FlexDurationFactors(Duration offset, float factor) { + public FlexDurationModifier(Duration offset, float factor) { if (factor < 0.1) { throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); } @@ -27,14 +27,18 @@ public int offsetInSeconds() { return offset; } - boolean nonZero() { + /** + * Check if this instance actually modifies the duration or simply passes it back without + * change. + */ + boolean modifies() { return offset != 0 && factor != 1.0; } @Override public String toString() { return ToStringBuilder - .of(FlexDurationFactors.class) + .of(FlexDurationModifier.class) .addNum("factor", factor) .addDurationSec("offset", offset) .toString(); diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index e16e1e5e1f7..33a064e610c 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -1,5 +1,7 @@ package org.opentripplanner.ext.flex.trip; +import static org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip.TimeType.FIXED_TIME; +import static org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip.TimeType.FLEXIBLE_TIME; import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; @@ -15,6 +17,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; +import org.opentripplanner.ext.flex.flexpathcalculator.DurationFactorCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.ScheduledFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; @@ -42,6 +45,8 @@ public class ScheduledDeviatedTrip private final BookingInfo[] dropOffBookingInfos; private final BookingInfo[] pickupBookingInfos; + private final FlexDurationModifier durationModifier; + ScheduledDeviatedTrip(ScheduledDeviatedTripBuilder builder) { super(builder); List stopTimes = builder.stopTimes(); @@ -59,6 +64,7 @@ public class ScheduledDeviatedTrip this.dropOffBookingInfos[i] = stopTimes.get(i).getDropOffBookingInfo(); this.pickupBookingInfos[i] = stopTimes.get(i).getPickupBookingInfo(); } + this.durationModifier = builder.durationModifier(); } public static ScheduledDeviatedTripBuilder of(FeedScopedId id) { @@ -104,7 +110,7 @@ public Stream getFlexAccessTemplates( toIndex, stop, date, - scheduledCalculator, + getCalculator(fromIndex, toIndex, scheduledCalculator), config ) ); @@ -114,6 +120,25 @@ public Stream getFlexAccessTemplates( return res.stream(); } + /** + * If any of the stops involved in the path computation, then apply the duration factors. + * If both from and to have a fixed time, then don't apply the factors. + */ + private FlexPathCalculator getCalculator( + int fromIndex, + int toIndex, + FlexPathCalculator scheduledCalculator + ) { + final boolean usesFlexWindow = + stopTimes[fromIndex].timeType == FLEXIBLE_TIME || + stopTimes[toIndex].timeType == FLEXIBLE_TIME; + if (usesFlexWindow && durationModifier.modifies()) { + return new DurationFactorCalculator(scheduledCalculator, FlexDurationModifier.NONE); + } else { + return scheduledCalculator; + } + } + @Override public Stream getFlexEgressTemplates( NearbyStop egress, @@ -144,7 +169,7 @@ public Stream getFlexEgressTemplates( toIndex, stop, date, - scheduledCalculator, + getCalculator(fromIndex, toIndex, scheduledCalculator), config ) ); @@ -176,7 +201,7 @@ public int earliestDepartureTime(int stopIndex) { @Override public int latestArrivalTime( int arrivalTime, - int fromStopIndex, + int ignored, int toStopIndex, int flexTripDurationSeconds ) { @@ -297,10 +322,13 @@ private static class ScheduledDeviatedStopTime implements Serializable { private final int arrivalTime; private final PickDrop pickupType; private final PickDrop dropOffType; + private final TimeType timeType; private ScheduledDeviatedStopTime(StopTime st) { this.stop = st.getStop(); + this.timeType = st.hasFlexWindow() ? FLEXIBLE_TIME : FIXED_TIME; + // Store the time the user is guaranteed to arrive at latest this.arrivalTime = st.getLatestPossibleArrivalTime(); // Store the time the user needs to be ready for pickup @@ -315,4 +343,9 @@ private ScheduledDeviatedStopTime(StopTime st) { this.dropOffType = arrivalTime == MISSING_VALUE ? PickDrop.NONE : st.getDropOffType(); } } + + enum TimeType { + FIXED_TIME, + FLEXIBLE_TIME, + } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java index 4033bbe7c59..3a33ebd50ec 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java @@ -8,6 +8,7 @@ public class ScheduledDeviatedTripBuilder extends FlexTripBuilder { private List stopTimes; + private FlexDurationModifier durationModifier = FlexDurationModifier.NONE; ScheduledDeviatedTripBuilder(FeedScopedId id) { super(id); @@ -29,6 +30,15 @@ public List stopTimes() { return stopTimes; } + public FlexDurationModifier durationModifier() { + return durationModifier; + } + + public ScheduledDeviatedTripBuilder withDurationModifier(FlexDurationModifier modifier) { + this.durationModifier = modifier; + return this; + } + @Override ScheduledDeviatedTripBuilder instance() { return this; diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index 712a446c7ed..d85c6b72c27 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -52,7 +52,7 @@ public class UnscheduledTrip extends FlexTrip getFlexAccessTemplates( } private FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { - if (duractionFactors.nonZero()) { - return new DurationFactorCalculator(calculator, duractionFactors); + if (durationModifier.modifies()) { + return new DurationFactorCalculator(calculator, durationModifier); } else { return calculator; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index e2d296d8154..7e2132ed036 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -8,7 +8,7 @@ public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; - private FlexDurationFactors durationFactors = FlexDurationFactors.ZERO; + private FlexDurationModifier durationModifier = FlexDurationModifier.NONE; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -30,13 +30,13 @@ public List stopTimes() { return stopTimes; } - public UnscheduledTripBuilder withDurationFactors(FlexDurationFactors factors) { - this.durationFactors = factors; + public UnscheduledTripBuilder withDurationModifier(FlexDurationModifier factors) { + this.durationModifier = factors; return this; } - public FlexDurationFactors durationFactors() { - return durationFactors; + public FlexDurationModifier durationModifier() { + return durationModifier; } @Override diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index 0164e7d77f9..03ef077d319 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -5,7 +5,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; import org.opentripplanner.framework.collection.MapUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.timetable.Trip; @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexSafeDurationFactors = new HashMap<>(); + private final Map flexSafeDurationFactors = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,7 +45,7 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexSafeDurationFactors() { + Map flexSafeDurationFactors() { return flexSafeDurationFactors; } @@ -78,12 +78,16 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { return trip; } - private Optional mapSafeDurationFactors(org.onebusaway.gtfs.model.Trip rhs) { + private Optional mapSafeDurationFactors( + org.onebusaway.gtfs.model.Trip rhs + ) { if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { var offset = Duration.ofSeconds(rhs.getSafeDurationOffset().longValue()); - return Optional.of(new FlexDurationFactors(offset, rhs.getSafeDurationFactor().floatValue())); + return Optional.of( + new FlexDurationModifier(offset, rhs.getSafeDurationFactor().floatValue()) + ); } } } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index e4d0d76cbe3..5811fe042c3 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -7,9 +7,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed; @@ -95,7 +94,7 @@ public class OtpTransitServiceBuilder { private final TripStopTimes stopTimesByTrip = new TripStopTimes(); - private final Map flexDurationFactors = new HashMap<>(); + private final Map flexDurationFactors = new HashMap<>(); private final EntityById fareZonesById = new DefaultEntityById<>(); @@ -214,7 +213,7 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } - public Map getFlexDurationFactors() { + public Map getFlexDurationFactors() { return flexDurationFactors; } From 90b15cd8684da65bb5bf2e1d984d00bc53129cdc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 6 Apr 2024 22:13:08 +0200 Subject: [PATCH 10/20] Rename and test --- .../DurationModifierCalculatorTest.java | 35 +++++++++++++++++ .../flex/flexpathcalculator/FlexPathTest.java | 39 +++++++++++++++++++ .../ext/flex/FlexTripsMapper.java | 4 +- ...r.java => DurationModifierCalculator.java} | 10 ++--- .../ext/flex/flexpathcalculator/FlexPath.java | 11 ++++-- ...ionModifier.java => DurationModifier.java} | 8 ++-- .../ext/flex/trip/ScheduledDeviatedTrip.java | 6 +-- .../trip/ScheduledDeviatedTripBuilder.java | 6 +-- .../ext/flex/trip/UnscheduledTrip.java | 6 +-- .../ext/flex/trip/UnscheduledTripBuilder.java | 6 +-- .../gtfs/mapping/TripMapper.java | 14 +++---- .../model/impl/OtpTransitServiceBuilder.java | 6 +-- .../_support/geometry/Polygons.java | 4 +- 13 files changed, 115 insertions(+), 40 deletions(-) create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java rename src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/{DurationFactorCalculator.java => DurationModifierCalculator.java} (62%) rename src/ext/java/org/opentripplanner/ext/flex/trip/{FlexDurationModifier.java => DurationModifier.java} (79%) diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java new file mode 100644 index 00000000000..fd81344893b --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java @@ -0,0 +1,35 @@ +package org.opentripplanner.ext.flex.flexpathcalculator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.time.Duration; +import org.junit.jupiter.api.Test; +import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.framework.geometry.GeometryUtils; +import org.opentripplanner.street.model._data.StreetModelForTest; + +class DurationModifierCalculatorTest { + + private static final int THIRTY_MINS_IN_SECONDS = (int) Duration.ofMinutes(30).toSeconds(); + + @Test + void calculate() { + FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> + new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> GeometryUtils.makeLineString(1, 1, 2, 2)); + + var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); + var calc = new DurationModifierCalculator(delegate, mod); + var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); + assertEquals(3300, path.durationSeconds); + } + + @Test + void nullValue() { + FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> null; + var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); + var calc = new DurationModifierCalculator(delegate, mod); + var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); + assertNull(path); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java new file mode 100644 index 00000000000..9bee310573b --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java @@ -0,0 +1,39 @@ +package org.opentripplanner.ext.flex.flexpathcalculator; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.Duration; +import java.util.List; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.locationtech.jts.geom.LineString; +import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.framework.geometry.GeometryUtils; + +class FlexPathTest { + + private static final int THIRTY_MINS_IN_SECONDS = (int) Duration.ofMinutes(30).toSeconds(); + private static final LineString LINE_STRING = GeometryUtils.makeLineString(1, 1, 2, 2); + private static final FlexPath PATH = new FlexPath( + 10_000, + THIRTY_MINS_IN_SECONDS, + () -> LINE_STRING + ); + + static List cases() { + return List.of( + Arguments.of(DurationModifier.NONE, THIRTY_MINS_IN_SECONDS), + Arguments.of(new DurationModifier(Duration.ofMinutes(10), 1), 2400), + Arguments.of(new DurationModifier(Duration.ofMinutes(10), 1.5f), 3300), + Arguments.of(new DurationModifier(Duration.ZERO, 3), 5400) + ); + } + + @ParameterizedTest + @MethodSource("cases") + void calculate(DurationModifier mod, int expectedSeconds) { + var modified = PATH.withDurationModifier(mod); + assertEquals(expectedSeconds, modified.durationSeconds); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 5881bc798a8..e6cad4d3e40 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; @@ -35,7 +35,7 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); - var modifier = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationModifier.NONE); + var modifier = builder.getFlexDurationFactors().getOrDefault(trip, DurationModifier.NONE); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { result.add( UnscheduledTrip diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java similarity index 62% rename from src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java rename to src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java index e0873fab187..643c2bb7b6f 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java @@ -1,15 +1,15 @@ package org.opentripplanner.ext.flex.flexpathcalculator; import javax.annotation.Nullable; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.street.model.vertex.Vertex; -public class DurationFactorCalculator implements FlexPathCalculator { +public class DurationModifierCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; - private final FlexDurationModifier factors; + private final DurationModifier factors; - public DurationFactorCalculator(FlexPathCalculator delegate, FlexDurationModifier factors) { + public DurationModifierCalculator(FlexPathCalculator delegate, DurationModifier factors) { this.delegate = delegate; this.factors = factors; } @@ -22,7 +22,7 @@ public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, i if (path == null) { return null; } else { - return path.withDurationFactors(factors); + return path.withDurationModifier(factors); } } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index 22752726ae7..54c11198b19 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -1,12 +1,14 @@ package org.opentripplanner.ext.flex.flexpathcalculator; import java.util.function.Supplier; +import javax.annotation.concurrent.Immutable; import org.locationtech.jts.geom.LineString; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; /** * This class contains the results from a FlexPathCalculator. */ +@Immutable public class FlexPath { private final Supplier geometrySupplier; @@ -34,8 +36,11 @@ public LineString getGeometry() { return geometry; } - public FlexPath withDurationFactors(FlexDurationModifier factors) { - int updatedDuration = (int) ((durationSeconds * factors.factor()) + factors.offsetInSeconds()); + /** + * Returns an (immutable) copy of this path with the duration modified. + */ + public FlexPath withDurationModifier(DurationModifier mod) { + int updatedDuration = (int) ((durationSeconds * mod.factor()) + mod.offsetInSeconds()); return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java similarity index 79% rename from src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java rename to src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java index 60f795d7280..b9abdd843ff 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java @@ -5,13 +5,13 @@ import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; -public class FlexDurationModifier implements Serializable { +public class DurationModifier implements Serializable { - public static FlexDurationModifier NONE = new FlexDurationModifier(Duration.ZERO, 1); + public static DurationModifier NONE = new DurationModifier(Duration.ZERO, 1); private final int offset; private final float factor; - public FlexDurationModifier(Duration offset, float factor) { + public DurationModifier(Duration offset, float factor) { if (factor < 0.1) { throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); } @@ -38,7 +38,7 @@ boolean modifies() { @Override public String toString() { return ToStringBuilder - .of(FlexDurationModifier.class) + .of(DurationModifier.class) .addNum("factor", factor) .addDurationSec("offset", offset) .toString(); diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index 33a064e610c..553fd2959a5 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -17,7 +17,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.flexpathcalculator.DurationFactorCalculator; +import org.opentripplanner.ext.flex.flexpathcalculator.DurationModifierCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.ScheduledFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; @@ -45,7 +45,7 @@ public class ScheduledDeviatedTrip private final BookingInfo[] dropOffBookingInfos; private final BookingInfo[] pickupBookingInfos; - private final FlexDurationModifier durationModifier; + private final DurationModifier durationModifier; ScheduledDeviatedTrip(ScheduledDeviatedTripBuilder builder) { super(builder); @@ -133,7 +133,7 @@ private FlexPathCalculator getCalculator( stopTimes[fromIndex].timeType == FLEXIBLE_TIME || stopTimes[toIndex].timeType == FLEXIBLE_TIME; if (usesFlexWindow && durationModifier.modifies()) { - return new DurationFactorCalculator(scheduledCalculator, FlexDurationModifier.NONE); + return new DurationModifierCalculator(scheduledCalculator, durationModifier); } else { return scheduledCalculator; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java index 3a33ebd50ec..3bd89ef2e02 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java @@ -8,7 +8,7 @@ public class ScheduledDeviatedTripBuilder extends FlexTripBuilder { private List stopTimes; - private FlexDurationModifier durationModifier = FlexDurationModifier.NONE; + private DurationModifier durationModifier = DurationModifier.NONE; ScheduledDeviatedTripBuilder(FeedScopedId id) { super(id); @@ -30,11 +30,11 @@ public List stopTimes() { return stopTimes; } - public FlexDurationModifier durationModifier() { + public DurationModifier durationModifier() { return durationModifier; } - public ScheduledDeviatedTripBuilder withDurationModifier(FlexDurationModifier modifier) { + public ScheduledDeviatedTripBuilder withDurationModifier(DurationModifier modifier) { this.durationModifier = modifier; return this; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index d85c6b72c27..d8e942d0c98 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -15,7 +15,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.flexpathcalculator.DurationFactorCalculator; +import org.opentripplanner.ext.flex.flexpathcalculator.DurationModifierCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; @@ -52,7 +52,7 @@ public class UnscheduledTrip extends FlexTrip getFlexAccessTemplates( private FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { if (durationModifier.modifies()) { - return new DurationFactorCalculator(calculator, durationModifier); + return new DurationModifierCalculator(calculator, durationModifier); } else { return calculator; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index 7e2132ed036..7b4617048a6 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -8,7 +8,7 @@ public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; - private FlexDurationModifier durationModifier = FlexDurationModifier.NONE; + private DurationModifier durationModifier = DurationModifier.NONE; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -30,12 +30,12 @@ public List stopTimes() { return stopTimes; } - public UnscheduledTripBuilder withDurationModifier(FlexDurationModifier factors) { + public UnscheduledTripBuilder withDurationModifier(DurationModifier factors) { this.durationModifier = factors; return this; } - public FlexDurationModifier durationModifier() { + public DurationModifier durationModifier() { return durationModifier; } diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index 03ef077d319..cc6e44343e6 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -5,7 +5,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.framework.collection.MapUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.timetable.Trip; @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexSafeDurationFactors = new HashMap<>(); + private final Map flexSafeDurationFactors = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,7 +45,7 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexSafeDurationFactors() { + Map flexSafeDurationFactors() { return flexSafeDurationFactors; } @@ -78,16 +78,12 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { return trip; } - private Optional mapSafeDurationFactors( - org.onebusaway.gtfs.model.Trip rhs - ) { + private Optional mapSafeDurationFactors(org.onebusaway.gtfs.model.Trip rhs) { if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { var offset = Duration.ofSeconds(rhs.getSafeDurationOffset().longValue()); - return Optional.of( - new FlexDurationModifier(offset, rhs.getSafeDurationFactor().floatValue()) - ); + return Optional.of(new DurationModifier(offset, rhs.getSafeDurationFactor().floatValue())); } } } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 5811fe042c3..16b7bf22388 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed; @@ -94,7 +94,7 @@ public class OtpTransitServiceBuilder { private final TripStopTimes stopTimesByTrip = new TripStopTimes(); - private final Map flexDurationFactors = new HashMap<>(); + private final Map flexDurationFactors = new HashMap<>(); private final EntityById fareZonesById = new DefaultEntityById<>(); @@ -213,7 +213,7 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } - public Map getFlexDurationFactors() { + public Map getFlexDurationFactors() { return flexDurationFactors; } diff --git a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java index a386d8a27e1..ee110ab4f4f 100644 --- a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java +++ b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java @@ -20,7 +20,7 @@ public class Polygons { } ); - public static Polygon OSLO = FAC.createPolygon( + public static final Polygon OSLO = FAC.createPolygon( new Coordinate[] { Coordinates.of(59.961055202323195, 10.62535658370308), Coordinates.of(59.889009435700416, 10.62535658370308), @@ -29,7 +29,7 @@ public class Polygons { Coordinates.of(59.961055202323195, 10.62535658370308), } ); - public static Polygon OSLO_FROGNER_PARK = FAC.createPolygon( + public static final Polygon OSLO_FROGNER_PARK = FAC.createPolygon( new Coordinate[] { Coordinates.of(59.92939032560119, 10.69770054003061), Coordinates.of(59.929138466684975, 10.695210909925208), From 41b2e505dc14d981fa1c15f4ae203494a7c0a301 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 8 Apr 2024 00:03:09 +0200 Subject: [PATCH 11/20] Extract class for building flex stop times --- .../ext/flex/FlexStopTimesForTest.java | 49 +++++++++++++++++++ .../DurationModifierCalculatorTest.java | 4 +- .../flex/flexpathcalculator/FlexPathTest.java | 6 +-- .../ScheduledFlexPathCalculatorTest.java | 38 ++++++++++++++ .../ext/flex/trip/UnscheduledTripTest.java | 42 +++------------- .../ext/flex/FlexibleTransitLeg.java | 1 + .../_support/geometry/LineStrings.java | 9 ++++ 7 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java create mode 100644 src/test/java/org/opentripplanner/_support/geometry/LineStrings.java diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java new file mode 100644 index 00000000000..d76382999a2 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java @@ -0,0 +1,49 @@ +package org.opentripplanner.ext.flex; + +import static org.opentripplanner.model.StopTime.MISSING_VALUE; + +import org.opentripplanner._support.geometry.Polygons; +import org.opentripplanner.framework.time.TimeUtils; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.site.StopLocation; + +public class FlexStopTimesForTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final StopLocation AREA_STOP = TEST_MODEL.areaStopForTest("area", Polygons.BERLIN); + private static final RegularStop REGULAR_STOP = TEST_MODEL.stop("stop").build(); + + public static StopTime area(String startTime, String endTime) { + return area(AREA_STOP, endTime, startTime); + } + + public static StopTime area(StopLocation areaStop, String endTime, String startTime) { + var stopTime = new StopTime(); + stopTime.setStop(areaStop); + stopTime.setFlexWindowStart(TimeUtils.time(startTime)); + stopTime.setFlexWindowEnd(TimeUtils.time(endTime)); + return stopTime; + } + + public static StopTime regularArrival(String arrivalTime) { + return regularStopTime(TimeUtils.time(arrivalTime), MISSING_VALUE); + } + + public static StopTime regularStopTime(String arrivalTime, String departureTime) { + return regularStopTime(TimeUtils.time(arrivalTime), TimeUtils.time(departureTime)); + } + + public static StopTime regularStopTime(int arrivalTime, int departureTime) { + var stopTime = new StopTime(); + stopTime.setStop(REGULAR_STOP); + stopTime.setArrivalTime(arrivalTime); + stopTime.setDepartureTime(departureTime); + return stopTime; + } + + public static StopTime regularDeparture(String departureTime) { + return regularStopTime(MISSING_VALUE, TimeUtils.time(departureTime)); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java index fd81344893b..f610cbe09c4 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java @@ -5,8 +5,8 @@ import java.time.Duration; import org.junit.jupiter.api.Test; +import org.opentripplanner._support.geometry.LineStrings; import org.opentripplanner.ext.flex.trip.DurationModifier; -import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.street.model._data.StreetModelForTest; class DurationModifierCalculatorTest { @@ -16,7 +16,7 @@ class DurationModifierCalculatorTest { @Test void calculate() { FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> - new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> GeometryUtils.makeLineString(1, 1, 2, 2)); + new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> LineStrings.SIMPLE); var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); var calc = new DurationModifierCalculator(delegate, mod); diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java index 9bee310573b..6a4c79ca2cc 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java @@ -7,18 +7,16 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.locationtech.jts.geom.LineString; +import org.opentripplanner._support.geometry.LineStrings; import org.opentripplanner.ext.flex.trip.DurationModifier; -import org.opentripplanner.framework.geometry.GeometryUtils; class FlexPathTest { private static final int THIRTY_MINS_IN_SECONDS = (int) Duration.ofMinutes(30).toSeconds(); - private static final LineString LINE_STRING = GeometryUtils.makeLineString(1, 1, 2, 2); private static final FlexPath PATH = new FlexPath( 10_000, THIRTY_MINS_IN_SECONDS, - () -> LINE_STRING + () -> LineStrings.SIMPLE ); static List cases() { diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java new file mode 100644 index 00000000000..70f39af2420 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java @@ -0,0 +1,38 @@ +package org.opentripplanner.ext.flex.flexpathcalculator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.area; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularStopTime; +import static org.opentripplanner.street.model._data.StreetModelForTest.V1; +import static org.opentripplanner.street.model._data.StreetModelForTest.V2; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; + +import java.time.Duration; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner._support.geometry.LineStrings; +import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; + +class ScheduledFlexPathCalculatorTest { + + private static final ScheduledDeviatedTrip TRIP = ScheduledDeviatedTrip + .of(id("123")) + .withStopTimes( + List.of( + regularStopTime("10:00", "10:01"), + area("10:10", "10:20"), + regularStopTime("10:25", "10:26"), + area("10:40", "10:50") + ) + ) + .build(); + + @Test + void calculateTime() { + var c = (FlexPathCalculator) (fromv, tov, fromStopIndex, toStopIndex) -> + new FlexPath(10_000, (int) Duration.ofMinutes(10).toSeconds(), () -> LineStrings.SIMPLE); + var calc = new ScheduledFlexPathCalculator(c, TRIP); + var path = calc.calculateFlexPath(V1, V2, 0, 1); + assertEquals(Duration.ofMinutes(19), Duration.ofSeconds(path.durationSeconds)); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java index fabe534ff23..bfcd9bad565 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.area; import static org.opentripplanner.ext.flex.trip.UnscheduledTrip.isUnscheduledTrip; import static org.opentripplanner.ext.flex.trip.UnscheduledTripTest.TestCase.tc; import static org.opentripplanner.model.PickDrop.NONE; @@ -22,6 +23,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner._support.geometry.Polygons; import org.opentripplanner.ext.flex.FlexServiceDate; +import org.opentripplanner.ext.flex.FlexStopTimesForTest; import org.opentripplanner.ext.flex.flexpathcalculator.DirectFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; @@ -48,11 +50,10 @@ class UnscheduledTripTest { private static final int T15_00 = TimeUtils.hm2time(15, 0); private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final StopLocation AREA_STOP = TEST_MODEL.areaStopForTest("area", Polygons.BERLIN); private static final RegularStop REGULAR_STOP = TEST_MODEL.stop("stop").build(); - private static final StopLocation AREA_STOP = TEST_MODEL.areaStopForTest("area", Polygons.BERLIN); - @Nested class IsUnscheduledTrip { @@ -196,7 +197,7 @@ void testUnscheduledFeederTripToScheduledStop() { static Stream testRegularStopToAreaEarliestDepartureTimeTestCases() { // REGULAR-STOP to AREA - (10:00-14:00) => (14:00) - var tc = tc(regularDeparture("10:00"), area("10:00", "14:00")); + var tc = tc(FlexStopTimesForTest.regularDeparture("10:00"), area("10:00", "14:00")); return Stream.of( tc .expected("Requested departure time is before flex service departure time", "10:00") @@ -245,7 +246,7 @@ void testRegularStopToAreaEarliestDepartureTime(TestCase tc) { static Stream testAreaToRegularStopEarliestDepartureTestCases() { // AREA TO REGULAR-STOP - (10:00-14:00) => (14:00) - var tc = tc(area("10:00", "14:00"), regularArrival("14:00")); + var tc = tc(area("10:00", "14:00"), FlexStopTimesForTest.regularArrival("14:00")); return Stream.of( tc .expected( @@ -366,7 +367,7 @@ void testAreaToAreaEarliestDepartureTime(TestCase tc) { static Stream testRegularStopToAreaLatestArrivalTimeTestCases() { // REGULAR-STOP to AREA - (10:00-14:00) => (14:00) - var tc = tc(regularDeparture("10:00"), area("10:00", "14:00")); + var tc = tc(FlexStopTimesForTest.regularDeparture("10:00"), area("10:00", "14:00")); return Stream.of( tc .expectedNotFound("Requested arrival time is before flex service arrival window start") @@ -421,7 +422,7 @@ void testRegularStopToAreaLatestArrivalTime(TestCase tc) { static Stream testAreaToRegularStopLatestArrivalTimeTestCases() { // AREA TO REGULAR-STOP - (10:00-14:00) => (14:00) - var tc = tc(area("10:00", "14:00"), regularArrival("14:00")); + var tc = tc(area("10:00", "14:00"), FlexStopTimesForTest.regularArrival("14:00")); return Stream.of( tc .expectedNotFound("Requested arrival time is before flex service arrival window start") @@ -661,35 +662,6 @@ private static String timeToString(int time) { return TimeUtils.timeToStrCompact(time, MISSING_VALUE, "MISSING_VALUE"); } - private static StopTime area(String startTime, String endTime) { - return area(AREA_STOP, endTime, startTime); - } - - @Nonnull - private static StopTime area(StopLocation areaStop, String endTime, String startTime) { - var stopTime = new StopTime(); - stopTime.setStop(areaStop); - stopTime.setFlexWindowStart(TimeUtils.time(startTime)); - stopTime.setFlexWindowEnd(TimeUtils.time(endTime)); - return stopTime; - } - - private static StopTime regularDeparture(String departureTime) { - return regularStopTime(MISSING_VALUE, TimeUtils.time(departureTime)); - } - - private static StopTime regularArrival(String arrivalTime) { - return regularStopTime(TimeUtils.time(arrivalTime), MISSING_VALUE); - } - - private static StopTime regularStopTime(int arrivalTime, int departureTime) { - var stopTime = new StopTime(); - stopTime.setStop(REGULAR_STOP); - stopTime.setArrivalTime(arrivalTime); - stopTime.setDepartureTime(departureTime); - return stopTime; - } - @Nonnull private static NearbyStop nearbyStop(AreaStop stop) { return new NearbyStop(stop, 1000, List.of(), null); diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java b/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java index fac1118556f..b9e10e29214 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java @@ -196,6 +196,7 @@ public int getGeneralizedCost() { return generalizedCost; } + @Override public void addAlert(TransitAlert alert) { transitAlerts.add(alert); } diff --git a/src/test/java/org/opentripplanner/_support/geometry/LineStrings.java b/src/test/java/org/opentripplanner/_support/geometry/LineStrings.java new file mode 100644 index 00000000000..515f161be92 --- /dev/null +++ b/src/test/java/org/opentripplanner/_support/geometry/LineStrings.java @@ -0,0 +1,9 @@ +package org.opentripplanner._support.geometry; + +import org.locationtech.jts.geom.LineString; +import org.opentripplanner.framework.geometry.GeometryUtils; + +public class LineStrings { + + public static final LineString SIMPLE = GeometryUtils.makeLineString(0, 0, 1, 1); +} From dec30cd3c95d788e2bb7691ec2ae73c611adcc00 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Apr 2024 09:57:06 +0200 Subject: [PATCH 12/20] Remove durationModifier to ScheduledDeviatedTrip --- .../ext/flex/FlexTripsMapper.java | 3 +-- .../ext/flex/trip/ScheduledDeviatedTrip.java | 27 ++----------------- .../trip/ScheduledDeviatedTripBuilder.java | 10 ------- 3 files changed, 3 insertions(+), 37 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index e6cad4d3e40..b4123a3b556 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -35,8 +35,8 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); - var modifier = builder.getFlexDurationFactors().getOrDefault(trip, DurationModifier.NONE); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { + var modifier = builder.getFlexDurationFactors().getOrDefault(trip, DurationModifier.NONE); result.add( UnscheduledTrip .of(trip.getId()) @@ -51,7 +51,6 @@ public class FlexTripsMapper { .of(trip.getId()) .withTrip(trip) .withStopTimes(stopTimes) - .withDurationModifier(modifier) .build() ); } else if (hasContinuousStops(stopTimes) && FlexTrip.containsFlexStops(stopTimes)) { diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index 553fd2959a5..84982c837dc 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -17,7 +17,6 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.flexpathcalculator.DurationModifierCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.ScheduledFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; @@ -45,8 +44,6 @@ public class ScheduledDeviatedTrip private final BookingInfo[] dropOffBookingInfos; private final BookingInfo[] pickupBookingInfos; - private final DurationModifier durationModifier; - ScheduledDeviatedTrip(ScheduledDeviatedTripBuilder builder) { super(builder); List stopTimes = builder.stopTimes(); @@ -64,7 +61,6 @@ public class ScheduledDeviatedTrip this.dropOffBookingInfos[i] = stopTimes.get(i).getDropOffBookingInfo(); this.pickupBookingInfos[i] = stopTimes.get(i).getPickupBookingInfo(); } - this.durationModifier = builder.durationModifier(); } public static ScheduledDeviatedTripBuilder of(FeedScopedId id) { @@ -110,7 +106,7 @@ public Stream getFlexAccessTemplates( toIndex, stop, date, - getCalculator(fromIndex, toIndex, scheduledCalculator), + scheduledCalculator, config ) ); @@ -120,25 +116,6 @@ public Stream getFlexAccessTemplates( return res.stream(); } - /** - * If any of the stops involved in the path computation, then apply the duration factors. - * If both from and to have a fixed time, then don't apply the factors. - */ - private FlexPathCalculator getCalculator( - int fromIndex, - int toIndex, - FlexPathCalculator scheduledCalculator - ) { - final boolean usesFlexWindow = - stopTimes[fromIndex].timeType == FLEXIBLE_TIME || - stopTimes[toIndex].timeType == FLEXIBLE_TIME; - if (usesFlexWindow && durationModifier.modifies()) { - return new DurationModifierCalculator(scheduledCalculator, durationModifier); - } else { - return scheduledCalculator; - } - } - @Override public Stream getFlexEgressTemplates( NearbyStop egress, @@ -169,7 +146,7 @@ public Stream getFlexEgressTemplates( toIndex, stop, date, - getCalculator(fromIndex, toIndex, scheduledCalculator), + scheduledCalculator, config ) ); diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java index 3bd89ef2e02..4033bbe7c59 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java @@ -8,7 +8,6 @@ public class ScheduledDeviatedTripBuilder extends FlexTripBuilder { private List stopTimes; - private DurationModifier durationModifier = DurationModifier.NONE; ScheduledDeviatedTripBuilder(FeedScopedId id) { super(id); @@ -30,15 +29,6 @@ public List stopTimes() { return stopTimes; } - public DurationModifier durationModifier() { - return durationModifier; - } - - public ScheduledDeviatedTripBuilder withDurationModifier(DurationModifier modifier) { - this.durationModifier = modifier; - return this; - } - @Override ScheduledDeviatedTripBuilder instance() { return this; From 354c96839897497b91615971e40775b5d3e3fafa Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Apr 2024 12:37:01 +0200 Subject: [PATCH 13/20] Add test and docs for DurationModifier --- .../ext/flex/trip/DurationModifierTest.java | 20 +++++++++++++++++++ .../ext/flex/trip/DurationModifier.java | 7 +++++++ 2 files changed, 27 insertions(+) create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java new file mode 100644 index 00000000000..c6f4e75f7c5 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java @@ -0,0 +1,20 @@ +package org.opentripplanner.ext.flex.trip; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import org.junit.jupiter.api.Test; + +class DurationModifierTest { + + @Test + void doesNotModify() { + assertFalse(DurationModifier.NONE.modifies()); + } + + @Test + void modifies() { + assertTrue(new DurationModifier(Duration.ofMinutes(1), 1.5f).modifies()); + } +} \ No newline at end of file diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java index b9abdd843ff..4832a7cc9bc 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java @@ -5,12 +5,19 @@ import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; +/** + * A modifier to influence the A*-calculated driving time of flex trips. + */ public class DurationModifier implements Serializable { public static DurationModifier NONE = new DurationModifier(Duration.ZERO, 1); private final int offset; private final float factor; + /** + * @param offset A fixed offset to add to the driving time. + * @param factor A factor to multiply the driving time with. + */ public DurationModifier(Duration offset, float factor) { if (factor < 0.1) { throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); From 991406ed25845660bd0a25e972e86c66227b7409 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Apr 2024 13:42:44 +0200 Subject: [PATCH 14/20] Cleanup, tests and documentation --- doc-templates/Flex.md | 4 +- docs/sandbox/Flex.md | 4 +- .../flex/flexpathcalculator/FlexPathTest.java | 1 + .../ext/flex/trip/DurationModifierTest.java | 2 +- .../trip/UnscheduledDrivingDurationTest.java | 44 ++++++++++++++++++ .../ext/flex/trip/UnscheduledTripTest.java | 11 ++--- .../ext/flex/FlexTripsMapper.java | 6 +-- .../DurationModifierCalculator.java | 4 ++ .../ext/flex/trip/ScheduledDeviatedTrip.java | 10 ----- .../ext/flex/trip/UnscheduledTrip.java | 6 ++- .../GTFSToOtpTransitServiceMapper.java | 2 +- .../gtfs/mapping/TripMapper.java | 10 ++--- .../gtfs/mapping/TripMapperTest.java | 45 ++++++++++++++----- 13 files changed, 107 insertions(+), 42 deletions(-) create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java diff --git a/doc-templates/Flex.md b/doc-templates/Flex.md index 2015e898cae..50512f66133 100644 --- a/doc-templates/Flex.md +++ b/doc-templates/Flex.md @@ -10,8 +10,8 @@ To enable this turn on `FlexRouting` as a feature in `otp-config.json`. -The GTFS feeds should conform to the -[GTFS-Flex v2 draft PR](https://github.com/google/transit/pull/388) +The GTFS feeds must conform to the final, approved version of the draft which has been +merged into the [mainline specification](https://gtfs.org/schedule/reference/) in March 2024. ## Configuration diff --git a/docs/sandbox/Flex.md b/docs/sandbox/Flex.md index 61d15851a56..277e4e617f2 100644 --- a/docs/sandbox/Flex.md +++ b/docs/sandbox/Flex.md @@ -10,8 +10,8 @@ To enable this turn on `FlexRouting` as a feature in `otp-config.json`. -The GTFS feeds should conform to the -[GTFS-Flex v2 draft PR](https://github.com/google/transit/pull/388) +The GTFS feeds must conform to the final, approved version of the draft which has been +merged into the [mainline specification](https://gtfs.org/schedule/reference/) in March 2024. ## Configuration diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java index 6a4c79ca2cc..b79093e26c5 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java @@ -33,5 +33,6 @@ static List cases() { void calculate(DurationModifier mod, int expectedSeconds) { var modified = PATH.withDurationModifier(mod); assertEquals(expectedSeconds, modified.durationSeconds); + assertEquals(LineStrings.SIMPLE, modified.getGeometry()); } } diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java index c6f4e75f7c5..05fb9965935 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java @@ -17,4 +17,4 @@ void doesNotModify() { void modifies() { assertTrue(new DurationModifier(Duration.ofMinutes(1), 1.5f).modifies()); } -} \ No newline at end of file +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java new file mode 100644 index 00000000000..45d8c5be624 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java @@ -0,0 +1,44 @@ +package org.opentripplanner.ext.flex.trip; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.street.model._data.StreetModelForTest.V1; +import static org.opentripplanner.street.model._data.StreetModelForTest.V2; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; + +import java.time.Duration; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner._support.geometry.LineStrings; +import org.opentripplanner.ext.flex.FlexStopTimesForTest; +import org.opentripplanner.ext.flex.flexpathcalculator.FlexPath; +import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; +import org.opentripplanner.model.StopTime; + +class UnscheduledDrivingDurationTest { + + static final FlexPathCalculator STATIC_CALCULATOR = (fromv, tov, fromStopIndex, toStopIndex) -> + new FlexPath(10_000, (int) Duration.ofMinutes(10).toSeconds(), () -> LineStrings.SIMPLE); + private static final StopTime STOP_TIME = FlexStopTimesForTest.area("10:00", "18:00"); + + @Test + void noModifier() { + var trip = UnscheduledTrip.of(id("1")).withStopTimes(List.of(STOP_TIME)).build(); + + var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); + var path = calculator.calculateFlexPath(V1, V2, 0, 0); + assertEquals(600, path.durationSeconds); + } + + @Test + void withModifier() { + var trip = UnscheduledTrip + .of(id("1")) + .withStopTimes(List.of(STOP_TIME)) + .withDurationModifier(new DurationModifier(Duration.ofMinutes(2), 1.5f)) + .build(); + + var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); + var path = calculator.calculateFlexPath(V1, V2, 0, 0); + assertEquals(1020, path.durationSeconds); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java index bfcd9bad565..80bda31fabf 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.ext.flex.FlexStopTimesForTest.area; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularArrival; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularDeparture; import static org.opentripplanner.ext.flex.trip.UnscheduledTrip.isUnscheduledTrip; import static org.opentripplanner.ext.flex.trip.UnscheduledTripTest.TestCase.tc; import static org.opentripplanner.model.PickDrop.NONE; @@ -23,7 +25,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner._support.geometry.Polygons; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.FlexStopTimesForTest; import org.opentripplanner.ext.flex.flexpathcalculator.DirectFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; @@ -197,7 +198,7 @@ void testUnscheduledFeederTripToScheduledStop() { static Stream testRegularStopToAreaEarliestDepartureTimeTestCases() { // REGULAR-STOP to AREA - (10:00-14:00) => (14:00) - var tc = tc(FlexStopTimesForTest.regularDeparture("10:00"), area("10:00", "14:00")); + var tc = tc(regularDeparture("10:00"), area("10:00", "14:00")); return Stream.of( tc .expected("Requested departure time is before flex service departure time", "10:00") @@ -246,7 +247,7 @@ void testRegularStopToAreaEarliestDepartureTime(TestCase tc) { static Stream testAreaToRegularStopEarliestDepartureTestCases() { // AREA TO REGULAR-STOP - (10:00-14:00) => (14:00) - var tc = tc(area("10:00", "14:00"), FlexStopTimesForTest.regularArrival("14:00")); + var tc = tc(area("10:00", "14:00"), regularArrival("14:00")); return Stream.of( tc .expected( @@ -367,7 +368,7 @@ void testAreaToAreaEarliestDepartureTime(TestCase tc) { static Stream testRegularStopToAreaLatestArrivalTimeTestCases() { // REGULAR-STOP to AREA - (10:00-14:00) => (14:00) - var tc = tc(FlexStopTimesForTest.regularDeparture("10:00"), area("10:00", "14:00")); + var tc = tc(regularDeparture("10:00"), area("10:00", "14:00")); return Stream.of( tc .expectedNotFound("Requested arrival time is before flex service arrival window start") @@ -422,7 +423,7 @@ void testRegularStopToAreaLatestArrivalTime(TestCase tc) { static Stream testAreaToRegularStopLatestArrivalTimeTestCases() { // AREA TO REGULAR-STOP - (10:00-14:00) => (14:00) - var tc = tc(area("10:00", "14:00"), FlexStopTimesForTest.regularArrival("14:00")); + var tc = tc(area("10:00", "14:00"), regularArrival("14:00")); return Stream.of( tc .expectedNotFound("Requested arrival time is before flex service arrival window start") diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index b4123a3b556..bb2ed2764f6 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -47,11 +47,7 @@ public class FlexTripsMapper { ); } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { result.add( - ScheduledDeviatedTrip - .of(trip.getId()) - .withTrip(trip) - .withStopTimes(stopTimes) - .build() + ScheduledDeviatedTrip.of(trip.getId()).withTrip(trip).withStopTimes(stopTimes).build() ); } else if (hasContinuousStops(stopTimes) && FlexTrip.containsFlexStops(stopTimes)) { store.add( diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java index 643c2bb7b6f..158e6a933c7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java @@ -4,6 +4,10 @@ import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.street.model.vertex.Vertex; +/** + * A calculator to delegates the main computation to another instance and applies a duration + * modifier afterward. + */ public class DurationModifierCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index 84982c837dc..0026d98c964 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -1,7 +1,5 @@ package org.opentripplanner.ext.flex.trip; -import static org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip.TimeType.FIXED_TIME; -import static org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip.TimeType.FLEXIBLE_TIME; import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; @@ -299,13 +297,10 @@ private static class ScheduledDeviatedStopTime implements Serializable { private final int arrivalTime; private final PickDrop pickupType; private final PickDrop dropOffType; - private final TimeType timeType; private ScheduledDeviatedStopTime(StopTime st) { this.stop = st.getStop(); - this.timeType = st.hasFlexWindow() ? FLEXIBLE_TIME : FIXED_TIME; - // Store the time the user is guaranteed to arrive at latest this.arrivalTime = st.getLatestPossibleArrivalTime(); // Store the time the user needs to be ready for pickup @@ -320,9 +315,4 @@ private ScheduledDeviatedStopTime(StopTime st) { this.dropOffType = arrivalTime == MISSING_VALUE ? PickDrop.NONE : st.getDropOffType(); } } - - enum TimeType { - FIXED_TIME, - FLEXIBLE_TIME, - } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index d8e942d0c98..bc3bd52cc4c 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -149,7 +149,11 @@ public Stream getFlexAccessTemplates( ); } - private FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { + /** + * Get the correct {@link FlexPathCalculator} depending on the {@code durationModified}. + * If the modifier doesn't actually modify, we don't + */ + protected FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { if (durationModifier.modifies()) { return new DurationModifierCalculator(calculator, durationModifier); } else { diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index 4ee1cdcf1d7..24db367f829 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -171,7 +171,7 @@ public void mapStopTripAndRouteDataIntoBuilder() { builder.getPathways().addAll(pathwayMapper.map(data.getAllPathways())); builder.getStopTimesSortedByTrip().addAll(stopTimeMapper.map(data.getAllStopTimes())); - builder.getFlexDurationFactors().putAll(tripMapper.flexSafeDurationFactors()); + builder.getFlexDurationFactors().putAll(tripMapper.flexSafeDurationModifiers()); builder.getTripsById().addAll(tripMapper.map(data.getAllTrips())); fareRulesBuilder.fareAttributes().addAll(fareAttributeMapper.map(data.getAllFareAttributes())); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index cc6e44343e6..ec7d1379ca0 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexSafeDurationFactors = new HashMap<>(); + private final Map flexSafeDurationModifiers = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,8 +45,8 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexSafeDurationFactors() { - return flexSafeDurationFactors; + Map flexSafeDurationModifiers() { + return flexSafeDurationModifiers; } private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { @@ -74,11 +74,11 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { lhs.withGtfsFareId(rhs.getFareId()); var trip = lhs.build(); - mapSafeDurationFactors(rhs).ifPresent(f -> flexSafeDurationFactors.put(trip, f)); + mapSafeDurationModifier(rhs).ifPresent(f -> flexSafeDurationModifiers.put(trip, f)); return trip; } - private Optional mapSafeDurationFactors(org.onebusaway.gtfs.model.Trip rhs) { + private Optional mapSafeDurationModifier(org.onebusaway.gtfs.model.Trip rhs) { if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java index 4f0c70f22d2..141c1d5bf6b 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java @@ -33,11 +33,15 @@ public class TripMapperTest { public static final DataImportIssueStore ISSUE_STORE = DataImportIssueStore.NOOP; - private final TripMapper subject = new TripMapper( - new RouteMapper(new AgencyMapper(FEED_ID), ISSUE_STORE, new TranslationHelper()), - new DirectionMapper(ISSUE_STORE), - new TranslationHelper() - ); + private final TripMapper subject = defaultTripMapper(); + + private static TripMapper defaultTripMapper() { + return new TripMapper( + new RouteMapper(new AgencyMapper(FEED_ID), ISSUE_STORE, new TranslationHelper()), + new DirectionMapper(ISSUE_STORE), + new TranslationHelper() + ); + } static { GtfsTestData data = new GtfsTestData(); @@ -56,14 +60,14 @@ public class TripMapperTest { } @Test - public void testMapCollection() throws Exception { + void testMapCollection() throws Exception { assertNull(subject.map((Collection) null)); assertTrue(subject.map(Collections.emptyList()).isEmpty()); assertEquals(1, subject.map(Collections.singleton(TRIP)).size()); } @Test - public void testMap() throws Exception { + void testMap() throws Exception { org.opentripplanner.transit.model.timetable.Trip result = subject.map(TRIP); assertEquals("A:1", result.getId().toString()); @@ -80,7 +84,7 @@ public void testMap() throws Exception { } @Test - public void testMapWithNulls() throws Exception { + void testMapWithNulls() throws Exception { Trip input = new Trip(); input.setId(AGENCY_AND_ID); input.setRoute(new GtfsTestData().route); @@ -101,12 +105,33 @@ public void testMapWithNulls() throws Exception { assertEquals(BikeAccess.UNKNOWN, result.getBikesAllowed()); } - /** Mapping the same object twice, should return the the same instance. */ + /** Mapping the same object twice, should return the same instance. */ @Test - public void testMapCache() throws Exception { + void testMapCache() throws Exception { org.opentripplanner.transit.model.timetable.Trip result1 = subject.map(TRIP); org.opentripplanner.transit.model.timetable.Trip result2 = subject.map(TRIP); assertSame(result1, result2); } + + @Test + void noFlexDurationModifier() { + var mapper = defaultTripMapper(); + mapper.map(TRIP); + assertTrue(mapper.flexSafeDurationModifiers().isEmpty()); + } + + @Test + void flexDurationModifier() { + var flexTrip = new Trip(); + flexTrip.setId(new AgencyAndId("1", "1")); + flexTrip.setSafeDurationFactor(1.5); + flexTrip.setSafeDurationOffset(600d); + flexTrip.setRoute(new GtfsTestData().route); + var mapper = defaultTripMapper(); + var mapped = mapper.map(flexTrip); + var mod = mapper.flexSafeDurationModifiers().get(mapped); + assertEquals(1.5f, mod.factor()); + assertEquals(600, mod.offsetInSeconds()); + } } From 6c7d95383a2fe297c4a2ca690dcc65b971b68a37 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 18 Apr 2024 16:32:04 +0200 Subject: [PATCH 15/20] Replace DurationModifier with TimePenalty --- .../DurationModifierCalculatorTest.java | 6 +-- .../flex/flexpathcalculator/FlexPathTest.java | 12 ++--- .../ext/flex/trip/DurationModifierTest.java | 20 ------- .../trip/UnscheduledDrivingDurationTest.java | 3 +- .../ext/flex/FlexTripsMapper.java | 4 +- .../DurationModifierCalculator.java | 8 +-- .../ext/flex/flexpathcalculator/FlexPath.java | 16 ++++-- .../ext/flex/trip/DurationModifier.java | 53 ------------------- .../ext/flex/trip/UnscheduledTrip.java | 5 +- .../ext/flex/trip/UnscheduledTripBuilder.java | 7 +-- .../gtfs/mapping/TripMapper.java | 10 ++-- .../model/impl/OtpTransitServiceBuilder.java | 6 +-- .../gtfs/mapping/TripMapperTest.java | 4 +- 13 files changed, 45 insertions(+), 109 deletions(-) delete mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java delete mode 100644 src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java index f610cbe09c4..a97460dda75 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java @@ -6,7 +6,7 @@ import java.time.Duration; import org.junit.jupiter.api.Test; import org.opentripplanner._support.geometry.LineStrings; -import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.street.model._data.StreetModelForTest; class DurationModifierCalculatorTest { @@ -18,7 +18,7 @@ void calculate() { FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> LineStrings.SIMPLE); - var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); + var mod = TimePenalty.of(Duration.ofMinutes(10), 1.5f); var calc = new DurationModifierCalculator(delegate, mod); var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); assertEquals(3300, path.durationSeconds); @@ -27,7 +27,7 @@ void calculate() { @Test void nullValue() { FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> null; - var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); + var mod = TimePenalty.of(Duration.ofMinutes(10), 1.5f); var calc = new DurationModifierCalculator(delegate, mod); var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); assertNull(path); diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java index b79093e26c5..8bd3abee785 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner._support.geometry.LineStrings; -import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.routing.api.request.framework.TimePenalty; class FlexPathTest { @@ -21,16 +21,16 @@ class FlexPathTest { static List cases() { return List.of( - Arguments.of(DurationModifier.NONE, THIRTY_MINS_IN_SECONDS), - Arguments.of(new DurationModifier(Duration.ofMinutes(10), 1), 2400), - Arguments.of(new DurationModifier(Duration.ofMinutes(10), 1.5f), 3300), - Arguments.of(new DurationModifier(Duration.ZERO, 3), 5400) + Arguments.of(TimePenalty.ZERO, THIRTY_MINS_IN_SECONDS), + Arguments.of(TimePenalty.of(Duration.ofMinutes(10), 1), 2400), + Arguments.of(TimePenalty.of(Duration.ofMinutes(10), 1.5f), 3300), + Arguments.of(TimePenalty.of(Duration.ZERO, 3), 5400) ); } @ParameterizedTest @MethodSource("cases") - void calculate(DurationModifier mod, int expectedSeconds) { + void calculate(TimePenalty mod, int expectedSeconds) { var modified = PATH.withDurationModifier(mod); assertEquals(expectedSeconds, modified.durationSeconds); assertEquals(LineStrings.SIMPLE, modified.getGeometry()); diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java deleted file mode 100644 index 05fb9965935..00000000000 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opentripplanner.ext.flex.trip; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.time.Duration; -import org.junit.jupiter.api.Test; - -class DurationModifierTest { - - @Test - void doesNotModify() { - assertFalse(DurationModifier.NONE.modifies()); - } - - @Test - void modifies() { - assertTrue(new DurationModifier(Duration.ofMinutes(1), 1.5f).modifies()); - } -} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java index 45d8c5be624..bd06a51960b 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java @@ -13,6 +13,7 @@ import org.opentripplanner.ext.flex.flexpathcalculator.FlexPath; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.model.StopTime; +import org.opentripplanner.routing.api.request.framework.TimePenalty; class UnscheduledDrivingDurationTest { @@ -34,7 +35,7 @@ void withModifier() { var trip = UnscheduledTrip .of(id("1")) .withStopTimes(List.of(STOP_TIME)) - .withDurationModifier(new DurationModifier(Duration.ofMinutes(2), 1.5f)) + .withDurationModifier(TimePenalty.of(Duration.ofMinutes(2), 1.5f)) .build(); var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index bb2ed2764f6..76a13255b67 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; -import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; @@ -13,6 +12,7 @@ import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TripStopTimes; import org.opentripplanner.model.impl.OtpTransitServiceBuilder; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.transit.model.timetable.Trip; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +36,7 @@ public class FlexTripsMapper { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { - var modifier = builder.getFlexDurationFactors().getOrDefault(trip, DurationModifier.NONE); + var modifier = builder.getFlexDurationFactors().getOrDefault(trip, TimePenalty.ZERO); result.add( UnscheduledTrip .of(trip.getId()) diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java index 158e6a933c7..0f521b55b1f 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java @@ -1,7 +1,7 @@ package org.opentripplanner.ext.flex.flexpathcalculator; import javax.annotation.Nullable; -import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.street.model.vertex.Vertex; /** @@ -11,11 +11,11 @@ public class DurationModifierCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; - private final DurationModifier factors; + private final TimePenalty factors; - public DurationModifierCalculator(FlexPathCalculator delegate, DurationModifier factors) { + public DurationModifierCalculator(FlexPathCalculator delegate, TimePenalty penalty) { this.delegate = delegate; - this.factors = factors; + this.factors = penalty; } @Nullable diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index 54c11198b19..3a692dff40b 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -1,9 +1,11 @@ package org.opentripplanner.ext.flex.flexpathcalculator; +import java.time.Duration; import java.util.function.Supplier; import javax.annotation.concurrent.Immutable; import org.locationtech.jts.geom.LineString; -import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.framework.lang.IntUtils; +import org.opentripplanner.routing.api.request.framework.TimePenalty; /** * This class contains the results from a FlexPathCalculator. @@ -25,7 +27,7 @@ public class FlexPath { */ public FlexPath(int distanceMeters, int durationSeconds, Supplier geometrySupplier) { this.distanceMeters = distanceMeters; - this.durationSeconds = durationSeconds; + this.durationSeconds = IntUtils.requireNotNegative(durationSeconds); this.geometrySupplier = geometrySupplier; } @@ -39,8 +41,12 @@ public LineString getGeometry() { /** * Returns an (immutable) copy of this path with the duration modified. */ - public FlexPath withDurationModifier(DurationModifier mod) { - int updatedDuration = (int) ((durationSeconds * mod.factor()) + mod.offsetInSeconds()); - return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); + public FlexPath withDurationModifier(TimePenalty mod) { + if (mod.isZero()) { + return this; + } else { + int updatedDuration = (int) mod.calculate(Duration.ofSeconds(durationSeconds)).toSeconds(); + return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); + } } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java deleted file mode 100644 index 4832a7cc9bc..00000000000 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.opentripplanner.ext.flex.trip; - -import java.io.Serializable; -import java.time.Duration; -import org.opentripplanner.framework.time.DurationUtils; -import org.opentripplanner.framework.tostring.ToStringBuilder; - -/** - * A modifier to influence the A*-calculated driving time of flex trips. - */ -public class DurationModifier implements Serializable { - - public static DurationModifier NONE = new DurationModifier(Duration.ZERO, 1); - private final int offset; - private final float factor; - - /** - * @param offset A fixed offset to add to the driving time. - * @param factor A factor to multiply the driving time with. - */ - public DurationModifier(Duration offset, float factor) { - if (factor < 0.1) { - throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); - } - this.offset = (int) DurationUtils.requireNonNegative(offset).toSeconds(); - this.factor = factor; - } - - public float factor() { - return factor; - } - - public int offsetInSeconds() { - return offset; - } - - /** - * Check if this instance actually modifies the duration or simply passes it back without - * change. - */ - boolean modifies() { - return offset != 0 && factor != 1.0; - } - - @Override - public String toString() { - return ToStringBuilder - .of(DurationModifier.class) - .addNum("factor", factor) - .addDurationSec("offset", offset) - .toString(); - } -} diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index bc3bd52cc4c..db662a5fd45 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -23,6 +23,7 @@ import org.opentripplanner.model.BookingInfo; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.routing.graphfinder.NearbyStop; import org.opentripplanner.standalone.config.sandbox.FlexConfig; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -52,7 +53,7 @@ public class UnscheduledTrip extends FlexTrip getFlexAccessTemplates( * If the modifier doesn't actually modify, we don't */ protected FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { - if (durationModifier.modifies()) { + if (!durationModifier.isZero()) { return new DurationModifierCalculator(calculator, durationModifier); } else { return calculator; diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index 7b4617048a6..cfffebe4fa7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -2,13 +2,14 @@ import java.util.List; import org.opentripplanner.model.StopTime; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.transit.model.framework.FeedScopedId; public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; - private DurationModifier durationModifier = DurationModifier.NONE; + private TimePenalty durationModifier = TimePenalty.ZERO; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -30,12 +31,12 @@ public List stopTimes() { return stopTimes; } - public UnscheduledTripBuilder withDurationModifier(DurationModifier factors) { + public UnscheduledTripBuilder withDurationModifier(TimePenalty factors) { this.durationModifier = factors; return this; } - public DurationModifier durationModifier() { + public TimePenalty durationModifier() { return durationModifier; } diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index ec7d1379ca0..46160fa5daa 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -5,9 +5,9 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.framework.collection.MapUtils; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.transit.model.timetable.Trip; /** Responsible for mapping GTFS TripMapper into the OTP model. */ @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexSafeDurationModifiers = new HashMap<>(); + private final Map flexSafeDurationModifiers = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,7 +45,7 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexSafeDurationModifiers() { + Map flexSafeDurationModifiers() { return flexSafeDurationModifiers; } @@ -78,12 +78,12 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { return trip; } - private Optional mapSafeDurationModifier(org.onebusaway.gtfs.model.Trip rhs) { + private Optional mapSafeDurationModifier(org.onebusaway.gtfs.model.Trip rhs) { if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { var offset = Duration.ofSeconds(rhs.getSafeDurationOffset().longValue()); - return Optional.of(new DurationModifier(offset, rhs.getSafeDurationFactor().floatValue())); + return Optional.of(TimePenalty.of(offset, rhs.getSafeDurationFactor().doubleValue())); } } } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 16b7bf22388..756a1ccf2a8 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed; @@ -24,6 +23,7 @@ import org.opentripplanner.model.calendar.impl.CalendarServiceDataFactoryImpl; import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.model.transfer.TransferPoint; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.DefaultEntityById; @@ -94,7 +94,7 @@ public class OtpTransitServiceBuilder { private final TripStopTimes stopTimesByTrip = new TripStopTimes(); - private final Map flexDurationFactors = new HashMap<>(); + private final Map flexDurationFactors = new HashMap<>(); private final EntityById fareZonesById = new DefaultEntityById<>(); @@ -213,7 +213,7 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } - public Map getFlexDurationFactors() { + public Map getFlexDurationFactors() { return flexDurationFactors; } diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java index 141c1d5bf6b..9424e44364b 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java @@ -131,7 +131,7 @@ void flexDurationModifier() { var mapper = defaultTripMapper(); var mapped = mapper.map(flexTrip); var mod = mapper.flexSafeDurationModifiers().get(mapped); - assertEquals(1.5f, mod.factor()); - assertEquals(600, mod.offsetInSeconds()); + assertEquals(1.5f, mod.coefficient()); + assertEquals(600, mod.constant().toSeconds()); } } From 355b287cb844464379c592ba94f8b6ecc850282e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 18 Apr 2024 16:56:31 +0200 Subject: [PATCH 16/20] Update documentation --- .../org/opentripplanner/ext/flex/trip/UnscheduledTrip.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index db662a5fd45..7fa31c78114 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -151,8 +151,8 @@ public Stream getFlexAccessTemplates( } /** - * Get the correct {@link FlexPathCalculator} depending on the {@code durationModified}. - * If the modifier doesn't actually modify, we don't + * Get the correct {@link FlexPathCalculator} depending on the {@code durationModifier}. + * If the modifier doesn't actually modify, we return the regular calculator. */ protected FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { if (!durationModifier.isZero()) { From 90148e089134c2289f1d6310bb50cf1645f0d000 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 17:41:19 +0200 Subject: [PATCH 17/20] Update docs about experimental fields --- doc-templates/Flex.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc-templates/Flex.md b/doc-templates/Flex.md index 50512f66133..0e79ecdbffc 100644 --- a/doc-templates/Flex.md +++ b/doc-templates/Flex.md @@ -13,6 +13,17 @@ To enable this turn on `FlexRouting` as a feature in `otp-config.json`. The GTFS feeds must conform to the final, approved version of the draft which has been merged into the [mainline specification](https://gtfs.org/schedule/reference/) in March 2024. +### Experimental features + +This sandbox feature also has experimental support for the following fields: + +- `safe_duration_factor` +- `safe_duration_offset` + +These features are currently [undergoing specification](https://github.com/MobilityData/gtfs-flex/pull/79) +and their definition might change. OTP's implementation will be also be changed so be careful +when relying on this feature. + ## Configuration This feature allows a limited number of config options. To change the configuration, add the From a4dc25b7d635ec872e4843d23d7ab1533f662249 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 17:46:16 +0200 Subject: [PATCH 18/20] Revert renaming --- docs/sandbox/Flex.md | 11 +++++++++++ .../ext/flex/trip/ScheduledDeviatedTrip.java | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/sandbox/Flex.md b/docs/sandbox/Flex.md index 277e4e617f2..7b08fd7d05f 100644 --- a/docs/sandbox/Flex.md +++ b/docs/sandbox/Flex.md @@ -13,6 +13,17 @@ To enable this turn on `FlexRouting` as a feature in `otp-config.json`. The GTFS feeds must conform to the final, approved version of the draft which has been merged into the [mainline specification](https://gtfs.org/schedule/reference/) in March 2024. +### Experimental features + +This sandbox feature also has experimental support for the following fields: + +- `safe_duration_factor` +- `safe_duration_offset` + +These features are currently [undergoing specification](https://github.com/MobilityData/gtfs-flex/pull/79) +and their definition might change. OTP's implementation will be also be changed so be careful +when relying on this feature. + ## Configuration This feature allows a limited number of config options. To change the configuration, add the diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index 0026d98c964..e16e1e5e1f7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -176,7 +176,7 @@ public int earliestDepartureTime(int stopIndex) { @Override public int latestArrivalTime( int arrivalTime, - int ignored, + int fromStopIndex, int toStopIndex, int flexTripDurationSeconds ) { From ea679755a77beb05536b5c163f174141f62fbd72 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 8 May 2024 13:02:43 +0200 Subject: [PATCH 19/20] Remove extra collection conversion --- src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 76a13255b67..5347e7504c5 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -33,8 +33,7 @@ public class FlexTripsMapper { ProgressTracker progress = ProgressTracker.track("Create flex trips", 500, tripSize); for (Trip trip : stopTimesByTrip.keys()) { - /* Fetch the stop times for this trip. Copy the list since it's immutable. */ - List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); + var stopTimes = stopTimesByTrip.get(trip); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { var modifier = builder.getFlexDurationFactors().getOrDefault(trip, TimePenalty.ZERO); result.add( From 695161a86c82dbbeed34140417d071df55823191 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 8 May 2024 13:25:53 +0200 Subject: [PATCH 20/20] Rename modifiers/factors to time penalty --- ...rTest.java => TimePenaltyCalculatorTest.java} | 6 +++--- .../trip/UnscheduledDrivingDurationTest.java | 6 +++--- .../ext/flex/FlexTripsMapper.java | 4 ++-- ...alculator.java => TimePenaltyCalculator.java} | 4 ++-- .../ext/flex/trip/UnscheduledTrip.java | 16 ++++++++++------ .../ext/flex/trip/UnscheduledTripBuilder.java | 10 +++++----- .../mapping/GTFSToOtpTransitServiceMapper.java | 2 +- .../model/impl/OtpTransitServiceBuilder.java | 2 +- .../api/request/framework/TimePenalty.java | 8 ++++++++ .../api/request/framework/TimePenaltyTest.java | 7 +++++++ 10 files changed, 42 insertions(+), 23 deletions(-) rename src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/{DurationModifierCalculatorTest.java => TimePenaltyCalculatorTest.java} (88%) rename src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/{DurationModifierCalculator.java => TimePenaltyCalculator.java} (83%) diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculatorTest.java similarity index 88% rename from src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java rename to src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculatorTest.java index a97460dda75..e504f8ac762 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculatorTest.java @@ -9,7 +9,7 @@ import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.street.model._data.StreetModelForTest; -class DurationModifierCalculatorTest { +class TimePenaltyCalculatorTest { private static final int THIRTY_MINS_IN_SECONDS = (int) Duration.ofMinutes(30).toSeconds(); @@ -19,7 +19,7 @@ void calculate() { new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> LineStrings.SIMPLE); var mod = TimePenalty.of(Duration.ofMinutes(10), 1.5f); - var calc = new DurationModifierCalculator(delegate, mod); + var calc = new TimePenaltyCalculator(delegate, mod); var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); assertEquals(3300, path.durationSeconds); } @@ -28,7 +28,7 @@ void calculate() { void nullValue() { FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> null; var mod = TimePenalty.of(Duration.ofMinutes(10), 1.5f); - var calc = new DurationModifierCalculator(delegate, mod); + var calc = new TimePenaltyCalculator(delegate, mod); var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); assertNull(path); } diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java index bd06a51960b..a4b245b7de8 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java @@ -22,7 +22,7 @@ class UnscheduledDrivingDurationTest { private static final StopTime STOP_TIME = FlexStopTimesForTest.area("10:00", "18:00"); @Test - void noModifier() { + void noPenalty() { var trip = UnscheduledTrip.of(id("1")).withStopTimes(List.of(STOP_TIME)).build(); var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); @@ -31,11 +31,11 @@ void noModifier() { } @Test - void withModifier() { + void withPenalty() { var trip = UnscheduledTrip .of(id("1")) .withStopTimes(List.of(STOP_TIME)) - .withDurationModifier(TimePenalty.of(Duration.ofMinutes(2), 1.5f)) + .withTimePenalty(TimePenalty.of(Duration.ofMinutes(2), 1.5f)) .build(); var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 5347e7504c5..c4167f2f9e1 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -35,13 +35,13 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { var stopTimes = stopTimesByTrip.get(trip); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { - var modifier = builder.getFlexDurationFactors().getOrDefault(trip, TimePenalty.ZERO); + var timePenalty = builder.getFlexTimePenalty().getOrDefault(trip, TimePenalty.NONE); result.add( UnscheduledTrip .of(trip.getId()) .withTrip(trip) .withStopTimes(stopTimes) - .withDurationModifier(modifier) + .withTimePenalty(timePenalty) .build() ); } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculator.java similarity index 83% rename from src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java rename to src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculator.java index 0f521b55b1f..63b661f0f9a 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculator.java @@ -8,12 +8,12 @@ * A calculator to delegates the main computation to another instance and applies a duration * modifier afterward. */ -public class DurationModifierCalculator implements FlexPathCalculator { +public class TimePenaltyCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; private final TimePenalty factors; - public DurationModifierCalculator(FlexPathCalculator delegate, TimePenalty penalty) { + public TimePenaltyCalculator(FlexPathCalculator delegate, TimePenalty penalty) { this.delegate = delegate; this.factors = penalty; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index 7fa31c78114..402d39e2aa7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -15,11 +15,13 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.flexpathcalculator.DurationModifierCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; +import org.opentripplanner.ext.flex.flexpathcalculator.TimePenaltyCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; +import org.opentripplanner.framework.lang.DoubleUtils; import org.opentripplanner.framework.lang.IntRange; +import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; @@ -53,7 +55,7 @@ public class UnscheduledTrip extends FlexTrip getFlexAccessTemplates( } /** - * Get the correct {@link FlexPathCalculator} depending on the {@code durationModifier}. + * Get the correct {@link FlexPathCalculator} depending on the {@code timePenalty}. * If the modifier doesn't actually modify, we return the regular calculator. */ protected FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { - if (!durationModifier.isZero()) { - return new DurationModifierCalculator(calculator, durationModifier); + if (timePenalty.modifies()) { + return new TimePenaltyCalculator(calculator, timePenalty); } else { return calculator; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index cfffebe4fa7..1f8585f5ad0 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -9,7 +9,7 @@ public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; - private TimePenalty durationModifier = TimePenalty.ZERO; + private TimePenalty timePenalty = TimePenalty.NONE; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -31,13 +31,13 @@ public List stopTimes() { return stopTimes; } - public UnscheduledTripBuilder withDurationModifier(TimePenalty factors) { - this.durationModifier = factors; + public UnscheduledTripBuilder withTimePenalty(TimePenalty factors) { + this.timePenalty = factors; return this; } - public TimePenalty durationModifier() { - return durationModifier; + public TimePenalty timePenalty() { + return timePenalty; } @Override diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index 24db367f829..ce65d6b0820 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -171,7 +171,7 @@ public void mapStopTripAndRouteDataIntoBuilder() { builder.getPathways().addAll(pathwayMapper.map(data.getAllPathways())); builder.getStopTimesSortedByTrip().addAll(stopTimeMapper.map(data.getAllStopTimes())); - builder.getFlexDurationFactors().putAll(tripMapper.flexSafeDurationModifiers()); + builder.getFlexTimePenalty().putAll(tripMapper.flexSafeDurationModifiers()); builder.getTripsById().addAll(tripMapper.map(data.getAllTrips())); fareRulesBuilder.fareAttributes().addAll(fareAttributeMapper.map(data.getAllFareAttributes())); diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 756a1ccf2a8..544ca29599d 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -213,7 +213,7 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } - public Map getFlexDurationFactors() { + public Map getFlexTimePenalty() { return flexDurationFactors; } diff --git a/src/main/java/org/opentripplanner/routing/api/request/framework/TimePenalty.java b/src/main/java/org/opentripplanner/routing/api/request/framework/TimePenalty.java index aaa81db5a09..0c6fdd96436 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/framework/TimePenalty.java +++ b/src/main/java/org/opentripplanner/routing/api/request/framework/TimePenalty.java @@ -7,6 +7,7 @@ public final class TimePenalty extends AbstractLinearFunction { public static final TimePenalty ZERO = new TimePenalty(Duration.ZERO, 0.0); + public static final TimePenalty NONE = new TimePenalty(Duration.ZERO, 1.0); private TimePenalty(Duration constant, double coefficient) { super(DurationUtils.requireNonNegative(constant), coefficient); @@ -31,6 +32,13 @@ protected boolean isZero(Duration value) { return value.isZero(); } + /** + * Does this penalty actually modify a duration or would it be returned unchanged? + */ + public boolean modifies() { + return !constant().isZero() && coefficient() != 1.0; + } + @Override protected Duration constantAsDuration() { return constant(); diff --git a/src/test/java/org/opentripplanner/routing/api/request/framework/TimePenaltyTest.java b/src/test/java/org/opentripplanner/routing/api/request/framework/TimePenaltyTest.java index f5bffe1f415..087ffa1d637 100644 --- a/src/test/java/org/opentripplanner/routing/api/request/framework/TimePenaltyTest.java +++ b/src/test/java/org/opentripplanner/routing/api/request/framework/TimePenaltyTest.java @@ -64,4 +64,11 @@ void calculate() { var subject = TimePenalty.of(D2m, 0.5); assertEquals(120 + 150, subject.calculate(Duration.ofMinutes(5)).toSeconds()); } + + @Test + void modifies() { + var subject = TimePenalty.of(D2m, 0.5); + assertTrue(subject.modifies()); + assertFalse(TimePenalty.ZERO.modifies()); + } }