From c529f214a0761fde2fc1f5fa000fcc5d4745bbb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6rl?= Date: Fri, 15 Dec 2023 18:16:50 +0100 Subject: [PATCH] feat: car access/egress (#183) * feat: car access/egress * add a comment and extend changelog --- CHANGELOG.md | 1 + .../core/scenario/config/GenerateConfig.java | 3 +- .../cutter/population/PlanCutter.java | 4 +- .../population/PopulationCutterModule.java | 21 +- .../trips/ModeAwareTripProcessor.java | 12 +- .../trips/NetworkTripProcessor.java | 94 ++++---- .../trips/TeleportationTripProcessor.java | 4 +- .../trips/TransitTripProcessor.java | 13 +- .../population/trips/TripProcessor.java | 2 +- ...faultNetworkRouteCrossingPointFinder.java} | 10 +- ...DefaultNetworkTripCrossingPointFinder.java | 80 +++++++ ...nt.java => NetworkRouteCrossingPoint.java} | 4 +- ...a => NetworkRouteCrossingPointFinder.java} | 4 +- .../network/NetworkTripCrossingPoint.java | 29 +++ .../NetworkTripCrossingPointFinder.java | 16 ++ ...DefaultTransitTripCrossingPointFinder.java | 21 +- .../parameters/ModeParameters.java | 2 +- .../utilities/predictors/CarPredictor.java | 26 ++- .../TestSpecialModeChoiceCases.java | 12 + .../trips/TestNetworkTripProcessor.java | 220 +++++++----------- .../trips/TestTransitTripProcessor.java | 14 +- ...TestDefaultNetworkCrossingPointFinder.java | 14 +- .../parameters/CorsicaDrtModeParameters.java | 2 +- .../parameters/IDFModeParameters.java | 2 +- .../main/resources/corsica/corsica_config.xml | 1 + .../eqasim/ile_de_france/TestCorisica.java | 18 +- .../parameters/LosAngelesModeParameters.java | 2 +- .../SanFranciscoModeParameters.java | 2 +- .../parameters/SaoPauloModeParameters.java | 2 +- .../parameters/SwissModeParameters.java | 2 +- 30 files changed, 378 insertions(+), 259 deletions(-) rename core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/{DefaultNetworkCrossingPointFinder.java => DefaultNetworkRouteCrossingPointFinder.java} (85%) create mode 100644 core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkTripCrossingPointFinder.java rename core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/{NetworkCrossingPoint.java => NetworkRouteCrossingPoint.java} (72%) rename core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/{NetworkCrossingPointFinder.java => NetworkRouteCrossingPointFinder.java} (64%) create mode 100644 core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkTripCrossingPoint.java create mode 100644 core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkTripCrossingPointFinder.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 1827423ce..62756f798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ included in the (note yet determined) next version number. **Development version** +- Network-based (car) routing now generates access and egress walk legs - Convert initial-routing only-walk legs to actual walk (instead of transit) - Don't put activities on motorway/trunk/link in the network - Updated to MATSim 14 diff --git a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java index f01ded695..662ffd116 100644 --- a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java +++ b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java @@ -114,8 +114,7 @@ protected void adaptConfiguration(Config config) { config.plansCalcRoute().setNetworkModes(NETWORK_MODES); - // TODO: Potentially defaults we should change after MATSim 12 - config.plansCalcRoute().setAccessEgressType(AccessEgressType.none); + config.plansCalcRoute().setAccessEgressType(AccessEgressType.accessEgressModeToLink); config.plansCalcRoute().setRoutingRandomness(0.0); ModeRoutingParams outsideParams = routingConfig.getOrCreateModeRoutingParams("outside"); diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/PlanCutter.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/PlanCutter.java index d97ee619d..56ff405b5 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/PlanCutter.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/PlanCutter.java @@ -55,8 +55,10 @@ public List processPlan(Id personId, List elem int legIndex = 0; for (TripStructureUtils.Trip trip : TripStructureUtils.getTrips(elements)) { + String routingMode = TripStructureUtils.getRoutingMode(trip.getLegsOnly().get(0)); + result.addAll(tripProcessor.process(personId, legIndex, trip.getOriginActivity(), - trip.getTripElements(), trip.getDestinationActivity())); + trip.getTripElements(), trip.getDestinationActivity(), routingMode)); addActivity(result, trip.getDestinationActivity()); legIndex += trip.getLegsOnly().size(); diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/PopulationCutterModule.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/PopulationCutterModule.java index 87793aedc..954af6c2e 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/PopulationCutterModule.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/PopulationCutterModule.java @@ -11,8 +11,10 @@ import org.eqasim.core.scenario.cutter.population.trips.TeleportationTripProcessor; import org.eqasim.core.scenario.cutter.population.trips.TransitTripProcessor; import org.eqasim.core.scenario.cutter.population.trips.TripProcessor; -import org.eqasim.core.scenario.cutter.population.trips.crossing.network.DefaultNetworkCrossingPointFinder; -import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkCrossingPointFinder; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.DefaultNetworkRouteCrossingPointFinder; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.DefaultNetworkTripCrossingPointFinder; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkRouteCrossingPointFinder; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkTripCrossingPointFinder; import org.eqasim.core.scenario.cutter.population.trips.crossing.network.timing.LinkTimingRegistry; import org.eqasim.core.scenario.cutter.population.trips.crossing.network.timing.LinkTimingRegistryHandler; import org.eqasim.core.scenario.cutter.population.trips.crossing.teleportation.DefaultTeleportationCrossingPointFinder; @@ -30,7 +32,6 @@ import org.matsim.core.controler.AbstractModule; import org.matsim.core.events.EventsUtils; import org.matsim.core.events.MatsimEventsReader; -import org.matsim.core.router.MainModeIdentifier; import org.matsim.pt.config.TransitConfigGroup; import org.matsim.pt.config.TransitRouterConfigGroup; @@ -57,14 +58,14 @@ public void install() { bind(TripProcessor.class).to(ModeAwareTripProcessor.class); bind(TeleportationCrossingPointFinder.class).to(DefaultTeleportationCrossingPointFinder.class); - bind(NetworkCrossingPointFinder.class).to(DefaultNetworkCrossingPointFinder.class); + bind(NetworkRouteCrossingPointFinder.class).to(DefaultNetworkRouteCrossingPointFinder.class); bind(TransitRouteCrossingPointFinder.class).to(DefaultTransitRouteCrossingPointFinder.class); bind(TransitTripCrossingPointFinder.class).to(DefaultTransitTripCrossingPointFinder.class); + bind(NetworkTripCrossingPointFinder.class).to(DefaultNetworkTripCrossingPointFinder.class); bind(MergeOutsideActivities.class).to(DefaultMergeOutsideActivities.class); bind(TeleportationTripProcessor.class); - bind(NetworkTripProcessor.class); bind(PlanCutter.class); @@ -113,10 +114,10 @@ private boolean checkDisjoint(Collection a, Collection b) { @Provides public ModeAwareTripProcessor provideModeAwareTripProcessor(PlansCalcRouteConfigGroup routingConfig, - TransitConfigGroup transitConfig, ScenarioExtent extent, MainModeIdentifier mainModeIdentifier, + TransitConfigGroup transitConfig, ScenarioExtent extent, TeleportationTripProcessor teleportationTripProcessor, NetworkTripProcessor networkTripProcessor, TransitTripProcessor transitTripProcessor) { - ModeAwareTripProcessor tripProcessor = new ModeAwareTripProcessor(mainModeIdentifier); + ModeAwareTripProcessor tripProcessor = new ModeAwareTripProcessor(); Collection networkModes = new HashSet<>(routingConfig.getNetworkModes()); Collection teleportedModes = getTeleportedModes(routingConfig); @@ -147,6 +148,12 @@ public TransitTripProcessor provideTransitTripProcessor(TransitTripCrossingPoint return new TransitTripProcessor(transitPointFinder, extent, routerConfig.getAdditionalTransferTime()); } + @Provides + public NetworkTripProcessor provideNetworkTripProcessor(NetworkTripCrossingPointFinder networkPointFinder, + ScenarioExtent extent, TransitRouterConfigGroup routerConfig) { + return new NetworkTripProcessor(networkPointFinder, extent); + } + @Provides @Singleton public LinkTimingRegistry provideLinkTimingRegistry(Network network) { diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/ModeAwareTripProcessor.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/ModeAwareTripProcessor.java index 1f8efc2b5..9a125aca3 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/ModeAwareTripProcessor.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/ModeAwareTripProcessor.java @@ -8,24 +8,18 @@ import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.PlanElement; -import org.matsim.core.router.MainModeIdentifier; public class ModeAwareTripProcessor implements TripProcessor { - private final MainModeIdentifier mainModeIdentifier; private final Map processors = new HashMap<>(); - public ModeAwareTripProcessor(MainModeIdentifier mainModeIdentifier) { - this.mainModeIdentifier = mainModeIdentifier; - } - public void setProcessor(String mode, TripProcessor processor) { this.processors.put(mode, processor); } @Override public List process(Id personId, int firstLegIndex, Activity firstActivity, - List trip, Activity secondActivity) { - String mainMode = mainModeIdentifier.identifyMainMode(trip); - return processors.get(mainMode).process(personId, firstLegIndex, firstActivity, trip, secondActivity); + List trip, Activity secondActivity, String routingMode) { + return processors.get(routingMode).process(personId, firstLegIndex, firstActivity, trip, secondActivity, + routingMode); } } diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/NetworkTripProcessor.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/NetworkTripProcessor.java index c87fa4c13..2503c3575 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/NetworkTripProcessor.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/NetworkTripProcessor.java @@ -5,80 +5,88 @@ import java.util.List; import org.eqasim.core.scenario.cutter.extent.ScenarioExtent; -import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkCrossingPoint; -import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkCrossingPointFinder; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkTripCrossingPoint; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkTripCrossingPointFinder; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.PlanElement; import org.matsim.core.population.PopulationUtils; -import org.matsim.core.population.routes.NetworkRoute; +import org.matsim.core.router.TripStructureUtils; import com.google.inject.Inject; public class NetworkTripProcessor implements TripProcessor { - final private NetworkCrossingPointFinder crossingPointFinder; + final private NetworkTripCrossingPointFinder crossingPointFinder; final private ScenarioExtent extent; @Inject - public NetworkTripProcessor(NetworkCrossingPointFinder crossingPointFinder, ScenarioExtent extent) { + public NetworkTripProcessor(NetworkTripCrossingPointFinder crossingPointFinder, ScenarioExtent extent) { this.crossingPointFinder = crossingPointFinder; this.extent = extent; } @Override public List process(Id personId, int firstLegIndex, Activity firstActivity, - List trip, Activity secondActivity) { - Leg leg = (Leg) trip.get(0); + List trip, Activity secondActivity, String routingMode) { + List crossingPoints = crossingPointFinder.findCrossingPoints(personId, firstLegIndex, + firstActivity.getCoord(), trip, secondActivity.getCoord()); - NetworkRoute route = (NetworkRoute) leg.getRoute(); - List crossingPoints = crossingPointFinder.findCrossingPoints(personId, firstLegIndex, - leg.getMode(), route, leg.getDepartureTime().seconds()); - - if (crossingPoints.size() > 0) { + if (crossingPoints.size() > 0) { // there are crossing points List result = new LinkedList<>(); - result.add(PopulationUtils.createLeg(crossingPoints.get(0).isOutgoing ? leg.getMode() : "outside")); - - for (NetworkCrossingPoint point : crossingPoints) { - Activity activity = PopulationUtils.createActivityFromLinkId("outside", point.link.getId()); - activity.setEndTime(point.leaveTime); - result.add(activity); - result.add(PopulationUtils.createLeg(point.isOutgoing ? "outside" : leg.getMode())); + result.add(PopulationUtils + .createLeg(crossingPoints.get(0).isOutgoing ? crossingPoints.get(0).legMode : "outside")); + + for (NetworkTripCrossingPoint point : crossingPoints) { + if (point.isInVehicle) { + Activity activity = PopulationUtils.createActivityFromLinkId("outside", + point.networkRoutePoint.link.getId()); + activity.setEndTime(point.networkRoutePoint.leaveTime); + result.add(activity); + } else { + Activity activity = PopulationUtils.createActivityFromCoord("outside", + point.teleportationPoint.coord); + activity.setEndTime(point.teleportationPoint.time); + result.add(activity); + } + + result.add(PopulationUtils.createLeg(point.isOutgoing ? "outside" : point.legMode)); } return result; - } else if (crossingPointFinder.isInside(route)) { - return Arrays.asList(PopulationUtils.createLeg(leg.getMode())); - } else { - // The route is outside. This does not mean that both (or any) activity is - // actually outside. These are mainly special cases in which the route is - // outside, but some activity is inside, across the border, because the link is - // parallel to the border. We put an outside activity right next to the inside - // activity. + } else { // there are no crossing points + if (crossingPointFinder.isInside(trip)) { // whole trip is inside + return Arrays.asList(PopulationUtils.createLeg(routingMode)); + } else { + // The route is outside. This does not mean that both (or any) activity is + // actually outside. These are mainly special cases in which the route is + // outside, but some activity is inside, across the border, because the link is + // parallel to the border. We put an outside activity right next to the inside + // activity. + + List result = new LinkedList<>(); - List result = new LinkedList<>(); + result.add(PopulationUtils.createLeg("outside")); - result.add(PopulationUtils.createLeg("outside")); + if (extent.isInside(firstActivity.getCoord())) { + Activity activity = PopulationUtils.createActivityFromLinkId("outside", firstActivity.getLinkId()); + activity.setEndTime(firstActivity.getEndTime().seconds()); + result.add(activity); - if (extent.isInside(firstActivity.getCoord())) { - Activity activity = PopulationUtils.createActivityFromLinkId("outside", firstActivity.getLinkId()); - activity.setEndTime(firstActivity.getEndTime().seconds()); - result.add(activity); + result.add(PopulationUtils.createLeg("outside")); + } - result.add(PopulationUtils.createLeg("outside")); - } + if (extent.isInside(secondActivity.getCoord())) { + Activity activity = PopulationUtils.createActivityFromLinkId("outside", secondActivity.getLinkId()); + activity.setEndTime(secondActivity.getStartTime().seconds()); + result.add(activity); - if (extent.isInside(secondActivity.getCoord())) { - Activity activity = PopulationUtils.createActivityFromLinkId("outside", secondActivity.getLinkId()); - activity.setEndTime(secondActivity.getStartTime().seconds()); - result.add(activity); + result.add(PopulationUtils.createLeg("outside")); + } - result.add(PopulationUtils.createLeg("outside")); + return result; } - - return result; } } } diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TeleportationTripProcessor.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TeleportationTripProcessor.java index f455e57c9..50e7ab227 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TeleportationTripProcessor.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TeleportationTripProcessor.java @@ -15,6 +15,7 @@ import org.matsim.api.core.v01.population.PlanElement; import org.matsim.core.population.PopulationUtils; +import com.google.common.base.Preconditions; import com.google.inject.Inject; public class TeleportationTripProcessor implements TripProcessor { @@ -29,7 +30,8 @@ public TeleportationTripProcessor(TeleportationCrossingPointFinder crossingPoint @Override public List process(Id personId, int firstLegIndex, Activity firstActivity, - List trip, Activity secondActivity) { + List trip, Activity secondActivity, String routingMode) { + Preconditions.checkArgument(trip.size() == 1); Leg leg = (Leg) trip.get(0); return process(firstActivity.getCoord(), secondActivity.getCoord(), leg.getTravelTime().seconds(), diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TransitTripProcessor.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TransitTripProcessor.java index eae90ca2b..49986c88e 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TransitTripProcessor.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TransitTripProcessor.java @@ -28,21 +28,22 @@ public TransitTripProcessor(TransitTripCrossingPointFinder transitPointFinder, S @Override public List process(Id personId, int firstLegIndex, Activity firstActivity, - List trip, Activity secondActivity) { + List trip, Activity secondActivity, String routingMode) { return process(firstActivity.getCoord(), trip, secondActivity.getCoord(), - !extent.isInside(firstActivity.getCoord()) && !extent.isInside(secondActivity.getCoord())); + !extent.isInside(firstActivity.getCoord()) && !extent.isInside(secondActivity.getCoord()), routingMode); } - public List process(Coord firstCoord, List trip, Coord secondCoord, boolean allOutside) { + public List process(Coord firstCoord, List trip, Coord secondCoord, boolean allOutside, + String routingMode) { List crossingPoints = transitPointFinder.findCrossingPoints(firstCoord, trip, secondCoord); if (crossingPoints.size() == 0) { - return Arrays.asList(PopulationUtils.createLeg(allOutside ? "outside" : "pt")); + return Arrays.asList(PopulationUtils.createLeg(allOutside ? "outside" : routingMode)); } else { List result = new LinkedList<>(); - result.add(PopulationUtils.createLeg(crossingPoints.get(0).isOutgoing ? "pt" : "outside")); + result.add(PopulationUtils.createLeg(crossingPoints.get(0).isOutgoing ? routingMode : "outside")); for (TransitTripCrossingPoint point : crossingPoints) { if (point.isInVehicle) { @@ -57,7 +58,7 @@ public List process(Coord firstCoord, List trip, Coord result.add(activity); } - result.add(PopulationUtils.createLeg(point.isOutgoing ? "outside" : "pt")); + result.add(PopulationUtils.createLeg(point.isOutgoing ? "outside" : routingMode)); } return result; diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TripProcessor.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TripProcessor.java index be9390239..e6a823961 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TripProcessor.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/TripProcessor.java @@ -9,5 +9,5 @@ public interface TripProcessor { List process(Id personId, int firstLegIndex, Activity firstActivity, List trip, - Activity secondActivity); + Activity secondActivity, String routingMode); } diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkCrossingPointFinder.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkRouteCrossingPointFinder.java similarity index 85% rename from core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkCrossingPointFinder.java rename to core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkRouteCrossingPointFinder.java index 9aa353db3..6d51edd54 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkCrossingPointFinder.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkRouteCrossingPointFinder.java @@ -17,7 +17,7 @@ import com.google.inject.Inject; -public class DefaultNetworkCrossingPointFinder implements NetworkCrossingPointFinder { +public class DefaultNetworkRouteCrossingPointFinder implements NetworkRouteCrossingPointFinder { final private ScenarioExtent extent; final private Network network; @@ -25,7 +25,7 @@ public class DefaultNetworkCrossingPointFinder implements NetworkCrossingPointFi final private LinkTimingRegistry timingRegistry; @Inject - public DefaultNetworkCrossingPointFinder(ScenarioExtent extent, Network network, + public DefaultNetworkRouteCrossingPointFinder(ScenarioExtent extent, Network network, Map travelTimes, LinkTimingRegistry timingRegistry) { this.extent = extent; this.network = network; @@ -34,9 +34,9 @@ public DefaultNetworkCrossingPointFinder(ScenarioExtent extent, Network network, } @Override - public List findCrossingPoints(Id personId, int legIndex, String mode, + public List findCrossingPoints(Id personId, int legIndex, String mode, NetworkRoute route, double departureTime) { - List crossingPoints = new LinkedList<>(); + List crossingPoints = new LinkedList<>(); List> fullRoute = new LinkedList<>(); fullRoute.add(route.getStartLinkId()); @@ -67,7 +67,7 @@ public List findCrossingPoints(Id personId, int le leaveTime = timingData.get().leaveTime; } - crossingPoints.add(new NetworkCrossingPoint(index, link, enterTime, leaveTime, fromIsInside)); + crossingPoints.add(new NetworkRouteCrossingPoint(index, link, enterTime, leaveTime, fromIsInside)); } index++; diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkTripCrossingPointFinder.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkTripCrossingPointFinder.java new file mode 100644 index 000000000..01198c649 --- /dev/null +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/DefaultNetworkTripCrossingPointFinder.java @@ -0,0 +1,80 @@ +package org.eqasim.core.scenario.cutter.population.trips.crossing.network; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import org.eqasim.core.scenario.cutter.population.trips.crossing.teleportation.TeleportationCrossingPointFinder; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.Route; +import org.matsim.core.population.routes.NetworkRoute; +import org.matsim.core.router.TripStructureUtils; + +import com.google.inject.Inject; + +public class DefaultNetworkTripCrossingPointFinder implements NetworkTripCrossingPointFinder { + final private NetworkRouteCrossingPointFinder networkFinder; + final private TeleportationCrossingPointFinder walkFinder; + + @Inject + public DefaultNetworkTripCrossingPointFinder(NetworkRouteCrossingPointFinder networkFinder, + TeleportationCrossingPointFinder walkFinder) { + this.networkFinder = networkFinder; + this.walkFinder = walkFinder; + } + + @Override + public List findCrossingPoints(Id personId, int firstLegIndex, Coord startCoord, + List trip, Coord endCoord) { + List result = new LinkedList<>(); + int legIndex = firstLegIndex; + + for (int i = 0; i < trip.size(); i++) { + PlanElement element = trip.get(i); + + if (element instanceof Leg) { + Leg leg = (Leg) element; + Route route = leg.getRoute(); + + if (route instanceof NetworkRoute) { + result.addAll(networkFinder + .findCrossingPoints(personId, legIndex, leg.getMode(), (NetworkRoute) route, + leg.getDepartureTime().seconds()) + .stream().map(p -> new NetworkTripCrossingPoint(p, leg.getMode())) + .collect(Collectors.toList())); + } else { + Coord legStartCoord = (i == 0) ? startCoord : ((Activity) trip.get(i - 1)).getCoord(); + Coord legEndCoord = (i == trip.size() - 1) ? endCoord : ((Activity) trip.get(i + 1)).getCoord(); + + result.addAll(walkFinder + .findCrossingPoints(legStartCoord, legEndCoord, leg.getTravelTime().seconds(), + leg.getDepartureTime().seconds()) + .stream().map(p -> new NetworkTripCrossingPoint(p, leg.getMode())) + .collect(Collectors.toList())); + } + + legIndex++; + } + } + + return result; + } + + @Override + public boolean isInside(List trip) { + for (Leg leg : TripStructureUtils.getLegs(trip)) { + if (leg.getRoute() instanceof NetworkRoute) { + if (!networkFinder.isInside((NetworkRoute) leg.getRoute())) { + return false; + } + } + } + + return true; + } +} diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkCrossingPoint.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkRouteCrossingPoint.java similarity index 72% rename from core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkCrossingPoint.java rename to core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkRouteCrossingPoint.java index bf76e1869..d2083ec7b 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkCrossingPoint.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkRouteCrossingPoint.java @@ -2,7 +2,7 @@ import org.matsim.api.core.v01.network.Link; -public class NetworkCrossingPoint { +public class NetworkRouteCrossingPoint { final public Link link; final public int index; @@ -11,7 +11,7 @@ public class NetworkCrossingPoint { final public boolean isOutgoing; - public NetworkCrossingPoint(int index, Link link, double enterTime, double leaveTime, boolean isOutgoing) { + public NetworkRouteCrossingPoint(int index, Link link, double enterTime, double leaveTime, boolean isOutgoing) { this.index = index; this.link = link; this.enterTime = enterTime; diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkCrossingPointFinder.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkRouteCrossingPointFinder.java similarity index 64% rename from core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkCrossingPointFinder.java rename to core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkRouteCrossingPointFinder.java index e316c9349..045955209 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkCrossingPointFinder.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkRouteCrossingPointFinder.java @@ -6,8 +6,8 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.core.population.routes.NetworkRoute; -public interface NetworkCrossingPointFinder { - List findCrossingPoints(Id personId, int legIndex, String mode, NetworkRoute route, +public interface NetworkRouteCrossingPointFinder { + List findCrossingPoints(Id personId, int legIndex, String mode, NetworkRoute route, double departureTime); boolean isInside(NetworkRoute route); diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkTripCrossingPoint.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkTripCrossingPoint.java new file mode 100644 index 000000000..a7026a0b9 --- /dev/null +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkTripCrossingPoint.java @@ -0,0 +1,29 @@ +package org.eqasim.core.scenario.cutter.population.trips.crossing.network; + +import org.eqasim.core.scenario.cutter.population.trips.crossing.teleportation.TeleportationCrossingPoint; + +public class NetworkTripCrossingPoint { + final public boolean isInVehicle; + final public boolean isOutgoing; + + final public NetworkRouteCrossingPoint networkRoutePoint; + final public TeleportationCrossingPoint teleportationPoint; + + final public String legMode; + + public NetworkTripCrossingPoint(NetworkRouteCrossingPoint networkRoutePoint, String legMode) { + this.isInVehicle = true; + this.networkRoutePoint = networkRoutePoint; + this.teleportationPoint = null; + this.isOutgoing = networkRoutePoint.isOutgoing; + this.legMode = legMode; + } + + public NetworkTripCrossingPoint(TeleportationCrossingPoint teleportationPoint, String legMode) { + this.isInVehicle = false; + this.networkRoutePoint = null; + this.teleportationPoint = teleportationPoint; + this.isOutgoing = teleportationPoint.isOutgoing; + this.legMode = legMode; + } +} diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkTripCrossingPointFinder.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkTripCrossingPointFinder.java new file mode 100644 index 000000000..37085d4d9 --- /dev/null +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/network/NetworkTripCrossingPointFinder.java @@ -0,0 +1,16 @@ +package org.eqasim.core.scenario.cutter.population.trips.crossing.network; + +import java.util.List; + +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.PlanElement; + +public interface NetworkTripCrossingPointFinder { + + List findCrossingPoints(Id personId, int firstLegIndex, Coord startCoord, + List trip, Coord endCoord); + + boolean isInside(List trip); +} diff --git a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/transit/DefaultTransitTripCrossingPointFinder.java b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/transit/DefaultTransitTripCrossingPointFinder.java index 8c436d4b1..34a08ddc0 100644 --- a/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/transit/DefaultTransitTripCrossingPointFinder.java +++ b/core/src/main/java/org/eqasim/core/scenario/cutter/population/trips/crossing/transit/DefaultTransitTripCrossingPointFinder.java @@ -9,6 +9,7 @@ import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.Route; import org.matsim.pt.routes.TransitPassengerRoute; import com.google.inject.Inject; @@ -36,9 +37,14 @@ public List findCrossingPoints(Coord startCoord, List< if (element instanceof Leg) { Leg leg = (Leg) element; - - switch (leg.getMode()) { - case "walk": + Route route = leg.getRoute(); + + if (route instanceof TransitPassengerRoute) { + result.addAll(transitFinder + .findCrossingPoints((TransitPassengerRoute) leg.getRoute(), + leg.getDepartureTime().seconds()) + .stream().map(p -> new TransitTripCrossingPoint(p)).collect(Collectors.toList())); + } else { Coord legStartCoord = (i == 0) ? startCoord : ((Activity) trip.get(i - 1)).getCoord(); Coord legEndCoord = (i == trip.size() - 1) ? endCoord : ((Activity) trip.get(i + 1)).getCoord(); @@ -46,15 +52,6 @@ public List findCrossingPoints(Coord startCoord, List< .findCrossingPoints(legStartCoord, legEndCoord, leg.getTravelTime().seconds(), leg.getDepartureTime().seconds()) .stream().map(p -> new TransitTripCrossingPoint(p)).collect(Collectors.toList())); - break; - case "pt": - result.addAll(transitFinder - .findCrossingPoints((TransitPassengerRoute) leg.getRoute(), - leg.getDepartureTime().seconds()) - .stream().map(p -> new TransitTripCrossingPoint(p)).collect(Collectors.toList())); - break; - default: - throw new IllegalStateException(String.format("Unknown mode: %s", leg.getMode())); } } } diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java index fb1edf678..0dbb4f8d5 100644 --- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java @@ -7,7 +7,7 @@ public class CarParameters { public double alpha_u = 0.0; public double betaTravelTime_u_min = 0.0; - public double constantAccessEgressWalkTime_min = 0.0; + public double additionalAccessEgressWalkTime_min = 0.0; public double constantParkingSearchPenalty_min = 0.0; } diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CarPredictor.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CarPredictor.java index f1cf62875..df6ce4600 100644 --- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CarPredictor.java +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CarPredictor.java @@ -5,11 +5,14 @@ import org.eqasim.core.simulation.mode_choice.cost.CostModel; import org.eqasim.core.simulation.mode_choice.parameters.ModeParameters; import org.eqasim.core.simulation.mode_choice.utilities.variables.CarVariables; +import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.PlanElement; import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip; +import org.matsim.core.router.TripStructureUtils; +import com.google.common.base.Verify; import com.google.inject.Inject; import com.google.inject.name.Named; @@ -25,18 +28,25 @@ public CarPredictor(ModeParameters parameters, @Named("car") CostModel costModel @Override public CarVariables predict(Person person, DiscreteModeChoiceTrip trip, List elements) { - if (elements.size() > 1) { - throw new IllegalStateException("We do not support multi-stage car trips yet."); + double carTravelTime_min = parameters.car.constantParkingSearchPenalty_min; + double accessEgressTime_min = parameters.car.additionalAccessEgressWalkTime_min; + + boolean foundCar = false; + + for (Leg leg : TripStructureUtils.getLegs(elements)) { + if (leg.getMode().equals(TransportMode.car)) { + Verify.verify(!foundCar); + carTravelTime_min += leg.getTravelTime().seconds() / 60.0; + } else if (leg.getMode().equals(TransportMode.walk)) { + accessEgressTime_min += leg.getTravelTime().seconds() / 60.0; + } else { + throw new IllegalStateException("Unexpected mode in car chain: " + leg.getMode()); + } } - Leg leg = (Leg) elements.get(0); - - double travelTime_min = leg.getTravelTime().seconds() / 60.0 + parameters.car.constantParkingSearchPenalty_min; double cost_MU = costModel.calculateCost_MU(person, trip, elements); - double euclideanDistance_km = PredictorUtils.calculateEuclideanDistance_km(trip); - double accessEgressTime_min = parameters.car.constantAccessEgressWalkTime_min; - return new CarVariables(travelTime_min, cost_MU, euclideanDistance_km, accessEgressTime_min); + return new CarVariables(carTravelTime_min, cost_MU, euclideanDistance_km, accessEgressTime_min); } } diff --git a/core/src/test/java/org/eqasim/mode_choice/TestSpecialModeChoiceCases.java b/core/src/test/java/org/eqasim/mode_choice/TestSpecialModeChoiceCases.java index 167023d44..3be25b238 100644 --- a/core/src/test/java/org/eqasim/mode_choice/TestSpecialModeChoiceCases.java +++ b/core/src/test/java/org/eqasim/mode_choice/TestSpecialModeChoiceCases.java @@ -20,8 +20,11 @@ import org.eqasim.core.simulation.mode_choice.EqasimModeChoiceModule; import org.eqasim.core.simulation.mode_choice.parameters.ModeParameters; import org.junit.Test; +import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Node; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; @@ -149,7 +152,16 @@ static private Set findChains(List trips, int sa // Now create the model EqasimConfigurator configurator = new EqasimConfigurator(); + Scenario scenario = ScenarioUtils.createScenario(config); + + // need to generate one link to avoid assertion in NetworkRoutingInclAccessEgressModule + Node node = scenario.getNetwork().getFactory().createNode(Id.createNodeId("node"), new Coord(0.0, 0.0)); + Link link = scenario.getNetwork().getFactory().createLink(Id.createLinkId("link"), node, node); + link.setAllowedModes(Collections.singleton("car")); + scenario.getNetwork().addNode(node); + scenario.getNetwork().addLink(link); + Injector injector = new InjectorBuilder(scenario) // .addOverridingModules(configurator.getModules()) // .addOverridingModule(new EqasimModeChoiceModule()) // diff --git a/core/src/test/java/org/eqasim/scenario/cutter/population/trips/TestNetworkTripProcessor.java b/core/src/test/java/org/eqasim/scenario/cutter/population/trips/TestNetworkTripProcessor.java index 663d8d062..16f593192 100644 --- a/core/src/test/java/org/eqasim/scenario/cutter/population/trips/TestNetworkTripProcessor.java +++ b/core/src/test/java/org/eqasim/scenario/cutter/population/trips/TestNetworkTripProcessor.java @@ -3,12 +3,13 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.Set; import org.eqasim.core.scenario.cutter.extent.ScenarioExtent; import org.eqasim.core.scenario.cutter.population.trips.NetworkTripProcessor; -import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkCrossingPoint; -import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkCrossingPointFinder; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkRouteCrossingPoint; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkTripCrossingPoint; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkTripCrossingPointFinder; +import org.eqasim.core.scenario.cutter.population.trips.crossing.teleportation.TeleportationCrossingPoint; import org.junit.Assert; import org.junit.Test; import org.matsim.api.core.v01.Coord; @@ -19,26 +20,26 @@ import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.PlanElement; -import org.matsim.core.population.routes.NetworkRoute; import org.matsim.core.utils.misc.OptionalTime; import org.matsim.utils.objectattributes.attributable.Attributes; import org.mockito.Mockito; public class TestNetworkTripProcessor { - static private class NetworkFinderMock implements NetworkCrossingPointFinder { - final private List points = new LinkedList<>(); + static private class NetworkFinderMock implements NetworkTripCrossingPointFinder { + final private List points = new LinkedList<>(); @Override - public List findCrossingPoints(Id personId, int legIndex, String mode, NetworkRoute route, double departureTime) { + public List findCrossingPoints(Id personId, int firstLegIndex, + Coord startCoord, List trip, Coord endCoord) { return points; } - public void add(NetworkCrossingPoint point) { + public void add(NetworkTripCrossingPoint point) { points.add(point); } @Override - public boolean isInside(NetworkRoute route) { + public boolean isInside(List trip) { return true; } } @@ -66,17 +67,28 @@ public void testCarTripProcessor() { NetworkTripProcessor processor; List result; - Link linkA = createLinkMock("A"); - Link linkB = createLinkMock("B"); + Node mockNode = Mockito.mock(Node.class); + Mockito.when(mockNode.getCoord()).thenReturn(new Coord(0.0, 0.0)); + + Link linkA = Mockito.mock(Link.class); + Mockito.when(linkA.getId()).thenReturn(Id.createLinkId("A")); + Mockito.when(linkA.getToNode()).thenReturn(mockNode); + Link linkB = Mockito.mock(Link.class); + Mockito.when(linkB.getId()).thenReturn(Id.createLinkId("B")); + Mockito.when(linkB.getToNode()).thenReturn(mockNode); + Leg mockLeg = Mockito.mock(Leg.class); Mockito.when(mockLeg.getDepartureTime()).thenReturn(OptionalTime.defined(100.0)); Mockito.when(mockLeg.getMode()).thenReturn("car"); + Activity activity = Mockito.mock(Activity.class); + Mockito.when(activity.getCoord()).thenReturn(new Coord(0.0, 0.0)); + // No crossing points finderMock = new NetworkFinderMock(); processor = new NetworkTripProcessor(finderMock, scenarioExtentMock); - result = processor.process(null, 0, null, Arrays.asList(mockLeg), null); + result = processor.process(null, 0, activity, Arrays.asList(mockLeg), activity, "car"); Assert.assertEquals(1, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); @@ -84,10 +96,10 @@ public void testCarTripProcessor() { // One crossing point, outgoing finderMock = new NetworkFinderMock(); - finderMock.add(new NetworkCrossingPoint(0, linkA, 10.0, 20.0, true)); + finderMock.add(new NetworkTripCrossingPoint(new NetworkRouteCrossingPoint(0, linkA, 10.0, 20.0, true), "car")); processor = new NetworkTripProcessor(finderMock, scenarioExtentMock); - result = processor.process(null, 0, null, Arrays.asList(mockLeg), null); + result = processor.process(null, 0, activity, Arrays.asList(mockLeg), activity, "car"); Assert.assertEquals(3, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); @@ -101,10 +113,10 @@ public void testCarTripProcessor() { // One crossing point, incoming finderMock = new NetworkFinderMock(); - finderMock.add(new NetworkCrossingPoint(0, linkA, 10.0, 20.0, false)); + finderMock.add(new NetworkTripCrossingPoint(new NetworkRouteCrossingPoint(0, linkA, 10.0, 20.0, false), "car")); processor = new NetworkTripProcessor(finderMock, scenarioExtentMock); - result = processor.process(null, 0, null, Arrays.asList(mockLeg), null); + result = processor.process(null, 0, activity, Arrays.asList(mockLeg), activity, "car"); Assert.assertEquals(3, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); @@ -118,11 +130,11 @@ public void testCarTripProcessor() { // Two crossing points, inside -> outside -> inside finderMock = new NetworkFinderMock(); - finderMock.add(new NetworkCrossingPoint(0, linkA, 10.0, 20.0, true)); - finderMock.add(new NetworkCrossingPoint(0, linkB, 30.0, 40.0, false)); + finderMock.add(new NetworkTripCrossingPoint(new NetworkRouteCrossingPoint(0, linkA, 10.0, 20.0, true), "car")); + finderMock.add(new NetworkTripCrossingPoint(new NetworkRouteCrossingPoint(0, linkB, 30.0, 40.0, false), "car")); processor = new NetworkTripProcessor(finderMock, scenarioExtentMock); - result = processor.process(null, 0, null, Arrays.asList(mockLeg), null); + result = processor.process(null, 0, activity, Arrays.asList(mockLeg), activity, "car"); Assert.assertEquals(5, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); @@ -139,120 +151,62 @@ public void testCarTripProcessor() { Assert.assertEquals(40.0, ((Activity) result.get(3)).getEndTime().seconds(), 1e-3); Assert.assertEquals(Id.createLinkId("A"), ((Activity) result.get(1)).getLinkId()); Assert.assertEquals(Id.createLinkId("B"), ((Activity) result.get(3)).getLinkId()); - } - static private Link createLinkMock(String id) { - return new Link() { - @Override - public Coord getCoord() { - return null; - } - - @Override - public Id getId() { - return Id.createLinkId(id); - } - - @Override - public Attributes getAttributes() { - return null; - } - - @Override - public boolean setFromNode(Node node) { - return false; - } - - @Override - public boolean setToNode(Node node) { - return false; - } - - @Override - public Node getToNode() { - return null; - } - - @Override - public Node getFromNode() { - return null; - } - - @Override - public double getLength() { - return 0; - } - - @Override - public double getNumberOfLanes() { - return 0; - } - - @Override - public double getNumberOfLanes(double time) { - return 0; - } - - @Override - public double getFreespeed() { - return 0; - } - - @Override - public double getFreespeed(double time) { - return 0; - } - - @Override - public double getCapacity() { - return 0; - } - - @Override - public double getCapacity(double time) { - return 0; - } - - @Override - public void setFreespeed(double freespeed) { - } - - @Override - public void setLength(double length) { - } - - @Override - public void setNumberOfLanes(double lanes) { - } - - @Override - public void setCapacity(double capacity) { - } - - @Override - public void setAllowedModes(Set modes) { - - } - - @Override - public Set getAllowedModes() { - return null; - } - - @Override - public double getFlowCapacityPerSec() { - return 0; - } - - @Override - public double getFlowCapacityPerSec(double time) { - return 0; - } - - @Override - public double getCapacityPeriod() { - return 0; - } - }; + // Crossing point at the access leg + Leg mockAccessLeg = Mockito.mock(Leg.class); + Mockito.when(mockAccessLeg.getMode()).thenReturn("walk"); + + Activity mockInteractionActivity = Mockito.mock(Activity.class); + Mockito.when(mockInteractionActivity.getType()).thenReturn("car interaction"); + + finderMock = new NetworkFinderMock(); + finderMock.add( + new NetworkTripCrossingPoint(new TeleportationCrossingPoint(new Coord(0.0, 0.0), 10.0, true), "walk")); + finderMock.add(new NetworkTripCrossingPoint(new NetworkRouteCrossingPoint(0, linkB, 30.0, 40.0, false), "car")); + + processor = new NetworkTripProcessor(finderMock, scenarioExtentMock); + result = processor.process(null, 0, activity, Arrays.asList(mockAccessLeg, mockInteractionActivity, mockLeg), + activity, "car"); + + Assert.assertEquals(5, result.size()); + Assert.assertTrue(result.get(0) instanceof Leg); + Assert.assertTrue(result.get(1) instanceof Activity); + Assert.assertTrue(result.get(2) instanceof Leg); + Assert.assertTrue(result.get(3) instanceof Activity); + Assert.assertTrue(result.get(4) instanceof Leg); + Assert.assertEquals("walk", ((Leg) result.get(0)).getMode()); + Assert.assertEquals("outside", ((Activity) result.get(1)).getType()); + Assert.assertEquals("outside", ((Leg) result.get(2)).getMode()); + Assert.assertEquals("outside", ((Activity) result.get(3)).getType()); + Assert.assertEquals("car", ((Leg) result.get(4)).getMode()); + Assert.assertEquals(10.0, ((Activity) result.get(1)).getEndTime().seconds(), 1e-3); + Assert.assertEquals(40.0, ((Activity) result.get(3)).getEndTime().seconds(), 1e-3); + + // Crossing point at the egress leg + Leg mockEgressLeg = Mockito.mock(Leg.class); + Mockito.when(mockEgressLeg.getMode()).thenReturn("walk"); + + finderMock = new NetworkFinderMock(); + finderMock.add(new NetworkTripCrossingPoint(new NetworkRouteCrossingPoint(0, linkB, 30.0, 40.0, true), "car")); + finderMock.add( + new NetworkTripCrossingPoint(new TeleportationCrossingPoint(new Coord(0.0, 0.0), 70.0, false), "walk")); + + processor = new NetworkTripProcessor(finderMock, scenarioExtentMock); + result = processor.process(null, 0, activity, Arrays.asList(mockLeg, mockInteractionActivity, mockEgressLeg), + activity, "car"); + + Assert.assertEquals(5, result.size()); + Assert.assertTrue(result.get(0) instanceof Leg); + Assert.assertTrue(result.get(1) instanceof Activity); + Assert.assertTrue(result.get(2) instanceof Leg); + Assert.assertTrue(result.get(3) instanceof Activity); + Assert.assertTrue(result.get(4) instanceof Leg); + Assert.assertEquals("car", ((Leg) result.get(0)).getMode()); + Assert.assertEquals("outside", ((Activity) result.get(1)).getType()); + Assert.assertEquals("outside", ((Leg) result.get(2)).getMode()); + Assert.assertEquals("outside", ((Activity) result.get(3)).getType()); + Assert.assertEquals("walk", ((Leg) result.get(4)).getMode()); + Assert.assertEquals(40.0, ((Activity) result.get(1)).getEndTime().seconds(), 1e-3); + Assert.assertEquals(70.0, ((Activity) result.get(3)).getEndTime().seconds(), 1e-3); } } diff --git a/core/src/test/java/org/eqasim/scenario/cutter/population/trips/TestTransitTripProcessor.java b/core/src/test/java/org/eqasim/scenario/cutter/population/trips/TestTransitTripProcessor.java index 777e4b7e8..c1da1fa28 100644 --- a/core/src/test/java/org/eqasim/scenario/cutter/population/trips/TestTransitTripProcessor.java +++ b/core/src/test/java/org/eqasim/scenario/cutter/population/trips/TestTransitTripProcessor.java @@ -21,6 +21,8 @@ import org.matsim.pt.transitSchedule.api.TransitRouteStop; import org.matsim.pt.transitSchedule.api.TransitScheduleFactory; import org.matsim.pt.transitSchedule.api.TransitStopFacility; +import org.matsim.utils.objectattributes.attributable.Attributes; +import org.mockito.Mockito; public class TestTransitTripProcessor { static private class PublicTransitFinderMock implements TransitTripCrossingPointFinder { @@ -71,7 +73,7 @@ public void testPublicTransitTripProcessor() { // No crossing points finderMock = new PublicTransitFinderMock(); processor = new TransitTripProcessor(finderMock, scenarioExtentMock, 1.0); - result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false); + result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false, "pt"); Assert.assertEquals(1, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); @@ -83,7 +85,7 @@ public void testPublicTransitTripProcessor() { new TransitRouteCrossingPoint(null, null, outsideStop, insideStop, 20.0, 10.0, true))); processor = new TransitTripProcessor(finderMock, scenarioExtentMock, 1.0); - result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false); + result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false, "pt"); Assert.assertEquals(3, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); @@ -100,7 +102,7 @@ public void testPublicTransitTripProcessor() { finderMock.add(new TransitTripCrossingPoint(new TeleportationCrossingPoint(new Coord(5.0, 0.0), 50.0, true))); processor = new TransitTripProcessor(finderMock, scenarioExtentMock, 1.0); - result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false); + result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false, "pt"); Assert.assertEquals(3, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); @@ -118,7 +120,7 @@ public void testPublicTransitTripProcessor() { new TransitRouteCrossingPoint(null, null, outsideStop, insideStop, 20.0, 10.0, false))); processor = new TransitTripProcessor(finderMock, scenarioExtentMock, 1.0); - result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false); + result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false, "pt"); Assert.assertEquals(3, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); @@ -135,7 +137,7 @@ public void testPublicTransitTripProcessor() { finderMock.add(new TransitTripCrossingPoint(new TeleportationCrossingPoint(new Coord(5.0, 0.0), 50.0, false))); processor = new TransitTripProcessor(finderMock, scenarioExtentMock, 1.0); - result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false); + result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false, "pt"); Assert.assertEquals(3, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); @@ -154,7 +156,7 @@ public void testPublicTransitTripProcessor() { finderMock.add(new TransitTripCrossingPoint(new TeleportationCrossingPoint(new Coord(5.0, 0.0), 50.0, false))); processor = new TransitTripProcessor(finderMock, scenarioExtentMock, 1.0); - result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false); + result = processor.process(new Coord(0.0, 0.0), Collections.emptyList(), new Coord(0.0, 0.0), false, "pt"); Assert.assertEquals(5, result.size()); Assert.assertTrue(result.get(0) instanceof Leg); diff --git a/core/src/test/java/org/eqasim/scenario/cutter/population/trips/crossing/TestDefaultNetworkCrossingPointFinder.java b/core/src/test/java/org/eqasim/scenario/cutter/population/trips/crossing/TestDefaultNetworkCrossingPointFinder.java index 492e12332..31ae078e4 100644 --- a/core/src/test/java/org/eqasim/scenario/cutter/population/trips/crossing/TestDefaultNetworkCrossingPointFinder.java +++ b/core/src/test/java/org/eqasim/scenario/cutter/population/trips/crossing/TestDefaultNetworkCrossingPointFinder.java @@ -6,9 +6,9 @@ import java.util.List; import org.eqasim.core.scenario.cutter.extent.ScenarioExtent; -import org.eqasim.core.scenario.cutter.population.trips.crossing.network.DefaultNetworkCrossingPointFinder; -import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkCrossingPoint; -import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkCrossingPointFinder; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.DefaultNetworkRouteCrossingPointFinder; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkRouteCrossingPoint; +import org.eqasim.core.scenario.cutter.population.trips.crossing.network.NetworkRouteCrossingPointFinder; import org.eqasim.core.scenario.cutter.population.trips.crossing.network.timing.LinkTimingRegistry; import org.junit.Assert; import org.junit.Test; @@ -94,11 +94,11 @@ final static NetworkRoute createRouteMock(int startId, int endId) { @Test public void testFindCrossingPoints() { - NetworkCrossingPointFinder finder = new DefaultNetworkCrossingPointFinder(extentMock, networkMock, + NetworkRouteCrossingPointFinder finder = new DefaultNetworkRouteCrossingPointFinder(extentMock, networkMock, Collections.singletonMap("car", travelTimeMock), new LinkTimingRegistry()); NetworkRoute route; - List result; + List result; // 1) Outside -> Inside route = createRouteMock(1, 3); @@ -170,11 +170,11 @@ public void testFindCrossingPointsFromTiming() { registry.register(personId, 23, Id.createLinkId("23"), 600.0, 700.0); registry.register(personId, 53, Id.createLinkId("23"), 650.0, 750.0); - NetworkCrossingPointFinder finder = new DefaultNetworkCrossingPointFinder(extentMock, networkMock, + NetworkRouteCrossingPointFinder finder = new DefaultNetworkRouteCrossingPointFinder(extentMock, networkMock, Collections.singletonMap("car", travelTimeMock), registry); NetworkRoute route = createRouteMock(1, 3); - List result; + List result; // Leg 23 result = finder.findCrossingPoints(personId, 23, "car", route, 100.0); diff --git a/examples/src/main/java/org/eqasim/examples/corsica_drt/mode_choice/parameters/CorsicaDrtModeParameters.java b/examples/src/main/java/org/eqasim/examples/corsica_drt/mode_choice/parameters/CorsicaDrtModeParameters.java index e12e3f2e1..e9dbd24ee 100644 --- a/examples/src/main/java/org/eqasim/examples/corsica_drt/mode_choice/parameters/CorsicaDrtModeParameters.java +++ b/examples/src/main/java/org/eqasim/examples/corsica_drt/mode_choice/parameters/CorsicaDrtModeParameters.java @@ -26,7 +26,7 @@ public static CorsicaDrtModeParameters buildDefault() { parameters.car.alpha_u = 1.35; parameters.car.betaTravelTime_u_min = -0.06; - parameters.car.constantAccessEgressWalkTime_min = 4.0; + parameters.car.additionalAccessEgressWalkTime_min = 4.0; parameters.car.constantParkingSearchPenalty_min = 4.0; parameters.idfCar.betaInsideUrbanArea = -0.5; diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFModeParameters.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFModeParameters.java index bf948e053..e145acd47 100644 --- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFModeParameters.java +++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFModeParameters.java @@ -27,7 +27,7 @@ public static IDFModeParameters buildDefault() { parameters.car.alpha_u = 1.35; parameters.car.betaTravelTime_u_min = -0.06; - parameters.car.constantAccessEgressWalkTime_min = 4.0; + parameters.car.additionalAccessEgressWalkTime_min = 4.0; parameters.car.constantParkingSearchPenalty_min = 4.0; parameters.idfCar.betaInsideUrbanArea = -0.5; diff --git a/ile_de_france/src/main/resources/corsica/corsica_config.xml b/ile_de_france/src/main/resources/corsica/corsica_config.xml index 386f484a9..4e09c5c7e 100644 --- a/ile_de_france/src/main/resources/corsica/corsica_config.xml +++ b/ile_de_france/src/main/resources/corsica/corsica_config.xml @@ -640,6 +640,7 @@ + diff --git a/ile_de_france/src/test/java/org/eqasim/ile_de_france/TestCorisica.java b/ile_de_france/src/test/java/org/eqasim/ile_de_france/TestCorisica.java index a6b7ae128..9da6b7fd7 100644 --- a/ile_de_france/src/test/java/org/eqasim/ile_de_france/TestCorisica.java +++ b/ile_de_france/src/test/java/org/eqasim/ile_de_france/TestCorisica.java @@ -52,9 +52,9 @@ public void testCorsicaPipeline() Assert.assertEquals(3162, countPersons("corsica_test/simulation_output/output_plans.xml.gz")); Map counts = countLegs("corsica_test/simulation_output/output_events.xml.gz"); - Assert.assertEquals(7781, (long) counts.get("car")); + Assert.assertEquals(7759, (long) counts.get("car")); Assert.assertEquals(894, (long) counts.get("car_passenger")); - Assert.assertEquals(2091, (long) counts.get("walk")); + Assert.assertEquals(3699, (long) counts.get("walk")); Assert.assertEquals(2, (long) counts.get("bike")); Assert.assertEquals(47, (long) counts.get("pt")); } @@ -81,14 +81,18 @@ public void testCorsicaPipeline() "--config:controler.outputDirectory", "corsica_test/cut_output", // }); - Assert.assertEquals(1286, countPersons("corsica_test/cut_output/output_plans.xml.gz")); - Map counts = countLegs("corsica_test/cut_output/output_events.xml.gz"); - Assert.assertEquals(3001, (long) counts.get("car")); + Assert.assertEquals(2991, (long) counts.get("car")); Assert.assertEquals(387, (long) counts.get("car_passenger")); - Assert.assertEquals(847, (long) counts.get("walk")); + // Note that walk has increased from 847 to 7458 when integrating access/egress + // for network-based modes. This is because MATSim really generates an access + // and egress for *every* network-based trip, even if the distance is zero. This + // value is higher than in the baseline (before cutting) case, because in the + // current Corsica scenario, there are no access egress legs in the base version + // (and don't get generated a lot after only two iterations). + Assert.assertEquals(7458, (long) counts.get("walk")); Assert.assertEquals(0, (long) counts.getOrDefault("bike", 0L)); - Assert.assertEquals(6, (long) counts.get("pt")); + Assert.assertEquals(9, (long) counts.get("pt")); Assert.assertEquals(95, (long) counts.get("outside")); } } diff --git a/los_angeles/src/main/java/org/eqasim/los_angeles/mode_choice/parameters/LosAngelesModeParameters.java b/los_angeles/src/main/java/org/eqasim/los_angeles/mode_choice/parameters/LosAngelesModeParameters.java index 0b1cd7161..a0a0c77d7 100644 --- a/los_angeles/src/main/java/org/eqasim/los_angeles/mode_choice/parameters/LosAngelesModeParameters.java +++ b/los_angeles/src/main/java/org/eqasim/los_angeles/mode_choice/parameters/LosAngelesModeParameters.java @@ -45,7 +45,7 @@ public static LosAngelesModeParameters buildDefault() { // Car parameters.car.alpha_u = 0.0; - parameters.car.constantAccessEgressWalkTime_min = 0.0; + parameters.car.additionalAccessEgressWalkTime_min = 0.0; parameters.car.constantParkingSearchPenalty_min = 0.0; parameters.laCar.vot_min = 0.1618; diff --git a/san_francisco/src/main/java/org/eqasim/san_francisco/mode_choice/parameters/SanFranciscoModeParameters.java b/san_francisco/src/main/java/org/eqasim/san_francisco/mode_choice/parameters/SanFranciscoModeParameters.java index fec81454f..97a81a32c 100644 --- a/san_francisco/src/main/java/org/eqasim/san_francisco/mode_choice/parameters/SanFranciscoModeParameters.java +++ b/san_francisco/src/main/java/org/eqasim/san_francisco/mode_choice/parameters/SanFranciscoModeParameters.java @@ -51,7 +51,7 @@ public static SanFranciscoModeParameters buildDefault() { parameters.car.alpha_u = 0.0; parameters.sfCar.vot_min = 0.2714; - parameters.car.constantAccessEgressWalkTime_min = 0.0; + parameters.car.additionalAccessEgressWalkTime_min = 0.0; parameters.car.constantParkingSearchPenalty_min = 0.0; // PT diff --git a/sao_paulo/src/main/java/org/eqasim/sao_paulo/mode_choice/parameters/SaoPauloModeParameters.java b/sao_paulo/src/main/java/org/eqasim/sao_paulo/mode_choice/parameters/SaoPauloModeParameters.java index 74303ea2b..e8199c27a 100644 --- a/sao_paulo/src/main/java/org/eqasim/sao_paulo/mode_choice/parameters/SaoPauloModeParameters.java +++ b/sao_paulo/src/main/java/org/eqasim/sao_paulo/mode_choice/parameters/SaoPauloModeParameters.java @@ -60,7 +60,7 @@ public static SaoPauloModeParameters buildDefault() { parameters.car.alpha_u = 0.0; parameters.car.betaTravelTime_u_min = -0.0246; - parameters.car.constantAccessEgressWalkTime_min = 0.0; + parameters.car.additionalAccessEgressWalkTime_min = 0.0; parameters.car.constantParkingSearchPenalty_min = 0.0; parameters.spCar.alpha_car_city = -0.1597; diff --git a/switzerland/src/main/java/org/eqasim/switzerland/mode_choice/parameters/SwissModeParameters.java b/switzerland/src/main/java/org/eqasim/switzerland/mode_choice/parameters/SwissModeParameters.java index 76316517a..18bfe6872 100644 --- a/switzerland/src/main/java/org/eqasim/switzerland/mode_choice/parameters/SwissModeParameters.java +++ b/switzerland/src/main/java/org/eqasim/switzerland/mode_choice/parameters/SwissModeParameters.java @@ -27,7 +27,7 @@ public static SwissModeParameters buildDefault() { parameters.car.alpha_u = 0.827; parameters.car.betaTravelTime_u_min = -0.067; - parameters.car.constantAccessEgressWalkTime_min = 4.0; + parameters.car.additionalAccessEgressWalkTime_min = 4.0; parameters.car.constantParkingSearchPenalty_min = 4.0; parameters.swissCar.betaStatedPreferenceRegion1_u = -0.4;