diff --git a/src/main/java/org/opentripplanner/astar/strategy/BatteryDistanceSkipEdgeStrategy.java b/src/main/java/org/opentripplanner/astar/strategy/BatteryDistanceSkipEdgeStrategy.java index c96ba289b79..b8087595daa 100644 --- a/src/main/java/org/opentripplanner/astar/strategy/BatteryDistanceSkipEdgeStrategy.java +++ b/src/main/java/org/opentripplanner/astar/strategy/BatteryDistanceSkipEdgeStrategy.java @@ -1,10 +1,9 @@ package org.opentripplanner.astar.strategy; -import java.util.Optional; +import java.util.function.Predicate; import org.opentripplanner.astar.spi.AStarEdge; import org.opentripplanner.astar.spi.AStarState; import org.opentripplanner.astar.spi.SkipEdgeStrategy; -import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; /** * Skips Edges when the available battery distance of a vehicle is less than the accumulated driving @@ -15,19 +14,14 @@ public class BatteryDistanceSkipEdgeStrategy< > implements SkipEdgeStrategy { + private final Predicate shouldSkip; + + public BatteryDistanceSkipEdgeStrategy(Predicate shouldSkip) { + this.shouldSkip = shouldSkip; + } + @Override public boolean shouldSkipEdge(State current, Edge edge) { - if (edge instanceof StreetVehicleRentalLink) { - Optional currentRangeMeters = - ((StreetVehicleRentalLink) edge).getCurrentRangeMeters(); - - if (currentRangeMeters.isEmpty()) { - return false; - } - double batteryDistance = - ((org.opentripplanner.street.search.state.State) current).batteryDistance; - return currentRangeMeters.get() < batteryDistance; - } - return false; + return shouldSkip.test(current); } } diff --git a/src/main/java/org/opentripplanner/routing/impl/BatteryValidator.java b/src/main/java/org/opentripplanner/routing/impl/BatteryValidator.java new file mode 100644 index 00000000000..1919a27c4d5 --- /dev/null +++ b/src/main/java/org/opentripplanner/routing/impl/BatteryValidator.java @@ -0,0 +1,17 @@ +package org.opentripplanner.routing.impl; + +import java.util.Optional; +import org.opentripplanner.street.search.state.State; + +public class BatteryValidator { + + public static boolean wouldBatteryRunOut(Object current) { + double drivenBatteryMeters = + ((org.opentripplanner.street.search.state.State) current).drivenBatteryMeters; + Optional currentRangeMeters = ((State) current).currentRangeMeters; + if (currentRangeMeters.isEmpty()) { + return false; + } + return currentRangeMeters.get() < drivenBatteryMeters; + } +} diff --git a/src/main/java/org/opentripplanner/routing/impl/GraphPathFinder.java b/src/main/java/org/opentripplanner/routing/impl/GraphPathFinder.java index 4a1ffaadf08..3a35a61978d 100644 --- a/src/main/java/org/opentripplanner/routing/impl/GraphPathFinder.java +++ b/src/main/java/org/opentripplanner/routing/impl/GraphPathFinder.java @@ -94,7 +94,7 @@ public List> getPaths( new DurationSkipEdgeStrategy( preferences.maxDirectDuration().valueOf(request.journey().direct().mode()) ), - new BatteryDistanceSkipEdgeStrategy() + new BatteryDistanceSkipEdgeStrategy(BatteryValidator::wouldBatteryRunOut) ) ) // FORCING the dominance function to weight only diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/street/StreetVehicleRentalLink.java b/src/main/java/org/opentripplanner/service/vehiclerental/street/StreetVehicleRentalLink.java index 1798022dd77..385b347d24a 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/street/StreetVehicleRentalLink.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/street/StreetVehicleRentalLink.java @@ -1,14 +1,11 @@ package org.opentripplanner.service.vehiclerental.street; -import java.util.Optional; import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.vertex.StreetVertex; import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.state.StateEditor; -import org.opentripplanner.transit.model.framework.FeedScopedId; /** * This represents the connection between a street vertex and a bike rental station vertex. @@ -74,11 +71,4 @@ public State[] traverse(State s0) { public I18NString getName() { return vehicleRentalPlaceVertex.getName(); } - - public Optional getCurrentRangeMeters() { - if (vehicleRentalPlaceVertex.getStation() instanceof VehicleRentalVehicle) { - return ((VehicleRentalVehicle) vehicleRentalPlaceVertex.getStation()).getCurrentRangeMeters(); - } - return Optional.empty(); - } } diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalEdge.java b/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalEdge.java index dc9fed8b9a3..f3ab146017c 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalEdge.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalEdge.java @@ -1,11 +1,13 @@ package org.opentripplanner.service.vehiclerental.street; import java.util.Collections; +import java.util.Optional; import java.util.Set; import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; +import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; import org.opentripplanner.street.model.RentalFormFactor; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.search.state.State; @@ -19,10 +21,12 @@ public class VehicleRentalEdge extends Edge { public final RentalFormFactor formFactor; + private final VehicleRentalPlaceVertex vertex; private VehicleRentalEdge(VehicleRentalPlaceVertex vertex, RentalFormFactor formFactor) { super(vertex, vertex); this.formFactor = formFactor; + this.vertex = vertex; } public static VehicleRentalEdge createVehicleRentalEdge( @@ -169,6 +173,7 @@ public State[] traverse(State s0) { ? (int) preferences.pickupTime().toSeconds() : (int) preferences.dropOffTime().toSeconds() ); + s1.setCurrentRangeMeters(getCurrentRangeMeters()); s1.setBackMode(null); return s1.makeStateArray(); } @@ -206,4 +211,11 @@ private static Set allowedModes(StreetMode streetMode) { default -> Set.of(); }; } + + public Optional getCurrentRangeMeters() { + if (vertex.getStation() instanceof VehicleRentalVehicle) { + return ((VehicleRentalVehicle) vertex.getStation()).getCurrentRangeMeters(); + } + return Optional.empty(); + } } diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index 0aabfd2b615..eeebb11ff0a 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -1184,7 +1184,7 @@ private StateEditor doTraverse(State s0, TraverseMode traverseMode, boolean walk } if (traverseMode.isCyclingIsh() && s1.isVehicleRentable()) { - s1.incrementBatteryDistance(getDistanceWithElevation()); + s1.incrementDrivenBatteryMeters(getDistanceWithElevation()); } if (costExtension != null) { diff --git a/src/main/java/org/opentripplanner/street/search/state/State.java b/src/main/java/org/opentripplanner/street/search/state/State.java index f9a03189467..c2a359df64d 100644 --- a/src/main/java/org/opentripplanner/street/search/state/State.java +++ b/src/main/java/org/opentripplanner/street/search/state/State.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Stream; import javax.annotation.Nonnull; @@ -52,8 +53,11 @@ public class State implements AStarState, Cloneable { // we should DEFINITELY rename this variable and the associated methods. public double walkDistance; - // how far a vehicle powered by battery has driven - public double batteryDistance; + // how far a sharing vehicle powered by battery has driven + public double drivenBatteryMeters; + + //the available battery distance of a currently selected sharing vehicle + public Optional currentRangeMeters; /* CONSTRUCTORS */ @@ -85,7 +89,8 @@ public State( vertex.rentalRestrictions().noDropOffNetworks(); } this.walkDistance = 0; - this.batteryDistance = 0; + this.drivenBatteryMeters = 0; + this.currentRangeMeters = Optional.empty(); this.time = startTime.getEpochSecond(); } @@ -364,7 +369,8 @@ public State reverse() { editor.incrementTimeInSeconds(orig.getAbsTimeDeltaSeconds()); editor.incrementWeight(orig.getWeightDelta()); editor.incrementWalkDistance(orig.getWalkDistanceDelta()); - editor.incrementBatteryDistance(orig.getBatteryDistanceDelta()); + editor.incrementDrivenBatteryMeters(orig.getBatteryDistanceDelta()); + editor.setCurrentRangeMeters(orig.getCurrentRangeMeters()); // propagate the modes through to the reversed edge editor.setBackMode(orig.getBackMode()); @@ -510,12 +516,20 @@ private double getWalkDistanceDelta() { private double getBatteryDistanceDelta() { if (backState != null) { - return Math.abs(this.batteryDistance - backState.batteryDistance); + return Math.abs(this.drivenBatteryMeters - backState.drivenBatteryMeters); } else { return 0.0; } } + private Optional getCurrentRangeMeters() { + if (backState != null) { + return backState.currentRangeMeters; + } else { + return Optional.empty(); + } + } + private State reversedClone() { StreetSearchRequest reversedRequest = request .copyOfReversed(getTime()) diff --git a/src/main/java/org/opentripplanner/street/search/state/StateEditor.java b/src/main/java/org/opentripplanner/street/search/state/StateEditor.java index 69a2696170c..d68fdbc4f47 100644 --- a/src/main/java/org/opentripplanner/street/search/state/StateEditor.java +++ b/src/main/java/org/opentripplanner/street/search/state/StateEditor.java @@ -1,5 +1,6 @@ package org.opentripplanner.street.search.state; +import java.util.Optional; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -196,13 +197,17 @@ public void incrementWalkDistance(double length) { child.walkDistance += length; } - public void incrementBatteryDistance(double length) { + public void incrementDrivenBatteryMeters(double length) { if (length < 0) { LOG.warn("A state's battery distance is being incremented by a negative amount."); defectiveTraversal = true; return; } - child.batteryDistance += length; + child.drivenBatteryMeters += length; + } + + public void setCurrentRangeMeters(Optional currentRangeMeters) { + child.currentRangeMeters = currentRangeMeters; } /* Basic Setters */ diff --git a/src/test/java/org/opentripplanner/astar/strategy/BatteryDistanceSkipEdgeStrategyTest.java b/src/test/java/org/opentripplanner/astar/strategy/BatteryDistanceSkipEdgeStrategyTest.java index 05caf23e6bf..9821cfd811f 100644 --- a/src/test/java/org/opentripplanner/astar/strategy/BatteryDistanceSkipEdgeStrategyTest.java +++ b/src/test/java/org/opentripplanner/astar/strategy/BatteryDistanceSkipEdgeStrategyTest.java @@ -3,15 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Optional; import org.junit.jupiter.api.Test; -import org.locationtech.jts.geom.Coordinate; import org.opentripplanner.routing.algorithm.GraphRoutingTest; -import org.opentripplanner.service.vehiclerental.model.VehicleRentalVehicle; -import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; -import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex; -import org.opentripplanner.street.model.edge.FreeEdge; -import org.opentripplanner.street.model.vertex.SimpleVertex; -import org.opentripplanner.street.model.vertex.StreetLocation; +import org.opentripplanner.routing.impl.BatteryValidator; import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.state.TestStateBuilder; @@ -23,15 +18,13 @@ public class BatteryDistanceSkipEdgeStrategyTest extends GraphRoutingTest { */ @Test void batteryIsNotEnough() { - var vertex = new SimpleVertex(null, -74.01, 40.01); - - var edge = getStreetVehicleRentalLink(0.0, vertex); - var state = getState(100.0); - var strategy = new BatteryDistanceSkipEdgeStrategy(); + state.currentRangeMeters = Optional.of(0.0); + + var strategy = new BatteryDistanceSkipEdgeStrategy(BatteryValidator::wouldBatteryRunOut); - assertTrue(strategy.shouldSkipEdge(state, edge)); + assertTrue(strategy.shouldSkipEdge(state, null)); } /** @@ -40,14 +33,11 @@ void batteryIsNotEnough() { */ @Test void batteryIsEnough() { - var vertex = new SimpleVertex(null, -74.01, 40.01); - - var edge = getStreetVehicleRentalLink(4000.0, vertex); - var state = getState(100.0); + state.currentRangeMeters = Optional.of(4000.0); - var strategy = new BatteryDistanceSkipEdgeStrategy(); - assertFalse(strategy.shouldSkipEdge(state, edge)); + var strategy = new BatteryDistanceSkipEdgeStrategy(BatteryValidator::wouldBatteryRunOut); + assertFalse(strategy.shouldSkipEdge(state, null)); } /** @@ -56,14 +46,12 @@ void batteryIsEnough() { */ @Test void batteryDiesAtFinalLocation() { - var vertex = new SimpleVertex(null, -74.01, 40.01); - - var edge = getStreetVehicleRentalLink(100.0, vertex); - var state = getState(100.0); - var strategy = new BatteryDistanceSkipEdgeStrategy(); - assertFalse(strategy.shouldSkipEdge(state, edge)); + state.currentRangeMeters = Optional.of(100.0); + + var strategy = new BatteryDistanceSkipEdgeStrategy(BatteryValidator::wouldBatteryRunOut); + assertFalse(strategy.shouldSkipEdge(state, null)); } /** @@ -72,28 +60,11 @@ void batteryDiesAtFinalLocation() { */ @Test void noDrivenMeters() { - var vertex = new SimpleVertex(null, -74.01, 40.01); - - var edge = getStreetVehicleRentalLink(100.0, vertex); - var state = getState(0.0); + state.currentRangeMeters = Optional.of(100.0); - var strategy = new BatteryDistanceSkipEdgeStrategy(); - assertFalse(strategy.shouldSkipEdge(state, edge)); - } - - /** - * Edge is of wrong Type (not StreetVehicleLink) -> does not skip Edge - * edge is no StreetVehicleRentalLink => false - */ - @Test - void edgeIsOfWrongType() { - var from = new StreetLocation(null, new Coordinate(0.0, 0.0), null); - var to = new StreetLocation(null, new Coordinate(1.0, 1.0), null); - var edge = FreeEdge.createFreeEdge(from, to); - var state = TestStateBuilder.ofScooterRental().build(); - var strategy = new BatteryDistanceSkipEdgeStrategy(); - assertFalse(strategy.shouldSkipEdge(state, edge)); + var strategy = new BatteryDistanceSkipEdgeStrategy(BatteryValidator::wouldBatteryRunOut); + assertFalse(strategy.shouldSkipEdge(state, null)); } /** @@ -102,35 +73,16 @@ void edgeIsOfWrongType() { */ @Test void batteryHasNoValue() { - var vertex = new SimpleVertex(null, -74.01, 40.01); - - var vehicle = new VehicleRentalVehicle(); - vehicle.currentRangeMeters = null; - - var rentalVertex = new VehicleRentalPlaceVertex(vehicle); - - var edge = StreetVehicleRentalLink.createStreetVehicleRentalLink(vertex, rentalVertex); - var state = TestStateBuilder.ofScooterRental().build(); + state.currentRangeMeters = Optional.empty(); - var strategy = new BatteryDistanceSkipEdgeStrategy(); - assertFalse(strategy.shouldSkipEdge(state, edge)); - } - - private static StreetVehicleRentalLink getStreetVehicleRentalLink( - double currentRangeMeters, - SimpleVertex vertex - ) { - var vehicle = new VehicleRentalVehicle(); - vehicle.currentRangeMeters = currentRangeMeters; - var rentalVertex = new VehicleRentalPlaceVertex(vehicle); - var edge = StreetVehicleRentalLink.createStreetVehicleRentalLink(vertex, rentalVertex); - return edge; + var strategy = new BatteryDistanceSkipEdgeStrategy(BatteryValidator::wouldBatteryRunOut); + assertFalse(strategy.shouldSkipEdge(state, null)); } - private static State getState(double batteryDistance) { + private static State getState(double drivenBatteryMetters) { var state = TestStateBuilder.ofScooterRental().build(); - state.batteryDistance = batteryDistance; + state.drivenBatteryMeters = drivenBatteryMetters; return state; } }