Skip to content

Commit 49986d3

Browse files
authored
feat: Feeder DRT automatically detects DRT service areas and considers them during routing (#248)
* feat: support of drt service area in feeder drt * fix: handling the absence of a skippedFacilities regex
1 parent 0c9a6c6 commit 49986d3

3 files changed

Lines changed: 80 additions & 30 deletions

File tree

core/src/main/java/org/eqasim/core/simulation/modes/feeder_drt/FeederDrtModeModule.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.google.inject.Inject;
44
import com.google.inject.Injector;
55
import com.google.inject.Provider;
6+
import org.eqasim.core.scenario.cutter.extent.ScenarioExtent;
7+
import org.eqasim.core.scenario.cutter.extent.ShapeScenarioExtent;
68
import org.eqasim.core.simulation.modes.feeder_drt.config.AccessEgressStopSelectorParams;
79
import org.eqasim.core.simulation.modes.feeder_drt.config.FeederDrtConfigGroup;
810
import org.eqasim.core.simulation.modes.feeder_drt.router.FeederDrtRoutingModule;
@@ -11,15 +13,23 @@
1113
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_selector.ClosestAccessEgressStopSelectorParameterSet;
1214
import org.matsim.api.core.v01.network.Network;
1315
import org.matsim.api.core.v01.population.Population;
16+
import org.matsim.contrib.drt.run.DrtConfigGroup;
17+
import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup;
1418
import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule;
1519
import org.matsim.contrib.dvrp.run.DvrpMode;
1620
import org.matsim.contrib.dvrp.run.DvrpModes;
21+
import org.matsim.core.config.ConfigGroup;
1722
import org.matsim.core.config.ReflectiveConfigGroup;
1823
import org.matsim.core.modal.ModalAnnotationCreator;
1924
import org.matsim.core.router.RoutingModule;
2025
import org.matsim.pt.transitSchedule.api.TransitSchedule;
2126

27+
import java.io.File;
28+
import java.io.IOException;
29+
import java.net.URI;
30+
import java.net.URISyntaxException;
2231
import java.util.Map;
32+
import java.util.Optional;
2333

2434

2535
public class FeederDrtModeModule extends AbstractDvrpModeModule {
@@ -34,6 +44,25 @@ public FeederDrtModeModule(FeederDrtConfigGroup feederDrtConfigGroup) {
3444

3545
@Override
3646
public void install() {
47+
MultiModeDrtConfigGroup multiModeDrtConfigGroup = (MultiModeDrtConfigGroup) getConfig().getModules().get(MultiModeDrtConfigGroup.GROUP_NAME);
48+
DrtConfigGroup coveredDrtConfig = multiModeDrtConfigGroup.getModalElements().stream().filter(drtConfigGroup -> drtConfigGroup.getMode().equals(this.config.accessEgressModeName)).findFirst().get();
49+
50+
ScenarioExtent serviceAreaExtent = null;
51+
if(coveredDrtConfig.operationalScheme.equals(DrtConfigGroup.OperationalScheme.serviceAreaBased)) {
52+
URI extentPath;
53+
try {
54+
extentPath = ConfigGroup.getInputFileURL(getConfig().getContext(), coveredDrtConfig.drtServiceAreaShapeFile).toURI();
55+
} catch (URISyntaxException e) {
56+
throw new RuntimeException(e);
57+
}
58+
try {
59+
serviceAreaExtent = new ShapeScenarioExtent.Builder(new File(extentPath), Optional.empty(), Optional.empty()).build();
60+
} catch (IOException e) {
61+
throw new RuntimeException(e);
62+
}
63+
}
64+
ScenarioExtent finalServiceAreaExtent = serviceAreaExtent;
65+
3766
FeederDrtConfigGroup feederDrtConfigGroup = this.config;
3867
ReflectiveConfigGroup reflectiveConfigGroup = feederDrtConfigGroup.getAccessEgressStopSelectorConfig().getAccessEgressStopSelectorParams();
3968
if(reflectiveConfigGroup instanceof ClosestAccessEgressStopSelectorParameterSet closestAccessEgressStopSelectorConfig) {
@@ -48,7 +77,7 @@ public void install() {
4877
public AccessEgressStopsSelector get() {
4978
ModalAnnotationCreator<DvrpMode> modalAnnotationCreator = DvrpModes::mode;
5079
Provider<Network> networkProvider = injector.getProvider(modalAnnotationCreator.key(Network.class, feederDrtConfigGroup.accessEgressModeName));
51-
return new ClosestAccessEgressStopSelector(closestAccessEgressStopSelectorConfig, networkProvider.get(), transitSchedule);
80+
return new ClosestAccessEgressStopSelector(closestAccessEgressStopSelectorConfig, networkProvider.get(), transitSchedule, finalServiceAreaExtent);
5281
}
5382
}).asEagerSingleton();
5483
} else {
@@ -71,7 +100,7 @@ public RoutingModule get() {
71100
RoutingModule drtRoutingModule = routingModuleProviders.get(feederDrtConfigGroup.accessEgressModeName).get();
72101
ModalAnnotationCreator<DvrpMode> modalAnnotationCreator = DvrpModes::mode;
73102
Provider<AccessEgressStopsSelector> accessEgressStopsSelectorProvider = injector.getProvider(modalAnnotationCreator.key(AccessEgressStopsSelector.class, feederDrtConfigGroup.mode));
74-
return new FeederDrtRoutingModule(feederDrtConfigGroup.mode, drtRoutingModule, ptRoutingModule, population.getFactory(), accessEgressStopsSelectorProvider.get());
103+
return new FeederDrtRoutingModule(feederDrtConfigGroup.mode, drtRoutingModule, ptRoutingModule, population.getFactory(), accessEgressStopsSelectorProvider.get(), finalServiceAreaExtent);
75104
}
76105
});
77106
}

core/src/main/java/org/eqasim/core/simulation/modes/feeder_drt/router/FeederDrtRoutingModule.java

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.eqasim.core.simulation.modes.feeder_drt.router;
22

3+
import org.eqasim.core.scenario.cutter.extent.ScenarioExtent;
34
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_selector.AccessEgressStopsSelector;
45
import org.matsim.api.core.v01.population.*;
56
import org.matsim.core.router.DefaultRoutingRequest;
@@ -23,14 +24,17 @@ public enum FeederDrtTripSegmentType {MAIN, DRT}
2324

2425
private final String mode;
2526
private final AccessEgressStopsSelector accessEgressStopsSelector;
27+
private final ScenarioExtent drtServiceAreaExtent;
2628

2729
public FeederDrtRoutingModule(String mode,RoutingModule feederRoutingModule, RoutingModule transitRoutingModule,
28-
PopulationFactory populationFactory, AccessEgressStopsSelector accessEgressStopsSelector) {
30+
PopulationFactory populationFactory, AccessEgressStopsSelector accessEgressStopsSelector,
31+
ScenarioExtent drtServiceAreaExtent) {
2932
this.mode = mode;
3033
this.drtRoutingModule = feederRoutingModule;
3134
this.transitRoutingModule = transitRoutingModule;
3235
this.populationFactory = populationFactory;
3336
this.accessEgressStopsSelector = accessEgressStopsSelector;
37+
this.drtServiceAreaExtent = drtServiceAreaExtent;
3438
}
3539

3640
@Override
@@ -46,19 +50,20 @@ public List<? extends PlanElement> calcRoute(RoutingRequest routingRequest) {
4650
Facility egressFacility = this.accessEgressStopsSelector.getEgressFacility(routingRequest);
4751

4852
List<PlanElement> intermodalRoute = new LinkedList<>();
49-
// Computing the access DRT route
50-
List<? extends PlanElement> drtRoute = null;
53+
List<? extends PlanElement> accessDrtRoute = null;
54+
List<? extends PlanElement> egressDrtRoute = null;
5155

52-
if (accessFacility != null) {
53-
drtRoute = drtRoutingModule.calcRoute(DefaultRoutingRequest.withoutAttributes(fromFacility, accessFacility, departureTime, person));
56+
// Computing the access DRT route if it's possible
57+
if (accessFacility != null && (drtServiceAreaExtent == null || drtServiceAreaExtent.isInside(fromFacility.getCoord()))) {
58+
accessDrtRoute = drtRoutingModule.calcRoute(DefaultRoutingRequest.withoutAttributes(fromFacility, accessFacility, departureTime, person));
5459
}
5560
double accessTime = departureTime;
56-
if (drtRoute == null) {
61+
if (accessDrtRoute == null) {
5762
// if no DRT route, next part of the trip starts from the origin
5863
accessFacility = fromFacility;
5964
} else {
6065
//Otherwise we have already a first part of the trip
61-
intermodalRoute.addAll(drtRoute);
66+
intermodalRoute.addAll(accessDrtRoute);
6267
for (PlanElement element : intermodalRoute) {
6368
if (element instanceof Leg leg) {
6469
accessTime = Math.max(accessTime, leg.getDepartureTime().seconds());
@@ -71,37 +76,44 @@ public List<? extends PlanElement> calcRoute(RoutingRequest routingRequest) {
7176
intermodalRoute.add(accessInteractionActivity);
7277
}
7378

74-
// Compute the PT part of the route
75-
List<PlanElement> ptRoute = new LinkedList<>(transitRoutingModule.calcRoute(DefaultRoutingRequest.withoutAttributes(accessFacility, egressFacility, accessTime, person)));
76-
double egressTime = accessTime;
7779

78-
for (PlanElement element : ptRoute) {
79-
if (element instanceof Leg leg) {
80-
egressTime = Math.max(egressTime, leg.getDepartureTime().seconds());
81-
egressTime += leg.getTravelTime().seconds();
82-
leg.getAttributes().putAttribute(CURRENT_SEGMENT_TYPE_ATTR, FeederDrtTripSegmentType.MAIN);
83-
}
80+
// We have to check the existence of the egress facility here, the pt router will not support a null value
81+
if (egressFacility == null || (drtServiceAreaExtent != null && !drtServiceAreaExtent.isInside(toFacility.getCoord()))) {
82+
egressFacility = toFacility;
8483
}
8584

86-
if (egressFacility != null) {
87-
drtRoute = drtRoutingModule.calcRoute(DefaultRoutingRequest.withoutAttributes(egressFacility, toFacility, egressTime, person));
88-
} else {
89-
drtRoute = null;
85+
// Compute the PT part of the route towards the egress (or to) facility
86+
List<PlanElement> ptRoute = new LinkedList<>(transitRoutingModule.calcRoute(DefaultRoutingRequest.withoutAttributes(accessFacility, egressFacility, accessTime, person)));
87+
ptRoute.stream().filter(planElement -> planElement instanceof Leg).map(planElement -> (Leg) planElement).forEach(leg -> leg.getAttributes().putAttribute(CURRENT_SEGMENT_TYPE_ATTR, FeederDrtTripSegmentType.MAIN));
88+
89+
// It's ok to compare reference here, we want to check if we assigned toFacility to egressFacility above
90+
if(egressFacility != toFacility) {
91+
double egressTime = accessTime;
92+
for (PlanElement element : ptRoute) {
93+
if (element instanceof Leg leg) {
94+
egressTime = Math.max(egressTime, leg.getDepartureTime().seconds());
95+
egressTime += leg.getTravelTime().seconds();
96+
}
97+
}
98+
egressDrtRoute = drtRoutingModule.calcRoute(DefaultRoutingRequest.withoutAttributes(egressFacility, toFacility, egressTime, person));
9099
}
91100

92-
// If no valid DRT route is found, we recompute a PT route from the access facility to the trip destination
93-
if (drtRoute == null) {
94-
ptRoute = new LinkedList<>(transitRoutingModule.calcRoute(DefaultRoutingRequest.withoutAttributes(accessFacility, toFacility, accessTime, person)));
95-
ptRoute.stream().filter(planElement -> planElement instanceof Leg).map(planElement -> (Leg) planElement).forEach(leg -> leg.getAttributes().putAttribute(CURRENT_SEGMENT_TYPE_ATTR, FeederDrtTripSegmentType.MAIN));
101+
// egressDrtRoute is assigned only in the above if, but it can be null without entering the if block, so we need to do this here, If no valid DRT route is found, we recompute a PT route from the access facility to the trip destination
102+
if (egressDrtRoute == null) {
103+
if(egressFacility != toFacility) {
104+
// In this case, the egressDrtRoute is null because the attempt to compute one wasn't successful, so we need to compute a pt route from the access (or from facility) to the to facility
105+
ptRoute = new LinkedList<>(transitRoutingModule.calcRoute(DefaultRoutingRequest.withoutAttributes(accessFacility, toFacility, accessTime, person)));
106+
ptRoute.stream().filter(planElement -> planElement instanceof Leg).map(planElement -> (Leg) planElement).forEach(leg -> leg.getAttributes().putAttribute(CURRENT_SEGMENT_TYPE_ATTR, FeederDrtTripSegmentType.MAIN));
107+
}
96108
intermodalRoute.addAll(ptRoute);
97109
} else {
98-
// Otherwise we add it as an egress to the whole route
110+
// Here we have a pt route and an egress drt route, we need to propriately concatenate them in the overall route
99111
intermodalRoute.addAll(ptRoute);
100112
Activity egressInteractionActivity = populationFactory.createActivityFromLinkId(this.mode + " interaction", egressFacility.getLinkId());
101113
egressInteractionActivity.setMaximumDuration(0);
102-
drtRoute.stream().filter(planElement -> planElement instanceof Leg).map(planElement -> (Leg) planElement).forEach(leg -> leg.getAttributes().putAttribute(CURRENT_SEGMENT_TYPE_ATTR, FeederDrtTripSegmentType.DRT));
114+
egressDrtRoute.stream().filter(planElement -> planElement instanceof Leg).map(planElement -> (Leg) planElement).forEach(leg -> leg.getAttributes().putAttribute(CURRENT_SEGMENT_TYPE_ATTR, FeederDrtTripSegmentType.DRT));
103115
intermodalRoute.add(egressInteractionActivity);
104-
intermodalRoute.addAll(drtRoute);
116+
intermodalRoute.addAll(egressDrtRoute);
105117
}
106118
return intermodalRoute;
107119
}

core/src/main/java/org/eqasim/core/simulation/modes/feeder_drt/router/access_egress_selector/ClosestAccessEgressStopSelector.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.apache.logging.log4j.LogManager;
44
import org.apache.logging.log4j.Logger;
5+
import org.eqasim.core.scenario.cutter.extent.ScenarioExtent;
56
import org.matsim.api.core.v01.Id;
67
import org.matsim.api.core.v01.network.Network;
78
import org.matsim.core.network.NetworkUtils;
@@ -24,6 +25,10 @@ public class ClosestAccessEgressStopSelector implements AccessEgressStopsSelecto
2425
private final Pattern skippedFacilitiesIdPattern;
2526

2627
public ClosestAccessEgressStopSelector(ClosestAccessEgressStopSelectorParameterSet config, Network drtNetwork, TransitSchedule schedule) {
28+
this(config, drtNetwork, schedule, null);
29+
}
30+
31+
public ClosestAccessEgressStopSelector(ClosestAccessEgressStopSelectorParameterSet config, Network drtNetwork, TransitSchedule schedule, ScenarioExtent serviceAreaExtent) {
2732
logger.info("Starting initialization");
2833
if(config.skipAccessAndEgressAtFacilities.length() > 0) {
2934
this.skippedFacilitiesIdPattern = Pattern.compile(config.skipAccessAndEgressAtFacilities);
@@ -44,6 +49,10 @@ public ClosestAccessEgressStopSelector(ClosestAccessEgressStopSelectorParameterS
4449
if (accessEgressTransitStopModes.size() == 0 || accessEgressTransitStopModes.contains(transitRoute.getTransportMode())) {
4550
for (TransitRouteStop transitRouteStop : transitRoute.getStops()) {
4651
TransitStopFacility transitStopFacility = transitRouteStop.getStopFacility();
52+
if(serviceAreaExtent != null && !serviceAreaExtent.isInside(transitStopFacility.getCoord())) {
53+
logger.warn("skipping this stop because it's outside of the service area: " + transitStopFacility.getName());
54+
continue;
55+
}
4756
if (!processedFacilities.contains(transitStopFacility.getId())) {
4857
processedFacilities.add(transitStopFacility.getId());
4958
Facility interactionFacility = FacilitiesUtils.wrapLink(NetworkUtils.getNearestLink(drtNetwork, transitStopFacility.getCoord()));
@@ -68,7 +77,7 @@ public ClosestAccessEgressStopSelector(ClosestAccessEgressStopSelectorParameterS
6877
}
6978

7079
private boolean skipFacility(Facility facility) {
71-
if(facility instanceof ActivityFacilityImpl activityFacility) {
80+
if(this.skippedFacilitiesIdPattern != null && facility instanceof ActivityFacilityImpl activityFacility) {
7281
return skippedFacilitiesIdPattern.matcher(activityFacility.getId().toString()).matches();
7382
}
7483
return false;

0 commit comments

Comments
 (0)