Skip to content

Commit 83a53fe

Browse files
authored
Feat: [FeederDrt] a more flexible structure for access and egress stop selection (#258)
1 parent ed29048 commit 83a53fe

18 files changed

+504
-169
lines changed

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

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,21 @@
55
import com.google.inject.Provider;
66
import org.eqasim.core.scenario.cutter.extent.ScenarioExtent;
77
import org.eqasim.core.scenario.cutter.extent.ShapeScenarioExtent;
8-
import org.eqasim.core.simulation.modes.feeder_drt.config.AccessEgressStopSelectorParams;
98
import org.eqasim.core.simulation.modes.feeder_drt.config.FeederDrtConfigGroup;
109
import org.eqasim.core.simulation.modes.feeder_drt.router.FeederDrtRoutingModule;
11-
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_selector.AccessEgressStopsSelector;
12-
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_selector.ClosestAccessEgressStopSelector;
13-
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_selector.ClosestAccessEgressStopSelectorParameterSet;
14-
import org.matsim.api.core.v01.network.Network;
10+
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_search.AccessEgressStopSearch;
11+
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_search.AccessEgressStopSearchModule;
12+
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_selection.AccessEgressStopSelector;
13+
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_selection.ClosestAccessEgressStopSelector;
1514
import org.matsim.api.core.v01.population.Population;
1615
import org.matsim.contrib.drt.run.DrtConfigGroup;
1716
import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup;
1817
import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule;
1918
import org.matsim.contrib.dvrp.run.DvrpMode;
2019
import org.matsim.contrib.dvrp.run.DvrpModes;
2120
import org.matsim.core.config.ConfigGroup;
22-
import org.matsim.core.config.ReflectiveConfigGroup;
2321
import org.matsim.core.modal.ModalAnnotationCreator;
2422
import org.matsim.core.router.RoutingModule;
25-
import org.matsim.pt.transitSchedule.api.TransitSchedule;
2623

2724
import java.io.File;
2825
import java.io.IOException;
@@ -64,24 +61,11 @@ public void install() {
6461
ScenarioExtent finalServiceAreaExtent = serviceAreaExtent;
6562

6663
FeederDrtConfigGroup feederDrtConfigGroup = this.config;
67-
ReflectiveConfigGroup reflectiveConfigGroup = feederDrtConfigGroup.getAccessEgressStopSelectorConfig().getAccessEgressStopSelectorParams();
68-
if(reflectiveConfigGroup instanceof ClosestAccessEgressStopSelectorParameterSet closestAccessEgressStopSelectorConfig) {
69-
bindModal(AccessEgressStopsSelector.class).toProvider(new Provider<>() {
70-
@Inject
71-
private Injector injector;
64+
install(new AccessEgressStopSearchModule(feederDrtConfigGroup.accessEgressStopSearchParams, feederDrtConfigGroup, coveredDrtConfig));
7265

73-
@Inject
74-
private TransitSchedule transitSchedule;
75-
76-
@Override
77-
public AccessEgressStopsSelector get() {
78-
ModalAnnotationCreator<DvrpMode> modalAnnotationCreator = DvrpModes::mode;
79-
Provider<Network> networkProvider = injector.getProvider(modalAnnotationCreator.key(Network.class, feederDrtConfigGroup.accessEgressModeName));
80-
return new ClosestAccessEgressStopSelector(closestAccessEgressStopSelectorConfig, networkProvider.get(), transitSchedule, finalServiceAreaExtent);
81-
}
82-
}).asEagerSingleton();
83-
} else {
84-
throw new RuntimeException(String.format("Unsupported %s: %s", AccessEgressStopSelectorParams.NAME, reflectiveConfigGroup));
66+
switch (this.config.accessEgressStopSelection) {
67+
case CLOSEST -> bindModal(AccessEgressStopSelector.class).to(ClosestAccessEgressStopSelector.class);
68+
// Extend here when more selectors are introduced
8569
}
8670

8771
addRoutingModuleBinding(this.config.mode).toProvider(new Provider<>() {
@@ -99,8 +83,9 @@ public RoutingModule get() {
9983
RoutingModule ptRoutingModule = routingModuleProviders.get(feederDrtConfigGroup.ptModeName).get();
10084
RoutingModule drtRoutingModule = routingModuleProviders.get(feederDrtConfigGroup.accessEgressModeName).get();
10185
ModalAnnotationCreator<DvrpMode> modalAnnotationCreator = DvrpModes::mode;
102-
Provider<AccessEgressStopsSelector> accessEgressStopsSelectorProvider = injector.getProvider(modalAnnotationCreator.key(AccessEgressStopsSelector.class, feederDrtConfigGroup.mode));
103-
return new FeederDrtRoutingModule(feederDrtConfigGroup.mode, drtRoutingModule, ptRoutingModule, population.getFactory(), accessEgressStopsSelectorProvider.get(), finalServiceAreaExtent);
86+
Provider<AccessEgressStopSelector> accessEgressStopsSelectorProvider = injector.getProvider(modalAnnotationCreator.key(AccessEgressStopSelector.class, feederDrtConfigGroup.mode));
87+
Provider<AccessEgressStopSearch> accessEgressStopGeneratorProvider = injector.getProvider(modalAnnotationCreator.key(AccessEgressStopSearch.class, feederDrtConfigGroup.mode));
88+
return new FeederDrtRoutingModule(feederDrtConfigGroup.mode, drtRoutingModule, ptRoutingModule, population.getFactory(), accessEgressStopGeneratorProvider.get(), accessEgressStopsSelectorProvider.get(), finalServiceAreaExtent, feederDrtConfigGroup.skipAccessAndEgressAtFacilities);
10489
}
10590
});
10691
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.eqasim.core.simulation.modes.feeder_drt.config;
2+
3+
import org.matsim.core.config.ReflectiveConfigGroup;
4+
5+
public class AccessEgressStopSearchParams extends ReflectiveConfigGroup {
6+
public AccessEgressStopSearchParams(String name) {
7+
super(name);
8+
}
9+
}

core/src/main/java/org/eqasim/core/simulation/modes/feeder_drt/config/AccessEgressStopSelectorParams.java

Lines changed: 0 additions & 24 deletions
This file was deleted.

core/src/main/java/org/eqasim/core/simulation/modes/feeder_drt/config/FeederDrtConfigGroup.java

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,26 @@
22

33
import jakarta.validation.constraints.NotBlank;
44
import jakarta.validation.constraints.NotNull;
5+
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_search.CompositeAccessEgressStopSearchParameterSet;
6+
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_search.TransitStopByIdAccessEgressStopSearchParameterSet;
7+
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_search.TransitStopByModeAccessEgressStopSearchParameterSet;
58
import org.matsim.contrib.common.util.ReflectiveConfigGroupWithConfigurableParameterSets;
69
import org.matsim.contrib.dvrp.run.Modal;
710

811
public class FeederDrtConfigGroup extends ReflectiveConfigGroupWithConfigurableParameterSets implements Modal {
912
public static final String GROUP_NAME = "feederDrt";
1013
public FeederDrtConfigGroup() {
1114
super(GROUP_NAME);
12-
this.addDefinition(AccessEgressStopSelectorParams.NAME, AccessEgressStopSelectorParams::new, () -> this.accessEgressStopSelectorConfig,
13-
params -> this.accessEgressStopSelectorConfig = (AccessEgressStopSelectorParams) params);
15+
16+
//AccessEgressStopSearch
17+
this.addDefinition(TransitStopByModeAccessEgressStopSearchParameterSet.NAME, TransitStopByModeAccessEgressStopSearchParameterSet::new, () -> this.accessEgressStopSearchParams,
18+
params -> this.accessEgressStopSearchParams = (TransitStopByModeAccessEgressStopSearchParameterSet) params);
19+
20+
this.addDefinition(TransitStopByIdAccessEgressStopSearchParameterSet.NAME, TransitStopByIdAccessEgressStopSearchParameterSet::new, () -> this.accessEgressStopSearchParams,
21+
params -> this.accessEgressStopSearchParams = (TransitStopByIdAccessEgressStopSearchParameterSet) params);
22+
23+
this.addDefinition(CompositeAccessEgressStopSearchParameterSet.NAME, CompositeAccessEgressStopSearchParameterSet::new, () -> this.accessEgressStopSearchParams,
24+
params -> this.accessEgressStopSearchParams = (CompositeAccessEgressStopSearchParameterSet) params);
1425
}
1526

1627
@Parameter
@@ -28,15 +39,21 @@ public FeederDrtConfigGroup() {
2839
@NotBlank
2940
public String mode = "feederDrt";
3041

42+
@Parameter
43+
@Comment("A regex that if matches with the from facility id (resp to facility id) will result in an access (resp egress) drt leg not being constructed. Leave empty to consider access and egress segments at all times")
44+
public String skipAccessAndEgressAtFacilities;
45+
3146
@NotNull
32-
private AccessEgressStopSelectorParams accessEgressStopSelectorConfig;
47+
public AccessEgressStopSearchParams accessEgressStopSearchParams;
48+
49+
public enum AccessEgressStopSelection {CLOSEST}
50+
51+
@Parameter
52+
@NotNull
53+
public AccessEgressStopSelection accessEgressStopSelection = AccessEgressStopSelection.CLOSEST;
3354

3455
@Override
3556
public String getMode() {
3657
return this.mode;
3758
}
38-
39-
public AccessEgressStopSelectorParams getAccessEgressStopSelectorConfig() {
40-
return this.accessEgressStopSelectorConfig;
41-
}
4259
}

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

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package org.eqasim.core.simulation.modes.feeder_drt.router;
22

33
import org.eqasim.core.scenario.cutter.extent.ScenarioExtent;
4-
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_selector.AccessEgressStopsSelector;
4+
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_search.AccessEgressStopSearch;
5+
import org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_selection.AccessEgressStopSelector;
56
import org.matsim.api.core.v01.population.*;
67
import org.matsim.core.router.DefaultRoutingRequest;
78
import org.matsim.core.router.RoutingModule;
89
import org.matsim.core.router.RoutingRequest;
10+
import org.matsim.facilities.ActivityFacilityImpl;
911
import org.matsim.facilities.Facility;
1012

1113
import java.util.*;
14+
import java.util.regex.Pattern;
1215

1316

1417
public class FeederDrtRoutingModule implements RoutingModule {
@@ -23,18 +26,26 @@ public enum FeederDrtTripSegmentType {MAIN, DRT}
2326
private final PopulationFactory populationFactory;
2427

2528
private final String mode;
26-
private final AccessEgressStopsSelector accessEgressStopsSelector;
29+
private final AccessEgressStopSelector accessEgressStopSelector;
2730
private final ScenarioExtent drtServiceAreaExtent;
31+
private final AccessEgressStopSearch accessEgressStopSearch;
32+
private final Pattern skippedFacilitiesIdPattern;
2833

29-
public FeederDrtRoutingModule(String mode,RoutingModule feederRoutingModule, RoutingModule transitRoutingModule,
30-
PopulationFactory populationFactory, AccessEgressStopsSelector accessEgressStopsSelector,
31-
ScenarioExtent drtServiceAreaExtent) {
34+
public FeederDrtRoutingModule(String mode, RoutingModule feederRoutingModule, RoutingModule transitRoutingModule,
35+
PopulationFactory populationFactory, AccessEgressStopSearch accessEgressStopSearch, AccessEgressStopSelector accessEgressStopSelector,
36+
ScenarioExtent drtServiceAreaExtent, String skipAccessAndEgressAtFacilities) {
3237
this.mode = mode;
3338
this.drtRoutingModule = feederRoutingModule;
3439
this.transitRoutingModule = transitRoutingModule;
3540
this.populationFactory = populationFactory;
36-
this.accessEgressStopsSelector = accessEgressStopsSelector;
41+
this.accessEgressStopSearch = accessEgressStopSearch;
42+
this.accessEgressStopSelector = accessEgressStopSelector;
3743
this.drtServiceAreaExtent = drtServiceAreaExtent;
44+
if(skipAccessAndEgressAtFacilities.length() > 0) {
45+
this.skippedFacilitiesIdPattern = Pattern.compile(skipAccessAndEgressAtFacilities);
46+
} else {
47+
skippedFacilitiesIdPattern = null;
48+
}
3849
}
3950

4051
@Override
@@ -45,9 +56,15 @@ public List<? extends PlanElement> calcRoute(RoutingRequest routingRequest) {
4556
Person person = routingRequest.getPerson();
4657

4758

48-
// Identify closest stations from the origin and destination of the trip
49-
Facility accessFacility = this.accessEgressStopsSelector.getAccessFacility(routingRequest);
50-
Facility egressFacility = this.accessEgressStopsSelector.getEgressFacility(routingRequest);
59+
// Identify closest stations from the origin and destination of the trip (if they are not skipped)
60+
Facility accessFacility = null;
61+
if(!skipFacility(routingRequest.getFromFacility())) {
62+
accessFacility = this.accessEgressStopSelector.getAccessFacility(routingRequest, this.accessEgressStopSearch.getAccessFacilitiesQuadTree());
63+
}
64+
Facility egressFacility = null;
65+
if(!skipFacility(routingRequest.getToFacility())) {
66+
egressFacility = this.accessEgressStopSelector.getEgressFacility(routingRequest, this.accessEgressStopSearch.getEgressFacilitiesQuadTree());
67+
}
5168

5269
List<PlanElement> intermodalRoute = new LinkedList<>();
5370
List<? extends PlanElement> accessDrtRoute = null;
@@ -117,4 +134,11 @@ public List<? extends PlanElement> calcRoute(RoutingRequest routingRequest) {
117134
}
118135
return intermodalRoute;
119136
}
137+
138+
private boolean skipFacility(Facility facility) {
139+
if(this.skippedFacilitiesIdPattern != null && facility instanceof ActivityFacilityImpl activityFacility) {
140+
return skippedFacilitiesIdPattern.matcher(activityFacility.getId().toString()).matches();
141+
}
142+
return false;
143+
}
120144
}

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

Lines changed: 0 additions & 9 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_search;
2+
3+
import org.matsim.core.utils.collections.QuadTree;
4+
import org.matsim.facilities.Facility;
5+
6+
import java.util.Collection;
7+
8+
public interface AccessEgressStopSearch {
9+
Collection<Facility> getAccessFacilitiesCollection();
10+
QuadTree<Facility> getAccessFacilitiesQuadTree();
11+
Collection<Facility> getEgressFacilitiesCollection();
12+
QuadTree<Facility> getEgressFacilitiesQuadTree();
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package org.eqasim.core.simulation.modes.feeder_drt.router.access_egress_stop_search;
2+
3+
import com.google.inject.Inject;
4+
import com.google.inject.Injector;
5+
import com.google.inject.Provider;
6+
import org.eqasim.core.scenario.cutter.extent.ScenarioExtent;
7+
import org.eqasim.core.scenario.cutter.extent.ShapeScenarioExtent;
8+
import org.eqasim.core.simulation.modes.feeder_drt.config.AccessEgressStopSearchParams;
9+
import org.eqasim.core.simulation.modes.feeder_drt.config.FeederDrtConfigGroup;
10+
import org.matsim.api.core.v01.network.Network;
11+
import org.matsim.contrib.drt.run.DrtConfigGroup;
12+
import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule;
13+
import org.matsim.contrib.dvrp.run.DvrpMode;
14+
import org.matsim.contrib.dvrp.run.DvrpModes;
15+
import org.matsim.core.config.ConfigGroup;
16+
import org.matsim.core.modal.ModalAnnotationCreator;
17+
import org.matsim.pt.transitSchedule.api.TransitSchedule;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.net.URI;
22+
import java.net.URISyntaxException;
23+
import java.util.List;
24+
import java.util.Optional;
25+
26+
public class AccessEgressStopSearchModule extends AbstractDvrpModeModule {
27+
28+
private final AccessEgressStopSearchParams config;
29+
private final FeederDrtConfigGroup feederDrtConfigGroup;
30+
private final DrtConfigGroup coveredDrtConfigGroup;
31+
32+
public AccessEgressStopSearchModule(AccessEgressStopSearchParams accessEgressStopSearchParams, FeederDrtConfigGroup feederDrtConfigGroup, DrtConfigGroup coveredDrtConfigGroup) {
33+
super(feederDrtConfigGroup.mode);
34+
this.config = accessEgressStopSearchParams;
35+
this.feederDrtConfigGroup = feederDrtConfigGroup;
36+
this.coveredDrtConfigGroup = coveredDrtConfigGroup;
37+
}
38+
39+
@Override
40+
public void install() {
41+
bindModal(AccessEgressStopSearch.class).toProvider(getAccessEgressStopSearchProvider(config)).asEagerSingleton();
42+
}
43+
44+
private AccessEgressStopSearch getAccessEgressStopSearch(AccessEgressStopSearchParams accessEgressStopSearchParams, Injector injector) {
45+
ModalAnnotationCreator<DvrpMode> modalAnnotationCreator = DvrpModes::mode;
46+
Provider<Network> networkProvider = injector.getProvider(modalAnnotationCreator.key(Network.class, feederDrtConfigGroup.accessEgressModeName));
47+
TransitSchedule transitSchedule = injector.getInstance(TransitSchedule.class);
48+
49+
if(accessEgressStopSearchParams instanceof TransitStopByModeAccessEgressStopSearchParameterSet transitStopByModeAccessEgressStopSearchParameterSet) {
50+
ScenarioExtent serviceAreaExtent = null;
51+
if (coveredDrtConfigGroup.operationalScheme.equals(DrtConfigGroup.OperationalScheme.serviceAreaBased)) {
52+
URI extentPath;
53+
try {
54+
extentPath = ConfigGroup.getInputFileURL(getConfig().getContext(), coveredDrtConfigGroup.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+
return new TransitStopByModeAccessEgressStopSearch(transitStopByModeAccessEgressStopSearchParameterSet, networkProvider.get(), transitSchedule, serviceAreaExtent);
65+
} else if(accessEgressStopSearchParams instanceof TransitStopByIdAccessEgressStopSearchParameterSet transitStopByIdAccessEgressStopSearchParameterSet) {
66+
return new TransitStopByIdAccessEgressStopSearch(transitStopByIdAccessEgressStopSearchParameterSet, transitSchedule, networkProvider.get());
67+
} else if(accessEgressStopSearchParams instanceof CompositeAccessEgressStopSearchParameterSet compositeAccessEgressStopSearchParameterSet) {
68+
List<AccessEgressStopSearch> delegates = compositeAccessEgressStopSearchParameterSet.getDelegateAccessEgressStopSearchParamSets().stream().map(params -> getAccessEgressStopSearch(params, injector)).toList();
69+
return new CompositeAccessEgressStopSearch(delegates, networkProvider.get());
70+
} else {
71+
throw new IllegalStateException(String.format("Unhandled subclass of AccessEgressStopSearchParams '%s'", accessEgressStopSearchParams.getClass().toString()));
72+
}
73+
}
74+
75+
private Provider<AccessEgressStopSearch> getAccessEgressStopSearchProvider(AccessEgressStopSearchParams accessEgressStopSearchParams) {
76+
return new Provider<>() {
77+
78+
@Inject
79+
private Injector injector;
80+
81+
@Override
82+
public AccessEgressStopSearch get() {
83+
return getAccessEgressStopSearch(accessEgressStopSearchParams, injector);
84+
}
85+
};
86+
}
87+
}

0 commit comments

Comments
 (0)