Skip to content

Commit

Permalink
Merge pull request #2841 from matsim-org/feature/add-stopaccesstime
Browse files Browse the repository at this point in the history
Add stop access and egress times
  • Loading branch information
jfbischoff authored Oct 11, 2023
2 parents f90ef2a + afb3416 commit ee2df4d
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.matsim.core.utils.collections.QuadTree;
import org.matsim.core.utils.geometry.CoordUtils;
import org.matsim.facilities.Facility;
import org.matsim.pt.transitSchedule.TransitScheduleUtils;
import org.matsim.pt.transitSchedule.api.TransitStopFacility;
import org.matsim.utils.objectattributes.attributable.Attributes;

Expand Down Expand Up @@ -105,7 +106,8 @@ private List<InitialStop> findAccessStops(Facility fromFacility, Facility toFaci
List<TransitStopFacility> stops = findNearbyStops(fromFacility, parameters, data);
List<InitialStop> initialStops = stops.stream().map(stop -> {
double beelineDistance = CoordUtils.calcEuclideanDistance(stop.getCoord(), fromFacility.getCoord());
double travelTime = Math.ceil(beelineDistance / parameters.getBeelineWalkSpeed());
double accessTime = TransitScheduleUtils.getStopAccessTime(stop);
double travelTime = Math.ceil(beelineDistance / parameters.getBeelineWalkSpeed()) + accessTime;
double disutility = travelTime * -parameters.getMarginalUtilityOfTravelTime_utl_s(TransportMode.walk);
return new InitialStop(stop, disutility, travelTime, beelineDistance * distanceFactor, TransportMode.walk);
}).collect(Collectors.toList());
Expand All @@ -122,7 +124,8 @@ private List<InitialStop> findEgressStops(Facility fromFacility, Facility toFaci
List<TransitStopFacility> stops = findNearbyStops(toFacility, parameters, data);
List<InitialStop> initialStops = stops.stream().map(stop -> {
double beelineDistance = CoordUtils.calcEuclideanDistance(stop.getCoord(), toFacility.getCoord());
double travelTime = Math.ceil(beelineDistance / parameters.getBeelineWalkSpeed());
double egressTime = TransitScheduleUtils.getStopEgressTime(stop);
double travelTime = Math.ceil(beelineDistance / parameters.getBeelineWalkSpeed()) + egressTime;
double disutility = travelTime * -parameters.getMarginalUtilityOfTravelTime_utl_s(TransportMode.walk);
return new InitialStop(stop, disutility, travelTime, beelineDistance * distanceFactor, TransportMode.walk);
}).collect(Collectors.toList());
Expand Down Expand Up @@ -223,26 +226,28 @@ private void addInitialStopsForParamSet(Facility fromFacility, Facility toFacili
// the router for the access/egress mode could not find a route, skip that access/egress mode
continue;
}
if (stopFacility != stop) {
if (direction == Direction.ACCESS) {
Leg transferLeg = PopulationUtils.createLeg(TransportMode.walk);
Route transferRoute = RouteUtils.createGenericRouteImpl(stopFacility.getLinkId(), stop.getLinkId());
transferRoute.setTravelTime(0);
transferRoute.setDistance(0);
transferLeg.setRoute(transferRoute);
transferLeg.setTravelTime(0);
double accessTime = TransitScheduleUtils.getStopAccessTime(stop);
double egressTime = TransitScheduleUtils.getStopEgressTime(stop);
if ((stopFacility != stop) || accessTime>0.0 || egressTime>0.0) {
if (direction == Direction.ACCESS) {
Leg transferLeg = PopulationUtils.createLeg(TransportMode.walk);
Route transferRoute = RouteUtils.createGenericRouteImpl(stopFacility.getLinkId(), stop.getLinkId());
transferRoute.setTravelTime(accessTime);
transferRoute.setDistance(0);
transferLeg.setRoute(transferRoute);
transferLeg.setTravelTime(accessTime);

List<PlanElement> tmp = new ArrayList<>(routeParts.size() + 1);
tmp.addAll(routeParts);
tmp.add(transferLeg);
routeParts = tmp;
} else {
Leg transferLeg = PopulationUtils.createLeg(TransportMode.walk);
Route transferRoute = RouteUtils.createGenericRouteImpl(stop.getLinkId(), stopFacility.getLinkId());
transferRoute.setTravelTime(0);
List<PlanElement> tmp = new ArrayList<>(routeParts.size() + 1);
tmp.addAll(routeParts);
tmp.add(transferLeg);
routeParts = tmp;
} else {
Leg transferLeg = PopulationUtils.createLeg(TransportMode.walk);
Route transferRoute = RouteUtils.createGenericRouteImpl(stop.getLinkId(), stopFacility.getLinkId());
transferRoute.setTravelTime(egressTime);
transferRoute.setDistance(0);
transferLeg.setRoute(transferRoute);
transferLeg.setTravelTime(0);
transferLeg.setTravelTime(egressTime);

List<PlanElement> tmp = new ArrayList<>(routeParts.size() + 1);
tmp.add(transferLeg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,43 +29,44 @@

/**
* Helper class for commonly used operations on TransitSchedules
*
*
* @author Thibaut Dubernet, gleich
*
*/
public final class TransitScheduleUtils {
// Logic gotten from PopulationUtils, but I am actually a bit unsure about the value of those methods now that
// attributable is the only way to get attributes...

public static Object getStopFacilityAttribute(TransitStopFacility facility, String key) {
return facility.getAttributes().getAttribute( key );
public final static String ACCESSTIME_ATTRIBUTE = "accessTime";
public final static String EGRESSTIME_ATTRIBUTE = "egressTime";
private TransitScheduleUtils() {
}

public static void putStopFacilityAttribute(TransitStopFacility facility, String key, Object value ) {
facility.getAttributes().putAttribute( key, value ) ;
public static double getStopAccessTime(TransitStopFacility stopFacility){
Object accessTime = stopFacility.getAttributes().getAttribute(ACCESSTIME_ATTRIBUTE);
return accessTime!=null?(double) accessTime:0.0;
}

public static Object removeStopFacilityAttribute( TransitStopFacility facility, String key ) {
return facility.getAttributes().removeAttribute( key );
public static void setStopAccessTime(TransitStopFacility stopFacility, double stopAccessTime){
stopFacility.getAttributes().putAttribute(ACCESSTIME_ATTRIBUTE,stopAccessTime);
}

public static Object getLineAttribute(TransitLine facility, String key) {
return facility.getAttributes().getAttribute( key );
public static double getStopEgressTime(TransitStopFacility stopFacility){
Object egressTime = stopFacility.getAttributes().getAttribute(EGRESSTIME_ATTRIBUTE);
return egressTime!=null?(double) egressTime:0.0;
}

public static void putLineAttribute(TransitLine facility, String key, Object value ) {
facility.getAttributes().putAttribute( key, value ) ;
public static void setStopEgressTime(TransitStopFacility stopFacility, double stopEgressTime){
stopFacility.getAttributes().putAttribute(EGRESSTIME_ATTRIBUTE,stopEgressTime);
}

public static Object removeLineAttribute( TransitLine facility, String key ) {
return facility.getAttributes().removeAttribute( key );
public static void setSymmetricStopAccessEgressTime(TransitStopFacility stopFacility, double stopAccessEgressTime){
setStopAccessTime(stopFacility,stopAccessEgressTime);
setStopEgressTime(stopFacility,stopAccessEgressTime);
}
public final static QuadTree<TransitStopFacility> createQuadTreeOfTransitStopFacilities(TransitSchedule transitSchedule) {

public static QuadTree<TransitStopFacility> createQuadTreeOfTransitStopFacilities(TransitSchedule transitSchedule) {
return createQuadTreeOfTransitStopFacilities(transitSchedule.getFacilities().values());
}
public final static QuadTree<TransitStopFacility> createQuadTreeOfTransitStopFacilities(Collection<TransitStopFacility> transitStopFacilities) {

public static QuadTree<TransitStopFacility> createQuadTreeOfTransitStopFacilities(Collection<TransitStopFacility> transitStopFacilities) {
double minX = Double.POSITIVE_INFINITY;
double minY = Double.POSITIVE_INFINITY;
double maxX = Double.NEGATIVE_INFINITY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@
import org.matsim.core.utils.geometry.CoordUtils;
import org.matsim.facilities.Facility;
import org.matsim.pt.PtConstants;
import org.matsim.pt.transitSchedule.TransitScheduleUtils;
import org.matsim.pt.transitSchedule.api.Departure;
import org.matsim.pt.transitSchedule.api.TransitLine;
import org.matsim.pt.transitSchedule.api.TransitRoute;
import org.matsim.pt.transitSchedule.api.TransitRouteStop;
import org.matsim.pt.transitSchedule.api.TransitSchedule;
import org.matsim.pt.transitSchedule.api.TransitScheduleFactory;
import org.matsim.pt.transitSchedule.api.TransitStopFacility;
import org.matsim.testcases.MatsimTestUtils;
import org.matsim.utils.objectattributes.attributable.AttributesImpl;

import java.util.ArrayList;
Expand Down Expand Up @@ -1235,6 +1237,69 @@ public List<? extends PlanElement> calcRoute(RoutingRequest request) {
Assert.assertEquals(Id.create("to", Link.class), leg.getRoute().getEndLinkId());
}

@Test
public void testIntermodalTripWithAccessAndEgressTimesAtStops() {
IntermodalFixture f = new IntermodalFixture();
f.scenario.getTransitSchedule().getFacilities().values()
.forEach(stopFacility -> TransitScheduleUtils.setSymmetricStopAccessEgressTime(stopFacility,120.0));
PlanCalcScoreConfigGroup.ModeParams walk = new PlanCalcScoreConfigGroup.ModeParams(TransportMode.walk);
walk.setMarginalUtilityOfTraveling(0.0);
f.config.planCalcScore().addModeParams(walk);

Map<String, RoutingModule> routingModules = new HashMap<>();
routingModules.put(TransportMode.walk,
new TeleportationRoutingModule(TransportMode.walk, f.scenario, 1.1, 1.3));
routingModules.put(TransportMode.bike,
new TeleportationRoutingModule(TransportMode.bike, f.scenario, 3, 1.4));

f.srrConfig.setUseIntermodalAccessEgress(true);
IntermodalAccessEgressParameterSet walkAccess = new IntermodalAccessEgressParameterSet();
walkAccess.setMode(TransportMode.walk);
walkAccess.setMaxRadius(1000);
walkAccess.setInitialSearchRadius(1000);
f.srrConfig.addIntermodalAccessEgress(walkAccess);
IntermodalAccessEgressParameterSet bikeAccess = new IntermodalAccessEgressParameterSet();
bikeAccess.setMode(TransportMode.bike);
bikeAccess.setMaxRadius(1500);
bikeAccess.setInitialSearchRadius(1500);
bikeAccess.setStopFilterAttribute("bikeAccessible");
bikeAccess.setLinkIdAttribute("accessLinkId_bike");
bikeAccess.setStopFilterValue("true");
f.srrConfig.addIntermodalAccessEgress(bikeAccess);

SwissRailRaptorData data = SwissRailRaptorData.create(f.scenario.getTransitSchedule(), null, RaptorUtils.createStaticConfig(f.config), f.scenario.getNetwork(), null);
DefaultRaptorStopFinder stopFinder = new DefaultRaptorStopFinder(new DefaultRaptorIntermodalAccessEgress(), routingModules);
SwissRailRaptor raptor = new SwissRailRaptor.Builder(data, f.scenario.getConfig()).with(stopFinder).build();

Facility fromFac = new FakeFacility(new Coord(10000, 10500), Id.create("from", Link.class));
Facility toFac = new FakeFacility(new Coord(50000, 10500), Id.create("to", Link.class));

List<? extends PlanElement> legs = raptor.calcRoute(DefaultRoutingRequest.withoutAttributes(fromFac, toFac, 7*3600, f.dummyPerson));
for (PlanElement leg : legs) {
System.out.println(leg);
}

Assert.assertEquals("wrong number of legs.", 5, legs.size());
Leg leg = (Leg) legs.get(1);
Assert.assertEquals(TransportMode.walk, leg.getMode());
Assert.assertEquals(Id.create("pt_2", Link.class), leg.getRoute().getStartLinkId());
Assert.assertEquals(Id.create("pt_2", Link.class), leg.getRoute().getEndLinkId());
Assert.assertEquals(120.0,leg.getTravelTime().seconds(), MatsimTestUtils.EPSILON);
leg = (Leg) legs.get(2);
Assert.assertEquals(TransportMode.pt, leg.getMode());
Assert.assertEquals(Id.create("pt_2", Link.class), leg.getRoute().getStartLinkId());
Assert.assertEquals(Id.create("pt_5", Link.class), leg.getRoute().getEndLinkId());
leg = (Leg) legs.get(3);
Assert.assertEquals(TransportMode.walk, leg.getMode());
Assert.assertEquals(Id.create("pt_5", Link.class), leg.getRoute().getStartLinkId());
Assert.assertEquals(Id.create("bike_5", Link.class), leg.getRoute().getEndLinkId());
Assert.assertEquals(120.0,leg.getTravelTime().seconds(), MatsimTestUtils.EPSILON);
leg = (Leg) legs.get(4);
Assert.assertEquals(TransportMode.bike, leg.getMode());
Assert.assertEquals(Id.create("bike_5", Link.class), leg.getRoute().getStartLinkId());
Assert.assertEquals(Id.create("to", Link.class), leg.getRoute().getEndLinkId());
}



/* for test of intermodal routing requiring transfers at the beginning or end of the pt trip,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.matsim.facilities.ActivityFacility;
import org.matsim.pt.router.TransitRouter;
import org.matsim.pt.routes.TransitPassengerRoute;
import org.matsim.pt.transitSchedule.TransitScheduleUtils;
import org.matsim.pt.transitSchedule.api.Departure;
import org.matsim.pt.transitSchedule.api.TransitLine;
import org.matsim.pt.transitSchedule.api.TransitRoute;
Expand Down Expand Up @@ -168,6 +169,28 @@ public void testWalkDurations() {
assertEquals(Math.ceil(expectedEgressWalkTime), ((Leg)legs.get(2)).getTravelTime().seconds(), MatsimTestUtils.EPSILON);
}


@Test
public void testStationAccessEgressTimes() {
Fixture f = new Fixture();
f.init();
RaptorParameters raptorParams = RaptorUtils.createParameters(f.config);
f.schedule.getFacilities().values().forEach(facility-> TransitScheduleUtils.setSymmetricStopAccessEgressTime(facility,120.0));
TransitRouter router = createTransitRouter(f.schedule, f.config, f.network);
Coord fromCoord = new Coord(3800, 5100);
Coord toCoord = new Coord(16100, 5050);
List<? extends PlanElement> legs = router.calcRoute(DefaultRoutingRequest.withoutAttributes(new FakeFacility(fromCoord), new FakeFacility(toCoord), 5.0*3600, null));
assertEquals(3, legs.size());
assertEquals(TransportMode.walk, ((Leg)legs.get(0)).getMode());
assertEquals(TransportMode.pt, ((Leg)legs.get(1)).getMode());
assertEquals(TransportMode.walk, ((Leg)legs.get(2)).getMode());

double expectedAccessWalkTime = 120.0 + CoordUtils.calcEuclideanDistance(f.schedule.getFacilities().get(Id.create("0", TransitStopFacility.class)).getCoord(), fromCoord) / raptorParams.getBeelineWalkSpeed();
assertEquals(Math.ceil(expectedAccessWalkTime), ((Leg)legs.get(0)).getTravelTime().seconds(), MatsimTestUtils.EPSILON);
double expectedEgressWalkTime = 120.0 + CoordUtils.calcEuclideanDistance(f.schedule.getFacilities().get(Id.create("6", TransitStopFacility.class)).getCoord(), toCoord) / raptorParams.getBeelineWalkSpeed();
assertEquals(Math.ceil(expectedEgressWalkTime), ((Leg)legs.get(2)).getTravelTime().seconds(), MatsimTestUtils.EPSILON);
}

@Test
public void testWalkDurations_range() {
Fixture f = new Fixture();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,33 +97,6 @@ public void testLoadScenario_loadPersonAttributes() {
Assert.assertTrue( caughtException );
}

@Test
public void testLoadScenario_loadTransitLinesAttributes() {
Config config = ConfigUtils.loadConfig(IOUtils.extendUrl(this.util.classInputResourcePath(), "transitConfig.xml"));
config.transit().setTransitLinesAttributesFile("transitLinesAttributes.xml");
config.transit().setInsistingOnUsingDeprecatedAttributeFiles(true);
Scenario scenario = ScenarioUtils.loadScenario(config);
Assert.assertEquals(
"unexpected attribute value",
"world",
TransitScheduleUtils.getLineAttribute(
scenario.getTransitSchedule().getTransitLines().get(Id.create("Blue Line", TransitLine.class)),
"hello"));
}

@Test
public void testLoadScenario_loadTransitStopsAttributes() {
Config config = ConfigUtils.loadConfig(IOUtils.extendUrl(this.util.classInputResourcePath(), "transitConfig.xml"));
config.transit().setTransitStopsAttributesFile("transitStopsAttributes.xml");
config.transit().setInsistingOnUsingDeprecatedAttributeFiles(true);
Scenario scenario = ScenarioUtils.loadScenario(config);
Assert.assertEquals(
"unexpected attribute value",
Boolean.TRUE,
TransitScheduleUtils.getStopFacilityAttribute(
scenario.getTransitSchedule().getFacilities().get(Id.create(1, TransitStopFacility.class)),
"hasP+R"));
}

@Test
public void testLoadScenario_loadFacilitiesAttributes() {
Expand Down

0 comments on commit ee2df4d

Please sign in to comment.